blob: 3ed76f0a30303dcc036b40ee2b64b42391d1e45b [file] [log] [blame]
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001/*
2 * Copyright (C) 2004-2015 Savoir-Faire Linux Inc.
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
21 * If you modify this program, or any covered work, by linking or
22 * combining it with the OpenSSL project's OpenSSL library (or a
23 * modified version of that library), containing parts covered by the
24 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25 * grants you additional permission to convey the resulting work.
26 * Corresponding Source for a non-source form of such a combination
27 * shall include the source code for the parts of OpenSSL used as well
28 * as that of the covered work.
29 */
30
31#include "video_widget.h"
32
33#include <clutter/clutter.h>
34#include <clutter-gtk/clutter-gtk.h>
35#include <video/renderer.h>
Stepan Salenikovich033dc832015-03-23 15:56:47 -040036#include <video/sourcemodel.h>
Stepan Salenikovich50c989b2015-03-21 18:32:46 -040037#include <video/devicemodel.h>
38#include <QtCore/QUrl>
39#include "../defines.h"
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -040040#include <stdlib.h>
41#include "xrectsel.h"
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050042
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040043#define VIDEO_LOCAL_SIZE 150
44#define VIDEO_LOCAL_OPACITY_DEFAULT 150 /* out of 255 */
45
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040046/* check video frame queues at this rate;
47 * use 30 ms (about 30 fps) since we don't expect to
48 * receive video frames faster than that */
49#define FRAME_RATE_PERIOD 30
50
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050051struct _VideoWidgetClass {
52 GtkBinClass parent_class;
53};
54
55struct _VideoWidget {
56 GtkBin parent;
57};
58
59typedef struct _VideoWidgetPrivate VideoWidgetPrivate;
60
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040061typedef struct _VideoWidgetRenderer VideoWidgetRenderer;
62
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050063struct _VideoWidgetPrivate {
64 GtkWidget *clutter_widget;
65 ClutterActor *stage;
66 ClutterActor *video_container;
67
68 /* remote peer data */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040069 VideoWidgetRenderer *remote;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050070
71 /* local peer data */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040072 VideoWidgetRenderer *local;
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040073
74 guint frame_timeout_source;
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040075};
76
77struct _VideoWidgetRenderer {
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040078 GAsyncQueue *frame_queue;
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040079 ClutterActor *actor;
80 Video::Renderer *renderer;
81 QMetaObject::Connection frame_update;
82 QMetaObject::Connection render_stop;
83 QMetaObject::Connection render_start;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050084};
85
86G_DEFINE_TYPE_WITH_PRIVATE(VideoWidget, video_widget, GTK_TYPE_BIN);
87
88#define VIDEO_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), VIDEO_WIDGET_TYPE, VideoWidgetPrivate))
89
90typedef struct _FrameInfo
91{
92 ClutterActor *image_actor;
93 ClutterContent *image;
94 guchar *data;
95 gint data_size;
96 gint width;
97 gint height;
98} FrameInfo;
99
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400100/* static prototypes */
101static FrameInfo *prepare_framedata(Video::Renderer *renderer, ClutterActor* image_actor);
102static void free_framedata(gpointer data);
103static void clutter_render_image(FrameInfo *frame);
104static gboolean check_frame_queue(VideoWidget *self);
105static void renderer_stop(VideoWidgetRenderer *renderer);
106static void renderer_start(VideoWidgetRenderer *renderer);
107static void video_widget_set_renderer(VideoWidgetRenderer *renderer);
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400108static void on_drag_data_received(GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint32, gpointer);
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400109static gboolean on_button_press_in_screen_event(GtkWidget *, GdkEventButton *, gpointer);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400110
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500111/*
112 * video_widget_dispose()
113 *
114 * The dispose function for the video_widget class.
115 */
116static void
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400117video_widget_dispose(GObject *object)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500118{
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400119 VideoWidget *self = VIDEO_WIDGET(object);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500120 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
121
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400122 QObject::disconnect(priv->remote->frame_update);
123 QObject::disconnect(priv->remote->render_stop);
124 QObject::disconnect(priv->remote->render_start);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500125
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400126 QObject::disconnect(priv->local->frame_update);
127 QObject::disconnect(priv->local->render_stop);
128 QObject::disconnect(priv->local->render_start);
129
Stepan Salenikovichbf74af82015-03-13 11:35:58 -0400130 /* dispose may be called multiple times, make sure
131 * not to call g_source_remove more than once */
132 if (priv->frame_timeout_source) {
133 g_source_remove(priv->frame_timeout_source);
134 priv->frame_timeout_source = 0;
135 }
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400136
Stepan Salenikovichbf74af82015-03-13 11:35:58 -0400137 if (priv->remote->frame_queue) {
138 g_async_queue_unref(priv->remote->frame_queue);
139 priv->remote->frame_queue = NULL;
140 }
141
142 if (priv->local->frame_queue) {
143 g_async_queue_unref(priv->local->frame_queue);
144 priv->local->frame_queue = NULL;
145 }
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400146
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400147 G_OBJECT_CLASS(video_widget_parent_class)->dispose(object);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500148}
149
150
151/*
152 * video_widget_finalize()
153 *
154 * The finalize function for the video_widget class.
155 */
156static void
157video_widget_finalize(GObject *object)
158{
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400159 VideoWidget *self = VIDEO_WIDGET(object);
160 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
161
162 g_free(priv->remote);
163 g_free(priv->local);
164
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500165 G_OBJECT_CLASS(video_widget_parent_class)->finalize(object);
166}
167
168
169/*
170 * video_widget_class_init()
171 *
172 * This function init the video_widget_class.
173 */
174static void
175video_widget_class_init(VideoWidgetClass *klass)
176{
177 GObjectClass *object_class = G_OBJECT_CLASS(klass);
178
179 /* override method */
180 object_class->dispose = video_widget_dispose;
181 object_class->finalize = video_widget_finalize;
182}
183
184
185/*
186 * video_widget_init()
187 *
188 * This function init the video_widget.
189 * - init clutter
190 * - init all the widget members
191 */
192static void
193video_widget_init(VideoWidget *self)
194{
195 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
196
197 /* init clutter widget */
198 priv->clutter_widget = gtk_clutter_embed_new();
199 /* add it to the video_widget */
200 gtk_container_add(GTK_CONTAINER(self), priv->clutter_widget);
201 /* get the stage */
202 priv->stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(priv->clutter_widget));
203
204 /* layout manager is used to arrange children in space, here we ask clutter
205 * to align children to fill the space when resizing the window */
206 clutter_actor_set_layout_manager(priv->stage,
207 clutter_bin_layout_new(CLUTTER_BIN_ALIGNMENT_FILL, CLUTTER_BIN_ALIGNMENT_FILL));
208
209 /* add a scene container where we can add and remove our actors */
210 priv->video_container = clutter_actor_new();
211 clutter_actor_set_background_color(priv->video_container, CLUTTER_COLOR_Black);
212 clutter_actor_add_child(priv->stage, priv->video_container);
213
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400214 /* init the remote and local structs */
215 priv->remote = g_new0(VideoWidgetRenderer, 1);
216 priv->local = g_new0(VideoWidgetRenderer, 1);
217
218 /* arrange remote actors */
219 priv->remote->actor = clutter_actor_new();
220 clutter_actor_insert_child_below(priv->video_container, priv->remote->actor, NULL);
221 /* the remote camera must always fill the container size */
222 ClutterConstraint *constraint = clutter_bind_constraint_new(priv->video_container,
223 CLUTTER_BIND_SIZE, 0);
224 clutter_actor_add_constraint(priv->remote->actor, constraint);
225
226 /* arrange local actor */
227 priv->local->actor = clutter_actor_new();
228 clutter_actor_insert_child_above(priv->video_container, priv->local->actor, NULL);
229 /* set size to square, but it will stay the aspect ratio when the image is rendered */
230 clutter_actor_set_size(priv->local->actor, VIDEO_LOCAL_SIZE, VIDEO_LOCAL_SIZE);
231 /* set position constraint to right cornder */
232 constraint = clutter_align_constraint_new(priv->video_container,
233 CLUTTER_ALIGN_BOTH, 0.99);
234 clutter_actor_add_constraint(priv->local->actor, constraint);
235 clutter_actor_set_opacity(priv->local->actor,
236 VIDEO_LOCAL_OPACITY_DEFAULT);
237
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400238 /* init frame queues and the timeout sources to check them */
239 priv->remote->frame_queue = g_async_queue_new_full((GDestroyNotify)free_framedata);
240 priv->local->frame_queue = g_async_queue_new_full((GDestroyNotify)free_framedata);
241 priv->frame_timeout_source = g_timeout_add(FRAME_RATE_PERIOD, (GSourceFunc)check_frame_queue, self);
242
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400243 /* handle button event */
244 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 -0500245
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400246 /* drag & drop files as video sources */
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400247 gtk_drag_dest_set(GTK_WIDGET(self), GTK_DEST_DEFAULT_ALL, NULL, 0, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_PRIVATE));
248 gtk_drag_dest_add_uri_targets(GTK_WIDGET(self));
249 g_signal_connect(GTK_WIDGET(self), "drag-data-received", G_CALLBACK(on_drag_data_received), NULL);
250}
251
252/*
253 * on_drag_data_received()
254 *
255 * Handle dragged data in the video widget window.
256 * Dropping an image causes the client to switch the video input to that image.
257 */
258static void
259on_drag_data_received(G_GNUC_UNUSED GtkWidget *self,
260 G_GNUC_UNUSED GdkDragContext *context,
261 G_GNUC_UNUSED gint x,
262 G_GNUC_UNUSED gint y,
263 GtkSelectionData *selection_data,
264 G_GNUC_UNUSED guint info,
265 G_GNUC_UNUSED guint32 time,
266 G_GNUC_UNUSED gpointer data)
267{
268 gchar **uris = gtk_selection_data_get_uris(selection_data);
269
270 /* only play the first selection */
271 if (uris && *uris)
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400272 Video::SourceModel::instance()->setFile(QUrl(*uris));
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400273
274 g_strfreev(uris);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500275}
276
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400277static void
278switch_video_input(G_GNUC_UNUSED GtkWidget *widget, Video::Device *device)
279{
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400280 Video::SourceModel::instance()->switchTo(device);
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400281}
282
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400283static void
284switch_video_input_screen(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED gpointer user_data)
285{
286 unsigned x, y;
287 unsigned width, height;
288
289 /* try to get the dispaly or default to 0 */
290 QString display_env{getenv("DISPLAY")};
291 int display = 0;
292
293 if (!display_env.isEmpty()) {
294 auto list = display_env.split(":", QString::SkipEmptyParts);
295 /* should only be one display, so get the first one */
296 if (list.size() > 0) {
297 display = list.at(0).toInt();
298 g_debug("sharing screen from DISPLAY %d", display);
299 }
300 }
301
302 x = y = width = height = 0;
303
304 xrectsel(&x, &y, &width, &height);
305
306 if (!width || !height) {
307 x = y = 0;
308 width = gdk_screen_width();
309 height = gdk_screen_height();
310 }
311
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400312 Video::SourceModel::instance()->setDisplay(display, QRect(x,y,width,height));
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400313}
314
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400315/*
316 * on_button_press_in_screen_event()
317 *
318 * Handle button event in the video screen.
319 */
320static gboolean
321on_button_press_in_screen_event(G_GNUC_UNUSED GtkWidget *widget,
322 GdkEventButton *event,
323 G_GNUC_UNUSED gpointer data)
324{
325 /* check for right click */
326 if (event->button != BUTTON_RIGHT_CLICK || event->type != GDK_BUTTON_PRESS)
327 return FALSE;
328
329 /* create menu with available video sources */
330 GtkWidget *menu = gtk_menu_new();
331
332 /* list available devices and check off the active device */
333 auto device_list = Video::DeviceModel::instance()->devices();
334
335 for( auto device: device_list) {
336 GtkWidget *item = gtk_check_menu_item_new_with_mnemonic(device->name().toLocal8Bit().constData());
337 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
338 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), device->isActive());
339 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input), device);
340 }
341
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400342 /* add screen area as an input */
343 GtkWidget *item = gtk_check_menu_item_new_with_mnemonic("Share screen area");
344 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
345 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input_screen), NULL);
346
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400347 /* show menu */
348 gtk_widget_show_all(menu);
349 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
350
351 return TRUE; /* event has been fully handled */
352}
353
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500354static FrameInfo *
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400355prepare_framedata(Video::Renderer *renderer, ClutterActor* image_actor)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500356{
357 const QByteArray& data = renderer->currentFrame();
358 QSize res = renderer->size();
359
360 /* copy frame data */
361 gpointer frame_data = g_memdup((gconstpointer)data.constData(), data.size());
362
363 FrameInfo *frame = g_new0(FrameInfo, 1);
364
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500365 frame->image_actor = image_actor;
366 frame->data = (guchar *)frame_data;
367 frame->data_size = data.size();
368 frame->width = res.width();
369 frame->height = res.height();
370
371 return frame;
372}
373
374static void
375free_framedata(gpointer data)
376{
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400377 if (data == NULL) return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500378 FrameInfo *frame = (FrameInfo *)data;
379 g_free(frame->data);
380 g_free(frame);
381}
382
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400383static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500384clutter_render_image(FrameInfo *frame)
385{
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400386 if (frame == NULL) return;
387 g_return_if_fail(CLUTTER_IS_ACTOR(frame->image_actor));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500388
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400389 ClutterContent * image_new = clutter_image_new();
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500390
391 const gint BPP = 4;
392 const gint ROW_STRIDE = BPP * frame->width;
393
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500394 GError *error = NULL;
395 clutter_image_set_data(
396 CLUTTER_IMAGE(image_new),
397 frame->data,
398 COGL_PIXEL_FORMAT_BGRA_8888,
399 frame->width,
400 frame->height,
401 ROW_STRIDE,
402 &error);
403 if (error) {
404 g_warning("error rendering image to clutter: %s", error->message);
405 g_error_free(error);
406 }
407
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400408 clutter_actor_set_content(frame->image_actor, image_new);
409 g_object_unref (image_new);
410 /* note: we must set the content gravity be "resize aspect" after setting the image data to make sure
411 * that the aspect ratio is correct
412 */
413 clutter_actor_set_content_gravity(frame->image_actor, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400414}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500415
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400416static gboolean
417check_frame_queue(VideoWidget *self)
418{
419 g_return_val_if_fail(IS_VIDEO_WIDGET(self), FALSE);
420 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
421
422 /* get the latest frame in the queue */
423 gpointer local_data_last = NULL;
424 gpointer local_data_next = g_async_queue_try_pop(priv->local->frame_queue);
425 while(local_data_next != NULL) {
426 // if (local_data_last != NULL) g_debug("skipping local frame");
427 /* make sure to free the frame we're skipping */
428 free_framedata(local_data_last);
429 local_data_last = local_data_next;
430 local_data_next = g_async_queue_try_pop(priv->local->frame_queue);
431 }
432
433 /* display the frame */
434 clutter_render_image((FrameInfo *)local_data_last);
435 free_framedata(local_data_last);
436
437 /* get the latest frame in the queue */
438 gpointer remote_data_last = NULL;
439 gpointer remote_data_next = g_async_queue_try_pop(priv->remote->frame_queue);
440 while(remote_data_next != NULL) {
441 // if (remote_data_last != NULL) g_debug("skipping remote frame");
442 /* make sure to free the frame we're skipping */
443 free_framedata(remote_data_last);
444 remote_data_last = remote_data_next;
445 remote_data_next = g_async_queue_try_pop(priv->remote->frame_queue);
446 }
447
448 /* display the frame */
449 clutter_render_image((FrameInfo *)remote_data_last);
450 free_framedata(remote_data_last);
451
452 return TRUE; /* keep going */
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500453}
454
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400455static void
456renderer_stop(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500457{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400458 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400459 QObject::disconnect(renderer->frame_update);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500460}
461
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400462static void
463renderer_start(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500464{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400465 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400466 QObject::disconnect(renderer->frame_update);
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400467 renderer->frame_update = QObject::connect(
468 renderer->renderer,
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500469 &Video::Renderer::frameUpdated,
470 [=]() {
Stepan Salenikovich57058802015-03-25 14:16:13 -0400471 if (!renderer->renderer->isRendering()) {
472 g_debug("got frame but not rendering");
473 return;
474 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500475
476 /* this callback comes from another thread;
477 * rendering must be done in the main loop;
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400478 * copy the frame and add it to the frame queue
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500479 */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400480 FrameInfo *frame = prepare_framedata(renderer->renderer,
481 renderer->actor);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400482 g_async_queue_push(renderer->frame_queue, frame);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500483 }
484 );
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500485}
486
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400487static void
488video_widget_set_renderer(VideoWidgetRenderer *renderer)
489{
490 if (renderer == NULL) return;
491
Stepan Salenikovich57058802015-03-25 14:16:13 -0400492 /* reset the content gravity so that the aspect ratio gets properly set if it chagnes */
493 clutter_actor_set_content_gravity(renderer->actor, CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
494
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400495 /* update the renderer */
496 QObject::disconnect(renderer->frame_update);
497 QObject::disconnect(renderer->render_stop);
498 QObject::disconnect(renderer->render_start);
499
Stepan Salenikovich57058802015-03-25 14:16:13 -0400500 if (renderer->renderer->isRendering())
501 renderer_start(renderer);
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400502
503 renderer->render_stop = QObject::connect(
504 renderer->renderer,
505 &Video::Renderer::stopped,
506 [=]() {
507 renderer_stop(renderer);
508 }
509 );
510
511 renderer->render_start = QObject::connect(
512 renderer->renderer,
513 &Video::Renderer::started,
514 [=]() {
515 renderer_start(renderer);
516 }
517 );
518}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500519
520/*
521 * video_widget_new()
522 *
523 * The function use to create a new video_widget
524 */
525GtkWidget*
526video_widget_new(void)
527{
528 GtkWidget *self = (GtkWidget *)g_object_new(VIDEO_WIDGET_TYPE, NULL);
529 return self;
530}
531
532void
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400533video_widget_add_renderer(VideoWidget *self, const VideoRenderer *new_renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500534{
535 g_return_if_fail(IS_VIDEO_WIDGET(self));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400536 if (new_renderer == NULL || new_renderer->renderer == NULL)
537 return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500538
539 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
540
541 /* update the renderer */
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400542 switch(new_renderer->type) {
543 case VIDEO_RENDERER_REMOTE:
544 priv->remote->renderer = new_renderer->renderer;
545 video_widget_set_renderer(priv->remote);
546 break;
547 case VIDEO_RENDERER_LOCAL:
548 priv->local->renderer = new_renderer->renderer;
549 video_widget_set_renderer(priv->local);
550 break;
551 case VIDEO_RENDERER_COUNT:
552 break;
553 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500554}