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 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 68GtkVideoRenderer::~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 79bool 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 100bool 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 140bool 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 160void GtkVideoRenderer::Pump() { 161 while (gtk_events_pending()) { 162 gtk_main_iteration(); 163 } 164} 165 166bool 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