blob: 7fd71250bf859f77297b12aabc584bfc9d313a17 [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"
Guillaume Roguez24834e02015-04-09 12:45:40 -040040
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -040041#include <stdlib.h>
Guillaume Roguez24834e02015-04-09 12:45:40 -040042#include <atomic>
43
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -040044#include "xrectsel.h"
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050045
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040046#define VIDEO_LOCAL_SIZE 150
47#define VIDEO_LOCAL_OPACITY_DEFAULT 150 /* out of 255 */
48
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040049/* check video frame queues at this rate;
50 * use 30 ms (about 30 fps) since we don't expect to
51 * receive video frames faster than that */
52#define FRAME_RATE_PERIOD 30
53
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050054struct _VideoWidgetClass {
55 GtkBinClass parent_class;
56};
57
58struct _VideoWidget {
59 GtkBin parent;
60};
61
62typedef struct _VideoWidgetPrivate VideoWidgetPrivate;
63
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040064typedef struct _VideoWidgetRenderer VideoWidgetRenderer;
65
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050066struct _VideoWidgetPrivate {
67 GtkWidget *clutter_widget;
68 ClutterActor *stage;
69 ClutterActor *video_container;
70
71 /* remote peer data */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040072 VideoWidgetRenderer *remote;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050073
74 /* local peer data */
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040075 VideoWidgetRenderer *local;
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040076
77 guint frame_timeout_source;
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040078};
79
80struct _VideoWidgetRenderer {
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040081 ClutterActor *actor;
82 Video::Renderer *renderer;
Guillaume Roguez24834e02015-04-09 12:45:40 -040083 std::atomic_bool running;
84 std::atomic_bool dirty;
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040085 QMetaObject::Connection frame_update;
86 QMetaObject::Connection render_stop;
87 QMetaObject::Connection render_start;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050088};
89
90G_DEFINE_TYPE_WITH_PRIVATE(VideoWidget, video_widget, GTK_TYPE_BIN);
91
92#define VIDEO_WIDGET_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), VIDEO_WIDGET_TYPE, VideoWidgetPrivate))
93
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040094/* static prototypes */
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -040095static gboolean check_frame_queue(VideoWidget *self);
96static void renderer_stop(VideoWidgetRenderer *renderer);
97static void renderer_start(VideoWidgetRenderer *renderer);
98static void video_widget_set_renderer(VideoWidgetRenderer *renderer);
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -040099static void on_drag_data_received(GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint32, gpointer);
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400100static gboolean on_button_press_in_screen_event(GtkWidget *, GdkEventButton *, gpointer);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400101
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500102/*
103 * video_widget_dispose()
104 *
105 * The dispose function for the video_widget class.
106 */
107static void
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400108video_widget_dispose(GObject *object)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500109{
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400110 VideoWidget *self = VIDEO_WIDGET(object);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500111 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
112
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400113 QObject::disconnect(priv->remote->frame_update);
114 QObject::disconnect(priv->remote->render_stop);
115 QObject::disconnect(priv->remote->render_start);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500116
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400117 QObject::disconnect(priv->local->frame_update);
118 QObject::disconnect(priv->local->render_stop);
119 QObject::disconnect(priv->local->render_start);
120
Stepan Salenikovichbf74af82015-03-13 11:35:58 -0400121 /* dispose may be called multiple times, make sure
122 * not to call g_source_remove more than once */
123 if (priv->frame_timeout_source) {
124 g_source_remove(priv->frame_timeout_source);
125 priv->frame_timeout_source = 0;
126 }
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400127
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400128 G_OBJECT_CLASS(video_widget_parent_class)->dispose(object);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500129}
130
131
132/*
133 * video_widget_finalize()
134 *
135 * The finalize function for the video_widget class.
136 */
137static void
138video_widget_finalize(GObject *object)
139{
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400140 VideoWidget *self = VIDEO_WIDGET(object);
141 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
142
143 g_free(priv->remote);
144 g_free(priv->local);
145
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500146 G_OBJECT_CLASS(video_widget_parent_class)->finalize(object);
147}
148
149
150/*
151 * video_widget_class_init()
152 *
153 * This function init the video_widget_class.
154 */
155static void
156video_widget_class_init(VideoWidgetClass *klass)
157{
158 GObjectClass *object_class = G_OBJECT_CLASS(klass);
159
160 /* override method */
161 object_class->dispose = video_widget_dispose;
162 object_class->finalize = video_widget_finalize;
163}
164
165
166/*
167 * video_widget_init()
168 *
169 * This function init the video_widget.
170 * - init clutter
171 * - init all the widget members
172 */
173static void
174video_widget_init(VideoWidget *self)
175{
176 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
177
178 /* init clutter widget */
179 priv->clutter_widget = gtk_clutter_embed_new();
180 /* add it to the video_widget */
181 gtk_container_add(GTK_CONTAINER(self), priv->clutter_widget);
182 /* get the stage */
183 priv->stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(priv->clutter_widget));
184
185 /* layout manager is used to arrange children in space, here we ask clutter
186 * to align children to fill the space when resizing the window */
187 clutter_actor_set_layout_manager(priv->stage,
188 clutter_bin_layout_new(CLUTTER_BIN_ALIGNMENT_FILL, CLUTTER_BIN_ALIGNMENT_FILL));
189
190 /* add a scene container where we can add and remove our actors */
191 priv->video_container = clutter_actor_new();
192 clutter_actor_set_background_color(priv->video_container, CLUTTER_COLOR_Black);
193 clutter_actor_add_child(priv->stage, priv->video_container);
194
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400195 /* init the remote and local structs */
196 priv->remote = g_new0(VideoWidgetRenderer, 1);
197 priv->local = g_new0(VideoWidgetRenderer, 1);
198
199 /* arrange remote actors */
200 priv->remote->actor = clutter_actor_new();
201 clutter_actor_insert_child_below(priv->video_container, priv->remote->actor, NULL);
202 /* the remote camera must always fill the container size */
203 ClutterConstraint *constraint = clutter_bind_constraint_new(priv->video_container,
204 CLUTTER_BIND_SIZE, 0);
205 clutter_actor_add_constraint(priv->remote->actor, constraint);
206
207 /* arrange local actor */
208 priv->local->actor = clutter_actor_new();
209 clutter_actor_insert_child_above(priv->video_container, priv->local->actor, NULL);
210 /* set size to square, but it will stay the aspect ratio when the image is rendered */
211 clutter_actor_set_size(priv->local->actor, VIDEO_LOCAL_SIZE, VIDEO_LOCAL_SIZE);
212 /* set position constraint to right cornder */
213 constraint = clutter_align_constraint_new(priv->video_container,
214 CLUTTER_ALIGN_BOTH, 0.99);
215 clutter_actor_add_constraint(priv->local->actor, constraint);
216 clutter_actor_set_opacity(priv->local->actor,
217 VIDEO_LOCAL_OPACITY_DEFAULT);
218
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400219 /* init frame queues and the timeout sources to check them */
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400220 priv->frame_timeout_source = g_timeout_add(FRAME_RATE_PERIOD, (GSourceFunc)check_frame_queue, self);
221
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400222 /* handle button event */
223 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 -0500224
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400225 /* drag & drop files as video sources */
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400226 gtk_drag_dest_set(GTK_WIDGET(self), GTK_DEST_DEFAULT_ALL, NULL, 0, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_PRIVATE));
227 gtk_drag_dest_add_uri_targets(GTK_WIDGET(self));
228 g_signal_connect(GTK_WIDGET(self), "drag-data-received", G_CALLBACK(on_drag_data_received), NULL);
229}
230
231/*
232 * on_drag_data_received()
233 *
234 * Handle dragged data in the video widget window.
235 * Dropping an image causes the client to switch the video input to that image.
236 */
237static void
238on_drag_data_received(G_GNUC_UNUSED GtkWidget *self,
239 G_GNUC_UNUSED GdkDragContext *context,
240 G_GNUC_UNUSED gint x,
241 G_GNUC_UNUSED gint y,
242 GtkSelectionData *selection_data,
243 G_GNUC_UNUSED guint info,
244 G_GNUC_UNUSED guint32 time,
245 G_GNUC_UNUSED gpointer data)
246{
247 gchar **uris = gtk_selection_data_get_uris(selection_data);
248
249 /* only play the first selection */
250 if (uris && *uris)
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400251 Video::SourceModel::instance()->setFile(QUrl(*uris));
Stepan Salenikovichcb6aa462015-03-21 15:13:37 -0400252
253 g_strfreev(uris);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500254}
255
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400256static void
257switch_video_input(G_GNUC_UNUSED GtkWidget *widget, Video::Device *device)
258{
Stepan Salenikovichbb382632015-03-26 12:40:46 -0400259 Video::DeviceModel::instance()->setActive(device);
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400260 Video::SourceModel::instance()->switchTo(device);
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400261}
262
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400263static void
Stepan Salenikovichfb5ff0a2015-03-29 22:47:47 -0400264switch_video_input_screen(G_GNUC_UNUSED GtkWidget *item, G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400265{
266 unsigned x, y;
267 unsigned width, height;
268
269 /* try to get the dispaly or default to 0 */
270 QString display_env{getenv("DISPLAY")};
271 int display = 0;
272
273 if (!display_env.isEmpty()) {
274 auto list = display_env.split(":", QString::SkipEmptyParts);
275 /* should only be one display, so get the first one */
276 if (list.size() > 0) {
277 display = list.at(0).toInt();
278 g_debug("sharing screen from DISPLAY %d", display);
279 }
280 }
281
282 x = y = width = height = 0;
283
284 xrectsel(&x, &y, &width, &height);
285
286 if (!width || !height) {
287 x = y = 0;
288 width = gdk_screen_width();
289 height = gdk_screen_height();
290 }
291
Stepan Salenikovich033dc832015-03-23 15:56:47 -0400292 Video::SourceModel::instance()->setDisplay(display, QRect(x,y,width,height));
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400293}
294
Stepan Salenikovich763c25a2015-03-26 13:51:31 -0400295static void
Stepan Salenikovichfb5ff0a2015-03-29 22:47:47 -0400296switch_video_input_file(G_GNUC_UNUSED GtkWidget *item, GtkWidget *parent)
Stepan Salenikovich763c25a2015-03-26 13:51:31 -0400297{
298 if (parent && GTK_IS_WIDGET(parent)) {
299 /* get parent window */
300 parent = gtk_widget_get_toplevel(GTK_WIDGET(parent));
301 }
302
303 gchar *uri = NULL;
304 GtkWidget *dialog = gtk_file_chooser_dialog_new(
305 "Choose File",
306 GTK_WINDOW(parent),
307 GTK_FILE_CHOOSER_ACTION_OPEN,
308 "_Cancel", GTK_RESPONSE_CANCEL,
309 "_Open", GTK_RESPONSE_ACCEPT,
310 NULL);
311
312 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
313 uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
314 }
315
316 gtk_widget_destroy(dialog);
317
318 Video::SourceModel::instance()->setFile(QUrl(uri));
319
320 g_free(uri);
321}
322
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400323/*
324 * on_button_press_in_screen_event()
325 *
326 * Handle button event in the video screen.
327 */
328static gboolean
Stepan Salenikovich763c25a2015-03-26 13:51:31 -0400329on_button_press_in_screen_event(GtkWidget *parent,
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400330 GdkEventButton *event,
331 G_GNUC_UNUSED gpointer data)
332{
333 /* check for right click */
334 if (event->button != BUTTON_RIGHT_CLICK || event->type != GDK_BUTTON_PRESS)
335 return FALSE;
336
337 /* create menu with available video sources */
338 GtkWidget *menu = gtk_menu_new();
339
340 /* list available devices and check off the active device */
341 auto device_list = Video::DeviceModel::instance()->devices();
342
343 for( auto device: device_list) {
344 GtkWidget *item = gtk_check_menu_item_new_with_mnemonic(device->name().toLocal8Bit().constData());
345 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
346 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), device->isActive());
347 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input), device);
348 }
349
Stepan Salenikovichbb382632015-03-26 12:40:46 -0400350 /* add separator */
351 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
352
Stepan Salenikovich8bc51e52015-03-21 20:17:29 -0400353 /* add screen area as an input */
354 GtkWidget *item = gtk_check_menu_item_new_with_mnemonic("Share screen area");
355 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
356 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input_screen), NULL);
357
Stepan Salenikovich763c25a2015-03-26 13:51:31 -0400358 /* add file as an input */
359 item = gtk_check_menu_item_new_with_mnemonic("Share file");
360 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
361 g_signal_connect(item, "activate", G_CALLBACK(switch_video_input_file), parent);
362
Stepan Salenikovich50c989b2015-03-21 18:32:46 -0400363 /* show menu */
364 gtk_widget_show_all(menu);
365 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
366
367 return TRUE; /* event has been fully handled */
368}
369
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500370static void
Guillaume Roguez24834e02015-04-09 12:45:40 -0400371clutter_render_image(VideoWidgetRenderer* wg_renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500372{
Guillaume Roguez24834e02015-04-09 12:45:40 -0400373 if (!wg_renderer->running)
374 return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500375
Guillaume Roguez24834e02015-04-09 12:45:40 -0400376 auto renderer = wg_renderer->renderer;
377 if (renderer == nullptr)
378 return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500379
Guillaume Roguez24834e02015-04-09 12:45:40 -0400380 auto actor = wg_renderer->actor;
381 g_return_if_fail(CLUTTER_IS_ACTOR(actor));
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500382
Guillaume Roguez24834e02015-04-09 12:45:40 -0400383 const auto frameData = (const guint8*)renderer->currentFrame().constData();
384 if (!frameData or !wg_renderer->dirty)
385 return;
386
387 wg_renderer->dirty = false;
388
389 auto image_new = clutter_image_new();
390 g_return_if_fail(image_new);
391
392 const auto& res = renderer->size();
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500393 const gint BPP = 4;
Guillaume Roguez24834e02015-04-09 12:45:40 -0400394 const gint ROW_STRIDE = BPP * res.width();
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500395
Guillaume Roguez24834e02015-04-09 12:45:40 -0400396 GError *error = nullptr;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500397 clutter_image_set_data(
Guillaume Roguez24834e02015-04-09 12:45:40 -0400398 CLUTTER_IMAGE(image_new),
399 frameData,
400 COGL_PIXEL_FORMAT_BGRA_8888,
401 res.width(),
402 res.height(),
403 ROW_STRIDE,
404 &error);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500405 if (error) {
406 g_warning("error rendering image to clutter: %s", error->message);
407 g_error_free(error);
Guillaume Roguez24834e02015-04-09 12:45:40 -0400408 g_object_unref (image_new);
409 return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500410 }
411
Guillaume Roguez24834e02015-04-09 12:45:40 -0400412 clutter_actor_set_content(actor, image_new);
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400413 g_object_unref (image_new);
Guillaume Roguez24834e02015-04-09 12:45:40 -0400414
Stepan Salenikovichcd1bf632015-03-09 16:24:08 -0400415 /* note: we must set the content gravity be "resize aspect" after setting the image data to make sure
416 * that the aspect ratio is correct
417 */
Guillaume Roguez24834e02015-04-09 12:45:40 -0400418 clutter_actor_set_content_gravity(actor, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400419}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500420
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400421static gboolean
422check_frame_queue(VideoWidget *self)
423{
424 g_return_val_if_fail(IS_VIDEO_WIDGET(self), FALSE);
425 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
426
Guillaume Roguez24834e02015-04-09 12:45:40 -0400427 /* display renderer's frames */
428 clutter_render_image(priv->local);
429 clutter_render_image(priv->remote);
Stepan Salenikovich5e6a0b72015-03-12 14:55:22 -0400430
431 return TRUE; /* keep going */
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500432}
433
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400434static void
435renderer_stop(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500436{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400437 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400438 QObject::disconnect(renderer->frame_update);
Guillaume Roguez24834e02015-04-09 12:45:40 -0400439 renderer->running = false;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500440}
441
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400442static void
443renderer_start(VideoWidgetRenderer *renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500444{
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400445 g_return_if_fail(CLUTTER_IS_ACTOR(renderer->actor));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400446 QObject::disconnect(renderer->frame_update);
Guillaume Roguez24834e02015-04-09 12:45:40 -0400447
448 renderer->running = true;
449
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400450 renderer->frame_update = QObject::connect(
451 renderer->renderer,
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500452 &Video::Renderer::frameUpdated,
Guillaume Roguez24834e02015-04-09 12:45:40 -0400453 [renderer]() {
454 // WARNING: this lambda is called in LRC renderer thread,
455 // but check_frame_queue() is in mainloop!
456 renderer->dirty = true;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500457 }
Guillaume Roguez24834e02015-04-09 12:45:40 -0400458 );
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500459}
460
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400461static void
462video_widget_set_renderer(VideoWidgetRenderer *renderer)
463{
Guillaume Roguez24834e02015-04-09 12:45:40 -0400464 if (renderer == nullptr) return;
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400465
Stepan Salenikovich57058802015-03-25 14:16:13 -0400466 /* reset the content gravity so that the aspect ratio gets properly set if it chagnes */
467 clutter_actor_set_content_gravity(renderer->actor, CLUTTER_CONTENT_GRAVITY_RESIZE_FILL);
468
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400469 /* update the renderer */
470 QObject::disconnect(renderer->frame_update);
471 QObject::disconnect(renderer->render_stop);
472 QObject::disconnect(renderer->render_start);
473
Stepan Salenikovich57058802015-03-25 14:16:13 -0400474 if (renderer->renderer->isRendering())
475 renderer_start(renderer);
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400476
477 renderer->render_stop = QObject::connect(
478 renderer->renderer,
479 &Video::Renderer::stopped,
480 [=]() {
481 renderer_stop(renderer);
482 }
483 );
484
485 renderer->render_start = QObject::connect(
486 renderer->renderer,
487 &Video::Renderer::started,
488 [=]() {
489 renderer_start(renderer);
490 }
491 );
492}
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500493
494/*
495 * video_widget_new()
496 *
497 * The function use to create a new video_widget
498 */
499GtkWidget*
500video_widget_new(void)
501{
502 GtkWidget *self = (GtkWidget *)g_object_new(VIDEO_WIDGET_TYPE, NULL);
503 return self;
504}
505
506void
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400507video_widget_add_renderer(VideoWidget *self, const VideoRenderer *new_renderer)
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500508{
509 g_return_if_fail(IS_VIDEO_WIDGET(self));
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400510 if (new_renderer == NULL || new_renderer->renderer == NULL)
511 return;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500512
513 VideoWidgetPrivate *priv = VIDEO_WIDGET_GET_PRIVATE(self);
514
515 /* update the renderer */
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400516 switch(new_renderer->type) {
517 case VIDEO_RENDERER_REMOTE:
518 priv->remote->renderer = new_renderer->renderer;
519 video_widget_set_renderer(priv->remote);
520 break;
521 case VIDEO_RENDERER_LOCAL:
522 priv->local->renderer = new_renderer->renderer;
523 video_widget_set_renderer(priv->local);
524 break;
525 case VIDEO_RENDERER_COUNT:
526 break;
527 }
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500528}