From 43386a7c92e14e56abdebfd4ed9335794c795f2b Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 2 Jan 2017 18:12:51 +0100 Subject: af_lavfi, vf_lavfi: work around recent libavfilter EOF bug Looks quite like a bug. If you have a filter chain with only the dynaudnorm filter, and send call av_buffersrc_add_frame(s, NULL), then subsequent av_buffersink_get_frame() calls will return EAGAIN instead of EOF. This was apparently caused by a recent change in FFmpeg. Some other circumstances (which I didn't fully analyze and which is due to the playloop's absurd temporary-EOF behavior on seeks) then led the decoder loop to send data again, but since libavfilter was stuck in the EOF state now, it could never recover. It kept sending new input (due to missing output), until the demuxer refused to return more audio packets. Each time a filter error was printed. Fortunately, it's pretty easy to workaround. We just mark the p->eof flag as we send an EOF frame to libavfilter. The p->eof flag is used only to recover from temporary EOF: it resets the filter if new data is available again. We don't care much about av_buffersink_get_frame() returning a broken EAGAIN state in this situation and essentially ignore it, meaning if we get EAGAIN after sending EOF, we assume effectively that EOF was fully reached. --- video/filter/vf_lavfi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'video/filter') diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c index a66bd2a971..6c18f829c4 100644 --- a/video/filter/vf_lavfi.c +++ b/video/filter/vf_lavfi.c @@ -298,6 +298,12 @@ static int filter_ext(struct vf_instance *vf, struct mp_image *mpi) if (!p->graph) return -1; + if (!mpi) { + if (p->eof) + return 0; + p->eof = true; + } + AVFrame *frame = mp_to_av(vf, mpi); int r = av_buffersrc_add_frame(p->in, frame) < 0 ? -1 : 0; av_frame_free(&frame); -- cgit v1.2.3 From e48f1f31cb8928b66834104bb492ade3eeafc701 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 13 Jan 2017 16:26:31 +0100 Subject: vf_lavfi: remove pixel format whitelist Pointless now. --- video/filter/vf_lavfi.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) (limited to 'video/filter') diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c index 6c18f829c4..aceaf50ecb 100644 --- a/video/filter/vf_lavfi.c +++ b/video/filter/vf_lavfi.c @@ -108,7 +108,7 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) { void *tmp = talloc_new(NULL); struct vf_priv_s *p = vf->priv; - AVFilterContext *in = NULL, *out = NULL, *f_format = NULL; + AVFilterContext *in = NULL, *out = NULL; if (bstr0(p->cfg_graph).len == 0) { MP_FATAL(vf, "lavfi: no filter graph set\n"); @@ -130,19 +130,6 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) if (!outputs || !inputs) goto error; - // Build list of acceptable output pixel formats. libavfilter will insert - // conversion filters if needed. - char *fmtstr = talloc_strdup(tmp, ""); - for (int n = IMGFMT_START; n < IMGFMT_END; n++) { - if (vf_next_query_format(vf, n)) { - const char *name = av_get_pix_fmt_name(imgfmt2pixfmt(n)); - if (name) { - const char *s = fmtstr[0] ? "|" : ""; - fmtstr = talloc_asprintf_append_buffer(fmtstr, "%s%s", s, name); - } - } - } - char *sws_flags = talloc_asprintf(tmp, "flags=%"PRId64, p->cfg_sws_flags); graph->scale_sws_opts = av_strdup(sws_flags); @@ -161,18 +148,11 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) "out", NULL, NULL, graph) < 0) goto error; - if (avfilter_graph_create_filter(&f_format, avfilter_get_by_name("format"), - "format", fmtstr, NULL, graph) < 0) - goto error; - - if (avfilter_link(f_format, 0, out, 0) < 0) - goto error; - outputs->name = av_strdup("in"); outputs->filter_ctx = in; inputs->name = av_strdup("out"); - inputs->filter_ctx = f_format; + inputs->filter_ctx = out; if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) goto error; -- cgit v1.2.3 From 91fb7078e22864ece2b94bde350edd193c3fb279 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 16 Jan 2017 15:41:50 +0100 Subject: vf_lavfi: switch to AVBufferSrcParameters Instead of using the awful older "API" that passed the parameters formatted as string. AVBufferSrcParameters is also a prerequisite for hardware frame filtering support. --- video/filter/vf_lavfi.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'video/filter') diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c index aceaf50ecb..291ddd315b 100644 --- a/video/filter/vf_lavfi.c +++ b/video/filter/vf_lavfi.c @@ -109,6 +109,7 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) void *tmp = talloc_new(NULL); struct vf_priv_s *p = vf->priv; AVFilterContext *in = NULL, *out = NULL; + int ret; if (bstr0(p->cfg_graph).len == 0) { MP_FATAL(vf, "lavfi: no filter graph set\n"); @@ -133,15 +134,27 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) char *sws_flags = talloc_asprintf(tmp, "flags=%"PRId64, p->cfg_sws_flags); graph->scale_sws_opts = av_strdup(sws_flags); - AVRational timebase = AV_TIME_BASE_Q; + in = avfilter_graph_alloc_filter(graph, avfilter_get_by_name("buffer"), "src"); + if (!in) + goto error; + + AVBufferSrcParameters *in_params = av_buffersrc_parameters_alloc(); + if (!in_params) + goto error; - char *src_args = talloc_asprintf(tmp, "%d:%d:%d:%d:%d:%d:%d", - fmt->w, fmt->h, imgfmt2pixfmt(fmt->imgfmt), - timebase.num, timebase.den, - fmt->p_w, fmt->p_h); + in_params->format = imgfmt2pixfmt(fmt->imgfmt); + in_params->time_base = AV_TIME_BASE_Q; + in_params->width = fmt->w; + in_params->height = fmt->h; + in_params->sample_aspect_ratio.num = fmt->p_w; + in_params->sample_aspect_ratio.den = fmt->p_h; + + ret = av_buffersrc_parameters_set(in, in_params); + av_free(in_params); + if (ret < 0) + goto error; - if (avfilter_graph_create_filter(&in, avfilter_get_by_name("buffer"), - "src", src_args, NULL, graph) < 0) + if (avfilter_init_str(in, NULL) < 0) goto error; if (avfilter_graph_create_filter(&out, avfilter_get_by_name("buffersink"), -- cgit v1.2.3 From 1b1771f2a709269440ccbf2fdc27cc7f5bb7248a Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 16 Jan 2017 15:52:13 +0100 Subject: video: support filtering hardware frames via libavfilter Requires a bunch of hacks: - we access AVFilterLink.hw_frames_ctx. This is not a public API in FFmpeg and Libav. Newer FFmpeg provides an accessor (av_buffersink_get_hw_frames_ctx), but it's not available in Libav or the current FFmpeg release or Libav. We need this value after filter graph creation, so We have no choice but to access this. One alternative is making filter creation and format negotiation fully lazy (i.e. delay it and do it as filters are output), but this would be a huge change. So for now, we knowingly violate FFmpeg's and Libav's ABI and API constraints because they don't provide anything better. On newer FFmpeg, we use the (quite ugly) accessor, though. - mp_image_params doesn't (and can't) have a field for the frames context AVBufferRef. So we pass it via vf_set_proto_frame(), and even more hacks. - if a filter needs a hw context, but we haven't created one yet (because normally we create them lazily), it will fail at init. - we allow any hw format now, although this could go horrible wrong. Why all this effort? We could move hw deinterlacing filters etc. to FFmpeg, which is a very worthy goal. --- video/filter/vf.c | 17 +++++++++++++++++ video/filter/vf.h | 7 +++++++ video/filter/vf_lavfi.c | 31 +++++++++++++++++++++++++------ 3 files changed, 49 insertions(+), 6 deletions(-) (limited to 'video/filter') diff --git a/video/filter/vf.c b/video/filter/vf.c index 41fbe9b208..94e6760603 100644 --- a/video/filter/vf.c +++ b/video/filter/vf.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -644,14 +645,20 @@ int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params) uint8_t unused[IMGFMT_END - IMGFMT_START]; update_formats(c, c->first, unused); + AVBufferRef *hwfctx = c->in_hwframes_ref; struct vf_instance *failing = NULL; for (struct vf_instance *vf = c->first; vf; vf = vf->next) { + av_buffer_unref(&vf->in_hwframes_ref); + av_buffer_unref(&vf->out_hwframes_ref); + vf->in_hwframes_ref = hwfctx ? av_buffer_ref(hwfctx) : NULL; + vf->out_hwframes_ref = hwfctx ? av_buffer_ref(hwfctx) : NULL; r = vf_reconfig_wrapper(vf, &cur); if (r < 0) { failing = vf; break; } cur = vf->fmt_out; + hwfctx = vf->out_hwframes_ref; } c->output_params = cur; c->initialized = r < 0 ? -1 : 1; @@ -665,6 +672,13 @@ int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params) return r; } +// Hack to get mp_image.hwctx before vf_reconfig() +void vf_set_proto_frame(struct vf_chain *c, struct mp_image *img) +{ + av_buffer_unref(&c->in_hwframes_ref); + c->in_hwframes_ref = img && img->hwctx ? av_buffer_ref(img->hwctx) : NULL; +} + struct vf_instance *vf_find_by_label(struct vf_chain *c, const char *label) { struct vf_instance *vf = c->first; @@ -678,6 +692,8 @@ struct vf_instance *vf_find_by_label(struct vf_chain *c, const char *label) static void vf_uninit_filter(vf_instance_t *vf) { + av_buffer_unref(&vf->in_hwframes_ref); + av_buffer_unref(&vf->out_hwframes_ref); if (vf->uninit) vf->uninit(vf); vf_forget_frames(vf); @@ -729,6 +745,7 @@ void vf_destroy(struct vf_chain *c) { if (!c) return; + av_buffer_unref(&c->in_hwframes_ref); while (c->first) { vf_instance_t *vf = c->first; c->first = vf->next; diff --git a/video/filter/vf.h b/video/filter/vf.h index 901ccead95..241af02081 100644 --- a/video/filter/vf.h +++ b/video/filter/vf.h @@ -90,6 +90,9 @@ typedef struct vf_instance { struct mp_image_params fmt_in, fmt_out; + // This is a dirty hack. + struct AVBufferRef *in_hwframes_ref, *out_hwframes_ref; + struct mp_image_pool *out_pool; struct vf_priv_s *priv; struct mp_log *log; @@ -123,6 +126,9 @@ struct vf_chain { struct mpv_global *global; struct mp_hwdec_devices *hwdec_devs; + // This is a dirty hack. + struct AVBufferRef *in_hwframes_ref; + // Call when the filter chain wants new processing (for filters with // asynchronous behavior) - must be immutable once filters are created, // since they are supposed to call it from foreign threads. @@ -150,6 +156,7 @@ enum vf_ctrl { struct vf_chain *vf_new(struct mpv_global *global); void vf_destroy(struct vf_chain *c); +void vf_set_proto_frame(struct vf_chain *c, struct mp_image *img); int vf_reconfig(struct vf_chain *c, const struct mp_image_params *params); int vf_control_any(struct vf_chain *c, int cmd, void *arg); int vf_control_by_label(struct vf_chain *c, int cmd, void *arg, bstr label); diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c index 291ddd315b..2f4bc92ecc 100644 --- a/video/filter/vf_lavfi.c +++ b/video/filter/vf_lavfi.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include "options/m_option.h" #include "common/tags.h" +#include "video/hwdec.h" #include "video/img_format.h" #include "video/mp_image.h" #include "video/sws_utils.h" @@ -148,6 +150,8 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) in_params->height = fmt->h; in_params->sample_aspect_ratio.num = fmt->p_w; in_params->sample_aspect_ratio.den = fmt->p_h; + // Assume it's ignored for non-hwaccel formats. + in_params->hw_frames_ctx = vf->in_hwframes_ref; ret = av_buffersrc_parameters_set(in, in_params); av_free(in_params); @@ -170,6 +174,13 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) goto error; + struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(vf->hwdec_devs); + for (int n = 0; n < graph->nb_filters; n++) { + AVFilterContext *filter = graph->filters[n]; + if (hwdec && hwdec->av_device_ref) + filter->hw_device_ctx = av_buffer_ref(hwdec->av_device_ref); + } + if (avfilter_graph_config(graph, NULL) < 0) goto error; @@ -226,17 +237,25 @@ static int reconfig(struct vf_instance *vf, struct mp_image_params *in, out->p_w = l_out->sample_aspect_ratio.num; out->p_h = l_out->sample_aspect_ratio.den; out->imgfmt = pixfmt2imgfmt(l_out->format); + av_buffer_unref(&vf->out_hwframes_ref); +#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(6, 69, 100) && \ + LIBAVFILTER_VERSION_MICRO >= 100 + AVBufferRef *hw_frames_ctx = av_buffersink_get_hw_frames_ctx(p->out); +#else + AVBufferRef *hw_frames_ctx = l_out->hw_frames_ctx; +#endif + if (hw_frames_ctx) { + AVHWFramesContext *fctx = (void *)hw_frames_ctx->data; + out->hw_subfmt = pixfmt2imgfmt(fctx->sw_format); + vf->out_hwframes_ref = av_buffer_ref(hw_frames_ctx); + } return 0; } static int query_format(struct vf_instance *vf, unsigned int fmt) { - // We accept all sws-convertable formats as inputs. Output formats are - // handled in config(). The current public libavfilter API doesn't really - // allow us to do anything more sophisticated. - // This breaks with filters which accept input pixel formats not - // supported by libswscale. - return !!mp_sws_supported_format(fmt); + // Format negotiation is not possible with libavfilter. + return 1; } static AVFrame *mp_to_av(struct vf_instance *vf, struct mp_image *img) -- cgit v1.2.3 From b14fac9afa79e44d8b0323c6ddbef1557cdd0d8d Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 24 Jan 2017 08:11:42 +0100 Subject: build: replace some FFmpeg API checks with version checks The FFmpeg versions we support all have the APIs we were checking for. Only Libav missed them. Simplify this by explicitly checking for FFmpeg in the code, instead of trying to detect the presence of the API. --- video/filter/vf_lavfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'video/filter') diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c index 2f4bc92ecc..e28d7fbb6f 100644 --- a/video/filter/vf_lavfi.c +++ b/video/filter/vf_lavfi.c @@ -287,7 +287,7 @@ static struct mp_image *av_to_mp(struct vf_instance *vf, AVFrame *av_frame) static void get_metadata_from_av_frame(struct vf_instance *vf, AVFrame *frame) { -#if HAVE_AVFRAME_METADATA +#if LIBAVUTIL_VERSION_MICRO >= 100 struct vf_priv_s *p = vf->priv; if (!p->metadata) p->metadata = talloc_zero(p, struct mp_tags); -- cgit v1.2.3 From 39adaf3dcc3a0e03df8048d93324940b5f69df51 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 25 Jan 2017 08:32:35 +0100 Subject: vf_lavfi: don't crash with VOs without hardware decoding support When playing with VOs which do not provide mp_hwdec_ctx, vf->hwdec_devs will remain NULL. This would make it crash on hwdec_devices_get_first(), even if no hardware decoding or filters using hardware decoding were involved. Fixes #4064. --- video/filter/vf_lavfi.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'video/filter') diff --git a/video/filter/vf_lavfi.c b/video/filter/vf_lavfi.c index e28d7fbb6f..d79bfd4011 100644 --- a/video/filter/vf_lavfi.c +++ b/video/filter/vf_lavfi.c @@ -174,11 +174,13 @@ static bool recreate_graph(struct vf_instance *vf, struct mp_image_params *fmt) if (graph_parse(graph, p->cfg_graph, inputs, outputs, NULL) < 0) goto error; - struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(vf->hwdec_devs); - for (int n = 0; n < graph->nb_filters; n++) { - AVFilterContext *filter = graph->filters[n]; - if (hwdec && hwdec->av_device_ref) - filter->hw_device_ctx = av_buffer_ref(hwdec->av_device_ref); + if (vf->hwdec_devs) { + struct mp_hwdec_ctx *hwdec = hwdec_devices_get_first(vf->hwdec_devs); + for (int n = 0; n < graph->nb_filters; n++) { + AVFilterContext *filter = graph->filters[n]; + if (hwdec && hwdec->av_device_ref) + filter->hw_device_ctx = av_buffer_ref(hwdec->av_device_ref); + } } if (avfilter_graph_config(graph, NULL) < 0) -- cgit v1.2.3