Home | History | Annotate | Download | only in devices
      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.html
     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
    180