OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2004 Google Inc. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions are met: | |
7 * | |
8 * 1. Redistributions of source code must retain the above copyright notice, | |
9 * this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 * this list of conditions and the following disclaimer in the documentation | |
12 * and/or other materials provided with the distribution. | |
13 * 3. The name of the author may not be used to endorse or promote products | |
14 * derived from this software without specific prior written permission. | |
15 * | |
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 */ | |
27 | |
28 // Implementation of GtkVideoRenderer | |
29 | |
30 #include "talk/media/devices/gtkvideorenderer.h" | |
31 | |
32 #include <gdk/gdk.h> | |
33 #include <glib.h> | |
34 #include <gtk/gtk.h> | |
35 | |
36 #include "talk/media/base/videocommon.h" | |
37 #include "talk/media/base/videoframe.h" | |
38 | |
39 namespace cricket { | |
40 | |
41 class ScopedGdkLock { | |
42 public: | |
43 ScopedGdkLock() { | |
44 gdk_threads_enter(); | |
45 } | |
46 | |
47 ~ScopedGdkLock() { | |
48 gdk_threads_leave(); | |
49 } | |
50 }; | |
51 | |
52 GtkVideoRenderer::GtkVideoRenderer(int x, int y) | |
53 : window_(NULL), | |
54 draw_area_(NULL), | |
55 initial_x_(x), | |
56 initial_y_(y), | |
57 width_(0), | |
58 height_(0) { | |
59 g_type_init(); | |
60 // g_thread_init API is deprecated since glib 2.31.0, see release note: | |
61 // http://mail.gnome.org/archives/gnome-announce-list/2011-October/msg00041.ht
ml | |
62 #if !GLIB_CHECK_VERSION(2, 31, 0) | |
63 g_thread_init(NULL); | |
64 #endif | |
65 gdk_threads_init(); | |
66 } | |
67 | |
68 GtkVideoRenderer::~GtkVideoRenderer() { | |
69 if (window_) { | |
70 ScopedGdkLock lock; | |
71 gtk_widget_destroy(window_); | |
72 // Run the Gtk main loop to tear down the window. | |
73 Pump(); | |
74 } | |
75 // Don't need to destroy draw_area_ because it is not top-level, so it is | |
76 // implicitly destroyed by the above. | |
77 } | |
78 | |
79 bool GtkVideoRenderer::SetSize(int width, int height, int reserved) { | |
80 ScopedGdkLock lock; | |
81 | |
82 // If the dimension is the same, no-op. | |
83 if (width_ == width && height_ == height) { | |
84 return true; | |
85 } | |
86 | |
87 // For the first frame, initialize the GTK window | |
88 if ((!window_ && !Initialize(width, height)) || IsClosed()) { | |
89 return false; | |
90 } | |
91 | |
92 image_.reset(new uint8_t[width * height * 4]); | |
93 gtk_widget_set_size_request(draw_area_, width, height); | |
94 | |
95 width_ = width; | |
96 height_ = height; | |
97 return true; | |
98 } | |
99 | |
100 bool GtkVideoRenderer::RenderFrame(const VideoFrame* video_frame) { | |
101 if (!video_frame) { | |
102 return false; | |
103 } | |
104 | |
105 const VideoFrame* frame = video_frame->GetCopyWithRotationApplied(); | |
106 | |
107 // Need to set size as the frame might be rotated. | |
108 if (!SetSize(frame->GetWidth(), frame->GetHeight(), 0)) { | |
109 return false; | |
110 } | |
111 | |
112 // convert I420 frame to ABGR format, which is accepted by GTK | |
113 frame->ConvertToRgbBuffer(cricket::FOURCC_ABGR, | |
114 image_.get(), | |
115 frame->GetWidth() * frame->GetHeight() * 4, | |
116 frame->GetWidth() * 4); | |
117 | |
118 ScopedGdkLock lock; | |
119 | |
120 if (IsClosed()) { | |
121 return false; | |
122 } | |
123 | |
124 // draw the ABGR image | |
125 gdk_draw_rgb_32_image(draw_area_->window, | |
126 draw_area_->style->fg_gc[GTK_STATE_NORMAL], | |
127 0, | |
128 0, | |
129 frame->GetWidth(), | |
130 frame->GetHeight(), | |
131 GDK_RGB_DITHER_MAX, | |
132 image_.get(), | |
133 frame->GetWidth() * 4); | |
134 | |
135 // Run the Gtk main loop to refresh the window. | |
136 Pump(); | |
137 return true; | |
138 } | |
139 | |
140 bool GtkVideoRenderer::Initialize(int width, int height) { | |
141 gtk_init(NULL, NULL); | |
142 window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
143 draw_area_ = gtk_drawing_area_new(); | |
144 if (!window_ || !draw_area_) { | |
145 return false; | |
146 } | |
147 | |
148 gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER); | |
149 gtk_window_set_title(GTK_WINDOW(window_), "Video Renderer"); | |
150 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | |
151 gtk_widget_set_size_request(draw_area_, width, height); | |
152 gtk_container_add(GTK_CONTAINER(window_), draw_area_); | |
153 gtk_widget_show_all(window_); | |
154 gtk_window_move(GTK_WINDOW(window_), initial_x_, initial_y_); | |
155 | |
156 image_.reset(new uint8_t[width * height * 4]); | |
157 return true; | |
158 } | |
159 | |
160 void GtkVideoRenderer::Pump() { | |
161 while (gtk_events_pending()) { | |
162 gtk_main_iteration(); | |
163 } | |
164 } | |
165 | |
166 bool GtkVideoRenderer::IsClosed() const { | |
167 if (!window_) { | |
168 // Not initialized yet, so hasn't been closed. | |
169 return false; | |
170 } | |
171 | |
172 if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) { | |
173 return true; | |
174 } | |
175 | |
176 return false; | |
177 } | |
178 | |
179 } // namespace cricket | |
OLD | NEW |