blob: b88c391cafa6f351587abca3113e5af04db6b7bb [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00001/*
kjellander1afca732016-02-07 20:46:45 -08002 * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 *
kjellander1afca732016-02-07 20:46:45 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00009 */
10
henrike@webrtc.org28e20752013-07-10 00:45:36 +000011// Implementation of GtkVideoRenderer
12
kjellandera96e2d72016-02-04 23:52:28 -080013#include "webrtc/media/devices/gtkvideorenderer.h"
nisseacd935b2016-11-11 03:55:13 -080014#include "webrtc/video_frame.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000015
henrike@webrtc.org28e20752013-07-10 00:45:36 +000016#include <gdk/gdk.h>
buildbot@webrtc.orga09a9992014-08-13 17:26:08 +000017#include <glib.h>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000018#include <gtk/gtk.h>
19
nisse9f8e37b2016-09-01 01:06:23 -070020#include "libyuv/convert_argb.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021
22namespace cricket {
23
24class ScopedGdkLock {
25 public:
26 ScopedGdkLock() {
27 gdk_threads_enter();
28 }
29
30 ~ScopedGdkLock() {
31 gdk_threads_leave();
32 }
33};
34
35GtkVideoRenderer::GtkVideoRenderer(int x, int y)
36 : window_(NULL),
37 draw_area_(NULL),
38 initial_x_(x),
guoweis@webrtc.org00c509a2015-03-12 21:37:26 +000039 initial_y_(y),
40 width_(0),
41 height_(0) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000042 g_type_init();
decurtis@webrtc.orgbfa3c722015-02-17 21:22:48 +000043 // g_thread_init API is deprecated since glib 2.31.0, see release note:
44 // http://mail.gnome.org/archives/gnome-announce-list/2011-October/msg00041.html
45#if !GLIB_CHECK_VERSION(2, 31, 0)
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046 g_thread_init(NULL);
decurtis@webrtc.orgbfa3c722015-02-17 21:22:48 +000047#endif
henrike@webrtc.org28e20752013-07-10 00:45:36 +000048 gdk_threads_init();
49}
50
51GtkVideoRenderer::~GtkVideoRenderer() {
52 if (window_) {
53 ScopedGdkLock lock;
54 gtk_widget_destroy(window_);
55 // Run the Gtk main loop to tear down the window.
56 Pump();
57 }
58 // Don't need to destroy draw_area_ because it is not top-level, so it is
59 // implicitly destroyed by the above.
60}
61
nisse1509fa12016-03-23 04:05:57 -070062bool GtkVideoRenderer::SetSize(int width, int height) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000063 ScopedGdkLock lock;
64
guoweis@webrtc.org00c509a2015-03-12 21:37:26 +000065 // If the dimension is the same, no-op.
66 if (width_ == width && height_ == height) {
67 return true;
68 }
69
henrike@webrtc.org28e20752013-07-10 00:45:36 +000070 // For the first frame, initialize the GTK window
71 if ((!window_ && !Initialize(width, height)) || IsClosed()) {
72 return false;
73 }
74
Peter Boström0c4e06b2015-10-07 12:23:21 +020075 image_.reset(new uint8_t[width * height * 4]);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076 gtk_widget_set_size_request(draw_area_, width, height);
guoweis@webrtc.org00c509a2015-03-12 21:37:26 +000077
78 width_ = width;
79 height_ = height;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000080 return true;
81}
82
nisseacd935b2016-11-11 03:55:13 -080083void GtkVideoRenderer::OnFrame(const webrtc::VideoFrame& video_frame) {
84 rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer(
nissed5074722016-08-30 08:45:44 -070085 webrtc::I420Buffer::Rotate(video_frame.video_frame_buffer(),
nisseacd935b2016-11-11 03:55:13 -080086 video_frame.rotation()));
guoweis@webrtc.org00c509a2015-03-12 21:37:26 +000087
88 // Need to set size as the frame might be rotated.
nisseacd935b2016-11-11 03:55:13 -080089 if (!SetSize(buffer->width(), buffer->height())) {
nisse1509fa12016-03-23 04:05:57 -070090 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091 }
92
93 // convert I420 frame to ABGR format, which is accepted by GTK
nisse9f8e37b2016-09-01 01:06:23 -070094 libyuv::I420ToARGB(buffer->DataY(), buffer->StrideY(),
95 buffer->DataU(), buffer->StrideU(),
96 buffer->DataV(), buffer->StrideV(),
nisseacd935b2016-11-11 03:55:13 -080097 image_.get(), buffer->width() * 4,
nisse9f8e37b2016-09-01 01:06:23 -070098 buffer->width(), buffer->height());
henrike@webrtc.org28e20752013-07-10 00:45:36 +000099
100 ScopedGdkLock lock;
101
102 if (IsClosed()) {
nisse1509fa12016-03-23 04:05:57 -0700103 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000104 }
105
106 // draw the ABGR image
107 gdk_draw_rgb_32_image(draw_area_->window,
108 draw_area_->style->fg_gc[GTK_STATE_NORMAL],
109 0,
110 0,
nisseacd935b2016-11-11 03:55:13 -0800111 buffer->width(),
112 buffer->height(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113 GDK_RGB_DITHER_MAX,
114 image_.get(),
nisseacd935b2016-11-11 03:55:13 -0800115 buffer->width() * 4);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000116
117 // Run the Gtk main loop to refresh the window.
118 Pump();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119}
120
121bool GtkVideoRenderer::Initialize(int width, int height) {
122 gtk_init(NULL, NULL);
123 window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
124 draw_area_ = gtk_drawing_area_new();
125 if (!window_ || !draw_area_) {
126 return false;
127 }
128
129 gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER);
130 gtk_window_set_title(GTK_WINDOW(window_), "Video Renderer");
131 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
132 gtk_widget_set_size_request(draw_area_, width, height);
133 gtk_container_add(GTK_CONTAINER(window_), draw_area_);
134 gtk_widget_show_all(window_);
135 gtk_window_move(GTK_WINDOW(window_), initial_x_, initial_y_);
136
Peter Boström0c4e06b2015-10-07 12:23:21 +0200137 image_.reset(new uint8_t[width * height * 4]);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138 return true;
139}
140
141void GtkVideoRenderer::Pump() {
142 while (gtk_events_pending()) {
143 gtk_main_iteration();
144 }
145}
146
147bool GtkVideoRenderer::IsClosed() const {
148 if (!window_) {
149 // Not initialized yet, so hasn't been closed.
150 return false;
151 }
152
153 if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) {
154 return true;
155 }
156
157 return false;
158}
159
160} // namespace cricket