gtkvideorenderer.cc revision f9a75d99b92402c56744121b7bc991a9c71cf324
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
39namespace cricket {
40
41class ScopedGdkLock {
42 public:
43  ScopedGdkLock() {
44    gdk_threads_enter();
45  }
46
47  ~ScopedGdkLock() {
48    gdk_threads_leave();
49  }
50};
51
52GtkVideoRenderer::GtkVideoRenderer(int x, int y)
53    : window_(NULL),
54      draw_area_(NULL),
55      initial_x_(x),
56      initial_y_(y) {
57  g_type_init();
58  // g_thread_init API is deprecated since glib 2.31.0, see release note:
59  // http://mail.gnome.org/archives/gnome-announce-list/2011-October/msg00041.html
60#if !GLIB_CHECK_VERSION(2, 31, 0)
61  g_thread_init(NULL);
62#endif
63  gdk_threads_init();
64}
65
66GtkVideoRenderer::~GtkVideoRenderer() {
67  if (window_) {
68    ScopedGdkLock lock;
69    gtk_widget_destroy(window_);
70    // Run the Gtk main loop to tear down the window.
71    Pump();
72  }
73  // Don't need to destroy draw_area_ because it is not top-level, so it is
74  // implicitly destroyed by the above.
75}
76
77bool GtkVideoRenderer::SetSize(int width, int height, int reserved) {
78  ScopedGdkLock lock;
79
80  // For the first frame, initialize the GTK window
81  if ((!window_ && !Initialize(width, height)) || IsClosed()) {
82    return false;
83  }
84
85  image_.reset(new uint8[width * height * 4]);
86  gtk_widget_set_size_request(draw_area_, width, height);
87  return true;
88}
89
90bool GtkVideoRenderer::RenderFrame(const VideoFrame* frame) {
91  if (!frame) {
92    return false;
93  }
94
95  // convert I420 frame to ABGR format, which is accepted by GTK
96  frame->ConvertToRgbBuffer(cricket::FOURCC_ABGR,
97                            image_.get(),
98                            frame->GetWidth() * frame->GetHeight() * 4,
99                            frame->GetWidth() * 4);
100
101  ScopedGdkLock lock;
102
103  if (IsClosed()) {
104    return false;
105  }
106
107  // draw the ABGR image
108  gdk_draw_rgb_32_image(draw_area_->window,
109                        draw_area_->style->fg_gc[GTK_STATE_NORMAL],
110                        0,
111                        0,
112                        frame->GetWidth(),
113                        frame->GetHeight(),
114                        GDK_RGB_DITHER_MAX,
115                        image_.get(),
116                        frame->GetWidth() * 4);
117
118  // Run the Gtk main loop to refresh the window.
119  Pump();
120  return true;
121}
122
123bool GtkVideoRenderer::Initialize(int width, int height) {
124  gtk_init(NULL, NULL);
125  window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
126  draw_area_ = gtk_drawing_area_new();
127  if (!window_ || !draw_area_) {
128    return false;
129  }
130
131  gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
132  gtk_window_set_title(GTK_WINDOW(window_), "Video Renderer");
133  gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
134  gtk_widget_set_size_request(draw_area_, width, height);
135  gtk_container_add(GTK_CONTAINER(window_), draw_area_);
136  gtk_widget_show_all(window_);
137  gtk_window_move(GTK_WINDOW(window_), initial_x_, initial_y_);
138
139  image_.reset(new uint8[width * height * 4]);
140  return true;
141}
142
143void GtkVideoRenderer::Pump() {
144  while (gtk_events_pending()) {
145    gtk_main_iteration();
146  }
147}
148
149bool GtkVideoRenderer::IsClosed() const {
150  if (!window_) {
151    // Not initialized yet, so hasn't been closed.
152    return false;
153  }
154
155  if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) {
156    return true;
157  }
158
159  return false;
160}
161
162}  // namespace cricket
163