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:
ScopedGdkLock()43 ScopedGdkLock() {
44 gdk_threads_enter();
45 }
46
~ScopedGdkLock()47 ~ScopedGdkLock() {
48 gdk_threads_leave();
49 }
50 };
51
GtkVideoRenderer(int x,int y)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.html
62 #if !GLIB_CHECK_VERSION(2, 31, 0)
63 g_thread_init(NULL);
64 #endif
65 gdk_threads_init();
66 }
67
~GtkVideoRenderer()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
SetSize(int width,int height,int reserved)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
RenderFrame(const VideoFrame * video_frame)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
Initialize(int width,int height)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
Pump()160 void GtkVideoRenderer::Pump() {
161 while (gtk_events_pending()) {
162 gtk_main_iteration();
163 }
164 }
165
IsClosed() const166 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
180