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