diff options
Diffstat (limited to 'player/command.c')
-rw-r--r-- | player/command.c | 834 |
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, ¤t); - 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, ¤t); - - 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, + |