| /* |
| * Copyright (c) 2004 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| // Implementation of GtkVideoRenderer |
| |
| #include "webrtc/media/devices/gtkvideorenderer.h" |
| |
| #include <gdk/gdk.h> |
| #include <glib.h> |
| #include <gtk/gtk.h> |
| |
| #include "libyuv/convert_argb.h" |
| #include "webrtc/media/engine/webrtcvideoframe.h" |
| |
| namespace cricket { |
| |
| class ScopedGdkLock { |
| public: |
| ScopedGdkLock() { |
| gdk_threads_enter(); |
| } |
| |
| ~ScopedGdkLock() { |
| gdk_threads_leave(); |
| } |
| }; |
| |
| GtkVideoRenderer::GtkVideoRenderer(int x, int y) |
| : window_(NULL), |
| draw_area_(NULL), |
| initial_x_(x), |
| initial_y_(y), |
| width_(0), |
| height_(0) { |
| g_type_init(); |
| // g_thread_init API is deprecated since glib 2.31.0, see release note: |
| // http://mail.gnome.org/archives/gnome-announce-list/2011-October/msg00041.html |
| #if !GLIB_CHECK_VERSION(2, 31, 0) |
| g_thread_init(NULL); |
| #endif |
| gdk_threads_init(); |
| } |
| |
| GtkVideoRenderer::~GtkVideoRenderer() { |
| if (window_) { |
| ScopedGdkLock lock; |
| gtk_widget_destroy(window_); |
| // Run the Gtk main loop to tear down the window. |
| Pump(); |
| } |
| // Don't need to destroy draw_area_ because it is not top-level, so it is |
| // implicitly destroyed by the above. |
| } |
| |
| bool GtkVideoRenderer::SetSize(int width, int height) { |
| ScopedGdkLock lock; |
| |
| // If the dimension is the same, no-op. |
| if (width_ == width && height_ == height) { |
| return true; |
| } |
| |
| // For the first frame, initialize the GTK window |
| if ((!window_ && !Initialize(width, height)) || IsClosed()) { |
| return false; |
| } |
| |
| image_.reset(new uint8_t[width * height * 4]); |
| gtk_widget_set_size_request(draw_area_, width, height); |
| |
| width_ = width; |
| height_ = height; |
| return true; |
| } |
| |
| void GtkVideoRenderer::OnFrame(const VideoFrame& video_frame) { |
| const cricket::WebRtcVideoFrame frame( |
| webrtc::I420Buffer::Rotate(video_frame.video_frame_buffer(), |
| video_frame.rotation()), |
| webrtc::kVideoRotation_0, video_frame.timestamp_us()); |
| |
| // Need to set size as the frame might be rotated. |
| if (!SetSize(frame.width(), frame.height())) { |
| return; |
| } |
| |
| // convert I420 frame to ABGR format, which is accepted by GTK |
| rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer( |
| frame.video_frame_buffer()); |
| libyuv::I420ToARGB(buffer->DataY(), buffer->StrideY(), |
| buffer->DataU(), buffer->StrideU(), |
| buffer->DataV(), buffer->StrideV(), |
| image_.get(), frame.width() * 4, |
| buffer->width(), buffer->height()); |
| |
| ScopedGdkLock lock; |
| |
| if (IsClosed()) { |
| return; |
| } |
| |
| // draw the ABGR image |
| gdk_draw_rgb_32_image(draw_area_->window, |
| draw_area_->style->fg_gc[GTK_STATE_NORMAL], |
| 0, |
| 0, |
| frame.width(), |
| frame.height(), |
| GDK_RGB_DITHER_MAX, |
| image_.get(), |
| frame.width() * 4); |
| |
| // Run the Gtk main loop to refresh the window. |
| Pump(); |
| } |
| |
| bool GtkVideoRenderer::Initialize(int width, int height) { |
| gtk_init(NULL, NULL); |
| window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
| draw_area_ = gtk_drawing_area_new(); |
| if (!window_ || !draw_area_) { |
| return false; |
| } |
| |
| gtk_window_set_position(GTK_WINDOW(window_), GTK_WIN_POS_CENTER); |
| gtk_window_set_title(GTK_WINDOW(window_), "Video Renderer"); |
| gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
| gtk_widget_set_size_request(draw_area_, width, height); |
| gtk_container_add(GTK_CONTAINER(window_), draw_area_); |
| gtk_widget_show_all(window_); |
| gtk_window_move(GTK_WINDOW(window_), initial_x_, initial_y_); |
| |
| image_.reset(new uint8_t[width * height * 4]); |
| return true; |
| } |
| |
| void GtkVideoRenderer::Pump() { |
| while (gtk_events_pending()) { |
| gtk_main_iteration(); |
| } |
| } |
| |
| bool GtkVideoRenderer::IsClosed() const { |
| if (!window_) { |
| // Not initialized yet, so hasn't been closed. |
| return false; |
| } |
| |
| if (!GTK_IS_WINDOW(window_) || !GTK_IS_DRAWING_AREA(draw_area_)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace cricket |