summaryrefslogtreecommitdiffstats
path: root/player/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/command.c')
-rw-r--r--player/command.c834
1 files changed, 565 insertions, 269 deletions
diff --git a/player/command.c b/player/command.c
index f4c10d48b9..1fa4695a91 100644
--- a/player/command.c
+++ b/player/command.c
@@ -68,7 +68,14 @@
#include "core.h"
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
struct command_ctx {
+ // All properties, terminated with a {0} item.
+ struct m_property *properties;
+
bool is_idle;
double last_seek_time;
@@ -94,6 +101,9 @@ struct command_ctx {
int64_t hook_seq; // for hook_handler.seq
struct ao_hotplug *hotplug;
+
+ char *cur_ipc;
+ char *cur_ipc_input;
};
struct overlay {
@@ -123,6 +133,9 @@ static int edit_filters(struct MPContext *mpctx, struct mp_log *log,
static int set_filters(struct MPContext *mpctx, enum stream_type mediatype,
struct m_obj_settings *new_chain);
+static int mp_property_do_silent(const char *name, int action, void *val,
+ struct MPContext *ctx);
+
static void hook_remove(struct MPContext *mpctx, int index)
{
struct command_ctx *cmd = mpctx->command_ctx;
@@ -174,6 +187,7 @@ void mp_hook_run(struct MPContext *mpctx, char *client, char *type)
if (h->active && strcmp(h->type, type) == 0) {
h->active = false;
found_current = true;
+ mp_wakeup_core(mpctx);
}
} else if (strcmp(h->type, type) == 0) {
index = n;
@@ -187,7 +201,7 @@ void mp_hook_run(struct MPContext *mpctx, char *client, char *type)
next->active = true;
if (!send_hook_msg(mpctx, next, "hook_run")) {
hook_remove(mpctx, index);
- mp_input_wakeup(mpctx->input); // repeat next iteration to finish
+ mp_wakeup_core(mpctx); // repeat next iteration to finish
}
}
@@ -250,12 +264,83 @@ static char *format_delay(double time)
return talloc_asprintf(NULL, "%d ms", (int)lrint(time * 1000));
}
+// Option-property bridge. This is used so that setting options via various
+// mechanisms (including command line parsing, config files, per-file options)
+// updates state associated with them. For that, they have to go through the
+// property layer. (Ideally, this would be the other way around, and there
+// would be per-option change handlers instead.)
+// Note that the property-option bridge sidesteps this, as we'd get infinite
+// recursion.
+int mp_on_set_option(void *ctx, struct m_config_option *co, void *data, int flags)
+{
+ struct MPContext *mpctx = ctx;
+
+ // These options are too inconsistent as they could be pulled through the
+ // property layer. Ideally we'd remove these inconsistencies in the future,
+ // though the actual problem is compatibility to user-expected behavior.
+ // What matters is whether _write_ access is different - property read
+ // access is not used here.
+ // We're also fine with cases where the property restricts the writable
+ // value range if playback is active, but not otherwise.
+ // OK, restrict during playback: vid, aid, sid, deinterlace, video-aspect,
+ // vf*, af*, chapter
+ // OK, is handled separately: playlist
+ // OK, does not conflict on low level: audio-file, sub-file, external-file
+ // OK, different value ranges, but happens to work for now: volume, edition
+ // All the other properties are deprecated in their current form.
+ static const char *const no_property[] = {
+ "demuxer", "idle", "length", "audio-samplerate", "audio-channels",
+ "audio-format", "fps", "cache", "playlist-pos", "chapter",
+ NULL
+ };
+
+ // Normalize "vf*" to "vf"
+ const char *name = co->name;
+ bstr bname = bstr0(name);
+ char tmp[50];
+ if (bstr_eatend0(&bname, "*")) {
+ snprintf(tmp, sizeof(name), "%.*s", BSTR_P(bname));
+ name = tmp;
+ }
+
+ for (int n = 0; no_property[n]; n++) {
+ if (strcmp(co->name, no_property[n]) == 0)
+ goto direct_option;
+ }
+
+ struct m_option type = {0};
+
+ int r = mp_property_do_silent(name, M_PROPERTY_GET_TYPE, &type, mpctx);
+ if (r == M_PROPERTY_UNKNOWN)
+ goto direct_option; // not mapped as property
+ if (r != M_PROPERTY_OK)
+ return M_OPT_INVALID; // shouldn't happen
+
+ assert(type.type == co->opt->type);
+ assert(type.max == co->opt->max);
+ assert(type.min == co->opt->min);
+
+ r = mp_property_do_silent(name, M_PROPERTY_SET, data, mpctx);
+ if (r != M_PROPERTY_OK)
+ return M_OPT_INVALID;
+
+ // The flags can't be passed through the property layer correctly.
+ m_config_mark_co_flags(co, flags);
+
+ return 0;
+
+direct_option:
+ mp_notify_property(mpctx, name);
+ return m_config_set_option_raw_direct(mpctx->mconfig, co, data, flags);
+}
+
// Property-option bridge. (Maps the property to the option with the same name.)
static int mp_property_generic_option(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
const char *optname = prop->name;
+ int flags = M_SETOPT_RUNTIME;
struct m_config_option *opt = m_config_get_co(mpctx->mconfig,
bstr0(optname));
@@ -272,12 +357,24 @@ static int mp_property_generic_option(void *ctx, struct m_property *prop,
m_option_copy(opt->opt, arg, valptr);
return M_PROPERTY_OK;
case M_PROPERTY_SET:
- m_option_copy(opt->opt, valptr, arg);
+ if (m_config_set_option_raw_direct(mpctx->mconfig, opt, arg, flags) < 0)
+ return M_PROPERTY_ERROR;
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
+// Dumb special-case: the option name ends in a "*".
+static int mp_property_generic_option_star(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ struct m_property prop2 = *prop;
+ char name[80];
+ snprintf(name, sizeof(name), "%s*", prop->name);
+ prop2.name = name;
+ return mp_property_generic_option(ctx, &prop2, action, arg);
+}
+
/// Playback speed (RW)
static int mp_property_playback_speed(void *ctx, struct m_property *prop,
int action, void *arg)
@@ -288,6 +385,7 @@ static int mp_property_playback_speed(void *ctx, struct m_property *prop,
case M_PROPERTY_SET: {
mpctx->opts->playback_speed = *(double *)arg;
update_playback_speed(mpctx);
+ mp_wakeup_core(mpctx);
return M_PROPERTY_OK;
}
case M_PROPERTY_PRINT:
@@ -449,10 +547,7 @@ static int mp_property_stream_capture(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->demuxer)
- return M_PROPERTY_UNAVAILABLE;
-
- if (action == M_PROPERTY_SET) {
+ if (mpctx->demuxer && action == M_PROPERTY_SET) {
struct change_stream_capture_args args = {*(char **)arg, mpctx->demuxer};
demux_run_on_thread(mpctx->demuxer, do_change_stream_capture, &args);
// fall through to mp_property_generic_option
@@ -576,7 +671,7 @@ static int mp_property_drop_frame_cnt(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!mpctx->vo_chain)
+ if (!mpctx->vo_chain)
return M_PROPERTY_UNAVAILABLE;
return m_property_int_ro(action, arg, mpctx->vo_chain->video_src->dropped_frames);
@@ -696,6 +791,18 @@ static int mp_property_time_pos(void *ctx, struct m_property *prop,
return property_time(action, arg, get_current_time(mpctx));
}
+/// Current audio pts in seconds (R)
+static int mp_property_audio_pts(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ if (!mpctx->playback_initialized || mpctx->audio_status < STATUS_PLAYING ||
+ mpctx->audio_status >= STATUS_EOF)
+ return M_PROPERTY_UNAVAILABLE;
+
+ return property_time(action, arg, playing_audio_pts(mpctx));
+}
+
static bool time_remaining(MPContext *mpctx, double *remaining)
{
double len = get_time_length(mpctx);
@@ -783,6 +890,9 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
+ if (!mpctx->playback_initialized)
+ return M_PROPERTY_UNAVAILABLE;
+
int chapter = get_current_chapter(mpctx);
int num = get_chapter_count(mpctx);
if (chapter < -1)
@@ -820,7 +930,9 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
if (current_chapter_start != MP_NOPTS_VALUE &&
get_current_time(mpctx) - current_chapter_start >
mpctx->opts->chapter_seek_threshold)
+ {
step_all++;
+ }
}
} else // Absolute set
step_all = *(int *)arg - chapter;
@@ -836,6 +948,7 @@ static int mp_property_chapter(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
if (!mpctx->stop_play)
mpctx->stop_play = PT_NEXT_ENTRY;
+ mp_wakeup_core(mpctx);
}
} else {
double pts = chapter_start_time(mpctx, chapter);
@@ -865,12 +978,75 @@ static int get_chapter_entry(int item, int action, void *arg, void *ctx)
return r;
}
+static int parse_node_chapters(struct MPContext *mpctx,
+ struct mpv_node *given_chapters)
+{
+ if (!mpctx->demuxer)
+ return M_PROPERTY_UNAVAILABLE;
+
+ if (given_chapters->format != MPV_FORMAT_NODE_ARRAY)
+ return M_PROPERTY_ERROR;
+
+ double len = get_time_length(mpctx);
+
+ talloc_free(mpctx->chapters);
+ mpctx->num_chapters = 0;
+ mpctx->chapters = talloc_array(NULL, struct demux_chapter, 0);
+
+ for (int n = 0; n < given_chapters->u.list->num; n++) {
+ struct mpv_node *chapter_data = &given_chapters->u.list->values[n];
+
+ if (chapter_data->format != MPV_FORMAT_NODE_MAP)
+ continue;
+
+ mpv_node_list *chapter_data_elements = chapter_data->u.list;
+
+ double time = -1;
+ char *title = 0;
+
+ for (int e = 0; e < chapter_data_elements->num; e++) {
+ struct mpv_node *chapter_data_element =
+ &chapter_data_elements->values[e];
+ char *key = chapter_data_elements->keys[e];
+ switch (chapter_data_element->format) {
+ case MPV_FORMAT_INT64:
+ if (strcmp(key, "time") == 0)
+ time = (double)chapter_data_element->u.int64;
+ break;
+ case MPV_FORMAT_DOUBLE:
+ if (strcmp(key, "time") == 0)
+ time = chapter_data_element->u.double_;
+ break;
+ case MPV_FORMAT_STRING:
+ if (strcmp(key, "title") == 0)
+ title = chapter_data_element->u.string;
+ break;
+ }
+ }
+
+ if (time >= 0 && time < len) {
+ struct demux_chapter new = {
+ .pts = time,
+ .metadata = talloc_zero(mpctx->chapters, struct mp_tags),
+ };
+ if (title)
+ mp_tags_set_str(new.metadata, "title", title);
+ MP_TARRAY_APPEND(NULL, mpctx->chapters, mpctx->num_chapters, new);
+ }
+ }
+
+ mp_notify(mpctx, MPV_EVENT_CHAPTER_CHANGE, NULL);
+
+ return M_PROPERTY_OK;
+}
+
static int mp_property_list_chapters(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
int count = get_chapter_count(mpctx);
- if (action == M_PROPERTY_PRINT) {
+ switch (action) {
+ case M_PROPERTY_PRINT: {
int cur = mpctx->playback_initialized ? get_current_chapter(mpctx) : -1;
char *res = NULL;
int n;
@@ -893,6 +1069,11 @@ static int mp_property_list_chapters(void *ctx, struct m_property *prop,
*(char **)arg = res;
return M_PROPERTY_OK;
}
+ case M_PROPERTY_SET: {
+ struct mpv_node *given_chapters = arg;
+ return parse_node_chapters(mpctx, given_chapters);
+ }
+ }
return m_property_read_list(action, arg, count, get_chapter_entry, mpctx);
}
@@ -900,12 +1081,9 @@ static int mp_property_edition(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- struct MPOpts *opts = mpctx->opts;
struct demuxer *demuxer = mpctx->demuxer;
- if (!demuxer)
- return M_PROPERTY_UNAVAILABLE;
- if (demuxer->num_editions <= 0)
- return M_PROPERTY_UNAVAILABLE;
+ if (!mpctx->playback_initialized || !demuxer || demuxer->num_editions <= 0)
+ return mp_property_generic_option(mpctx, prop, action, arg);
int edition = demuxer->edition;
@@ -916,24 +1094,21 @@ static int mp_property_edition(void *ctx, struct m_property *prop,
case M_PROPERTY_SET: {
edition = *(int *)arg;
if (edition != demuxer->edition) {
- opts->edition_id = edition;
+ mpctx->opts->edition_id = edition;
if (!mpctx->stop_play)
mpctx->stop_play = PT_RELOAD_FILE;
+ mp_wakeup_core(mpctx);
+ break; // make it accessible to the demuxer via option change notify
}
return M_PROPERTY_OK;
}
- case M_PROPERTY_GET_TYPE: {
- struct m_option opt = {
- .type = CONF_TYPE_INT,
- .flags = CONF_RANGE,
- .min = 0,
- .max = demuxer->num_editions - 1,
- };
- *(struct m_option *)arg = opt;
- return M_PROPERTY_OK;
+ case M_PROPERTY_GET_CONSTRICTED_TYPE: {
+ int r = mp_property_generic_option(mpctx, prop, M_PROPERTY_GET_TYPE, arg);
+ ((struct m_option *)arg)->max = demuxer->num_editions - 1;
+ return r;
}
}
- return M_PROPERTY_NOT_IMPLEMENTED;
+ return mp_property_generic_option(mpctx, prop, action, arg);
}
static int get_edition_entry(int item, int action, void *arg, void *ctx)
@@ -1105,6 +1280,7 @@ static int mp_property_angle(void *ctx, struct m_property *prop,
reset_audio_state(mpctx);
reset_video_state(mpctx);
+ mp_wakeup_core(mpctx);
return ris == STREAM_OK ? M_PROPERTY_OK : M_PROPERTY_ERROR;
case M_PROPERTY_GET_TYPE: {
@@ -1284,7 +1460,7 @@ static int mp_property_pause(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
- if (action == M_PROPERTY_SET) {
+ if (mpctx->playback_initialized && action == M_PROPERTY_SET) {
if (*(int *)arg) {
pause_player(mpctx);
} else {
@@ -1580,7 +1756,7 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
struct MPOpts *opts = mpctx->opts;
switch (action) {
- case M_PROPERTY_GET_TYPE:
+ case M_PROPERTY_GET_CONSTRICTED_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_FLOAT,
.flags = M_OPT_RANGE,
@@ -1608,7 +1784,7 @@ static int mp_property_mute(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
- if (action == M_PROPERTY_GET_TYPE) {
+ if (action == M_PROPERTY_GET_CONSTRICTED_TYPE) {
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
return M_PROPERTY_OK;
}
@@ -1705,11 +1881,14 @@ static int get_device_entry(int item, int action, void *arg, void *ctx)
return m_property_read_sub(props, action, arg);
}
-static void reload_audio_output(struct MPContext *mpctx)
+static void create_hotplug(struct MPContext *mpctx)
{
- if (!mpctx->ao)
- return;
- ao_request_reload(mpctx->ao);
+ struct command_ctx *cmd = mpctx->command_ctx;
+
+ if (!cmd->hotplug) {
+ cmd->hotplug = ao_hotplug_create(mpctx->global, mp_wakeup_core_cb,
+ mpctx);
+ }
}
static int mp_property_audio_device(void *ctx, struct m_property *prop,
@@ -1718,8 +1897,8 @@ static int mp_property_audio_device(void *ctx, struct m_property *prop,
struct MPContext *mpctx = ctx;
struct command_ctx *cmd = mpctx->command_ctx;
if (action == M_PROPERTY_PRINT) {
- if (!cmd->hotplug)
- cmd->hotplug = ao_hotplug_create(mpctx->global, mpctx->input);
+ create_hotplug(mpctx);
+
struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug);
for (int n = 0; n < list->num_devices; n++) {
struct ao_device_desc *dev = &list->devices[n];
@@ -1729,10 +1908,7 @@ static int mp_property_audio_device(void *ctx, struct m_property *prop,
}
}
}
- int r = mp_property_generic_option(mpctx, prop, action, arg);
- if (action == M_PROPERTY_SET)
- reload_audio_output(mpctx);
- return r;
+ return mp_property_generic_option(mpctx, prop, action, arg);
}
static int mp_property_audio_devices(void *ctx, struct m_property *prop,
@@ -1740,8 +1916,7 @@ static int mp_property_audio_devices(void *ctx, struct m_property *prop,
{
struct MPContext *mpctx = ctx;
struct command_ctx *cmd = mpctx->command_ctx;
- if (!cmd->hotplug)
- cmd->hotplug = ao_hotplug_create(mpctx->global, mpctx->input);
+ create_hotplug(mpctx);
struct ao_device_list *list = ao_hotplug_get_device_list(cmd->hotplug);
return m_property_read_list(action, arg, list->num_devices,
@@ -1760,8 +1935,7 @@ static int mp_property_ao_detected_device(void *ctx,struct m_property *prop,
{
struct MPContext *mpctx = ctx;
struct command_ctx *cmd = mpctx->command_ctx;
- if (!cmd->hotplug)
- cmd->hotplug = ao_hotplug_create(mpctx->global, mpctx->input);
+ create_hotplug(mpctx);
const char *d = ao_hotplug_get_detected_device(cmd->hotplug);
return m_property_strdup_ro(action, arg, d);
@@ -1772,8 +1946,6 @@ static int mp_property_audio_delay(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- if (!(mpctx->ao_chain && mpctx->vo_chain))
- return M_PROPERTY_UNAVAILABLE;
float delay = mpctx->opts->audio_delay;
switch (action) {
case M_PROPERTY_PRINT:
@@ -1781,7 +1953,9 @@ static int mp_property_audio_delay(void *ctx, struct m_property *prop,
return M_PROPERTY_OK;
case M_PROPERTY_SET:
mpctx->opts->audio_delay = *(float *)arg;
- mpctx->delay += mpctx->opts->audio_delay - delay;
+ if (mpctx->ao_chain && mpctx->vo_chain)
+ mpctx->delay += mpctx->opts->audio_delay - delay;
+ mp_wakeup_core(mpctx);
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
@@ -1932,9 +2106,10 @@ static int property_switch_track(struct m_property *prop, int action, void *arg,
if (!mpctx->playback_initialized)
return M_PROPERTY_ERROR;
struct m_property_switch_arg *sarg = arg;
- mp_switch_track_n(mpctx, order, type,
- track_next(mpctx, type, sarg->inc >= 0 ? +1 : -1, track),
- FLAG_MARK_SELECTION);
+ do {
+ track = track_next(mpctx, type, sarg->inc >= 0 ? +1 : -1, track);
+ mp_switch_track_n(mpctx, order, type, track, FLAG_MARK_SELECTION);
+ } while (mpctx->current_track[order][type] != track);
print_track_list(mpctx, "Track switched:");
return M_PROPERTY_OK;
}
@@ -1943,6 +2118,7 @@ static int property_switch_track(struct m_property *prop, int action, void *arg,
track = mp_track_by_tid(mpctx, type, *(int *)arg);
mp_switch_track_n(mpctx, order, type, track, FLAG_MARK_SELECTION);
print_track_list(mpctx, "Track switched:");
+ mp_wakeup_core(mpctx);
} else {
mpctx->opts->stream_id[order][type] = *(int *)arg;
}
@@ -2143,7 +2319,7 @@ static int mp_property_program(void *ctx, struct m_property *prop,
demux_program_t prog;
struct demuxer *demuxer = mpctx->demuxer;
- if (!demuxer)
+ if (!demuxer || !mpctx->playback_initialized)
return M_PROPERTY_UNAVAILABLE;
switch (action) {
@@ -2256,54 +2432,6 @@ static int mp_property_hwdec_interop(void *ctx, struct m_property *prop,
return m_property_strdup_ro(action, arg, name);
}
-static int mp_property_hwdec_active(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- struct dec_video *vd = track ? track->d_video : NULL;
- bool active = false;
- if (vd) {
- int current = 0;
- video_vd_control(vd, VDCTRL_GET_HWDEC, &current);
- active = current > 0;
- }
- return m_property_flag_ro(action, arg, active);
-}
-
-static int mp_property_detected_hwdec(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- struct track *track = mpctx->current_track[0][STREAM_VIDEO];
- struct dec_video *vd = track ? track->d_video : NULL;
-
- switch (action) {
- case M_PROPERTY_GET_TYPE: {
- // Abuse another hwdec option to resolve the value names
- struct m_property dummy = {.name = "hwdec"};
- return mp_property_generic_option(mpctx, &dummy, action, arg);
- }
- case M_PROPERTY_GET: {
- int current = 0;
- if (vd)
- video_vd_control(vd, VDCTRL_GET_HWDEC, &current);
-
- if (current <= 0 && vd && vd->hwdec_devs) {
- struct mp_hwdec_ctx *hwctx = hwdec_devices_get_first(vd->hwdec_devs);
- if (hwctx)
- current = hwctx->type;
- }
-
- // In case of the "-copy" ones, which are "detected" every time the
- // decoder is opened, return "no" if no decoding is active.
- *(int *)arg = current > 0 ? current : 0;
- return M_PROPERTY_OK;
- }
- }
- return M_PROPERTY_NOT_IMPLEMENTED;
-}
-
static int mp_property_deinterlace(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2314,36 +2442,14 @@ static int mp_property_deinterlace(void *ctx, struct m_property *prop,
case M_PROPERTY_GET:
*(int *)arg = get_deinterlacing(mpctx) > 0;
return M_PROPERTY_OK;
- case M_PROPERTY_GET_TYPE:
+ case M_PROPERTY_GET_CONSTRICTED_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
return M_PROPERTY_OK;
case M_PROPERTY_SET:
set_deinterlacing(mpctx, *(int *)arg);
return M_PROPERTY_OK;
}
- return M_PROPERTY_NOT_IMPLEMENTED;
-}
-
-static int video_simple_refresh_property(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- int r = mp_property_generic_option(mpctx, prop, action, arg);
- if (action == M_PROPERTY_SET && r == M_PROPERTY_OK)
- mp_force_video_refresh(mpctx);
- return r;
-}
-
-// Update options which are managed through VOCTRL_GET/SET_PANSCAN.
-static int panscan_property_helper(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
-
- int r = mp_property_generic_option(mpctx, prop, action, arg);
- if (mpctx->video_out && action == M_PROPERTY_SET)
- vo_control(mpctx->video_out, VOCTRL_SET_PANSCAN, NULL);
- return r;
+ return mp_property_generic_option(mpctx, prop, action, arg);
}
/// Helper to set vo flags.
@@ -2352,18 +2458,13 @@ static int panscan_property_helper(void *ctx, struct m_property *prop,
static int mp_property_vo_flag(struct m_property *prop, int action, void *arg,
int vo_ctrl, int *vo_var, MPContext *mpctx)
{
- if (action == M_PROPERTY_SET) {
- int desired = !!*(int *) arg;
- if (*vo_var == desired)
- return M_PROPERTY_OK;
- if (mpctx->video_out) {
+ int old = *vo_var;
+ int res = mp_property_generic_option(mpctx, prop, action, arg);
+ if (action == M_PROPERTY_SET && old != *vo_var) {
+ if (mpctx->video_out)
vo_control(mpctx->video_out, vo_ctrl, 0);
- } else {
- *vo_var = desired;
- }
- return *vo_var == desired ? M_PROPERTY_OK : M_PROPERTY_ERROR;
}
- return mp_property_generic_option(mpctx, prop, action, arg);
+ return res;
}
/// Fullscreen state (RW)
@@ -2371,10 +2472,10 @@ static int mp_property_fullscreen(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
- int oldval = mpctx->opts->vo.fullscreen;
+ int oldval = mpctx->opts->vo->fullscreen;
int r = mp_property_vo_flag(prop, action, arg, VOCTRL_FULLSCREEN,
- &mpctx->opts->vo.fullscreen, mpctx);
- if (oldval && oldval != mpctx->opts->vo.fullscreen)
+ &mpctx->opts->vo->fullscreen, mpctx);
+ if (oldval && oldval != mpctx->opts->vo->fullscreen)
mpctx->mouse_event_ts--; // Show mouse cursor
return r;
}
@@ -2386,11 +2487,11 @@ static int mp_property_taskbar_progress(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
if (action == M_PROPERTY_SET) {
int desired = !!*(int *) arg;
- if (mpctx->opts->vo.taskbar_progress == desired)
+ if (mpctx->opts->vo->taskbar_progress == desired)
return M_PROPERTY_OK;
- mpctx->opts->vo.taskbar_progress = desired;
+ mpctx->opts->vo->taskbar_progress = desired;
if (mpctx->video_out)
- update_vo_playback_state( mpctx );
+ update_vo_playback_state(mpctx);
return M_PROPERTY_OK;
}
return mp_property_generic_option(mpctx, prop, action, arg);
@@ -2402,7 +2503,7 @@ static int mp_property_ontop(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
return mp_property_vo_flag(prop, action, arg, VOCTRL_ONTOP,
- &mpctx->opts->vo.ontop, mpctx);
+ &mpctx->opts->vo->ontop, mpctx);
}
/// Show window borders (RW)
@@ -2411,7 +2512,7 @@ static int mp_property_border(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
return mp_property_vo_flag(prop, action, arg, VOCTRL_BORDER,
- &mpctx->opts->vo.border, mpctx);
+ &mpctx->opts->vo->border, mpctx);
}
static int mp_property_all_workspaces(void *ctx, struct m_property *prop,
@@ -2419,7 +2520,7 @@ static int mp_property_all_workspaces(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
return mp_property_vo_flag(prop, action, arg, VOCTRL_ALL_WORKSPACES,
- &mpctx->opts->vo.all_workspaces, mpctx);
+ &mpctx->opts->vo->all_workspaces, mpctx);
}
static int get_frame_count(struct MPContext *mpctx)
@@ -2466,7 +2567,7 @@ static int mp_property_video_color(void *ctx, struct m_property *prop,
const char *name = prop->priv ? prop->priv : prop->name;
MPContext *mpctx = ctx;
if (!mpctx->vo_chain)
- return M_PROPERTY_UNAVAILABLE;
+ return mp_property_generic_option(mpctx, prop, action, arg);
switch (action) {
case M_PROPERTY_SET: {
@@ -2569,6 +2670,19 @@ static int mp_property_vo_imgparams(void *ctx, struct m_property *prop,
return property_imgparams(get_video_out_params(ctx), action, arg);
}
+static int mp_property_dec_imgparams(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct mp_image_params p = {0};
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ if (vo_c && vo_c->video_src)
+ video_get_dec_params(vo_c->video_src, &p);
+ if (!p.imgfmt)
+ return M_PROPERTY_UNAVAILABLE;
+ return property_imgparams(p, action, arg);
+}
+
static int mp_property_vd_imgparams(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2625,7 +2739,7 @@ static int mp_property_window_scale(void *ctx, struct m_property *prop,
MPContext *mpctx = ctx;
struct vo *vo = mpctx->video_out;
if (!vo)
- return M_PROPERTY_UNAVAILABLE;
+ return mp_property_generic_option(mpctx, prop, action, arg);
struct mp_image_params params = get_video_out_params(mpctx);
int vid_w, vid_h;
@@ -2639,7 +2753,10 @@ static int mp_property_window_scale(void *ctx, struct m_property *prop,
int s[2] = {vid_w * scale, vid_h * scale};
if (s[0] > 0 && s[1] > 0 &&
vo_control(vo, VOCTRL_SET_UNFS_WINDOW_SIZE, s) > 0)
+ {
+ mpctx->opts->vo->window_scale = scale;
return M_PROPERTY_OK;
+ }
return M_PROPERTY_UNAVAILABLE;
}
case M_PROPERTY_GET: {
@@ -2653,13 +2770,7 @@ static int mp_property_window_scale(void *ctx, struct m_property *prop,
return M_PROPERTY_OK;
}
case M_PROPERTY_GET_TYPE:
- *(struct m_option *)arg = (struct m_option){
- .type = CONF_TYPE_DOUBLE,
- .flags = CONF_RANGE,
- .min = 0.125,
- .max = 8,
- };
- return M_PROPERTY_OK;
+ return mp_property_generic_option(mpctx, prop, action, arg);
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
@@ -2696,6 +2807,16 @@ static int mp_property_display_fps(void *ctx, struct m_property *prop,
return m_property_double_ro(action, arg, fps);
}
+static int mp_property_framedrop(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ int ret = mp_property_generic_option(mpctx, prop, action, arg);
+ if (action == M_PROPERTY_SET && ret == M_PROPERTY_OK && mpctx->video_out)
+ vo_event(mpctx->video_out, VO_EVENT_WIN_STATE);
+ return ret;
+}
+
static int mp_property_estimated_display_fps(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2885,16 +3006,8 @@ static int mp_property_aspect(void *ctx, struct m_property *prop,
}
switch (action) {
- case M_PROPERTY_SET: {
- mpctx->opts->movie_aspect = *(float *)arg;
- if (track && track->d_video) {
- video_reset_aspect(track->d_video);
- mp_force_video_refresh(mpctx);
- }
- return M_PROPERTY_OK;
- }
case M_PROPERTY_PRINT: {
- if (mpctx->opts->movie_aspect <= 0) {
+ if (mpctx->opts->movie_aspect < 0) {
*(char **)arg = talloc_asprintf(NULL, "%.3f (original)", aspect);
return M_PROPERTY_OK;
}
@@ -2904,27 +3017,7 @@ static int mp_property_aspect(void *ctx, struct m_property *prop,
*(float *)arg = aspect;
return M_PROPERTY_OK;
}
- case M_PROPERTY_GET_TYPE:
- *(struct m_option *)arg = (struct m_option){
- .type = CONF_TYPE_FLOAT,
- .flags = CONF_RANGE,
- .min = -1,
- .max = 10,
- };
- return M_PROPERTY_OK;
}
- return M_PROPERTY_NOT_IMPLEMENTED;
-}
-
-// For OSD and subtitle related properties using the generic option bridge.
-// - Fail as unavailable if no video is active
-// - Trigger OSD state update when property is set
-static int property_osd_helper(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- if (action == M_PROPERTY_SET)
- osd_changed_all(mpctx->osd);
return mp_property_generic_option(mpctx, prop, action, arg);
}
@@ -2947,14 +3040,35 @@ static int mp_property_sub_delay(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
- if (!mpctx->video_out)
- return M_PROPERTY_UNAVAILABLE;
switch (action) {
case M_PROPERTY_PRINT:
*(char **)arg = format_delay(opts->sub_delay);
return M_PROPERTY_OK;
}
- return property_osd_helper(mpctx, prop, action, arg);
+ return mp_property_generic_option(mpctx, prop, action, arg);
+}
+
+/// Subtitle speed (RW)
+static int mp_property_sub_speed(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct MPOpts *opts = mpctx->opts;
+ switch (action) {
+ case M_PROPERTY_SET: {
+ opts->sub_speed = *(float *)arg;
+ struct track *track = mpctx->current_track[0][STREAM_SUB];
+ struct dec_sub *sub = track ? track->d_sub : NULL;
+ if (sub) {
+ sub_control(track->d_sub, SD_CTRL_UPDATE_SPEED, NULL);
+ }
+ return M_PROPERTY_OK;
+ }
+ case M_PROPERTY_PRINT:
+ *(char **)arg = talloc_asprintf(NULL, "%4.1f%%", 100 * opts->sub_speed);
+ return M_PROPERTY_OK;
+ }
+ return mp_property_generic_option(mpctx, prop, action, arg);
}
static int mp_property_sub_pos(void *ctx, struct m_property *prop,
@@ -2962,13 +3076,30 @@ static int mp_property_sub_pos(void *ctx, struct m_property *prop,
{
MPContext *mpctx = ctx;
struct MPOpts *opts = mpctx->opts;
- if (!mpctx->video_out)
- return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_PRINT) {
*(char **)arg = talloc_asprintf(NULL, "%d/100", opts->sub_pos);
return M_PROPERTY_OK;
}
- return property_osd_helper(mpctx, prop, action, arg);
+ return mp_property_generic_option(mpctx, prop, action, arg);
+}
+
+static int mp_property_sub_text(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct track *track = mpctx->current_track[0][STREAM_SUB];
+ struct dec_sub *sub = track ? track->d_sub : NULL;
+ double pts = mpctx->playback_pts;
+ if (!sub || pts == MP_NOPTS_VALUE)
+ return M_PROPERTY_UNAVAILABLE;
+
+ pts -= mpctx->opts->sub_delay;
+
+ char *text = sub_get_text(sub, pts);
+ if (!text)
+ text = "";
+
+ return m_property_strdup_ro(action, arg, text);
}
static int mp_property_cursor_autohide(void *ctx, struct m_property *prop,
@@ -3327,6 +3458,7 @@ static int mp_property_ab_loop(void *ctx, struct m_property *prop,
}
// Update if visible
set_osd_bar_chapters(mpctx, OSD_BAR_SEEK);
+ mp_wakeup_core(mpctx);
}
return r;
}
@@ -3515,6 +3647,7 @@ static int access_options(struct m_property_action_arg *ka, bool local,
return M_PROPERTY_ERROR;
int flags = M_SETOPT_RUNTIME | (local ? M_SETOPT_BACKUP : 0);
int r = m_config_set_option_raw(mpctx->mconfig, opt, ka->arg, flags);
+ mp_wakeup_core(mpctx);
return r < 0 ? M_PROPERTY_ERROR : M_PROPERTY_OK;
}
case M_PROPERTY_GET_TYPE:
@@ -3625,11 +3758,12 @@ static int mp_property_option_info(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
-static const struct m_property mp_properties[];
-
static int mp_property_list(void *ctx, struct m_property *prop,
int action, void *arg)
{
+ struct MPContext *mpctx = ctx;
+ struct command_ctx *cmd = mpctx->command_ctx;
+
switch (action) {
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_STRING_LIST};
@@ -3637,9 +3771,9 @@ static int mp_property_list(void *ctx, struct m_property *prop,
case M_PROPERTY_GET: {
char **list = NULL;
int num = 0;
- for (int n = 0; mp_properties[n].name; n++) {
+ for (int n = 0; cmd->properties[n].name; n++) {
MP_TARRAY_APPEND(NULL, list, num,
- talloc_strdup(NULL, mp_properties[n].name));
+ talloc_strdup(NULL, cmd->properties[n].name));
}
MP_TARRAY_APPEND(NULL, list, num, NULL);
*(char ***)arg = list;
@@ -3649,6 +3783,22 @@ static int mp_property_list(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
+static int mp_profile_list(void *ctx, struct m_property *prop,
+