diff options
Diffstat (limited to '')
-rw-r--r-- | player/command.c | 680 |
1 files changed, 494 insertions, 186 deletions
diff --git a/player/command.c b/player/command.c index 8bff0cd..ff5ca35 100644 --- a/player/command.c +++ b/player/command.c @@ -55,7 +55,9 @@ #include "options/m_option.h" #include "options/m_property.h" #include "options/m_config_frontend.h" +#include "options/parse_configfile.h" #include "osdep/getpid.h" +#include "video/out/gpu/context.h" #include "video/out/vo.h" #include "video/csputils.h" #include "video/hwdec.h" @@ -72,6 +74,7 @@ #include "osdep/io.h" #include "osdep/subprocess.h" +#include "osdep/terminal.h" #include "core.h" @@ -91,6 +94,8 @@ struct command_ctx { char **warned_deprecated; int num_warned_deprecated; + bool command_opts_processed; + struct overlay *overlays; int num_overlays; // One of these is in use by the OSD; the other one exists so that the @@ -109,9 +114,9 @@ struct command_ctx { char **script_props; mpv_node udata; + mpv_node mdata; double cached_window_scale; - bool shared_script_warning; }; static const struct m_option script_props_type = { @@ -122,9 +127,14 @@ static const struct m_option udata_type = { .type = CONF_TYPE_NODE }; +static const struct m_option mdata_type = { + .type = CONF_TYPE_NODE +}; + struct overlay { struct mp_image *source; int x, y; + int dw, dh; }; struct hook_handler { @@ -137,12 +147,27 @@ struct hook_handler { bool active; // hook is currently in progress (only 1 at a time for now) }; -// U+279C HEAVY ROUND-TIPPED RIGHTWARDS ARROW +enum load_action_type { + LOAD_TYPE_REPLACE, + LOAD_TYPE_INSERT_AT, + LOAD_TYPE_INSERT_NEXT, + LOAD_TYPE_APPEND, +}; + +struct load_action { + enum load_action_type type; + bool play; +}; + +// U+25CB WHITE CIRCLE +// U+25CF BLACK CIRCLE // U+00A0 NO-BREAK SPACE -#define ARROW_SP "\342\236\234\302\240" +#define WHITECIRCLE "\xe2\x97\x8b" +#define BLACKCIRCLE "\xe2\x97\x8f" +#define NBSP "\xc2\xa0" -const char list_current[] = OSD_ASS_0 ARROW_SP OSD_ASS_1; -const char list_normal[] = OSD_ASS_0 "{\\alpha&HFF}" ARROW_SP "{\\r}" OSD_ASS_1; +const char list_current[] = BLACKCIRCLE NBSP; +const char list_normal[] = WHITECIRCLE NBSP; static int edit_filters(struct MPContext *mpctx, struct mp_log *log, enum stream_type mediatype, @@ -399,9 +424,9 @@ static int mp_property_playback_speed(void *ctx, struct m_property *prop, int action, void *arg) { MPContext *mpctx = ctx; - if (action == M_PROPERTY_PRINT) { - double speed = mpctx->opts->playback_speed; - *(char **)arg = talloc_asprintf(NULL, "%.2f", speed); + if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) { + *(char **)arg = mp_format_double(NULL, mpctx->opts->playback_speed, 2, + false, false, action != M_PROPERTY_FIXED_LEN_PRINT); return M_PROPERTY_OK; } return mp_property_generic_option(mpctx, prop, action, arg); @@ -419,8 +444,9 @@ static int mp_property_av_speed_correction(void *ctx, struct m_property *prop, default: MP_ASSERT_UNREACHABLE(); } - if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%+.3g%%", (val - 1) * 100); + if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) { + *(char **)arg = mp_format_double(NULL, (val - 1) * 100, 2, true, + true, action != M_PROPERTY_FIXED_LEN_PRINT); return M_PROPERTY_OK; } @@ -652,13 +678,9 @@ static int mp_property_avsync(void *ctx, struct m_property *prop, MPContext *mpctx = ctx; if (!mpctx->ao_chain || !mpctx->vo_chain) return M_PROPERTY_UNAVAILABLE; - if (action == M_PROPERTY_PRINT) { - // Truncate anything < 1e-4 to avoid switching to scientific notation - if (fabs(mpctx->last_av_difference) < 1e-4) { - *(char **)arg = talloc_strdup(NULL, "0"); - } else { - *(char **)arg = talloc_asprintf(NULL, "%+.2g", mpctx->last_av_difference); - } + if (action == M_PROPERTY_PRINT || action == M_PROPERTY_FIXED_LEN_PRINT) { + *(char **)arg = mp_format_double(NULL, mpctx->last_av_difference, 4, + true, false, action != M_PROPERTY_FIXED_LEN_PRINT); return M_PROPERTY_OK; } return m_property_double_ro(action, arg, mpctx->last_av_difference); @@ -1355,6 +1377,18 @@ static int mp_property_core_idle(void *ctx, struct m_property *prop, return m_property_bool_ro(action, arg, !mpctx->playback_active); } +static int mp_property_deinterlace(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + struct vo_chain *vo_c = mpctx->vo_chain; + if (!vo_c) + return M_PROPERTY_UNAVAILABLE; + + bool deinterlace_active = mp_output_chain_deinterlace_active(vo_c->filter); + return m_property_bool_ro(action, arg, deinterlace_active); +} + static int mp_property_idle(void *ctx, struct m_property *prop, int action, void *arg) { @@ -1624,6 +1658,28 @@ static int mp_property_volume(void *ctx, struct m_property *prop, return mp_property_generic_option(mpctx, prop, action, arg); } +static int mp_property_volume_gain(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + struct MPOpts *opts = mpctx->opts; + + switch (action) { + case M_PROPERTY_GET_CONSTRICTED_TYPE: + *(struct m_option *)arg = (struct m_option){ + .type = CONF_TYPE_FLOAT, + .min = opts->softvol_gain_min, + .max = opts->softvol_gain_max, + }; + return M_PROPERTY_OK; + case M_PROPERTY_PRINT: + *(char **)arg = talloc_asprintf(NULL, "%.1f", opts->softvol_gain); + return M_PROPERTY_OK; + } + + return mp_property_generic_option(mpctx, prop, action, arg); +} + static int mp_property_ao_volume(void *ctx, struct m_property *prop, int action, void *arg) { @@ -1758,8 +1814,7 @@ static int mp_property_audio_devices(void *ctx, struct m_property *prop, static int mp_property_ao(void *ctx, struct m_property *p, int action, void *arg) { MPContext *mpctx = ctx; - return m_property_strdup_ro(action, arg, - mpctx->ao ? ao_get_name(mpctx->ao) : NULL); + return m_property_strdup_ro(action, arg, mpctx->ao ? ao_get_name(mpctx->ao) : NULL); } /// Audio delay (RW) @@ -1774,28 +1829,6 @@ static int mp_property_audio_delay(void *ctx, struct m_property *prop, return mp_property_generic_option(mpctx, prop, action, arg); } -/// Audio codec tag (RO) -static int mp_property_audio_codec_name(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct track *track = mpctx->current_track[0][STREAM_AUDIO]; - const char *c = track && track->stream ? track->stream->codec->codec : NULL; - return m_property_strdup_ro(action, arg, c); -} - -/// Audio codec name (RO) -static int mp_property_audio_codec(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct track *track = mpctx->current_track[0][STREAM_AUDIO]; - char desc[256] = ""; - if (track && track->dec) - mp_decoder_wrapper_get_desc(track->dec, desc, sizeof(desc)); - return m_property_strdup_ro(action, arg, desc[0] ? desc : NULL); -} - static int property_audiofmt(struct mp_aframe *fmt, int action, void *arg) { if (!fmt || !mp_aframe_config_is_valid(fmt)) @@ -2000,6 +2033,10 @@ static int get_track_entry(int item, int action, void *arg, void *ctx) .unavailable = !decoder_desc[0]}, {"codec", SUB_PROP_STR(p.codec), .unavailable = !p.codec}, + {"codec-desc", SUB_PROP_STR(p.codec_desc), + .unavailable = !p.codec_desc}, + {"codec-profile", SUB_PROP_STR(p.codec_profile), + .unavailable = !p.codec_profile}, {"demux-w", SUB_PROP_INT(p.disp_w), .unavailable = !p.disp_w}, {"demux-h", SUB_PROP_INT(p.disp_h), .unavailable = !p.disp_h}, {"demux-crop-x",SUB_PROP_INT(p.crop.x0), .unavailable = !has_crop}, @@ -2016,6 +2053,7 @@ static int get_track_entry(int item, int action, void *arg, void *ctx) {"demux-bitrate", SUB_PROP_INT(p.bitrate), .unavailable = p.bitrate <= 0}, {"demux-rotation", SUB_PROP_INT(p.rotate), .unavailable = p.rotate <= 0}, {"demux-par", SUB_PROP_DOUBLE(par), .unavailable = par <= 0}, + {"format-name", SUB_PROP_STR(p.format_name), .unavailable = !p.format_name}, {"replaygain-track-peak", SUB_PROP_FLOAT(rg.track_peak), .unavailable = !has_rg}, {"replaygain-track-gain", SUB_PROP_FLOAT(rg.track_gain), @@ -2208,28 +2246,6 @@ static int mp_property_frame_count(void *ctx, struct m_property *prop, return m_property_int_ro(action, arg, frames); } -/// Video codec tag (RO) -static int mp_property_video_format(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct track *track = mpctx->current_track[0][STREAM_VIDEO]; - const char *c = track && track->stream ? track->stream->codec->codec : NULL; - return m_property_strdup_ro(action, arg, c); -} - -/// Video codec name (RO) -static int mp_property_video_codec(void *ctx, struct m_property *prop, - int action, void *arg) -{ - MPContext *mpctx = ctx; - struct track *track = mpctx->current_track[0][STREAM_VIDEO]; - char desc[256] = ""; - if (track && track->dec) - mp_decoder_wrapper_get_desc(track->dec, desc, sizeof(desc)); - return m_property_strdup_ro(action, arg, desc[0] ? desc : NULL); -} - static const char *get_aspect_ratio_name(double ratio) { // Depending on cropping/mastering exact ratio may differ. @@ -2272,73 +2288,77 @@ static const char *get_aspect_ratio_name(double ratio) #undef RATIO_CASE } -static int property_imgparams(struct mp_image_params p, int action, void *arg) +static int property_imgparams(const struct mp_image_params *p, int action, void *arg) { - if (!p.imgfmt) + if (!p->imgfmt && !p->imgfmt_name) return M_PROPERTY_UNAVAILABLE; int d_w, d_h; - mp_image_params_get_dsize(&p, &d_w, &d_h); + mp_image_params_get_dsize(p, &d_w, &d_h); - struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(p.imgfmt); int bpp = 0; - for (int i = 0; i < desc.num_planes; i++) - bpp += desc.bpp[i] >> (desc.xs[i] + desc.ys[i]); + enum pl_alpha_mode alpha = p->repr.alpha; + int fmt = p->hw_subfmt ? p->hw_subfmt : p->imgfmt; + if (fmt) { + struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt); + for (int i = 0; i < desc.num_planes; i++) + bpp += desc.bpp[i] >> (desc.xs[i] + desc.ys[i]); - // Alpha type is not supported by FFmpeg, so MP_ALPHA_AUTO may mean alpha - // is of an unknown type, or simply not present. Normalize to AUTO=no alpha. - if (!!(desc.flags & MP_IMGFLAG_ALPHA) != (p.alpha != MP_ALPHA_AUTO)) { - p.alpha = - (desc.flags & MP_IMGFLAG_ALPHA) ? MP_ALPHA_STRAIGHT : MP_ALPHA_AUTO; + // Alpha type is not supported by FFmpeg, so PL_ALPHA_UNKNOWN may mean alpha + // is of an unknown type, or simply not present. Normalize to AUTO=no alpha. + if (!!(desc.flags & MP_IMGFLAG_ALPHA) != (alpha != PL_ALPHA_UNKNOWN)) + alpha = (desc.flags & MP_IMGFLAG_ALPHA) ? PL_ALPHA_INDEPENDENT : PL_ALPHA_UNKNOWN; } - const struct pl_hdr_metadata *hdr = &p.color.hdr; + const struct pl_hdr_metadata *hdr = &p->color.hdr; bool has_cie_y = pl_hdr_metadata_contains(hdr, PL_HDR_METADATA_CIE_Y); bool has_hdr10 = pl_hdr_metadata_contains(hdr, PL_HDR_METADATA_HDR10); bool has_hdr10plus = pl_hdr_metadata_contains(hdr, PL_HDR_METADATA_HDR10PLUS); - bool has_crop = mp_rect_w(p.crop) > 0 && mp_rect_h(p.crop) > 0; + bool has_crop = mp_rect_w(p->crop) > 0 && mp_rect_h(p->crop) > 0; const char *aspect_name = get_aspect_ratio_name(d_w / (double)d_h); - const char *sar_name = get_aspect_ratio_name(p.w / (double)p.h); + const char *sar_name = get_aspect_ratio_name(p->w / (double)p->h); + const char *pixelformat_name = p->imgfmt_name ? p->imgfmt_name : + mp_imgfmt_to_name(p->imgfmt); struct m_sub_property props[] = { - {"pixelformat", SUB_PROP_STR(mp_imgfmt_to_name(p.imgfmt))}, - {"hw-pixelformat", SUB_PROP_STR(mp_imgfmt_to_name(p.hw_subfmt)), - .unavailable = !p.hw_subfmt}, + {"pixelformat", SUB_PROP_STR(pixelformat_name)}, + {"hw-pixelformat", SUB_PROP_STR(mp_imgfmt_to_name(p->hw_subfmt)), + .unavailable = !p->hw_subfmt}, {"average-bpp", SUB_PROP_INT(bpp), .unavailable = !bpp}, - {"w", SUB_PROP_INT(p.w)}, - {"h", SUB_PROP_INT(p.h)}, + {"w", SUB_PROP_INT(p->w)}, + {"h", SUB_PROP_INT(p->h)}, {"dw", SUB_PROP_INT(d_w)}, {"dh", SUB_PROP_INT(d_h)}, - {"crop-x", SUB_PROP_INT(p.crop.x0), .unavailable = !has_crop}, - {"crop-y", SUB_PROP_INT(p.crop.y0), .unavailable = !has_crop}, - {"crop-w", SUB_PROP_INT(mp_rect_w(p.crop)), .unavailable = !has_crop}, - {"crop-h", SUB_PROP_INT(mp_rect_h(p.crop)), .unavailable = !has_crop}, + {"crop-x", SUB_PROP_INT(p->crop.x0), .unavailable = !has_crop}, + {"crop-y", SUB_PROP_INT(p->crop.y0), .unavailable = !has_crop}, + {"crop-w", SUB_PROP_INT(mp_rect_w(p->crop)), .unavailable = !has_crop}, + {"crop-h", SUB_PROP_INT(mp_rect_h(p->crop)), .unavailable = !has_crop}, {"aspect", SUB_PROP_FLOAT(d_w / (double)d_h)}, {"aspect-name", SUB_PROP_STR(aspect_name), .unavailable = !aspect_name}, - {"par", SUB_PROP_FLOAT(p.p_w / (double)p.p_h)}, - {"sar", SUB_PROP_FLOAT(p.w / (double)p.h)}, + {"par", SUB_PROP_FLOAT(p->p_w / (double)p->p_h)}, + {"sar", SUB_PROP_FLOAT(p->w / (double)p->h)}, {"sar-name", SUB_PROP_STR(sar_name), .unavailable = !sar_name}, {"colormatrix", - SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.color.space))}, + SUB_PROP_STR(m_opt_choice_str(pl_csp_names, p->repr.sys))}, {"colorlevels", - SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.color.levels))}, + SUB_PROP_STR(m_opt_choice_str(pl_csp_levels_names, p->repr.levels))}, {"primaries", - SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.color.primaries))}, + SUB_PROP_STR(m_opt_choice_str(pl_csp_prim_names, p->color.primaries))}, {"gamma", - SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.color.gamma))}, - {"sig-peak", SUB_PROP_FLOAT(p.color.hdr.max_luma / MP_REF_WHITE)}, + SUB_PROP_STR(m_opt_choice_str(pl_csp_trc_names, p->color.transfer))}, + {"sig-peak", SUB_PROP_FLOAT(p->color.hdr.max_luma / MP_REF_WHITE)}, {"light", - SUB_PROP_STR(m_opt_choice_str(mp_csp_light_names, p.color.light))}, + SUB_PROP_STR(m_opt_choice_str(mp_csp_light_names, p->light))}, {"chroma-location", - SUB_PROP_STR(m_opt_choice_str(mp_chroma_names, p.chroma_location))}, + SUB_PROP_STR(m_opt_choice_str(pl_chroma_names, p->chroma_location))}, {"stereo-in", - SUB_PROP_STR(m_opt_choice_str(mp_stereo3d_names, p.stereo3d))}, - {"rotate", SUB_PROP_INT(p.rotate)}, + SUB_PROP_STR(m_opt_choice_str(mp_stereo3d_names, p->stereo3d))}, + {"rotate", SUB_PROP_INT(p->rotate)}, {"alpha", - SUB_PROP_STR(m_opt_choice_str(mp_alpha_names, p.alpha)), + SUB_PROP_STR(m_opt_choice_str(pl_alpha_names, alpha)), // avoid using "auto" for "no", so just make it unavailable - .unavailable = p.alpha == MP_ALPHA_AUTO}, + .unavailable = alpha == PL_ALPHA_UNKNOWN}, {"min-luma", SUB_PROP_FLOAT(hdr->min_luma), .unavailable = !has_hdr10}, {"max-luma", SUB_PROP_FLOAT(hdr->max_luma), .unavailable = !has_hdr10}, {"max-cll", SUB_PROP_FLOAT(hdr->max_cll), .unavailable = !has_hdr10}, @@ -2384,7 +2404,24 @@ static int mp_property_vo_imgparams(void *ctx, struct m_property *prop, if (valid != M_PROPERTY_VALID) return valid; - return property_imgparams(vo_get_current_params(vo), action, arg); + struct mp_image_params p = vo_get_current_params(vo); + return property_imgparams(&p, action, arg); +} + +static int mp_property_tgt_imgparams(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + struct vo *vo = mpctx->video_out; + if (!mpctx->video_out) + return M_PROPERTY_UNAVAILABLE; + + int valid = m_property_read_sub_validate(ctx, prop, action, arg); + if (valid != M_PROPERTY_VALID) + return valid; + + struct mp_image_params p = vo_get_target_params(vo); + return property_imgparams(&p, action, arg); } static int mp_property_dec_imgparams(void *ctx, struct m_property *prop, @@ -2403,7 +2440,7 @@ static int mp_property_dec_imgparams(void *ctx, struct m_property *prop, mp_decoder_wrapper_get_video_dec_params(vo_c->track->dec, &p); if (!p.imgfmt) return M_PROPERTY_UNAVAILABLE; - return property_imgparams(p, action, arg); + return property_imgparams(&p, action, arg); } static int mp_property_vd_imgparams(void *ctx, struct m_property *prop, @@ -2417,7 +2454,7 @@ static int mp_property_vd_imgparams(void *ctx, struct m_property *prop, struct mp_codec_params *c = track && track->stream ? track->stream->codec : NULL; if (vo_c->filter->input_params.imgfmt) { - return property_imgparams(vo_c->filter->input_params, action, arg); + return property_imgparams(&vo_c->filter->input_params, action, arg); } else if (c && c->disp_w && c->disp_h) { // Simplistic fallback for stupid scripts querying "width"/"height" // before the first frame is decoded. @@ -2591,6 +2628,26 @@ static int mp_property_hidpi_scale(void *ctx, struct m_property *prop, return m_property_double_ro(action, arg, cmd->cached_window_scale); } +static void update_hidpi_window_scale(struct MPContext *mpctx, bool hidpi_scale) +{ + struct command_ctx *cmd = mpctx->command_ctx; + struct vo *vo = mpctx->video_out; + if (!vo || cmd->cached_window_scale <= 0) + return; + + double scale = hidpi_scale ? cmd->cached_window_scale : 1 / cmd->cached_window_scale; + + int s[2]; + if (vo_control(vo, VOCTRL_GET_UNFS_WINDOW_SIZE, s) <= 0 || s[0] < 1 || s[1] < 1) + return; + + s[0] *= scale; + s[1] *= scale; + if (s[0] <= 0 || s[1] <= 0) + return; + vo_control(vo, VOCTRL_SET_UNFS_WINDOW_SIZE, s); +} + static int mp_property_focused(void *ctx, struct m_property *prop, int action, void *arg) { @@ -2739,8 +2796,15 @@ static int mp_property_perf_info(void *ctx, struct m_property *p, int action, static int mp_property_vo(void *ctx, struct m_property *p, int action, void *arg) { MPContext *mpctx = ctx; - return m_property_strdup_ro(action, arg, - mpctx->video_out ? mpctx->video_out->driver->name : NULL); + return m_property_strdup_ro(action, arg, mpctx->video_out ? + mpctx->video_out->driver->name : NULL); +} + +static int mp_property_gpu_context(void *ctx, struct m_property *p, int action, void *arg) +{ + MPContext *mpctx = ctx; + return m_property_strdup_ro(action, arg, mpctx->video_out ? + mpctx->video_out->context_name : NULL); } static int mp_property_osd_dim(void *ctx, struct m_property *prop, @@ -2790,6 +2854,23 @@ static int mp_property_osd_ass(void *ctx, struct m_property *prop, return m_property_read_sub(props, action, arg); } +static int mp_property_term_size(void *ctx, struct m_property *prop, + int action, void *arg) +{ + int w = -1, h = -1; + terminal_get_size(&w, &h); + if (w == -1 || h == -1) + return M_PROPERTY_UNAVAILABLE; + + struct m_sub_property props[] = { + {"w", SUB_PROP_INT(w)}, + {"h", SUB_PROP_INT(h)}, + {0} + }; + + return m_property_read_sub(props, action, arg); +} + static int mp_property_mouse_pos(void *ctx, struct m_property *prop, int action, void *arg) { @@ -2875,9 +2956,10 @@ static int mp_property_sub_delay(void *ctx, struct m_property *prop, { MPContext *mpctx = ctx; struct MPOpts *opts = mpctx->opts; + int track_ind = *(int *)prop->priv; switch (action) { case M_PROPERTY_PRINT: - *(char **)arg = format_delay(opts->subs_rend->sub_delay); + *(char **)arg = format_delay(opts->subs_shared->sub_delay[track_ind]); return M_PROPERTY_OK; } return mp_property_generic_option(mpctx, prop, action, arg); @@ -2902,8 +2984,9 @@ static int mp_property_sub_pos(void *ctx, struct m_property *prop, { MPContext *mpctx = ctx; struct MPOpts *opts = mpctx->opts; + int track_ind = *(int *)prop->priv; if (action == M_PROPERTY_PRINT) { - *(char **)arg = talloc_asprintf(NULL, "%4.2f%%/100", opts->subs_rend->sub_pos); + *(char **)arg = talloc_asprintf(NULL, "%4.2f%%/100", opts->subs_shared->sub_pos[track_ind]); return M_PROPERTY_OK; } return mp_property_generic_option(mpctx, prop, action, arg); @@ -2932,11 +3015,14 @@ static int mp_property_sub_ass_extradata(void *ctx, struct m_property *prop, return M_PROPERTY_NOT_IMPLEMENTED; } -static int get_sub_text(void *ctx, struct m_property *prop, - int action, void *arg, int sub_index) +static int mp_property_sub_text(void *ctx, struct m_property *prop, + int action, void *arg) { - int type = *(int *)prop->priv; MPContext *mpctx = ctx; + const int *def = prop->priv; + int sub_index = def[0]; + int type = def[1]; + struct track *track = mpctx->current_track[sub_index][STREAM_SUB]; struct dec_sub *sub = track ? track->d_sub : NULL; double pts = mpctx->playback_pts; @@ -2958,18 +3044,6 @@ static int get_sub_text(void *ctx, struct m_property *prop, return M_PROPERTY_NOT_IMPLEMENTED; } -static int mp_property_sub_text(void *ctx, struct m_property *prop, - int action, void *arg) -{ - return get_sub_text(ctx, prop, action, arg, 0); -} - -static int mp_property_secondary_sub_text(void *ctx, struct m_property *prop, - int action, void *arg) -{ - return get_sub_text(ctx, prop, action, arg, 1); -} - static struct sd_times get_times(void *ctx, struct m_property *prop, int action, void *arg) { @@ -3239,7 +3313,7 @@ static int mp_property_packet_bitrate(void *ctx, struct m_property *prop, if (rate < 1000) { *(char **)arg = talloc_asprintf(NULL, "%d kbps", (int)rate); } else { - *(char **)arg = talloc_asprintf(NULL, "%.3f mbps", rate / 1000.0); + *(char **)arg = talloc_asprintf(NULL, "%.3f Mbps", rate / 1000.0); } return M_PROPERTY_OK; } @@ -3624,29 +3698,32 @@ static int mp_property_bindings(void *ctx, struct m_property *prop, return M_PROPERTY_NOT_IMPLEMENTED; } - -static int mp_property_script_props(void *ctx, struct m_property *prop, - int action, void *arg) +static int mp_property_mdata(void *ctx, struct m_property *prop, + int action, void *arg) { MPContext *mpctx = ctx; - struct command_ctx *cmd = mpctx->command_ctx; - if (!cmd->shared_script_warning) { - MP_WARN(mpctx, "The shared-script-properties property is deprecated and will " - "be removed in the future. Use the user-data property instead.\n"); - cmd->shared_script_warning = true; - } + mpv_node *node = &mpctx->command_ctx->mdata; + switch (action) { case M_PROPERTY_GET_TYPE: - *(struct m_option *)arg = script_props_type; + *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_NODE}; return M_PROPERTY_OK; case M_PROPERTY_GET: - m_option_copy(&script_props_type, arg, &cmd->script_props); + case M_PROPERTY_GET_NODE: + m_option_copy(&mdata_type, arg, node); return M_PROPERTY_OK; case M_PROPERTY_SET: - m_option_copy(&script_props_type, &cmd->script_props, arg); + case M_PROPERTY_SET_NODE: { + m_option_copy(&mdata_type, node, arg); + talloc_steal(mpctx->command_ctx, node_get_alloc(node)); mp_notify_property(mpctx, prop->name); + + struct vo *vo = mpctx->video_out; + if (vo) + vo_control(vo, VOCTRL_UPDATE_MENU, arg); return M_PROPERTY_OK; } + } return M_PROPERTY_NOT_IMPLEMENTED; } @@ -3673,8 +3750,9 @@ static int do_op_udata(struct udata_ctx* ctx, int action, void *arg) assert(node); m_option_copy(&udata_type, arg, node); return M_PROPERTY_OK; + case M_PROPERTY_FIXED_LEN_PRINT: case M_PROPERTY_PRINT: { - char *str = m_option_pretty_print(&udata_type, node); + char *str = m_option_pretty_print(&udata_type, node, action == M_PROPERTY_FIXED_LEN_PRINT); *(char **)arg = str; return str != NULL; } @@ -3781,7 +3859,7 @@ static int do_list_udata(int item, int action, void *arg, void *ctx) { struct udata_ctx nctx = *(struct udata_ctx*)ctx; nctx.node = &nctx.node->u.list->values[item]; - nctx.ta_parent = &nctx.node->u.list; + nctx.ta_parent = nctx.node->u.list; return do_op_udata(&nctx, action, arg); } @@ -3807,7 +3885,7 @@ static int mp_property_udata(void *ctx, struct m_property *prop, .mpctx = mpctx, .path = path, .node = &mpctx->command_ctx->udata, - .ta_parent = &mpctx->command_ctx, + .ta_parent = mpctx->command_ctx, }; int ret = do_op_udata(&nctx, action, arg); @@ -3885,6 +3963,7 @@ static const struct m_property mp_properties_base[] = { {"clock", mp_property_clock}, {"seekable", mp_property_seekable}, {"partially-seekable", mp_property_partially_seekable}, + {"deinterlace-active", mp_property_deinterlace}, {"idle-active", mp_property_idle}, {"window-id", mp_property_window_id}, @@ -3904,11 +3983,12 @@ static const struct m_property mp_properties_base[] = { // Audio {"mixer-active", mp_property_mixer_active}, {"volume", mp_property_volume}, + {"volume-gain", mp_property_volume_gain}, {"ao-volume", mp_property_ao_volume}, {"ao-mute", mp_property_ao_mute}, {"audio-delay", mp_property_audio_delay}, - {"audio-codec-name", mp_property_audio_codec_name}, - {"audio-codec", mp_property_audio_codec}, + M_PROPERTY_ALIAS("audio-codec-name", "current-tracks/audio/codec"), + M_PROPERTY_ALIAS("audio-codec", "current-tracks/audio/codec-desc"), {"audio-params", mp_property_audio_params}, {"audio-out-params", mp_property_audio_out_params}, {"aid", property_switch_track, .priv = (void *)(const int[]){0, STREAM_AUDIO}}, @@ -3917,12 +3997,13 @@ static const struct m_property mp_properties_base[] = { {"current-ao", mp_property_ao}, // Video + {"video-target-params", mp_property_tgt_imgparams}, {"video-out-params", mp_property_vo_imgparams}, {"video-dec-params", mp_property_dec_imgparams}, {"video-params", mp_property_vd_imgparams}, - {"video-format", mp_property_video_format}, {"video-frame-info", mp_property_video_frame_info}, - {"video-codec", mp_property_video_codec}, + M_PROPERTY_ALIAS("video-format", "current-tracks/video/codec"), + M_PROPERTY_ALIAS("video-codec", "current-tracks/video/codec-desc"), M_PROPERTY_ALIAS("dwidth", "video-out-params/dw"), M_PROPERTY_ALIAS("dheight", "video-out-params/dh"), M_PROPERTY_ALIAS("width", "video-params/w"), @@ -3932,6 +4013,7 @@ static const struct m_property mp_properties_base[] = { {"vo-passes", mp_property_vo_passes}, {"perf-info", mp_property_perf_info}, {"current-vo", mp_property_vo}, + {"current-gpu-context", mp_property_gpu_context}, {"container-fps", mp_property_fps}, {"estimated-vf-fps", mp_property_vf_fps}, {"video-aspect-override", mp_property_video_aspect_override}, @@ -3956,16 +4038,20 @@ static const struct m_property mp_properties_base[] = { {"sid", property_switch_track, .priv = (void *)(const int[]){0, STREAM_SUB}}, {"secondary-sid", property_switch_track, .priv = (void *)(const int[]){1, STREAM_SUB}}, - {"sub-delay", mp_property_sub_delay}, + {"sub-delay", mp_property_sub_delay, .priv = (void *)&(const int){0}}, + {"secondary-sub-delay", mp_property_sub_delay, + .priv = (void *)&(const int){1}}, {"sub-speed", mp_property_sub_speed}, - {"sub-pos", mp_property_sub_pos}, + {"sub-pos", mp_property_sub_pos, .priv = (void *)&(const int){0}}, + {"secondary-sub-pos", mp_property_sub_pos, + .priv = (void *)&(const int){1}}, {"sub-ass-extradata", mp_property_sub_ass_extradata}, {"sub-text", mp_property_sub_text, - .priv = (void *)&(const int){SD_TEXT_TYPE_PLAIN}}, - {"secondary-sub-text", mp_property_secondary_sub_text, - .priv = (void *)&(const int){SD_TEXT_TYPE_PLAIN}}, + .priv = (void *)&(const int[]){0, SD_TEXT_TYPE_PLAIN}}, + {"secondary-sub-text", mp_property_sub_text, + .priv = (void *)&(const int[]){1, SD_TEXT_TYPE_PLAIN}}, {"sub-text-ass", mp_property_sub_text, - .priv = (void *)&(const int){SD_TEXT_TYPE_ASS}}, + .priv = (void *)&(const int[]){0, SD_TEXT_TYPE_ASS}}, {"sub-start", mp_property_sub_start, .priv = (void *)&(const int){0}}, {"secondary-sub-start", mp_property_sub_start, @@ -4020,8 +4106,10 @@ static const struct m_property mp_properties_base[] = { {"command-list", mp_property_commands}, {"input-bindings", mp_property_bindings}, - {"shared-script-properties", mp_property_script_props}, + {"menu-data", mp_property_mdata}, + {"user-data", mp_property_udata}, + {"term-size", mp_property_term_size}, M_PROPERTY_ALIAS("video", "vid"), M_PROPERTY_ALIAS("audio", "aid"), @@ -4054,17 +4142,18 @@ static const char *const *const mp_event_property_change[] = { "secondary-sub-text", "audio-bitrate", "video-bitrate", "sub-bitrate", "decoder-frame-drop-count", "frame-drop-count", "video-frame-info", "vf-metadata", "af-metadata", "sub-start", "sub-end", "secondary-sub-start", - "secondary-sub-end", "video-out-params", "video-dec-params", "video-params"), + "secondary-sub-end", "video-out-params", "video-dec-params", "video-params", + "deinterlace-active", "video-target-params"), E(MP_EVENT_DURATION_UPDATE, "duration"), E(MPV_EVENT_VIDEO_RECONFIG, "video-out-params", "video-params", "video-format", "video-codec", "video-bitrate", "dwidth", "dheight", "width", "height", "container-fps", "aspect", "aspect-name", "vo-configured", "current-vo", - "video-dec-params", "osd-dimensions", - "hwdec", "hwdec-current", "hwdec-interop"), + "video-dec-params", "osd-dimensions", "hwdec", "hwdec-current", "hwdec-interop", + "window-id", "track-list", "current-tracks"), E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate", - "samplerate", "channels", "audio", "volume", "mute", - "current-ao", "audio-codec-name", "audio-params", - "audio-out-params", "volume-max", "mixer-active"), + "samplerate", "channels", "audio", "volume", "volume-gain", "mute", + "current-ao", "audio-codec-name", "audio-params", "track-list", "current-tracks", + "audio-out-params", "volume-max", "volume-gain-min", "volume-gain-max", "mixer-active"), E(MPV_EVENT_SEEK, "seeking", "core-idle", "eof-reached"), E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle", "eof-reached"), E(MP_EVENT_METADATA_UPDATE, "metadata", "filtered-metadata", "media-title"), @@ -4247,6 +4336,9 @@ static const struct property_osd_display { {"volume", "Volume", .msg = "Volume: ${?volume:${volume}% ${?mute==yes:(Muted)}}${!volume:${volume}}", .osd_progbar = OSD_VOLUME, .marker = 100}, + {"volume-gain", "Volume gain", + .msg = "Volume gain: ${?volume-gain:${volume-gain} dB ${?mute==yes:(Muted)}}${!volume-gain:${volume-gain}}", + .osd_progbar = OSD_VOLUME, .marker = 0}, {"ao-volume", "AO Volume", .msg = "AO Volume: ${?ao-volume:${ao-volume}% ${?ao-mute==yes:(Muted)}}${!ao-volume:${ao-volume}}", .osd_progbar = OSD_VOLUME, .marker = 100}, @@ -4273,7 +4365,9 @@ static const struct property_osd_display { {"sub", "Subtitles"}, {"secondary-sid", "Secondary subtitles"}, {"sub-pos", "Sub position"}, + {"secondary-sub-pos", "Secondary sub position"}, {"sub-delay", "Sub delay"}, + {"secondary-sub-delay", "Secondary sub delay"}, {"sub-speed", "Sub speed"}, {"sub-visibility", .msg = "Subtitles ${!sub-visibility==yes:hidden}" @@ -4285,6 +4379,7 @@ static const struct property_osd_display { {"sub-scale", "Sub Scale"}, {"sub-ass-vsfilter-aspect-compat", "Subtitle VSFilter aspect compat"}, {"sub-ass-override", "ASS subtitle style override"}, + {"secondary-sub-ass-override", "Secondary sub ASS subtitle style override"}, {"vf", "Video filters", .msg = "Video filters:\n${vf}"}, {"af", "Audio filters", .msg = "Audio filters:\n${af}"}, {"ab-loop-a", "A-B loop start"}, @@ -4474,8 +4569,8 @@ static void recreate_overlays(struct MPContext *mpctx) struct sub_bitmap b = { .bitmap = s->planes[0], .stride = s->stride[0], - .w = s->w, .dw = s->w, - .h = s->h, .dh = s->h, + .w = s->w, .dw = o->dw, + .h = s->h, .dh = o->dh, .x = o->x, .y = o->y, }; @@ -4572,7 +4667,12 @@ static void cmd_overlay_add(void *pcmd) int offset = cmd->args[4].v.i; char *fmt = cmd->args[5].v.s; int w = cmd->args[6].v.i, h = cmd->args[7].v.i, stride = cmd->args[8].v.i; + int dw = cmd->args[9].v.i, dh = cmd->args[10].v.i; + if (dw <= 0) + dw = w; + if (dh <= 0) + dh = h; if (strcmp(fmt, "bgra") != 0) { MP_ERR(mpctx, "overlay-add: unsupported OSD format '%s'\n", fmt); goto error; @@ -4589,6 +4689,8 @@ static void cmd_overlay_add(void *pcmd) .source = mp_image_alloc(IMGFMT_BGRA, w, h), .x = x, .y = y, + .dw = dw, + .dh = dh, }; if (!overlay.source) goto error; @@ -5408,15 +5510,22 @@ static void cmd_sub_step_seek(void *p) a[1] = cmd->args[0].v.i; if (sub_control(sub, SD_CTRL_SUB_STEP, a) > 0) { if (step) { - mpctx->opts->subs_rend->sub_delay -= a[0] - refpts; + mpctx->opts->subs_shared->sub_delay[track_ind] -= a[0] - refpts; m_config_notify_change_opt_ptr_notify(mpctx->mconfig, - &mpctx->opts->subs_rend->sub_delay); - show_property_osd(mpctx, "sub-delay", cmd->on_osd); + &mpctx->opts->subs_shared->sub_delay[track_ind]); + show_property_osd( + mpctx, + track_ind == 0 ? "sub-delay" : "secondary-sub-delay", + cmd->on_osd); } else { // We can easily seek/step to the wrong subtitle line (because - // video frame PTS and sub PTS rarely match exactly). Add an - // arbitrary forward offset as a workaround. - a[0] += SUB_SEEK_OFFSET; + // video frame PTS and sub PTS rarely match exactly). + // sub/sd_ass.c adds SUB_SEEK_OFFSET as a workaround, and we + // need an even bigger offset without a video. + if (!mpctx->current_track[0][STREAM_VIDEO] || + mpctx->current_track[0][STREAM_VIDEO]->image) { + a[0] += SUB_SEEK_WITHOUT_VIDEO_OFFSET - SUB_SEEK_OFFSET; + } mark_seek(mpctx); queue_seek(mpctx, MPSEEK_ABSOLUTE, a[0], MPSEEK_EXACT, MPSEEK_FLAG_DELAY); @@ -5472,29 +5581,84 @@ static void cmd_expand_path(void *p) }; } +static void cmd_escape_ass(void *p) +{ + struct mp_cmd_ctx *cmd = p; + bstr dst = {0}; + + osd_mangle_ass(&dst, cmd->args[0].v.s, true); + + cmd->result = (mpv_node){ + .format = MPV_FORMAT_STRING, + .u.string = dst.len ? (char *)dst.start : talloc_strdup(NULL, ""), + }; +} + +static struct load_action get_load_action(struct MPContext *mpctx, int action_flag) +{ + switch (action_flag) { + case 0: // replace + return (struct load_action){LOAD_TYPE_REPLACE, .play = true}; + case 1: // append + return (struct load_action){LOAD_TYPE_APPEND, .play = false}; + case 2: // append-play + return (struct load_action){LOAD_TYPE_APPEND, .play = true}; + case 3: // insert-next + return (struct load_action){LOAD_TYPE_INSERT_NEXT, .play = false}; + case 4: // insert-next-play + return (struct load_action){LOAD_TYPE_INSERT_NEXT, .play = true}; + case 5: // insert-at + return (struct load_action){LOAD_TYPE_INSERT_AT, .play = false}; + case 6: // insert-at-play + return (struct load_action){LOAD_TYPE_INSERT_AT, .play = true}; + default: // default: replace + return (struct load_action){LOAD_TYPE_REPLACE, .play = true}; + } +} + +static struct playlist_entry *get_insert_entry(struct MPContext *mpctx, struct load_action *action, + int insert_at_idx) +{ + switch (action->type) { + case LOAD_TYPE_INSERT_NEXT: + return playlist_get_next(mpctx->playlist, +1); + case LOAD_TYPE_INSERT_AT: + return playlist_entry_from_index(mpctx->playlist, insert_at_idx); + case LOAD_TYPE_REPLACE: + case LOAD_TYPE_APPEND: + default: + return NULL; + } +} + static void cmd_loadfile(void *p) { struct mp_cmd_ctx *cmd = p; struct MPContext *mpctx = cmd->mpctx; char *filename = cmd->args[0].v.s; - int append = cmd->args[1].v.i; + int action_flag = cmd->args[1].v.i; + int insert_at_idx = cmd->args[2].v.i; + + struct load_action action = get_load_action(mpctx, action_flag); - if (!append) + if (action.type == LOAD_TYPE_REPLACE) playlist_clear(mpctx->playlist); struct playlist_entry *entry = playlist_entry_new(filename); - if (cmd->args[2].v.str_list) { - char **pairs = cmd->args[2].v.str_list; + if (cmd->args[3].v.str_list) { + char **pairs = cmd->args[3].v.str_list; for (int i = 0; pairs[i] && pairs[i + 1]; i += 2) playlist_entry_add_param(entry, bstr0(pairs[i]), bstr0(pairs[i + 1])); } - playlist_add(mpctx->playlist, entry); + + struct playlist_entry *at = get_insert_entry(mpctx, &action, insert_at_idx); + playlist_insert_at(mpctx->playlist, entry, at); struct mpv_node *res = &cmd->result; node_init(res, MPV_FORMAT_NODE_MAP, NULL); node_map_add_int64(res, "playlist_entry_id", entry->id); - if (!append || (append == 2 && !mpctx->playlist->current)) { + if (action.type == LOAD_TYPE_REPLACE || (action.play && !mpctx->playlist->current)) { if (mpctx->opts->position_save_on_quit) // requested in issue #1148 mp_write_watch_later_conf(mpctx); mp_set_playlist_entry(mpctx, entry); @@ -5508,25 +5672,37 @@ static void cmd_loadlist(void *p) struct mp_cmd_ctx *cmd = p; struct MPContext *mpctx = cmd->mpctx; char *filename = cmd->args[0].v.s; - int append = cmd->args[1].v.i; + int action_flag = cmd->args[1].v.i; + int insert_at_idx = cmd->args[2].v.i; + + struct load_action action = get_load_action(mpctx, action_flag); struct playlist *pl = playlist_parse_file(filename, cmd->abort->cancel, mpctx->global); if (pl) { prepare_playlist(mpctx, pl); struct playlist_entry *new = pl->current; - if (!append) + if (action.type == LOAD_TYPE_REPLACE) playlist_clear(mpctx->playlist); struct playlist_entry *first = playlist_entry_from_index(pl, 0); int num_entries = pl->num_entries; - playlist_append_entries(mpctx->playlist, pl); + + struct playlist_entry *at = get_insert_entry(mpctx, &action, insert_at_idx); + if (at == NULL) { + playlist_append_entries(mpctx->playlist, pl); + } else { + int at_index = playlist_entry_to_index(mpctx->playlist, at); + playlist_transfer_entries_to(mpctx->playlist, at_index, pl); + } talloc_free(pl); if (!new) new = playlist_get_first(mpctx->playlist); - if ((!append || (append == 2 && !mpctx->playlist->current)) && new) + if ((action.type == LOAD_TYPE_REPLACE || + (action.play && !mpctx->playlist->current)) && new) { mp_set_playlist_entry(mpctx, new); + } struct mpv_node *res = &cmd->result; node_init(res, MPV_FORMAT_NODE_MAP, NULL); @@ -5752,6 +5928,10 @@ static void cmd_track_reload(void *p) } struct track *nt = mpctx->tracks[nt_num]; + + if (!nt->lang) + nt->lang = mp_guess_lang_from_filename(nt, nt->external_filename); + mp_switch_track(mpctx, nt->type, nt, 0); print_track_list(mpctx, "Reloaded:"); } @@ -5787,7 +5967,7 @@ static void cmd_run(void *p) char **args = talloc_zero_array(NULL, char *, cmd->num_args + 1); for (int n = 0; n < cmd->num_args; n++) args[n] = cmd->args[n].v.s; - mp_msg_flush_status_line(mpctx->log); + mp_msg_flush_status_line(mpctx->log, true); struct mp_subprocess_opts opts = { .exe = args[0], .args = args, @@ -6176,7 +6356,7 @@ static void cmd_mouse(void *p) if (button == -1) {// no button if (pre_key) - mp_input_put_key_artificial(mpctx->input, pre_key); + mp_input_put_key_artificial(mpctx->input, pre_key, 1); mp_input_set_mouse_pos_artificial(mpctx->input, x, y); return; } @@ -6194,9 +6374,9 @@ static void cmd_mouse(void *p) } button += dbc ? MP_MBTN_DBL_BASE : MP_MBTN_BASE; if (pre_key) - mp_input_put_key_artificial(mpctx->input, pre_key); + mp_input_put_key_artificial(mpctx->input, pre_key, 1); mp_input_set_mouse_pos_artificial(mpctx->input, x, y); - mp_input_put_key_artificial(mpctx->input, button); + mp_input_put_key_artificial(mpctx->input, button, 1); } static void cmd_key(void *p) @@ -6207,7 +6387,7 @@ static void cmd_key(void *p) const char *key_name = cmd->args[0].v.s; if (key_name[0] == '\0' && action == MP_KEY_STATE_UP) { - mp_input_put_key_artificial(mpctx->input, MP_INPUT_RELEASE_ALL); + mp_input_put_key_artificial(mpctx->input, MP_INPUT_RELEASE_ALL, 1); } else { int code = mp_input_get_key_from_name(key_name); if (code < 0) { @@ -6215,7 +6395,8 @@ static void cmd_key(void *p) cmd->success = false; return; } - mp_input_put_key_artificial(mpctx->input, code | action); + double scale = action == 0 ? cmd->args[1].v.d : 1; + mp_input_put_key_artificial(mpctx->input, code | action, scale); } } @@ -6248,6 +6429,32 @@ static void cmd_apply_profile(void *p) } } +static void cmd_load_config_file(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + + char *config_file = cmd->args[0].v.s; + int r = m_config_parse_config_file(mpctx->mconfig, mpctx->global, + config_file, NULL, 0); + + if (r < 1) { + cmd->success = false; + return; + } + + mp_notify_property(mpctx, "profile-list"); +} + +static void cmd_load_input_conf(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + + char *config_file = cmd->args[0].v.s; + cmd->success = mp_input_load_config_file(mpctx->input, config_file); +} + static void cmd_load_script(void *p) { struct mp_cmd_ctx *cmd = p; @@ -6349,6 +6556,26 @@ static void cmd_dump_cache_ab(void *p) cmd->args[0].v.s); } +static void cmd_begin_vo_dragging(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + struct vo *vo = mpctx->video_out; + + if (vo) + vo_control(vo, VOCTRL_BEGIN_DRAGGING, NULL); +} + +static void cmd_context_menu(void *p) +{ + struct mp_cmd_ctx *cmd = p; + struct MPContext *mpctx = cmd->mpctx; + struct vo *vo = mpctx->video_out; + + if (vo) + vo_control(vo, VOCTRL_SHOW_MENU, NULL); +} + /* This array defines all known commands. * The first field the command name used in libmpv and input.conf. * The second field is the handler function (see mp_cmd_def.handler and @@ -6474,6 +6701,8 @@ const struct mp_cmd_def mp_cmds[] = { .is_noisy = true }, { "expand-path", cmd_expand_path, { {"text", OPT_STRING(v.s)} }, .is_noisy = true }, + { "escape-ass", cmd_escape_ass, { {"text", OPT_STRING(v.s)} }, + .is_noisy = true }, { "show-progress", cmd_show_progress, .allow_auto_repeat = true, .is_noisy = true }, @@ -6600,8 +6829,13 @@ const struct mp_cmd_def mp_cmds[] = { {"flags", OPT_CHOICE(v.i, {"replace", 0}, {"append", 1}, - {"append-play", 2}), + {"append-play", 2}, + {"insert-next", 3}, + {"insert-next-play", 4}, + {"insert-at", 5}, + {"insert-at-play", 6}), .flags = MP_CMD_OPT_ARG}, + {"index", OPT_INT(v.i), OPTDEF_INT(-1)}, {"options", OPT_KEYVALUELIST(v.str_list), .flags = MP_CMD_OPT_ARG}, }, }, @@ -6611,8 +6845,13 @@ const struct mp_cmd_def mp_cmds[] = { {"flags", OPT_CHOICE(v.i, {"replace", 0}, {"append", 1}, - {"append-play", 2}), + {"append-play", 2}, + {"insert-next", 3}, + {"insert-next-play", 4}, + {"insert-at", 5}, + {"insert-at-play", 6}), .flags = MP_CMD_OPT_ARG}, + {"index", OPT_INT(v.i), OPTDEF_INT(-1)}, }, .spawn_thread = true, .can_abort = true, @@ -6740,7 +6979,9 @@ const struct mp_cmd_def mp_cmds[] = { {"fmt", OPT_STRING(v.s)}, {"w", OPT_INT(v.i)}, {"h", OPT_INT(v.i)}, - {"stride", OPT_INT(v.i)}, }}, + {"stride", OPT_INT(v.i)}, + {"dw", OPT_INT(v.i), OPTDEF_INT(0)}, + {"dh", OPT_INT(v.i), OPTDEF_INT(0)}, }}, { "overlay-remove", cmd_overlay_remove, { {"id", OPT_INT(v.i)} } }, { "osd-overlay", cmd_osd_overlay, @@ -6769,7 +7010,8 @@ const struct mp_cmd_def mp_cmds[] = { .flags = MP_CMD_OPT_ARG}}}, { "keybind", cmd_key_bind, { {"name", OPT_STRING(v.s)}, {"cmd", OPT_STRING(v.s)} }}, - { "keypress", cmd_key, { {"name", OPT_STRING(v.s)} }, + { "keypress", cmd_key, { {"name", OPT_STRING(v.s)}, + {"scale", OPT_DOUBLE(v.d), OPTDEF_DOUBLE(1)} }, .priv = &(const int){0}}, { "keydown", cmd_key, { {"name", OPT_STRING(v.s)} }, .priv = &(const int){MP_KEY_STATE_DOWN}}, @@ -6782,6 +7024,10 @@ const struct mp_cmd_def mp_cmds[] = { .flags = MP_CMD_OPT_ARG}, } }, + { "load-config-file", cmd_load_config_file, {{"filename", OPT_STRING(v.s)}} }, + + { "load-input-conf", cmd_load_input_conf, {{"filename", OPT_STRING(v.s)}} }, + { "load-script", cmd_load_script, {{"filename", OPT_STRING(v.s)}} }, { "dump-cache", cmd_dump_cache, { {"start", OPT_TIME(v.d), @@ -6800,6 +7046,10 @@ const struct mp_cmd_def mp_cmds[] = { { "ab-loop-align-cache", cmd_align_cache_ab }, + { "begin-vo-dragging", cmd_begin_vo_dragging }, + + { "context-menu", cmd_context_menu }, + {0} }; @@ -6821,6 +7071,11 @@ void command_uninit(struct MPContext *mpctx) mpctx->command_ctx = NULL; } +static int str_compare(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + void command_init(struct MPContext *mpctx) { struct command_ctx *ctx = talloc(NULL, struct command_ctx); @@ -6835,6 +7090,11 @@ void command_init(struct MPContext *mpctx) talloc_zero_array(ctx, struct m_property, num_base + num_opts + 1); memcpy(ctx->properties, mp_properties_base, sizeof(mp_properties_base)); + const char **prop_names = talloc_array(NULL, const char *, num_base); + for (int i = 0; i < num_base; ++i) + prop_names[i] = mp_properties_base[i].name; + qsort(prop_names, num_base, sizeof(const char *), str_compare); + int count = num_base; for (int n = 0; n < num_opts; n++) { struct m_config_option *co = m_config_get_co_index(mpctx->mconfig, n); @@ -6868,14 +7128,18 @@ void command_init(struct MPContext *mpctx) } // The option might be covered by a manual property already. - if (m_property_list_find(ctx->properties, prop.name)) + if (bsearch(&prop.name, prop_names, num_base, sizeof(const char *), str_compare)) continue; ctx->properties[count++] = prop; } + node_init(&ctx->mdata, MPV_FORMAT_NODE_ARRAY, NULL); + talloc_steal(ctx, ctx->mdata.u.list); + node_init(&ctx->udata, MPV_FORMAT_NODE_MAP, NULL); talloc_steal(ctx, ctx->udata.u.list); + talloc_free(prop_names); } static void command_event(struct MPContext *mpctx, int event, void *arg) @@ -6891,6 +7155,9 @@ static void command_event(struct MPContext *mpctx, int event, void *arg) if (event == MPV_EVENT_PLAYBACK_RESTART) ctx->last_seek_time = mp_time_sec(); + if (event == MPV_EVENT_END_FILE) + mp_msg_flush_status_line(mpctx->log, false); + if (event == MPV_EVENT_END_FILE || event == MPV_EVENT_FILE_LOADED) { // Update chapters - does nothing if something else is visible. set_osd_bar_chapters(mpctx, OSD_BAR_SEEK); @@ -6921,6 +7188,27 @@ void handle_command_updates(struct MPContext *mpctx) // Depends on polling demuxer wakeup callback notifications. cache_dump_poll(mpctx); + + // Potentially run the commands now (idle) instead of waiting for a file to load. + if (mpctx->stop_play == PT_STOP) + run_command_opts(mpctx); +} + +void run_command_opts(struct MPContext *mpctx) +{ + struct MPOpts *opts = mpctx->opts; + struct command_ctx *ctx = mpctx->command_ctx; + + if (!opts->input_commands || ctx->command_opts_processed) + return; + + // Take easy way out and add these to the input queue. + for (int i = 0; opts->input_commands[i]; i++) { + struct mp_cmd *cmd = mp_input_parse_cmd(mpctx->input, bstr0(opts->input_commands[i]), + "the command line"); + mp_input_queue_cmd(mpctx->input, cmd); + } + ctx->command_opts_processed = true; } void mp_notify(struct MPContext *mpctx, int event, void *arg) @@ -6987,8 +7275,11 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags, if (sub) { int ret = sub_control(sub, SD_CTRL_UPDATE_OPTS, (void *)(uintptr_t)flags); - if (ret == CONTROL_OK && flags & (UPDATE_SUB_FILT | UPDATE_SUB_HARD)) + if (ret == CONTROL_OK && flags & (UPDATE_SUB_FILT | UPDATE_SUB_HARD)) { sub_redecode_cached_packets(sub); + if (track->selected) + reselect_demux_stream(mpctx, track, true); + } } } osd_changed(mpctx->osd); @@ -7016,13 +7307,15 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags, mpctx->ipc_ctx = mp_init_ipc(mpctx->clients, mpctx->global); } - if (opt_ptr == &opts->vo->video_driver_list) { + if (opt_ptr == &opts->vo->video_driver_list || + opt_ptr == &opts->ra_ctx_opts->context_name || + opt_ptr == &opts->ra_ctx_opts->context_type) { struct track *track = mpctx->current_track[0][STREAM_VIDEO]; uninit_video_out(mpctx); handle_force_window(mpctx, true); reinit_video_chain(mpctx); if (track) - reselect_demux_stream(mpctx, track, true); + queue_seek(mpctx, MPSEEK_RELATIVE, 0.0, MPSEEK_EXACT, 0); mp_wakeup_core(mpctx); } @@ -7042,11 +7335,23 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags, if (flags & UPDATE_LAVFI_COMPLEX) update_lavfi_complex(mpctx); + if (flags & UPDATE_VIDEO) { + if (mpctx->video_out) { + vo_control(mpctx->video_out, VOCTRL_UPDATE_RENDER_OPTS, NULL); + mp_wakeup_core(mpctx); + } + } + if (opt_ptr == &opts->vo->android_surface_size) { if (mpctx->video_out) vo_control(mpctx->video_out, VOCTRL_EXTERNAL_RESIZE, NULL); } + if (opt_ptr == &opts->input_commands) { + mpctx->command_ctx->command_opts_processed = false; + run_command_opts(mpctx); + } + if (opt_ptr == &opts->playback_speed) { update_playback_speed(mpctx); mp_wakeup_core(mpctx); @@ -7103,6 +7408,9 @@ void mp_option_change_callback(void *ctx, struct m_config_option *co, int flags, if (opt_ptr == &opts->vo->window_scale) update_window_scale(mpctx); + if (opt_ptr == &opts->vo->hidpi_window_scale) + update_hidpi_window_scale(mpctx, opts->vo->hidpi_window_scale); + if (opt_ptr == &opts->cursor_autohide_delay) mpctx->mouse_timer = 0; |