blob: 6a27bb5e4512395b2861f6b8aae3ae290041cdea [file] [log] [blame]
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001/*
2 * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
3 * Author: Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>
4 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Additional permission under GNU GPL version 3 section 7:
21 *
22 * If you modify this program, or any covered work, by linking or
23 * combining it with the OpenSSL project's OpenSSL library (or a
24 * modified version of that library), containing parts covered by the
25 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
26 * grants you additional permission to convey the resulting work.
27 * Corresponding Source for a non-source form of such a combination
28 * shall include the source code for the parts of OpenSSL used as well
29 * as that of the covered work.
30 */
31
32#include "video_widget.h"
33
34#include <clutter/clutter.h>
35#include <clutter-gtk/clutter-gtk.h>
36#include <video/renderer.h>
Stepan Salenikovich033dc832015-03-23 15:56:47 -040037#include <video/sourcemodel.h>
Stepan Salenikovich50c989b2015-03-21 18:32:46 -040038#include <video/devicemodel.h>
39#include <QtCore/QUrl>
40#include "../defines.h"
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -040041#include <stdlib.h>
42#include "xrectsel.h"
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050043
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040044#define VIDEO_LOCAL_SIZE 150
45#define VIDEO_LOCAL_OPACITY_DEFAULT 150 /* out of 255 */
46
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040047/* check video frame queues at this rate;
48 * use 30 ms (about 30 fps) since we don't expect to
49 * receive video frames faster than that */
50#define FRAME_RATE_PERIOD 30
51
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050052struct _VideoWidgetClass {
53 GtkBinClass parent_class;
54};
55
56struct _VideoWidget {
57 GtkBin parent;
58};
59
60typedef struct _VideoWidgetPrivate VideoWidgetPrivate;
61
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040062typedef struct _VideoWidgetRenderer VideoWidgetRenderer;
63
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050064struct _VideoWidgetPrivate {
65 GtkWidget *clutter_widget;
66 ClutterActor *stage;
67 ClutterActor *video_container;
68
69 /* remote peer data */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040070 VideoWidgetRenderer *remote;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050071
72 /* local peer data */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040073 VideoWidgetRenderer *local;
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040074
75 guint frame_timeout_source;
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040076};
77
78struct _VideoWidgetRenderer {
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040079 GAsyncQueue *frame_queue;
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040080 ClutterActor *actor;
81 Video::Renderer *renderer;
82 QMetaObject::Connection frame_update;
83 QMetaObject::Connection render_stop;
84 QMetaObject::Connection render_start;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050085};
86
87G_DEFINE_TYPE_WITH_PRIVATE(VideoWidget, video_widget, GTK_TYPE_BIN);
88
89#define VIDEO_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), VIDEO_WIDGET_TYPE, VideoWidgetPrivate))
90
91typedef struct _FrameInfo
92{
93 ClutterActor *image_actor;
94 ClutterContent *image;
95 guchar *data;
96 gint data_size;
97 gint width;
98 gint height;
99} FrameInfo;
100
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400101/* static prototypes */
102static FrameInfo *prepare_framedata(Video::Renderer *renderer, ClutterActor* image_actor);
103static void free_framedata(gpointer data);
104static void clutter_render_image(FrameInfo *frame);
105static gboolean check_frame_queue(VideoWidget *self);
106static void renderer_stop(VideoWidgetRenderer *renderer);
107static void renderer_start(VideoWidgetRenderer *renderer);
108static void video_widget_set_renderer(VideoWidgetRenderer *renderer);
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400109static void on_drag_data_received(GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint32, gpointer);
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400110static gboolean on_button_press_in_screen_event(GtkWidget *, GdkEventButton *, gpointer);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400111
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500112/*
113 * video_widget_dispose()
114 *
115 * The dispose function for the video_widget class.
116 */
117static void
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400118video_widget_dispose(GObject *object)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500119{
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400120 VideoWidget *self = VIDEO_WIDGET(object);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500121 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
122
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400123 QObject::disconnect(priv->remote->frame_update);
124 QObject::disconnect(priv->remote->render_stop);
125 QObject::disconnect(priv->remote->render_start);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500126
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400127 QObject::disconnect(priv->local->frame_update);
128 QObject::disconnect(priv->local->render_stop);
129 QObject::disconnect(priv->local->render_start);
130
Stepan Salenikovichbf74af82015-03-13 11:35:58 -0400131 /* dispose may be called multiple times, make sure
132 * not to call g_source_remove more than once */
133 if (priv->frame_timeout_source) {
134 g_source_remove(priv->frame_timeout_source);
135 priv->frame_timeout_source = 0;
136 }
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400137
Stepan Salenikovichbf74af82015-03-13 11:35:58 -0400138 if (priv->remote->frame_queue) {
139 g_async_queue_unref(priv->remote->frame_queue);
140 priv->remote->frame_queue = NULL;
141 }
142
143 if (priv->local->frame_queue) {
144 g_async_queue_unref(priv->local->frame_queue);
145 priv->local->frame_queue = NULL;
146 }
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400147
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400148 G_OBJECT_CLASS(video_widget_parent_class)->dispose(object);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500149}
150
151
152/*
153 * video_widget_finalize()
154 *
155 * The finalize function for the video_widget class.
156 */
157static void
158video_widget_finalize(GObject *object)
159{
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400160 VideoWidget *self = VIDEO_WIDGET(object);
161 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
162
163 g_free(priv->remote);
164 g_free(priv->local);
165
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500166 G_OBJECT_CLASS(video_widget_parent_class)->finalize(object);
167}
168
169
170/*
171 * video_widget_class_init()
172 *
173 * This function init the video_widget_class.
174 */
175static void
176video_widget_class_init(VideoWidgetClass *klass)
177{
178 GObjectClass *object_class = G_OBJECT_CLASS(klass);
179
180 /* override method */
181 object_class->dispose = video_widget_dispose;
182 object_class->finalize = video_widget_finalize;
183}
184
185
186/*
187 * video_widget_init()
188 *
189 * This function init the video_widget.
190 * - init clutter
191 * - init all the widget members
192 */
193static void
194video_widget_init(VideoWidget *self)
195{
196 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
197
198 /* init clutter widget */
199 priv->clutter_widget = gtk_clutter_embed_new();
200 /* add it to the video_widget */
201 gtk_container_add(GTK_CONTAINER(self), priv->clutter_widget);
202 /* get the stage */
203 priv->stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(priv->clutter_widget));
204
205 /* layout manager is used to arrange children in space, here we ask clutter
206 * to align children to fill the space when resizing the window */
207 clutter_actor_set_layout_manager(priv->stage,
208 clutter_bin_layout_new(CLUTTER_BIN_ALIGNMENT_FILL, CLUTTER_BIN_ALIGNMENT_FILL));
209
210 /* add a scene container where we can add and remove our actors */
211 priv->video_container = clutter_actor_new();
212 clutter_actor_set_background_color(priv->video_container, CLUTTER_COLOR_Black);
213 clutter_actor_add_child(priv->stage, priv->video_container);
214
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400215 /* init the remote and local structs */
216 priv->remote = g_new0(VideoWidgetRenderer, 1);
217 priv->local = g_new0(VideoWidgetRenderer, 1);
218
219 /* arrange remote actors */
220 priv->remote->actor = clutter_actor_new();
221 clutter_actor_insert_child_below(priv->video_container, priv->remote->actor, NULL);
222 /* the remote camera must always fill the container size */
223 ClutterConstraint *constraint = clutter_bind_constraint_new(priv->video_container,
224 CLUTTER_BIND_SIZE, 0);
225 clutter_actor_add_constraint(priv->remote->actor, constraint);
226
227 /* arrange local actor */
228 priv->local->actor = clutter_actor_new();
229 clutter_actor_insert_child_above(priv->video_container, priv->local->actor, NULL);
230 /* set size to square, but it will stay the aspect ratio when the image is rendered */
231 clutter_actor_set_size(priv->local->actor, VIDEO_LOCAL_SIZE, VIDEO_LOCAL_SIZE);
232 /* set position constraint to right cornder */
233 constraint = clutter_align_constraint_new(priv->video_container,
234 CLUTTER_ALIGN_BOTH, 0.99);
235 clutter_actor_add_constraint(priv->local->actor, constraint);
236 clutter_actor_set_opacity(priv->local->actor,
237 VIDEO_LOCAL_OPACITY_DEFAULT);
238
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400239 /* init frame queues and the timeout sources to check them */
240 priv->remote->frame_queue = g_async_queue_new_full((GDestroyNotify)free_framedata);
241 priv->local->frame_queue = g_async_queue_new_full((GDestroyNotify)free_framedata);
242 priv->frame_timeout_source = g_timeout_add(FRAME_RATE_PERIOD, (GSourceFunc)check_frame_queue, self);
243
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400244 /* handle button event */
245 g_signal_connect(GTK_WIDGET(self), "button-press-event", G_CALLBACK(on_button_press_in_screen_event), NULL);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500246
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400247 /* drag & drop files as video sources */
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400248 gtk_drag_dest_set(GTK_WIDGET(self), GTK_DEST_DEFAULT_ALL, NULL, 0, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_PRIVATE));
249 gtk_drag_dest_add_uri_targets(GTK_WIDGET(self));
250 g_signal_connect(GTK_WIDGET(self), "drag-data-received", G_CALLBACK(on_drag_data_received), NULL);
251}
252
253/*
254 * on_drag_data_received()
255 *
256 * Handle dragged data in the video widget window.
257 * Dropping an image causes the client to switch the video input to that image.
258 */
259static void
260on_drag_data_received(G_GNUC_UNUSED GtkWidget *self,
261 G_GNUC_UNUSED GdkDragContext *context,
262 G_GNUC_UNUSED gint x,
263 G_GNUC_UNUSED gint y,
264 GtkSelectionData *selection_data,
265 G_GNUC_UNUSED guint info,
266 G_GNUC_UNUSED guint32 time,
267 G_GNUC_UNUSED gpointer data)
268{
269 gchar **uris = gtk_selection_data_get_uris(selection_data);
270
271 /* only play the first selection */
272 if (uris && *uris)
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400273 Video::SourceModel::instance()->setFile(QUrl(*uris));
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400274
275 g_strfreev(uris);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500276}
277
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400278static void
279switch_video_input(G_GNUC_UNUSED GtkWidget *widget, Video::Device *device)
280{
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400281 Video::SourceModel::instance()->switchTo(device);
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400282}
283
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400284static void
285switch_video_input_screen(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gpointer user_data)
286{
287 unsigned x, y;
288 unsigned width, height;
289
290 /* try to get the dispaly or default to 0 */
291 QString display_env{getenv("DISPLAY")};
292 int display = 0;
293
294 if (!display_env.isEmpty()) {
295 auto list = display_env.split(":", QString::SkipEmptyParts);
296 /* should only be one display, so get the first one */
297 if (list.size() > 0) {
298 display = list.at(0).toInt();
299 g_debug("sharing screen from DISPLAY %d", display);
300 }
301 }
302
303 x = y = width = height = 0;
304
305 xrectsel(&x, &y, &width, &height);
306
307 if (!width || !height) {
308 x = y = 0;
309 width = gdk_screen_width();
310 height = gdk_screen_height();
311 }
312
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400313 Video::SourceModel::instance()->setDisplay(display, QRect(x,y,width,height));
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400314}
315
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400316/*
317 * on_button_press_in_screen_event()
318 *
319 * Handle button event in the video screen.
320 */
321static gboolean
322on_button_press_in_screen_event(G_GNUC_UNUSED GtkWidget *widget,
323 GdkEventButton *event,
324 G_GNUC_UNUSED gpointer data)
325{
326 /* check for right click */
327 if (event->button != BUTTON_RIGHT_CLICK || event->type != GDK_BUTTON_PRESS)
328 return FALSE;
329
330 /* create menu with available video sources */
331 GtkWidget *menu = gtk_menu_new();
332
333 /* list available devices and check off the active device */
334 auto device_list = Video::DeviceModel::instance()->devices();
335
336 for( auto device: device_list) {
337 GtkWidget *item = gtk_check_menu_item_new_with_mnemonic(device->name().toLocal8Bit().constData());
338 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
339 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), device->isActive());
340 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input), device);
341 }
342
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400343 /* add screen area as an input */
344 GtkWidget *item = gtk_check_menu_item_new_with_mnemonic("Share screen area");
345 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
346 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input_screen), NULL);
347
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400348 /* show menu */
349 gtk_widget_show_all(menu);
350 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
351
352 return TRUE; /* event has been fully handled */
353}
354
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500355static FrameInfo *
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400356prepare_framedata(Video::Renderer *renderer, ClutterActor* image_actor)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500357{
358 const QByteArray& data = renderer->currentFrame();
359 QSize res = renderer->size();
360
361 /* copy frame data */
362 gpointer frame_data = g_memdup((gconstpointer)data.constData(), data.size());
363
364 FrameInfo *frame = g_new0(FrameInfo, 1);
365
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500366 frame->image_actor = image_actor;
367 frame->data = (guchar *)frame_data;
368 frame->data_size = data.size();
369 frame->width = res.width();
370 frame->height = res.height();
371
372 return frame;
373}
374
375static void
376free_framedata(gpointer data)
377{
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400378 if (data == NULL) return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500379 FrameInfo *frame = (FrameInfo *)data;
380 g_free(frame->data);
381 g_free(frame);
382}
383
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400384static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500385clutter_render_image(FrameInfo *frame)
386{
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400387 if (frame == NULL) return;
388 g_return_if_fail(CLUTTER_IS_ACTOR(frame->image_actor));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500389
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400390 ClutterContent * image_new = clutter_image_new();
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500391
392 const gint BPP = 4;
393 const gint ROW_STRIDE = BPP * frame->width;
394
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500395 GError *error = NULL;
396 clutter_image_set_data(
397 CLUTTER_IMAGE(image_new),
398 frame->data,
399 COGL_PIXEL_FORMAT_BGRA_8888,
400 frame->width,
401 frame->height,
402 ROW_STRIDE,
403 &error);
404 if (error) {
405 g_warning("error rendering image to clutter: %s", error->message);
406 g_error_free(error);
407 }
408
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400409 clutter_actor_set_content(frame->image_actor, image_new);
410 g_object_unref (image_new);
411 /* note: we must set the content gravity be "resize aspect" after setting the image data to make sure
412 * that the aspect ratio is correct
413 */
414 clutter_actor_set_content_gravity(frame->image_actor, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400415}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500416
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400417static gboolean
418check_frame_queue(VideoWidget *self)
419{
420 g_return_val_if_fail(IS_VIDEO_WIDGET(self), FALSE);
421 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
422
423 /* get the latest frame in the queue */
424 gpointer local_data_last = NULL;
425 gpointer local_data_next = g_async_queue_try_pop(priv->local->frame_queue);
426 while(local_data_next != NULL) {
427 // if (local_data_last != NULL) g_debug("skipping local frame");
428 /* make sure to free the frame we're skipping */
429 free_framedata(local_data_last);
430 local_data_last = local_data_next;
431 local_data_next = g_async_queue_try_pop(priv->local->frame_queue);
432 }
433
434 /* display the frame */
435 clutter_render_image((FrameInfo *)local_data_last);
436 free_framedata(local_data_last);
437
438 /* get the latest frame in the queue */
439 gpointer remote_data_last = NULL;
440 gpointer remote_data_next = g_async_queue_try_pop(priv->remote->frame_queue);
441 while(remote_data_next != NULL) {
442 // if (remote_data_last != NULL) g_debug("skipping remote frame");
443 /* make sure to free the frame we're skipping */
444 free_framedata(remote_data_last);
445 remote_data_last = remote_data_next;
446 remote_data_next = g_async_queue_try_pop(priv->remote->frame_queue);
447 }
448
449 /* display the frame */
450 clutter_render_image((FrameInfo *)remote_data_last);
451 free_framedata(remote_data_last);
452
453 return TRUE; /* keep going */
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500454}
455
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400456static void
457renderer_stop(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500458{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400459 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400460 QObject::disconnect(renderer->frame_update);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500461}
462
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400463static void
464renderer_start(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500465{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400466 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400467 QObject::disconnect(renderer->frame_update);
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400468 renderer->frame_update = QObject::connect(
469 renderer->renderer,
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500470 &Video::Renderer::frameUpdated,
471 [=]() {
Stepan Salenikovich57058802015-03-25 14:16:13 -0400472 if (!renderer->renderer->isRendering()) {
473 g_debug("got frame but not rendering");
474 return;
475 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500476
477 /* this callback comes from another thread;
478 * rendering must be done in the main loop;
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400479 * copy the frame and add it to the frame queue
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500480 */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400481 FrameInfo *frame = prepare_framedata(renderer->renderer,
482 renderer->actor);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400483 g_async_queue_push(renderer->frame_queue, frame);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500484 }
485 );
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500486}
487
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400488static void
489video_widget_set_renderer(VideoWidgetRenderer *renderer)
490{
491 if (renderer == NULL) return;
492
Stepan Salenikovich57058802015-03-25 14:16:13 -0400493 /* reset the content gravity so that the aspect ratio gets properly set if it chagnes */
494 clutter_actor_set_content_gravity(renderer->actor, CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
495
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400496 /* update the renderer */
497 QObject::disconnect(renderer->frame_update);
498 QObject::disconnect(renderer->render_stop);
499 QObject::disconnect(renderer->render_start);
500
Stepan Salenikovich57058802015-03-25 14:16:13 -0400501 if (renderer->renderer->isRendering())
502 renderer_start(renderer);
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400503
504 renderer->render_stop = QObject::connect(
505 renderer->renderer,
506 &Video::Renderer::stopped,
507 [=]() {
508 renderer_stop(renderer);
509 }
510 );
511
512 renderer->render_start = QObject::connect(
513 renderer->renderer,
514 &Video::Renderer::started,
515 [=]() {
516 renderer_start(renderer);
517 }
518 );
519}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500520
521/*
522 * video_widget_new()
523 *
524 * The function use to create a new video_widget
525 */
526GtkWidget*
527video_widget_new(void)
528{
529 GtkWidget *self = (GtkWidget *)g_object_new(VIDEO_WIDGET_TYPE, NULL);
530 return self;
531}
532
533void
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400534video_widget_add_renderer(VideoWidget *self, const VideoRenderer *new_renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500535{
536 g_return_if_fail(IS_VIDEO_WIDGET(self));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400537 if (new_renderer == NULL || new_renderer->renderer == NULL)
538 return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500539
540 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
541
542 /* update the renderer */
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400543 switch(new_renderer->type) {
544 case VIDEO_RENDERER_REMOTE:
545 priv->remote->renderer = new_renderer->renderer;
546 video_widget_set_renderer(priv->remote);
547 break;
548 case VIDEO_RENDERER_LOCAL:
549 priv->local->renderer = new_renderer->renderer;
550 video_widget_set_renderer(priv->local);
551 break;
552 case VIDEO_RENDERER_COUNT:
553 break;
554 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500555}