summaryrefslogtreecommitdiffstats
path: root/player
diff options
context:
space:
mode:
Diffstat (limited to 'player')
-rw-r--r--player/audio.c2
-rw-r--r--player/command.c330
-rw-r--r--player/command.h3
-rw-r--r--player/configfiles.c3
-rw-r--r--player/core.h2
-rw-r--r--player/main.c12
-rw-r--r--player/osd.c6
-rw-r--r--player/playloop.c2
-rw-r--r--player/video.c122
9 files changed, 287 insertions, 195 deletions
diff --git a/player/audio.c b/player/audio.c
index 2ce1669366..b61e464090 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -132,7 +132,7 @@ static int recreate_audio_filters(struct MPContext *mpctx)
if (afs->initialized < 1 && af_init(afs) < 0)
goto fail;
- mixer_reinit_audio(mpctx->mixer, mpctx->ao, afs);
+ mixer_reinit_audio(mpctx->mixer, afs);
mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL);
diff --git a/player/command.c b/player/command.c
index bc48d8cf2d..3bca64bd5e 100644
--- a/player/command.c
+++ b/player/command.c
@@ -60,6 +60,7 @@
#include "audio/filter/af.h"
#include "video/decode/dec_video.h"
#include "audio/decode/dec_audio.h"
+#include "video/out/bitmap_packer.h"
#include "options/path.h"
#include "screenshot.h"
@@ -88,7 +89,8 @@ struct command_ctx {
// One of these is in use by the OSD; the other one exists so that the
// bitmap list can be manipulated without additional synchronization.
struct sub_bitmaps overlay_osd[2];
- struct sub_bitmaps *overlay_osd_current;
+ int overlay_osd_current;
+ struct bitmap_packer *overlay_packer;
struct hook_handler **hooks;
int num_hooks;
@@ -98,9 +100,8 @@ struct command_ctx {
};
struct overlay {
- void *map_start;
- size_t map_size;
- struct sub_bitmap osd;
+ struct mp_image *source;
+ int x, y;
};
struct hook_handler {
@@ -1568,44 +1569,29 @@ static int mp_property_volume(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
+ struct MPOpts *opts = mpctx->opts;
+
switch (action) {
- case M_PROPERTY_GET:
- mixer_getbothvolume(mpctx->mixer, arg);
- return M_PROPERTY_OK;
case M_PROPERTY_GET_TYPE:
*(struct m_option *)arg = (struct m_option){
.type = CONF_TYPE_FLOAT,
.flags = M_OPT_RANGE,
.min = 0,
- .max = mixer_getmaxvolume(mpctx->mixer),
+ .max = opts->softvol_max,
};
return M_PROPERTY_OK;
case M_PROPERTY_GET_NEUTRAL:
*(float *)arg = 100;
return M_PROPERTY_OK;
- case M_PROPERTY_PRINT: {
- float val;
- mixer_getbothvolume(mpctx->mixer, &val);
- *(char **)arg = talloc_asprintf(NULL, "%i", (int)val);
- return M_PROPERTY_OK;
- }
- case M_PROPERTY_SET:
- mixer_setvolume(mpctx->mixer, *(float *) arg, *(float *) arg);
- return M_PROPERTY_OK;
- case M_PROPERTY_SWITCH: {
- struct m_property_switch_arg *sarg = arg;
- mixer_addvolume(mpctx->mixer, sarg->inc);
+ case M_PROPERTY_PRINT:
+ *(char **)arg = talloc_asprintf(NULL, "%i", (int)opts->softvol_volume);
return M_PROPERTY_OK;
}
- }
- return M_PROPERTY_NOT_IMPLEMENTED;
-}
-static int mp_property_volume_max(void *ctx, struct m_property *prop,
- int action, void *arg)
-{
- MPContext *mpctx = ctx;
- return m_property_float_ro(action, arg, mixer_getmaxvolume(mpctx->mixer));
+ int r = mp_property_generic_option(mpctx, prop, action, arg);
+ if (action == M_PROPERTY_SET)
+ mixer_update_volume(mpctx->mixer);
+ return r;
}
/// Mute (RW)
@@ -1613,34 +1599,76 @@ static int mp_property_mute(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
+
+ if (action == M_PROPERTY_GET_TYPE) {
+ *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
+ return M_PROPERTY_OK;
+ }
+
+ int r = mp_property_generic_option(mpctx, prop, action, arg);
+ if (action == M_PROPERTY_SET)
+ mixer_update_volume(mpctx->mixer);
+ return r;
+}
+
+static int mp_property_ao_volume(void *ctx, struct m_property *prop,
+ int action, void *arg)
+{
+ MPContext *mpctx = ctx;
+ struct ao *ao = mpctx->ao;
+ if (!ao)
+ return M_PROPERTY_NOT_IMPLEMENTED;
+
switch (action) {
- case M_PROPERTY_SET:
- mixer_setmute(mpctx->mixer, *(int *) arg);
+ case M_PROPERTY_SET: {
+ float value = *(float *)arg;
+ ao_control_vol_t vol = {value, value};
+ if (ao_control(ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK)
+ return M_PROPERTY_UNAVAILABLE;
return M_PROPERTY_OK;
- case M_PROPERTY_GET:
- *(int *)arg = mixer_getmute(mpctx->mixer);
+ }
+ case M_PROPERTY_GET: {
+ ao_control_vol_t vol = {0};
+ if (ao_control(ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK)
+ return M_PROPERTY_UNAVAILABLE;
+ *(float *)arg = (vol.left + vol.right) / 2.0f;
return M_PROPERTY_OK;
+ }
case M_PROPERTY_GET_TYPE:
- *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
+ *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLOAT};
return M_PROPERTY_OK;
}
return M_PROPERTY_NOT_IMPLEMENTED;
}
-static int mp_property_volrestore(void *ctx, struct m_property *prop,
- int action, void *arg)
+
+static int mp_property_ao_mute(void *ctx, struct m_property *prop,
+ int action, void *arg)
{
MPContext *mpctx = ctx;
+ struct ao *ao = mpctx->ao;
+ if (!ao)
+ return M_PROPERTY_NOT_IMPLEMENTED;
+
switch (action) {
+ case M_PROPERTY_SET: {
+ bool value = *(int *)arg;
+ if (ao_control(ao, AOCONTROL_SET_MUTE, &value) != CONTROL_OK)
+ return M_PROPERTY_UNAVAILABLE;
+ return M_PROPERTY_OK;
+ }
case M_PROPERTY_GET: {
- char *s = mixer_get_volume_restore_data(mpctx->mixer);
- *(char **)arg = s;
- return s ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE;
+ bool value = false;
+ if (ao_control(ao, AOCONTROL_GET_MUTE, &value) != CONTROL_OK)
+ return M_PROPERTY_UNAVAILABLE;
+ *(int *)arg = value;
+ return M_PROPERTY_OK;
}
- case M_PROPERTY_SET:
- return M_PROPERTY_NOT_IMPLEMENTED;
+ case M_PROPERTY_GET_TYPE:
+ *(struct m_option *)arg = (struct m_option){.type = CONF_TYPE_FLAG};
+ return M_PROPERTY_OK;
}
- return mp_property_generic_option(mpctx, prop, action, arg);
+ return M_PROPERTY_NOT_IMPLEMENTED;
}
static int get_device_entry(int item, int action, void *arg, void *ctx)
@@ -2257,88 +2285,6 @@ static int mp_property_detected_hwdec(void *ctx, struct m_property *prop,
return M_PROPERTY_NOT_IMPLEMENTED;
}
-#define VF_DEINTERLACE_LABEL "deinterlace"
-
-static bool probe_deint_filter(struct MPContext *mpctx, const char *filt)
-{
- char filter[80];
- // add a label so that removing the filter is easier
- snprintf(filter, sizeof(filter), "@%s:%s", VF_DEINTERLACE_LABEL, filt);
- return edit_filters(mpctx, mp_null_log, STREAM_VIDEO, "pre", filter) >= 0;
-}
-
-static bool check_output_format(struct MPContext *mpctx, int imgfmt)
-{
- struct vo_chain *vo_c = mpctx->vo_chain;
- if (!vo_c)
- return false;
- return vo_c->vf->allowed_output_formats[imgfmt - IMGFMT_START];
-}
-
-static int probe_deint_filters(struct MPContext *mpctx)
-{
- if (check_output_format(mpctx, IMGFMT_VDPAU)) {
- char filter[80] = "vdpaupp:deint=yes";
- int pref = 0;
- vo_control(mpctx->video_out, VOCTRL_GET_PREF_DEINT, &pref);
- pref = pref < 0 ? -pref : pref;
- if (pref > 0 && pref <= 4) {
- const char *types[] =
- {"", "first-field", "bob", "temporal", "temporal-spatial"};
- mp_snprintf_cat(filter, sizeof(filter), ":deint-mode=%s",
- types[pref]);
- }
-
- probe_deint_filter(mpctx, filter);
- return 0;
- }
- if (check_output_format(mpctx, IMGFMT_VAAPI) &&
- probe_deint_filter(mpctx, "vavpp"))
- return 0;
- if ((check_output_format(mpctx, IMGFMT_D3D11VA) ||
- check_output_format(mpctx, IMGFMT_D3D11NV12)) &&
- probe_deint_filter(mpctx, "d3d11vpp"))
- return 0;
- if (probe_deint_filter(mpctx, "yadif"))
- return 0;
- return -1;
-}
-
-static int get_deinterlacing(struct MPContext *mpctx)
-{
- struct vo_chain *vo_c = mpctx->vo_chain;
- int enabled = 0;
- if (video_vf_vo_control(vo_c, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
- enabled = -1;
- if (enabled < 0) {
- // vf_lavfi doesn't support VFCTRL_GET_DEINTERLACE
- if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL))
- enabled = 1;
- }
- return enabled;
-}
-
-void remove_deint_filter(struct MPContext *mpctx)
-{
- edit_filters(mpctx, mp_null_log, STREAM_VIDEO, "del", "@" VF_DEINTERLACE_LABEL);
-}
-
-void set_deinterlacing(struct MPContext *mpctx, bool enable)
-{
- struct vo_chain *vo_c = mpctx->vo_chain;
- if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL)) {
- if (!enable)
- remove_deint_filter(mpctx);
- } else {
- if ((get_deinterlacing(mpctx) > 0) != enable) {
- int arg = enable;
- if (video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &arg) != CONTROL_OK)
- probe_deint_filters(mpctx);
- }
- }
- mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
-}
-
static int mp_property_deinterlace(void *ctx, struct m_property *prop,
int action, void *arg)
{
@@ -2568,13 +2514,13 @@ static int property_imgparams(struct mp_image_params p, int action, void *arg)
{"aspect", SUB_PROP_FLOAT(d_w / (double)d_h)},
{"par", SUB_PROP_FLOAT(p.p_w / (double)p.p_h)},
{"colormatrix",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.colorspace))},
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_names, p.color.space))},
{"colorlevels",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.colorlevels))},
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_levels_names, p.color.levels))},
{"primaries",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.primaries))},
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_prim_names, p.color.primaries))},
{"gamma",
- SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.gamma))},
+ SUB_PROP_STR(m_opt_choice_str(mp_csp_trc_names, p.color.gamma))},
{"chroma-location",
SUB_PROP_STR(m_opt_choice_str(mp_chroma_names, p.chroma_location))},
{"stereo-in",
@@ -3751,8 +3697,10 @@ static const struct m_property mp_properties[] = {
// Audio
{"mixer-active", mp_property_mixer_active},
{"volume", mp_property_volume},
- {"volume-max", mp_property_volume_max},
+ {"volume-max", mp_property_generic_option},
{"mute", mp_property_mute},
+ {"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},
@@ -3762,7 +3710,6 @@ static const struct m_property mp_properties[] = {
M_PROPERTY_DEPRECATED_ALIAS("audio-channels", "audio-params/channel-count"),
{"aid", mp_property_audio},
{"balance", mp_property_balance},
- {"volume-restore-data", mp_property_volrestore},
{"audio-device", mp_property_audio_device},
{"audio-device-list", mp_property_audio_devices},
{"current-ao", mp_property_ao},
@@ -3940,7 +3887,7 @@ static const char *const *const mp_event_property_change[] = {
"colormatrix-output-range", "colormatrix-primaries", "video-aspect"),
E(MPV_EVENT_AUDIO_RECONFIG, "audio-format", "audio-codec", "audio-bitrate",
"samplerate", "channels", "audio", "volume", "mute", "balance",
- "volume-restore-data", "current-ao", "audio-codec-name", "audio-params",
+ "current-ao", "audio-codec-name", "audio-params",
"audio-out-params", "volume-max", "mixer-active"),
E(MPV_EVENT_SEEK, "seeking", "core-idle", "eof-reached"),
E(MPV_EVENT_PLAYBACK_RESTART, "seeking", "core-idle", "eof-reached"),
@@ -4314,20 +4261,85 @@ static int edit_filters_osd(struct MPContext *mpctx, enum stream_type mediatype,
static void recreate_overlays(struct MPContext *mpctx)
{
struct command_ctx *cmd = mpctx->command_ctx;
- struct sub_bitmaps *new = &cmd->overlay_osd[0];
- if (new == cmd->overlay_osd_current)
- new += 1; // pick the unused one
+ int overlay_next = !cmd->overlay_osd_current;
+ struct sub_bitmaps *new = &cmd->overlay_osd[overlay_next];
new->format = SUBBITMAP_RGBA;
new->change_id = 1;
- // overlay array can have unused entries, but parts list must be "packed"
+
+ bool valid = false;
+
new->num_parts = 0;
for (int n = 0; n < cmd->num_overlays; n++) {
struct overlay *o = &cmd->overlays[n];
- if (o->osd.bitmap)
- MP_TARRAY_APPEND(cmd, new->parts, new->num_parts, o->osd);
+ if (o->source) {
+ struct mp_image *s = o->source;
+ struct sub_bitmap b = {
+ .bitmap = s->planes[0],
+ .stride = s->stride[0],
+ .w = s->w, .dw = s->w,
+ .h = s->h, .dh = s->h,
+ .x = o->x,
+ .y = o->y,
+ };
+ MP_TARRAY_APPEND(cmd, new->parts, new->num_parts, b);
+ }
+ }
+
+ if (!cmd->overlay_packer)
+ cmd->overlay_packer = talloc_zero(cmd, struct bitmap_packer);
+
+ cmd->overlay_packer->padding = 1; // assume bilinear scaling
+ packer_set_size(cmd->overlay_packer, new->num_parts);
+
+ for (int n = 0; n < new->num_parts; n++)
+ cmd->overlay_packer->in[n] = (struct pos){new->parts[n].w, new->parts[n].h};
+
+ if (packer_pack(cmd->overlay_packer) < 0 || new->num_parts == 0)
+ goto done;
+
+ struct pos bb[2];
+ packer_get_bb(cmd->overlay_packer, bb);
+
+ new->packed_w = bb[1].x;
+ new->packed_h = bb[1].y;
+
+ if (!new->packed || new->packed->w < new->packed_w ||
+ new->packed->h < new->packed_h)
+ {
+ talloc_free(new->packed);
+ new->packed = mp_image_alloc(IMGFMT_BGRA, cmd->overlay_packer->w,
+ cmd->overlay_packer->h);
+ if (!new->packed)
+ goto done;
+ }
+
+ // clear padding
+ mp_image_clear(new->packed, 0, 0, new->packed->w, new->packed->h);
+
+ for (int n = 0; n < new->num_parts; n++) {
+ struct sub_bitmap *b = &new->parts[n];
+ struct pos pos = cmd->overlay_packer->result[n];
+
+ int stride = new->packed->stride[0];
+ void *pdata = (uint8_t *)new->packed->planes[0] + pos.y * stride + pos.x * 4;
+ memcpy_pic(pdata, b->bitmap, b->w * 4, b->h, stride, b->stride);
+
+ b->bitmap = pdata;
+ b->stride = stride;
+
+ b->src_x = pos.x;
+ b->src_y = pos.y;
+ }
+
+ valid = true;
+done:
+ if (!valid) {
+ new->format = SUBBITMAP_EMPTY;
+ new->num_parts = 0;
}
- cmd->overlay_osd_current = new;
- osd_set_external2(mpctx->osd, cmd->overlay_osd_current);
+
+ osd_set_external2(mpctx->osd, new);
+ cmd->overlay_osd_current = overlay_next;
}
// Set overlay with the given ID to the contents as described by "new".
@@ -4342,17 +4354,11 @@ static void replace_overlay(struct MPContext *mpctx, int id, struct overlay *new
}
struct overlay *ptr = &cmd->overlays[id];
- struct overlay old = *ptr;
-
- if (!ptr->osd.bitmap && !new->osd.bitmap)
- return; // don't need to recreate or unmap
+ talloc_free(ptr->source);
*ptr = *new;
- recreate_overlays(mpctx);
- // Do this afterwards, so we never unmap while the OSD is using it.
- if (old.map_start && old.map_size)
- munmap(old.map_start, old.map_size);
+ recreate_overlays(mpctx);
}
static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
@@ -4368,18 +4374,17 @@ static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
MP_ERR(mpctx, "overlay_add: invalid id %d\n", id);
goto error;
}
- if (w < 0 || h < 0 || stride < w * 4 || (stride % 4)) {
+ if (w <= 0 || h <= 0 || stride < w * 4 || (stride % 4)) {
MP_ERR(mpctx, "overlay_add: inconsistent parameters\n");
goto error;
}
struct overlay overlay = {
- .osd = {
- .stride = stride,
- .x = x, .y = y,
- .w = w, .h = h,
- .dw = w, .dh = h,
- },
+ .source = mp_image_alloc(IMGFMT_BGRA, w, h),
+ .x = x,
+ .y = y,
};
+ if (!overlay.source)
+ goto error;
int fd = -1;
bool close_fd = true;
void *p = NULL;
@@ -4398,21 +4403,25 @@ static int overlay_add(struct MPContext *mpctx, int id, int x, int y,
} else {
fd = open(file, O_RDONLY | O_BINARY | O_CLOEXEC);
}
+ int map_size = 0;
if (fd >= 0) {
- overlay.map_size = offset + h * stride;
- void *m = mmap(NULL, overlay.map_size, PROT_READ, MAP_SHARED, fd, 0);
+ map_size = offset + h * stride;
+ void *m = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, 0);
if (close_fd)
close(fd);
- if (m && m != MAP_FAILED) {
- overlay.map_start = m;
+ if (m && m != MAP_FAILED)
p = m;
- }
}
if (!p) {
MP_ERR(mpctx, "overlay_add: could not open or map '%s'\n", file);
+ talloc_free(overlay.source);
goto error;
}
- overlay.osd.bitmap = (char *)p + offset;
+ memcpy_pic(overlay.source->planes[0], (char *)p + offset, w * 4, h,
+ overlay.source->stride[0], stride);
+ if (map_size)
+ munmap(p, map_size);
+
replace_overlay(mpctx, id, &overlay);
r = 0;
error:
@@ -4434,6 +4443,8 @@ static void overlay_uninit(struct MPContext *mpctx)
for (int id = 0; id < cmd->num_overlays; id++)
overlay_remove(mpctx, id);
osd_set_external2(mpctx->osd, NULL);
+ for (int n = 0; n < 2; n++)
+ mp_image_unrefp(&cmd->overlay_osd[n].packed);
}
struct cycle_counter {
@@ -4958,6 +4969,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
mpctx->add_osd_seek_info |=
(msg_osd ? OSD_SEEK_INFO_TEXT : 0) |
(bar_osd ? OSD_SEEK_INFO_BAR : 0);
+ mpctx->osd_force_update = true;
break;
case MP_CMD_TV_LAST_CHANNEL: {
diff --git a/player/command.h b/player/command.h
index 7c3994c39e..a233319ad7 100644
--- a/player/command.h
+++ b/player/command.h
@@ -59,7 +59,4 @@ void mp_hook_run(struct MPContext *mpctx, char *client, char *type);
void handle_ab_loop(struct MPContext *mpctx);
-void remove_deint_filter(struct MPContext *mpctx);
-void set_deinterlacing(struct MPContext *mpctx, bool enable);
-
#endif /* MPLAYER_COMMAND_H */
diff --git a/player/configfiles.c b/player/configfiles.c
index 3c42331de0..7356a9a81b 100644
--- a/player/configfiles.c
+++ b/player/configfiles.c
@@ -208,7 +208,8 @@ static const char *const backup_properties[] = {
"options/speed",
"options/edition",
"options/pause",
- "volume-restore-data",
+ "volume",
+ "mute",
"options/audio-delay",
//"balance",
"options/fullscreen",
diff --git a/player/core.h b/player/core.h
index 61360b726e..8afcfbe64d 100644
--- a/player/core.h
+++ b/player/core.h
@@ -555,5 +555,7 @@ void uninit_video_out(struct MPContext *mpctx);
void uninit_video_chain(struct MPContext *mpctx);
double calc_average_frame_duration(struct MPContext *mpctx);
int init_video_decoder(struct MPContext *mpctx, struct track *track);
+int get_deinterlacing(struct MPContext *mpctx);
+void set_deinterlacing(struct MPContext *mpctx, bool enable);
#endif /* MPLAYER_MP_CORE_H */
diff --git a/player/main.c b/player/main.c
index 21c273338c..88b60e1937 100644
--- a/player/main.c
+++ b/player/main.c
@@ -418,6 +418,18 @@ int mp_initialize(struct MPContext *mpctx, char **options)
if (handle_help_options(mpctx))
return -2;
+ if (!print_libav_versions(mp_null_log, 0)) {
+ // Using mismatched libraries can be legitimate, but even then it's
+ // a bad idea. We don't acknowledge its usefulness and stability.
+ print_libav_versions(mpctx->log, MSGL_FATAL);
+ MP_FATAL(mpctx, "\nmpv was compiled against a different version of "
+ "FFmpeg/Libav than the shared\nlibrary it is linked against. "
+ "This is most likely a broken build and could\nresult in "
+ "misbehavior and crashes.\n\nmpv does not support this "
+ "configuration and will not run - rebuild mpv instead.\n");
+ return -1;
+ }
+
if (opts->dump_stats && opts->dump_stats[0]) {
if (mp_msg_open_stats_file(mpctx->global, opts->dump_stats) < 0)
MP_ERR(mpctx, "Failed to open stats file '%s'\n", opts->dump_stats);
diff --git a/player/osd.c b/player/osd.c
index 69b8dbb8b1..aa4d7243cc 100644
--- a/player/osd.c
+++ b/player/osd.c
@@ -129,6 +129,12 @@ static void term_osd_set_status(struct MPContext *mpctx, const char *text)
{
talloc_free(mpctx->term_osd_status);
mpctx->term_osd_status = talloc_strdup(mpctx, text);
+
+ int w = 80, h = 24;
+ terminal_get_size(&w, &h);
+ if (strlen(mpctx->term_osd_status) > w)
+ mpctx->term_osd_status[w] = '\0';
+
term_osd_update(mpctx);
}
diff --git a/player/playloop.c b/player/playloop.c
index 311bbd178d..0062a30888 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -189,7 +189,7 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
if (!mpctx->demuxer->seekable) {
MP_ERR(mpctx, "Cannot seek in this file.\n");
- MP_ERR(mpctx, "You can forcibly enable it with '--force-seeking=yes'.\n");
+ MP_ERR(mpctx, "You can forcibly enable it with '--force-seekable=yes'.\n");
return;
}
diff --git a/player/video.c b/player/video.c
index 1d2dc29fc6..59ce72f23e 100644
--- a/player/video.c
+++ b/player/video.c
@@ -49,6 +49,8 @@
#include "command.h"
#include "screenshot.h"
+#define VF_DEINTERLACE_LABEL "deinterlace"
+
enum {
// update_video() - code also uses: <0 error, 0 eof, >0 progress
VD_ERROR = -1,
@@ -153,8 +155,45 @@ static int try_filter(struct vo_chain *vo_c, char *name, char *label, char **arg
return 0;
}
+static bool check_output_format(struct vo_chain *vo_c, int imgfmt)
+{
+ return vo_c->vf->output_params.imgfmt == imgfmt;
+}
+
+static int probe_deint_filters(struct vo_chain *vo_c)
+{
+ // Usually, we prefer inserting/removing deint filters. But If there's VO
+ // support, or the user inserted a filter that supports swichting deint and
+ // that has no VF_DEINTERLACE_LABEL, or if the filter was auto-inserted
+ // for other reasons and supports switching deint (like vf_d3d11vpp), then
+ // use the runtime switching method.
+ if (video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &(int){1}) == CONTROL_OK)
+ return 0;
+
+ if (check_output_format(vo_c, IMGFMT_VDPAU)) {
+ char *args[5] = {"deint", "yes"};
+ int pref = 0;
+ vo_control(vo_c->vo, VOCTRL_GET_PREF_DEINT, &pref);
+ pref = pref < 0 ? -pref : pref;
+ if (pref > 0 && pref <= 4) {
+ const char *types[] =
+ {"", "first-field", "bob", "temporal", "temporal-spatial"};
+ args[2] = "deint-mode";
+ args[3] = (char *)types[pref];
+ }
+
+ return try_filter(vo_c, "vdpaupp", VF_DEINTERLACE_LABEL, args);
+ }
+ if (check_output_format(vo_c, IMGFMT_VAAPI))
+ return try_filter(vo_c, "vavpp", VF_DEINTERLACE_LABEL, NULL);
+ if (check_output_format(vo_c, IMGFMT_D3D11VA) ||
+ check_output_format(vo_c, IMGFMT_D3D11NV12))
+ return try_filter(vo_c, "d3d11vpp", VF_DEINTERLACE_LABEL, NULL);
+ return try_filter(vo_c, "yadif", VF_DEINTERLACE_LABEL, NULL);
+}
+
// Reconfigure the filter chain according to the new input format.
-static void filter_reconfig(struct vo_chain *vo_c)
+static void filter_reconfig(struct MPContext *mpctx, struct vo_chain *vo_c)
{
struct mp_image_params params = vo_c->input_format;
if (!params.imgfmt)
@@ -162,19 +201,21 @@ static void filter_reconfig(struct vo_chain *vo_c)
set_allowed_vo_formats(vo_c);
- if (vf_reconfig(vo_c->vf, &params) < 0)
- return;
-
- char *filters[] = {"autorotate", "autostereo3d", NULL};
+ char *filters[] = {"autorotate", "autostereo3d", "deinterlace", NULL};
for (int n = 0; filters[n]; n++) {
struct vf_instance *vf = vf_find_by_label(vo_c->vf, filters[n]);
- if (vf) {
+ if (vf)
vf_remove_filter(vo_c->vf, vf);
- if (vf_reconfig(vo_c->vf, &params) < 0)
- return;
- }
}
+ if (vo_c->vf->initialized < 1) {
+ if (vf_reconfig(vo_c->vf, &params) < 0)
+ return;
+ }
+
+ // Make sure to reset this even if runtime deint switching is used.
+ video_vf_vo_control(vo_c, VFCTRL_SET_DEINTERLACE, &(int){0});
+
if (params.rotate && (params.rotate % 90 == 0)) {
if (!(vo_c->vo->driver->caps & VO_CAP_ROTATE90)) {
// Try to insert a rotation filter.
@@ -194,6 +235,42 @@ static void filter_reconfig(struct vo_chain *vo_c)
MP_ERR(vo_c, "Can't insert 3D conversion filter.\n");
}
}
+
+ if (mpctx->opts->deinterlace == 1)
+ probe_deint_filters(vo_c);
+}
+
+static void recreate_auto_filters(struct MPContext *mpctx)
+{
+ filter_reconfig(mpctx, mpctx->vo_chain);
+
+ mp_force_video_refresh(mpctx);
+
+ mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
+}
+
+int get_deinterlacing(struct MPContext *mpctx)
+{
+ struct vo_chain *vo_c = mpctx->vo_chain;
+ int enabled = 0;
+ if (video_vf_vo_control(vo_c, VFCTRL_GET_DEINTERLACE, &enabled) != CONTROL_OK)
+ enabled = -1;
+ if (enabled < 0) {
+ // vf_lavfi doesn't support VFCTRL_GET_DEINTERLACE
+ if (vf_find_by_label(vo_c->vf, VF_DEINTERLACE_LABEL))
+ enabled = 1;
+ }
+ return enabled;
+}
+
+void set_deinterlacing(struct MPContext *mpctx, bool enable)
+{
+ if (enable == (get_deinterlacing(mpctx) > 0))
+ return;
+
+ mpctx->opts->deinterlace = enable;
+ recreate_auto_filters(mpctx);
+ mpctx->opts->deinterlace = get_deinterlacing(mpctx) > 0;
}
static void recreate_video_filters(struct MPContext *mpctx)
@@ -230,7 +307,7 @@ int reinit_video_filters(struct MPContext *mpctx)
recreate_video_filters(mpctx);
if (need_reconfig)
- filter_reconfig(vo_c);
+ filter_reconfig(mpctx, vo_c);
mp_force_video_refresh(mpctx);
@@ -314,7 +391,6 @@ void uninit_video_chain(struct MPContext *mpctx)
mpctx->video_status = STATUS_EOF;
- remove_deint_filter(mpctx);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
}
}
@@ -522,22 +598,6 @@ static int decode_image(struct MPContext *mpctx)
}
}
-// Called after video reinit. This can be generally used to try to insert more
-// filters using the filter chain edit functionality in command.c.
-static void init_filter_params(struct MPContext *mpctx)
-{
- struct MPOpts *opts = mpctx->opts;
-
- // Note that the filter chain is already initialized. This code might
- // recreate the chain a second time, which is not very elegant, but allows
- // us to test whether enabling deinterlacing works with the current video
- // format and other filters.
- if (opts->deinterlace >= 0) {
- remove_deint_filter(mpctx);
- set_deinterlacing(mpctx, opts->deinterlace != 0);
- }
-}
-
// Feed newly decoded frames to the filter, take care of format changes.
// If eof=true, drain the filter chain, and return VD_EOF if empty.
static int video_filter(struct MPContext *mpctx, bool eof)
@@ -564,7 +624,8 @@ static int video_filter(struct MPContext *mpctx, bool eof)
return VD_PROGRESS;
// The filter chain is drained; execute the filter format change.
- filter_reconfig(mpctx->vo_chain);
+ vf->initialized = 0;
+ filter_reconfig(mpctx, mpctx->vo_chain);
mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL);
@@ -586,7 +647,6 @@ static int video_filter(struct MPContext *mpctx, bool eof)
MP_FATAL(mpctx, "Cannot initialize video filters.\n");
return VD_ERROR;
}
- init_filter_params(mpctx);
return VD_RECONFIG;
}
@@ -1360,11 +1420,13 @@ void write_video(struct MPContext *mpctx)
};
calculate_frame_duration(mpctx);
+ int req = vo_get_num_req_frames(mpctx->video_out);
+ assert(req >= 1 && req <= VO_MAX_REQ_FRAMES);
struct vo_frame dummy = {
.pts = pts,
.duration = -1,
.still = mpctx->step_frames > 0,
- .num_frames = MPMIN(mpctx->num_next_frames, VO_MAX_REQ_FRAMES),
+ .num_frames = MPMIN(mpctx->num_next_frames, req),
.num_vsyncs = 1,
};
for (int n = 0; n < dummy.num_frames; n++)