1 /* Copyright (C) 2017 the mpv developers
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14  */
15 
16 #include <stddef.h>
17 #include <stdint.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <math.h>
23 #include <assert.h>
24 
25 #include "common/common.h"
26 #include "common/global.h"
27 #include "common/msg.h"
28 #include "common/msg_control.h"
29 #include "common/global.h"
30 #include "input/input.h"
31 #include "input/cmd.h"
32 #include "misc/ctype.h"
33 #include "misc/dispatch.h"
34 #include "misc/node.h"
35 #include "misc/rendezvous.h"
36 #include "misc/thread_tools.h"
37 #include "options/m_config.h"
38 #include "options/m_option.h"
39 #include "options/m_property.h"
40 #include "options/path.h"
41 #include "options/parse_configfile.h"
42 #include "osdep/atomic.h"
43 #include "osdep/threads.h"
44 #include "osdep/timer.h"
45 #include "osdep/io.h"
46 #include "stream/stream.h"
47 
48 #include "command.h"
49 #include "core.h"
50 #include "client.h"
51 
52 /*
53  * Locking hierarchy:
54  *
55  *  MPContext > mp_client_api.lock > mpv_handle.lock > * > mpv_handle.wakeup_lock
56  *
57  * MPContext strictly speaking has no locks, and instead is implicitly managed
58  * by MPContext.dispatch, which basically stops the playback thread at defined
59  * points in order to let clients access it in a synchronized manner. Since
60  * MPContext code accesses the client API, it's on top of the lock hierarchy.
61  *
62  */
63 
64 struct mp_client_api {
65     struct MPContext *mpctx;
66 
67     pthread_mutex_t lock;
68 
69     // -- protected by lock
70 
71     struct mpv_handle **clients;
72     int num_clients;
73     bool shutting_down; // do not allow new clients
74     bool have_terminator; // a client took over the role of destroying the core
75     bool terminate_core_thread; // make libmpv core thread exit
76     // This is incremented whenever the clients[] array above changes. This is
77     // used to safely unlock mp_client_api.lock while iterating the list of
78     // clients.
79     uint64_t clients_list_change_ts;
80     int64_t id_alloc;
81 
82     struct mp_custom_protocol *custom_protocols;
83     int num_custom_protocols;
84 
85     struct mpv_render_context *render_context;
86 };
87 
88 struct observe_property {
89     // -- immutable
90     struct mpv_handle *owner;
91     char *name;
92     int id;                 // ==mp_get_property_id(name)
93     uint64_t event_mask;    // ==mp_get_property_event_mask(name)
94     int64_t reply_id;
95     mpv_format format;
96     const struct m_option *type;
97     // -- protected by owner->lock
98     size_t refcount;
99     uint64_t change_ts;     // logical timestamp incremented on each change
100     uint64_t value_ts;      // logical timestamp for value contents
101     bool value_valid;
102     union m_option_value value;
103     uint64_t value_ret_ts;  // logical timestamp of value returned to user
104     union m_option_value value_ret;
105     bool waiting_for_hook;  // flag for draining old property changes on a hook
106 };
107 
108 struct mpv_handle {
109     // -- immmutable
110     char name[MAX_CLIENT_NAME];
111     struct mp_log *log;
112     struct MPContext *mpctx;
113     struct mp_client_api *clients;
114     int64_t id;
115 
116     // -- not thread-safe
117     struct mpv_event *cur_event;
118     struct mpv_event_property cur_property_event;
119     struct observe_property *cur_property;
120 
121     pthread_mutex_t lock;
122 
123     pthread_mutex_t wakeup_lock;
124     pthread_cond_t wakeup;
125 
126     // -- protected by wakeup_lock
127     bool need_wakeup;
128     void (*wakeup_cb)(void *d);
129     void *wakeup_cb_ctx;
130     int wakeup_pipe[2];
131 
132     // -- protected by lock
133 
134     uint64_t event_mask;
135     bool queued_wakeup;
136 
137     mpv_event *events;      // ringbuffer of max_events entries
138     int max_events;         // allocated number of entries in events
139     int first_event;        // events[first_event] is the first readable event
140     int num_events;         // number of readable events
141     int reserved_events;    // number of entries reserved for replies
142     size_t async_counter;   // pending other async events
143     bool choked;            // recovering from queue overflow
144     bool destroying;        // pending destruction; no API accesses allowed
145     bool hook_pending;      // hook events are returned after draining properties
146 
147     struct observe_property **properties;
148     int num_properties;
149     bool has_pending_properties; // (maybe) new property events (producer side)
150     bool new_property_events; // new property events (consumer side)
151     int cur_property_index; // round-robin for property events (consumer side)
152     uint64_t property_event_masks; // or-ed together event masks of all properties
153     // This is incremented whenever the properties[] array above changes. This
154     // is used to safely unlock mpv_handle.lock while reading a property. If
155     // the counter didn't change between unlock and relock, then it will assume
156     // the array did not change.
157     uint64_t properties_change_ts;
158 
159     bool fuzzy_initialized; // see scripting.c wait_loaded()
160     bool is_weak;           // can not keep core alive on its own
161     struct mp_log_buffer *messages;
162     int messages_level;
163 };
164 
165 static bool gen_log_message_event(struct mpv_handle *ctx);
166 static bool gen_property_change_event(struct mpv_handle *ctx);
167 static void notify_property_events(struct mpv_handle *ctx, int event);
168 
169 // Must be called with prop->owner->lock held.
prop_unref(struct observe_property * prop)170 static void prop_unref(struct observe_property *prop)
171 {
172     if (!prop)
173         return;
174 
175     assert(prop->refcount > 0);
176     prop->refcount -= 1;
177     if (!prop->refcount)
178         talloc_free(prop);
179 }
180 
mp_clients_init(struct MPContext * mpctx)181 void mp_clients_init(struct MPContext *mpctx)
182 {
183     mpctx->clients = talloc_ptrtype(NULL, mpctx->clients);
184     *mpctx->clients = (struct mp_client_api) {
185         .mpctx = mpctx,
186     };
187     mpctx->global->client_api = mpctx->clients;
188     pthread_mutex_init(&mpctx->clients->lock, NULL);
189 }
190 
mp_clients_destroy(struct MPContext * mpctx)191 void mp_clients_destroy(struct MPContext *mpctx)
192 {
193     if (!mpctx->clients)
194         return;
195     assert(mpctx->clients->num_clients == 0);
196 
197     // The API user is supposed to call mpv_render_context_free(). It's simply
198     // not allowed not to do this.
199     if (mpctx->clients->render_context) {
200         MP_FATAL(mpctx, "Broken API use: mpv_render_context_free() not called.\n");
201         abort();
202     }
203 
204     pthread_mutex_destroy(&mpctx->clients->lock);
205     talloc_free(mpctx->clients);
206     mpctx->clients = NULL;
207 }
208 
209 // Test for "fuzzy" initialization of all clients. That is, all clients have
210 // at least called mpv_wait_event() at least once since creation (or exited).
mp_clients_all_initialized(struct MPContext * mpctx)211 bool mp_clients_all_initialized(struct MPContext *mpctx)
212 {
213     bool all_ok = true;
214     pthread_mutex_lock(&mpctx->clients->lock);
215     for (int n = 0; n < mpctx->clients->num_clients; n++) {
216         struct mpv_handle *ctx = mpctx->clients->clients[n];
217         pthread_mutex_lock(&ctx->lock);
218         all_ok &= ctx->fuzzy_initialized;
219         pthread_mutex_unlock(&ctx->lock);
220     }
221     pthread_mutex_unlock(&mpctx->clients->lock);
222     return all_ok;
223 }
224 
find_client_id(struct mp_client_api * clients,int64_t id)225 static struct mpv_handle *find_client_id(struct mp_client_api *clients, int64_t id)
226 {
227     for (int n = 0; n < clients->num_clients; n++) {
228         if (clients->clients[n]->id == id)
229             return clients->clients[n];
230     }
231     return NULL;
232 }
233 
find_client(struct mp_client_api * clients,const char * name)234 static struct mpv_handle *find_client(struct mp_client_api *clients,
235                                       const char *name)
236 {
237     if (name[0] == '@') {
238         char *end;
239         errno = 0;
240         long long int id = strtoll(name + 1, &end, 10);
241         if (errno || end[0])
242             return NULL;
243         return find_client_id(clients, id);
244     }
245 
246     for (int n = 0; n < clients->num_clients; n++) {
247         if (strcmp(clients->clients[n]->name, name) == 0)
248             return clients->clients[n];
249     }
250 
251     return NULL;
252 }
253 
mp_client_id_exists(struct MPContext * mpctx,int64_t id)254 bool mp_client_id_exists(struct MPContext *mpctx, int64_t id)
255 {
256     pthread_mutex_lock(&mpctx->clients->lock);
257     bool r = find_client_id(mpctx->clients, id);
258     pthread_mutex_unlock(&mpctx->clients->lock);
259     return r;
260 }
261 
mp_new_client(struct mp_client_api * clients,const char * name)262 struct mpv_handle *mp_new_client(struct mp_client_api *clients, const char *name)
263 {
264     pthread_mutex_lock(&clients->lock);
265 
266     char nname[MAX_CLIENT_NAME];
267     for (int n = 1; n < 1000; n++) {
268         if (!name)
269             name = "client";
270         snprintf(nname, sizeof(nname) - 3, "%s", name); // - space for number
271         for (int i = 0; nname[i]; i++)
272             nname[i] = mp_isalnum(nname[i]) ? nname[i] : '_';
273         if (n > 1)
274             mp_snprintf_cat(nname, sizeof(nname), "%d", n);
275         if (!find_client(clients, nname))
276             break;
277         nname[0] = '\0';
278     }
279 
280     if (!nname[0] || clients->shutting_down) {
281         pthread_mutex_unlock(&clients->lock);
282         return NULL;
283     }
284 
285     int num_events = 1000;
286 
287     struct mpv_handle *client = talloc_ptrtype(NULL, client);
288     *client = (struct mpv_handle){
289         .log = mp_log_new(client, clients->mpctx->log, nname),
290         .mpctx = clients->mpctx,
291         .clients = clients,
292         .id = ++(clients->id_alloc),
293         .cur_event = talloc_zero(client, struct mpv_event),
294         .events = talloc_array(client, mpv_event, num_events),
295         .max_events = num_events,
296         .event_mask = (1ULL << INTERNAL_EVENT_BASE) - 1, // exclude internal events
297         .wakeup_pipe = {-1, -1},
298     };
299     pthread_mutex_init(&client->lock, NULL);
300     pthread_mutex_init(&client->wakeup_lock, NULL);
301     pthread_cond_init(&client->wakeup, NULL);
302 
303     snprintf(client->name, sizeof(client->name), "%s", nname);
304 
305     clients->clients_list_change_ts += 1;
306     MP_TARRAY_APPEND(clients, clients->clients, clients->num_clients, client);
307 
308     if (clients->num_clients == 1 && !clients->mpctx->is_cli)
309         client->fuzzy_initialized = true;
310 
311     pthread_mutex_unlock(&clients->lock);
312 
313     mpv_request_event(client, MPV_EVENT_TICK, 0);
314 
315     return client;
316 }
317 
mp_client_set_weak(struct mpv_handle * ctx)318 void mp_client_set_weak(struct mpv_handle *ctx)
319 {
320     pthread_mutex_lock(&ctx->lock);
321     ctx->is_weak = true;
322     pthread_mutex_unlock(&ctx->lock);
323 }
324 
mpv_client_name(mpv_handle * ctx)325 const char *mpv_client_name(mpv_handle *ctx)
326 {
327     return ctx->name;
328 }
329 
mpv_client_id(mpv_handle * ctx)330 int64_t mpv_client_id(mpv_handle *ctx)
331 {
332     return ctx->id;
333 }
334 
mp_client_get_log(struct mpv_handle * ctx)335 struct mp_log *mp_client_get_log(struct mpv_handle *ctx)
336 {
337     return ctx->log;
338 }
339 
mp_client_get_global(struct mpv_handle * ctx)340 struct mpv_global *mp_client_get_global(struct mpv_handle *ctx)
341 {
342     return ctx->mpctx->global;
343 }
344 
wakeup_client(struct mpv_handle * ctx)345 static void wakeup_client(struct mpv_handle *ctx)
346 {
347     pthread_mutex_lock(&ctx->wakeup_lock);
348     if (!ctx->need_wakeup) {
349         ctx->need_wakeup = true;
350         pthread_cond_broadcast(&ctx->wakeup);
351         if (ctx->wakeup_cb)
352             ctx->wakeup_cb(ctx->wakeup_cb_ctx);
353         if (ctx->wakeup_pipe[0] != -1)
354             (void)write(ctx->wakeup_pipe[1], &(char){0}, 1);
355     }
356     pthread_mutex_unlock(&ctx->wakeup_lock);
357 }
358 
359 // Note: the caller has to deal with sporadic wakeups.
wait_wakeup(struct mpv_handle * ctx,int64_t end)360 static int wait_wakeup(struct mpv_handle *ctx, int64_t end)
361 {
362     int r = 0;
363     pthread_mutex_unlock(&ctx->lock);
364     pthread_mutex_lock(&ctx->wakeup_lock);
365     if (!ctx->need_wakeup) {
366         struct timespec ts = mp_time_us_to_timespec(end);
367         r = pthread_cond_timedwait(&ctx->wakeup, &ctx->wakeup_lock, &ts);
368     }
369     if (r == 0)
370         ctx->need_wakeup = false;
371     pthread_mutex_unlock(&ctx->wakeup_lock);
372     pthread_mutex_lock(&ctx->lock);
373     return r;
374 }
375 
mpv_set_wakeup_callback(mpv_handle * ctx,void (* cb)(void * d),void * d)376 void mpv_set_wakeup_callback(mpv_handle *ctx, void (*cb)(void *d), void *d)
377 {
378     pthread_mutex_lock(&ctx->wakeup_lock);
379     ctx->wakeup_cb = cb;
380     ctx->wakeup_cb_ctx = d;
381     if (ctx->wakeup_cb)
382         ctx->wakeup_cb(ctx->wakeup_cb_ctx);
383     pthread_mutex_unlock(&ctx->wakeup_lock);
384 }
385 
mpv_suspend(mpv_handle * ctx)386 void mpv_suspend(mpv_handle *ctx)
387 {
388     MP_ERR(ctx, "mpv_suspend() is deprecated and does nothing.\n");
389 }
390 
mpv_resume(mpv_handle * ctx)391 void mpv_resume(mpv_handle *ctx)
392 {
393 }
394 
lock_core(mpv_handle * ctx)395 static void lock_core(mpv_handle *ctx)
396 {
397     mp_dispatch_lock(ctx->mpctx->dispatch);
398 }
399 
unlock_core(mpv_handle * ctx)400 static void unlock_core(mpv_handle *ctx)
401 {
402     mp_dispatch_unlock(ctx->mpctx->dispatch);
403 }
404 
mpv_wait_async_requests(mpv_handle * ctx)405 void mpv_wait_async_requests(mpv_handle *ctx)
406 {
407     pthread_mutex_lock(&ctx->lock);
408     while (ctx->reserved_events || ctx->async_counter)
409         wait_wakeup(ctx, INT64_MAX);
410     pthread_mutex_unlock(&ctx->lock);
411 }
412 
413 // Send abort signal to all matching work items.
414 // If type==0, destroy all of the matching ctx.
415 // If ctx==0, destroy all.
abort_async(struct MPContext * mpctx,mpv_handle * ctx,int type,uint64_t id)416 static void abort_async(struct MPContext *mpctx, mpv_handle *ctx,
417                         int type, uint64_t id)
418 {
419     pthread_mutex_lock(&mpctx->abort_lock);
420 
421     // Destroy all => ensure any newly appearing work is aborted immediately.
422     if (ctx == NULL)
423         mpctx->abort_all = true;
424 
425     for (int n = 0; n < mpctx->num_abort_list; n++) {
426         struct mp_abort_entry *abort = mpctx->abort_list[n];
427         if (!ctx || (abort->client == ctx && (!type ||
428             (abort->client_work_type == type && abort->client_work_id == id))))
429         {
430             mp_abort_trigger_locked(mpctx, abort);
431         }
432     }
433 
434     pthread_mutex_unlock(&mpctx->abort_lock);
435 }
436 
get_thread(void * ptr)437 static void get_thread(void *ptr)
438 {
439     *(pthread_t *)ptr = pthread_self();
440 }
441 
mp_destroy_client(mpv_handle * ctx,bool terminate)442 static void mp_destroy_client(mpv_handle *ctx, bool terminate)
443 {
444     if (!ctx)
445         return;
446 
447     struct MPContext *mpctx = ctx->mpctx;
448     struct mp_client_api *clients = ctx->clients;
449 
450     MP_DBG(ctx, "Exiting...\n");
451 
452     if (terminate)
453         mpv_command(ctx, (const char*[]){"quit", NULL});
454 
455     pthread_mutex_lock(&ctx->lock);
456 
457     ctx->destroying = true;
458 
459     for (int n = 0; n < ctx->num_properties; n++)
460         prop_unref(ctx->properties[n]);
461     ctx->num_properties = 0;
462     ctx->properties_change_ts += 1;
463 
464     prop_unref(ctx->cur_property);
465     ctx->cur_property = NULL;
466 
467     pthread_mutex_unlock(&ctx->lock);
468 
469     abort_async(mpctx, ctx, 0, 0);
470 
471     // reserved_events equals the number of asynchronous requests that weren't
472     // yet replied. In order to avoid that trying to reply to a removed client
473     // causes a crash, block until all asynchronous requests were served.
474     mpv_wait_async_requests(ctx);
475 
476     osd_set_external_remove_owner(mpctx->osd, ctx);
477     mp_input_remove_sections_by_owner(mpctx->input, ctx->name);
478 
479     pthread_mutex_lock(&clients->lock);
480 
481     for (int n = 0; n < clients->num_clients; n++) {
482         if (clients->clients[n] == ctx) {
483             clients->clients_list_change_ts += 1;
484             MP_TARRAY_REMOVE_AT(clients->clients, clients->num_clients, n);
485             while (ctx->num_events) {
486                 talloc_free(ctx->events[ctx->first_event].data);
487                 ctx->first_event = (ctx->first_event + 1) % ctx->max_events;
488                 ctx->num_events--;
489             }
490             mp_msg_log_buffer_destroy(ctx->messages);
491             pthread_cond_destroy(&ctx->wakeup);
492             pthread_mutex_destroy(&ctx->wakeup_lock);
493             pthread_mutex_destroy(&ctx->lock);
494             if (ctx->wakeup_pipe[0] != -1) {
495                 close(ctx->wakeup_pipe[0]);
496                 close(ctx->wakeup_pipe[1]);
497             }
498             talloc_free(ctx);
499             ctx = NULL;
500             break;
501         }
502     }
503     assert(!ctx);
504 
505     if (mpctx->is_cli) {
506         terminate = false;
507     } else {
508         // If the last strong mpv_handle got destroyed, destroy the core.
509         bool has_strong_ref = false;
510         for (int n = 0; n < clients->num_clients; n++)
511             has_strong_ref |= !clients->clients[n]->is_weak;
512         if (!has_strong_ref)
513             terminate = true;
514 
515         // Reserve the right to destroy mpctx for us.
516         if (clients->have_terminator)
517             terminate = false;
518         clients->have_terminator |= terminate;
519     }
520 
521     // mp_shutdown_clients() sleeps to avoid wasting CPU.
522     // mp_hook_test_completion() also relies on this a bit.
523     mp_wakeup_core(mpctx);
524 
525     pthread_mutex_unlock(&clients->lock);
526 
527     // Note that even if num_clients==0, having set have_terminator keeps mpctx
528     // and the core thread alive.
529     if (terminate) {
530         // Make sure the core stops playing files etc. Being able to lock the
531         // dispatch queue requires that the core thread is still active.
532         mp_dispatch_lock(mpctx->dispatch);
533         mpctx->stop_play = PT_QUIT;
534         mp_dispatch_unlock(mpctx->dispatch);
535 
536         pthread_t playthread;
537         mp_dispatch_run(mpctx->dispatch, get_thread, &playthread);
538 
539         // Ask the core thread to stop.
540         pthread_mutex_lock(&clients->lock);
541         clients->terminate_core_thread = true;
542         pthread_mutex_unlock(&clients->lock);
543         mp_wakeup_core(mpctx);
544 
545         // Blocking wait for all clients and core thread to terminate.
546         pthread_join(playthread, NULL);
547 
548         mp_destroy(mpctx);
549     }
550 }
551 
mpv_destroy(mpv_handle * ctx)552 void mpv_destroy(mpv_handle *ctx)
553 {
554     mp_destroy_client(ctx, false);
555 }
556 
mpv_detach_destroy(mpv_handle * ctx)557 void mpv_detach_destroy(mpv_handle *ctx)
558 {
559     mpv_destroy(ctx);
560 }
561 
mpv_terminate_destroy(mpv_handle * ctx)562 void mpv_terminate_destroy(mpv_handle *ctx)
563 {
564     mp_destroy_client(ctx, true);
565 }
566 
567 // Can be called on the core thread only. Idempotent.
568 // Also happens to take care of shutting down any async work.
mp_shutdown_clients(struct MPContext * mpctx)569 void mp_shutdown_clients(struct MPContext *mpctx)
570 {
571     struct mp_client_api *clients = mpctx->clients;
572 
573     // Forcefully abort async work after 2 seconds of waiting.
574     double abort_time = mp_time_sec() + 2;
575 
576     pthread_mutex_lock(&clients->lock);
577 
578     // Prevent that new clients can appear.
579     clients->shutting_down = true;
580 
581     // Wait until we can terminate.
582     while (clients->num_clients || mpctx->outstanding_async ||
583            !(mpctx->is_cli || clients->terminate_core_thread))
584     {
585         pthread_mutex_unlock(&clients->lock);
586 
587         double left = abort_time - mp_time_sec();
588         if (left >= 0) {
589             mp_set_timeout(mpctx, left);
590         } else {
591             // Forcefully abort any ongoing async work. This is quite rude and
592             // probably not what everyone wants, so it happens only after a
593             // timeout.
594             abort_async(mpctx, NULL, 0, 0);
595         }
596 
597         mp_client_broadcast_event(mpctx, MPV_EVENT_SHUTDOWN, NULL);
598         mp_wait_events(mpctx);
599 
600         pthread_mutex_lock(&clients->lock);
601     }
602 
603     pthread_mutex_unlock(&clients->lock);
604 }
605 
mp_is_shutting_down(struct MPContext * mpctx)606 bool mp_is_shutting_down(struct MPContext *mpctx)
607 {
608     struct mp_client_api *clients = mpctx->clients;
609     pthread_mutex_lock(&clients->lock);
610     bool res = clients->shutting_down;
611     pthread_mutex_unlock(&clients->lock);
612     return res;
613 }
614 
core_thread(void * p)615 static void *core_thread(void *p)
616 {
617     struct MPContext *mpctx = p;
618 
619     mpthread_set_name("mpv core");
620 
621     while (!mpctx->initialized && mpctx->stop_play != PT_QUIT)
622         mp_idle(mpctx);
623 
624     if (mpctx->initialized)
625         mp_play_files(mpctx);
626 
627     // This actually waits until all clients are gone before actually
628     // destroying mpctx. Actual destruction is done by whatever destroys
629     // the last mpv_handle.
630     mp_shutdown_clients(mpctx);
631 
632     return NULL;
633 }
634 
mpv_create(void)635 mpv_handle *mpv_create(void)
636 {
637     struct MPContext *mpctx = mp_create();
638     if (!mpctx)
639         return NULL;
640 
641     m_config_set_profile(mpctx->mconfig, "libmpv", 0);
642 
643     mpv_handle *ctx = mp_new_client(mpctx->clients, "main");
644     if (!ctx) {
645         mp_destroy(mpctx);
646         return NULL;
647     }
648 
649     pthread_t thread;
650     if (pthread_create(&thread, NULL, core_thread, mpctx) != 0) {
651         ctx->clients->have_terminator = true; // avoid blocking
652         mpv_terminate_destroy(ctx);
653         mp_destroy(mpctx);
654         return NULL;
655     }
656 
657     return ctx;
658 }
659 
mpv_create_client(mpv_handle * ctx,const char * name)660 mpv_handle *mpv_create_client(mpv_handle *ctx, const char *name)
661 {
662     if (!ctx)
663         return mpv_create();
664     mpv_handle *new = mp_new_client(ctx->mpctx->clients, name);
665     if (new)
666         mpv_wait_event(new, 0); // set fuzzy_initialized
667     return new;
668 }
669 
mpv_create_weak_client(mpv_handle * ctx,const char * name)670 mpv_handle *mpv_create_weak_client(mpv_handle *ctx, const char *name)
671 {
672     mpv_handle *new = mpv_create_client(ctx, name);
673     if (new)
674         mp_client_set_weak(new);
675     return new;
676 }
677 
mpv_initialize(mpv_handle * ctx)678 int mpv_initialize(mpv_handle *ctx)
679 {
680     lock_core(ctx);
681     int res = mp_initialize(ctx->mpctx, NULL) ? MPV_ERROR_INVALID_PARAMETER : 0;
682     mp_wakeup_core(ctx->mpctx);
683     unlock_core(ctx);
684     return res;
685 }
686 
687 // set ev->data to a new copy of the original data
688 // (done only for message types that are broadcast)
dup_event_data(struct mpv_event * ev)689 static void dup_event_data(struct mpv_event *ev)
690 {
691     switch (ev->event_id) {
692     case MPV_EVENT_CLIENT_MESSAGE: {
693         struct mpv_event_client_message *src = ev->data;
694         struct mpv_event_client_message *msg =
695             talloc_zero(NULL, struct mpv_event_client_message);
696         for (int n = 0; n < src->num_args; n++) {
697             MP_TARRAY_APPEND(msg, msg->args, msg->num_args,
698                              talloc_strdup(msg, src->args[n]));
699         }
700         ev->data = msg;
701         break;
702     }
703     case MPV_EVENT_START_FILE:
704         ev->data = talloc_memdup(NULL, ev->data, sizeof(mpv_event_start_file));
705         break;
706     case MPV_EVENT_END_FILE:
707         ev->data = talloc_memdup(NULL, ev->data, sizeof(mpv_event_end_file));
708         break;
709     default:
710         // Doesn't use events with memory allocation.
711         if (ev->data)
712             abort();
713     }
714 }
715 
716 // Reserve an entry in the ring buffer. This can be used to guarantee that the
717 // reply can be made, even if the buffer becomes congested _after_ sending
718 // the request.
719 // Returns an error code if the buffer is full.
reserve_reply(struct mpv_handle * ctx)720 static int reserve_reply(struct mpv_handle *ctx)
721 {
722     int res = MPV_ERROR_EVENT_QUEUE_FULL;
723     pthread_mutex_lock(&ctx->lock);
724     if (ctx->reserved_events + ctx->num_events < ctx->max_events && !ctx->choked)
725     {
726         ctx->reserved_events++;
727         res = 0;
728     }
729     pthread_mutex_unlock(&ctx->lock);
730     return res;
731 }
732 
append_event(struct mpv_handle * ctx,struct mpv_event event,bool copy)733 static int append_event(struct mpv_handle *ctx, struct mpv_event event, bool copy)
734 {
735     if (ctx->num_events + ctx->reserved_events >= ctx->max_events)
736         return -1;
737     if (copy)
738         dup_event_data(&event);
739     ctx->events[(ctx->first_event + ctx->num_events) % ctx->max_events] = event;
740     ctx->num_events++;
741     wakeup_client(ctx);
742     if (event.event_id == MPV_EVENT_SHUTDOWN)
743         ctx->event_mask &= ctx->event_mask & ~(1ULL << MPV_EVENT_SHUTDOWN);
744     return 0;
745 }
746 
send_event(struct mpv_handle * ctx,struct mpv_event * event,bool copy)747 static int send_event(struct mpv_handle *ctx, struct mpv_event *event, bool copy)
748 {
749     pthread_mutex_lock(&ctx->lock);
750     uint64_t mask = 1ULL << event->event_id;
751     if (ctx->property_event_masks & mask)
752         notify_property_events(ctx, event->event_id);
753     int r;
754     if (!(ctx->event_mask & mask)) {
755         r = 0;
756     } else if (ctx->choked) {
757         r = -1;
758     } else {
759         r = append_event(ctx, *event, copy);
760         if (r < 0) {
761             MP_ERR(ctx, "Too many events queued.\n");
762             ctx->choked = true;
763         }
764     }
765     pthread_mutex_unlock(&ctx->lock);
766     return r;
767 }
768 
769 // Send a reply; the reply must have been previously reserved with
770 // reserve_reply (otherwise, use send_event()).
send_reply(struct mpv_handle * ctx,uint64_t userdata,struct mpv_event * event)771 static void send_reply(struct mpv_handle *ctx, uint64_t userdata,
772                        struct mpv_event *event)
773 {
774     event->reply_userdata = userdata;
775     pthread_mutex_lock(&ctx->lock);
776     // If this fails, reserve_reply() probably wasn't called.
777     assert(ctx->reserved_events > 0);
778     ctx->reserved_events--;
779     if (append_event(ctx, *event, false) < 0)
780         abort(); // not reached
781     pthread_mutex_unlock(&ctx->lock);
782 }
783 
mp_client_broadcast_event(struct MPContext * mpctx,int event,void * data)784 void mp_client_broadcast_event(struct MPContext *mpctx, int event, void *data)
785 {
786     struct mp_client_api *clients = mpctx->clients;
787 
788     pthread_mutex_lock(&clients->lock);
789 
790     for (int n = 0; n < clients->num_clients; n++) {
791         struct mpv_event event_data = {
792             .event_id = event,
793             .data = data,
794         };
795         send_event(clients->clients[n], &event_data, true);
796     }
797 
798     pthread_mutex_unlock(&clients->lock);
799 }
800 
801 // Like mp_client_broadcast_event(), but can be called from any thread.
802 // Avoid using this.
mp_client_broadcast_event_external(struct mp_client_api * api,int event,void * data)803 void mp_client_broadcast_event_external(struct mp_client_api *api, int event,
804                                         void *data)
805 {
806     struct MPContext *mpctx = api->mpctx;
807 
808     mp_client_broadcast_event(mpctx, event, data);
809     mp_wakeup_core(mpctx);
810 }
811 
812 // If client_name == NULL, then broadcast and free the event.
mp_client_send_event(struct MPContext * mpctx,const char * client_name,uint64_t reply_userdata,int event,void * data)813 int mp_client_send_event(struct MPContext *mpctx, const char *client_name,
814                          uint64_t reply_userdata, int event, void *data)
815 {
816     if (!client_name) {
817         mp_client_broadcast_event(mpctx, event, data);
818         talloc_free(data);
819         return 0;
820     }
821 
822     struct mp_client_api *clients = mpctx->clients;
823     int r = 0;
824 
825     struct mpv_event event_data = {
826         .event_id = event,
827         .data = data,
828         .reply_userdata = reply_userdata,
829     };
830 
831     pthread_mutex_lock(&clients->lock);
832 
833     struct mpv_handle *ctx = find_client(clients, client_name);
834     if (ctx) {
835         r = send_event(ctx, &event_data, false);
836     } else {
837         r = -1;
838         talloc_free(data);
839     }
840 
841     pthread_mutex_unlock(&clients->lock);
842 
843     return r;
844 }
845 
mp_client_send_event_dup(struct MPContext * mpctx,const char * client_name,int event,void * data)846 int mp_client_send_event_dup(struct MPContext *mpctx, const char *client_name,
847                              int event, void *data)
848 {
849     if (!client_name) {
850         mp_client_broadcast_event(mpctx, event, data);
851         return 0;
852     }
853 
854     struct mpv_event event_data = {
855         .event_id = event,
856         .data = data,
857     };
858 
859     dup_event_data(&event_data);
860     return mp_client_send_event(mpctx, client_name, 0, event, event_data.data);
861 }
862 
863 static bool deprecated_events[] = {
864     [MPV_EVENT_TRACKS_CHANGED] = true,
865     [MPV_EVENT_TRACK_SWITCHED] = true,
866     [MPV_EVENT_IDLE] = true,
867     [MPV_EVENT_PAUSE] = true,
868     [MPV_EVENT_UNPAUSE] = true,
869     [MPV_EVENT_TICK] = true,
870     [MPV_EVENT_SCRIPT_INPUT_DISPATCH] = true,
871     [MPV_EVENT_METADATA_UPDATE] = true,
872     [MPV_EVENT_CHAPTER_CHANGE] = true,
873 };
874 
mpv_request_event(mpv_handle * ctx,mpv_event_id event,int enable)875 int mpv_request_event(mpv_handle *ctx, mpv_event_id event, int enable)
876 {
877     if (!mpv_event_name(event) || enable < 0 || enable > 1)
878         return MPV_ERROR_INVALID_PARAMETER;
879     if (event == MPV_EVENT_SHUTDOWN && !enable)
880         return MPV_ERROR_INVALID_PARAMETER;
881     assert(event < (int)INTERNAL_EVENT_BASE); // excluded above; they have no name
882     pthread_mutex_lock(&ctx->lock);
883     uint64_t bit = 1ULL << event;
884     ctx->event_mask = enable ? ctx->event_mask | bit : ctx->event_mask & ~bit;
885     if (enable && event < MP_ARRAY_SIZE(deprecated_events) &&
886         deprecated_events[event])
887     {
888         MP_WARN(ctx, "The '%s' event is deprecated and will be removed.\n",
889                 mpv_event_name(event));
890     }
891     pthread_mutex_unlock(&ctx->lock);
892     return 0;
893 }
894 
895 // Set waiting_for_hook==true for all possibly pending properties.
set_wait_for_hook_flags(mpv_handle * ctx)896 static void set_wait_for_hook_flags(mpv_handle *ctx)
897 {
898     for (int n = 0; n < ctx->num_properties; n++) {
899         struct observe_property *prop = ctx->properties[n];
900 
901         if (prop->value_ret_ts != prop->change_ts)
902             prop->waiting_for_hook = true;
903     }
904 }
905 
906 // Return whether any property still has waiting_for_hook set.
check_for_for_hook_flags(mpv_handle * ctx)907 static bool check_for_for_hook_flags(mpv_handle *ctx)
908 {
909     for (int n = 0; n < ctx->num_properties; n++) {
910         if (ctx->properties[n]->waiting_for_hook)
911             return true;
912     }
913     return false;
914 }
915 
mpv_wait_event(mpv_handle * ctx,double timeout)916 mpv_event *mpv_wait_event(mpv_handle *ctx, double timeout)
917 {
918     mpv_event *event = ctx->cur_event;
919 
920     pthread_mutex_lock(&ctx->lock);
921 
922     if (!ctx->fuzzy_initialized)
923         mp_wakeup_core(ctx->clients->mpctx);
924     ctx->fuzzy_initialized = true;
925 
926     if (timeout < 0)
927         timeout = 1e20;
928 
929     int64_t deadline = mp_add_timeout(mp_time_us(), timeout);
930 
931     *event = (mpv_event){0};
932     talloc_free_children(event);
933 
934     while (1) {
935         if (ctx->queued_wakeup)
936             deadline = 0;
937         // Recover from overflow.
938         if (ctx->choked && !ctx->num_events) {
939             ctx->choked = false;
940             event->event_id = MPV_EVENT_QUEUE_OVERFLOW;
941             break;
942         }
943         struct mpv_event *ev =
944             ctx->num_events ? &ctx->events[ctx->first_event] : NULL;
945         if (ev && ev->event_id == MPV_EVENT_HOOK) {
946             // Give old property notifications priority over hooks. This is a
947             // guarantee given to clients to simplify their logic. New property
948             // changes after this are treated normally, so
949             if (!ctx->hook_pending) {
950                 ctx->hook_pending = true;
951                 set_wait_for_hook_flags(ctx);
952             }
953             if (check_for_for_hook_flags(ctx)) {
954                 ev = NULL; // delay
955             } else {
956                 ctx->hook_pending = false;
957             }
958         }
959         if (ev) {
960             *event = *ev;
961             ctx->first_event = (ctx->first_event + 1) % ctx->max_events;
962             ctx->num_events--;
963             talloc_steal(event, event->data);
964             break;
965         }
966         // If there's a changed property, generate change event (never queued).
967         if (gen_property_change_event(ctx))
968             break;
969         // Pop item from message queue, and return as event.
970         if (gen_log_message_event(ctx))
971             break;
972         int r = wait_wakeup(ctx, deadline);
973         if (r == ETIMEDOUT)
974             break;
975     }
976     ctx->queued_wakeup = false;
977 
978     pthread_mutex_unlock(&ctx->lock);
979 
980     return event;
981 }
982 
mpv_wakeup(mpv_handle * ctx)983 void mpv_wakeup(mpv_handle *ctx)
984 {
985     pthread_mutex_lock(&ctx->lock);
986     ctx->queued_wakeup = true;
987     wakeup_client(ctx);
988     pthread_mutex_unlock(&ctx->lock);
989 }
990 
991 // map client API types to internal types
992 static const struct m_option type_conv[] = {
993     [MPV_FORMAT_STRING]     = { .type = CONF_TYPE_STRING },
994     [MPV_FORMAT_FLAG]       = { .type = CONF_TYPE_FLAG },
995     [MPV_FORMAT_INT64]      = { .type = CONF_TYPE_INT64 },
996     [MPV_FORMAT_DOUBLE]     = { .type = CONF_TYPE_DOUBLE },
997     [MPV_FORMAT_NODE]       = { .type = CONF_TYPE_NODE },
998 };
999 
get_mp_type(mpv_format format)1000 static const struct m_option *get_mp_type(mpv_format format)
1001 {
1002     if ((unsigned)format >= MP_ARRAY_SIZE(type_conv))
1003         return NULL;
1004     if (!type_conv[format].type)
1005         return NULL;
1006     return &type_conv[format];
1007 }
1008 
1009 // for read requests - MPV_FORMAT_OSD_STRING special handling
get_mp_type_get(mpv_format format)1010 static const struct m_option *get_mp_type_get(mpv_format format)
1011 {
1012     if (format == MPV_FORMAT_OSD_STRING)
1013         format = MPV_FORMAT_STRING; // it's string data, just other semantics
1014     return get_mp_type(format);
1015 }
1016 
1017 // move src->dst, and do implicit conversion if possible (conversions to or
1018 // from strings are handled otherwise)
conv_node_to_format(void * dst,mpv_format dst_fmt,mpv_node * src)1019 static bool conv_node_to_format(void *dst, mpv_format dst_fmt, mpv_node *src)
1020 {
1021     if (dst_fmt == src->format) {
1022         const struct m_option *type = get_mp_type(dst_fmt);
1023         memcpy(dst, &src->u, type->type->size);
1024         return true;
1025     }
1026     if (dst_fmt == MPV_FORMAT_DOUBLE && src->format == MPV_FORMAT_INT64) {
1027         *(double *)dst = src->u.int64;
1028         return true;
1029     }
1030     if (dst_fmt == MPV_FORMAT_INT64 && src->format == MPV_FORMAT_DOUBLE) {
1031         if (src->u.double_ > (double)INT64_MIN &&
1032             src->u.double_ < (double)INT64_MAX)
1033         {
1034             *(int64_t *)dst = src->u.double_;
1035             return true;
1036         }
1037     }
1038     return false;
1039 }
1040 
mpv_free_node_contents(mpv_node * node)1041 void mpv_free_node_contents(mpv_node *node)
1042 {
1043     static const struct m_option type = { .type = CONF_TYPE_NODE };
1044     m_option_free(&type, node);
1045 }
1046 
mpv_set_option(mpv_handle * ctx,const char * name,mpv_format format,void * data)1047 int mpv_set_option(mpv_handle *ctx, const char *name, mpv_format format,
1048                    void *data)
1049 {
1050     const struct m_option *type = get_mp_type(format);
1051     if (!type)
1052         return MPV_ERROR_OPTION_FORMAT;
1053     struct mpv_node tmp;
1054     if (format != MPV_FORMAT_NODE) {
1055         tmp.format = format;
1056         memcpy(&tmp.u, data, type->type->size);
1057         data = &tmp;
1058     }
1059     lock_core(ctx);
1060     int err = m_config_set_option_node(ctx->mpctx->mconfig, bstr0(name), data, 0);
1061     unlock_core(ctx);
1062     switch (err) {
1063     case M_OPT_MISSING_PARAM:
1064     case M_OPT_INVALID:
1065         return MPV_ERROR_OPTION_ERROR;
1066     case M_OPT_OUT_OF_RANGE:
1067         return MPV_ERROR_OPTION_FORMAT;
1068     case M_OPT_UNKNOWN:
1069         return MPV_ERROR_OPTION_NOT_FOUND;
1070     default:
1071         if (err >= 0)
1072             return 0;
1073         return MPV_ERROR_OPTION_ERROR;
1074     }
1075 }
1076 
mpv_set_option_string(mpv_handle * ctx,const char * name,const char * data)1077 int mpv_set_option_string(mpv_handle *ctx, const char *name, const char *data)
1078 {
1079     return mpv_set_option(ctx, name, MPV_FORMAT_STRING, &data);
1080 }
1081 
1082 // Run a command in the playback thread.
run_locked(mpv_handle * ctx,void (* fn)(void * fn_data),void * fn_data)1083 static void run_locked(mpv_handle *ctx, void (*fn)(void *fn_data), void *fn_data)
1084 {
1085     mp_dispatch_lock(ctx->mpctx->dispatch);
1086     fn(fn_data);
1087     mp_dispatch_unlock(ctx->mpctx->dispatch);
1088 }
1089 
1090 // Run a command asynchronously. It's the responsibility of the caller to
1091 // actually send the reply. This helper merely saves a small part of the
1092 // required boilerplate to do so.
1093 //  fn: callback to execute the request
1094 //  fn_data: opaque caller-defined argument for fn. This will be automatically
1095 //           freed with talloc_free(fn_data).
run_async(mpv_handle * ctx,void (* fn)(void * fn_data),void * fn_data)1096 static int run_async(mpv_handle *ctx, void (*fn)(void *fn_data), void *fn_data)
1097 {
1098     int err = reserve_reply(ctx);
1099     if (err < 0) {
1100         talloc_free(fn_data);
1101         return err;
1102     }
1103     mp_dispatch_enqueue(ctx->mpctx->dispatch, fn, fn_data);
1104     return 0;
1105 }
1106 
1107 struct cmd_request {
1108     struct MPContext *mpctx;
1109     struct mp_cmd *cmd;
1110     int status;
1111     struct mpv_node *res;
1112     struct mp_waiter completion;
1113 };
1114 
cmd_complete(struct mp_cmd_ctx * cmd)1115 static void cmd_complete(struct mp_cmd_ctx *cmd)
1116 {
1117     struct cmd_request *req = cmd->on_completion_priv;
1118 
1119     req->status = cmd->success ? 0 : MPV_ERROR_COMMAND;
1120     if (req->res) {
1121         *req->res = cmd->result;
1122         cmd->result = (mpv_node){0};
1123     }
1124 
1125     // Unblock the waiting thread (especially for async commands).
1126     mp_waiter_wakeup(&req->completion, 0);
1127 }
1128 
run_client_command(mpv_handle * ctx,struct mp_cmd * cmd,mpv_node * res)1129 static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res)
1130 {
1131     if (!cmd)
1132         return MPV_ERROR_INVALID_PARAMETER;
1133     if (!ctx->mpctx->initialized) {
1134         talloc_free(cmd);
1135         return MPV_ERROR_UNINITIALIZED;
1136     }
1137 
1138     cmd->sender = ctx->name;
1139 
1140     struct cmd_request req = {
1141         .mpctx = ctx->mpctx,
1142         .cmd = cmd,
1143         .res = res,
1144         .completion = MP_WAITER_INITIALIZER,
1145     };
1146 
1147     bool async = cmd->flags & MP_ASYNC_CMD;
1148 
1149     lock_core(ctx);
1150     if (async) {
1151         run_command(ctx->mpctx, cmd, NULL, NULL, NULL);
1152     } else {
1153         struct mp_abort_entry *abort = NULL;
1154         if (cmd->def->can_abort) {
1155             abort = talloc_zero(NULL, struct mp_abort_entry);
1156             abort->client = ctx;
1157         }
1158         run_command(ctx->mpctx, cmd, abort, cmd_complete, &req);
1159     }
1160     unlock_core(ctx);
1161 
1162     if (!async)
1163         mp_waiter_wait(&req.completion);
1164 
1165     return req.status;
1166 }
1167 
mpv_command(mpv_handle * ctx,const char ** args)1168 int mpv_command(mpv_handle *ctx, const char **args)
1169 {
1170     return run_client_command(ctx, mp_input_parse_cmd_strv(ctx->log, args), NULL);
1171 }
1172 
mpv_command_node(mpv_handle * ctx,mpv_node * args,mpv_node * result)1173 int mpv_command_node(mpv_handle *ctx, mpv_node *args, mpv_node *result)
1174 {
1175     struct mpv_node rn = {.format = MPV_FORMAT_NONE};
1176     int r = run_client_command(ctx, mp_input_parse_cmd_node(ctx->log, args), &rn);
1177     if (result && r >= 0)
1178         *result = rn;
1179     return r;
1180 }
1181 
mpv_command_ret(mpv_handle * ctx,const char ** args,mpv_node * result)1182 int mpv_command_ret(mpv_handle *ctx, const char **args, mpv_node *result)
1183 {
1184     struct mpv_node rn = {.format = MPV_FORMAT_NONE};
1185     int r = run_client_command(ctx, mp_input_parse_cmd_strv(ctx->log, args), &rn);
1186     if (result && r >= 0)
1187         *result = rn;
1188     return r;
1189 }
1190 
mpv_command_string(mpv_handle * ctx,const char * args)1191 int mpv_command_string(mpv_handle *ctx, const char *args)
1192 {
1193     return run_client_command(ctx,
1194         mp_input_parse_cmd(ctx->mpctx->input, bstr0((char*)args), ctx->name), NULL);
1195 }
1196 
1197 struct async_cmd_request {
1198     struct MPContext *mpctx;
1199     struct mp_cmd *cmd;
1200     struct mpv_handle *reply_ctx;
1201     uint64_t userdata;
1202 };
1203 
async_cmd_complete(struct mp_cmd_ctx * cmd)1204 static void async_cmd_complete(struct mp_cmd_ctx *cmd)
1205 {
1206     struct async_cmd_request *req = cmd->on_completion_priv;
1207 
1208     struct mpv_event_command *data = talloc_zero(NULL, struct mpv_event_command);
1209     data->result = cmd->result;
1210     cmd->result = (mpv_node){0};
1211     talloc_steal(data, node_get_alloc(&data->result));
1212 
1213     struct mpv_event reply = {
1214         .event_id = MPV_EVENT_COMMAND_REPLY,
1215         .data = data,
1216         .error = cmd->success ? 0 : MPV_ERROR_COMMAND,
1217     };
1218     send_reply(req->reply_ctx, req->userdata, &reply);
1219 
1220     talloc_free(req);
1221 }
1222 
async_cmd_fn(void * data)1223 static void async_cmd_fn(void *data)
1224 {
1225     struct async_cmd_request *req = data;
1226 
1227     struct mp_cmd *cmd = req->cmd;
1228     ta_set_parent(cmd, NULL);
1229     req->cmd = NULL;
1230 
1231     struct mp_abort_entry *abort = NULL;
1232     if (cmd->def->can_abort) {
1233         abort = talloc_zero(NULL, struct mp_abort_entry);
1234         abort->client = req->reply_ctx;
1235         abort->client_work_type = MPV_EVENT_COMMAND_REPLY;
1236         abort->client_work_id = req->userdata;
1237     }
1238 
1239     // This will synchronously or asynchronously call cmd_complete (depending
1240     // on the command).
1241     run_command(req->mpctx, cmd, abort, async_cmd_complete, req);
1242 }
1243 
run_async_cmd(mpv_handle * ctx,uint64_t ud,struct mp_cmd * cmd)1244 static int run_async_cmd(mpv_handle *ctx, uint64_t ud, struct mp_cmd *cmd)
1245 {
1246     if (!cmd)
1247         return MPV_ERROR_INVALID_PARAMETER;
1248     if (!ctx->mpctx->initialized) {
1249         talloc_free(cmd);
1250         return MPV_ERROR_UNINITIALIZED;
1251     }
1252 
1253     cmd->sender = ctx->name;
1254 
1255     struct async_cmd_request *req = talloc_ptrtype(NULL, req);
1256     *req = (struct async_cmd_request){
1257         .mpctx = ctx->mpctx,
1258         .cmd = talloc_steal(req, cmd),
1259         .reply_ctx = ctx,
1260         .userdata = ud,
1261     };
1262     return run_async(ctx, async_cmd_fn, req);
1263 }
1264 
mpv_command_async(mpv_handle * ctx,uint64_t ud,const char ** args)1265 int mpv_command_async(mpv_handle *ctx, uint64_t ud, const char **args)
1266 {
1267     return run_async_cmd(ctx, ud, mp_input_parse_cmd_strv(ctx->log, args));
1268 }
1269 
mpv_command_node_async(mpv_handle * ctx,uint64_t ud,mpv_node * args)1270 int mpv_command_node_async(mpv_handle *ctx, uint64_t ud, mpv_node *args)
1271 {
1272     return run_async_cmd(ctx, ud, mp_input_parse_cmd_node(ctx->log, args));
1273 }
1274 
mpv_abort_async_command(mpv_handle * ctx,uint64_t reply_userdata)1275 void mpv_abort_async_command(mpv_handle *ctx, uint64_t reply_userdata)
1276 {
1277     abort_async(ctx->mpctx, ctx, MPV_EVENT_COMMAND_REPLY, reply_userdata);
1278 }
1279 
translate_property_error(int errc)1280 static int translate_property_error(int errc)
1281 {
1282     switch (errc) {
1283     case M_PROPERTY_OK:                 return 0;
1284     case M_PROPERTY_ERROR:              return MPV_ERROR_PROPERTY_ERROR;
1285     case M_PROPERTY_UNAVAILABLE:        return MPV_ERROR_PROPERTY_UNAVAILABLE;
1286     case M_PROPERTY_NOT_IMPLEMENTED:    return MPV_ERROR_PROPERTY_ERROR;
1287     case M_PROPERTY_UNKNOWN:            return MPV_ERROR_PROPERTY_NOT_FOUND;
1288     case M_PROPERTY_INVALID_FORMAT:     return MPV_ERROR_PROPERTY_FORMAT;
1289     // shouldn't happen
1290     default:                            return MPV_ERROR_PROPERTY_ERROR;
1291     }
1292 }
1293 
1294 struct setproperty_request {
1295     struct MPContext *mpctx;
1296     const char *name;
1297     int format;
1298     void *data;
1299     int status;
1300     struct mpv_handle *reply_ctx;
1301     uint64_t userdata;
1302 };
1303 
setproperty_fn(void * arg)1304 static void setproperty_fn(void *arg)
1305 {
1306     struct setproperty_request *req = arg;
1307     const struct m_option *type = get_mp_type(req->format);
1308 
1309     struct mpv_node *node;
1310     struct mpv_node tmp;
1311     if (req->format == MPV_FORMAT_NODE) {
1312         node = req->data;
1313     } else {
1314         tmp.format = req->format;
1315         memcpy(&tmp.u, req->data, type->type->size);
1316         node = &tmp;
1317     }
1318 
1319     int err = mp_property_do(req->name, M_PROPERTY_SET_NODE, node, req->mpctx);
1320 
1321     req->status = translate_property_error(err);
1322 
1323     if (req->reply_ctx) {
1324         struct mpv_event reply = {
1325             .event_id = MPV_EVENT_SET_PROPERTY_REPLY,
1326             .error = req->status,
1327         };
1328         send_reply(req->reply_ctx, req->userdata, &reply);
1329         talloc_free(req);
1330     }
1331 }
1332 
mpv_set_property(mpv_handle * ctx,const char * name,mpv_format format,void * data)1333 int mpv_set_property(mpv_handle *ctx, const char *name, mpv_format format,
1334                      void *data)
1335 {
1336     if (!ctx->mpctx->initialized) {
1337         int r = mpv_set_option(ctx, name, format, data);
1338         if (r == MPV_ERROR_OPTION_NOT_FOUND &&
1339             mp_get_property_id(ctx->mpctx, name) >= 0)
1340             return MPV_ERROR_PROPERTY_UNAVAILABLE;
1341         switch (r) {
1342         case MPV_ERROR_SUCCESS:          return MPV_ERROR_SUCCESS;
1343         case MPV_ERROR_OPTION_FORMAT:    return MPV_ERROR_PROPERTY_FORMAT;
1344         case MPV_ERROR_OPTION_NOT_FOUND: return MPV_ERROR_PROPERTY_NOT_FOUND;
1345         default:                         return MPV_ERROR_PROPERTY_ERROR;
1346         }
1347     }
1348     if (!get_mp_type(format))
1349         return MPV_ERROR_PROPERTY_FORMAT;
1350 
1351     struct setproperty_request req = {
1352         .mpctx = ctx->mpctx,
1353         .name = name,
1354         .format = format,
1355         .data = data,
1356     };
1357     run_locked(ctx, setproperty_fn, &req);
1358     return req.status;
1359 }
1360 
mpv_set_property_string(mpv_handle * ctx,const char * name,const char * data)1361 int mpv_set_property_string(mpv_handle *ctx, const char *name, const char *data)
1362 {
1363     return mpv_set_property(ctx, name, MPV_FORMAT_STRING, &data);
1364 }
1365 
free_prop_set_req(void * ptr)1366 static void free_prop_set_req(void *ptr)
1367 {
1368     struct setproperty_request *req = ptr;
1369     const struct m_option *type = get_mp_type(req->format);
1370     m_option_free(type, req->data);
1371 }
1372 
mpv_set_property_async(mpv_handle * ctx,uint64_t ud,const char * name,mpv_format format,void * data)1373 int mpv_set_property_async(mpv_handle *ctx, uint64_t ud, const char *name,
1374                            mpv_format format, void *data)
1375 {
1376     const struct m_option *type = get_mp_type(format);
1377     if (!ctx->mpctx->initialized)
1378         return MPV_ERROR_UNINITIALIZED;
1379     if (!type)
1380         return MPV_ERROR_PROPERTY_FORMAT;
1381 
1382     struct setproperty_request *req = talloc_ptrtype(NULL, req);
1383     *req = (struct setproperty_request){
1384         .mpctx = ctx->mpctx,
1385         .name = talloc_strdup(req, name),
1386         .format = format,
1387         .data = talloc_zero_size(req, type->type->size),
1388         .reply_ctx = ctx,
1389         .userdata = ud,
1390     };
1391 
1392     m_option_copy(type, req->data, data);
1393     talloc_set_destructor(req, free_prop_set_req);
1394 
1395     return run_async(ctx, setproperty_fn, req);
1396 }
1397 
1398 struct getproperty_request {
1399     struct MPContext *mpctx;
1400     const char *name;
1401     mpv_format format;
1402     void *data;
1403     int status;
1404     struct mpv_handle *reply_ctx;
1405     uint64_t userdata;
1406 };
1407 
free_prop_data(void * ptr)1408 static void free_prop_data(void *ptr)
1409 {
1410     struct mpv_event_property *prop = ptr;
1411     const struct m_option *type = get_mp_type_get(prop->format);
1412     m_option_free(type, prop->data);
1413 }
1414 
getproperty_fn(void * arg)1415 static void getproperty_fn(void *arg)
1416 {
1417     struct getproperty_request *req = arg;
1418     const struct m_option *type = get_mp_type_get(req->format);
1419 
1420     union m_option_value xdata = {0};
1421     void *data = req->data ? req->data : &xdata;
1422 
1423     int err = -1;
1424     switch (req->format) {
1425     case MPV_FORMAT_OSD_STRING:
1426         err = mp_property_do(req->name, M_PROPERTY_PRINT, data, req->mpctx);
1427         break;
1428     case MPV_FORMAT_STRING: {
1429         char *s = NULL;
1430         err = mp_property_do(req->name, M_PROPERTY_GET_STRING, &s, req->mpctx);
1431         if (err == M_PROPERTY_OK)
1432             *(char **)data = s;
1433         break;
1434     }
1435     case MPV_FORMAT_NODE:
1436     case MPV_FORMAT_FLAG:
1437     case MPV_FORMAT_INT64:
1438     case MPV_FORMAT_DOUBLE: {
1439         struct mpv_node node = {{0}};
1440         err = mp_property_do(req->name, M_PROPERTY_GET_NODE, &node, req->mpctx);
1441         if (err == M_PROPERTY_NOT_IMPLEMENTED) {
1442             // Go through explicit string conversion. Same reasoning as on the
1443             // GET code path.
1444             char *s = NULL;
1445             err = mp_property_do(req->name, M_PROPERTY_GET_STRING, &s,
1446                                  req->mpctx);
1447             if (err != M_PROPERTY_OK)
1448                 break;
1449             node.format = MPV_FORMAT_STRING;
1450             node.u.string = s;
1451         } else if (err <= 0)
1452             break;
1453         if (req->format == MPV_FORMAT_NODE) {
1454             *(struct mpv_node *)data = node;
1455         } else {
1456             if (!conv_node_to_format(data, req->format, &node)) {
1457                 err = M_PROPERTY_INVALID_FORMAT;
1458                 mpv_free_node_contents(&node);
1459             }
1460         }
1461         break;
1462     }
1463     default:
1464         abort();
1465     }
1466 
1467     req->status = translate_property_error(err);
1468 
1469     if (req->reply_ctx) {
1470         struct mpv_event_property *prop = talloc_ptrtype(NULL, prop);
1471         *prop = (struct mpv_event_property){
1472             .name = talloc_steal(prop, (char *)req->name),
1473             .format = req->format,
1474             .data = talloc_size(prop, type->type->size),
1475         };
1476         // move data
1477         memcpy(prop->data, &xdata, type->type->size);
1478         talloc_set_destructor(prop, free_prop_data);
1479         struct mpv_event reply = {
1480             .event_id = MPV_EVENT_GET_PROPERTY_REPLY,
1481             .data = prop,
1482             .error = req->status,
1483         };
1484         send_reply(req->reply_ctx, req->userdata, &reply);
1485         talloc_free(req);
1486     }
1487 }
1488 
mpv_get_property(mpv_handle * ctx,const char * name,mpv_format format,void * data)1489 int mpv_get_property(mpv_handle *ctx, const char *name, mpv_format format,
1490                      void *data)
1491 {
1492     if (!ctx->mpctx->initialized)
1493         return MPV_ERROR_UNINITIALIZED;
1494     if (!data)
1495         return MPV_ERROR_INVALID_PARAMETER;
1496     if (!get_mp_type_get(format))
1497         return MPV_ERROR_PROPERTY_FORMAT;
1498 
1499     struct getproperty_request req = {
1500         .mpctx = ctx->mpctx,
1501         .name = name,
1502         .format = format,
1503         .data = data,
1504     };
1505     run_locked(ctx, getproperty_fn, &req);
1506     return req.status;
1507 }
1508 
mpv_get_property_string(mpv_handle * ctx,const char * name)1509 char *mpv_get_property_string(mpv_handle *ctx, const char *name)
1510 {
1511     char *str = NULL;
1512     mpv_get_property(ctx, name, MPV_FORMAT_STRING, &str);
1513     return str;
1514 }
1515 
mpv_get_property_osd_string(mpv_handle * ctx,const char * name)1516 char *mpv_get_property_osd_string(mpv_handle *ctx, const char *name)
1517 {
1518     char *str = NULL;
1519     mpv_get_property(ctx, name, MPV_FORMAT_OSD_STRING, &str);
1520     return str;
1521 }
1522 
mpv_get_property_async(mpv_handle * ctx,uint64_t ud,const char * name,mpv_format format)1523 int mpv_get_property_async(mpv_handle *ctx, uint64_t ud, const char *name,
1524                            mpv_format format)
1525 {
1526     if (!ctx->mpctx->initialized)
1527         return MPV_ERROR_UNINITIALIZED;
1528     if (!get_mp_type_get(format))
1529         return MPV_ERROR_PROPERTY_FORMAT;
1530 
1531     struct getproperty_request *req = talloc_ptrtype(NULL, req);
1532     *req = (struct getproperty_request){
1533         .mpctx = ctx->mpctx,
1534         .name = talloc_strdup(req, name),
1535         .format = format,
1536         .reply_ctx = ctx,
1537         .userdata = ud,
1538     };
1539     return run_async(ctx, getproperty_fn, req);
1540 }
1541 
property_free(void * p)1542 static void property_free(void *p)
1543 {
1544     struct observe_property *prop = p;
1545 
1546     assert(prop->refcount == 0);
1547 
1548     if (prop->type) {
1549         m_option_free(prop->type, &prop->value);
1550         m_option_free(prop->type, &prop->value_ret);
1551     }
1552 }
1553 
mpv_observe_property(mpv_handle * ctx,uint64_t userdata,const char * name,mpv_format format)1554 int mpv_observe_property(mpv_handle *ctx, uint64_t userdata,
1555                          const char *name, mpv_format format)
1556 {
1557     const struct m_option *type = get_mp_type_get(format);
1558     if (format != MPV_FORMAT_NONE && !type)
1559         return MPV_ERROR_PROPERTY_FORMAT;
1560     // Explicitly disallow this, because it would require a special code path.
1561     if (format == MPV_FORMAT_OSD_STRING)
1562         return MPV_ERROR_PROPERTY_FORMAT;
1563 
1564     pthread_mutex_lock(&ctx->lock);
1565     assert(!ctx->destroying);
1566     struct observe_property *prop = talloc_ptrtype(ctx, prop);
1567     talloc_set_destructor(prop, property_free);
1568     *prop = (struct observe_property){
1569         .owner = ctx,
1570         .name = talloc_strdup(prop, name),
1571         .id = mp_get_property_id(ctx->mpctx, name),
1572         .event_mask = mp_get_property_event_mask(name),
1573         .reply_id = userdata,
1574         .format = format,
1575         .type = type,
1576         .change_ts = 1, // force initial event
1577         .refcount = 1,
1578     };
1579     ctx->properties_change_ts += 1;
1580     MP_TARRAY_APPEND(ctx, ctx->properties, ctx->num_properties, prop);
1581     ctx->property_event_masks |= prop->event_mask;
1582     ctx->new_property_events = true;
1583     ctx->cur_property_index = 0;
1584     ctx->has_pending_properties = true;
1585     pthread_mutex_unlock(&ctx->lock);
1586     mp_wakeup_core(ctx->mpctx);
1587     return 0;
1588 }
1589 
mpv_unobserve_property(mpv_handle * ctx,uint64_t userdata)1590 int mpv_unobserve_property(mpv_handle *ctx, uint64_t userdata)
1591 {
1592     pthread_mutex_lock(&ctx->lock);
1593     int count = 0;
1594     for (int n = ctx->num_properties - 1; n >= 0; n--) {
1595         struct observe_property *prop = ctx->properties[n];
1596         // Perform actual removal of the property lazily to avoid creating
1597         // dangling pointers and such.
1598         if (prop->reply_id == userdata) {
1599             prop_unref(prop);
1600             ctx->properties_change_ts += 1;
1601             MP_TARRAY_REMOVE_AT(ctx->properties, ctx->num_properties, n);
1602             ctx->cur_property_index = 0;
1603             count++;
1604         }
1605     }
1606     pthread_mutex_unlock(&ctx->lock);
1607     return count;
1608 }
1609 
1610 // Broadcast that a property has changed.
mp_client_property_change(struct MPContext * mpctx,const char * name)1611 void mp_client_property_change(struct MPContext *mpctx, const char *name)
1612 {
1613     struct mp_client_api *clients = mpctx->clients;
1614     int id = mp_get_property_id(mpctx, name);
1615     bool any_pending = false;
1616 
1617     pthread_mutex_lock(&clients->lock);
1618 
1619     for (int n = 0; n < clients->num_clients; n++) {
1620         struct mpv_handle *client = clients->clients[n];
1621         pthread_mutex_lock(&client->lock);
1622         for (int i = 0; i < client->num_properties; i++) {
1623             if (client->properties[i]->id == id) {
1624                 client->properties[i]->change_ts += 1;
1625                 client->has_pending_properties = true;
1626                 any_pending = true;
1627             }
1628         }
1629         pthread_mutex_unlock(&client->lock);
1630     }
1631 
1632     pthread_mutex_unlock(&clients->lock);
1633 
1634     // If we're inside mp_dispatch_queue_process(), this will cause the playloop
1635     // to be re-run (to get mp_client_send_property_changes() called). If we're
1636     // inside the normal playloop, this does nothing, but the latter function
1637     // will be called at the end of the playloop anyway.
1638     if (any_pending)
1639         mp_dispatch_adjust_timeout(mpctx->dispatch, 0);
1640 }
1641 
1642 // Mark properties as changed in reaction to specific events.
1643 // Called with ctx->lock held.
notify_property_events(struct mpv_handle * ctx,int event)1644 static void notify_property_events(struct mpv_handle *ctx, int event)
1645 {
1646     uint64_t mask = 1ULL << event;
1647     for (int i = 0; i < ctx->num_properties; i++) {
1648         if (ctx->properties[i]->event_mask & mask) {
1649             ctx->properties[i]->change_ts += 1;
1650             ctx->has_pending_properties = true;
1651         }
1652     }
1653 
1654     // Same as in mp_client_property_change().
1655     if (ctx->has_pending_properties)
1656         mp_dispatch_adjust_timeout(ctx->mpctx->dispatch, 0);
1657 }
1658 
1659 // Call with ctx->lock held (only). May temporarily drop the lock.
send_client_property_changes(struct mpv_handle * ctx)1660 static void send_client_property_changes(struct mpv_handle *ctx)
1661 {
1662     uint64_t cur_ts = ctx->properties_change_ts;
1663 
1664     ctx->has_pending_properties = false;
1665 
1666     for (int n = 0; n < ctx->num_properties; n++) {
1667         struct observe_property *prop = ctx->properties[n];
1668 
1669         if (prop->value_ts == prop->change_ts)
1670             continue;
1671 
1672         bool changed = false;
1673         if (prop->format) {
1674             const struct m_option *type = prop->type;
1675             union m_option_value val = {0};
1676             struct getproperty_request req = {
1677                 .mpctx = ctx->mpctx,
1678                 .name = prop->name,
1679                 .format = prop->format,
1680                 .data = &val,
1681             };
1682 
1683             // Temporarily unlock and read the property. The very important
1684             // thing is that property getters can do whatever they want, _and_
1685             // that they may wait on the client API user thread (if vo_libmpv
1686             // or similar things are involved).
1687             prop->refcount += 1; // keep prop alive (esp. prop->name)
1688             ctx->async_counter += 1; // keep ctx alive
1689             pthread_mutex_unlock(&ctx->lock);
1690             getproperty_fn(&req);
1691             pthread_mutex_lock(&ctx->lock);
1692             ctx->async_counter -= 1;
1693             prop_unref(prop);
1694 
1695             // Set if observed properties was changed or something similar
1696             // => start over, retry next time.
1697             if (cur_ts != ctx->properties_change_ts || ctx->destroying) {
1698                 m_option_free(type, &val);
1699                 mp_wakeup_core(ctx->mpctx);
1700                 ctx->has_pending_properties = true;
1701                 break;
1702             }
1703             assert(prop->refcount > 0);
1704 
1705             bool val_valid = req.status >= 0;
1706             changed = prop->value_valid != val_valid;
1707             if (prop->value_valid && val_valid)
1708                 changed = !equal_mpv_value(&prop->value, &val, prop->format);
1709             if (prop->value_ts == 0)
1710                 changed = true; // initial event
1711 
1712             prop->value_valid = val_valid;
1713             if (changed && val_valid) {
1714                 // move val to prop->value
1715                 m_option_free(type, &prop->value);
1716                 memcpy(&prop->value, &val, type->type->size);
1717                 memset(&val, 0, type->type->size);
1718             }
1719 
1720             m_option_free(prop->type, &val);
1721         } else {
1722             changed = true;
1723         }
1724 
1725         if (prop->waiting_for_hook)
1726             ctx->new_property_events = true; // make sure to wakeup
1727 
1728         // Avoid retriggering the change event if the property didn't change,
1729         // and the previous value was actually returned to the client.
1730         if (!changed && prop->value_ret_ts == prop->value_ts) {
1731             prop->value_ret_ts = prop->change_ts; // no change => no event
1732             prop->waiting_for_hook = false;
1733         } else {
1734             ctx->new_property_events = true;
1735         }
1736 
1737         prop->value_ts = prop->change_ts;
1738     }
1739 
1740     if (ctx->destroying || ctx->new_property_events)
1741         wakeup_client(ctx);
1742 }
1743 
mp_client_send_property_changes(struct MPContext * mpctx)1744 void mp_client_send_property_changes(struct MPContext *mpctx)
1745 {
1746     struct mp_client_api *clients = mpctx->clients;
1747 
1748     pthread_mutex_lock(&clients->lock);
1749     uint64_t cur_ts = clients->clients_list_change_ts;
1750 
1751     for (int n = 0; n < clients->num_clients; n++) {
1752         struct mpv_handle *ctx = clients->clients[n];
1753 
1754         pthread_mutex_lock(&ctx->lock);
1755         if (!ctx->has_pending_properties || ctx->destroying) {
1756             pthread_mutex_unlock(&ctx->lock);
1757             continue;
1758         }
1759         // Keep ctx->lock locked (unlock order does not matter).
1760         pthread_mutex_unlock(&clients->lock);
1761         send_client_property_changes(ctx);
1762         pthread_mutex_unlock(&ctx->lock);
1763         pthread_mutex_lock(&clients->lock);
1764         if (cur_ts != clients->clients_list_change_ts) {
1765             // List changed; need to start over. Do it in the next iteration.
1766             mp_wakeup_core(mpctx);
1767             break;
1768         }
1769     }
1770 
1771     pthread_mutex_unlock(&clients->lock);
1772 }
1773 
1774 // Set ctx->cur_event to a generated property change event, if there is any
1775 // outstanding property.
gen_property_change_event(struct mpv_handle * ctx)1776 static bool gen_property_change_event(struct mpv_handle *ctx)
1777 {
1778     if (!ctx->mpctx->initialized)
1779         return false;
1780 
1781     while (1) {
1782         if (ctx->cur_property_index >= ctx->num_properties) {
1783             ctx->new_property_events &= ctx->num_properties > 0;
1784             if (!ctx->new_property_events)
1785                 break;
1786             ctx->new_property_events = false;
1787             ctx->cur_property_index = 0;
1788         }
1789 
1790         struct observe_property *prop = ctx->properties[ctx->cur_property_index++];
1791 
1792         if (prop->value_ts == prop->change_ts &&    // not a stale value?
1793             prop->value_ret_ts != prop->value_ts)   // other value than last time?
1794         {
1795             prop->value_ret_ts = prop->value_ts;
1796             prop->waiting_for_hook = false;
1797             prop_unref(ctx->cur_property);
1798             ctx->cur_property = prop;
1799             prop->refcount += 1;
1800 
1801             if (prop->value_valid)
1802                 m_option_copy(prop->type, &prop->value_ret, &prop->value);
1803 
1804             ctx->cur_property_event = (struct mpv_event_property){
1805                 .name = prop->name,
1806                 .format = prop->value_valid ? prop->format : 0,
1807                 .data = prop->value_valid ? &prop->value_ret : NULL,
1808             };
1809             *ctx->cur_event = (struct mpv_event){
1810                 .event_id = MPV_EVENT_PROPERTY_CHANGE,
1811                 .reply_userdata = prop->reply_id,
1812                 .data = &ctx->cur_property_event,
1813             };
1814             return true;
1815         }
1816     }
1817 
1818     return false;
1819 }
1820 
mpv_hook_add(mpv_handle * ctx,uint64_t reply_userdata,const char * name,int priority)1821 int mpv_hook_add(mpv_handle *ctx, uint64_t reply_userdata,
1822                  const char *name, int priority)
1823 {
1824     lock_core(ctx);
1825     mp_hook_add(ctx->mpctx, ctx->name, ctx->id, name, reply_userdata, priority);
1826     unlock_core(ctx);
1827     return 0;
1828 }
1829 
mpv_hook_continue(mpv_handle * ctx,uint64_t id)1830 int mpv_hook_continue(mpv_handle *ctx, uint64_t id)
1831 {
1832     lock_core(ctx);
1833     int r = mp_hook_continue(ctx->mpctx, ctx->id, id);
1834     unlock_core(ctx);
1835     return r;
1836 }
1837 
mpv_load_config_file(mpv_handle * ctx,const char * filename)1838 int mpv_load_config_file(mpv_handle *ctx, const char *filename)
1839 {
1840     lock_core(ctx);
1841     int r = m_config_parse_config_file(ctx->mpctx->mconfig, filename, NULL, 0);
1842     unlock_core(ctx);
1843     if (r == 0)
1844         return MPV_ERROR_INVALID_PARAMETER;
1845     if (r < 0)
1846         return MPV_ERROR_OPTION_ERROR;
1847     return 0;
1848 }
1849 
msg_wakeup(void * p)1850 static void msg_wakeup(void *p)
1851 {
1852     mpv_handle *ctx = p;
1853     wakeup_client(ctx);
1854 }
1855 
1856 // Undocumented: if min_level starts with "silent:", then log messages are not
1857 // returned to the API user, but are stored until logging is enabled normally
1858 // again by calling this without "silent:". (Using a different level will
1859 // flush it, though.)
mpv_request_log_messages(mpv_handle * ctx,const char * min_level)1860 int mpv_request_log_messages(mpv_handle *ctx, const char *min_level)
1861 {
1862     bstr blevel = bstr0(min_level);
1863     bool silent = bstr_eatstart0(&blevel, "silent:");
1864 
1865     int level = -1;
1866     for (int n = 0; n < MSGL_MAX + 1; n++) {
1867         if (mp_log_levels[n] && bstr_equals0(blevel, mp_log_levels[n])) {
1868             level = n;
1869             break;
1870         }
1871     }
1872     if (bstr_equals0(blevel, "terminal-default"))
1873         level = MP_LOG_BUFFER_MSGL_TERM;
1874 
1875     if (level < 0 && strcmp(min_level, "no") != 0)
1876         return MPV_ERROR_INVALID_PARAMETER;
1877 
1878     pthread_mutex_lock(&ctx->lock);
1879     if (level < 0 || level != ctx->messages_level) {
1880         mp_msg_log_buffer_destroy(ctx->messages);
1881         ctx->messages = NULL;
1882     }
1883     if (level >= 0) {
1884         if (!ctx->messages) {
1885             int size = level >= MSGL_V ? 10000 : 1000;
1886             ctx->messages = mp_msg_log_buffer_new(ctx->mpctx->global, size,
1887                                                   level, msg_wakeup, ctx);
1888             ctx->messages_level = level;
1889         }
1890         mp_msg_log_buffer_set_silent(ctx->messages, silent);
1891     }
1892     wakeup_client(ctx);
1893     pthread_mutex_unlock(&ctx->lock);
1894     return 0;
1895 }
1896 
1897 // Set ctx->cur_event to a generated log message event, if any available.
gen_log_message_event(struct mpv_handle * ctx)1898 static bool gen_log_message_event(struct mpv_handle *ctx)
1899 {
1900     if (ctx->messages) {
1901         struct mp_log_buffer_entry *msg =
1902             mp_msg_log_buffer_read(ctx->messages);
1903         if (msg) {
1904             struct mpv_event_log_message *cmsg =
1905                 talloc_ptrtype(ctx->cur_event, cmsg);
1906             talloc_steal(cmsg, msg);
1907             *cmsg = (struct mpv_event_log_message){
1908                 .prefix = msg->prefix,
1909                 .level = mp_log_levels[msg->level],
1910                 .log_level = mp_mpv_log_levels[msg->level],
1911                 .text = msg->text,
1912             };
1913             *ctx->cur_event = (struct mpv_event){
1914                 .event_id = MPV_EVENT_LOG_MESSAGE,
1915                 .data = cmsg,
1916             };
1917             return true;
1918         }
1919     }
1920     return false;
1921 }
1922 
mpv_get_wakeup_pipe(mpv_handle * ctx)1923 int mpv_get_wakeup_pipe(mpv_handle *ctx)
1924 {
1925     pthread_mutex_lock(&ctx->wakeup_lock);
1926     if (ctx->wakeup_pipe[0] == -1) {
1927         if (mp_make_wakeup_pipe(ctx->wakeup_pipe) >= 0)
1928             (void)write(ctx->wakeup_pipe[1], &(char){0}, 1);
1929     }
1930     int fd = ctx->wakeup_pipe[0];
1931     pthread_mutex_unlock(&ctx->wakeup_lock);
1932     return fd;
1933 }
1934 
mpv_client_api_version(void)1935 unsigned long mpv_client_api_version(void)
1936 {
1937     return MPV_CLIENT_API_VERSION;
1938 }
1939 
mpv_event_to_node(mpv_node * dst,mpv_event * event)1940 int mpv_event_to_node(mpv_node *dst, mpv_event *event)
1941 {
1942     *dst = (mpv_node){0};
1943 
1944     node_init(dst, MPV_FORMAT_NODE_MAP, NULL);
1945     node_map_add_string(dst, "event", mpv_event_name(event->event_id));
1946 
1947     if (event->error < 0)
1948         node_map_add_string(dst, "error", mpv_error_string(event->error));
1949 
1950     if (event->reply_userdata)
1951         node_map_add_int64(dst, "id", event->reply_userdata);
1952 
1953     switch (event->event_id) {
1954 
1955     case MPV_EVENT_START_FILE: {
1956         mpv_event_start_file *esf = event->data;
1957 
1958         node_map_add_int64(dst, "playlist_entry_id", esf->playlist_entry_id);
1959         break;
1960     }
1961 
1962     case MPV_EVENT_END_FILE: {
1963         mpv_event_end_file *eef = event->data;
1964 
1965         const char *reason;
1966         switch (eef->reason) {
1967         case MPV_END_FILE_REASON_EOF: reason = "eof"; break;
1968         case MPV_END_FILE_REASON_STOP: reason = "stop"; break;
1969         case MPV_END_FILE_REASON_QUIT: reason = "quit"; break;
1970         case MPV_END_FILE_REASON_ERROR: reason = "error"; break;
1971         case MPV_END_FILE_REASON_REDIRECT: reason = "redirect"; break;
1972         default:
1973             reason = "unknown";
1974         }
1975         node_map_add_string(dst, "reason", reason);
1976 
1977         node_map_add_int64(dst, "playlist_entry_id", eef->playlist_entry_id);
1978 
1979         if (eef->playlist_insert_id) {
1980             node_map_add_int64(dst, "playlist_insert_id", eef->playlist_insert_id);
1981             node_map_add_int64(dst, "playlist_insert_num_entries",
1982                                eef->playlist_insert_num_entries);
1983         }
1984 
1985         if (eef->reason == MPV_END_FILE_REASON_ERROR)
1986             node_map_add_string(dst, "file_error", mpv_error_string(eef->error));
1987         break;
1988     }
1989 
1990     case MPV_EVENT_LOG_MESSAGE: {
1991         mpv_event_log_message *msg = event->data;
1992 
1993         node_map_add_string(dst, "prefix", msg->prefix);
1994         node_map_add_string(dst, "level",  msg->level);
1995         node_map_add_string(dst, "text",   msg->text);
1996         break;
1997     }
1998 
1999     case MPV_EVENT_CLIENT_MESSAGE: {
2000         mpv_event_client_message *msg = event->data;
2001 
2002         struct mpv_node *args = node_map_add(dst, "args", MPV_FORMAT_NODE_ARRAY);
2003         for (int n = 0; n < msg->num_args; n++) {
2004             struct mpv_node *sn = node_array_add(args, MPV_FORMAT_NONE);
2005             sn->format = MPV_FORMAT_STRING;
2006             sn->u.string = (char *)msg->args[n];
2007         }
2008         break;
2009     }
2010 
2011     case MPV_EVENT_PROPERTY_CHANGE: {
2012         mpv_event_property *prop = event->data;
2013 
2014         node_map_add_string(dst, "name", prop->name);
2015 
2016         switch (prop->format) {
2017         case MPV_FORMAT_NODE:
2018             *node_map_add(dst, "data", MPV_FORMAT_NONE) =
2019                 *(struct mpv_node *)prop->data;
2020             break;
2021         case MPV_FORMAT_DOUBLE:
2022             node_map_add_double(dst, "data", *(double *)prop->data);
2023             break;
2024         case MPV_FORMAT_FLAG:
2025             node_map_add_flag(dst, "data", *(int *)prop->data);
2026             break;
2027         case MPV_FORMAT_STRING:
2028             node_map_add_string(dst, "data", *(char **)prop->data);
2029             break;
2030         default: ;
2031         }
2032         break;
2033     }
2034 
2035     case MPV_EVENT_COMMAND_REPLY: {
2036         mpv_event_command *cmd = event->data;
2037 
2038         *node_map_add(dst, "result", MPV_FORMAT_NONE) = cmd->result;
2039         break;
2040     }
2041 
2042     case MPV_EVENT_HOOK: {
2043         mpv_event_hook *hook = event->data;
2044 
2045         node_map_add_int64(dst, "hook_id", hook->id);
2046         break;
2047     }
2048 
2049     }
2050     return 0;
2051 }
2052 
2053 static const char *const err_table[] = {
2054     [-MPV_ERROR_SUCCESS] = "success",
2055     [-MPV_ERROR_EVENT_QUEUE_FULL] = "event queue full",
2056     [-MPV_ERROR_NOMEM] = "memory allocation failed",
2057     [-MPV_ERROR_UNINITIALIZED] = "core not uninitialized",
2058     [-MPV_ERROR_INVALID_PARAMETER] = "invalid parameter",
2059     [-MPV_ERROR_OPTION_NOT_FOUND] = "option not found",
2060     [-MPV_ERROR_OPTION_FORMAT] = "unsupported format for accessing option",
2061     [-MPV_ERROR_OPTION_ERROR] = "error setting option",
2062     [-MPV_ERROR_PROPERTY_NOT_FOUND] = "property not found",
2063     [-MPV_ERROR_PROPERTY_FORMAT] = "unsupported format for accessing property",
2064     [-MPV_ERROR_PROPERTY_UNAVAILABLE] = "property unavailable",
2065     [-MPV_ERROR_PROPERTY_ERROR] = "error accessing property",
2066     [-MPV_ERROR_COMMAND] = "error running command",
2067     [-MPV_ERROR_LOADING_FAILED] = "loading failed",
2068     [-MPV_ERROR_AO_INIT_FAILED] = "audio output initialization failed",
2069     [-MPV_ERROR_VO_INIT_FAILED] = "video output initialization failed",
2070     [-MPV_ERROR_NOTHING_TO_PLAY] = "no audio or video data played",
2071     [-MPV_ERROR_UNKNOWN_FORMAT] = "unrecognized file format",
2072     [-MPV_ERROR_UNSUPPORTED] = "not supported",
2073     [-MPV_ERROR_NOT_IMPLEMENTED] = "operation not implemented",
2074     [-MPV_ERROR_GENERIC] = "something happened",
2075 };
2076 
mpv_error_string(int error)2077 const char *mpv_error_string(int error)
2078 {
2079     error = -error;
2080     if (error < 0)
2081         error = 0;
2082     const char *name = NULL;
2083     if (error < MP_ARRAY_SIZE(err_table))
2084         name = err_table[error];
2085     return name ? name : "unknown error";
2086 }
2087 
2088 static const char *const event_table[] = {
2089     [MPV_EVENT_NONE] = "none",
2090     [MPV_EVENT_SHUTDOWN] = "shutdown",
2091     [MPV_EVENT_LOG_MESSAGE] = "log-message",
2092     [MPV_EVENT_GET_PROPERTY_REPLY] = "get-property-reply",
2093     [MPV_EVENT_SET_PROPERTY_REPLY] = "set-property-reply",
2094     [MPV_EVENT_COMMAND_REPLY] = "command-reply",
2095     [MPV_EVENT_START_FILE] = "start-file",
2096     [MPV_EVENT_END_FILE] = "end-file",
2097     [MPV_EVENT_FILE_LOADED] = "file-loaded",
2098     [MPV_EVENT_TRACKS_CHANGED] = "tracks-changed",
2099     [MPV_EVENT_TRACK_SWITCHED] = "track-switched",
2100     [MPV_EVENT_IDLE] = "idle",
2101     [MPV_EVENT_PAUSE] = "pause",
2102     [MPV_EVENT_UNPAUSE] = "unpause",
2103     [MPV_EVENT_TICK] = "tick",
2104     [MPV_EVENT_SCRIPT_INPUT_DISPATCH] = "script-input-dispatch",
2105     [MPV_EVENT_CLIENT_MESSAGE] = "client-message",
2106     [MPV_EVENT_VIDEO_RECONFIG] = "video-reconfig",
2107     [MPV_EVENT_AUDIO_RECONFIG] = "audio-reconfig",
2108     [MPV_EVENT_METADATA_UPDATE] = "metadata-update",
2109     [MPV_EVENT_SEEK] = "seek",
2110     [MPV_EVENT_PLAYBACK_RESTART] = "playback-restart",
2111     [MPV_EVENT_PROPERTY_CHANGE] = "property-change",
2112     [MPV_EVENT_CHAPTER_CHANGE] = "chapter-change",
2113     [MPV_EVENT_QUEUE_OVERFLOW] = "event-queue-overflow",
2114     [MPV_EVENT_HOOK] = "hook",
2115 };
2116 
mpv_event_name(mpv_event_id event)2117 const char *mpv_event_name(mpv_event_id event)
2118 {
2119     if ((unsigned)event >= MP_ARRAY_SIZE(event_table))
2120         return NULL;
2121     return event_table[event];
2122 }
2123 
mpv_free(void * data)2124 void mpv_free(void *data)
2125 {
2126     talloc_free(data);
2127 }
2128 
mpv_get_time_us(mpv_handle * ctx)2129 int64_t mpv_get_time_us(mpv_handle *ctx)
2130 {
2131     return mp_time_us();
2132 }
2133 
2134 #include "video/out/libmpv.h"
2135 
do_kill(void * ptr)2136 static void do_kill(void *ptr)
2137 {
2138     struct MPContext *mpctx = ptr;
2139 
2140     struct track *track = mpctx->vo_chain ? mpctx->vo_chain->track : NULL;
2141     uninit_video_out(mpctx);
2142     if (track) {
2143         mpctx->error_playing = MPV_ERROR_VO_INIT_FAILED;
2144         error_on_track(mpctx, track);
2145     }
2146 }
2147 
2148 // Used by vo_libmpv to (a)synchronously uninitialize video.
kill_video_async(struct mp_client_api * client_api)2149 void kill_video_async(struct mp_client_api *client_api)
2150 {
2151     struct MPContext *mpctx = client_api->mpctx;
2152     mp_dispatch_enqueue(mpctx->dispatch, do_kill, mpctx);
2153 }
2154 
2155 // Used by vo_libmpv to set the current render context.
mp_set_main_render_context(struct mp_client_api * client_api,struct mpv_render_context * ctx,bool active)2156 bool mp_set_main_render_context(struct mp_client_api *client_api,
2157                                 struct mpv_render_context *ctx, bool active)
2158 {
2159     assert(ctx);
2160 
2161     pthread_mutex_lock(&client_api->lock);
2162     bool is_set = !!client_api->render_context;
2163     bool is_same = client_api->render_context == ctx;
2164     // Can set if it doesn't remove another existing ctx.
2165     bool res = is_same || !is_set;
2166     if (res)
2167         client_api->render_context = active ? ctx : NULL;
2168     pthread_mutex_unlock(&client_api->lock);
2169     return res;
2170 }
2171 
2172 // Used by vo_libmpv. Relies on guarantees by mp_render_context_acquire().
2173 struct mpv_render_context *
mp_client_api_acquire_render_context(struct mp_client_api * ca)2174 mp_client_api_acquire_render_context(struct mp_client_api *ca)
2175 {
2176     struct mpv_render_context *res = NULL;
2177     pthread_mutex_lock(&ca->lock);
2178     if (ca->render_context && mp_render_context_acquire(ca->render_context))
2179         res = ca->render_context;
2180     pthread_mutex_unlock(&ca->lock);
2181     return res;
2182 }
2183 
2184 // Emulation of old opengl_cb API.
2185 
2186 #include "libmpv/opengl_cb.h"
2187 #include "libmpv/render_gl.h"
2188 
mpv_opengl_cb_set_update_callback(mpv_opengl_cb_context * ctx,mpv_opengl_cb_update_fn callback,void * callback_ctx)2189 void mpv_opengl_cb_set_update_callback(mpv_opengl_cb_context *ctx,
2190                                        mpv_opengl_cb_update_fn callback,
2191                                        void *callback_ctx)
2192 {
2193 }
2194 
mpv_opengl_cb_init_gl(mpv_opengl_cb_context * ctx,const char * exts,mpv_opengl_cb_get_proc_address_fn get_proc_address,void * get_proc_address_ctx)2195 int mpv_opengl_cb_init_gl(mpv_opengl_cb_context *ctx, const char *exts,
2196                           mpv_opengl_cb_get_proc_address_fn get_proc_address,
2197                           void *get_proc_address_ctx)
2198 {
2199     return MPV_ERROR_NOT_IMPLEMENTED;
2200 }
2201 
mpv_opengl_cb_draw(mpv_opengl_cb_context * ctx,int fbo,int w,int h)2202 int mpv_opengl_cb_draw(mpv_opengl_cb_context *ctx, int fbo, int w, int h)
2203 {
2204     return MPV_ERROR_NOT_IMPLEMENTED;
2205 }
2206 
mpv_opengl_cb_report_flip(mpv_opengl_cb_context * ctx,int64_t time)2207 int mpv_opengl_cb_report_flip(mpv_opengl_cb_context *ctx, int64_t time)
2208 {
2209     return MPV_ERROR_NOT_IMPLEMENTED;
2210 }
2211 
mpv_opengl_cb_uninit_gl(mpv_opengl_cb_context * ctx)2212 int mpv_opengl_cb_uninit_gl(mpv_opengl_cb_context *ctx)
2213 {
2214     return 0;
2215 }
2216 
mpv_opengl_cb_render(mpv_opengl_cb_context * ctx,int fbo,int vp[4])2217 int mpv_opengl_cb_render(mpv_opengl_cb_context *ctx, int fbo, int vp[4])
2218 {
2219     return MPV_ERROR_NOT_IMPLEMENTED;
2220 }
2221 
mpv_get_sub_api(mpv_handle * ctx,mpv_sub_api sub_api)2222 void *mpv_get_sub_api(mpv_handle *ctx, mpv_sub_api sub_api)
2223 {
2224     if (!ctx->mpctx->initialized || sub_api != MPV_SUB_API_OPENGL_CB)
2225         return NULL;
2226     // Return something non-NULL, as I think most API users will not check
2227     // this properly. The other opengl_cb stubs do not use this value.
2228     MP_WARN(ctx, "The opengl_cb API is not supported anymore.\n"
2229                  "Use the similar API in render.h instead.\n");
2230     return "no";
2231 }
2232 
2233 // stream_cb
2234 
2235 struct mp_custom_protocol {
2236     char *protocol;
2237     void *user_data;
2238     mpv_stream_cb_open_ro_fn open_fn;
2239 };
2240 
mpv_stream_cb_add_ro(mpv_handle * ctx,const char * protocol,void * user_data,mpv_stream_cb_open_ro_fn open_fn)2241 int mpv_stream_cb_add_ro(mpv_handle *ctx, const char *protocol, void *user_data,
2242                          mpv_stream_cb_open_ro_fn open_fn)
2243 {
2244     if (!open_fn)
2245         return MPV_ERROR_INVALID_PARAMETER;
2246 
2247     struct mp_client_api *clients = ctx->clients;
2248     int r = 0;
2249     pthread_mutex_lock(&clients->lock);
2250     for (int n = 0; n < clients->num_custom_protocols; n++) {
2251         struct mp_custom_protocol *proto = &clients->custom_protocols[n];
2252         if (strcmp(proto->protocol, protocol) == 0) {
2253             r = MPV_ERROR_INVALID_PARAMETER;
2254             break;
2255         }
2256     }
2257     if (stream_has_proto(protocol))
2258         r = MPV_ERROR_INVALID_PARAMETER;
2259     if (r >= 0) {
2260         struct mp_custom_protocol proto = {
2261             .protocol = talloc_strdup(clients, protocol),
2262             .user_data = user_data,
2263             .open_fn = open_fn,
2264         };
2265         MP_TARRAY_APPEND(clients, clients->custom_protocols,
2266                          clients->num_custom_protocols, proto);
2267     }
2268     pthread_mutex_unlock(&clients->lock);
2269     return r;
2270 }
2271 
mp_streamcb_lookup(struct mpv_global * g,const char * protocol,void ** out_user_data,mpv_stream_cb_open_ro_fn * out_fn)2272 bool mp_streamcb_lookup(struct mpv_global *g, const char *protocol,
2273                         void **out_user_data, mpv_stream_cb_open_ro_fn *out_fn)
2274 {
2275     struct mp_client_api *clients = g->client_api;
2276     bool found = false;
2277     pthread_mutex_lock(&clients->lock);
2278     for (int n = 0; n < clients->num_custom_protocols; n++) {
2279         struct mp_custom_protocol *proto = &clients->custom_protocols[n];
2280         if (strcmp(proto->protocol, protocol) == 0) {
2281             *out_user_data = proto->user_data;
2282             *out_fn = proto->open_fn;
2283             found = true;
2284             break;
2285         }
2286     }
2287     pthread_mutex_unlock(&clients->lock);
2288     return found;
2289 }
2290