1 /*
2 * Copyright (c) 2014-2021 gnome-mpv
3 *
4 * This file is part of Celluloid.
5 *
6 * Celluloid 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 * Celluloid 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 Celluloid. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <gio/gio.h>
21 #include <gtk/gtk.h>
22 #include <stdio.h>
23 #include <locale.h>
24 #include <unistd.h>
25 #include <glib-object.h>
26 #include <glib/gi18n.h>
27 #include <gdk/gdk.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <epoxy/gl.h>
32 #ifdef GDK_WINDOWING_X11
33 #include <gdk/x11/gdkx.h>
34 #include <epoxy/glx.h>
35 #endif
36 #ifdef GDK_WINDOWING_WAYLAND
37 #include <gdk/wayland/gdkwayland.h>
38 #include <epoxy/egl.h>
39 #endif
40 #ifdef GDK_WINDOWING_WIN32
41 #include <gdk/gdkwin32.h>
42 #include <epoxy/wgl.h>
43 #endif
44
45 #include "celluloid-mpv.h"
46 #include "celluloid-common.h"
47 #include "celluloid-def.h"
48 #include "celluloid-marshal.h"
49
50 #define get_private(mpv) \
51 ((CelluloidMpvPrivate *)celluloid_mpv_get_instance_private(mpv))
52
53 typedef struct _CelluloidMpvPrivate CelluloidMpvPrivate;
54
55 enum
56 {
57 PROP_0,
58 PROP_WID,
59 PROP_READY,
60 N_PROPERTIES
61 };
62
63 struct _CelluloidMpvPrivate
64 {
65 mpv_handle *mpv_ctx;
66 mpv_render_context *render_ctx;
67 gboolean ready;
68 gchar *tmp_input_file;
69 GSList *log_level_list;
70 gboolean init_vo_config;
71 gboolean force_opengl;
72 gboolean use_opengl;
73 gint64 wid;
74 void *render_update_callback_data;
75 void (*render_update_callback)(void *data);
76 };
77
78 static void *
79 get_proc_address(void *fn_ctx, const gchar *name);
80
81 static void
82 set_property( GObject *object,
83 guint property_id,
84 const GValue *value,
85 GParamSpec *pspec );
86
87 static void
88 get_property( GObject *object,
89 guint property_id,
90 GValue *value,
91 GParamSpec *pspec );
92
93 static
94 void dispose(GObject *object);
95
96 static
97 void finalize(GObject *object);
98
99 static
100 void wakeup_callback(void *data);
101
102 static void
103 mpv_property_changed(CelluloidMpv *mpv, const gchar *name, gpointer value);
104
105 static void
106 mpv_log_message( CelluloidMpv *mpv,
107 mpv_log_level log_level,
108 const gchar *prefix,
109 const gchar *text );
110
111 static void
112 mpv_event_notify(CelluloidMpv *mpv, gint event_id, gpointer event_data);
113
114 static gboolean
115 process_mpv_events(gpointer data);
116
117 static gboolean
118 check_mpv_version(const gchar *version);
119
120 static gpointer
121 get_wl_display(void);
122
123 static gpointer
124 get_x11_display(void);
125
126 static void
127 initialize(CelluloidMpv *mpv);
128
129 static void
130 load_file(CelluloidMpv *mpv, const gchar *uri, gboolean append);
131
132 static void
133 reset(CelluloidMpv *mpv);
134
G_DEFINE_TYPE_WITH_PRIVATE(CelluloidMpv,celluloid_mpv,G_TYPE_OBJECT)135 G_DEFINE_TYPE_WITH_PRIVATE(CelluloidMpv, celluloid_mpv, G_TYPE_OBJECT)
136
137 static void *
138 get_proc_address(void *fn_ctx, const gchar *name)
139 {
140 GdkDisplay *display = gdk_display_get_default();
141
142 #ifdef GDK_WINDOWING_WAYLAND
143 if (GDK_IS_WAYLAND_DISPLAY(display))
144 return eglGetProcAddress(name);
145 #endif
146 #ifdef GDK_WINDOWING_X11
147 if (GDK_IS_X11_DISPLAY(display))
148 return (void *)(intptr_t)
149 glXGetProcAddressARB((const GLubyte *)name);
150 #endif
151 #ifdef GDK_WINDOWING_WIN32
152 if (GDK_IS_WIN32_DISPLAY(display))
153 return wglGetProcAddress(name);
154 #endif
155 g_assert_not_reached();
156 return NULL;
157 }
158
159 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)160 set_property( GObject *object,
161 guint property_id,
162 const GValue *value,
163 GParamSpec *pspec )
164 {
165 CelluloidMpvPrivate *priv = get_private(CELLULOID_MPV(object));
166
167 if(property_id == PROP_WID)
168 {
169 priv->wid = g_value_get_int64(value);
170 }
171 else if(property_id == PROP_READY)
172 {
173 priv->ready = g_value_get_boolean(value);
174 }
175 else
176 {
177 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
178 }
179 }
180
181 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)182 get_property( GObject *object,
183 guint property_id,
184 GValue *value,
185 GParamSpec *pspec )
186 {
187 CelluloidMpvPrivate *priv = get_private(CELLULOID_MPV(object));
188
189 if(property_id == PROP_WID)
190 {
191 g_value_set_int64(value, priv->wid);
192 }
193 else if(property_id == PROP_READY)
194 {
195 g_value_set_boolean(value, priv->ready);
196 }
197 else
198 {
199 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
200 }
201 }
202
203 static void
dispose(GObject * object)204 dispose(GObject *object)
205 {
206 CelluloidMpv *mpv = CELLULOID_MPV(object);
207
208 if(get_private(mpv)->mpv_ctx)
209 {
210 celluloid_mpv_quit(mpv);
211 while(g_source_remove_by_user_data(object));
212 }
213
214 G_OBJECT_CLASS(celluloid_mpv_parent_class)->dispose(object);
215 }
216
217 static void
finalize(GObject * object)218 finalize(GObject *object)
219 {
220 G_OBJECT_CLASS(celluloid_mpv_parent_class)->finalize(object);
221 }
222
223 static void
wakeup_callback(void * data)224 wakeup_callback(void *data)
225 {
226 g_idle_add_full(G_PRIORITY_HIGH_IDLE, process_mpv_events, data, NULL);
227 }
228
229 static void
mpv_property_changed(CelluloidMpv * mpv,const gchar * name,gpointer value)230 mpv_property_changed(CelluloidMpv *mpv, const gchar *name, gpointer value)
231 {
232 g_debug("Received mpv property change event for \"%s\"", name);
233 }
234
235 static void
mpv_event_notify(CelluloidMpv * mpv,gint event_id,gpointer event_data)236 mpv_event_notify(CelluloidMpv *mpv, gint event_id, gpointer event_data)
237 {
238 if(event_id == MPV_EVENT_PROPERTY_CHANGE)
239 {
240 mpv_event_property *prop = event_data;
241
242 g_signal_emit_by_name( mpv,
243 "mpv-property-changed",
244 prop->name,
245 prop->data );
246 }
247 else if(event_id == MPV_EVENT_IDLE)
248 {
249 celluloid_mpv_set_property_flag(mpv, "pause", TRUE);
250 }
251 else if(event_id == MPV_EVENT_END_FILE)
252 {
253 GSettings *settings = g_settings_new(CONFIG_ROOT);
254 const gchar *ignore_key = "ignore-playback-errors";
255
256 mpv_event_end_file *ef_event = event_data;
257
258 if( !g_settings_get_boolean(settings, ignore_key) &&
259 ef_event->reason == MPV_END_FILE_REASON_ERROR )
260 {
261 const gchar *err;
262 gchar *msg;
263
264 err = mpv_error_string(ef_event->error);
265 msg = g_strdup_printf
266 ( _("Playback was terminated "
267 "abnormally. Reason: %s."),
268 err );
269
270 celluloid_mpv_set_property_flag(mpv, "pause", TRUE);
271 g_signal_emit_by_name(mpv, "error", msg);
272
273 g_free(msg);
274 }
275
276 g_object_unref(settings);
277 }
278 else if(event_id == MPV_EVENT_LOG_MESSAGE)
279 {
280 mpv_event_log_message* message = event_data;
281
282 g_signal_emit_by_name( mpv,
283 "mpv-log-message",
284 message->log_level,
285 message->prefix,
286 message->text );
287 }
288 else if(event_id == MPV_EVENT_CLIENT_MESSAGE)
289 {
290 mpv_event_client_message *event_cmsg = event_data;
291 gchar* msg = strnjoinv( " ",
292 event_cmsg->args,
293 (gsize)event_cmsg->num_args );
294
295 g_signal_emit_by_name(mpv, "message", msg);
296 g_free(msg);
297 }
298 else if(event_id == MPV_EVENT_SHUTDOWN)
299 {
300 g_signal_emit_by_name(mpv, "shutdown");
301 }
302 }
303
304 static gboolean
process_mpv_events(gpointer data)305 process_mpv_events(gpointer data)
306 {
307 CelluloidMpv *mpv = data;
308 CelluloidMpvPrivate *priv = get_private(mpv);
309 gboolean done = !mpv;
310
311 while(!done)
312 {
313 mpv_event *event = priv->mpv_ctx?
314 mpv_wait_event(priv->mpv_ctx, 0):
315 NULL;
316
317 if(event)
318 {
319 if( !priv->mpv_ctx ||
320 event->event_id == MPV_EVENT_SHUTDOWN ||
321 event->event_id == MPV_EVENT_NONE )
322 {
323 done = TRUE;
324 }
325
326 g_signal_emit_by_name( mpv,
327 "mpv-event-notify",
328 event->event_id,
329 event->data );
330 }
331 else
332 {
333 done = TRUE;
334 }
335 }
336
337 return FALSE;
338 }
339
340 static gboolean
check_mpv_version(const gchar * version)341 check_mpv_version(const gchar *version)
342 {
343 guint64 min_version[] = {MIN_MPV_MAJOR, MIN_MPV_MINOR, MIN_MPV_PATCH};
344 const guint min_version_length = G_N_ELEMENTS(min_version);
345 gchar **tokens = NULL;
346 gboolean done = FALSE;
347 gboolean result = TRUE;
348
349 /* Skip to the version number */
350 if(strncmp(version, "mpv ", 4) == 0)
351 {
352 tokens = g_strsplit(version+4, ".", (gint)min_version_length);
353 }
354
355 done = !tokens || g_strv_length(tokens) != min_version_length;
356 result = !done;
357
358 for(guint i = 0; i < min_version_length && !done && result; i++)
359 {
360 gchar *endptr = NULL;
361 guint64 token = g_ascii_strtoull(tokens[i], &endptr, 10);
362
363 /* If the token is equal to the minimum, continue checking the
364 * rest of the tokens. If it is greater, just skip them.
365 */
366 result &= !(*endptr) && (token >= min_version[i]);
367 done = result && (token >= min_version[i]);
368 }
369
370 g_strfreev(tokens);
371
372 return result;
373 }
374
375 static gpointer
get_wl_display(void)376 get_wl_display(void)
377 {
378 gpointer wl_display = NULL;
379
380 #ifdef GDK_WINDOWING_WAYLAND
381 GdkDisplay *display = gdk_display_get_default();
382
383 if(GDK_IS_WAYLAND_DISPLAY(display))
384 {
385 wl_display = gdk_wayland_display_get_wl_display(display);
386 }
387 #endif
388
389 return wl_display;
390 }
391
392 static gpointer
get_x11_display(void)393 get_x11_display(void)
394 {
395 gpointer x11_display = NULL;
396
397 #ifdef GDK_WINDOWING_X11
398 GdkDisplay *display = gdk_display_get_default();
399
400 if(GDK_IS_X11_DISPLAY(display))
401 {
402 x11_display = gdk_x11_display_get_xdisplay(display);
403 }
404 #endif
405
406 return x11_display;
407 }
408
409 static void
initialize(CelluloidMpv * mpv)410 initialize(CelluloidMpv *mpv)
411 {
412 CelluloidMpvPrivate *priv = get_private(mpv);
413 gchar *current_vo = NULL;
414 gchar *mpv_version = NULL;
415
416 if(priv->wid == 0)
417 {
418 g_info("Forcing --vo=null");
419 mpv_set_option_string(priv->mpv_ctx, "vo", "null");
420 }
421 else
422 {
423 g_info("Forcing --vo=libmpv");
424 mpv_set_option_string(priv->mpv_ctx, "vo", "libmpv");
425 }
426
427 mpv_set_wakeup_callback(priv->mpv_ctx, wakeup_callback, mpv);
428 mpv_initialize(priv->mpv_ctx);
429
430 mpv_version = celluloid_mpv_get_property_string(mpv, "mpv-version");
431 current_vo = celluloid_mpv_get_property_string(mpv, "current-vo");
432 priv->use_opengl = (!current_vo && priv->wid != 0);
433
434 g_info("Using %s", mpv_version);
435
436 if(!check_mpv_version(mpv_version))
437 {
438 g_warning( "Minimum mpv version requirement (%d.%d.%d) not met",
439 MIN_MPV_MAJOR,
440 MIN_MPV_MINOR,
441 MIN_MPV_PATCH );
442 }
443
444 priv->ready = TRUE;
445 g_object_notify(G_OBJECT(mpv), "ready");
446
447 mpv_free(current_vo);
448 mpv_free(mpv_version);
449 }
450
451 static void
load_file(CelluloidMpv * mpv,const gchar * uri,gboolean append)452 load_file(CelluloidMpv *mpv, const gchar *uri, gboolean append)
453 {
454 CelluloidMpvPrivate *priv = get_private(mpv);
455 gchar *path = get_path_from_uri(uri);
456 const gchar *load_cmd[] = {"loadfile", path, NULL, NULL};
457 gint64 playlist_count = 0;
458
459 g_assert(uri);
460 g_info( "Loading file (append=%s): %s", append?"TRUE":"FALSE", uri);
461
462 mpv_get_property( priv->mpv_ctx,
463 "playlist-count",
464 MPV_FORMAT_INT64,
465 &playlist_count );
466
467 load_cmd[2] = (append && playlist_count > 0)?"append":"replace";
468
469 if(!append)
470 {
471 celluloid_mpv_set_property_flag(mpv, "pause", FALSE);
472 }
473
474 g_assert(priv->mpv_ctx);
475 mpv_request_event(priv->mpv_ctx, MPV_EVENT_END_FILE, 0);
476 mpv_command(priv->mpv_ctx, load_cmd);
477 mpv_request_event(priv->mpv_ctx, MPV_EVENT_END_FILE, 1);
478
479 g_free(path);
480 }
481
482 static void
reset(CelluloidMpv * mpv)483 reset(CelluloidMpv *mpv)
484 {
485 CelluloidMpvPrivate *priv = get_private(mpv);
486 gchar *loop_file_str;
487 gchar *loop_playlist_str;
488 gboolean loop_file;
489 gboolean loop_playlist;
490
491 loop_file_str = celluloid_mpv_get_property_string
492 (mpv, "loop-file");
493 loop_playlist_str = celluloid_mpv_get_property_string
494 (mpv, "loop-playlist");
495 loop_file = (g_strcmp0(loop_file_str, "inf") == 0);
496 loop_playlist = (g_strcmp0(loop_playlist_str, "inf") == 0);
497
498 mpv_free(loop_file_str);
499 mpv_free(loop_playlist_str);
500
501 /* Reset priv->mpv_ctx */
502 priv->ready = FALSE;
503 g_object_notify(G_OBJECT(mpv), "ready");
504
505 celluloid_mpv_command_string(mpv, "write-watch-later-config");
506 celluloid_mpv_quit(mpv);
507
508 priv->mpv_ctx = mpv_create();
509 celluloid_mpv_initialize(mpv);
510
511 celluloid_mpv_set_render_update_callback
512 ( mpv,
513 priv->render_update_callback,
514 priv->render_update_callback_data );
515
516 celluloid_mpv_set_property_string
517 (mpv, "loop-file", loop_file?"inf":"no");
518 celluloid_mpv_set_property_string
519 (mpv, "loop-playlist", loop_playlist?"inf":"no");
520 }
521
522 static void
mpv_log_message(CelluloidMpv * mpv,mpv_log_level log_level,const gchar * prefix,const gchar * text)523 mpv_log_message( CelluloidMpv *mpv,
524 mpv_log_level log_level,
525 const gchar *prefix,
526 const gchar *text )
527 {
528 }
529
530 static void
celluloid_mpv_class_init(CelluloidMpvClass * klass)531 celluloid_mpv_class_init(CelluloidMpvClass* klass)
532 {
533 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
534 GParamSpec *pspec = NULL;
535
536 klass->mpv_event_notify = mpv_event_notify;
537 klass->mpv_log_message = mpv_log_message;
538 klass->mpv_property_changed = mpv_property_changed;
539 klass->initialize = initialize;
540 klass->load_file = load_file;
541 klass->reset = reset;
542 obj_class->set_property = set_property;
543 obj_class->get_property = get_property;
544 obj_class->dispose = dispose;
545 obj_class->finalize = finalize;
546
547 pspec = g_param_spec_int64
548 ( "wid",
549 "WID",
550 "The ID of the window to attach to",
551 G_MININT64,
552 G_MAXINT64,
553 -1,
554 G_PARAM_CONSTRUCT_ONLY|G_PARAM_READWRITE );
555 g_object_class_install_property(obj_class, PROP_WID, pspec);
556
557 pspec = g_param_spec_boolean
558 ( "ready",
559 "Ready",
560 "Whether mpv is initialized and ready to receive commands",
561 FALSE,
562 G_PARAM_READABLE );
563 g_object_class_install_property(obj_class, PROP_READY, pspec);
564
565 g_signal_new( "error",
566 G_TYPE_FROM_CLASS(klass),
567 G_SIGNAL_RUN_FIRST,
568 0,
569 NULL,
570 NULL,
571 g_cclosure_marshal_VOID__STRING,
572 G_TYPE_NONE,
573 1,
574 G_TYPE_STRING );
575 g_signal_new( "mpv-event-notify",
576 G_TYPE_FROM_CLASS(klass),
577 G_SIGNAL_RUN_FIRST,
578 G_STRUCT_OFFSET(CelluloidMpvClass, mpv_event_notify),
579 NULL,
580 NULL,
581 g_cclosure_gen_marshal_VOID__INT_POINTER,
582 G_TYPE_NONE,
583 2,
584 G_TYPE_INT,
585 G_TYPE_POINTER );
586 g_signal_new( "mpv-log-message",
587 G_TYPE_FROM_CLASS(klass),
588 G_SIGNAL_RUN_FIRST,
589 G_STRUCT_OFFSET(CelluloidMpvClass, mpv_log_message),
590 NULL,
591 NULL,
592 g_cclosure_gen_marshal_VOID__INT_STRING_STRING,
593 G_TYPE_NONE,
594 3,
595 G_TYPE_INT,
596 G_TYPE_STRING,
597 G_TYPE_STRING );
598 g_signal_new( "mpv-property-changed",
599 G_TYPE_FROM_CLASS(klass),
600 G_SIGNAL_RUN_FIRST,
601 G_STRUCT_OFFSET(CelluloidMpvClass, mpv_property_changed),
602 NULL,
603 NULL,
604 g_cclosure_gen_marshal_VOID__STRING_POINTER,
605 G_TYPE_NONE,
606 2,
607 G_TYPE_STRING,
608 G_TYPE_POINTER );
609 g_signal_new( "message",
610 G_TYPE_FROM_CLASS(klass),
611 G_SIGNAL_RUN_FIRST,
612 0,
613 NULL,
614 NULL,
615 g_cclosure_marshal_VOID__STRING,
616 G_TYPE_NONE,
617 1,
618 G_TYPE_STRING );
619 g_signal_new( "window-resize",
620 G_TYPE_FROM_CLASS(klass),
621 G_SIGNAL_RUN_FIRST,
622 0,
623 NULL,
624 NULL,
625 g_cclosure_gen_marshal_VOID__INT64_INT64,
626 G_TYPE_NONE,
627 2,
628 G_TYPE_INT64,
629 G_TYPE_INT64 );
630 g_signal_new( "window-move",
631 G_TYPE_FROM_CLASS(klass),
632 G_SIGNAL_RUN_FIRST,
633 0,
634 NULL,
635 NULL,
636 g_cclosure_gen_marshal_VOID__BOOLEAN_BOOLEAN_POINTER_POINTER,
637 G_TYPE_NONE,
638 4,
639 G_TYPE_BOOLEAN,
640 G_TYPE_BOOLEAN,
641 G_TYPE_POINTER,
642 G_TYPE_POINTER );
643 g_signal_new( "shutdown",
644 G_TYPE_FROM_CLASS(klass),
645 G_SIGNAL_RUN_FIRST,
646 0,
647 NULL,
648 NULL,
649 g_cclosure_marshal_VOID__VOID,
650 G_TYPE_NONE,
651 0 );
652 }
653
654 static void
celluloid_mpv_init(CelluloidMpv * mpv)655 celluloid_mpv_init(CelluloidMpv *mpv)
656 {
657 CelluloidMpvPrivate *priv = get_private(mpv);
658
659 setlocale(LC_NUMERIC, "C");
660
661 priv->mpv_ctx = mpv_create();
662 priv->render_ctx = NULL;
663 priv->ready = FALSE;
664 priv->init_vo_config = TRUE;
665 priv->use_opengl = FALSE;
666 priv->wid = -1;
667 priv->render_update_callback_data = NULL;
668 priv->render_update_callback = NULL;
669 }
670
671 CelluloidMpv *
celluloid_mpv_new(gint64 wid)672 celluloid_mpv_new(gint64 wid)
673 {
674 return CELLULOID_MPV(g_object_new(celluloid_mpv_get_type(), "wid", wid, NULL));
675 }
676
677 inline mpv_render_context *
celluloid_mpv_get_render_context(CelluloidMpv * mpv)678 celluloid_mpv_get_render_context(CelluloidMpv *mpv)
679 {
680 return get_private(mpv)->render_ctx;
681 }
682
683 inline gboolean
celluloid_mpv_get_use_opengl_cb(CelluloidMpv * mpv)684 celluloid_mpv_get_use_opengl_cb(CelluloidMpv *mpv)
685 {
686 return get_private(mpv)->use_opengl;
687 }
688
689 void
celluloid_mpv_initialize(CelluloidMpv * mpv)690 celluloid_mpv_initialize(CelluloidMpv *mpv)
691 {
692 CELLULOID_MPV_GET_CLASS(mpv)->initialize(mpv);
693 }
694
695 void
celluloid_mpv_init_gl(CelluloidMpv * mpv)696 celluloid_mpv_init_gl(CelluloidMpv *mpv)
697 {
698 CelluloidMpvPrivate *priv = get_private(mpv);
699 mpv_opengl_init_params init_params =
700 {.get_proc_address = get_proc_address};
701 mpv_render_param params[] =
702 { {MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_OPENGL},
703 {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &init_params},
704 {MPV_RENDER_PARAM_WL_DISPLAY, get_wl_display()},
705 {MPV_RENDER_PARAM_X11_DISPLAY, get_x11_display()},
706 {0, NULL} };
707 gint rc = mpv_render_context_create( &priv->render_ctx,
708 priv->mpv_ctx,
709 params );
710
711 if(rc >= 0)
712 {
713 g_debug("Initialized render context");
714 }
715 else
716 {
717 g_critical("Failed to initialize render context");
718 }
719 }
720
721 void
celluloid_mpv_reset(CelluloidMpv * mpv)722 celluloid_mpv_reset(CelluloidMpv *mpv)
723 {
724 CELLULOID_MPV_GET_CLASS(mpv)->reset(mpv);
725 }
726
727 void
celluloid_mpv_quit(CelluloidMpv * mpv)728 celluloid_mpv_quit(CelluloidMpv *mpv)
729 {
730 CelluloidMpvPrivate *priv = get_private(mpv);
731
732 g_info("Terminating mpv");
733 celluloid_mpv_command_string(mpv, "quit");
734
735 if(priv->render_ctx)
736 {
737 g_debug("Uninitializing render context");
738 mpv_render_context_free(priv->render_ctx);
739
740 priv->render_ctx = NULL;
741 }
742
743 g_assert(priv->mpv_ctx);
744 mpv_terminate_destroy(priv->mpv_ctx);
745
746 priv->mpv_ctx = NULL;
747 }
748
749 void
celluloid_mpv_load_track(CelluloidMpv * mpv,const gchar * uri,TrackType type)750 celluloid_mpv_load_track(CelluloidMpv *mpv, const gchar *uri, TrackType type)
751 {
752 const gchar *cmd[3] = {NULL};
753 gchar *path = g_filename_from_uri(uri, NULL, NULL);
754
755 switch(type)
756 {
757 case TRACK_TYPE_AUDIO:
758 cmd[0] = "audio-add";
759 break;
760
761 case TRACK_TYPE_VIDEO:
762 cmd[0] = "video-add";
763 break;
764
765 case TRACK_TYPE_SUBTITLE:
766 cmd[0] = "sub-add";
767 break;
768
769 default:
770 g_assert_not_reached();
771 break;
772 }
773
774 cmd[1] = path?:uri;
775
776 g_debug("Loading external track %s with type %d", cmd[1], type);
777 celluloid_mpv_command(mpv, cmd);
778
779 g_free(path);
780 }
781
782 void
celluloid_mpv_load_file(CelluloidMpv * mpv,const gchar * uri,gboolean append)783 celluloid_mpv_load_file(CelluloidMpv *mpv, const gchar *uri, gboolean append)
784 {
785 CELLULOID_MPV_GET_CLASS(mpv)->load_file(mpv, uri, append);
786 }
787
788 void
celluloid_mpv_load(CelluloidMpv * mpv,const gchar * uri,gboolean append)789 celluloid_mpv_load(CelluloidMpv *mpv, const gchar *uri, gboolean append)
790 {
791 const gchar *subtitle_exts[] = SUBTITLE_EXTS;
792
793 if(extension_matches(uri, subtitle_exts))
794 {
795 celluloid_mpv_load_track(mpv, uri, TRACK_TYPE_SUBTITLE);
796 }
797 else
798 {
799 celluloid_mpv_load_file(mpv, uri, append);
800 }
801 }
802
803 gint
celluloid_mpv_command(CelluloidMpv * mpv,const gchar ** cmd)804 celluloid_mpv_command(CelluloidMpv *mpv, const gchar **cmd)
805 {
806 CelluloidMpvPrivate *priv = get_private(mpv);
807 gint rc = MPV_ERROR_UNINITIALIZED;
808
809 if(priv->mpv_ctx)
810 {
811 rc = mpv_command(priv->mpv_ctx, cmd);
812 }
813
814 if(rc < 0)
815 {
816 gchar *cmd_str = g_strjoinv(" ", (gchar **)cmd);
817
818 g_warning( "Failed to run mpv command \"%s\". Reason: %s.",
819 cmd_str,
820 mpv_error_string(rc) );
821
822 g_free(cmd_str);
823 }
824
825 return rc;
826 }
827
828 gint
celluloid_mpv_command_async(CelluloidMpv * mpv,const gchar ** cmd)829 celluloid_mpv_command_async(CelluloidMpv *mpv, const gchar **cmd)
830 {
831 CelluloidMpvPrivate *priv = get_private(mpv);
832 gint rc = MPV_ERROR_UNINITIALIZED;
833
834 if(priv->mpv_ctx)
835 {
836 rc = mpv_command_async(priv->mpv_ctx, 0, cmd);
837 }
838
839 if(rc < 0)
840 {
841 gchar *cmd_str = g_strjoinv(" ", (gchar **)cmd);
842
843 g_warning( "Failed to dispatch async mpv command \"%s\". "
844 "Reason: %s.",
845 cmd_str,
846 mpv_error_string(rc) );
847
848 g_free(cmd_str);
849 }
850
851 return rc;
852 }
853
854 gint
celluloid_mpv_command_string(CelluloidMpv * mpv,const gchar * cmd)855 celluloid_mpv_command_string(CelluloidMpv *mpv, const gchar *cmd)
856 {
857 CelluloidMpvPrivate *priv = get_private(mpv);
858 gint rc = MPV_ERROR_UNINITIALIZED;
859
860 if(priv->mpv_ctx)
861 {
862 rc = mpv_command_string(priv->mpv_ctx, cmd);
863 }
864
865 if(rc < 0)
866 {
867 g_warning( "Failed to run mpv command string \"%s\". "
868 "Reason: %s.",
869 cmd,
870 mpv_error_string(rc) );
871 }
872
873 return rc;
874 }
875
876 gint
celluloid_mpv_set_option_string(CelluloidMpv * mpv,const gchar * name,const gchar * value)877 celluloid_mpv_set_option_string( CelluloidMpv *mpv,
878 const gchar *name,
879 const gchar *value )
880 {
881 return mpv_set_option_string(get_private(mpv)->mpv_ctx, name, value);
882 }
883
884 gint
celluloid_mpv_get_property(CelluloidMpv * mpv,const gchar * name,mpv_format format,void * data)885 celluloid_mpv_get_property( CelluloidMpv *mpv,
886 const gchar *name,
887 mpv_format format,
888 void *data )
889 {
890 CelluloidMpvPrivate *priv = get_private(mpv);
891 gint rc = MPV_ERROR_UNINITIALIZED;
892
893 if(priv->mpv_ctx)
894 {
895 rc = mpv_get_property(priv->mpv_ctx, name, format, data);
896 }
897
898 if(rc < 0)
899 {
900 g_info( "Failed to retrieve property \"%s\" "
901 "using mpv format %d. Reason: %s.",
902 name,
903 format,
904 mpv_error_string(rc) );
905 }
906
907 return rc;
908 }
909
910 gchar *
celluloid_mpv_get_property_string(CelluloidMpv * mpv,const gchar * name)911 celluloid_mpv_get_property_string(CelluloidMpv *mpv, const gchar *name)
912 {
913 CelluloidMpvPrivate *priv = get_private(mpv);
914 gchar *value = NULL;
915
916 if(priv->mpv_ctx)
917 {
918 value = mpv_get_property_string(priv->mpv_ctx, name);
919 }
920
921 if(!value)
922 {
923 g_info("Failed to retrieve property \"%s\" as string.", name);
924 }
925
926 return value;
927 }
928
929 gboolean
celluloid_mpv_get_property_flag(CelluloidMpv * mpv,const gchar * name)930 celluloid_mpv_get_property_flag(CelluloidMpv *mpv, const gchar *name)
931 {
932 CelluloidMpvPrivate *priv = get_private(mpv);
933 gboolean value = FALSE;
934 gint rc = MPV_ERROR_UNINITIALIZED;
935
936 if(priv->mpv_ctx)
937 {
938 rc = mpv_get_property
939 (priv->mpv_ctx, name, MPV_FORMAT_FLAG, &value);
940 }
941
942 if(rc < 0)
943 {
944 g_info( "Failed to retrieve property \"%s\" as flag. "
945 "Reason: %s.",
946 name,
947 mpv_error_string(rc) );
948 }
949
950 return value;
951 }
952
953 gint
celluloid_mpv_set_property(CelluloidMpv * mpv,const gchar * name,mpv_format format,void * data)954 celluloid_mpv_set_property( CelluloidMpv *mpv,
955 const gchar *name,
956 mpv_format format,
957 void *data )
958 {
959 CelluloidMpvPrivate *priv = get_private(mpv);
960 gint rc = MPV_ERROR_UNINITIALIZED;
961
962 if(priv->mpv_ctx)
963 {
964 rc = mpv_set_property(priv->mpv_ctx, name, format, data);
965 }
966
967 if(rc < 0)
968 {
969 g_info( "Failed to set property \"%s\" using mpv format %d. "
970 "Reason: %s.",
971 name,
972 format,
973 mpv_error_string(rc) );
974 }
975
976 return rc;
977 }
978
979 gint
celluloid_mpv_set_property_string(CelluloidMpv * mpv,const gchar * name,const char * data)980 celluloid_mpv_set_property_string( CelluloidMpv *mpv,
981 const gchar *name,
982 const char *data )
983 {
984 CelluloidMpvPrivate *priv = get_private(mpv);
985 gint rc = MPV_ERROR_UNINITIALIZED;
986
987 if(priv->mpv_ctx)
988 {
989 rc = mpv_set_property_string(priv->mpv_ctx, name, data);
990 }
991
992 if(rc < 0)
993 {
994 g_info( "Failed to set property \"%s\" as string. Reason: %s.",
995 name,
996 mpv_error_string(rc) );
997 }
998
999 return rc;
1000 }
1001
1002 gint
celluloid_mpv_set_property_flag(CelluloidMpv * mpv,const gchar * name,gboolean value)1003 celluloid_mpv_set_property_flag( CelluloidMpv *mpv,
1004 const gchar *name,
1005 gboolean value )
1006 {
1007 CelluloidMpvPrivate *priv = get_private(mpv);
1008 gint rc = MPV_ERROR_UNINITIALIZED;
1009
1010 if(priv->mpv_ctx)
1011 {
1012 rc = mpv_set_property
1013 (priv->mpv_ctx, name, MPV_FORMAT_FLAG, &value);
1014 }
1015
1016 if(rc < 0)
1017 {
1018 g_info( "Failed to set property \"%s\" as flag. Reason: %s.",
1019 name,
1020 mpv_error_string(rc) );
1021 }
1022
1023 return rc;
1024 }
1025
1026 void
celluloid_mpv_set_render_update_callback(CelluloidMpv * mpv,mpv_render_update_fn func,void * data)1027 celluloid_mpv_set_render_update_callback( CelluloidMpv *mpv,
1028 mpv_render_update_fn func,
1029 void *data )
1030 {
1031 CelluloidMpvPrivate *priv = get_private(mpv);
1032
1033 priv->render_update_callback = func;
1034 priv->render_update_callback_data = data;
1035
1036 if(priv->render_ctx)
1037 {
1038 mpv_render_context_set_update_callback
1039 (priv->render_ctx, func, data);
1040 }
1041 }
1042
1043 guint64
celluloid_mpv_render_context_update(CelluloidMpv * mpv)1044 celluloid_mpv_render_context_update(CelluloidMpv *mpv)
1045 {
1046 return mpv_render_context_update(get_private(mpv)->render_ctx);
1047 }
1048
1049 gint
celluloid_mpv_load_config_file(CelluloidMpv * mpv,const gchar * filename)1050 celluloid_mpv_load_config_file(CelluloidMpv *mpv, const gchar *filename)
1051 {
1052 return mpv_load_config_file(get_private(mpv)->mpv_ctx, filename);
1053 }
1054
1055 gint
celluloid_mpv_observe_property(CelluloidMpv * mpv,guint64 reply_userdata,const gchar * name,mpv_format format)1056 celluloid_mpv_observe_property( CelluloidMpv *mpv,
1057 guint64 reply_userdata,
1058 const gchar *name,
1059 mpv_format format )
1060 {
1061 return mpv_observe_property( get_private(mpv)->mpv_ctx,
1062 reply_userdata,
1063 name,
1064 format );
1065 }
1066
1067 gint
celluloid_mpv_request_log_messages(CelluloidMpv * mpv,const gchar * min_level)1068 celluloid_mpv_request_log_messages(CelluloidMpv *mpv, const gchar *min_level)
1069 {
1070 return mpv_request_log_messages(get_private(mpv)->mpv_ctx, min_level);
1071 }
1072