diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2012-06-24 02:09:53 +0200 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2012-06-24 02:09:53 +0200 |
commit | 1c600888857544986d6576bc164e0dc8f0f4b6c7 (patch) | |
tree | f4e77c53d1050223a9170f75e4ea719f29cd04eb | |
parent | da728d5d2e7993911c7ed92c212ab900b7be180c (diff) | |
parent | fe07c9c6b5a870b8f2ffcfac649228b4d76e9505 (diff) | |
download | ffmpeg-1c600888857544986d6576bc164e0dc8f0f4b6c7.tar.gz |
Merge remote-tracking branch 'qatar/master'
* qatar/master:
x86: Only use optimizations with cmov if the CPU supports the instruction
x86: Add CPU flag for the i686 cmov instruction
x86: remove unused inline asm macros from dsputil_mmx.h
x86: move some inline asm macros to the only places they are used
lavfi: Add the af_channelmap audio channel mapping filter.
lavfi: add join audio filter.
lavfi: allow audio filters to request a given number of samples.
lavfi: support automatically inserting the fifo filter when needed.
lavfi/audio: eliminate ff_default_filter_samples().
Conflicts:
Changelog
libavcodec/x86/h264dsp_mmx.c
libavfilter/Makefile
libavfilter/allfilters.c
libavfilter/avfilter.h
libavfilter/avfiltergraph.c
libavfilter/version.h
libavutil/x86/cpu.c
Merged-by: Michael Niedermayer <michaelni@gmx.at>
44 files changed, 1314 insertions, 279 deletions
@@ -12,6 +12,8 @@ version next: - RTMPT protocol support - iLBC encoding/decoding via libilbc - Microsoft Screen 1 decoder +- join audio filter +- audio channel mapping filter - showwaves filter - LucasArts SMUSH playback support diff --git a/doc/APIchanges b/doc/APIchanges index 0b5ccc9216..2703610639 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -4,7 +4,7 @@ since the last major version increase. The last version increases were: libavcodec: 2012-01-27 libavdevice: 2011-04-18 -libavfilter: 2011-04-18 +libavfilter: 2012-06-22 libavformat: 2012-01-27 libavresample: 2012-04-24 libpostproc: 2011-04-18 diff --git a/doc/filters.texi b/doc/filters.texi index bf7b12b610..0a5bea6299 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -649,6 +649,76 @@ front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]' side_right.wav @end example +@section channelmap +Remap input channels to new locations. + +This filter accepts the following named parameters: +@table @option +@item channel_layout +Channel layout of the output stream. + +@item map +Map channels from input to output. The argument is a comma-separated list of +mappings, each in the @code{@var{in_channel}-@var{out_channel}} or +@var{in_channel} form. @var{in_channel} can be either the name of the input +channel (e.g. FL for front left) or its index in the input channel layout. +@var{out_channel} is the name of the output channel or its index in the output +channel layout. If @var{out_channel} is not given then it is implicitly an +index, starting with zero and increasing by one for each mapping. +@end table + +If no mapping is present, the filter will implicitly map input channels to +output channels preserving index. + +For example, assuming a 5.1+downmix input MOV file +@example +ffmpeg -i in.mov -filter 'channelmap=map=DL-FL\,DR-FR' out.wav +@end example +will create an output WAV file tagged as stereo from the downmix channels of +the input. + +To fix a 5.1 WAV improperly encoded in AAC's native channel order +@example +ffmpeg -i in.wav -filter 'channelmap=1\,2\,0\,5\,3\,4:channel_layout=5.1' out.wav +@end example + +@section join +Join multiple input streams into one multi-channel stream. + +The filter accepts the following named parameters: +@table @option + +@item inputs +Number of input streams. Defaults to 2. + +@item channel_layout +Desired output channel layout. Defaults to stereo. + +@item map +Map channels from inputs to output. The argument is a comma-separated list of +mappings, each in the @code{@var{input_idx}.@var{in_channel}-@var{out_channel}} +form. @var{input_idx} is the 0-based index of the input stream. @var{in_channel} +can be either the name of the input channel (e.g. FR for front left) or its +index in the specified input stream. @var{out_channel} is the name of the output +channel. +@end table + +The filter will attempt to guess the mappings when those are not specified +explicitly. It does so by first trying to find an unused matching input channel +and if that fails it picks the first unused input channel. + +E.g. to join 3 inputs (with properly set channel layouts) +@example +ffmpeg -i INPUT1 -i INPUT2 -i INPUT3 -filter_complex join=inputs=3 OUTPUT +@end example + +To build a 5.1 output from 6 single-channel streams: +@example +ffmpeg -i fl -i fr -i fc -i sl -i sr -i lfe -filter_complex +'join=inputs=6:channel_layout=5.1:map=0.0-FL\,1.0-FR\,2.0-FC\,3.0-SL\,4.0-SR\,5.0-LFE' +out +@end example + @section resample Convert the audio sample format, sample rate and channel layout. This filter is not meant to be used directly. diff --git a/libavcodec/x86/cavsdsp_mmx.c b/libavcodec/x86/cavsdsp_mmx.c index 0f5fdaa53d..37b34a82d5 100644 --- a/libavcodec/x86/cavsdsp_mmx.c +++ b/libavcodec/x86/cavsdsp_mmx.c @@ -29,6 +29,12 @@ #include "libavcodec/cavsdsp.h" #include "dsputil_mmx.h" +/* in/out: mma=mma+mmb, mmb=mmb-mma */ +#define SUMSUB_BA( a, b ) \ + "paddw "#b", "#a" \n\t"\ + "paddw "#b", "#b" \n\t"\ + "psubw "#a", "#b" \n\t" + /***************************************************************************** * * inverse transform diff --git a/libavcodec/x86/dsputil_mmx.c b/libavcodec/x86/dsputil_mmx.c index 31277d547b..e829bbd8b6 100644 --- a/libavcodec/x86/dsputil_mmx.c +++ b/libavcodec/x86/dsputil_mmx.c @@ -631,6 +631,34 @@ static void add_hfyu_median_prediction_cmov(uint8_t *dst, const uint8_t *top, } #endif +static inline void transpose4x4(uint8_t *dst, uint8_t *src, x86_reg dst_stride, x86_reg src_stride){ + __asm__ volatile( //FIXME could save 1 instruction if done as 8x4 ... + "movd (%1), %%mm0 \n\t" + "add %3, %1 \n\t" + "movd (%1), %%mm1 \n\t" + "movd (%1,%3,1), %%mm2 \n\t" + "movd (%1,%3,2), %%mm3 \n\t" + "punpcklbw %%mm1, %%mm0 \n\t" + "punpcklbw %%mm3, %%mm2 \n\t" + "movq %%mm0, %%mm1 \n\t" + "punpcklwd %%mm2, %%mm0 \n\t" + "punpckhwd %%mm2, %%mm1 \n\t" + "movd %%mm0, (%0) \n\t" + "add %2, %0 \n\t" + "punpckhdq %%mm0, %%mm0 \n\t" + "movd %%mm0, (%0) \n\t" + "movd %%mm1, (%0,%2,1) \n\t" + "punpckhdq %%mm1, %%mm1 \n\t" + "movd %%mm1, (%0,%2,2) \n\t" + + : "+&r" (dst), + "+&r" (src) + : "r" (dst_stride), + "r" (src_stride) + : "memory" + ); +} + #define H263_LOOP_FILTER \ "pxor %%mm7, %%mm7 \n\t" \ "movq %0, %%mm0 \n\t" \ @@ -2902,7 +2930,8 @@ static void dsputil_init_3dnow(DSPContext *c, AVCodecContext *avctx, c->vorbis_inverse_coupling = vorbis_inverse_coupling_3dnow; #if HAVE_7REGS - c->add_hfyu_median_prediction = add_hfyu_median_prediction_cmov; + if (mm_flags & AV_CPU_FLAG_CMOV) + c->add_hfyu_median_prediction = add_hfyu_median_prediction_cmov; #endif } diff --git a/libavcodec/x86/dsputil_mmx.h b/libavcodec/x86/dsputil_mmx.h index 91940f6ee8..d0b0344917 100644 --- a/libavcodec/x86/dsputil_mmx.h +++ b/libavcodec/x86/dsputil_mmx.h @@ -66,24 +66,6 @@ extern const xmm_reg ff_pb_FE; extern const double ff_pd_1[2]; extern const double ff_pd_2[2]; -#define LOAD4(stride,in,a,b,c,d)\ - "movq 0*"#stride"+"#in", "#a"\n\t"\ - "movq 1*"#stride"+"#in", "#b"\n\t"\ - "movq 2*"#stride"+"#in", "#c"\n\t"\ - "movq 3*"#stride"+"#in", "#d"\n\t" - -#define STORE4(stride,out,a,b,c,d)\ - "movq "#a", 0*"#stride"+"#out"\n\t"\ - "movq "#b", 1*"#stride"+"#out"\n\t"\ - "movq "#c", 2*"#stride"+"#out"\n\t"\ - "movq "#d", 3*"#stride"+"#out"\n\t" - -/* in/out: mma=mma+mmb, mmb=mmb-mma */ -#define SUMSUB_BA( a, b ) \ - "paddw "#b", "#a" \n\t"\ - "paddw "#b", "#b" \n\t"\ - "psubw "#a", "#b" \n\t" - #define SBUTTERFLY(a,b,t,n,m)\ "mov" #m " " #a ", " #t " \n\t" /* abcd */\ "punpckl" #n " " #b ", " #a " \n\t" /* aebf */\ @@ -95,90 +77,6 @@ extern const double ff_pd_2[2]; SBUTTERFLY(a,c,d,dq,q) /* a=aeim d=bfjn */\ SBUTTERFLY(t,b,c,dq,q) /* t=cgko c=dhlp */ -static inline void transpose4x4(uint8_t *dst, uint8_t *src, x86_reg dst_stride, x86_reg src_stride){ - __asm__ volatile( //FIXME could save 1 instruction if done as 8x4 ... - "movd (%1), %%mm0 \n\t" - "add %3, %1 \n\t" - "movd (%1), %%mm1 \n\t" - "movd (%1,%3,1), %%mm2 \n\t" - "movd (%1,%3,2), %%mm3 \n\t" - "punpcklbw %%mm1, %%mm0 \n\t" - "punpcklbw %%mm3, %%mm2 \n\t" - "movq %%mm0, %%mm1 \n\t" - "punpcklwd %%mm2, %%mm0 \n\t" - "punpckhwd %%mm2, %%mm1 \n\t" - "movd %%mm0, (%0) \n\t" - "add %2, %0 \n\t" - "punpckhdq %%mm0, %%mm0 \n\t" - "movd %%mm0, (%0) \n\t" - "movd %%mm1, (%0,%2,1) \n\t" - "punpckhdq %%mm1, %%mm1 \n\t" - "movd %%mm1, (%0,%2,2) \n\t" - - : "+&r" (dst), - "+&r" (src) - : "r" (dst_stride), - "r" (src_stride) - : "memory" - ); -} - -// e,f,g,h can be memory -// out: a,d,t,c -#define TRANSPOSE8x4(a,b,c,d,e,f,g,h,t)\ - "punpcklbw " #e ", " #a " \n\t" /* a0 e0 a1 e1 a2 e2 a3 e3 */\ - "punpcklbw " #f ", " #b " \n\t" /* b0 f0 b1 f1 b2 f2 b3 f3 */\ - "punpcklbw " #g ", " #c " \n\t" /* c0 g0 c1 g1 c2 g2 d3 g3 */\ - "punpcklbw " #h ", " #d " \n\t" /* d0 h0 d1 h1 d2 h2 d3 h3 */\ - SBUTTERFLY(a, b, t, bw, q) /* a= a0 b0 e0 f0 a1 b1 e1 f1 */\ - /* t= a2 b2 e2 f2 a3 b3 e3 f3 */\ - SBUTTERFLY(c, d, b, bw, q) /* c= c0 d0 g0 h0 c1 d1 g1 h1 */\ - /* b= c2 d2 g2 h2 c3 d3 g3 h3 */\ - SBUTTERFLY(a, c, d, wd, q) /* a= a0 b0 c0 d0 e0 f0 g0 h0 */\ - /* d= a1 b1 c1 d1 e1 f1 g1 h1 */\ - SBUTTERFLY(t, b, c, wd, q) /* t= a2 b2 c2 d2 e2 f2 g2 h2 */\ - /* c= a3 b3 c3 d3 e3 f3 g3 h3 */ - -#if ARCH_X86_64 -// permutes 01234567 -> 05736421 -#define TRANSPOSE8(a,b,c,d,e,f,g,h,t)\ - SBUTTERFLY(a,b,%%xmm8,wd,dqa)\ - SBUTTERFLY(c,d,b,wd,dqa)\ - SBUTTERFLY(e,f,d,wd,dqa)\ - SBUTTERFLY(g,h,f,wd,dqa)\ - SBUTTERFLY(a,c,h,dq,dqa)\ - SBUTTERFLY(%%xmm8,b,c,dq,dqa)\ - SBUTTERFLY(e,g,b,dq,dqa)\ - SBUTTERFLY(d,f,g,dq,dqa)\ - SBUTTERFLY(a,e,f,qdq,dqa)\ - SBUTTERFLY(%%xmm8,d,e,qdq,dqa)\ - SBUTTERFLY(h,b,d,qdq,dqa)\ - SBUTTERFLY(c,g,b,qdq,dqa)\ - "movdqa %%xmm8, "#g" \n\t" -#else -#define TRANSPOSE8(a,b,c,d,e,f,g,h,t)\ - "movdqa "#h", "#t" \n\t"\ - SBUTTERFLY(a,b,h,wd,dqa)\ - "movdqa "#h", 16"#t" \n\t"\ - "movdqa "#t", "#h" \n\t"\ - SBUTTERFLY(c,d,b,wd,dqa)\ - SBUTTERFLY(e,f,d,wd,dqa)\ - SBUTTERFLY(g,h,f,wd,dqa)\ - SBUTTERFLY(a,c,h,dq,dqa)\ - "movdqa "#h", "#t" \n\t"\ - "movdqa 16"#t", "#h" \n\t"\ - SBUTTERFLY(h,b,c,dq,dqa)\ - SBUTTERFLY(e,g,b,dq,dqa)\ - SBUTTERFLY(d,f,g,dq,dqa)\ - SBUTTERFLY(a,e,f,qdq,dqa)\ - SBUTTERFLY(h,d,e,qdq,dqa)\ - "movdqa "#h", 16"#t" \n\t"\ - "movdqa "#t", "#h" \n\t"\ - SBUTTERFLY(h,b,d,qdq,dqa)\ - SBUTTERFLY(c,g,b,qdq,dqa)\ - "movdqa 16"#t", "#g" \n\t" -#endif - #define MOVQ_WONE(regd) \ __asm__ volatile ( \ "pcmpeqd %%" #regd ", %%" #regd " \n\t" \ diff --git a/libavcodec/x86/h264dsp_mmx.c b/libavcodec/x86/h264dsp_mmx.c index 367995c6cb..be7224aed4 100644 --- a/libavcodec/x86/h264dsp_mmx.c +++ b/libavcodec/x86/h264dsp_mmx.c @@ -362,7 +362,7 @@ void ff_h264dsp_init_x86(H264DSPContext *c, const int bit_depth, const int chrom c->h264_idct_add8 = ff_h264_idct_add8_8_mmx; c->h264_idct_add16intra = ff_h264_idct_add16intra_8_mmx; if (mm_flags & AV_CPU_FLAG_CMOV) - c->h264_luma_dc_dequant_idct= ff_h264_luma_dc_dequant_idct_mmx; + c->h264_luma_dc_dequant_idct = ff_h264_luma_dc_dequant_idct_mmx; if (mm_flags & AV_CPU_FLAG_MMX2) { c->h264_idct_dc_add = ff_h264_idct_dc_add_8_mmx2; diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 088ab8f6a4..5d2c81babf 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -59,8 +59,10 @@ OBJS-$(CONFIG_ASPLIT_FILTER) += split.o OBJS-$(CONFIG_ASTREAMSYNC_FILTER) += af_astreamsync.o OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o OBJS-$(CONFIG_ATEMPO_FILTER) += af_atempo.o +OBJS-$(CONFIG_CHANNELMAP_FILTER) += af_channelmap.o OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o +OBJS-$(CONFIG_JOIN_FILTER) += af_join.o OBJS-$(CONFIG_PAN_FILTER) += af_pan.o OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o OBJS-$(CONFIG_SILENCEDETECT_FILTER) += af_silencedetect.o diff --git a/libavfilter/af_aconvert.c b/libavfilter/af_aconvert.c index 3cf593b723..53d6b87cdd 100644 --- a/libavfilter/af_aconvert.c +++ b/libavfilter/af_aconvert.c @@ -75,14 +75,14 @@ static int query_formats(AVFilterContext *ctx) AVFilterLink *outlink = ctx->outputs[0]; AVFilterChannelLayouts *layouts; - ff_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), + ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO), &inlink->out_formats); if (aconvert->out_sample_fmt != AV_SAMPLE_FMT_NONE) { formats = NULL; ff_add_format(&formats, aconvert->out_sample_fmt); ff_formats_ref(formats, &outlink->in_formats); } else - ff_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), + ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO), &outlink->in_formats); ff_channel_layouts_ref(ff_all_channel_layouts(), diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c index 68a38f2189..b8df111063 100644 --- a/libavfilter/af_aformat.c +++ b/libavfilter/af_aformat.c @@ -134,8 +134,7 @@ AVFilter avfilter_af_aformat = { .priv_size = sizeof(AFormatContext), .inputs = (AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_samples = ff_null_filter_samples }, + .type = AVMEDIA_TYPE_AUDIO, }, { .name = NULL}}, .outputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO}, diff --git a/libavfilter/af_amerge.c b/libavfilter/af_amerge.c index e71c55f8bd..f90412c16b 100644 --- a/libavfilter/af_amerge.c +++ b/libavfilter/af_amerge.c @@ -169,7 +169,7 @@ static int request_frame(AVFilterLink *outlink) for (i = 0; i < am->nb_inputs; i++) if (!am->in[i].nb_samples) - if ((ret = avfilter_request_frame(ctx->inputs[i])) < 0) + if ((ret = ff_request_frame(ctx->inputs[i])) < 0) return ret; return 0; } diff --git a/libavfilter/af_anull.c b/libavfilter/af_anull.c index 81d1bf8a25..f52db1e56d 100644 --- a/libavfilter/af_anull.c +++ b/libavfilter/af_anull.c @@ -34,8 +34,7 @@ AVFilter avfilter_af_anull = { .inputs = (const AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .get_audio_buffer = ff_null_get_audio_buffer, - .filter_samples = ff_null_filter_samples }, + .get_audio_buffer = ff_null_get_audio_buffer, }, { .name = NULL}}, .outputs = (const AVFilterPad[]) {{ .name = "default", diff --git a/libavfilter/af_aresample.c b/libavfilter/af_aresample.c index 81f326ff47..fdbc828b6b 100644 --- a/libavfilter/af_aresample.c +++ b/libavfilter/af_aresample.c @@ -112,7 +112,7 @@ static int query_formats(AVFilterContext *ctx) if(out_format != AV_SAMPLE_FMT_NONE) { out_formats = ff_make_format_list((int[]){ out_format, -1 }); } else - out_formats = avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO); + out_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO); ff_formats_ref(out_formats, &outlink->in_formats); if(out_layout) { @@ -211,7 +211,7 @@ static int request_frame(AVFilterLink *outlink) aresample->req_fullfilled = 0; do{ - ret = avfilter_request_frame(ctx->inputs[0]); + ret = ff_request_frame(ctx->inputs[0]); }while(!aresample->req_fullfilled && ret>=0); if (ret == AVERROR_EOF) { diff --git a/libavfilter/af_asetnsamples.c b/libavfilter/af_asetnsamples.c index b373fc254e..d371c98340 100644 --- a/libavfilter/af_asetnsamples.c +++ b/libavfilter/af_asetnsamples.c @@ -164,7 +164,7 @@ static int request_frame(AVFilterLink *outlink) asns->req_fullfilled = 0; do { - ret = avfilter_request_frame(inlink); + ret = ff_request_frame(inlink); } while (!asns->req_fullfilled && ret >= 0); if (ret == AVERROR_EOF) diff --git a/libavfilter/af_astreamsync.c b/libavfilter/af_astreamsync.c index 18a2fe6217..ff3f3c2ce0 100644 --- a/libavfilter/af_astreamsync.c +++ b/libavfilter/af_astreamsync.c @@ -157,7 +157,7 @@ static int request_frame(AVFilterLink *outlink) send_next(ctx); } else { as->eof |= 1 << as->next_out; - avfilter_request_frame(ctx->inputs[as->next_out]); + ff_request_frame(ctx->inputs[as->next_out]); if (as->eof & (1 << as->next_out)) as->next_out = !as->next_out; } diff --git a/libavfilter/af_atempo.c b/libavfilter/af_atempo.c index 92390cf217..445843968a 100644 --- a/libavfilter/af_atempo.c +++ b/libavfilter/af_atempo.c @@ -1083,7 +1083,7 @@ static int request_frame(AVFilterLink *outlink) atempo->request_fulfilled = 0; do { - ret = avfilter_request_frame(ctx->inputs[0]); + ret = ff_request_frame(ctx->inputs[0]); } while (!atempo->request_fulfilled && ret >= 0); diff --git a/libavfilter/af_channelmap.c b/libavfilter/af_channelmap.c new file mode 100644 index 0000000000..fe766fdf11 --- /dev/null +++ b/libavfilter/af_channelmap.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2012 Google, Inc. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * audio channel mapping filter + */ + +#include <ctype.h> + +#include "libavutil/audioconvert.h" +#include "libavutil/avstring.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +struct ChannelMap { + uint64_t in_channel; + uint64_t out_channel; + int in_channel_idx; + int out_channel_idx; +}; + +enum MappingMode { + MAP_NONE, + MAP_ONE_INT, + MAP_ONE_STR, + MAP_PAIR_INT_INT, + MAP_PAIR_INT_STR, + MAP_PAIR_STR_INT, + MAP_PAIR_STR_STR +}; + +#define MAX_CH 64 +typedef struct ChannelMapContext { + const AVClass *class; + AVFilterChannelLayouts *channel_layouts; + char *mapping_str; + char *channel_layout_str; + uint64_t output_layout; + struct ChannelMap map[MAX_CH]; + int nch; + enum MappingMode mode; +} ChannelMapContext; + +#define OFFSET(x) offsetof(ChannelMapContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption options[] = { + { "map", "A comma-separated list of input channel numbers in output order.", + OFFSET(mapping_str), AV_OPT_TYPE_STRING, .flags = A }, + { "channel_layout", "Output channel layout.", + OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A }, + { NULL }, +}; + +static const AVClass channelmap_class = { + .class_name = "channel map filter", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static char* split(char *message, char delim) { + char *next = strchr(message, delim); + if (next) + *next++ = '\0'; + return next; +} + +static int get_channel_idx(char **map, int *ch, char delim, int max_ch) +{ + char *next = split(*map, delim); + int len; + int n = 0; + if (!next && delim == '-') + return AVERROR(EINVAL); + len = strlen(*map); + sscanf(*map, "%d%n", ch, &n); + if (n != len) + return AVERROR(EINVAL); + if (*ch < 0 || *ch > max_ch) + return AVERROR(EINVAL); + *map = next; + return 0; +} + +static int get_channel(char **map, uint64_t *ch, char delim) +{ + char *next = split(*map, delim); + if (!next && delim == '-') + return AVERROR(EINVAL); + *ch = av_get_channel_layout(*map); + if (av_get_channel_layout_nb_channels(*ch) != 1) + return AVERROR(EINVAL); + *map = next; + return 0; +} + +static av_cold int channelmap_init(AVFilterContext *ctx, const char *args, + void *opaque) +{ + ChannelMapContext *s = ctx->priv; + int ret; + char *mapping; + enum mode; + int map_entries = 0; + char buf[256]; + enum MappingMode mode; + uint64_t out_ch_mask = 0; + int i; + + if (!args) { + av_log(ctx, AV_LOG_ERROR, "No parameters supplied.\n"); + return AVERROR(EINVAL); + } + + s->class = &channelmap_class; + av_opt_set_defaults(s); + + if ((ret = av_set_options_string(s, args, "=", ":")) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args); + return ret; + } + + mapping = s->mapping_str; + + if (!mapping) { + mode = MAP_NONE; + } else { + char *dash = strchr(mapping, '-'); + if (!dash) { // short mapping + if (isdigit(*mapping)) + mode = MAP_ONE_INT; + else + mode = MAP_ONE_STR; + } else if (isdigit(*mapping)) { + if (isdigit(*(dash+1))) + mode = MAP_PAIR_INT_INT; + else + mode = MAP_PAIR_INT_STR; + } else { + if (isdigit(*(dash+1))) + mode = MAP_PAIR_STR_INT; + else + mode = MAP_PAIR_STR_STR; + } + } + + if (mode != MAP_NONE) { + char *comma = mapping; + map_entries = 1; + while ((comma = strchr(comma, ','))) { + if (*++comma) // Allow trailing comma + map_entries++; + } + } + + if (map_entries > MAX_CH) { + av_log(ctx, AV_LOG_ERROR, "Too many channels mapped: '%d'.\n", map_entries); + ret = AVERROR(EINVAL); + goto fail; + } + + for (i = 0; i < map_entries; i++) { + int in_ch_idx = -1, out_ch_idx = -1; + uint64_t in_ch = 0, out_ch = 0; + static const char err[] = "Failed to parse channel map\n"; + switch (mode) { + case MAP_ONE_INT: + if (get_channel_idx(&mapping, &in_ch_idx, ',', MAX_CH) < 0) { + ret = AVERROR(EINVAL); + av_log(ctx, AV_LOG_ERROR, err); + goto fail; + } + s->map[i].in_channel_idx = in_ch_idx; + s->map[i].out_channel_idx = i; + break; + case MAP_ONE_STR: + if (!get_channel(&mapping, &in_ch, ',')) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel = in_ch; + s->map[i].out_channel_idx = i; + break; + case MAP_PAIR_INT_INT: + if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 || + get_channel_idx(&mapping, &out_ch_idx, ',', MAX_CH) < 0) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel_idx = in_ch_idx; + s->map[i].out_channel_idx = out_ch_idx; + break; + case MAP_PAIR_INT_STR: + if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 || + get_channel(&mapping, &out_ch, ',') < 0 || + out_ch & out_ch_mask) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel_idx = in_ch_idx; + s->map[i].out_channel = out_ch; + out_ch_mask |= out_ch; + break; + case MAP_PAIR_STR_INT: + if (get_channel(&mapping, &in_ch, '-') < 0 || + get_channel_idx(&mapping, &out_ch_idx, ',', MAX_CH) < 0) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel = in_ch; + s->map[i].out_channel_idx = out_ch_idx; + break; + case MAP_PAIR_STR_STR: + if (get_channel(&mapping, &in_ch, '-') < 0 || + get_channel(&mapping, &out_ch, ',') < 0 || + out_ch & out_ch_mask) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel = in_ch; + s->map[i].out_channel = out_ch; + out_ch_mask |= out_ch; + break; + } + } + s->mode = mode; + s->nch = map_entries; + s->output_layout = out_ch_mask ? out_ch_mask : + av_get_default_channel_layout(map_entries); + + if (s->channel_layout_str) { + uint64_t fmt; + if ((fmt = av_get_channel_layout(s->channel_layout_str)) == 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: '%s'.\n", + s->channel_layout_str); + ret = AVERROR(EINVAL); + goto fail; + } + if (mode == MAP_NONE) { + int i; + s->nch = av_get_channel_layout_nb_channels(fmt); + for (i = 0; i < s->nch; i++) { + s->map[i].in_channel_idx = i; + s->map[i].out_channel_idx = i; + } + } else if (out_ch_mask && out_ch_mask != fmt) { + av_get_channel_layout_string(buf, sizeof(buf), 0, out_ch_mask); + av_log(ctx, AV_LOG_ERROR, + "Output channel layout '%s' does not match the list of channel mapped: '%s'.\n", + s->channel_layout_str, buf); + ret = AVERROR(EINVAL); + goto fail; + } else if (s->nch != av_get_channel_layout_nb_channels(fmt)) { + av_log(ctx, AV_LOG_ERROR, + "Output channel layout %s does not match the number of channels mapped %d.\n", + s->channel_layout_str, s->nch); + ret = AVERROR(EINVAL); + goto fail; + } + s->output_layout = fmt; + } + ff_add_channel_layout(&s->channel_layouts, s->output_layout); + + if (mode == MAP_PAIR_INT_STR || mode == MAP_PAIR_STR_STR) { + for (i = 0; i < s->nch; i++) { + s->map[i].out_channel_idx = av_get_channel_layout_channel_index( + s->output_layout, s->map[i].out_channel); + } + } + +fail: + av_opt_free(s); + return ret; +} + +static int channelmap_query_formats(AVFilterContext *ctx) +{ + ChannelMapContext *s = ctx->priv; + + ff_set_common_formats(ctx, ff_planar_sample_fmts()); + ff_set_common_samplerates(ctx, ff_all_samplerates()); + ff_channel_layouts_ref(ff_all_channel_layouts(), &ctx->inputs[0]->out_channel_layouts); + ff_channel_layouts_ref(s->channel_layouts, &ctx->outputs[0]->in_channel_layouts); + + return 0; +} + +static void channelmap_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + const ChannelMapContext *s = ctx->priv; + const int nch_in = av_get_channel_layout_nb_channels(inlink->channel_layout); + const int nch_out = s->nch; + int ch; + uint8_t *source_planes[MAX_CH]; + + memcpy(source_planes, buf->extended_data, + nch_in * sizeof(source_planes[0])); + + if (nch_out > nch_in) { + if (nch_out > FF_ARRAY_ELEMS(buf->data)) { + uint8_t **new_extended_data = + av_mallocz(nch_out * sizeof(*buf->extended_data)); + if (!new_extended_data) + return; + if (buf->extended_data == buf->data) { + buf->extended_data = new_extended_data; + } else { + buf->extended_data = new_extended_data; + av_free(buf->extended_data); + } + } else if (buf->extended_data != buf->data) { + av_free(buf->extended_data); + buf->extended_data = buf->data; + } + } + + for (ch = 0; ch < nch_out; ch++) { + buf->extended_data[s->map[ch].out_channel_idx] = + source_planes[s->map[ch].in_channel_idx]; + } + + if (buf->data != buf->extended_data) + memcpy(buf->data, buf->extended_data, + FFMIN(FF_ARRAY_ELEMS(buf->data), nch_out) * sizeof(buf->data[0])); + + ff_filter_samples(outlink, buf); +} + +static int channelmap_config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + ChannelMapContext *s = ctx->priv; + int i, err = 0; + const char *channel_name; + char layout_name[256]; + + if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR) { + for (i = 0; i < s->nch; i++) { + s->map[i].in_channel_idx = av_get_channel_layout_channel_index( + inlink->channel_layout, s->map[i].in_channel); + if (s->map[i].in_channel_idx < 0) { + channel_name = av_get_channel_name(s->map[i].in_channel); + av_get_channel_layout_string(layout_name, sizeof(layout_name), + 0, inlink->channel_layout); + av_log(ctx, AV_LOG_ERROR, + "input channel '%s' not available from input layout '%s'\n", + channel_name, layout_name); + err = AVERROR(EINVAL); + } + } + } + + return err; +} + +AVFilter avfilter_af_channelmap = { + .name = "channelmap", + .description = NULL_IF_CONFIG_SMALL("Remap audio channels."), + .init = channelmap_init, + .query_formats = channelmap_query_formats, + .priv_size = sizeof(ChannelMapContext), + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_samples = channelmap_filter_samples, + .config_props = channelmap_config_input }, + { .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO }, + { .name = NULL }}, +}; diff --git a/libavfilter/af_join.c b/libavfilter/af_join.c new file mode 100644 index 0000000000..ab5e9055b3 --- /dev/null +++ b/libavfilter/af_join.c @@ -0,0 +1,500 @@ +/* + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Audio join filter + * + * Join multiple audio inputs as different channels in + * a single output + */ + +#include "libavutil/audioconvert.h" +#include "libavutil/avassert.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +typedef struct ChannelMap { + int input; ///< input stream index + int in_channel_idx; ///< index of in_channel in the input stream data + uint64_t in_channel; ///< layout describing the input channel + uint64_t out_channel; ///< layout describing the output channel +} ChannelMap; + +typedef struct JoinContext { + const AVClass *class; + + int inputs; + char *map; + char *channel_layout_str; + uint64_t channel_layout; + + int nb_channels; + ChannelMap *channels; + + /** + * Temporary storage for input frames, until we get one on each input. + */ + AVFilterBufferRef **input_frames; + + /** + * Temporary storage for data pointers, for assembling the output buffer. + */ + uint8_t **data; +} JoinContext; + +/** + * To avoid copying the data from input buffers, this filter creates + * a custom output buffer that stores references to all inputs and + * unrefs them on free. + */ +typedef struct JoinBufferPriv { + AVFilterBufferRef **in_buffers; + int nb_in_buffers; +} JoinBufferPriv; + +#define OFFSET(x) offsetof(JoinContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption join_options[] = { + { "inputs", "Number of input streams.", OFFSET(inputs), AV_OPT_TYPE_INT, { 2 }, 1, INT_MAX, A }, + { "channel_layout", "Channel layout of the " + "output stream.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, A }, + { "map", "A comma-separated list of channels maps in the format " + "'input_stream.input_channel-output_channel.", + OFFSET(map), AV_OPT_TYPE_STRING, .flags = A }, + { NULL }, +}; + +static const AVClass join_class = { + .class_name = "join filter", + .item_name = av_default_item_name, + .option = join_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static void filter_samples(AVFilterLink *link, AVFilterBufferRef *buf) +{ + AVFilterContext *ctx = link->dst; + JoinContext *s = ctx->priv; + int i; + + for (i = 0; i < ctx->nb_inputs; i++) + if (link == ctx->inputs[i]) + break; + av_assert0(i < ctx->nb_inputs); + av_assert0(!s->input_frames[i]); + s->input_frames[i] = buf; +} + +static int parse_maps(AVFilterContext *ctx) +{ + JoinContext *s = ctx->priv; + char *cur = s->map; + + while (cur && *cur) { + char *sep, *next, *p; + uint64_t in_channel = 0, out_channel = 0; + int input_idx, out_ch_idx, in_ch_idx; + + next = strchr(cur, ','); + if (next) + *next++ = 0; + + /* split the map into input and output parts */ + if (!(sep = strchr(cur, '-'))) { + av_log(ctx, AV_LOG_ERROR, "Missing separator '-' in channel " + "map '%s'\n", cur); + return AVERROR(EINVAL); + } + *sep++ = 0; + +#define PARSE_CHANNEL(str, var, inout) \ + if (!(var = av_get_channel_layout(str))) { \ + av_log(ctx, AV_LOG_ERROR, "Invalid " inout " channel: %s.\n", str);\ + return AVERROR(EINVAL); \ + } \ + if (av_get_channel_layout_nb_channels(var) != 1) { \ + av_log(ctx, AV_LOG_ERROR, "Channel map describes more than one " \ + inout " channel.\n"); \ + return AVERROR(EINVAL); \ + } + + /* parse output channel */ + PARSE_CHANNEL(sep, out_channel, "output"); + if (!(out_channel & s->channel_layout)) { + av_log(ctx, AV_LOG_ERROR, "Output channel '%s' is not present in " + "requested channel layout.\n", sep); + return AVERROR(EINVAL); + } + + out_ch_idx = av_get_channel_layout_channel_index(s->channel_layout, + out_channel); + if (s->channels[out_ch_idx].input >= 0) { + av_log(ctx, AV_LOG_ERROR, "Multiple maps for output channel " + "'%s'.\n", sep); + return AVERROR(EINVAL); + } + + /* parse input channel */ + input_idx = strtol(cur, &cur, 0); + if (input_idx < 0 || input_idx >= s->inputs) { + av_log(ctx, AV_LOG_ERROR, "Invalid input stream index: %d.\n", + input_idx); + return AVERROR(EINVAL); + } + + if (*cur) + cur++; + + in_ch_idx = strtol(cur, &p, 0); + if (p == cur) { + /* channel specifier is not a number, + * try to parse as channel name */ + PARSE_CHANNEL(cur, in_channel, "input"); + } + + s->channels[out_ch_idx].input = input_idx; + if (in_channel) + s->channels[out_ch_idx].in_channel = in_channel; + else + s->channels[out_ch_idx].in_channel_idx = in_ch_idx; + + cur = next; + } + return 0; +} + +static int join_init(AVFilterContext *ctx, const char *args, void *opaque) +{ + JoinContext *s = ctx->priv; + int ret, i; + + s->class = &join_class; + av_opt_set_defaults(s); + if ((ret = av_set_options_string(s, args, "=", ":")) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args); + return ret; + } + + if (!(s->channel_layout = av_get_channel_layout(s->channel_layout_str))) { + av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n", + s->channel_layout_str); + ret = AVERROR(EINVAL); + goto fail; + } + + s->nb_channels = av_get_channel_layout_nb_channels(s->channel_layout); + s->channels = av_mallocz(sizeof(*s->channels) * s->nb_channels); + s->data = av_mallocz(sizeof(*s->data) * s->nb_channels); + s->input_frames = av_mallocz(sizeof(*s->input_frames) * s->inputs); + if (!s->channels || !s->data || !s->input_frames) { + ret = AVERROR(ENOMEM); + goto fail; + } + + for (i = 0; i < s->nb_channels; i++) { + s->channels[i].out_channel = av_channel_layout_extract_channel(s->channel_layout, i); + s->channels[i].input = -1; + } + + if ((ret = parse_maps(ctx)) < 0) + goto fail; + + for (i = 0; i < s->inputs; i++) { + char name[32]; + AVFilterPad pad = { 0 }; + + snprintf(name, sizeof(name), "input%d", i); + pad.type = AVMEDIA_TYPE_AUDIO; + pad.name = av_strdup(name); + pad.filter_samples = filter_samples; + + pad.needs_fifo = 1; + + ff_insert_inpad(ctx, i, &pad); + } + +fail: + av_opt_free(s); + return ret; +} + +static void join_uninit(AVFilterContext *ctx) +{ + JoinContext *s = ctx->priv; + int i; + + for (i = 0; i < ctx->nb_inputs; i++) { + av_freep(&ctx->input_pads[i].name); + avfilter_unref_buffer(s->input_frames[i]); + } + + av_freep(&s->channels); + av_freep(&s->data); + av_freep(&s->input_frames); +} + +static int join_query_formats(AVFilterContext *ctx) +{ + JoinContext *s = ctx->priv; + AVFilterChannelLayouts *layouts = NULL; + int i; + + ff_add_channel_layout(&layouts, s->channel_layout); + ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts); + + for (i = 0; i < ctx->nb_inputs; i++) + ff_channel_layouts_ref(ff_all_channel_layouts(), + &ctx->inputs[i]->out_channel_layouts); + + ff_set_common_formats (ctx, ff_planar_sample_fmts()); + ff_set_common_samplerates(ctx, ff_all_samplerates()); + + return 0; +} + +static void guess_map_matching(AVFilterContext *ctx, ChannelMap *ch, + uint64_t *inputs) +{ + int i; + + for (i = 0; i < ctx->nb_inputs; i++) { + AVFilterLink *link = ctx->inputs[i]; + + if (ch->out_channel & link->channel_layout && + !(ch->out_channel & inputs[i])) { + ch->input = i; + ch->in_channel = ch->out_channel; + inputs[i] |= ch->out_channel; + return; + } + } +} + +static void guess_map_any(AVFilterContext *ctx, ChannelMap *ch, + uint64_t *inputs) +{ + int i; + + for (i = 0; i < ctx->nb_inputs; i++) { + AVFilterLink *link = ctx->inputs[i]; + + if ((inputs[i] & link->channel_layout) != link->channel_layout) { + uint64_t unused = link->channel_layout & ~inputs[i]; + + ch->input = i; + ch->in_channel = av_channel_layout_extract_channel(unused, 0); + inputs[i] |= ch->in_channel; + return; + } + } +} + +static int join_config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + JoinContext *s = ctx->priv; + uint64_t *inputs; // nth element tracks which channels are used from nth input + int i, ret = 0; + + /* initialize inputs to user-specified mappings */ + if (!(inputs = av_mallocz(sizeof(*inputs) * ctx->nb_inputs))) + return AVERROR(ENOMEM); + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + AVFilterLink *inlink; + + if (ch->input < 0) + continue; + + inlink = ctx->inputs[ch->input]; + + if (!ch->in_channel) + ch->in_channel = av_channel_layout_extract_channel(inlink->channel_layout, + ch->in_channel_idx); + + if (!(ch->in_channel & inlink->channel_layout)) { + av_log(ctx, AV_LOG_ERROR, "Requested channel %s is not present in " + "input stream #%d.\n", av_get_channel_name(ch->in_channel), + ch->input); + ret = AVERROR(EINVAL); + goto fail; + } + + inputs[ch->input] |= ch->in_channel; + } + + /* guess channel maps when not explicitly defined */ + /* first try unused matching channels */ + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + + if (ch->input < 0) + guess_map_matching(ctx, ch, inputs); + } + + /* if the above failed, try to find _any_ unused input channel */ + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + + if (ch->input < 0) + guess_map_any(ctx, ch, inputs); + + if (ch->input < 0) { + av_log(ctx, AV_LOG_ERROR, "Could not find input channel for " + "output channel '%s'.\n", + av_get_channel_name(ch->out_channel)); + goto fail; + } + + ch->in_channel_idx = av_get_channel_layout_channel_index(ctx->inputs[ch->input]->channel_layout, + ch->in_channel); + } + + /* print mappings */ + av_log(ctx, AV_LOG_VERBOSE, "mappings: "); + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + av_log(ctx, AV_LOG_VERBOSE, "%d.%s => %s ", ch->input, + av_get_channel_name(ch->in_channel), + av_get_channel_name(ch->out_channel)); + } + av_log(ctx, AV_LOG_VERBOSE, "\n"); + + for (i = 0; i < ctx->nb_inputs; i++) { + if (!inputs[i]) + av_log(ctx, AV_LOG_WARNING, "No channels are used from input " + "stream %d.\n", i); + } + +fail: + av_freep(&inputs); + return ret; +} + +static void join_free_buffer(AVFilterBuffer *buf) +{ + JoinBufferPriv *priv = buf->priv; + + if (priv) { + int i; + + for (i = 0; i < priv->nb_in_buffers; i++) + avfilter_unref_buffer(priv->in_buffers[i]); + + av_freep(&priv->in_buffers); + av_freep(&buf->priv); + } + + if (buf->extended_data != buf->data) + av_freep(&buf->extended_data); + av_freep(&buf); +} + +static int join_request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + JoinContext *s = ctx->priv; + AVFilterBufferRef *buf; + JoinBufferPriv *priv; + int linesize = INT_MAX; + int perms = ~0; + int nb_samples; + int i, j, ret; + + /* get a frame on each input */ + for (i = 0; i < ctx->nb_inputs; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + + if (!s->input_frames[i] && + (ret = ff_request_frame(inlink)) < 0) + return ret; + + /* request the same number of samples on all inputs */ + if (i == 0) { + nb_samples = s->input_frames[0]->audio->nb_samples; + + for (j = 1; !i && j < ctx->nb_inputs; j++) + ctx->inputs[j]->request_samples = nb_samples; + } + } + + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + AVFilterBufferRef *cur_buf = s->input_frames[ch->input]; + + s->data[i] = cur_buf->extended_data[ch->in_channel_idx]; + linesize = FFMIN(linesize, cur_buf->linesize[0]); + perms &= cur_buf->perms; + } + + buf = avfilter_get_audio_buffer_ref_from_arrays(s->data, linesize, perms, + nb_samples, outlink->format, + outlink->channel_layout); + if (!buf) + return AVERROR(ENOMEM); + + buf->buf->free = join_free_buffer; + buf->pts = s->input_frames[0]->pts; + + if (!(priv = av_mallocz(sizeof(*priv)))) + goto fail; + if (!(priv->in_buffers = av_mallocz(sizeof(*priv->in_buffers) * ctx->nb_inputs))) + goto fail; + + for (i = 0; i < ctx->nb_inputs; i++) + priv->in_buffers[i] = s->input_frames[i]; + priv->nb_in_buffers = ctx->nb_inputs; + buf->buf->priv = priv; + + ff_filter_samples(outlink, buf); + + memset(s->input_frames, 0, sizeof(*s->input_frames) * ctx->nb_inputs); + + return 0; + +fail: + avfilter_unref_buffer(buf); + if (priv) + av_freep(&priv->in_buffers); + av_freep(&priv); + return AVERROR(ENOMEM); +} + +AVFilter avfilter_af_join = { + .name = "join", + .description = NULL_IF_CONFIG_SMALL("Join multiple audio streams into " + "multi-channel output"), + .priv_size = sizeof(JoinContext), + + .init = join_init, + .uninit = join_uninit, + .query_formats = join_query_formats, + + .inputs = (const AVFilterPad[]){{ NULL }}, + .outputs = (const AVFilterPad[]){{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = join_config_output, + .request_frame = join_request_frame, }, + { NULL }}, +}; diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c index aed59cd284..cda40a7cdf 100644 --- a/libavfilter/af_pan.c +++ b/libavfilter/af_pan.c @@ -217,7 +217,7 @@ static int query_formats(AVFilterContext *ctx) pan->pure_gains = are_gains_pure(pan); /* libswr supports any sample and packing formats */ - ff_set_common_formats(ctx, avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO)); + ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO)); formats = ff_all_samplerates(); if (!formats) diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 3590b815a6..6458fc5fa7 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -47,8 +47,10 @@ void avfilter_register_all(void) REGISTER_FILTER (ASTREAMSYNC, astreamsync, af); REGISTER_FILTER (ASYNCTS, asyncts, af); REGISTER_FILTER (ATEMPO, atempo, af); + REGISTER_FILTER (CHANNELMAP, channelmap, af); REGISTER_FILTER (CHANNELSPLIT,channelsplit,af); REGISTER_FILTER (EARWAX, earwax, af); + REGISTER_FILTER (JOIN, join, af); REGISTER_FILTER (PAN, pan, af); REGISTER_FILTER (SILENCEDETECT, silencedetect, af); REGISTER_FILTER (VOLUME, volume, af); diff --git a/libavfilter/audio.c b/libavfilter/audio.c index d052ad9cf3..6a86597342 100644 --- a/libavfilter/audio.c +++ b/libavfilter/audio.c @@ -150,32 +150,12 @@ fail: return NULL; } -void ff_null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) +static void default_filter_samples(AVFilterLink *link, + AVFilterBufferRef *samplesref) { ff_filter_samples(link->dst->outputs[0], samplesref); } -/* FIXME: samplesref is same as link->cur_buf. Need to consider removing the redundant parameter. */ -void ff_default_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *samplesref) -{ - AVFilterLink *outlink = NULL; - - if (inlink->dst->nb_outputs) - outlink = inlink->dst->outputs[0]; - - if (outlink) { - outlink->out_buf = ff_default_get_audio_buffer(inlink, AV_PERM_WRITE, - samplesref->audio->nb_samples); - outlink->out_buf->pts = samplesref->pts; - outlink->out_buf->audio->sample_rate = samplesref->audio->sample_rate; - ff_filter_samples(outlink, avfilter_ref_buffer(outlink->out_buf, ~0)); - avfilter_unref_buffer(outlink->out_buf); - outlink->out_buf = NULL; - } - avfilter_unref_buffer(samplesref); - inlink->cur_buf = NULL; -} - void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) { void (*filter_samples)(AVFilterLink *, AVFilterBufferRef *); @@ -186,7 +166,7 @@ void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) FF_TPRINTF_START(NULL, filter_samples); ff_tlog_link(NULL, link, 1); if (!(filter_samples = dst->filter_samples)) - filter_samples = ff_default_filter_samples; + filter_samples = default_filter_samples; /* prepare to copy the samples if the buffer has insufficient permissions */ if ((dst->min_perms & samplesref->perms) != dst->min_perms || diff --git a/libavfilter/audio.h b/libavfilter/audio.h index e361edc5f8..d4282b59fe 100644 --- a/libavfilter/audio.h +++ b/libavfilter/audio.h @@ -63,12 +63,6 @@ AVFilterBufferRef *ff_null_get_audio_buffer(AVFilterLink *link, int perms, AVFilterBufferRef *ff_get_audio_buffer(AVFilterLink *link, int perms, int nb_samples); -/** default handler for filter_samples() for audio inputs */ -void ff_default_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref); - -/** filter_samples() handler for filters which simply pass audio along */ -void ff_null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref); - /** * Send a buffer of audio samples to the next filter. * diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c index 121f785c15..f6fa45458e 100644 --- a/libavfilter/avf_showwaves.c +++ b/libavfilter/avf_showwaves.c @@ -170,7 +170,7 @@ static int request_frame(AVFilterLink *outlink) showwaves->req_fullfilled = 0; do { - ret = avfilter_request_frame(inlink); + ret = ff_request_frame(inlink); } while (!showwaves->req_fullfilled && ret >= 0); if (ret == AVERROR_EOF && showwaves->outpicref) diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 3c7af2d83d..d0a259c22e 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -211,7 +211,6 @@ AVFilterBufferRef *avfilter_ref_buffer(AVFilterBufferRef *ref, int pmask); */ void avfilter_unref_buffer(AVFilterBufferRef *ref); -#if FF_API_FILTERS_PUBLIC /** * Remove a reference to a buffer and set the pointer to NULL. * If this is the last reference to the buffer, the buffer itself @@ -221,6 +220,7 @@ void avfilter_unref_buffer(AVFilterBufferRef *ref); */ void avfilter_unref_bufferp(AVFilterBufferRef **ref); +#if FF_API_FILTERS_PUBLIC /** * A list of supported formats for one end of a filter link. This is used * during the format negotiation process to try to pick the best format to @@ -291,7 +291,7 @@ AVFilterFormats *avfilter_make_format_list(const int *fmts); * * @return a non negative value in case of success, or a negative * value corresponding to an AVERROR code in case of error - * @deprecated Use avfilter_make_all_formats() instead. + * @deprecated Use ff_all_formats() instead. */ attribute_deprecated int avfilter_add_format(AVFilterFormats **avff, int64_t fmt); @@ -479,7 +479,7 @@ struct AVFilterPad { * Frame request callback. A call to this should result in at least one * frame being output over the given link. This should return zero on * success, and another value on error. - * See avfilter_request_frame() for the error codes with a specific + * See ff_request_frame() for the error codes with a specific * meaning. * * Output pads only. @@ -504,6 +504,14 @@ struct AVFilterPad { * and another value on error. */ int (*config_props)(AVFilterLink *link); + + /** + * The filter expects a fifo to be inserted on its input link, + * typically because it has a delay. + * + * input pads only. + */ + int needs_fifo; }; #endif @@ -529,6 +537,10 @@ const char *avfilter_pad_get_name(AVFilterPad *pads, int pad_idx); */ enum AVMediaType avfilter_pad_get_type(AVFilterPad *pads, int pad_idx); +/** default handler for end_frame() for video inputs */ +attribute_deprecated +void avfilter_default_end_frame(AVFilterLink *link); + #if FF_API_FILTERS_PUBLIC /** default handler for start_frame() for video inputs */ attribute_deprecated @@ -538,10 +550,6 @@ void avfilter_default_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) attribute_deprecated void avfilter_default_draw_slice(AVFilterLink *link, int y, int h, int slice_dir); -/** default handler for end_frame() for video inputs */ -attribute_deprecated -void avfilter_default_end_frame(AVFilterLink *link); - /** default handler for get_video_buffer() for video inputs */ attribute_deprecated AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, @@ -756,6 +764,15 @@ struct AVFilterLink { struct AVFilterChannelLayouts *in_channel_layouts; struct AVFilterChannelLayouts *out_channel_layouts; + /** + * Audio only, the destination filter sets this to a non-zero value to + * request that buffers with the given number of samples should be sent to + * it. AVFilterPad.needs_fifo must also be set on the corresponding input + * pad. + * Last buffer before EOF will be padded with silence. + */ + int request_samples; + struct AVFilterPool *pool; /** @@ -785,7 +802,6 @@ struct AVFilterLink { * It is similar to the r_frae_rate field in AVStream. */ AVRational frame_rate; - }; /** diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index a4ad4da3ce..8892e9decd 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -187,7 +187,7 @@ static int filter_query_formats(AVFilterContext *ctx) if ((ret = ctx->filter->query_formats(ctx)) < 0) return ret; - formats = avfilter_make_all_formats(type); + formats = ff_all_formats(type); if (!formats) return AVERROR(ENOMEM); ff_set_common_formats(ctx, formats); @@ -815,12 +815,52 @@ static int ff_avfilter_graph_config_pointers(AVFilterGraph *graph, return 0; } +static int graph_insert_fifos(AVFilterGraph *graph, AVClass *log_ctx) +{ + AVFilterContext *f; + int i, j, ret; + int fifo_count = 0; + + for (i = 0; i < graph->filter_count; i++) { + f = graph->filters[i]; + + for (j = 0; j < f->nb_inputs; j++) { + AVFilterLink *link = f->inputs[j]; + AVFilterContext *fifo_ctx; + AVFilter *fifo; + char name[32]; + + if (!link->dstpad->needs_fifo) + continue; + + fifo = f->inputs[j]->type == AVMEDIA_TYPE_VIDEO ? + avfilter_get_by_name("fifo") : + avfilter_get_by_name("afifo"); + + snprintf(name, sizeof(name), "auto-inserted fifo %d", fifo_count++); + + ret = avfilter_graph_create_filter(&fifo_ctx, fifo, name, NULL, + NULL, graph); + if (ret < 0) + return ret; + + ret = avfilter_insert_filter(link, fifo_ctx, 0, 0); + if (ret < 0) + return ret; + } + } + + return 0; +} + int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) { int ret; if ((ret = graph_check_validity(graphctx, log_ctx))) return ret; + if ((ret = graph_insert_fifos(graphctx, log_ctx)) < 0) + return ret; if ((ret = graph_config_formats(graphctx, log_ctx))) return ret; if ((ret = graph_config_links(graphctx, log_ctx))) @@ -939,7 +979,7 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) { while (graph->sink_links_count) { AVFilterLink *oldest = graph->sink_links[0]; - int r = avfilter_request_frame(oldest); + int r = ff_request_frame(oldest); if (r != AVERROR_EOF) return r; /* EOF: remove the link from the heap */ diff --git a/libavfilter/avfiltergraph.h b/libavfilter/avfiltergraph.h index 7e47566129..f983bf8469 100644 --- a/libavfilter/avfiltergraph.h +++ b/libavfilter/avfiltergraph.h @@ -257,7 +257,7 @@ char *avfilter_graph_dump(AVFilterGraph *graph, const char *options); * of a filtergraph, only a convenience function to help drain a filtergraph * in a balanced way under normal circumstances. * - * @return the return value of avfilter_request_frame, + * @return the return value of ff_request_frame, * or AVERROR_EOF of all links returned AVERROR_EOF. */ int avfilter_graph_request_oldest(AVFilterGraph *graph); diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index bd693baca8..642350080b 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -25,7 +25,7 @@ #include "libavutil/audio_fifo.h" #include "libavutil/audioconvert.h" -#include "libavutil/fifo.h" +#include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "audio.h" @@ -34,86 +34,45 @@ #include "internal.h" typedef struct { - AVFifoBuffer *fifo; ///< FIFO buffer of frame references - + AVFilterBufferRef *cur_buf; ///< last buffer delivered on the sink AVAudioFifo *audio_fifo; ///< FIFO for audio samples int64_t next_pts; ///< interpolating audio pts } BufferSinkContext; -#define FIFO_INIT_SIZE 8 - static av_cold void uninit(AVFilterContext *ctx) { BufferSinkContext *sink = ctx->priv; - while (sink->fifo && av_fifo_size(sink->fifo)) { - AVFilterBufferRef *buf; - av_fifo_generic_read(sink->fifo, &buf, sizeof(buf), NULL); - avfilter_unref_buffer(buf); - } - av_fifo_free(sink->fifo); - if (sink->audio_fifo) av_audio_fifo_free(sink->audio_fifo); } -static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) -{ - BufferSinkContext *sink = ctx->priv; - - if (!(sink->fifo = av_fifo_alloc(FIFO_INIT_SIZE*sizeof(AVFilterBufferRef*)))) { - av_log(ctx, AV_LOG_ERROR, "Failed to allocate fifo\n"); - return AVERROR(ENOMEM); - } - - return 0; -} - -static void write_buf(AVFilterContext *ctx, AVFilterBufferRef *buf) +static void start_frame(AVFilterLink *link, AVFilterBufferRef *buf) { - BufferSinkContext *sink = ctx->priv; - - if (av_fifo_space(sink->fifo) < sizeof(AVFilterBufferRef *) && - (av_fifo_realloc2(sink->fifo, av_fifo_size(sink->fifo) * 2) < 0)) { - av_log(ctx, AV_LOG_ERROR, "Error reallocating the FIFO.\n"); - return; - } + BufferSinkContext *s = link->dst->priv; - av_fifo_generic_write(sink->fifo, &buf, sizeof(buf), NULL); -} - -static void end_frame(AVFilterLink *link) -{ - write_buf(link->dst, link->cur_buf); +// av_assert0(!s->cur_buf); + s->cur_buf = buf; link->cur_buf = NULL; -} - -static void filter_samples(AVFilterLink *link, AVFilterBufferRef *buf) -{ - write_buf(link->dst, buf); -} +}; int av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf) { - BufferSinkContext *sink = ctx->priv; + BufferSinkContext *s = ctx->priv; AVFilterLink *link = ctx->inputs[0]; int ret; - if (!buf) { - if (av_fifo_size(sink->fifo)) - return av_fifo_size(sink->fifo)/sizeof(*buf); - else - return ff_poll_frame(ctx->inputs[0]); - } + if (!buf) + return ff_poll_frame(ctx->inputs[0]); - if (!av_fifo_size(sink->fifo) && - (ret = ff_request_frame(link)) < 0) + if ((ret = ff_request_frame(link)) < 0) return ret; - if (!av_fifo_size(sink->fifo)) + if (!s->cur_buf) return AVERROR(EINVAL); - av_fifo_generic_read(sink->fifo, buf, sizeof(*buf), NULL); + *buf = s->cur_buf; + s->cur_buf = NULL; return 0; } @@ -182,13 +141,13 @@ AVFilter avfilter_vsink_buffer = { .name = "buffersink_old", .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), .priv_size = sizeof(BufferSinkContext), - .init = init, .uninit = uninit, .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .end_frame = end_frame, - .min_perms = AV_PERM_READ, }, + .start_frame = start_frame, + .min_perms = AV_PERM_READ, + .needs_fifo = 1 }, { .name = NULL }}, .outputs = (AVFilterPad[]) {{ .name = NULL }}, }; @@ -197,13 +156,13 @@ AVFilter avfilter_asink_abuffer = { .name = "abuffersink_old", .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), .priv_size = sizeof(BufferSinkContext), - .init = init, .uninit = uninit, .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_samples = filter_samples, - .min_perms = AV_PERM_READ, }, + .filter_samples = start_frame, + .min_perms = AV_PERM_READ, + .needs_fifo = 1 }, { .name = NULL }}, .outputs = (AVFilterPad[]) {{ .name = NULL }}, }; diff --git a/libavfilter/defaults.c b/libavfilter/defaults.c index c18eacd23a..aa1231db69 100644 --- a/libavfilter/defaults.c +++ b/libavfilter/defaults.c @@ -47,27 +47,6 @@ static void set_common_formats(AVFilterContext *ctx, AVFilterFormats *fmts, } } -void avfilter_set_common_pixel_formats(AVFilterContext *ctx, AVFilterFormats *formats) -{ - set_common_formats(ctx, formats, AVMEDIA_TYPE_VIDEO, - offsetof(AVFilterLink, in_formats), - offsetof(AVFilterLink, out_formats)); -} - -void avfilter_set_common_sample_formats(AVFilterContext *ctx, AVFilterFormats *formats) -{ - set_common_formats(ctx, formats, AVMEDIA_TYPE_AUDIO, - offsetof(AVFilterLink, in_formats), - offsetof(AVFilterLink, out_formats)); -} - -void avfilter_set_common_channel_layouts(AVFilterContext *ctx, AVFilterFormats *formats) -{ - set_common_formats(ctx, formats, AVMEDIA_TYPE_AUDIO, - offsetof(AVFilterLink, in_channel_layouts), - offsetof(AVFilterLink, out_channel_layouts)); -} - #if FF_API_PACKING void avfilter_set_common_packing_formats(AVFilterContext *ctx, AVFilterFormats *formats) { diff --git a/libavfilter/fifo.c b/libavfilter/fifo.c index da5aa648f6..540dea648a 100644 --- a/libavfilter/fifo.c +++ b/libavfilter/fifo.c @@ -23,6 +23,11 @@ * FIFO buffering filter */ +#include "libavutil/avassert.h" +#include "libavutil/audioconvert.h" +#include "libavutil/mathematics.h" +#include "libavutil/samplefmt.h" + #include "audio.h" #include "avfilter.h" #include "internal.h" @@ -36,6 +41,13 @@ typedef struct Buf { typedef struct { Buf root; Buf *last; ///< last buffered frame + + /** + * When a specific number of output samples is requested, the partial + * buffer is stored here + */ + AVFilterBufferRef *buf_out; + int allocated_samples; ///< number of samples buf_out was allocated for } FifoContext; static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) @@ -57,6 +69,8 @@ static av_cold void uninit(AVFilterContext *ctx) avfilter_unref_buffer(buf->buf); av_free(buf); } + + avfilter_unref_buffer(fifo->buf_out); } static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) @@ -68,14 +82,143 @@ static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) fifo->last->buf = buf; } +static void queue_pop(FifoContext *s) +{ + Buf *tmp = s->root.next->next; + if (s->last == s->root.next) + s->last = &s->root; + av_freep(&s->root.next); + s->root.next = tmp; +} + static void end_frame(AVFilterLink *inlink) { } static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { } +/** + * Move data pointers and pts offset samples forward. + */ +static void buffer_offset(AVFilterLink *link, AVFilterBufferRef *buf, + int offset) +{ + int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); + int planar = av_sample_fmt_is_planar(link->format); + int planes = planar ? nb_channels : 1; + int block_align = av_get_bytes_per_sample(link->format) * (planar ? 1 : nb_channels); + int i; + + av_assert0(buf->audio->nb_samples > offset); + + for (i = 0; i < planes; i++) + buf->extended_data[i] += block_align*offset; + if (buf->data != buf->extended_data) + memcpy(buf->data, buf->extended_data, + FFMIN(planes, FF_ARRAY_ELEMS(buf->data)) * sizeof(*buf->data)); + buf->linesize[0] -= block_align*offset; + buf->audio->nb_samples -= offset; + + if (buf->pts != AV_NOPTS_VALUE) { + buf->pts += av_rescale_q(offset, (AVRational){1, link->sample_rate}, + link->time_base); + } +} + +static int calc_ptr_alignment(AVFilterBufferRef *buf) +{ + int planes = av_sample_fmt_is_planar(buf->format) ? + av_get_channel_layout_nb_channels(buf->audio->channel_layout) : 1; + int min_align = 128; + int p; + + for (p = 0; p < planes; p++) { + int cur_align = 128; + while ((intptr_t)buf->extended_data[p] % cur_align) + cur_align >>= 1; + if (cur_align < min_align) + min_align = cur_align; + } + return min_align; +} + +static int return_audio_frame(AVFilterContext *ctx) +{ + AVFilterLink *link = ctx->outputs[0]; + FifoContext *s = ctx->priv; + AVFilterBufferRef *head = s->root.next->buf; + AVFilterBufferRef *buf_out; + int ret; + + if (!s->buf_out && + head->audio->nb_samples >= link->request_samples && + calc_ptr_alignment(head) >= 32) { + if (head->audio->nb_samples == link->request_samples) { + buf_out = head; + queue_pop(s); + } else { + buf_out = avfilter_ref_buffer(head, AV_PERM_READ); + buf_out->audio->nb_samples = link->request_samples; + buffer_offset(link, head, link->request_samples); + } + } else { + int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); + + if (!s->buf_out) { + s->buf_out = ff_get_audio_buffer(link, AV_PERM_WRITE, + link->request_samples); + if (!s->buf_out) + return AVERROR(ENOMEM); + + s->buf_out->audio->nb_samples = 0; + s->buf_out->pts = head->pts; + s->allocated_samples = link->request_samples; + } else if (link->request_samples != s->allocated_samples) { + av_log(ctx, AV_LOG_ERROR, "request_samples changed before the " + "buffer was returned.\n"); + return AVERROR(EINVAL); + } + + while (s->buf_out->audio->nb_samples < s->allocated_samples) { + int len = FFMIN(s->allocated_samples - s->buf_out->audio->nb_samples, + head->audio->nb_samples); + + av_samples_copy(s->buf_out->extended_data, head->extended_data, + s->buf_out->audio->nb_samples, 0, len, nb_channels, + link->format); + s->buf_out->audio->nb_samples += len; + + if (len == head->audio->nb_samples) { + avfilter_unref_buffer(head); + queue_pop(s); + + if (!s->root.next && + (ret = ff_request_frame(ctx->inputs[0])) < 0) { + if (ret == AVERROR_EOF) { + av_samples_set_silence(s->buf_out->extended_data, + s->buf_out->audio->nb_samples, + s->allocated_samples - + s->buf_out->audio->nb_samples, + nb_channels, link->format); + s->buf_out->audio->nb_samples = s->allocated_samples; + break; + } + return ret; + } + head = s->root.next->buf; + } else { + buffer_offset(link, head, len); + } + } + buf_out = s->buf_out; + s->buf_out = NULL; + } + ff_filter_samples(link, buf_out); + + return 0; +} + static int request_frame(AVFilterLink *outlink) { FifoContext *fifo = outlink->src->priv; - Buf *tmp; int ret; if (!fifo->root.next) { @@ -90,20 +233,20 @@ static int request_frame(AVFilterLink *outlink) ff_start_frame(outlink, fifo->root.next->buf); ff_draw_slice (outlink, 0, outlink->h, 1); ff_end_frame (outlink); + queue_pop(fifo); break; case AVMEDIA_TYPE_AUDIO: - ff_filter_samples(outlink, fifo->root.next->buf); + if (outlink->request_samples) { + return return_audio_frame(outlink->src); + } else { + ff_filter_samples(outlink, fifo->root.next->buf); + queue_pop(fifo); + } break; default: return AVERROR(EINVAL); } - if (fifo->last == fifo->root.next) - fifo->last = &fifo->root; - tmp = fifo->root.next->next; - av_free(fifo->root.next); - fifo->root.next = tmp; - return 0; } diff --git a/libavfilter/formats.c b/libavfilter/formats.c index 8cdd3bee9a..c23f24d6d3 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -262,11 +262,6 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) AVFilterFormats *ff_all_formats(enum AVMediaType type) { - return avfilter_make_all_formats(type); -} - -AVFilterFormats *avfilter_make_all_formats(enum AVMediaType type) -{ AVFilterFormats *ret = NULL; int fmt; int num_formats = type == AVMEDIA_TYPE_VIDEO ? PIX_FMT_NB : diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 9a95da10f1..40ffef5721 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -173,6 +173,14 @@ struct AVFilterPad { * and another value on error. */ int (*config_props)(AVFilterLink *link); + + /** + * The filter expects a fifo to be inserted on its input link, + * typically because it has a delay. + * + * input pads only. + */ + int needs_fifo; }; #endif diff --git a/libavfilter/libmpcodecs/vf_uspp.c b/libavfilter/libmpcodecs/vf_uspp.c index d9c5de8fb1..ca9b17aad1 100644 --- a/libavfilter/libmpcodecs/vf_uspp.c +++ b/libavfilter/libmpcodecs/vf_uspp.c @@ -94,7 +94,7 @@ static const uint8_t offset[511][2]= { { 7, 1}, {15, 1}, { 7, 9}, {15, 9}, { 7, 3}, {15, 3}, { 7,11}, {15,11}, { 7, 5}, {15, 5}, { 7,13}, {15,13}, { 7, 7}, {15, 7}, { 7,15}, {15,15}, -{ 0, 0}, { 8, 0}, { 0, 8}, { 8, 8}, { 4, 4}, {12, 4}, { 4,12}, {12,12}, { 0, 4}, { 8, 4}, { 0,12}, { 8,12}, { 4, 0}, {12, 0}, { 4, 8}, {12, 8}, { 2, 2}, {10, 2}, { 2,10}, {10,10}, { 6, 6}, {14, 6}, { 6,14}, {14,14}, { 2, 6}, {10, 6}, { 2,14}, {10,14}, { 6, 2}, {14, 2}, { 6,10}, {14,10}, { 0, 2}, { 8, 2}, { 0,10}, { 8,10}, { 4, 6}, {12, 6}, { 4,14}, {12,14}, { 0, 6}, { 8, 6}, { 0,14}, { 8,14}, { 4, 2}, {12, 2}, { 4,10}, {12,10}, { 2, 0}, {10, 0}, { 2, 8}, {10, 8}, { 6, 4}, {14, 4}, { 6,12}, {14,12}, { 2, 4}, {10, 4}, { 2,12}, {10,12}, { 6, 0}, {14, 0}, { 6, 8}, {14, 8}, { 1, 1}, { 9, 1}, { 1, 9}, { 9, 9}, { 5, 5}, {13, 5}, { 5,13}, {13,13}, { 1, 5}, { 9, 5}, { 1,13}, { 9,13}, { 5, 1}, {13, 1}, { 5, 9}, {13, 9}, { 3, 3}, {11, 3}, { 3,11}, {11,11}, { 7, 7}, {15, 7}, { 7,15}, {15,15}, { 3, 7}, {11, 7}, { 3,15}, {11,15}, { 7, 3}, {15, 3}, { 7,11}, {15,11}, { 1, 3}, { 9, 3}, { 1,11}, { 9,11}, { 5, 7}, {13, 7}, { 5,15}, {13,15}, { 1, 7}, { 9, 7}, { 1,15}, { 9,15}, { 5, 3}, {13, 3}, { 5,11}, {13,11}, { 3, 1}, {11, 1}, { 3, 9}, {11, 9}, { 7, 5}, {15, 5}, { 7,13}, {15,13}, { 3, 5}, {11, 5}, { 3,13}, {11,13}, { 7, 1}, {15, 1}, { 7, 9}, {15, 9}, { 0, 1}, { 8, 1}, { 0, 9}, { 8, 9}, { 4, 5}, {12, 5}, { 4,13}, {12,13}, { 0, 5}, { 8, 5}, { 0,13}, { 8,13}, { 4, 1}, {12, 1}, { 4, 9}, {12, 9}, { 2, 3}, {10, 3}, { 2,11}, {10,11}, { 6, 7}, {14, 7}, { 6,15}, {14,15}, { 2, 7}, {10, 7}, { 2,15}, {10,15}, { 6, 3}, {14, 3}, { 6,11}, {14,11}, { 0, 3}, { 8, 3}, { 0,11}, { 8,11}, { 4, 7}, {12, 7}, { 4,15}, {12,15}, { 0, 7}, { 8, 7}, { 0,15}, { 8,15}, { 4, 3}, {12, 3}, { 4,11}, {12,11}, { 2, 1}, {10, 1}, { 2, 9}, {10, 9}, { 6, 5}, {14, 5}, { 6,13}, {14,13}, { 2, 5}, {10, 5}, { 2,13}, {10,13}, { 6, 1}, {14, 1}, { 6, 9}, {14, 9}, { 1, 0}, { 9, 0}, { 1, 8}, { 9, 8}, { 5, 4}, {13, 4}, { 5,12}, {13,12}, { 1, 4}, { 9, 4}, { 1,12}, { 9,12}, { 5, 0}, {13, 0}, { 5, 8}, {13, 8}, { 3, 2}, {11, 2}, { 3,10}, {11,10}, { 7, 6}, {15, 6}, { 7,14}, {15,14}, { 3, 6}, {11, 6}, { 3,14}, {11,14}, { 7, 2}, {15, 2}, { 7,10}, {15,10}, { 1, 2}, { 9, 2}, { 1,10}, { 9,10}, { 5, 6}, {13, 6}, { 5,14}, {13,14}, { 1, 6}, { 9, 6}, { 1,14}, { 9,14}, { 5, 2}, {13, 2}, { 5,10}, {13,10}, { 3, 0}, {11, 0}, { 3, 8}, {11, 8}, { 7, 4}, {15, 4}, { 7,12}, {15,12}, { 3, 4}, {11, 4}, { 3,12}, {11,12}, { 7, 0}, {15, 0}, { 7, 8}, {15, 8}, +{ 0, 0}, { 8, 0}, { 0, 8}, { 8, 8}, { 4, 4}, {12, 4}, { 4,12}, {12,12}, { 0, 4}, { 8, 4}, { 0,12}, { 8,12}, { 4, 0}, {12, 0}, { 4, 8}, {12, 8}, { 2, 2}, {10, 2}, { 2,10}, {10,10}, { 6, 6}, {14, 6}, { 6,14}, {14,14}, { 2, 6}, {10, 6}, { 2,14}, {10,14}, { 6, 2}, {14, 2}, { 6,10}, {14,10}, { 0, 2}, { 8, 2}, { 0,10}, { 8,10}, { 4, 6}, {12, 6}, { 4,14}, {12,14}, { 0, 6}, { 8, 6}, { 0,14}, { 8,14}, { 4, 2}, {12, 2}, { 4,10}, {12,10}, { 2, 0}, {10, 0}, { 2, 8}, {10, 8}, { 6, 4}, {14, 4}, { 6,12}, {14,12}, { 2, 4}, {10, 4}, { 2,12}, {10,12}, { 6, 0}, {14, 0}, { 6, 8}, {14, 8}, { 1, 1}, { 9, 1}, { 1, 9}, { 9, 9}, { 5, 5}, {13, 5}, { 5,13}, {13,13}, { 1, 5}, { 9, 5}, { 1,13}, { 9,13}, { 5, 1}, {13, 1}, { 5, 9}, {13, 9}, { 3, 3}, {11, 3}, { 3,11}, {11,11}, { 7, 7}, {15, 7}, { 7,15}, {15,15}, { 3, 7}, {11, 7}, { 3,15}, {11,15}, { 7, 3}, {15, 3}, { 7,11}, {15,11}, { 1, 3}, { 9, 3}, { 1,11}, { 9,11}, { 5, 7}, {13, 7}, { 5,15}, {13,15}, { 1, 7}, { 9, 7}, { 1,15}, { 9,15}, { 5, 3}, {13, 3}, { 5,11}, {13,11}, { 3, 1}, {11,1}, { 3, 9}, {11, 9}, { 7, 5}, {15, 5}, { 7,13}, {15,13}, { 3, 5}, {11, 5}, { 3,13}, {11,13}, { 7, 1}, {15, 1}, { 7, 9}, {15, 9}, { 0, 1}, { 8, 1}, { 0, 9}, { 8, 9}, { 4, 5}, {12, 5}, { 4,13}, {12,13}, { 0, 5}, { 8, 5}, { 0,13}, { 8,13}, { 4, 1}, {12, 1}, { 4, 9}, {12, 9}, { 2, 3}, {10, 3}, { 2,11}, {10,11}, { 6, 7}, {14, 7}, { 6,15}, {14,15}, { 2, 7}, {10, 7}, { 2,15}, {10,15}, { 6, 3}, {14, 3}, { 6,11}, {14,11}, { 0, 3}, { 8, 3}, { 0,11}, { 8,11}, { 4, 7}, {12, 7}, { 4,15}, {12,15}, { 0, 7}, { 8, 7}, { 0,15}, { 8,15}, { 4, 3}, {12, 3}, { 4,11}, {12,11}, { 2, 1}, {10, 1}, { 2, 9}, {10, 9}, { 6, 5}, {14, 5}, { 6,13}, {14,13}, { 2, 5}, {10, 5}, { 2,13}, {10,13}, { 6, 1}, {14, 1}, { 6, 9}, {14, 9}, { 1, 0}, { 9, 0}, { 1, 8}, { 9, 8}, { 5, 4}, {13, 4}, { 5,12}, {13,12}, { 1, 4}, { 9, 4}, { 1,12}, { 9,12}, { 5, 0}, {13, 0}, { 5, 8}, {13, 8}, { 3, 2}, {11, 2}, { 3,10}, {11,10}, { 7, 6}, {15, 6}, { 7,14}, {15,14}, { 3, 6}, {11, 6}, { 3,14}, {11,14}, { 7, 2}, {15, 2}, { 7,10}, {15,10}, { 1, 2}, { 9, 2}, { 1,10}, {9,10}, { 5, 6}, {13, 6}, { 5,14}, {13,14}, { 1, 6}, { 9, 6}, { 1,14}, { 9,14}, { 5, 2}, {13, 2}, { 5,10}, {13,10}, { 3, 0}, {11, 0}, { 3, 8}, {11, 8}, { 7, 4}, {15, 4}, { 7,12}, {15,12}, { 3, 4}, {11, 4}, { 3,12}, {11,12}, { 7, 0}, {15, 0}, { 7, 8}, {15, 8}, }; struct vf_priv_s { diff --git a/libavfilter/sink_buffer.c b/libavfilter/sink_buffer.c index 27f21b017d..ad516398b4 100644 --- a/libavfilter/sink_buffer.c +++ b/libavfilter/sink_buffer.c @@ -127,7 +127,7 @@ int av_buffersink_get_buffer_ref(AVFilterContext *ctx, if (!av_fifo_size(buf->fifo)) { if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST) return AVERROR(EAGAIN); - if ((ret = avfilter_request_frame(inlink)) < 0) + if ((ret = ff_request_frame(inlink)) < 0) return ret; } diff --git a/libavfilter/version.h b/libavfilter/version.h index 221a0de041..ab32f29a03 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -28,8 +28,8 @@ #include "libavutil/avutil.h" -#define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 82 +#define LIBAVFILTER_VERSION_MAJOR 3 +#define LIBAVFILTER_VERSION_MINOR 0 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ diff --git a/libavfilter/vf_blackdetect.c b/libavfilter/vf_blackdetect.c index 28020a4134..9d29c4559d 100644 --- a/libavfilter/vf_blackdetect.c +++ b/libavfilter/vf_blackdetect.c @@ -136,7 +136,7 @@ static int request_frame(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; BlackDetectContext *blackdetect = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; - int ret = avfilter_request_frame(inlink); + int ret = ff_request_frame(inlink); if (ret == AVERROR_EOF && blackdetect->black_started) { // FIXME: black_end should be set to last_picref_pts + last_picref_duration diff --git a/libavfilter/vf_idet.c b/libavfilter/vf_idet.c index 48aad5f417..1dcf7da4dd 100644 --- a/libavfilter/vf_idet.c +++ b/libavfilter/vf_idet.c @@ -216,7 +216,7 @@ static int request_frame(AVFilterLink *link) do { int ret; - if ((ret = avfilter_request_frame(link->src->inputs[0]))) + if ((ret = ff_request_frame(link->src->inputs[0]))) return ret; } while (!idet->cur); @@ -231,7 +231,7 @@ static int poll_frame(AVFilterLink *link) val = ff_poll_frame(link->src->inputs[0]); if (val >= 1 && !idet->next) { //FIXME change API to not requre this red tape - if ((ret = avfilter_request_frame(link->src->inputs[0])) < 0) + if ((ret = ff_request_frame(link->src->inputs[0])) < 0) return ret; val = ff_poll_frame(link->src->inputs[0]); } diff --git a/libavfilter/vf_mp.c b/libavfilter/vf_mp.c index bf3975af3d..43eecc3c91 100644 --- a/libavfilter/vf_mp.c +++ b/libavfilter/vf_mp.c @@ -25,6 +25,9 @@ */ #include "avfilter.h" +#include "video.h" +#include "formats.h" +#include "internal.h" #include "libavutil/avassert.h" #include "libavutil/pixdesc.h" #include "libavutil/intreadwrite.h" @@ -633,9 +636,9 @@ int vf_next_put_image(struct vf_instance *vf,mp_image_t *mpi, double pts){ if(pts != MP_NOPTS_VALUE) picref->pts= pts * av_q2d(outlink->time_base); - avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0)); - avfilter_draw_slice(outlink, 0, picref->video->h, 1); - avfilter_end_frame(outlink); + ff_start_frame(outlink, avfilter_ref_buffer(picref, ~0)); + ff_draw_slice(outlink, 0, picref->video->h, 1); + ff_end_frame(outlink); avfilter_unref_buffer(picref); m->frame_returned++; @@ -788,14 +791,14 @@ static int query_formats(AVFilterContext *ctx) if(m->vf.query_format(&m->vf, conversion_map[i].fmt)){ av_log(ctx, AV_LOG_DEBUG, "supported,adding\n"); if (conversion_map[i].pix_fmt != lastpixfmt) { - avfilter_add_format(&avfmts, conversion_map[i].pix_fmt); + ff_add_format(&avfmts, conversion_map[i].pix_fmt); lastpixfmt = conversion_map[i].pix_fmt; } } } //We assume all allowed input formats are also allowed output formats - avfilter_set_common_pixel_formats(ctx, avfmts); + ff_set_common_formats(ctx, avfmts); return 0; } @@ -836,7 +839,7 @@ static int request_frame(AVFilterLink *outlink) av_log(m->avfctx, AV_LOG_DEBUG, "mp request_frame\n"); for(m->frame_returned=0; !m->frame_returned;){ - ret=avfilter_request_frame(outlink->src->inputs[0]); + ret=ff_request_frame(outlink->src->inputs[0]); if(ret<0) break; } diff --git a/libavfilter/vf_thumbnail.c b/libavfilter/vf_thumbnail.c index 215f50a509..45238b6a77 100644 --- a/libavfilter/vf_thumbnail.c +++ b/libavfilter/vf_thumbnail.c @@ -178,7 +178,7 @@ static int request_frame(AVFilterLink *link) /* loop until a frame thumbnail is available (when a frame is queued, * thumb->n is reset to zero) */ do { - int ret = avfilter_request_frame(link->src->inputs[0]); + int ret = ff_request_frame(link->src->inputs[0]); if (ret < 0) return ret; } while (thumb->n); @@ -203,7 +203,7 @@ static int poll_frame(AVFilterLink *link) /* we have some frame(s) available in the input link, but not yet enough to * output a thumbnail, so we request more */ - ret = avfilter_request_frame(inlink); + ret = ff_request_frame(inlink); return ret < 0 ? ret : 0; } diff --git a/libavfilter/vf_tile.c b/libavfilter/vf_tile.c index e6971de9ee..83ee45dda0 100644 --- a/libavfilter/vf_tile.c +++ b/libavfilter/vf_tile.c @@ -28,6 +28,7 @@ #include "drawutils.h" #include "formats.h" #include "video.h" +#include "internal.h" typedef struct { unsigned w, h; @@ -170,7 +171,7 @@ static int request_frame(AVFilterLink *outlink) int r; while (1) { - r = avfilter_request_frame(inlink); + r = ff_request_frame(inlink); if (r < 0) { if (r == AVERROR_EOF && tile->current) end_last_frame(ctx); diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c index 8f9dc004fc..b0d04eb418 100644 --- a/libavfilter/vf_tinterlace.c +++ b/libavfilter/vf_tinterlace.c @@ -334,7 +334,7 @@ static int poll_frame(AVFilterLink *outlink) val = ff_poll_frame(inlink); if (val == 1 && !tinterlace->next) { - if ((ret = avfilter_request_frame(inlink)) < 0) + if ((ret = ff_request_frame(inlink)) < 0) return ret; val = ff_poll_frame(inlink); } @@ -351,7 +351,7 @@ static int request_frame(AVFilterLink *outlink) do { int ret; - if ((ret = avfilter_request_frame(inlink)) < 0) + if ((ret = ff_request_frame(inlink)) < 0) return ret; } while (!tinterlace->cur); diff --git a/libavfilter/video.c b/libavfilter/video.c index ab6e2cd5b8..561abbc531 100644 --- a/libavfilter/video.c +++ b/libavfilter/video.c @@ -321,6 +321,11 @@ void ff_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) draw_slice(link, y, h, slice_dir); } +void avfilter_default_end_frame(AVFilterLink *inlink) +{ + default_end_frame(inlink); +} + #if FF_API_FILTERS_PUBLIC AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, int perms, int w, int h) { @@ -330,10 +335,6 @@ void avfilter_default_start_frame(AVFilterLink *inlink, AVFilterBufferRef *picre { default_start_frame(inlink, picref); } -void avfilter_default_end_frame(AVFilterLink *inlink) -{ - default_end_frame(inlink); -} void avfilter_default_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { default_draw_slice(inlink, y, h, slice_dir); diff --git a/libavutil/cpu.c b/libavutil/cpu.c index b911880cad..7a7a272c82 100644 --- a/libavutil/cpu.c +++ b/libavutil/cpu.c @@ -49,7 +49,7 @@ void av_set_cpu_flags_mask(int mask) int av_parse_cpu_flags(const char *s) { -#define CPUFLAG_MMX2 (AV_CPU_FLAG_MMX | AV_CPU_FLAG_MMX2) +#define CPUFLAG_MMX2 (AV_CPU_FLAG_MMX | AV_CPU_FLAG_MMX2 | AV_CPU_FLAG_CMOV) #define CPUFLAG_3DNOW (AV_CPU_FLAG_3DNOW | AV_CPU_FLAG_MMX) #define CPUFLAG_3DNOWEXT (AV_CPU_FLAG_3DNOWEXT | CPUFLAG_3DNOW) #define CPUFLAG_SSE (AV_CPU_FLAG_SSE | CPUFLAG_MMX2) @@ -84,6 +84,7 @@ int av_parse_cpu_flags(const char *s) { "fma4" , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_FMA4 }, .unit = "flags" }, { "3dnow" , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOW }, .unit = "flags" }, { "3dnowext", NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOWEXT }, .unit = "flags" }, + { "cmov", NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_CMOV }, .unit = "flags" }, #elif ARCH_ARM { "armv5te", NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_ARMV5TE }, .unit = "flags" }, { "armv6", NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_ARMV6 }, .unit = "flags" }, @@ -188,6 +189,7 @@ static const struct { { AV_CPU_FLAG_FMA4, "fma4" }, { AV_CPU_FLAG_3DNOW, "3dnow" }, { AV_CPU_FLAG_3DNOWEXT, "3dnowext" }, + { AV_CPU_FLAG_CMOV, "cmov" }, #endif { 0 } }; diff --git a/libavutil/cpu.h b/libavutil/cpu.h index dec0566d04..ee6705adef 100644 --- a/libavutil/cpu.h +++ b/libavutil/cpu.h @@ -40,9 +40,14 @@ #define AV_CPU_FLAG_SSE4 0x0100 ///< Penryn SSE4.1 functions #define AV_CPU_FLAG_SSE42 0x0200 ///< Nehalem SSE4.2 functions #define AV_CPU_FLAG_AVX 0x4000 ///< AVX functions: requires OS support even if YMM registers aren't used -#define AV_CPU_FLAG_CMOV 0x1000000 ///< supports cmov instruction #define AV_CPU_FLAG_XOP 0x0400 ///< Bulldozer XOP functions #define AV_CPU_FLAG_FMA4 0x0800 ///< Bulldozer FMA4 functions +#if LIBAVUTIL_VERSION_MAJOR <52 +#define AV_CPU_FLAG_CMOV 0x1001000 ///< supports cmov instruction +#else +#define AV_CPU_FLAG_CMOV 0x1000 ///< supports cmov instruction +#endif + #define AV_CPU_FLAG_ALTIVEC 0x0001 ///< standard #define AV_CPU_FLAG_ARMV5TE (1 << 0) diff --git a/libavutil/x86/cpu.c b/libavutil/x86/cpu.c index b53379bbd3..5782ff73cf 100644 --- a/libavutil/x86/cpu.c +++ b/libavutil/x86/cpu.c @@ -83,7 +83,7 @@ int ff_get_cpu_flags_x86(void) cpuid(1, eax, ebx, ecx, std_caps); family = ((eax>>8)&0xf) + ((eax>>20)&0xff); model = ((eax>>4)&0xf) + ((eax>>12)&0xf0); - if (std_caps & (1<<15)) + if (std_caps & (1 << 15)) rval |= AV_CPU_FLAG_CMOV; if (std_caps & (1<<23)) rval |= AV_CPU_FLAG_MMX; |