test/libmpv: use common wrappers for properties and commands
This is mainly for better, more descriptive logging when something goes wrong and fails. Common operations that manipulate properties and commands go through shared functions that do better detailed logging. Also it means we don't need check_api_error around anymore so get rid of that. libmpv_lifetime is left untouched since that one is a bit special and works a little differently.
This commit is contained in:
1 parent
e0cf3f8061
commit
3bbb94077d
7 files changed
+123
-71
No files matched your search
+64
-12
@@ -26,6 +26,15 @@
|
||||
|
||||
#include "osdep/compiler.h"
|
||||
|
||||
#define command(cmd) command_impl(__FILE__, __LINE__, (cmd))
|
||||
#define command_string(cmd) command_string_impl(__FILE__, __LINE__, (cmd))
|
||||
#define get_property(property, format, result) \
|
||||
get_property_impl(__FILE__, __LINE__, (property), (format), (result))
|
||||
#define set_option_or_property(property, format, value, option) \
|
||||
set_option_or_property_impl(__FILE__, __LINE__, (property), (format), (value), (option))
|
||||
#define set_property_string(property, value) \
|
||||
set_property_string_impl(__FILE__, __LINE__, (property), (value))
|
||||
|
||||
// Global handle.
|
||||
static mpv_handle *ctx;
|
||||
MP_NORETURN PRINTF_ATTRIBUTE(1, 2)
|
||||
@@ -65,16 +74,55 @@ static inline mpv_event *wrap_wait_event(void)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void check_api_error(int status)
|
||||
static inline void command_impl(const char *file, int line, const char *cmd[])
|
||||
{
|
||||
if (status < 0)
|
||||
fail("mpv API error: %s\n", mpv_error_string(status));
|
||||
int ret = mpv_command(ctx, cmd);
|
||||
if (ret < 0)
|
||||
fail("mpv API error while running command '%s' at %s:%d (%s)\n",
|
||||
cmd[0], file, line, mpv_error_string(ret));
|
||||
}
|
||||
|
||||
static inline void command_string_impl(const char *file, int line, const char *cmd)
|
||||
{
|
||||
int ret = mpv_command_string(ctx, cmd);
|
||||
if (ret < 0)
|
||||
fail("mpv API error while running command '%s' at %s:%d (%s)\n",
|
||||
cmd, file, line, mpv_error_string(ret));
|
||||
}
|
||||
|
||||
static inline void get_property_impl(const char *file, int line, const char *property,
|
||||
int format, void *result)
|
||||
{
|
||||
int ret = mpv_get_property(ctx, property, format, result);
|
||||
if (ret < 0)
|
||||
fail("mpv API error while getting property '%s' at %s:%d (%s)\n",
|
||||
property, file, line, mpv_error_string(ret));
|
||||
}
|
||||
|
||||
static inline void set_option_or_property_impl(const char *file, int line, const char *property,
|
||||
int format, void *value, bool option)
|
||||
{
|
||||
int ret = option ? mpv_set_option(ctx, property, format, value) :
|
||||
mpv_set_property(ctx, property, format, value);
|
||||
if (ret < 0)
|
||||
fail("mpv API while setting %s '%s' at %s:%d (%s)\n", option ? "option" : "property",
|
||||
property, file, line, mpv_error_string(ret));
|
||||
|
||||
}
|
||||
|
||||
static inline void set_property_string_impl(const char *file, int line, const char *property,
|
||||
const char *value)
|
||||
{
|
||||
int ret = mpv_set_property_string(ctx, property, value);
|
||||
if (ret < 0)
|
||||
fail("mpv API error while setting property '%s' to '%s' at %s:%d (%s)\n",
|
||||
property, value, file, line, mpv_error_string(ret));
|
||||
}
|
||||
|
||||
MP_UNUSED static void check_double(const char *property, double expect)
|
||||
{
|
||||
double result_double;
|
||||
check_api_error(mpv_get_property(ctx, property, MPV_FORMAT_DOUBLE, &result_double));
|
||||
get_property(property, MPV_FORMAT_DOUBLE, &result_double);
|
||||
if (expect != result_double)
|
||||
fail("Double: expected '%f' but got '%f'!\n", expect, result_double);
|
||||
}
|
||||
@@ -82,7 +130,7 @@ MP_UNUSED static void check_double(const char *property, double expect)
|
||||
MP_UNUSED static void check_flag(const char *property, int expect)
|
||||
{
|
||||
int result_flag;
|
||||
check_api_error(mpv_get_property(ctx, property, MPV_FORMAT_FLAG, &result_flag));
|
||||
get_property(property, MPV_FORMAT_FLAG, &result_flag);
|
||||
if (expect != result_flag)
|
||||
fail("Flag: expected '%d' but got '%d'!\n", expect, result_flag);
|
||||
}
|
||||
@@ -90,7 +138,7 @@ MP_UNUSED static void check_flag(const char *property, int expect)
|
||||
MP_UNUSED static void check_int(const char *property, int64_t expect)
|
||||
{
|
||||
int64_t result_int;
|
||||
check_api_error(mpv_get_property(ctx, property, MPV_FORMAT_INT64, &result_int));
|
||||
get_property(property, MPV_FORMAT_INT64, &result_int);
|
||||
if (expect != result_int)
|
||||
fail("Int: expected '%" PRId64 "' but got '%" PRId64 "'!\n", expect, result_int);
|
||||
}
|
||||
@@ -98,7 +146,7 @@ MP_UNUSED static void check_int(const char *property, int64_t expect)
|
||||
MP_UNUSED static inline void check_string(const char *property, const char *expect)
|
||||
{
|
||||
char *result_string;
|
||||
check_api_error(mpv_get_property(ctx, property, MPV_FORMAT_STRING, &result_string));
|
||||
get_property(property, MPV_FORMAT_STRING, &result_string);
|
||||
if (strcmp(expect, result_string) != 0)
|
||||
fail("String: expected '%s' but got '%s'!\n", expect, result_string);
|
||||
mpv_free(result_string);
|
||||
@@ -106,16 +154,20 @@ MP_UNUSED static inline void check_string(const char *property, const char *expe
|
||||
|
||||
static inline void initialize(void)
|
||||
{
|
||||
check_api_error(mpv_set_option_string(ctx, "vo", "null"));
|
||||
check_api_error(mpv_set_option_string(ctx, "ao", "null"));
|
||||
check_api_error(mpv_request_log_messages(ctx, "debug"));
|
||||
check_api_error(mpv_initialize(ctx));
|
||||
set_property_string("vo", "null");
|
||||
set_property_string("ao", "null");
|
||||
int ret = mpv_request_log_messages(ctx, "debug");
|
||||
if (ret < 0)
|
||||
fail("mpv API error while setting log level to debug: %s\n", mpv_error_string(ret));
|
||||
ret = mpv_initialize(ctx);
|
||||
if (ret < 0)
|
||||
fail("mpv API error while initializing mpv: %s\n", mpv_error_string(ret));
|
||||
}
|
||||
|
||||
static inline void reload_file(const char *path)
|
||||
{
|
||||
const char *cmd[] = {"loadfile", path, NULL};
|
||||
check_api_error(mpv_command(ctx, cmd));
|
||||
command(cmd);
|
||||
bool loaded = false;
|
||||
while (!loaded) {
|
||||
mpv_event *event = wrap_wait_event();
|
||||
|
||||
@@ -93,19 +93,19 @@ int main(int argc, char *argv[])
|
||||
out_path = path;
|
||||
#endif
|
||||
|
||||
check_api_error(mpv_set_option_string(ctx, "o", out_path));
|
||||
check_api_error(mpv_set_option_string(ctx, "of", "matroska"));
|
||||
check_api_error(mpv_set_option_string(ctx, "end", "1.5"));
|
||||
check_api_error(mpv_set_option_string(ctx, "terminal", "yes"));
|
||||
check_api_error(mpv_set_option_string(ctx, "msg-level", "all=v"));
|
||||
set_property_string("o", out_path);
|
||||
set_property_string("of", "matroska");
|
||||
set_property_string("end", "1.5");
|
||||
set_property_string("terminal", "yes");
|
||||
set_property_string("msg-level", "all=v");
|
||||
|
||||
if (mpv_initialize(ctx) != 0)
|
||||
return 1;
|
||||
|
||||
check_api_error(mpv_set_option_string(ctx, "idle", "once"));
|
||||
set_property_string("idle", "once");
|
||||
|
||||
const char *cmd[] = {"loadfile", "av://lavfi:testsrc", NULL};
|
||||
check_api_error(mpv_command(ctx, cmd));
|
||||
command(cmd);
|
||||
|
||||
wait_done();
|
||||
mpv_destroy(ctx);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
static void test_file_loading(char *file)
|
||||
{
|
||||
const char *cmd[] = {"loadfile", file, NULL};
|
||||
check_api_error(mpv_command(ctx, cmd));
|
||||
command(cmd);
|
||||
bool loaded = false;
|
||||
bool finished = false;
|
||||
while (!finished) {
|
||||
@@ -58,7 +58,7 @@ int main(int argc, char *argv[])
|
||||
test_file_loading(argv[1]);
|
||||
printf("================ SHUTDOWN ================\n");
|
||||
|
||||
mpv_command_string(ctx, "quit");
|
||||
command_string("quit");
|
||||
while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
static void test_lavfi_complex(char *file)
|
||||
{
|
||||
const char *cmd[] = {"loadfile", file, NULL};
|
||||
check_api_error(mpv_command(ctx, cmd));
|
||||
command(cmd);
|
||||
bool finished = false;
|
||||
bool loaded = false;
|
||||
while (!finished) {
|
||||
@@ -29,10 +29,10 @@ static void test_lavfi_complex(char *file)
|
||||
case MPV_EVENT_FILE_LOADED:
|
||||
// Add file as external and toggle lavfi-complex on.
|
||||
if (!loaded) {
|
||||
check_api_error(mpv_set_property_string(ctx, "external-files", file));
|
||||
set_property_string("external-files", file);
|
||||
const char *add_cmd[] = {"video-add", file, "auto", NULL};
|
||||
check_api_error(mpv_command(ctx, add_cmd));
|
||||
check_api_error(mpv_set_property_string(ctx, "lavfi-complex", "[vid1] [vid2] vstack [vo]"));
|
||||
command(add_cmd);
|
||||
set_property_string("lavfi-complex", "[vid1] [vid2] vstack [vo]");
|
||||
}
|
||||
loaded = true;
|
||||
break;
|
||||
@@ -64,7 +64,7 @@ int main(int argc, char *argv[])
|
||||
test_lavfi_complex(argv[1]);
|
||||
printf("================ SHUTDOWN ================\n");
|
||||
|
||||
mpv_command_string(ctx, "quit");
|
||||
command_string("quit");
|
||||
while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
|
||||
|
||||
return 0;
|
||||
|
||||
+10
-10
@@ -49,20 +49,20 @@ static void set_options_and_properties(const char *options[], const char *proper
|
||||
for (int i = 0; options[i]; i++) {
|
||||
switch (formats[i]) {
|
||||
case MPV_FORMAT_STRING:
|
||||
check_api_error(mpv_set_option(ctx, options[i], formats[i], &str));
|
||||
check_api_error(mpv_set_property(ctx, properties[i], formats[i], &str));
|
||||
set_option_or_property(options[i], formats[i], &str, true);
|
||||
set_option_or_property(properties[i], formats[i], &str, false);
|
||||
break;
|
||||
case MPV_FORMAT_FLAG:
|
||||
check_api_error(mpv_set_option(ctx, options[i], formats[i], &flag));
|
||||
check_api_error(mpv_set_property(ctx, properties[i], formats[i], &flag));
|
||||
set_option_or_property(options[i], formats[i], &flag, true);
|
||||
set_option_or_property(properties[i], formats[i], &flag, false);
|
||||
break;
|
||||
case MPV_FORMAT_INT64:
|
||||
check_api_error(mpv_set_option(ctx, options[i], formats[i], &int_));
|
||||
check_api_error(mpv_set_property(ctx, properties[i], formats[i], &int_));
|
||||
set_option_or_property(options[i], formats[i], &int_, true);
|
||||
set_option_or_property(properties[i], formats[i], &int_, false);
|
||||
break;
|
||||
case MPV_FORMAT_DOUBLE:
|
||||
check_api_error(mpv_set_option(ctx, options[i], formats[i], &double_));
|
||||
check_api_error(mpv_set_property(ctx, properties[i], formats[i], &double_));
|
||||
set_option_or_property(options[i], formats[i], &double_, true);
|
||||
set_option_or_property(properties[i], formats[i], &double_, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ static void test_options_and_properties(void)
|
||||
|
||||
// Ensure the format is still MPV_FORMAT_FLAG for these property types.
|
||||
mpv_node result_node;
|
||||
check_api_error(mpv_get_property(ctx, "idle-active", MPV_FORMAT_NODE, &result_node));
|
||||
get_property("idle-active", MPV_FORMAT_NODE, &result_node);
|
||||
if (result_node.format != MPV_FORMAT_FLAG)
|
||||
fail("Node: expected mpv format '%d' but got '%d'!\n", MPV_FORMAT_FLAG, result_node.format);
|
||||
|
||||
@@ -132,7 +132,7 @@ int main(int argc, char *argv[])
|
||||
test_options_and_properties();
|
||||
printf("================ SHUTDOWN ================\n");
|
||||
|
||||
mpv_command_string(ctx, "quit");
|
||||
command_string("quit");
|
||||
while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -33,9 +33,9 @@ static void test_sdh_filter(char *file, char *path, char *harder)
|
||||
if (ret)
|
||||
fail("Test file, '%s', was not found!\n", path);
|
||||
|
||||
check_api_error(mpv_set_property_string(ctx, "sub-filter-sdh", "yes"));
|
||||
set_property_string("sub-filter-sdh", "yes");
|
||||
if (harder)
|
||||
check_api_error(mpv_set_property_string(ctx, "sub-filter-sdh-harder", "yes"));
|
||||
set_property_string("sub-filter-sdh-harder", "yes");
|
||||
|
||||
char *expect = "";
|
||||
if (strcmp(file, "sdh_default") == 0 && harder) {
|
||||
@@ -91,13 +91,13 @@ int main(int argc, char *argv[])
|
||||
const char *fmt = "================ TEST: %s %s ================\n";
|
||||
printf(fmt, "test_sdh_filter", argv[1]);
|
||||
// Set pause and start from 1 second to ensure the subtitle is loaded.
|
||||
mpv_set_property_string(ctx, "pause", "yes");
|
||||
mpv_set_property_string(ctx, "start", "1");
|
||||
set_property_string("pause", "yes");
|
||||
set_property_string("start", "1");
|
||||
mpv_observe_property(ctx, 0, "sub-text", MPV_FORMAT_STRING);
|
||||
test_sdh_filter(argv[1], argv[2], argv[3]);
|
||||
printf("================ SHUTDOWN ================\n");
|
||||
|
||||
mpv_command_string(ctx, "quit");
|
||||
command_string("quit");
|
||||
while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -106,12 +106,12 @@ static void test_track_selection(char *file, char *path)
|
||||
check_string("current-tracks/sub/selected", "yes");
|
||||
|
||||
// --subs-falback=no
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-fallback", "no"));
|
||||
set_property_string("subs-fallback", "no");
|
||||
reload_file(path);
|
||||
check_string("track-list/2/selected", "no");
|
||||
} else if (strcmp(file, "eng_default_forced.mkv") == 0) {
|
||||
// --subs-fallback-forced=no
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-fallback-forced", "no"));
|
||||
set_property_string("subs-fallback-forced", "no");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/sub/selected", "yes");
|
||||
} else if (strcmp(file, "eng_forced_matching_audio.mkv") == 0 ||
|
||||
@@ -125,7 +125,7 @@ static void test_track_selection(char *file, char *path)
|
||||
check_string("track-list/2/selected", "no");
|
||||
} else if (strcmp(file, "eng_forced_always_audio.mkv") == 0) {
|
||||
// forced track should be selected anyway despite no matching audio
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-fallback-forced", "always"));
|
||||
set_property_string("subs-fallback-forced", "always");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/sub/selected", "yes");
|
||||
} else if (strcmp(file, "eng_no_default.mkv") == 0) {
|
||||
@@ -134,7 +134,7 @@ static void test_track_selection(char *file, char *path)
|
||||
check_string("track-list/2/selected", "no");
|
||||
|
||||
// --subs-fallback=yes
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-fallback", "yes"));
|
||||
set_property_string("subs-fallback", "yes");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/sub/selected", "yes");
|
||||
} else if (strcmp(file, "locale.mkv") == 0) {
|
||||
@@ -143,26 +143,26 @@ static void test_track_selection(char *file, char *path)
|
||||
check_string("current-tracks/sub/lang", "eng");
|
||||
|
||||
// default german subs
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-match-os-language", "no"));
|
||||
set_property_string("subs-match-os-language", "no");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/sub/lang", "ger");
|
||||
} else if (strcmp(file, "multilang.mkv") == 0) {
|
||||
// --alang=jpn should select forced jpn subs
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "jpn"));
|
||||
set_property_string("alang", "jpn");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "jpn");
|
||||
check_string("current-tracks/sub/lang", "jpn");
|
||||
|
||||
// --alang=pol should select default, non-forced ger subs
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "pol"));
|
||||
set_property_string("alang", "pol");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "pol");
|
||||
check_string("current-tracks/sub/lang", "ger");
|
||||
|
||||
// --slang=eng and --subs-with-matching-audio should not pick any subs
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "eng"));
|
||||
check_api_error(mpv_set_property_string(ctx, "slang", "eng"));
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-with-matching-audio", "no"));
|
||||
set_property_string("alang", "eng");
|
||||
set_property_string("slang", "eng");
|
||||
set_property_string("subs-with-matching-audio", "no");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "eng");
|
||||
check_string("track-list/5/selected", "no");
|
||||
@@ -171,65 +171,65 @@ static void test_track_selection(char *file, char *path)
|
||||
check_string("track-list/8/selected", "no");
|
||||
|
||||
// --subs-with-matching-audio=forced checks
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-with-matching-audio", "forced"));
|
||||
set_property_string("subs-with-matching-audio", "forced");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "eng");
|
||||
check_string("current-tracks/sub/lang", "eng");
|
||||
|
||||
// forced jpn subs should be selected
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "jpn"));
|
||||
check_api_error(mpv_set_property_string(ctx, "slang", "jpn"));
|
||||
set_property_string("alang", "jpn");
|
||||
set_property_string("slang", "jpn");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "jpn");
|
||||
check_string("current-tracks/sub/lang", "jpn");
|
||||
|
||||
// default+forced eng subs should be selected
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "ger"));
|
||||
check_api_error(mpv_set_property_string(ctx, "slang", "ger"));
|
||||
set_property_string("alang", "ger");
|
||||
set_property_string("slang", "ger");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "ger");
|
||||
check_string("current-tracks/sub/lang", "eng");
|
||||
|
||||
// eng audio and pol subs should be selected
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "it"));
|
||||
check_api_error(mpv_set_property_string(ctx, "slang", "pt,it,pol,ger"));
|
||||
set_property_string("alang", "it");
|
||||
set_property_string("slang", "pt,it,pol,ger");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "eng");
|
||||
check_string("current-tracks/sub/lang", "pol");
|
||||
|
||||
// forced jpn subs should be selected
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "ger"));
|
||||
check_api_error(mpv_set_property_string(ctx, "slang", "jpn,pol"));
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-with-matching-audio", "yes"));
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-fallback-forced", "always"));
|
||||
set_property_string("alang", "ger");
|
||||
set_property_string("slang", "jpn,pol");
|
||||
set_property_string("subs-with-matching-audio", "yes");
|
||||
set_property_string("subs-fallback-forced", "always");
|
||||
reload_file(path);
|
||||
check_string("current-tracks/audio/lang", "ger");
|
||||
check_string("current-tracks/sub/lang", "jpn");
|
||||
} else if (strcmp(file, "multilang2.mkv") == 0) {
|
||||
// default jpn subs
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-match-os-language", "no"));
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "jpn"));
|
||||
set_property_string("subs-match-os-language", "no");
|
||||
set_property_string("alang", "jpn");
|
||||
reload_file(path);
|
||||
check_string("track-list/3/selected", "yes");
|
||||
|
||||
// forced eng subs
|
||||
check_api_error(mpv_set_property_string(ctx, "alang", "eng"));
|
||||
set_property_string("alang", "eng");
|
||||
reload_file(path);
|
||||
check_string("track-list/4/selected", "yes");
|
||||
|
||||
// default jpn subs
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-fallback-forced", "no"));
|
||||
set_property_string("subs-fallback-forced", "no");
|
||||
reload_file(path);
|
||||
check_string("track-list/3/selected", "yes");
|
||||
|
||||
// default eng subs
|
||||
check_api_error(mpv_set_property_string(ctx, "slang", "eng"));
|
||||
set_property_string("slang", "eng");
|
||||
reload_file(path);
|
||||
check_string("track-list/6/selected", "yes");
|
||||
|
||||
// no subs
|
||||
check_api_error(mpv_set_property_string(ctx, "slang", ""));
|
||||
check_api_error(mpv_set_property_string(ctx, "subs-fallback", "no"));
|
||||
set_property_string("slang", "");
|
||||
set_property_string("subs-fallback", "no");
|
||||
reload_file(path);
|
||||
check_string("track-list/3/selected", "no");
|
||||
check_string("track-list/4/selected", "no");
|
||||
@@ -237,7 +237,7 @@ static void test_track_selection(char *file, char *path)
|
||||
check_string("track-list/6/selected", "no");
|
||||
|
||||
// untagged eng subs
|
||||
check_api_error(mpv_set_property_string(ctx, "sid", "3"));
|
||||
set_property_string("sid", "3");
|
||||
reload_file(path);
|
||||
check_string("track-list/5/selected", "yes");
|
||||
}
|
||||
@@ -266,7 +266,7 @@ int main(int argc, char *argv[])
|
||||
test_track_selection(argv[1], argv[2]);
|
||||
printf("================ SHUTDOWN ================\n");
|
||||
|
||||
mpv_command_string(ctx, "quit");
|
||||
command_string("quit");
|
||||
while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in new issue
Block a user