summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmpv/client.h19
-rw-r--r--player/client.c34
-rw-r--r--player/core.h1
-rw-r--r--player/main.c4
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);
}