From a4208b9b7d62c09414bd42172c6f30a8296dd1eb Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 13 Mar 2013 14:24:45 +0100 Subject: [PATCH 1/6] avconv: add options for reading filtergraphs from a file. --- Changelog | 2 ++ avconv.c | 1 + avconv.h | 2 ++ avconv_opt.c | 90 ++++++++++++++++++++++++++++++++++++++++++++----- doc/avconv.texi | 12 +++++++ 5 files changed, 99 insertions(+), 8 deletions(-) diff --git a/Changelog b/Changelog index 15c0873768..1c493c2dc8 100644 --- a/Changelog +++ b/Changelog @@ -7,6 +7,8 @@ version 10: - reference-counting for AVFrame and AVPacket data - avconv now fails when input options are used for output file or vice versa +- new avconv options -filter_script and -filter_complex_script, which allow a + filtergraph description to be read from a file version 9: diff --git a/avconv.c b/avconv.c index 1a18ad336d..a5a4c7f61e 100644 --- a/avconv.c +++ b/avconv.c @@ -160,6 +160,7 @@ static void exit_program(void) av_freep(&filtergraphs[i]->outputs[j]); } av_freep(&filtergraphs[i]->outputs); + av_freep(&filtergraphs[i]->graph_desc); av_freep(&filtergraphs[i]); } av_freep(&filtergraphs); diff --git a/avconv.h b/avconv.h index eb9df9b16a..73e20e8fe0 100644 --- a/avconv.h +++ b/avconv.h @@ -158,6 +158,8 @@ typedef struct OptionsContext { int nb_copy_initial_nonkeyframes; SpecifierOpt *filters; int nb_filters; + SpecifierOpt *filter_scripts; + int nb_filter_scripts; SpecifierOpt *pass; int nb_pass; SpecifierOpt *passlogfiles; diff --git a/avconv_opt.c b/avconv_opt.c index 2c43da2bbd..2e5cb05a4a 100644 --- a/avconv_opt.c +++ b/avconv_opt.c @@ -926,6 +926,59 @@ static void parse_matrix_coeffs(uint16_t *dest, const char *str) } } +/* read file contents into a string */ +static uint8_t *read_file(const char *filename) +{ + AVIOContext *pb = NULL; + AVIOContext *dyn_buf = NULL; + int ret = avio_open(&pb, filename, AVIO_FLAG_READ); + uint8_t buf[1024], *str; + + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Error opening file %s.\n", filename); + return NULL; + } + + ret = avio_open_dyn_buf(&dyn_buf); + if (ret < 0) { + avio_closep(&pb); + return NULL; + } + while ((ret = avio_read(pb, buf, sizeof(buf))) > 0) + avio_write(dyn_buf, buf, ret); + avio_w8(dyn_buf, 0); + avio_closep(&pb); + + ret = avio_close_dyn_buf(dyn_buf, &str); + if (ret < 0) + return NULL; + return str; +} + +static char *get_ost_filters(OptionsContext *o, AVFormatContext *oc, + OutputStream *ost) +{ + AVStream *st = ost->st; + char *filter = NULL, *filter_script = NULL; + + MATCH_PER_STREAM_OPT(filter_scripts, str, filter_script, oc, st); + MATCH_PER_STREAM_OPT(filters, str, filter, oc, st); + + if (filter_script && filter) { + av_log(NULL, AV_LOG_ERROR, "Both -filter and -filter_script set for " + "output stream #%d:%d.\n", nb_output_files, st->index); + exit(1); + } + + if (filter_script) + return read_file(filter_script); + else if (filter) + return av_strdup(filter); + + return av_strdup(st->codec->codec_type == AVMEDIA_TYPE_VIDEO ? + "null" : "anull"); +} + static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc) { AVStream *st; @@ -941,7 +994,6 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc) char *frame_rate = NULL, *frame_size = NULL; char *frame_aspect_ratio = NULL, *frame_pix_fmt = NULL; char *intra_matrix = NULL, *inter_matrix = NULL; - const char *filters = "null"; int do_pass = 0; int i; @@ -1036,8 +1088,10 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc) ost->top_field_first = -1; MATCH_PER_STREAM_OPT(top_field_first, i, ost->top_field_first, oc, st); - MATCH_PER_STREAM_OPT(filters, str, filters, oc, st); - ost->avfilter = av_strdup(filters); + + ost->avfilter = get_ost_filters(o, oc, ost); + if (!ost->avfilter) + exit(1); } else { MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, ost->copy_initial_nonkeyframes, oc ,st); } @@ -1059,7 +1113,6 @@ static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc) if (!ost->stream_copy) { char *sample_fmt = NULL; - const char *filters = "anull"; MATCH_PER_STREAM_OPT(audio_channels, i, audio_enc->channels, oc, st); @@ -1072,8 +1125,9 @@ static OutputStream *new_audio_stream(OptionsContext *o, AVFormatContext *oc) MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, oc, st); - MATCH_PER_STREAM_OPT(filters, str, filters, oc, st); - ost->avfilter = av_strdup(filters); + ost->avfilter = get_ost_filters(o, oc, ost); + if (!ost->avfilter) + exit(1); } return ost; @@ -1878,8 +1932,24 @@ static int opt_filter_complex(void *optctx, const char *opt, const char *arg) GROW_ARRAY(filtergraphs, nb_filtergraphs); if (!(filtergraphs[nb_filtergraphs - 1] = av_mallocz(sizeof(*filtergraphs[0])))) return AVERROR(ENOMEM); - filtergraphs[nb_filtergraphs - 1]->index = nb_filtergraphs - 1; - filtergraphs[nb_filtergraphs - 1]->graph_desc = arg; + filtergraphs[nb_filtergraphs - 1]->index = nb_filtergraphs - 1; + filtergraphs[nb_filtergraphs - 1]->graph_desc = av_strdup(arg); + if (!filtergraphs[nb_filtergraphs - 1]->graph_desc) + return AVERROR(ENOMEM); + return 0; +} + +static int opt_filter_complex_script(void *optctx, const char *opt, const char *arg) +{ + uint8_t *graph_desc = read_file(arg); + if (!graph_desc) + return AVERROR(EINVAL); + + GROW_ARRAY(filtergraphs, nb_filtergraphs); + if (!(filtergraphs[nb_filtergraphs - 1] = av_mallocz(sizeof(*filtergraphs[0])))) + return AVERROR(ENOMEM); + filtergraphs[nb_filtergraphs - 1]->index = nb_filtergraphs - 1; + filtergraphs[nb_filtergraphs - 1]->graph_desc = graph_desc; return 0; } @@ -2138,8 +2208,12 @@ const OptionDef options[] = { "use fixed quality scale (VBR)", "q" }, { "filter", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filters) }, "set stream filterchain", "filter_list" }, + { "filter_script", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filter_scripts) }, + "read stream filtergraph description from a file", "filename" }, { "filter_complex", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex }, "create a complex filtergraph", "graph_description" }, + { "filter_complex_script", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex_script }, + "read complex filtergraph description from a file", "filename" }, { "stats", OPT_BOOL, { &print_stats }, "print progress report during encoding", }, { "attach", HAS_ARG | OPT_PERFILE | OPT_EXPERT | diff --git a/doc/avconv.texi b/doc/avconv.texi index 7341d2fda6..3e1dc4cf34 100644 --- a/doc/avconv.texi +++ b/doc/avconv.texi @@ -330,6 +330,12 @@ the stream. Use @code{-filters} to show all the available filters See also the @option{-filter_complex} option if you want to create filter graphs with multiple inputs and/or outputs. + +@item -filter_script[:@var{stream_specifier}] @var{filename} (@emph{output,per-stream}) +This option is similar to @option{-filter}, the only difference is that its +argument is the name of the file from which a filtergraph description is to be +read. + @item -pre[:@var{stream_specifier}] @var{preset_name} (@emph{output,per-stream}) Specify the preset for matching stream(s). @@ -823,6 +829,12 @@ To generate 5 seconds of pure red video using lavfi @code{color} source: @example avconv -filter_complex 'color=red' -t 5 out.mkv @end example + +@item -filter_complex_script @var{filename} (@emph{global}) +This option is similar to @option{-filter_complex}, the only difference is that +its argument is the name of the file from which a complex filtergraph +description is to be read. + @end table @c man end OPTIONS From f2a59722d161981c2161ba25f3aea42d65b818f8 Mon Sep 17 00:00:00 2001 From: Diego Biurrun Date: Sat, 1 Dec 2012 20:38:26 +0100 Subject: [PATCH 2/6] fate: filter: Add dependencies Signed-off-by: Anton Khirnov --- tests/fate/filter.mak | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/fate/filter.mak b/tests/fate/filter.mak index f16aee19cc..398ecfa855 100644 --- a/tests/fate/filter.mak +++ b/tests/fate/filter.mak @@ -1,3 +1,7 @@ +FILTERDEMDEC = $(call ALLYES, $(1)_FILTER $(2)_DEMUXER $(3)_DECODER) +FILTERDEMDECMUX = $(call ALLYES, $(1)_FILTER $(2)_DEMUXER $(3)_DECODER $(4)_MUXER) +FILTERDEMDECENCMUX = $(call ALLYES, $(1)_FILTER $(2)_DEMUXER $(3)_DECODER $(4)_ENCODER $(5)_MUXER) + FATE_AMIX += fate-filter-amix-simple fate-filter-amix-simple: CMD = avconv -filter_complex amix -i $(SRC) -ss 3 -i $(SRC1) -f f32le - fate-filter-amix-simple: REF = $(SAMPLES)/filter/amix_simple.pcm @@ -18,9 +22,9 @@ $(FATE_AMIX): SRC1 = $(TARGET_PATH)/tests/data/asynth-44100-2-2.wav $(FATE_AMIX): CMP = oneoff $(FATE_AMIX): CMP_UNIT = f32 -FATE_FILTER-$(CONFIG_AMIX_FILTER) += $(FATE_AMIX) +FATE_FILTER-$(call FILTERDEMDECENCMUX, AMIX, WAV, PCM_S16LE, PCM_F32LE, PCM_F32LE) += $(FATE_AMIX) -FATE_FILTER-$(CONFIG_ASYNCTS_FILTER) += fate-filter-asyncts +FATE_FILTER-$(call FILTERDEMDECMUX, ASYNCTS, FLV, NELLYMOSER, PCM_S16LE) += fate-filter-asyncts fate-filter-asyncts: SRC = $(SAMPLES)/nellymoser/nellymoser-discont.flv fate-filter-asyncts: CMD = pcm -analyzeduration 10000000 -i $(SRC) -af asyncts fate-filter-asyncts: CMP = oneoff @@ -28,7 +32,7 @@ fate-filter-asyncts: REF = $(SAMPLES)/nellymoser/nellymoser-discont.pcm fate-filter-delogo: CMD = framecrc -i $(SAMPLES)/real/rv30.rm -vf delogo=show=0:x=290:y=25:w=26:h=16 -an -FATE_FILTER-$(CONFIG_DELOGO_FILTER) += fate-filter-delogo +FATE_FILTER-$(call FILTERDEMDEC, DELOGO, RM, RV30) += fate-filter-delogo FATE_YADIF += fate-filter-yadif-mode0 fate-filter-yadif-mode0: CMD = framecrc -flags bitexact -idct simple -i $(SAMPLES)/mpeg2/mpeg2_field_encoding.ts -vf yadif=0 @@ -36,7 +40,7 @@ fate-filter-yadif-mode0: CMD = framecrc -flags bitexact -idct simple -i $(SAMPLE FATE_YADIF += fate-filter-yadif-mode1 fate-filter-yadif-mode1: CMD = framecrc -flags bitexact -idct simple -i $(SAMPLES)/mpeg2/mpeg2_field_encoding.ts -vf yadif=1 -FATE_FILTER-$(CONFIG_YADIF_FILTER) += $(FATE_YADIF) +FATE_FILTER-$(call FILTERDEMDEC, YADIF, MPEGTS, MPEG2VIDEO) += $(FATE_YADIF) FATE_SAMPLES_AVCONV += $(FATE_FILTER-yes) fate-filter: $(FATE_FILTER-yes) From 8b9a153ef3673d5847291987fa0dcddeac4a640b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= Date: Wed, 5 Dec 2012 04:58:03 +0100 Subject: [PATCH 3/6] lavfi/gradfun: do not increment DC pointer for odd values. First DC is only used once otherwise. This also makes the code consistent with ASM versions. Signed-off-by: Anton Khirnov --- libavfilter/vf_gradfun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavfilter/vf_gradfun.c b/libavfilter/vf_gradfun.c index ca7ef69144..80b7e412e3 100644 --- a/libavfilter/vf_gradfun.c +++ b/libavfilter/vf_gradfun.c @@ -56,7 +56,7 @@ DECLARE_ALIGNED(16, static const uint16_t, dither)[8][8] = { void ff_gradfun_filter_line_c(uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers) { int x; - for (x = 0; x < width; x++, dc += x & 1) { + for (x = 0; x < width; dc += x & 1, x++) { int pix = src[x] << 7; int delta = dc[0] - pix; int m = abs(delta) * thresh >> 16; From 2d66fc543b01995d6146fc132a778d3e722ca665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= Date: Fri, 7 Dec 2012 00:36:29 +0100 Subject: [PATCH 4/6] lavfi/gradfun: fix rounding in MMX code. Current code divides before increasing precision. Also reduce upper bound for strength from 255 to 64. This will prevent an overflow in the SSSE3 and MMX filter_line code: delta is expressed as an u16 being shifted by 2 to the left. If it overflows, having a strength not above 64 will make sure that m is set to 0 (making the m*m*delta >> 14 expression void). A value above 64 should not make any sense unless gradfun is used as a blur filter. Signed-off-by: Anton Khirnov --- doc/filters.texi | 2 +- libavfilter/vf_gradfun.c | 2 +- libavfilter/x86/vf_gradfun.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 9b39ea50ef..88888c60e3 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -1142,7 +1142,7 @@ The filter takes two optional parameters, separated by ':': @var{strength} is the maximum amount by which the filter will change any one pixel. Also the threshold for detecting nearly flat -regions. Acceptable values range from .51 to 255, default value is +regions. Acceptable values range from .51 to 64, default value is 1.2, out-of-range values will be clipped to the valid range. @var{radius} is the neighborhood to fit the gradient to. A larger diff --git a/libavfilter/vf_gradfun.c b/libavfilter/vf_gradfun.c index 80b7e412e3..900ef604e8 100644 --- a/libavfilter/vf_gradfun.c +++ b/libavfilter/vf_gradfun.c @@ -128,7 +128,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args) if (args) sscanf(args, "%f:%d", &thresh, &radius); - thresh = av_clipf(thresh, 0.51, 255); + thresh = av_clipf(thresh, 0.51, 64); gf->thresh = (1 << 15) / thresh; gf->radius = av_clip((radius + 1) & ~1, 4, 32); diff --git a/libavfilter/x86/vf_gradfun.c b/libavfilter/x86/vf_gradfun.c index b4ca86c617..75c117a9a2 100644 --- a/libavfilter/x86/vf_gradfun.c +++ b/libavfilter/x86/vf_gradfun.c @@ -62,8 +62,8 @@ static void gradfun_filter_line_mmxext(uint8_t *dst, uint8_t *src, uint16_t *dc, "pminsw %%mm7, %%mm2 \n" // m = -max(0, 127-m) "pmullw %%mm2, %%mm2 \n" "paddw %%mm4, %%mm0 \n" // pix += dither - "pmulhw %%mm2, %%mm1 \n" "psllw $2, %%mm1 \n" // m = m*m*delta >> 14 + "pmulhw %%mm2, %%mm1 \n" "paddw %%mm1, %%mm0 \n" // pix += m "psraw $7, %%mm0 \n" "packuswb %%mm0, %%mm0 \n" From 38a2f88d39e50c573193b6d3f14df58b6c4e3a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= Date: Fri, 7 Dec 2012 00:39:31 +0100 Subject: [PATCH 5/6] lavfi/gradfun: fix dithering in MMX code. Current dithering only uses the first 4 instead of the whole 8 random values. Signed-off-by: Anton Khirnov --- libavfilter/x86/vf_gradfun.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/libavfilter/x86/vf_gradfun.c b/libavfilter/x86/vf_gradfun.c index 75c117a9a2..a9e069a5bf 100644 --- a/libavfilter/x86/vf_gradfun.c +++ b/libavfilter/x86/vf_gradfun.c @@ -46,8 +46,33 @@ static void gradfun_filter_line_mmxext(uint8_t *dst, uint8_t *src, uint16_t *dc, "pxor %%mm7, %%mm7 \n" "pshufw $0, %%mm5, %%mm5 \n" "movq %6, %%mm6 \n" - "movq %5, %%mm4 \n" + "movq (%5), %%mm3 \n" + "movq 8(%5), %%mm4 \n" + "1: \n" + "movd (%2,%0), %%mm0 \n" + "movd (%3,%0), %%mm1 \n" + "punpcklbw %%mm7, %%mm0 \n" + "punpcklwd %%mm1, %%mm1 \n" + "psllw $7, %%mm0 \n" + "pxor %%mm2, %%mm2 \n" + "psubw %%mm0, %%mm1 \n" // delta = dc - pix + "psubw %%mm1, %%mm2 \n" + "pmaxsw %%mm1, %%mm2 \n" + "pmulhuw %%mm5, %%mm2 \n" // m = abs(delta) * thresh >> 16 + "psubw %%mm6, %%mm2 \n" + "pminsw %%mm7, %%mm2 \n" // m = -max(0, 127-m) + "pmullw %%mm2, %%mm2 \n" + "paddw %%mm3, %%mm0 \n" // pix += dither + "psllw $2, %%mm1 \n" // m = m*m*delta >> 14 + "pmulhw %%mm2, %%mm1 \n" + "paddw %%mm1, %%mm0 \n" // pix += m + "psraw $7, %%mm0 \n" + "packuswb %%mm0, %%mm0 \n" + "movd %%mm0, (%1,%0) \n" // dst = clip(pix>>7) + "add $4, %0 \n" + "jnl 2f \n" + "movd (%2,%0), %%mm0 \n" "movd (%3,%0), %%mm1 \n" "punpcklbw %%mm7, %%mm0 \n" @@ -70,10 +95,12 @@ static void gradfun_filter_line_mmxext(uint8_t *dst, uint8_t *src, uint16_t *dc, "movd %%mm0, (%1,%0) \n" // dst = clip(pix>>7) "add $4, %0 \n" "jl 1b \n" + + "2: \n" "emms \n" :"+r"(x) :"r"(dst+width), "r"(src+width), "r"(dc+width/2), - "rm"(thresh), "m"(*dithers), "m"(*pw_7f) + "rm"(thresh), "r"(dithers), "m"(*pw_7f) :"memory" ); } From 1ae44c87c924b69a0657256fbaa8ad140df2f27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= Date: Fri, 7 Dec 2012 00:41:04 +0100 Subject: [PATCH 6/6] lavfi/gradfun: remove rounding to match C and SSE code. There is no noticable benefit for such precision. Signed-off-by: Anton Khirnov --- libavfilter/x86/vf_gradfun.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libavfilter/x86/vf_gradfun.c b/libavfilter/x86/vf_gradfun.c index a9e069a5bf..65a8172cbc 100644 --- a/libavfilter/x86/vf_gradfun.c +++ b/libavfilter/x86/vf_gradfun.c @@ -136,9 +136,9 @@ static void gradfun_filter_line_ssse3(uint8_t *dst, uint8_t *src, uint16_t *dc, "psubw %%xmm6, %%xmm2 \n" "pminsw %%xmm7, %%xmm2 \n" // m = -max(0, 127-m) "pmullw %%xmm2, %%xmm2 \n" - "psllw $1, %%xmm2 \n" + "psllw $2, %%xmm1 \n" "paddw %%xmm4, %%xmm0 \n" // pix += dither - "pmulhrsw %%xmm2, %%xmm1 \n" // m = m*m*delta >> 14 + "pmulhw %%xmm2, %%xmm1 \n" // m = m*m*delta >> 14 "paddw %%xmm1, %%xmm0 \n" // pix += m "psraw $7, %%xmm0 \n" "packuswb %%xmm0, %%xmm0 \n"