blob: 5499161464d0843ffb2a9c8628a6796b91c8fabf [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 Salenikovichbb382632015-03-26 12:40:46 -0400280 Video::DeviceModel::instance()->setActive(device);
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 Salenikovichbb382632015-03-26 12:40:46 -0400343 /* add separator */
344 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
345
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400346 /* add screen area as an input */
347 GtkWidget *item = gtk_check_menu_item_new_with_mnemonic("Share screen area");
348 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
349 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input_screen), NULL);
350
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400351 /* show menu */
352 gtk_widget_show_all(menu);
353 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
354
355 return TRUE; /* event has been fully handled */
356}
357
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500358static FrameInfo *
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400359prepare_framedata(Video::Renderer *renderer, ClutterActor* image_actor)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500360{
361 const QByteArray& data = renderer->currentFrame();
362 QSize res = renderer->size();
363
364 /* copy frame data */
365 gpointer frame_data = g_memdup((gconstpointer)data.constData(), data.size());
366
367 FrameInfo *frame = g_new0(FrameInfo, 1);
368
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500369 frame->image_actor = image_actor;
370 frame->data = (guchar *)frame_data;
371 frame->data_size = data.size();
372 frame->width = res.width();
373 frame->height = res.height();
374
375 return frame;
376}
377
378static void
379free_framedata(gpointer data)
380{
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400381 if (data == NULL) return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500382 FrameInfo *frame = (FrameInfo *)data;
383 g_free(frame->data);
384 g_free(frame);
385}
386
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400387static void
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500388clutter_render_image(FrameInfo *frame)
389{
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400390 if (frame == NULL) return;
391 g_return_if_fail(CLUTTER_IS_ACTOR(frame->image_actor));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500392
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400393 ClutterContent * image_new = clutter_image_new();
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500394
395 const gint BPP = 4;
396 const gint ROW_STRIDE = BPP * frame->width;
397
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500398 GError *error = NULL;
399 clutter_image_set_data(
400 CLUTTER_IMAGE(image_new),
401 frame->data,
402 COGL_PIXEL_FORMAT_BGRA_8888,
403 frame->width,
404 frame->height,
405 ROW_STRIDE,
406 &error);
407 if (error) {
408 g_warning("error rendering image to clutter: %s", error->message);
409 g_error_free(error);
410 }
411
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400412 clutter_actor_set_content(frame->image_actor, image_new);
413 g_object_unref (image_new);
414 /* note: we must set the content gravity be "resize aspect" after setting the image data to make sure
415 * that the aspect ratio is correct
416 */
417 clutter_actor_set_content_gravity(frame->image_actor, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400418}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500419
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400420static gboolean
421check_frame_queue(VideoWidget *self)
422{
423 g_return_val_if_fail(IS_VIDEO_WIDGET(self), FALSE);
424 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
425
426 /* get the latest frame in the queue */
427 gpointer local_data_last = NULL;
428 gpointer local_data_next = g_async_queue_try_pop(priv->local->frame_queue);
429 while(local_data_next != NULL) {
430 // if (local_data_last != NULL) g_debug("skipping local frame");
431 /* make sure to free the frame we're skipping */
432 free_framedata(local_data_last);
433 local_data_last = local_data_next;
434 local_data_next = g_async_queue_try_pop(priv->local->frame_queue);
435 }
436
437 /* display the frame */
438 clutter_render_image((FrameInfo *)local_data_last);
439 free_framedata(local_data_last);
440
441 /* get the latest frame in the queue */
442 gpointer remote_data_last = NULL;
443 gpointer remote_data_next = g_async_queue_try_pop(priv->remote->frame_queue);
444 while(remote_data_next != NULL) {
445 // if (remote_data_last != NULL) g_debug("skipping remote frame");
446 /* make sure to free the frame we're skipping */
447 free_framedata(remote_data_last);
448 remote_data_last = remote_data_next;
449 remote_data_next = g_async_queue_try_pop(priv->remote->frame_queue);
450 }
451
452 /* display the frame */
453 clutter_render_image((FrameInfo *)remote_data_last);
454 free_framedata(remote_data_last);
455
456 return TRUE; /* keep going */
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500457}
458
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400459static void
460renderer_stop(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500461{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400462 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400463 QObject::disconnect(renderer->frame_update);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500464}
465
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400466static void
467renderer_start(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500468{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400469 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400470 QObject::disconnect(renderer->frame_update);
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400471 renderer->frame_update = QObject::connect(
472 renderer->renderer,
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500473 &Video::Renderer::frameUpdated,
474 [=]() {
Stepan Salenikovich57058802015-03-25 14:16:13 -0400475 if (!renderer->renderer->isRendering()) {
476 g_debug("got frame but not rendering");
477 return;
478 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500479
480 /* this callback comes from another thread;
481 * rendering must be done in the main loop;
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400482 * copy the frame and add it to the frame queue
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500483 */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400484 FrameInfo *frame = prepare_framedata(renderer->renderer,
485 renderer->actor);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400486 g_async_queue_push(renderer->frame_queue, frame);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500487 }
488 );
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500489}
490
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400491static void
492video_widget_set_renderer(VideoWidgetRenderer *renderer)
493{
494 if (renderer == NULL) return;
495
Stepan Salenikovich57058802015-03-25 14:16:13 -0400496 /* reset the content gravity so that the aspect ratio gets properly set if it chagnes */
497 clutter_actor_set_content_gravity(renderer->actor, CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
498
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400499 /* update the renderer */
500 QObject::disconnect(renderer->frame_update);
501 QObject::disconnect(renderer->render_stop);
502 QObject::disconnect(renderer->render_start);
503
Stepan Salenikovich57058802015-03-25 14:16:13 -0400504 if (renderer->renderer->isRendering())
505 renderer_start(renderer);
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400506
507 renderer->render_stop = QObject::connect(
508 renderer->renderer,
509 &Video::Renderer::stopped,
510 [=]() {
511 renderer_stop(renderer);
512 }
513 );
514
515 renderer->render_start = QObject::connect(
516 renderer->renderer,
517 &Video::Renderer::started,
518 [=]() {
519 renderer_start(renderer);
520 }
521 );
522}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500523
524/*
525 * video_widget_new()
526 *
527 * The function use to create a new video_widget
528 */
529GtkWidget*
530video_widget_new(void)
531{
532 GtkWidget *self = (GtkWidget *)g_object_new(VIDEO_WIDGET_TYPE, NULL);
533 return self;
534}
535
536void
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400537video_widget_add_renderer(VideoWidget *self, const VideoRenderer *new_renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500538{
539 g_return_if_fail(IS_VIDEO_WIDGET(self));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400540 if (new_renderer == NULL || new_renderer->renderer == NULL)
541 return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500542
543 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
544
545 /* update the renderer */
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400546 switch(new_renderer->type) {
547 case VIDEO_RENDERER_REMOTE:
548 priv->remote->renderer = new_renderer->renderer;
549 video_widget_set_renderer(priv->remote);
550 break;
551 case VIDEO_RENDERER_LOCAL:
552 priv->local->renderer = new_renderer->renderer;
553 video_widget_set_renderer(priv->local);
554 break;
555 case VIDEO_RENDERER_COUNT:
556 break;
557 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500558}