diff options
Diffstat (limited to 'player')
-rw-r--r-- | player/audio.c | 2 | ||||
-rw-r--r-- | player/command.c | 330 | ||||
-rw-r--r-- | player/command.h | 3 | ||||
-rw-r--r-- | player/configfiles.c | 3 | ||||
-rw-r--r-- | player/core.h | 2 | ||||
-rw-r--r-- | player/main.c | 12 | ||||
-rw-r--r-- | player/osd.c | 6 | ||||
-rw-r--r-- | player/playloop.c | 2 | ||||
-rw-r--r-- | player/video.c | 122 |
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, ¶ms) < 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, ¶ms) < 0) - return; - } } + if (vo_c->vf->initialized < 1) { + if (vf_reconfig(vo_c->vf, ¶ms) < 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++) |