diff options
-rw-r--r-- | libmpv/client.h | 19 | ||||
-rw-r--r-- | player/client.c | 34 | ||||
-rw-r--r-- | player/core.h | 1 | ||||
-rw-r--r-- | player/main.c | 4 |
4 files changed, 55 insertions, 3 deletions
diff --git a/libmpv/client.h b/libmpv/client.h index e5e6d5a3e8..d1fe312500 100644 --- a/libmpv/client.h +++ b/libmpv/client.h @@ -316,11 +316,28 @@ int mpv_initialize(mpv_handle *ctx); * Disconnect and destroy the client context. ctx will be deallocated with this * API call. This leaves the player running. If you want to be sure that the * player is terminated, send a "quit" command, and wait until the - * MPV_EVENT_SHUTDOWN event is received. + * MPV_EVENT_SHUTDOWN event is received, or use mpv_terminate_destroy(). */ void mpv_destroy(mpv_handle *ctx); /** + * Similar to mpv_destroy(), but brings the player and all clients down as well, + * and waits until all of them are destroyed. This function blocks. The + * advantage over mpv_destroy() is that while mpv_destroy() merely detaches + * the client handle from the player, this function quits the player, waits + * until all other clients are destroyed (i.e. all mpv_handles are detached), + * and also waits for the final termination of the player. + * + * Since mpv_destroy() is called somewhere on the way, it's not safe to call + * other functions concurrently on the same context. + * + * If this is called on a mpv_handle that was not created with mpv_create(), + * this function will merely send a quit command and then call mpv_destroy(), + * without waiting for the actual shutdown. + */ +void mpv_terminate_destroy(mpv_handle *ctx); + +/** * Load a config file. This loads and parses the file, and sets every entry in * the config file's default section as if mpv_set_option_string() is called. * diff --git a/player/client.c b/player/client.c index be8cbc5d88..9a984052b1 100644 --- a/player/client.c +++ b/player/client.c @@ -77,6 +77,7 @@ struct observe_property { struct mpv_handle { // -- immmutable char *name; + bool owner; struct mp_log *log; struct MPContext *mpctx; struct mp_client_api *clients; @@ -299,11 +300,41 @@ void mpv_destroy(mpv_handle *ctx) assert(!ctx); } +static void get_thread(void *ptr) +{ + *(pthread_t *)ptr = pthread_self(); +} + +void mpv_terminate_destroy(mpv_handle *ctx) +{ + mpv_command(ctx, (const char*[]){"quit", NULL}); + + if (!ctx->owner) { + mpv_destroy(ctx); + return; + } + + mp_dispatch_lock(ctx->mpctx->dispatch); + assert(ctx->mpctx->autodetach); + ctx->mpctx->autodetach = false; + mp_dispatch_unlock(ctx->mpctx->dispatch); + + pthread_t playthread; + mp_dispatch_run(ctx->mpctx->dispatch, get_thread, &playthread); + + mpv_destroy(ctx); + + // And this is also the reason why we only allow 1 thread (the owner) to + // call this function. + pthread_join(playthread, NULL); +} + mpv_handle *mpv_create(void) { struct MPContext *mpctx = mp_create(); mpv_handle *ctx = mp_new_client(mpctx->clients, "main"); if (ctx) { + ctx->owner = true; // Set some defaults. mpv_set_option_string(ctx, "config", "no"); mpv_set_option_string(ctx, "idle", "yes"); @@ -319,8 +350,7 @@ mpv_handle *mpv_create(void) static void *playback_thread(void *p) { struct MPContext *mpctx = p; - - pthread_detach(pthread_self()); + mpctx->autodetach = true; mp_play_files(mpctx); diff --git a/player/core.h b/player/core.h index b107edc7e4..ca61ab8f11 100644 --- a/player/core.h +++ b/player/core.h @@ -144,6 +144,7 @@ enum { typedef struct MPContext { bool initialized; bool is_cplayer; + bool autodetach; struct mpv_global *global; struct MPOpts *opts; struct mp_log *log; diff --git a/player/main.c b/player/main.c index 0bfd25bdd5..d7b34e0ef8 100644 --- a/player/main.c +++ b/player/main.c @@ -23,6 +23,7 @@ #include <assert.h> #include <ctype.h> #include <string.h> +#include <pthread.h> #include "config.h" #include "talloc.h" @@ -148,6 +149,9 @@ void mp_destroy(struct MPContext *mpctx) } uninit_libav(mpctx->global); + if (mpctx->autodetach) + pthread_detach(pthread_self()); + mp_msg_uninit(mpctx->global); talloc_free(mpctx); } |