summaryrefslogtreecommitdiffstats
path: root/player/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/command.c')
-rw-r--r--player/command.c680
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;