* 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>tags/n1.0
| @@ -12,6 +12,8 @@ version next: | |||||
| - RTMPT protocol support | - RTMPT protocol support | ||||
| - iLBC encoding/decoding via libilbc | - iLBC encoding/decoding via libilbc | ||||
| - Microsoft Screen 1 decoder | - Microsoft Screen 1 decoder | ||||
| - join audio filter | |||||
| - audio channel mapping filter | |||||
| - showwaves filter | - showwaves filter | ||||
| - LucasArts SMUSH playback support | - LucasArts SMUSH playback support | ||||
| @@ -4,7 +4,7 @@ since the last major version increase. | |||||
| The last version increases were: | The last version increases were: | ||||
| libavcodec: 2012-01-27 | libavcodec: 2012-01-27 | ||||
| libavdevice: 2011-04-18 | libavdevice: 2011-04-18 | ||||
| libavfilter: 2011-04-18 | |||||
| libavfilter: 2012-06-22 | |||||
| libavformat: 2012-01-27 | libavformat: 2012-01-27 | ||||
| libavresample: 2012-04-24 | libavresample: 2012-04-24 | ||||
| libpostproc: 2011-04-18 | libpostproc: 2011-04-18 | ||||
| @@ -649,6 +649,76 @@ front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]' | |||||
| side_right.wav | side_right.wav | ||||
| @end example | @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 | @section resample | ||||
| Convert the audio sample format, sample rate and channel layout. This filter is | Convert the audio sample format, sample rate and channel layout. This filter is | ||||
| not meant to be used directly. | not meant to be used directly. | ||||
| @@ -29,6 +29,12 @@ | |||||
| #include "libavcodec/cavsdsp.h" | #include "libavcodec/cavsdsp.h" | ||||
| #include "dsputil_mmx.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 | * inverse transform | ||||
| @@ -631,6 +631,34 @@ static void add_hfyu_median_prediction_cmov(uint8_t *dst, const uint8_t *top, | |||||
| } | } | ||||
| #endif | #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 \ | #define H263_LOOP_FILTER \ | ||||
| "pxor %%mm7, %%mm7 \n\t" \ | "pxor %%mm7, %%mm7 \n\t" \ | ||||
| "movq %0, %%mm0 \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; | c->vorbis_inverse_coupling = vorbis_inverse_coupling_3dnow; | ||||
| #if HAVE_7REGS | #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 | #endif | ||||
| } | } | ||||
| @@ -66,24 +66,6 @@ extern const xmm_reg ff_pb_FE; | |||||
| extern const double ff_pd_1[2]; | extern const double ff_pd_1[2]; | ||||
| extern const double ff_pd_2[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)\ | #define SBUTTERFLY(a,b,t,n,m)\ | ||||
| "mov" #m " " #a ", " #t " \n\t" /* abcd */\ | "mov" #m " " #a ", " #t " \n\t" /* abcd */\ | ||||
| "punpckl" #n " " #b ", " #a " \n\t" /* aebf */\ | "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(a,c,d,dq,q) /* a=aeim d=bfjn */\ | ||||
| SBUTTERFLY(t,b,c,dq,q) /* t=cgko c=dhlp */ | 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) \ | #define MOVQ_WONE(regd) \ | ||||
| __asm__ volatile ( \ | __asm__ volatile ( \ | ||||
| "pcmpeqd %%" #regd ", %%" #regd " \n\t" \ | "pcmpeqd %%" #regd ", %%" #regd " \n\t" \ | ||||
| @@ -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_add8 = ff_h264_idct_add8_8_mmx; | ||||
| c->h264_idct_add16intra = ff_h264_idct_add16intra_8_mmx; | c->h264_idct_add16intra = ff_h264_idct_add16intra_8_mmx; | ||||
| if (mm_flags & AV_CPU_FLAG_CMOV) | 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) { | if (mm_flags & AV_CPU_FLAG_MMX2) { | ||||
| c->h264_idct_dc_add = ff_h264_idct_dc_add_8_mmx2; | c->h264_idct_dc_add = ff_h264_idct_dc_add_8_mmx2; | ||||
| @@ -59,8 +59,10 @@ OBJS-$(CONFIG_ASPLIT_FILTER) += split.o | |||||
| OBJS-$(CONFIG_ASTREAMSYNC_FILTER) += af_astreamsync.o | OBJS-$(CONFIG_ASTREAMSYNC_FILTER) += af_astreamsync.o | ||||
| OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o | OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o | ||||
| OBJS-$(CONFIG_ATEMPO_FILTER) += af_atempo.o | OBJS-$(CONFIG_ATEMPO_FILTER) += af_atempo.o | ||||
| OBJS-$(CONFIG_CHANNELMAP_FILTER) += af_channelmap.o | |||||
| OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o | OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o | ||||
| OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o | OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o | ||||
| OBJS-$(CONFIG_JOIN_FILTER) += af_join.o | |||||
| OBJS-$(CONFIG_PAN_FILTER) += af_pan.o | OBJS-$(CONFIG_PAN_FILTER) += af_pan.o | ||||
| OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o | OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o | ||||
| OBJS-$(CONFIG_SILENCEDETECT_FILTER) += af_silencedetect.o | OBJS-$(CONFIG_SILENCEDETECT_FILTER) += af_silencedetect.o | ||||
| @@ -75,14 +75,14 @@ static int query_formats(AVFilterContext *ctx) | |||||
| AVFilterLink *outlink = ctx->outputs[0]; | AVFilterLink *outlink = ctx->outputs[0]; | ||||
| AVFilterChannelLayouts *layouts; | AVFilterChannelLayouts *layouts; | ||||
| ff_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), | |||||
| ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO), | |||||
| &inlink->out_formats); | &inlink->out_formats); | ||||
| if (aconvert->out_sample_fmt != AV_SAMPLE_FMT_NONE) { | if (aconvert->out_sample_fmt != AV_SAMPLE_FMT_NONE) { | ||||
| formats = NULL; | formats = NULL; | ||||
| ff_add_format(&formats, aconvert->out_sample_fmt); | ff_add_format(&formats, aconvert->out_sample_fmt); | ||||
| ff_formats_ref(formats, &outlink->in_formats); | ff_formats_ref(formats, &outlink->in_formats); | ||||
| } else | } else | ||||
| ff_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), | |||||
| ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO), | |||||
| &outlink->in_formats); | &outlink->in_formats); | ||||
| ff_channel_layouts_ref(ff_all_channel_layouts(), | ff_channel_layouts_ref(ff_all_channel_layouts(), | ||||
| @@ -134,8 +134,7 @@ AVFilter avfilter_af_aformat = { | |||||
| .priv_size = sizeof(AFormatContext), | .priv_size = sizeof(AFormatContext), | ||||
| .inputs = (AVFilterPad[]) {{ .name = "default", | .inputs = (AVFilterPad[]) {{ .name = "default", | ||||
| .type = AVMEDIA_TYPE_AUDIO, | |||||
| .filter_samples = ff_null_filter_samples }, | |||||
| .type = AVMEDIA_TYPE_AUDIO, }, | |||||
| { .name = NULL}}, | { .name = NULL}}, | ||||
| .outputs = (AVFilterPad[]) {{ .name = "default", | .outputs = (AVFilterPad[]) {{ .name = "default", | ||||
| .type = AVMEDIA_TYPE_AUDIO}, | .type = AVMEDIA_TYPE_AUDIO}, | ||||
| @@ -169,7 +169,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| for (i = 0; i < am->nb_inputs; i++) | for (i = 0; i < am->nb_inputs; i++) | ||||
| if (!am->in[i].nb_samples) | 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 ret; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -34,8 +34,7 @@ AVFilter avfilter_af_anull = { | |||||
| .inputs = (const AVFilterPad[]) {{ .name = "default", | .inputs = (const AVFilterPad[]) {{ .name = "default", | ||||
| .type = AVMEDIA_TYPE_AUDIO, | .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}}, | { .name = NULL}}, | ||||
| .outputs = (const AVFilterPad[]) {{ .name = "default", | .outputs = (const AVFilterPad[]) {{ .name = "default", | ||||
| @@ -112,7 +112,7 @@ static int query_formats(AVFilterContext *ctx) | |||||
| if(out_format != AV_SAMPLE_FMT_NONE) { | if(out_format != AV_SAMPLE_FMT_NONE) { | ||||
| out_formats = ff_make_format_list((int[]){ out_format, -1 }); | out_formats = ff_make_format_list((int[]){ out_format, -1 }); | ||||
| } else | } 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); | ff_formats_ref(out_formats, &outlink->in_formats); | ||||
| if(out_layout) { | if(out_layout) { | ||||
| @@ -211,7 +211,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| aresample->req_fullfilled = 0; | aresample->req_fullfilled = 0; | ||||
| do{ | do{ | ||||
| ret = avfilter_request_frame(ctx->inputs[0]); | |||||
| ret = ff_request_frame(ctx->inputs[0]); | |||||
| }while(!aresample->req_fullfilled && ret>=0); | }while(!aresample->req_fullfilled && ret>=0); | ||||
| if (ret == AVERROR_EOF) { | if (ret == AVERROR_EOF) { | ||||
| @@ -164,7 +164,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| asns->req_fullfilled = 0; | asns->req_fullfilled = 0; | ||||
| do { | do { | ||||
| ret = avfilter_request_frame(inlink); | |||||
| ret = ff_request_frame(inlink); | |||||
| } while (!asns->req_fullfilled && ret >= 0); | } while (!asns->req_fullfilled && ret >= 0); | ||||
| if (ret == AVERROR_EOF) | if (ret == AVERROR_EOF) | ||||
| @@ -157,7 +157,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| send_next(ctx); | send_next(ctx); | ||||
| } else { | } else { | ||||
| as->eof |= 1 << as->next_out; | 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)) | if (as->eof & (1 << as->next_out)) | ||||
| as->next_out = !as->next_out; | as->next_out = !as->next_out; | ||||
| } | } | ||||
| @@ -1083,7 +1083,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| atempo->request_fulfilled = 0; | atempo->request_fulfilled = 0; | ||||
| do { | do { | ||||
| ret = avfilter_request_frame(ctx->inputs[0]); | |||||
| ret = ff_request_frame(ctx->inputs[0]); | |||||
| } | } | ||||
| while (!atempo->request_fulfilled && ret >= 0); | while (!atempo->request_fulfilled && ret >= 0); | ||||
| @@ -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 }}, | |||||
| }; | |||||
| @@ -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 }}, | |||||
| }; | |||||
| @@ -217,7 +217,7 @@ static int query_formats(AVFilterContext *ctx) | |||||
| pan->pure_gains = are_gains_pure(pan); | pan->pure_gains = are_gains_pure(pan); | ||||
| /* libswr supports any sample and packing formats */ | /* 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(); | formats = ff_all_samplerates(); | ||||
| if (!formats) | if (!formats) | ||||
| @@ -47,8 +47,10 @@ void avfilter_register_all(void) | |||||
| REGISTER_FILTER (ASTREAMSYNC, astreamsync, af); | REGISTER_FILTER (ASTREAMSYNC, astreamsync, af); | ||||
| REGISTER_FILTER (ASYNCTS, asyncts, af); | REGISTER_FILTER (ASYNCTS, asyncts, af); | ||||
| REGISTER_FILTER (ATEMPO, atempo, af); | REGISTER_FILTER (ATEMPO, atempo, af); | ||||
| REGISTER_FILTER (CHANNELMAP, channelmap, af); | |||||
| REGISTER_FILTER (CHANNELSPLIT,channelsplit,af); | REGISTER_FILTER (CHANNELSPLIT,channelsplit,af); | ||||
| REGISTER_FILTER (EARWAX, earwax, af); | REGISTER_FILTER (EARWAX, earwax, af); | ||||
| REGISTER_FILTER (JOIN, join, af); | |||||
| REGISTER_FILTER (PAN, pan, af); | REGISTER_FILTER (PAN, pan, af); | ||||
| REGISTER_FILTER (SILENCEDETECT, silencedetect, af); | REGISTER_FILTER (SILENCEDETECT, silencedetect, af); | ||||
| REGISTER_FILTER (VOLUME, volume, af); | REGISTER_FILTER (VOLUME, volume, af); | ||||
| @@ -150,32 +150,12 @@ fail: | |||||
| return NULL; | 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); | 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 ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) | ||||
| { | { | ||||
| void (*filter_samples)(AVFilterLink *, AVFilterBufferRef *); | 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); | FF_TPRINTF_START(NULL, filter_samples); ff_tlog_link(NULL, link, 1); | ||||
| if (!(filter_samples = dst->filter_samples)) | 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 */ | /* prepare to copy the samples if the buffer has insufficient permissions */ | ||||
| if ((dst->min_perms & samplesref->perms) != dst->min_perms || | if ((dst->min_perms & samplesref->perms) != dst->min_perms || | ||||
| @@ -63,12 +63,6 @@ AVFilterBufferRef *ff_null_get_audio_buffer(AVFilterLink *link, int perms, | |||||
| AVFilterBufferRef *ff_get_audio_buffer(AVFilterLink *link, int perms, | AVFilterBufferRef *ff_get_audio_buffer(AVFilterLink *link, int perms, | ||||
| int nb_samples); | 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. | * Send a buffer of audio samples to the next filter. | ||||
| * | * | ||||
| @@ -170,7 +170,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| showwaves->req_fullfilled = 0; | showwaves->req_fullfilled = 0; | ||||
| do { | do { | ||||
| ret = avfilter_request_frame(inlink); | |||||
| ret = ff_request_frame(inlink); | |||||
| } while (!showwaves->req_fullfilled && ret >= 0); | } while (!showwaves->req_fullfilled && ret >= 0); | ||||
| if (ret == AVERROR_EOF && showwaves->outpicref) | if (ret == AVERROR_EOF && showwaves->outpicref) | ||||
| @@ -211,7 +211,6 @@ AVFilterBufferRef *avfilter_ref_buffer(AVFilterBufferRef *ref, int pmask); | |||||
| */ | */ | ||||
| void avfilter_unref_buffer(AVFilterBufferRef *ref); | void avfilter_unref_buffer(AVFilterBufferRef *ref); | ||||
| #if FF_API_FILTERS_PUBLIC | |||||
| /** | /** | ||||
| * Remove a reference to a buffer and set the pointer to NULL. | * Remove a reference to a buffer and set the pointer to NULL. | ||||
| * If this is the last reference to the buffer, the buffer itself | * 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); | 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 | * 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 | * 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 | * @return a non negative value in case of success, or a negative | ||||
| * value corresponding to an AVERROR code in case of error | * 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 | attribute_deprecated | ||||
| int avfilter_add_format(AVFilterFormats **avff, int64_t fmt); | 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 request callback. A call to this should result in at least one | ||||
| * frame being output over the given link. This should return zero on | * frame being output over the given link. This should return zero on | ||||
| * success, and another value on error. | * 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. | * meaning. | ||||
| * | * | ||||
| * Output pads only. | * Output pads only. | ||||
| @@ -504,6 +504,14 @@ struct AVFilterPad { | |||||
| * and another value on error. | * and another value on error. | ||||
| */ | */ | ||||
| int (*config_props)(AVFilterLink *link); | 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 | #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); | 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 | #if FF_API_FILTERS_PUBLIC | ||||
| /** default handler for start_frame() for video inputs */ | /** default handler for start_frame() for video inputs */ | ||||
| attribute_deprecated | attribute_deprecated | ||||
| @@ -538,10 +550,6 @@ void avfilter_default_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) | |||||
| attribute_deprecated | attribute_deprecated | ||||
| void avfilter_default_draw_slice(AVFilterLink *link, int y, int h, int slice_dir); | 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 */ | /** default handler for get_video_buffer() for video inputs */ | ||||
| attribute_deprecated | attribute_deprecated | ||||
| AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, | AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, | ||||
| @@ -756,6 +764,15 @@ struct AVFilterLink { | |||||
| struct AVFilterChannelLayouts *in_channel_layouts; | struct AVFilterChannelLayouts *in_channel_layouts; | ||||
| struct AVFilterChannelLayouts *out_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; | struct AVFilterPool *pool; | ||||
| /** | /** | ||||
| @@ -785,7 +802,6 @@ struct AVFilterLink { | |||||
| * It is similar to the r_frae_rate field in AVStream. | * It is similar to the r_frae_rate field in AVStream. | ||||
| */ | */ | ||||
| AVRational frame_rate; | AVRational frame_rate; | ||||
| }; | }; | ||||
| /** | /** | ||||
| @@ -187,7 +187,7 @@ static int filter_query_formats(AVFilterContext *ctx) | |||||
| if ((ret = ctx->filter->query_formats(ctx)) < 0) | if ((ret = ctx->filter->query_formats(ctx)) < 0) | ||||
| return ret; | return ret; | ||||
| formats = avfilter_make_all_formats(type); | |||||
| formats = ff_all_formats(type); | |||||
| if (!formats) | if (!formats) | ||||
| return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
| ff_set_common_formats(ctx, formats); | ff_set_common_formats(ctx, formats); | ||||
| @@ -815,12 +815,52 @@ static int ff_avfilter_graph_config_pointers(AVFilterGraph *graph, | |||||
| return 0; | 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 avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) | ||||
| { | { | ||||
| int ret; | int ret; | ||||
| if ((ret = graph_check_validity(graphctx, log_ctx))) | if ((ret = graph_check_validity(graphctx, log_ctx))) | ||||
| return ret; | return ret; | ||||
| if ((ret = graph_insert_fifos(graphctx, log_ctx)) < 0) | |||||
| return ret; | |||||
| if ((ret = graph_config_formats(graphctx, log_ctx))) | if ((ret = graph_config_formats(graphctx, log_ctx))) | ||||
| return ret; | return ret; | ||||
| if ((ret = graph_config_links(graphctx, log_ctx))) | if ((ret = graph_config_links(graphctx, log_ctx))) | ||||
| @@ -939,7 +979,7 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) | |||||
| { | { | ||||
| while (graph->sink_links_count) { | while (graph->sink_links_count) { | ||||
| AVFilterLink *oldest = graph->sink_links[0]; | AVFilterLink *oldest = graph->sink_links[0]; | ||||
| int r = avfilter_request_frame(oldest); | |||||
| int r = ff_request_frame(oldest); | |||||
| if (r != AVERROR_EOF) | if (r != AVERROR_EOF) | ||||
| return r; | return r; | ||||
| /* EOF: remove the link from the heap */ | /* EOF: remove the link from the heap */ | ||||
| @@ -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 | * of a filtergraph, only a convenience function to help drain a filtergraph | ||||
| * in a balanced way under normal circumstances. | * 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. | * or AVERROR_EOF of all links returned AVERROR_EOF. | ||||
| */ | */ | ||||
| int avfilter_graph_request_oldest(AVFilterGraph *graph); | int avfilter_graph_request_oldest(AVFilterGraph *graph); | ||||
| @@ -25,7 +25,7 @@ | |||||
| #include "libavutil/audio_fifo.h" | #include "libavutil/audio_fifo.h" | ||||
| #include "libavutil/audioconvert.h" | #include "libavutil/audioconvert.h" | ||||
| #include "libavutil/fifo.h" | |||||
| #include "libavutil/avassert.h" | |||||
| #include "libavutil/mathematics.h" | #include "libavutil/mathematics.h" | ||||
| #include "audio.h" | #include "audio.h" | ||||
| @@ -34,86 +34,45 @@ | |||||
| #include "internal.h" | #include "internal.h" | ||||
| typedef struct { | 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 | AVAudioFifo *audio_fifo; ///< FIFO for audio samples | ||||
| int64_t next_pts; ///< interpolating audio pts | int64_t next_pts; ///< interpolating audio pts | ||||
| } BufferSinkContext; | } BufferSinkContext; | ||||
| #define FIFO_INIT_SIZE 8 | |||||
| static av_cold void uninit(AVFilterContext *ctx) | static av_cold void uninit(AVFilterContext *ctx) | ||||
| { | { | ||||
| BufferSinkContext *sink = ctx->priv; | 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) | if (sink->audio_fifo) | ||||
| av_audio_fifo_free(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; | 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) | int av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf) | ||||
| { | { | ||||
| BufferSinkContext *sink = ctx->priv; | |||||
| BufferSinkContext *s = ctx->priv; | |||||
| AVFilterLink *link = ctx->inputs[0]; | AVFilterLink *link = ctx->inputs[0]; | ||||
| int ret; | 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; | return ret; | ||||
| if (!av_fifo_size(sink->fifo)) | |||||
| if (!s->cur_buf) | |||||
| return AVERROR(EINVAL); | return AVERROR(EINVAL); | ||||
| av_fifo_generic_read(sink->fifo, buf, sizeof(*buf), NULL); | |||||
| *buf = s->cur_buf; | |||||
| s->cur_buf = NULL; | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -182,13 +141,13 @@ AVFilter avfilter_vsink_buffer = { | |||||
| .name = "buffersink_old", | .name = "buffersink_old", | ||||
| .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), | .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), | ||||
| .priv_size = sizeof(BufferSinkContext), | .priv_size = sizeof(BufferSinkContext), | ||||
| .init = init, | |||||
| .uninit = uninit, | .uninit = uninit, | ||||
| .inputs = (AVFilterPad[]) {{ .name = "default", | .inputs = (AVFilterPad[]) {{ .name = "default", | ||||
| .type = AVMEDIA_TYPE_VIDEO, | .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 }}, | { .name = NULL }}, | ||||
| .outputs = (AVFilterPad[]) {{ .name = NULL }}, | .outputs = (AVFilterPad[]) {{ .name = NULL }}, | ||||
| }; | }; | ||||
| @@ -197,13 +156,13 @@ AVFilter avfilter_asink_abuffer = { | |||||
| .name = "abuffersink_old", | .name = "abuffersink_old", | ||||
| .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), | .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), | ||||
| .priv_size = sizeof(BufferSinkContext), | .priv_size = sizeof(BufferSinkContext), | ||||
| .init = init, | |||||
| .uninit = uninit, | .uninit = uninit, | ||||
| .inputs = (AVFilterPad[]) {{ .name = "default", | .inputs = (AVFilterPad[]) {{ .name = "default", | ||||
| .type = AVMEDIA_TYPE_AUDIO, | .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 }}, | { .name = NULL }}, | ||||
| .outputs = (AVFilterPad[]) {{ .name = NULL }}, | .outputs = (AVFilterPad[]) {{ .name = NULL }}, | ||||
| }; | }; | ||||
| @@ -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 | #if FF_API_PACKING | ||||
| void avfilter_set_common_packing_formats(AVFilterContext *ctx, AVFilterFormats *formats) | void avfilter_set_common_packing_formats(AVFilterContext *ctx, AVFilterFormats *formats) | ||||
| { | { | ||||
| @@ -23,6 +23,11 @@ | |||||
| * FIFO buffering filter | * FIFO buffering filter | ||||
| */ | */ | ||||
| #include "libavutil/avassert.h" | |||||
| #include "libavutil/audioconvert.h" | |||||
| #include "libavutil/mathematics.h" | |||||
| #include "libavutil/samplefmt.h" | |||||
| #include "audio.h" | #include "audio.h" | ||||
| #include "avfilter.h" | #include "avfilter.h" | ||||
| #include "internal.h" | #include "internal.h" | ||||
| @@ -36,6 +41,13 @@ typedef struct Buf { | |||||
| typedef struct { | typedef struct { | ||||
| Buf root; | Buf root; | ||||
| Buf *last; ///< last buffered frame | 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; | } FifoContext; | ||||
| static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) | 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); | avfilter_unref_buffer(buf->buf); | ||||
| av_free(buf); | av_free(buf); | ||||
| } | } | ||||
| avfilter_unref_buffer(fifo->buf_out); | |||||
| } | } | ||||
| static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) | 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; | 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 end_frame(AVFilterLink *inlink) { } | ||||
| static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { } | 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) | static int request_frame(AVFilterLink *outlink) | ||||
| { | { | ||||
| FifoContext *fifo = outlink->src->priv; | FifoContext *fifo = outlink->src->priv; | ||||
| Buf *tmp; | |||||
| int ret; | int ret; | ||||
| if (!fifo->root.next) { | if (!fifo->root.next) { | ||||
| @@ -90,20 +233,20 @@ static int request_frame(AVFilterLink *outlink) | |||||
| ff_start_frame(outlink, fifo->root.next->buf); | ff_start_frame(outlink, fifo->root.next->buf); | ||||
| ff_draw_slice (outlink, 0, outlink->h, 1); | ff_draw_slice (outlink, 0, outlink->h, 1); | ||||
| ff_end_frame (outlink); | ff_end_frame (outlink); | ||||
| queue_pop(fifo); | |||||
| break; | break; | ||||
| case AVMEDIA_TYPE_AUDIO: | 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; | break; | ||||
| default: | default: | ||||
| return AVERROR(EINVAL); | 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; | return 0; | ||||
| } | } | ||||
| @@ -261,11 +261,6 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) | |||||
| } | } | ||||
| AVFilterFormats *ff_all_formats(enum AVMediaType type) | AVFilterFormats *ff_all_formats(enum AVMediaType type) | ||||
| { | |||||
| return avfilter_make_all_formats(type); | |||||
| } | |||||
| AVFilterFormats *avfilter_make_all_formats(enum AVMediaType type) | |||||
| { | { | ||||
| AVFilterFormats *ret = NULL; | AVFilterFormats *ret = NULL; | ||||
| int fmt; | int fmt; | ||||
| @@ -173,6 +173,14 @@ struct AVFilterPad { | |||||
| * and another value on error. | * and another value on error. | ||||
| */ | */ | ||||
| int (*config_props)(AVFilterLink *link); | 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 | #endif | ||||
| @@ -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, 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}, | { 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 { | struct vf_priv_s { | ||||
| @@ -127,7 +127,7 @@ int av_buffersink_get_buffer_ref(AVFilterContext *ctx, | |||||
| if (!av_fifo_size(buf->fifo)) { | if (!av_fifo_size(buf->fifo)) { | ||||
| if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST) | if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST) | ||||
| return AVERROR(EAGAIN); | return AVERROR(EAGAIN); | ||||
| if ((ret = avfilter_request_frame(inlink)) < 0) | |||||
| if ((ret = ff_request_frame(inlink)) < 0) | |||||
| return ret; | return ret; | ||||
| } | } | ||||
| @@ -28,8 +28,8 @@ | |||||
| #include "libavutil/avutil.h" | #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_MICRO 100 | ||||
| #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ | #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ | ||||
| @@ -136,7 +136,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| AVFilterContext *ctx = outlink->src; | AVFilterContext *ctx = outlink->src; | ||||
| BlackDetectContext *blackdetect = ctx->priv; | BlackDetectContext *blackdetect = ctx->priv; | ||||
| AVFilterLink *inlink = ctx->inputs[0]; | AVFilterLink *inlink = ctx->inputs[0]; | ||||
| int ret = avfilter_request_frame(inlink); | |||||
| int ret = ff_request_frame(inlink); | |||||
| if (ret == AVERROR_EOF && blackdetect->black_started) { | if (ret == AVERROR_EOF && blackdetect->black_started) { | ||||
| // FIXME: black_end should be set to last_picref_pts + last_picref_duration | // FIXME: black_end should be set to last_picref_pts + last_picref_duration | ||||
| @@ -216,7 +216,7 @@ static int request_frame(AVFilterLink *link) | |||||
| do { | do { | ||||
| int ret; | int ret; | ||||
| if ((ret = avfilter_request_frame(link->src->inputs[0]))) | |||||
| if ((ret = ff_request_frame(link->src->inputs[0]))) | |||||
| return ret; | return ret; | ||||
| } while (!idet->cur); | } while (!idet->cur); | ||||
| @@ -231,7 +231,7 @@ static int poll_frame(AVFilterLink *link) | |||||
| val = ff_poll_frame(link->src->inputs[0]); | val = ff_poll_frame(link->src->inputs[0]); | ||||
| if (val >= 1 && !idet->next) { //FIXME change API to not requre this red tape | 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; | return ret; | ||||
| val = ff_poll_frame(link->src->inputs[0]); | val = ff_poll_frame(link->src->inputs[0]); | ||||
| } | } | ||||
| @@ -25,6 +25,9 @@ | |||||
| */ | */ | ||||
| #include "avfilter.h" | #include "avfilter.h" | ||||
| #include "video.h" | |||||
| #include "formats.h" | |||||
| #include "internal.h" | |||||
| #include "libavutil/avassert.h" | #include "libavutil/avassert.h" | ||||
| #include "libavutil/pixdesc.h" | #include "libavutil/pixdesc.h" | ||||
| #include "libavutil/intreadwrite.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) | if(pts != MP_NOPTS_VALUE) | ||||
| picref->pts= pts * av_q2d(outlink->time_base); | 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); | avfilter_unref_buffer(picref); | ||||
| m->frame_returned++; | m->frame_returned++; | ||||
| @@ -788,14 +791,14 @@ static int query_formats(AVFilterContext *ctx) | |||||
| if(m->vf.query_format(&m->vf, conversion_map[i].fmt)){ | if(m->vf.query_format(&m->vf, conversion_map[i].fmt)){ | ||||
| av_log(ctx, AV_LOG_DEBUG, "supported,adding\n"); | av_log(ctx, AV_LOG_DEBUG, "supported,adding\n"); | ||||
| if (conversion_map[i].pix_fmt != lastpixfmt) { | 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; | lastpixfmt = conversion_map[i].pix_fmt; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| //We assume all allowed input formats are also allowed output formats | //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; | return 0; | ||||
| } | } | ||||
| @@ -836,7 +839,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| av_log(m->avfctx, AV_LOG_DEBUG, "mp request_frame\n"); | av_log(m->avfctx, AV_LOG_DEBUG, "mp request_frame\n"); | ||||
| for(m->frame_returned=0; !m->frame_returned;){ | 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) | if(ret<0) | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -178,7 +178,7 @@ static int request_frame(AVFilterLink *link) | |||||
| /* loop until a frame thumbnail is available (when a frame is queued, | /* loop until a frame thumbnail is available (when a frame is queued, | ||||
| * thumb->n is reset to zero) */ | * thumb->n is reset to zero) */ | ||||
| do { | do { | ||||
| int ret = avfilter_request_frame(link->src->inputs[0]); | |||||
| int ret = ff_request_frame(link->src->inputs[0]); | |||||
| if (ret < 0) | if (ret < 0) | ||||
| return ret; | return ret; | ||||
| } while (thumb->n); | } 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 | /* we have some frame(s) available in the input link, but not yet enough to | ||||
| * output a thumbnail, so we request more */ | * output a thumbnail, so we request more */ | ||||
| ret = avfilter_request_frame(inlink); | |||||
| ret = ff_request_frame(inlink); | |||||
| return ret < 0 ? ret : 0; | return ret < 0 ? ret : 0; | ||||
| } | } | ||||
| @@ -28,6 +28,7 @@ | |||||
| #include "drawutils.h" | #include "drawutils.h" | ||||
| #include "formats.h" | #include "formats.h" | ||||
| #include "video.h" | #include "video.h" | ||||
| #include "internal.h" | |||||
| typedef struct { | typedef struct { | ||||
| unsigned w, h; | unsigned w, h; | ||||
| @@ -170,7 +171,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| int r; | int r; | ||||
| while (1) { | while (1) { | ||||
| r = avfilter_request_frame(inlink); | |||||
| r = ff_request_frame(inlink); | |||||
| if (r < 0) { | if (r < 0) { | ||||
| if (r == AVERROR_EOF && tile->current) | if (r == AVERROR_EOF && tile->current) | ||||
| end_last_frame(ctx); | end_last_frame(ctx); | ||||
| @@ -334,7 +334,7 @@ static int poll_frame(AVFilterLink *outlink) | |||||
| val = ff_poll_frame(inlink); | val = ff_poll_frame(inlink); | ||||
| if (val == 1 && !tinterlace->next) { | if (val == 1 && !tinterlace->next) { | ||||
| if ((ret = avfilter_request_frame(inlink)) < 0) | |||||
| if ((ret = ff_request_frame(inlink)) < 0) | |||||
| return ret; | return ret; | ||||
| val = ff_poll_frame(inlink); | val = ff_poll_frame(inlink); | ||||
| } | } | ||||
| @@ -351,7 +351,7 @@ static int request_frame(AVFilterLink *outlink) | |||||
| do { | do { | ||||
| int ret; | int ret; | ||||
| if ((ret = avfilter_request_frame(inlink)) < 0) | |||||
| if ((ret = ff_request_frame(inlink)) < 0) | |||||
| return ret; | return ret; | ||||
| } while (!tinterlace->cur); | } while (!tinterlace->cur); | ||||
| @@ -321,6 +321,11 @@ void ff_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) | |||||
| draw_slice(link, y, h, 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 | #if FF_API_FILTERS_PUBLIC | ||||
| AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, int perms, int w, int h) | 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); | 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) | void avfilter_default_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) | ||||
| { | { | ||||
| default_draw_slice(inlink, y, h, slice_dir); | default_draw_slice(inlink, y, h, slice_dir); | ||||
| @@ -49,7 +49,7 @@ void av_set_cpu_flags_mask(int mask) | |||||
| int av_parse_cpu_flags(const char *s) | 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_3DNOW (AV_CPU_FLAG_3DNOW | AV_CPU_FLAG_MMX) | ||||
| #define CPUFLAG_3DNOWEXT (AV_CPU_FLAG_3DNOWEXT | CPUFLAG_3DNOW) | #define CPUFLAG_3DNOWEXT (AV_CPU_FLAG_3DNOWEXT | CPUFLAG_3DNOW) | ||||
| #define CPUFLAG_SSE (AV_CPU_FLAG_SSE | CPUFLAG_MMX2) | #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" }, | { "fma4" , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_FMA4 }, .unit = "flags" }, | ||||
| { "3dnow" , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOW }, .unit = "flags" }, | { "3dnow" , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOW }, .unit = "flags" }, | ||||
| { "3dnowext", NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOWEXT }, .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 | #elif ARCH_ARM | ||||
| { "armv5te", NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_ARMV5TE }, .unit = "flags" }, | { "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" }, | { "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_FMA4, "fma4" }, | ||||
| { AV_CPU_FLAG_3DNOW, "3dnow" }, | { AV_CPU_FLAG_3DNOW, "3dnow" }, | ||||
| { AV_CPU_FLAG_3DNOWEXT, "3dnowext" }, | { AV_CPU_FLAG_3DNOWEXT, "3dnowext" }, | ||||
| { AV_CPU_FLAG_CMOV, "cmov" }, | |||||
| #endif | #endif | ||||
| { 0 } | { 0 } | ||||
| }; | }; | ||||
| @@ -40,9 +40,14 @@ | |||||
| #define AV_CPU_FLAG_SSE4 0x0100 ///< Penryn SSE4.1 functions | #define AV_CPU_FLAG_SSE4 0x0100 ///< Penryn SSE4.1 functions | ||||
| #define AV_CPU_FLAG_SSE42 0x0200 ///< Nehalem SSE4.2 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_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_XOP 0x0400 ///< Bulldozer XOP functions | ||||
| #define AV_CPU_FLAG_FMA4 0x0800 ///< Bulldozer FMA4 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_ALTIVEC 0x0001 ///< standard | ||||
| #define AV_CPU_FLAG_ARMV5TE (1 << 0) | #define AV_CPU_FLAG_ARMV5TE (1 << 0) | ||||
| @@ -83,7 +83,7 @@ int ff_get_cpu_flags_x86(void) | |||||
| cpuid(1, eax, ebx, ecx, std_caps); | cpuid(1, eax, ebx, ecx, std_caps); | ||||
| family = ((eax>>8)&0xf) + ((eax>>20)&0xff); | family = ((eax>>8)&0xf) + ((eax>>20)&0xff); | ||||
| model = ((eax>>4)&0xf) + ((eax>>12)&0xf0); | model = ((eax>>4)&0xf) + ((eax>>12)&0xf0); | ||||
| if (std_caps & (1<<15)) | |||||
| if (std_caps & (1 << 15)) | |||||
| rval |= AV_CPU_FLAG_CMOV; | rval |= AV_CPU_FLAG_CMOV; | ||||
| if (std_caps & (1<<23)) | if (std_caps & (1<<23)) | ||||
| rval |= AV_CPU_FLAG_MMX; | rval |= AV_CPU_FLAG_MMX; | ||||