You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	Merge remote-tracking branch 'qatar/master'
* qatar/master: x86: Only use optimizations with cmov if the CPU supports the instruction x86: Add CPU flag for the i686 cmov instruction x86: remove unused inline asm macros from dsputil_mmx.h x86: move some inline asm macros to the only places they are used lavfi: Add the af_channelmap audio channel mapping filter. lavfi: add join audio filter. lavfi: allow audio filters to request a given number of samples. lavfi: support automatically inserting the fifo filter when needed. lavfi/audio: eliminate ff_default_filter_samples(). Conflicts: Changelog libavcodec/x86/h264dsp_mmx.c libavfilter/Makefile libavfilter/allfilters.c libavfilter/avfilter.h libavfilter/avfiltergraph.c libavfilter/version.h libavutil/x86/cpu.c Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
		| @@ -12,6 +12,8 @@ version next: | ||||
| - RTMPT protocol support | ||||
| - iLBC encoding/decoding via libilbc | ||||
| - Microsoft Screen 1 decoder | ||||
| - join audio filter | ||||
| - audio channel mapping filter | ||||
| - showwaves filter | ||||
| - LucasArts SMUSH playback support | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,7 @@ since the last major version increase. | ||||
| The last version increases were: | ||||
| libavcodec:    2012-01-27 | ||||
| libavdevice:   2011-04-18 | ||||
| libavfilter:   2011-04-18 | ||||
| libavfilter:   2012-06-22 | ||||
| libavformat:   2012-01-27 | ||||
| libavresample: 2012-04-24 | ||||
| libpostproc:   2011-04-18 | ||||
|   | ||||
| @@ -649,6 +649,76 @@ front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]' | ||||
| side_right.wav | ||||
| @end example | ||||
|  | ||||
| @section channelmap | ||||
| Remap input channels to new locations. | ||||
|  | ||||
| This filter accepts the following named parameters: | ||||
| @table @option | ||||
| @item channel_layout | ||||
| Channel layout of the output stream. | ||||
|  | ||||
| @item map | ||||
| Map channels from input to output. The argument is a comma-separated list of | ||||
| mappings, each in the @code{@var{in_channel}-@var{out_channel}} or | ||||
| @var{in_channel} form. @var{in_channel} can be either the name of the input | ||||
| channel (e.g. FL for front left) or its index in the input channel layout. | ||||
| @var{out_channel} is the name of the output channel or its index in the output | ||||
| channel layout. If @var{out_channel} is not given then it is implicitly an | ||||
| index, starting with zero and increasing by one for each mapping. | ||||
| @end table | ||||
|  | ||||
| If no mapping is present, the filter will implicitly map input channels to | ||||
| output channels preserving index. | ||||
|  | ||||
| For example, assuming a 5.1+downmix input MOV file | ||||
| @example | ||||
| ffmpeg -i in.mov -filter 'channelmap=map=DL-FL\,DR-FR' out.wav | ||||
| @end example | ||||
| will create an output WAV file tagged as stereo from the downmix channels of | ||||
| the input. | ||||
|  | ||||
| To fix a 5.1 WAV improperly encoded in AAC's native channel order | ||||
| @example | ||||
| ffmpeg -i in.wav -filter 'channelmap=1\,2\,0\,5\,3\,4:channel_layout=5.1' out.wav | ||||
| @end example | ||||
|  | ||||
| @section join | ||||
| Join multiple input streams into one multi-channel stream. | ||||
|  | ||||
| The filter accepts the following named parameters: | ||||
| @table @option | ||||
|  | ||||
| @item inputs | ||||
| Number of input streams. Defaults to 2. | ||||
|  | ||||
| @item channel_layout | ||||
| Desired output channel layout. Defaults to stereo. | ||||
|  | ||||
| @item map | ||||
| Map channels from inputs to output. The argument is a comma-separated list of | ||||
| mappings, each in the @code{@var{input_idx}.@var{in_channel}-@var{out_channel}} | ||||
| form. @var{input_idx} is the 0-based index of the input stream. @var{in_channel} | ||||
| can be either the name of the input channel (e.g. FR for front left) or its | ||||
| index in the specified input stream. @var{out_channel} is the name of the output | ||||
| channel. | ||||
| @end table | ||||
|  | ||||
| The filter will attempt to guess the mappings when those are not specified | ||||
| explicitly. It does so by first trying to find an unused matching input channel | ||||
| and if that fails it picks the first unused input channel. | ||||
|  | ||||
| E.g. to join 3 inputs (with properly set channel layouts) | ||||
| @example | ||||
| ffmpeg -i INPUT1 -i INPUT2 -i INPUT3 -filter_complex join=inputs=3 OUTPUT | ||||
| @end example | ||||
|  | ||||
| To build a 5.1 output from 6 single-channel streams: | ||||
| @example | ||||
| ffmpeg -i fl -i fr -i fc -i sl -i sr -i lfe -filter_complex | ||||
| 'join=inputs=6:channel_layout=5.1:map=0.0-FL\,1.0-FR\,2.0-FC\,3.0-SL\,4.0-SR\,5.0-LFE' | ||||
| out | ||||
| @end example | ||||
|  | ||||
| @section resample | ||||
| Convert the audio sample format, sample rate and channel layout. This filter is | ||||
| not meant to be used directly. | ||||
|   | ||||
| @@ -29,6 +29,12 @@ | ||||
| #include "libavcodec/cavsdsp.h" | ||||
| #include "dsputil_mmx.h" | ||||
|  | ||||
| /* in/out: mma=mma+mmb, mmb=mmb-mma */ | ||||
| #define SUMSUB_BA( a, b ) \ | ||||
|     "paddw "#b", "#a" \n\t"\ | ||||
|     "paddw "#b", "#b" \n\t"\ | ||||
|     "psubw "#a", "#b" \n\t" | ||||
|  | ||||
| /***************************************************************************** | ||||
|  * | ||||
|  * inverse transform | ||||
|   | ||||
| @@ -631,6 +631,34 @@ static void add_hfyu_median_prediction_cmov(uint8_t *dst, const uint8_t *top, | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static inline void transpose4x4(uint8_t *dst, uint8_t *src, x86_reg dst_stride, x86_reg src_stride){ | ||||
|     __asm__ volatile( //FIXME could save 1 instruction if done as 8x4 ... | ||||
|         "movd  (%1), %%mm0              \n\t" | ||||
|         "add   %3, %1                   \n\t" | ||||
|         "movd  (%1), %%mm1              \n\t" | ||||
|         "movd  (%1,%3,1), %%mm2         \n\t" | ||||
|         "movd  (%1,%3,2), %%mm3         \n\t" | ||||
|         "punpcklbw %%mm1, %%mm0         \n\t" | ||||
|         "punpcklbw %%mm3, %%mm2         \n\t" | ||||
|         "movq %%mm0, %%mm1              \n\t" | ||||
|         "punpcklwd %%mm2, %%mm0         \n\t" | ||||
|         "punpckhwd %%mm2, %%mm1         \n\t" | ||||
|         "movd  %%mm0, (%0)              \n\t" | ||||
|         "add   %2, %0                   \n\t" | ||||
|         "punpckhdq %%mm0, %%mm0         \n\t" | ||||
|         "movd  %%mm0, (%0)              \n\t" | ||||
|         "movd  %%mm1, (%0,%2,1)         \n\t" | ||||
|         "punpckhdq %%mm1, %%mm1         \n\t" | ||||
|         "movd  %%mm1, (%0,%2,2)         \n\t" | ||||
|  | ||||
|         :  "+&r" (dst), | ||||
|            "+&r" (src) | ||||
|         :  "r" (dst_stride), | ||||
|            "r" (src_stride) | ||||
|         :  "memory" | ||||
|     ); | ||||
| } | ||||
|  | ||||
| #define H263_LOOP_FILTER                        \ | ||||
|     "pxor      %%mm7, %%mm7             \n\t"   \ | ||||
|     "movq         %0, %%mm0             \n\t"   \ | ||||
| @@ -2902,7 +2930,8 @@ static void dsputil_init_3dnow(DSPContext *c, AVCodecContext *avctx, | ||||
|     c->vorbis_inverse_coupling = vorbis_inverse_coupling_3dnow; | ||||
|  | ||||
| #if HAVE_7REGS | ||||
|     c->add_hfyu_median_prediction = add_hfyu_median_prediction_cmov; | ||||
|     if (mm_flags & AV_CPU_FLAG_CMOV) | ||||
|         c->add_hfyu_median_prediction = add_hfyu_median_prediction_cmov; | ||||
| #endif | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -66,24 +66,6 @@ extern const xmm_reg  ff_pb_FE; | ||||
| extern const double ff_pd_1[2]; | ||||
| extern const double ff_pd_2[2]; | ||||
|  | ||||
| #define LOAD4(stride,in,a,b,c,d)\ | ||||
|     "movq 0*"#stride"+"#in", "#a"\n\t"\ | ||||
|     "movq 1*"#stride"+"#in", "#b"\n\t"\ | ||||
|     "movq 2*"#stride"+"#in", "#c"\n\t"\ | ||||
|     "movq 3*"#stride"+"#in", "#d"\n\t" | ||||
|  | ||||
| #define STORE4(stride,out,a,b,c,d)\ | ||||
|     "movq "#a", 0*"#stride"+"#out"\n\t"\ | ||||
|     "movq "#b", 1*"#stride"+"#out"\n\t"\ | ||||
|     "movq "#c", 2*"#stride"+"#out"\n\t"\ | ||||
|     "movq "#d", 3*"#stride"+"#out"\n\t" | ||||
|  | ||||
| /* in/out: mma=mma+mmb, mmb=mmb-mma */ | ||||
| #define SUMSUB_BA( a, b ) \ | ||||
|     "paddw "#b", "#a" \n\t"\ | ||||
|     "paddw "#b", "#b" \n\t"\ | ||||
|     "psubw "#a", "#b" \n\t" | ||||
|  | ||||
| #define SBUTTERFLY(a,b,t,n,m)\ | ||||
|     "mov" #m " " #a ", " #t "         \n\t" /* abcd */\ | ||||
|     "punpckl" #n " " #b ", " #a "     \n\t" /* aebf */\ | ||||
| @@ -95,90 +77,6 @@ extern const double ff_pd_2[2]; | ||||
|     SBUTTERFLY(a,c,d,dq,q) /* a=aeim d=bfjn */\ | ||||
|     SBUTTERFLY(t,b,c,dq,q) /* t=cgko c=dhlp */ | ||||
|  | ||||
| static inline void transpose4x4(uint8_t *dst, uint8_t *src, x86_reg dst_stride, x86_reg src_stride){ | ||||
|     __asm__ volatile( //FIXME could save 1 instruction if done as 8x4 ... | ||||
|         "movd  (%1), %%mm0              \n\t" | ||||
|         "add   %3, %1                   \n\t" | ||||
|         "movd  (%1), %%mm1              \n\t" | ||||
|         "movd  (%1,%3,1), %%mm2         \n\t" | ||||
|         "movd  (%1,%3,2), %%mm3         \n\t" | ||||
|         "punpcklbw %%mm1, %%mm0         \n\t" | ||||
|         "punpcklbw %%mm3, %%mm2         \n\t" | ||||
|         "movq %%mm0, %%mm1              \n\t" | ||||
|         "punpcklwd %%mm2, %%mm0         \n\t" | ||||
|         "punpckhwd %%mm2, %%mm1         \n\t" | ||||
|         "movd  %%mm0, (%0)              \n\t" | ||||
|         "add   %2, %0                   \n\t" | ||||
|         "punpckhdq %%mm0, %%mm0         \n\t" | ||||
|         "movd  %%mm0, (%0)              \n\t" | ||||
|         "movd  %%mm1, (%0,%2,1)         \n\t" | ||||
|         "punpckhdq %%mm1, %%mm1         \n\t" | ||||
|         "movd  %%mm1, (%0,%2,2)         \n\t" | ||||
|  | ||||
|         :  "+&r" (dst), | ||||
|            "+&r" (src) | ||||
|         :  "r" (dst_stride), | ||||
|            "r" (src_stride) | ||||
|         :  "memory" | ||||
|     ); | ||||
| } | ||||
|  | ||||
| // e,f,g,h can be memory | ||||
| // out: a,d,t,c | ||||
| #define TRANSPOSE8x4(a,b,c,d,e,f,g,h,t)\ | ||||
|     "punpcklbw " #e ", " #a " \n\t" /* a0 e0 a1 e1 a2 e2 a3 e3 */\ | ||||
|     "punpcklbw " #f ", " #b " \n\t" /* b0 f0 b1 f1 b2 f2 b3 f3 */\ | ||||
|     "punpcklbw " #g ", " #c " \n\t" /* c0 g0 c1 g1 c2 g2 d3 g3 */\ | ||||
|     "punpcklbw " #h ", " #d " \n\t" /* d0 h0 d1 h1 d2 h2 d3 h3 */\ | ||||
|     SBUTTERFLY(a, b, t, bw, q)   /* a= a0 b0 e0 f0 a1 b1 e1 f1 */\ | ||||
|                                  /* t= a2 b2 e2 f2 a3 b3 e3 f3 */\ | ||||
|     SBUTTERFLY(c, d, b, bw, q)   /* c= c0 d0 g0 h0 c1 d1 g1 h1 */\ | ||||
|                                  /* b= c2 d2 g2 h2 c3 d3 g3 h3 */\ | ||||
|     SBUTTERFLY(a, c, d, wd, q)   /* a= a0 b0 c0 d0 e0 f0 g0 h0 */\ | ||||
|                                  /* d= a1 b1 c1 d1 e1 f1 g1 h1 */\ | ||||
|     SBUTTERFLY(t, b, c, wd, q)   /* t= a2 b2 c2 d2 e2 f2 g2 h2 */\ | ||||
|                                  /* c= a3 b3 c3 d3 e3 f3 g3 h3 */ | ||||
|  | ||||
| #if ARCH_X86_64 | ||||
| // permutes 01234567 -> 05736421 | ||||
| #define TRANSPOSE8(a,b,c,d,e,f,g,h,t)\ | ||||
|     SBUTTERFLY(a,b,%%xmm8,wd,dqa)\ | ||||
|     SBUTTERFLY(c,d,b,wd,dqa)\ | ||||
|     SBUTTERFLY(e,f,d,wd,dqa)\ | ||||
|     SBUTTERFLY(g,h,f,wd,dqa)\ | ||||
|     SBUTTERFLY(a,c,h,dq,dqa)\ | ||||
|     SBUTTERFLY(%%xmm8,b,c,dq,dqa)\ | ||||
|     SBUTTERFLY(e,g,b,dq,dqa)\ | ||||
|     SBUTTERFLY(d,f,g,dq,dqa)\ | ||||
|     SBUTTERFLY(a,e,f,qdq,dqa)\ | ||||
|     SBUTTERFLY(%%xmm8,d,e,qdq,dqa)\ | ||||
|     SBUTTERFLY(h,b,d,qdq,dqa)\ | ||||
|     SBUTTERFLY(c,g,b,qdq,dqa)\ | ||||
|     "movdqa %%xmm8, "#g"              \n\t" | ||||
| #else | ||||
| #define TRANSPOSE8(a,b,c,d,e,f,g,h,t)\ | ||||
|     "movdqa "#h", "#t"                \n\t"\ | ||||
|     SBUTTERFLY(a,b,h,wd,dqa)\ | ||||
|     "movdqa "#h", 16"#t"              \n\t"\ | ||||
|     "movdqa "#t", "#h"                \n\t"\ | ||||
|     SBUTTERFLY(c,d,b,wd,dqa)\ | ||||
|     SBUTTERFLY(e,f,d,wd,dqa)\ | ||||
|     SBUTTERFLY(g,h,f,wd,dqa)\ | ||||
|     SBUTTERFLY(a,c,h,dq,dqa)\ | ||||
|     "movdqa "#h", "#t"                \n\t"\ | ||||
|     "movdqa 16"#t", "#h"              \n\t"\ | ||||
|     SBUTTERFLY(h,b,c,dq,dqa)\ | ||||
|     SBUTTERFLY(e,g,b,dq,dqa)\ | ||||
|     SBUTTERFLY(d,f,g,dq,dqa)\ | ||||
|     SBUTTERFLY(a,e,f,qdq,dqa)\ | ||||
|     SBUTTERFLY(h,d,e,qdq,dqa)\ | ||||
|     "movdqa "#h", 16"#t"              \n\t"\ | ||||
|     "movdqa "#t", "#h"                \n\t"\ | ||||
|     SBUTTERFLY(h,b,d,qdq,dqa)\ | ||||
|     SBUTTERFLY(c,g,b,qdq,dqa)\ | ||||
|     "movdqa 16"#t", "#g"              \n\t" | ||||
| #endif | ||||
|  | ||||
| #define MOVQ_WONE(regd) \ | ||||
|     __asm__ volatile ( \ | ||||
|     "pcmpeqd %%" #regd ", %%" #regd " \n\t" \ | ||||
|   | ||||
| @@ -362,7 +362,7 @@ void ff_h264dsp_init_x86(H264DSPContext *c, const int bit_depth, const int chrom | ||||
|             c->h264_idct_add8       = ff_h264_idct_add8_8_mmx; | ||||
|         c->h264_idct_add16intra     = ff_h264_idct_add16intra_8_mmx; | ||||
|         if (mm_flags & AV_CPU_FLAG_CMOV) | ||||
|             c->h264_luma_dc_dequant_idct= ff_h264_luma_dc_dequant_idct_mmx; | ||||
|             c->h264_luma_dc_dequant_idct = ff_h264_luma_dc_dequant_idct_mmx; | ||||
|  | ||||
|         if (mm_flags & AV_CPU_FLAG_MMX2) { | ||||
|             c->h264_idct_dc_add    = ff_h264_idct_dc_add_8_mmx2; | ||||
|   | ||||
| @@ -59,8 +59,10 @@ OBJS-$(CONFIG_ASPLIT_FILTER)                 += split.o | ||||
| OBJS-$(CONFIG_ASTREAMSYNC_FILTER)            += af_astreamsync.o | ||||
| OBJS-$(CONFIG_ASYNCTS_FILTER)                += af_asyncts.o | ||||
| OBJS-$(CONFIG_ATEMPO_FILTER)                 += af_atempo.o | ||||
| OBJS-$(CONFIG_CHANNELMAP_FILTER)             += af_channelmap.o | ||||
| OBJS-$(CONFIG_CHANNELSPLIT_FILTER)           += af_channelsplit.o | ||||
| OBJS-$(CONFIG_EARWAX_FILTER)                 += af_earwax.o | ||||
| OBJS-$(CONFIG_JOIN_FILTER)                   += af_join.o | ||||
| OBJS-$(CONFIG_PAN_FILTER)                    += af_pan.o | ||||
| OBJS-$(CONFIG_RESAMPLE_FILTER)               += af_resample.o | ||||
| OBJS-$(CONFIG_SILENCEDETECT_FILTER)          += af_silencedetect.o | ||||
|   | ||||
| @@ -75,14 +75,14 @@ static int query_formats(AVFilterContext *ctx) | ||||
|     AVFilterLink *outlink = ctx->outputs[0]; | ||||
|     AVFilterChannelLayouts *layouts; | ||||
|  | ||||
|     ff_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), | ||||
|     ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO), | ||||
|                          &inlink->out_formats); | ||||
|     if (aconvert->out_sample_fmt != AV_SAMPLE_FMT_NONE) { | ||||
|         formats = NULL; | ||||
|         ff_add_format(&formats, aconvert->out_sample_fmt); | ||||
|         ff_formats_ref(formats, &outlink->in_formats); | ||||
|     } else | ||||
|         ff_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), | ||||
|         ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO), | ||||
|                              &outlink->in_formats); | ||||
|  | ||||
|     ff_channel_layouts_ref(ff_all_channel_layouts(), | ||||
|   | ||||
| @@ -134,8 +134,7 @@ AVFilter avfilter_af_aformat = { | ||||
|     .priv_size     = sizeof(AFormatContext), | ||||
|  | ||||
|     .inputs        = (AVFilterPad[]) {{ .name            = "default", | ||||
|                                         .type            = AVMEDIA_TYPE_AUDIO, | ||||
|                                         .filter_samples  = ff_null_filter_samples }, | ||||
|                                         .type            = AVMEDIA_TYPE_AUDIO, }, | ||||
|                                       { .name = NULL}}, | ||||
|     .outputs       = (AVFilterPad[]) {{ .name            = "default", | ||||
|                                         .type            = AVMEDIA_TYPE_AUDIO}, | ||||
|   | ||||
| @@ -169,7 +169,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|  | ||||
|     for (i = 0; i < am->nb_inputs; i++) | ||||
|         if (!am->in[i].nb_samples) | ||||
|             if ((ret = avfilter_request_frame(ctx->inputs[i])) < 0) | ||||
|             if ((ret = ff_request_frame(ctx->inputs[i])) < 0) | ||||
|                 return ret; | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -34,8 +34,7 @@ AVFilter avfilter_af_anull = { | ||||
|  | ||||
|     .inputs    = (const AVFilterPad[]) {{ .name       = "default", | ||||
|                                     .type             = AVMEDIA_TYPE_AUDIO, | ||||
|                                     .get_audio_buffer = ff_null_get_audio_buffer, | ||||
|                                     .filter_samples   = ff_null_filter_samples }, | ||||
|                                     .get_audio_buffer = ff_null_get_audio_buffer, }, | ||||
|                                   { .name = NULL}}, | ||||
|  | ||||
|     .outputs   = (const AVFilterPad[]) {{ .name       = "default", | ||||
|   | ||||
| @@ -112,7 +112,7 @@ static int query_formats(AVFilterContext *ctx) | ||||
|     if(out_format != AV_SAMPLE_FMT_NONE) { | ||||
|         out_formats = ff_make_format_list((int[]){ out_format, -1 }); | ||||
|     } else | ||||
|         out_formats = avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO); | ||||
|         out_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO); | ||||
|     ff_formats_ref(out_formats, &outlink->in_formats); | ||||
|  | ||||
|     if(out_layout) { | ||||
| @@ -211,7 +211,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|  | ||||
|     aresample->req_fullfilled = 0; | ||||
|     do{ | ||||
|         ret = avfilter_request_frame(ctx->inputs[0]); | ||||
|         ret = ff_request_frame(ctx->inputs[0]); | ||||
|     }while(!aresample->req_fullfilled && ret>=0); | ||||
|  | ||||
|     if (ret == AVERROR_EOF) { | ||||
|   | ||||
| @@ -164,7 +164,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|  | ||||
|     asns->req_fullfilled = 0; | ||||
|     do { | ||||
|         ret = avfilter_request_frame(inlink); | ||||
|         ret = ff_request_frame(inlink); | ||||
|     } while (!asns->req_fullfilled && ret >= 0); | ||||
|  | ||||
|     if (ret == AVERROR_EOF) | ||||
|   | ||||
| @@ -157,7 +157,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|             send_next(ctx); | ||||
|         } else { | ||||
|             as->eof |= 1 << as->next_out; | ||||
|             avfilter_request_frame(ctx->inputs[as->next_out]); | ||||
|             ff_request_frame(ctx->inputs[as->next_out]); | ||||
|             if (as->eof & (1 << as->next_out)) | ||||
|                 as->next_out = !as->next_out; | ||||
|         } | ||||
|   | ||||
| @@ -1083,7 +1083,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|  | ||||
|     atempo->request_fulfilled = 0; | ||||
|     do { | ||||
|         ret = avfilter_request_frame(ctx->inputs[0]); | ||||
|         ret = ff_request_frame(ctx->inputs[0]); | ||||
|     } | ||||
|     while (!atempo->request_fulfilled && ret >= 0); | ||||
|  | ||||
|   | ||||
							
								
								
									
										402
									
								
								libavfilter/af_channelmap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										402
									
								
								libavfilter/af_channelmap.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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 }}, | ||||
| }; | ||||
							
								
								
									
										500
									
								
								libavfilter/af_join.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										500
									
								
								libavfilter/af_join.c
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
|     /* libswr supports any sample and packing formats */ | ||||
|     ff_set_common_formats(ctx, avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO)); | ||||
|     ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO)); | ||||
|  | ||||
|     formats = ff_all_samplerates(); | ||||
|     if (!formats) | ||||
|   | ||||
| @@ -47,8 +47,10 @@ void avfilter_register_all(void) | ||||
|     REGISTER_FILTER (ASTREAMSYNC, astreamsync, af); | ||||
|     REGISTER_FILTER (ASYNCTS,     asyncts,     af); | ||||
|     REGISTER_FILTER (ATEMPO,      atempo,      af); | ||||
|     REGISTER_FILTER (CHANNELMAP,  channelmap,  af); | ||||
|     REGISTER_FILTER (CHANNELSPLIT,channelsplit,af); | ||||
|     REGISTER_FILTER (EARWAX,      earwax,      af); | ||||
|     REGISTER_FILTER (JOIN,        join,        af); | ||||
|     REGISTER_FILTER (PAN,         pan,         af); | ||||
|     REGISTER_FILTER (SILENCEDETECT, silencedetect, af); | ||||
|     REGISTER_FILTER (VOLUME,      volume,      af); | ||||
|   | ||||
| @@ -150,32 +150,12 @@ fail: | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void ff_null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) | ||||
| static void default_filter_samples(AVFilterLink *link, | ||||
|                                    AVFilterBufferRef *samplesref) | ||||
| { | ||||
|     ff_filter_samples(link->dst->outputs[0], samplesref); | ||||
| } | ||||
|  | ||||
| /* FIXME: samplesref is same as link->cur_buf. Need to consider removing the redundant parameter. */ | ||||
| void ff_default_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *samplesref) | ||||
| { | ||||
|     AVFilterLink *outlink = NULL; | ||||
|  | ||||
|     if (inlink->dst->nb_outputs) | ||||
|         outlink = inlink->dst->outputs[0]; | ||||
|  | ||||
|     if (outlink) { | ||||
|         outlink->out_buf = ff_default_get_audio_buffer(inlink, AV_PERM_WRITE, | ||||
|                                                        samplesref->audio->nb_samples); | ||||
|         outlink->out_buf->pts                = samplesref->pts; | ||||
|         outlink->out_buf->audio->sample_rate = samplesref->audio->sample_rate; | ||||
|         ff_filter_samples(outlink, avfilter_ref_buffer(outlink->out_buf, ~0)); | ||||
|         avfilter_unref_buffer(outlink->out_buf); | ||||
|         outlink->out_buf = NULL; | ||||
|     } | ||||
|     avfilter_unref_buffer(samplesref); | ||||
|     inlink->cur_buf = NULL; | ||||
| } | ||||
|  | ||||
| void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) | ||||
| { | ||||
|     void (*filter_samples)(AVFilterLink *, AVFilterBufferRef *); | ||||
| @@ -186,7 +166,7 @@ void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) | ||||
|     FF_TPRINTF_START(NULL, filter_samples); ff_tlog_link(NULL, link, 1); | ||||
|  | ||||
|     if (!(filter_samples = dst->filter_samples)) | ||||
|         filter_samples = ff_default_filter_samples; | ||||
|         filter_samples = default_filter_samples; | ||||
|  | ||||
|     /* prepare to copy the samples if the buffer has insufficient permissions */ | ||||
|     if ((dst->min_perms & samplesref->perms) != dst->min_perms || | ||||
|   | ||||
| @@ -63,12 +63,6 @@ AVFilterBufferRef *ff_null_get_audio_buffer(AVFilterLink *link, int perms, | ||||
| AVFilterBufferRef *ff_get_audio_buffer(AVFilterLink *link, int perms, | ||||
|                                              int nb_samples); | ||||
|  | ||||
| /** default handler for filter_samples() for audio inputs */ | ||||
| void ff_default_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref); | ||||
|  | ||||
| /** filter_samples() handler for filters which simply pass audio along */ | ||||
| void ff_null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref); | ||||
|  | ||||
| /** | ||||
|  * Send a buffer of audio samples to the next filter. | ||||
|  * | ||||
|   | ||||
| @@ -170,7 +170,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|  | ||||
|     showwaves->req_fullfilled = 0; | ||||
|     do { | ||||
|         ret = avfilter_request_frame(inlink); | ||||
|         ret = ff_request_frame(inlink); | ||||
|     } while (!showwaves->req_fullfilled && ret >= 0); | ||||
|  | ||||
|     if (ret == AVERROR_EOF && showwaves->outpicref) | ||||
|   | ||||
| @@ -211,7 +211,6 @@ AVFilterBufferRef *avfilter_ref_buffer(AVFilterBufferRef *ref, int pmask); | ||||
|  */ | ||||
| void avfilter_unref_buffer(AVFilterBufferRef *ref); | ||||
|  | ||||
| #if FF_API_FILTERS_PUBLIC | ||||
| /** | ||||
|  * Remove a reference to a buffer and set the pointer to NULL. | ||||
|  * If this is the last reference to the buffer, the buffer itself | ||||
| @@ -221,6 +220,7 @@ void avfilter_unref_buffer(AVFilterBufferRef *ref); | ||||
|  */ | ||||
| void avfilter_unref_bufferp(AVFilterBufferRef **ref); | ||||
|  | ||||
| #if FF_API_FILTERS_PUBLIC | ||||
| /** | ||||
|  * A list of supported formats for one end of a filter link. This is used | ||||
|  * during the format negotiation process to try to pick the best format to | ||||
| @@ -291,7 +291,7 @@ AVFilterFormats *avfilter_make_format_list(const int *fmts); | ||||
|  * | ||||
|  * @return a non negative value in case of success, or a negative | ||||
|  * value corresponding to an AVERROR code in case of error | ||||
|  * @deprecated Use avfilter_make_all_formats() instead. | ||||
|  * @deprecated Use ff_all_formats() instead. | ||||
|  */ | ||||
| attribute_deprecated | ||||
| int avfilter_add_format(AVFilterFormats **avff, int64_t fmt); | ||||
| @@ -479,7 +479,7 @@ struct AVFilterPad { | ||||
|      * Frame request callback. A call to this should result in at least one | ||||
|      * frame being output over the given link. This should return zero on | ||||
|      * success, and another value on error. | ||||
|      * See avfilter_request_frame() for the error codes with a specific | ||||
|      * See ff_request_frame() for the error codes with a specific | ||||
|      * meaning. | ||||
|      * | ||||
|      * Output pads only. | ||||
| @@ -504,6 +504,14 @@ struct AVFilterPad { | ||||
|      * and another value on error. | ||||
|      */ | ||||
|     int (*config_props)(AVFilterLink *link); | ||||
|  | ||||
|     /** | ||||
|      * The filter expects a fifo to be inserted on its input link, | ||||
|      * typically because it has a delay. | ||||
|      * | ||||
|      * input pads only. | ||||
|      */ | ||||
|     int needs_fifo; | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| @@ -529,6 +537,10 @@ const char *avfilter_pad_get_name(AVFilterPad *pads, int pad_idx); | ||||
|  */ | ||||
| enum AVMediaType avfilter_pad_get_type(AVFilterPad *pads, int pad_idx); | ||||
|  | ||||
| /** default handler for end_frame() for video inputs */ | ||||
| attribute_deprecated | ||||
| void avfilter_default_end_frame(AVFilterLink *link); | ||||
|  | ||||
| #if FF_API_FILTERS_PUBLIC | ||||
| /** default handler for start_frame() for video inputs */ | ||||
| attribute_deprecated | ||||
| @@ -538,10 +550,6 @@ void avfilter_default_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) | ||||
| attribute_deprecated | ||||
| void avfilter_default_draw_slice(AVFilterLink *link, int y, int h, int slice_dir); | ||||
|  | ||||
| /** default handler for end_frame() for video inputs */ | ||||
| attribute_deprecated | ||||
| void avfilter_default_end_frame(AVFilterLink *link); | ||||
|  | ||||
| /** default handler for get_video_buffer() for video inputs */ | ||||
| attribute_deprecated | ||||
| AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, | ||||
| @@ -756,6 +764,15 @@ struct AVFilterLink { | ||||
|     struct AVFilterChannelLayouts  *in_channel_layouts; | ||||
|     struct AVFilterChannelLayouts *out_channel_layouts; | ||||
|  | ||||
|     /** | ||||
|      * Audio only, the destination filter sets this to a non-zero value to | ||||
|      * request that buffers with the given number of samples should be sent to | ||||
|      * it. AVFilterPad.needs_fifo must also be set on the corresponding input | ||||
|      * pad. | ||||
|      * Last buffer before EOF will be padded with silence. | ||||
|      */ | ||||
|     int request_samples; | ||||
|  | ||||
|     struct AVFilterPool *pool; | ||||
|  | ||||
|     /** | ||||
| @@ -785,7 +802,6 @@ struct AVFilterLink { | ||||
|      * It is similar to the r_frae_rate field in AVStream. | ||||
|      */ | ||||
|     AVRational frame_rate; | ||||
|  | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -187,7 +187,7 @@ static int filter_query_formats(AVFilterContext *ctx) | ||||
|     if ((ret = ctx->filter->query_formats(ctx)) < 0) | ||||
|         return ret; | ||||
|  | ||||
|     formats = avfilter_make_all_formats(type); | ||||
|     formats = ff_all_formats(type); | ||||
|     if (!formats) | ||||
|         return AVERROR(ENOMEM); | ||||
|     ff_set_common_formats(ctx, formats); | ||||
| @@ -815,12 +815,52 @@ static int ff_avfilter_graph_config_pointers(AVFilterGraph *graph, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int graph_insert_fifos(AVFilterGraph *graph, AVClass *log_ctx) | ||||
| { | ||||
|     AVFilterContext *f; | ||||
|     int i, j, ret; | ||||
|     int fifo_count = 0; | ||||
|  | ||||
|     for (i = 0; i < graph->filter_count; i++) { | ||||
|         f = graph->filters[i]; | ||||
|  | ||||
|         for (j = 0; j < f->nb_inputs; j++) { | ||||
|             AVFilterLink *link = f->inputs[j]; | ||||
|             AVFilterContext *fifo_ctx; | ||||
|             AVFilter *fifo; | ||||
|             char name[32]; | ||||
|  | ||||
|             if (!link->dstpad->needs_fifo) | ||||
|                 continue; | ||||
|  | ||||
|             fifo = f->inputs[j]->type == AVMEDIA_TYPE_VIDEO ? | ||||
|                    avfilter_get_by_name("fifo") : | ||||
|                    avfilter_get_by_name("afifo"); | ||||
|  | ||||
|             snprintf(name, sizeof(name), "auto-inserted fifo %d", fifo_count++); | ||||
|  | ||||
|             ret = avfilter_graph_create_filter(&fifo_ctx, fifo, name, NULL, | ||||
|                                                NULL, graph); | ||||
|             if (ret < 0) | ||||
|                 return ret; | ||||
|  | ||||
|             ret = avfilter_insert_filter(link, fifo_ctx, 0, 0); | ||||
|             if (ret < 0) | ||||
|                 return ret; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) | ||||
| { | ||||
|     int ret; | ||||
|  | ||||
|     if ((ret = graph_check_validity(graphctx, log_ctx))) | ||||
|         return ret; | ||||
|     if ((ret = graph_insert_fifos(graphctx, log_ctx)) < 0) | ||||
|         return ret; | ||||
|     if ((ret = graph_config_formats(graphctx, log_ctx))) | ||||
|         return ret; | ||||
|     if ((ret = graph_config_links(graphctx, log_ctx))) | ||||
| @@ -939,7 +979,7 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) | ||||
| { | ||||
|     while (graph->sink_links_count) { | ||||
|         AVFilterLink *oldest = graph->sink_links[0]; | ||||
|         int r = avfilter_request_frame(oldest); | ||||
|         int r = ff_request_frame(oldest); | ||||
|         if (r != AVERROR_EOF) | ||||
|             return r; | ||||
|         /* EOF: remove the link from the heap */ | ||||
|   | ||||
| @@ -257,7 +257,7 @@ char *avfilter_graph_dump(AVFilterGraph *graph, const char *options); | ||||
|  * of a filtergraph, only a convenience function to help drain a filtergraph | ||||
|  * in a balanced way under normal circumstances. | ||||
|  * | ||||
|  * @return  the return value of avfilter_request_frame, | ||||
|  * @return  the return value of ff_request_frame, | ||||
|  *          or AVERROR_EOF of all links returned AVERROR_EOF. | ||||
|  */ | ||||
| int avfilter_graph_request_oldest(AVFilterGraph *graph); | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
|  | ||||
| #include "libavutil/audio_fifo.h" | ||||
| #include "libavutil/audioconvert.h" | ||||
| #include "libavutil/fifo.h" | ||||
| #include "libavutil/avassert.h" | ||||
| #include "libavutil/mathematics.h" | ||||
|  | ||||
| #include "audio.h" | ||||
| @@ -34,86 +34,45 @@ | ||||
| #include "internal.h" | ||||
|  | ||||
| typedef struct { | ||||
|     AVFifoBuffer *fifo;          ///< FIFO buffer of frame references | ||||
|  | ||||
|     AVFilterBufferRef *cur_buf;  ///< last buffer delivered on the sink | ||||
|     AVAudioFifo  *audio_fifo;    ///< FIFO for audio samples | ||||
|     int64_t next_pts;            ///< interpolating audio pts | ||||
| } BufferSinkContext; | ||||
|  | ||||
| #define FIFO_INIT_SIZE 8 | ||||
|  | ||||
| static av_cold void uninit(AVFilterContext *ctx) | ||||
| { | ||||
|     BufferSinkContext *sink = ctx->priv; | ||||
|  | ||||
|     while (sink->fifo && av_fifo_size(sink->fifo)) { | ||||
|         AVFilterBufferRef *buf; | ||||
|         av_fifo_generic_read(sink->fifo, &buf, sizeof(buf), NULL); | ||||
|         avfilter_unref_buffer(buf); | ||||
|     } | ||||
|     av_fifo_free(sink->fifo); | ||||
|  | ||||
|     if (sink->audio_fifo) | ||||
|         av_audio_fifo_free(sink->audio_fifo); | ||||
| } | ||||
|  | ||||
| static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) | ||||
| static void start_frame(AVFilterLink *link, AVFilterBufferRef *buf) | ||||
| { | ||||
|     BufferSinkContext *sink = ctx->priv; | ||||
|     BufferSinkContext *s = link->dst->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) | ||||
| { | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     av_fifo_generic_write(sink->fifo, &buf, sizeof(buf), NULL); | ||||
| } | ||||
|  | ||||
| static void end_frame(AVFilterLink *link) | ||||
| { | ||||
|     write_buf(link->dst, link->cur_buf); | ||||
| //     av_assert0(!s->cur_buf); | ||||
|     s->cur_buf    = buf; | ||||
|     link->cur_buf = NULL; | ||||
| } | ||||
|  | ||||
| static void filter_samples(AVFilterLink *link, AVFilterBufferRef *buf) | ||||
| { | ||||
|     write_buf(link->dst, buf); | ||||
| } | ||||
| }; | ||||
|  | ||||
| int av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf) | ||||
| { | ||||
|     BufferSinkContext *sink = ctx->priv; | ||||
|     BufferSinkContext *s    = ctx->priv; | ||||
|     AVFilterLink      *link = ctx->inputs[0]; | ||||
|     int ret; | ||||
|  | ||||
|     if (!buf) { | ||||
|         if (av_fifo_size(sink->fifo)) | ||||
|             return av_fifo_size(sink->fifo)/sizeof(*buf); | ||||
|         else | ||||
|             return ff_poll_frame(ctx->inputs[0]); | ||||
|     } | ||||
|     if (!buf) | ||||
|         return ff_poll_frame(ctx->inputs[0]); | ||||
|  | ||||
|     if (!av_fifo_size(sink->fifo) && | ||||
|         (ret = ff_request_frame(link)) < 0) | ||||
|     if ((ret = ff_request_frame(link)) < 0) | ||||
|         return ret; | ||||
|  | ||||
|     if (!av_fifo_size(sink->fifo)) | ||||
|     if (!s->cur_buf) | ||||
|         return AVERROR(EINVAL); | ||||
|  | ||||
|     av_fifo_generic_read(sink->fifo, buf, sizeof(*buf), NULL); | ||||
|     *buf       = s->cur_buf; | ||||
|     s->cur_buf = NULL; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
| @@ -182,13 +141,13 @@ AVFilter avfilter_vsink_buffer = { | ||||
|     .name      = "buffersink_old", | ||||
|     .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), | ||||
|     .priv_size = sizeof(BufferSinkContext), | ||||
|     .init      = init, | ||||
|     .uninit    = uninit, | ||||
|  | ||||
|     .inputs    = (AVFilterPad[]) {{ .name          = "default", | ||||
|                                     .type          = AVMEDIA_TYPE_VIDEO, | ||||
|                                     .end_frame     = end_frame, | ||||
|                                     .min_perms     = AV_PERM_READ, }, | ||||
|                                     .start_frame   = start_frame, | ||||
|                                     .min_perms     = AV_PERM_READ, | ||||
|                                     .needs_fifo    = 1 }, | ||||
|                                   { .name = NULL }}, | ||||
|     .outputs   = (AVFilterPad[]) {{ .name = NULL }}, | ||||
| }; | ||||
| @@ -197,13 +156,13 @@ AVFilter avfilter_asink_abuffer = { | ||||
|     .name      = "abuffersink_old", | ||||
|     .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), | ||||
|     .priv_size = sizeof(BufferSinkContext), | ||||
|     .init      = init, | ||||
|     .uninit    = uninit, | ||||
|  | ||||
|     .inputs    = (AVFilterPad[]) {{ .name           = "default", | ||||
|                                     .type           = AVMEDIA_TYPE_AUDIO, | ||||
|                                     .filter_samples = filter_samples, | ||||
|                                     .min_perms      = AV_PERM_READ, }, | ||||
|                                     .filter_samples = start_frame, | ||||
|                                     .min_perms      = AV_PERM_READ, | ||||
|                                     .needs_fifo     = 1 }, | ||||
|                                   { .name = NULL }}, | ||||
|     .outputs   = (AVFilterPad[]) {{ .name = NULL }}, | ||||
| }; | ||||
|   | ||||
| @@ -47,27 +47,6 @@ static void set_common_formats(AVFilterContext *ctx, AVFilterFormats *fmts, | ||||
|     } | ||||
| } | ||||
|  | ||||
| void avfilter_set_common_pixel_formats(AVFilterContext *ctx, AVFilterFormats *formats) | ||||
| { | ||||
|     set_common_formats(ctx, formats, AVMEDIA_TYPE_VIDEO, | ||||
|                        offsetof(AVFilterLink, in_formats), | ||||
|                        offsetof(AVFilterLink, out_formats)); | ||||
| } | ||||
|  | ||||
| void avfilter_set_common_sample_formats(AVFilterContext *ctx, AVFilterFormats *formats) | ||||
| { | ||||
|     set_common_formats(ctx, formats, AVMEDIA_TYPE_AUDIO, | ||||
|                        offsetof(AVFilterLink, in_formats), | ||||
|                        offsetof(AVFilterLink, out_formats)); | ||||
| } | ||||
|  | ||||
| void avfilter_set_common_channel_layouts(AVFilterContext *ctx, AVFilterFormats *formats) | ||||
| { | ||||
|     set_common_formats(ctx, formats, AVMEDIA_TYPE_AUDIO, | ||||
|                        offsetof(AVFilterLink, in_channel_layouts), | ||||
|                        offsetof(AVFilterLink, out_channel_layouts)); | ||||
| } | ||||
|  | ||||
| #if FF_API_PACKING | ||||
| void avfilter_set_common_packing_formats(AVFilterContext *ctx, AVFilterFormats *formats) | ||||
| { | ||||
|   | ||||
| @@ -23,6 +23,11 @@ | ||||
|  * FIFO buffering filter | ||||
|  */ | ||||
|  | ||||
| #include "libavutil/avassert.h" | ||||
| #include "libavutil/audioconvert.h" | ||||
| #include "libavutil/mathematics.h" | ||||
| #include "libavutil/samplefmt.h" | ||||
|  | ||||
| #include "audio.h" | ||||
| #include "avfilter.h" | ||||
| #include "internal.h" | ||||
| @@ -36,6 +41,13 @@ typedef struct Buf { | ||||
| typedef struct { | ||||
|     Buf  root; | ||||
|     Buf *last;   ///< last buffered frame | ||||
|  | ||||
|     /** | ||||
|      * When a specific number of output samples is requested, the partial | ||||
|      * buffer is stored here | ||||
|      */ | ||||
|     AVFilterBufferRef *buf_out; | ||||
|     int allocated_samples;      ///< number of samples buf_out was allocated for | ||||
| } FifoContext; | ||||
|  | ||||
| static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) | ||||
| @@ -57,6 +69,8 @@ static av_cold void uninit(AVFilterContext *ctx) | ||||
|         avfilter_unref_buffer(buf->buf); | ||||
|         av_free(buf); | ||||
|     } | ||||
|  | ||||
|     avfilter_unref_buffer(fifo->buf_out); | ||||
| } | ||||
|  | ||||
| static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) | ||||
| @@ -68,14 +82,143 @@ static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) | ||||
|     fifo->last->buf = buf; | ||||
| } | ||||
|  | ||||
| static void queue_pop(FifoContext *s) | ||||
| { | ||||
|     Buf *tmp = s->root.next->next; | ||||
|     if (s->last == s->root.next) | ||||
|         s->last = &s->root; | ||||
|     av_freep(&s->root.next); | ||||
|     s->root.next = tmp; | ||||
| } | ||||
|  | ||||
| static void end_frame(AVFilterLink *inlink) { } | ||||
|  | ||||
| static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { } | ||||
|  | ||||
| /** | ||||
|  * Move data pointers and pts offset samples forward. | ||||
|  */ | ||||
| static void buffer_offset(AVFilterLink *link, AVFilterBufferRef *buf, | ||||
|                           int offset) | ||||
| { | ||||
|     int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); | ||||
|     int planar = av_sample_fmt_is_planar(link->format); | ||||
|     int planes = planar ? nb_channels : 1; | ||||
|     int block_align = av_get_bytes_per_sample(link->format) * (planar ? 1 : nb_channels); | ||||
|     int i; | ||||
|  | ||||
|     av_assert0(buf->audio->nb_samples > offset); | ||||
|  | ||||
|     for (i = 0; i < planes; i++) | ||||
|         buf->extended_data[i] += block_align*offset; | ||||
|     if (buf->data != buf->extended_data) | ||||
|         memcpy(buf->data, buf->extended_data, | ||||
|                FFMIN(planes, FF_ARRAY_ELEMS(buf->data)) * sizeof(*buf->data)); | ||||
|     buf->linesize[0] -= block_align*offset; | ||||
|     buf->audio->nb_samples -= offset; | ||||
|  | ||||
|     if (buf->pts != AV_NOPTS_VALUE) { | ||||
|         buf->pts += av_rescale_q(offset, (AVRational){1, link->sample_rate}, | ||||
|                                  link->time_base); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int calc_ptr_alignment(AVFilterBufferRef *buf) | ||||
| { | ||||
|     int planes = av_sample_fmt_is_planar(buf->format) ? | ||||
|                  av_get_channel_layout_nb_channels(buf->audio->channel_layout) : 1; | ||||
|     int min_align = 128; | ||||
|     int p; | ||||
|  | ||||
|     for (p = 0; p < planes; p++) { | ||||
|         int cur_align = 128; | ||||
|         while ((intptr_t)buf->extended_data[p] % cur_align) | ||||
|             cur_align >>= 1; | ||||
|         if (cur_align < min_align) | ||||
|             min_align = cur_align; | ||||
|     } | ||||
|     return min_align; | ||||
| } | ||||
|  | ||||
| static int return_audio_frame(AVFilterContext *ctx) | ||||
| { | ||||
|     AVFilterLink *link = ctx->outputs[0]; | ||||
|     FifoContext *s = ctx->priv; | ||||
|     AVFilterBufferRef *head = s->root.next->buf; | ||||
|     AVFilterBufferRef *buf_out; | ||||
|     int ret; | ||||
|  | ||||
|     if (!s->buf_out && | ||||
|         head->audio->nb_samples >= link->request_samples && | ||||
|         calc_ptr_alignment(head) >= 32) { | ||||
|         if (head->audio->nb_samples == link->request_samples) { | ||||
|             buf_out = head; | ||||
|             queue_pop(s); | ||||
|         } else { | ||||
|             buf_out = avfilter_ref_buffer(head, AV_PERM_READ); | ||||
|             buf_out->audio->nb_samples = link->request_samples; | ||||
|             buffer_offset(link, head, link->request_samples); | ||||
|         } | ||||
|     } else { | ||||
|         int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); | ||||
|  | ||||
|         if (!s->buf_out) { | ||||
|             s->buf_out = ff_get_audio_buffer(link, AV_PERM_WRITE, | ||||
|                                              link->request_samples); | ||||
|             if (!s->buf_out) | ||||
|                 return AVERROR(ENOMEM); | ||||
|  | ||||
|             s->buf_out->audio->nb_samples = 0; | ||||
|             s->buf_out->pts               = head->pts; | ||||
|             s->allocated_samples          = link->request_samples; | ||||
|         } else if (link->request_samples != s->allocated_samples) { | ||||
|             av_log(ctx, AV_LOG_ERROR, "request_samples changed before the " | ||||
|                    "buffer was returned.\n"); | ||||
|             return AVERROR(EINVAL); | ||||
|         } | ||||
|  | ||||
|         while (s->buf_out->audio->nb_samples < s->allocated_samples) { | ||||
|             int len = FFMIN(s->allocated_samples - s->buf_out->audio->nb_samples, | ||||
|                             head->audio->nb_samples); | ||||
|  | ||||
|             av_samples_copy(s->buf_out->extended_data, head->extended_data, | ||||
|                             s->buf_out->audio->nb_samples, 0, len, nb_channels, | ||||
|                             link->format); | ||||
|             s->buf_out->audio->nb_samples += len; | ||||
|  | ||||
|             if (len == head->audio->nb_samples) { | ||||
|                 avfilter_unref_buffer(head); | ||||
|                 queue_pop(s); | ||||
|  | ||||
|                 if (!s->root.next && | ||||
|                     (ret = ff_request_frame(ctx->inputs[0])) < 0) { | ||||
|                     if (ret == AVERROR_EOF) { | ||||
|                         av_samples_set_silence(s->buf_out->extended_data, | ||||
|                                                s->buf_out->audio->nb_samples, | ||||
|                                                s->allocated_samples - | ||||
|                                                s->buf_out->audio->nb_samples, | ||||
|                                                nb_channels, link->format); | ||||
|                         s->buf_out->audio->nb_samples = s->allocated_samples; | ||||
|                         break; | ||||
|                     } | ||||
|                     return ret; | ||||
|                 } | ||||
|                 head = s->root.next->buf; | ||||
|             } else { | ||||
|                 buffer_offset(link, head, len); | ||||
|             } | ||||
|         } | ||||
|         buf_out = s->buf_out; | ||||
|         s->buf_out = NULL; | ||||
|     } | ||||
|     ff_filter_samples(link, buf_out); | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int request_frame(AVFilterLink *outlink) | ||||
| { | ||||
|     FifoContext *fifo = outlink->src->priv; | ||||
|     Buf *tmp; | ||||
|     int ret; | ||||
|  | ||||
|     if (!fifo->root.next) { | ||||
| @@ -90,20 +233,20 @@ static int request_frame(AVFilterLink *outlink) | ||||
|         ff_start_frame(outlink, fifo->root.next->buf); | ||||
|         ff_draw_slice (outlink, 0, outlink->h, 1); | ||||
|         ff_end_frame  (outlink); | ||||
|         queue_pop(fifo); | ||||
|         break; | ||||
|     case AVMEDIA_TYPE_AUDIO: | ||||
|         ff_filter_samples(outlink, fifo->root.next->buf); | ||||
|         if (outlink->request_samples) { | ||||
|             return return_audio_frame(outlink->src); | ||||
|         } else { | ||||
|             ff_filter_samples(outlink, fifo->root.next->buf); | ||||
|             queue_pop(fifo); | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         return AVERROR(EINVAL); | ||||
|     } | ||||
|  | ||||
|     if (fifo->last == fifo->root.next) | ||||
|         fifo->last = &fifo->root; | ||||
|     tmp = fifo->root.next->next; | ||||
|     av_free(fifo->root.next); | ||||
|     fifo->root.next = tmp; | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -261,11 +261,6 @@ int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) | ||||
| } | ||||
|  | ||||
| AVFilterFormats *ff_all_formats(enum AVMediaType type) | ||||
| { | ||||
|     return avfilter_make_all_formats(type); | ||||
| } | ||||
|  | ||||
| AVFilterFormats *avfilter_make_all_formats(enum AVMediaType type) | ||||
| { | ||||
|     AVFilterFormats *ret = NULL; | ||||
|     int fmt; | ||||
|   | ||||
| @@ -173,6 +173,14 @@ struct AVFilterPad { | ||||
|      * and another value on error. | ||||
|      */ | ||||
|     int (*config_props)(AVFilterLink *link); | ||||
|  | ||||
|     /** | ||||
|      * The filter expects a fifo to be inserted on its input link, | ||||
|      * typically because it has a delay. | ||||
|      * | ||||
|      * input pads only. | ||||
|      */ | ||||
|     int needs_fifo; | ||||
| }; | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -94,7 +94,7 @@ static const uint8_t offset[511][2]= { | ||||
| { 7, 1}, {15, 1}, { 7, 9}, {15, 9}, { 7, 3}, {15, 3}, { 7,11}, {15,11}, | ||||
| { 7, 5}, {15, 5}, { 7,13}, {15,13}, { 7, 7}, {15, 7}, { 7,15}, {15,15}, | ||||
|  | ||||
| { 0, 0}, { 8, 0}, { 0, 8}, { 8, 8}, { 4, 4}, {12, 4}, { 4,12}, {12,12}, { 0, 4}, { 8, 4}, { 0,12}, { 8,12}, { 4, 0}, {12, 0}, { 4, 8}, {12, 8}, { 2, 2}, {10, 2}, { 2,10}, {10,10}, { 6, 6}, {14, 6}, { 6,14}, {14,14}, { 2, 6}, {10, 6}, { 2,14}, {10,14}, { 6, 2}, {14, 2}, { 6,10}, {14,10}, { 0, 2}, { 8, 2}, { 0,10}, { 8,10}, { 4, 6}, {12, 6}, { 4,14}, {12,14}, { 0, 6}, { 8, 6}, { 0,14}, { 8,14}, { 4, 2}, {12, 2}, { 4,10}, {12,10}, { 2, 0}, {10, 0}, { 2, 8}, {10, 8}, { 6, 4}, {14, 4}, { 6,12}, {14,12}, { 2, 4}, {10, 4}, { 2,12}, {10,12}, { 6, 0}, {14, 0}, { 6, 8}, {14, 8}, { 1, 1}, { 9, 1}, { 1, 9}, { 9, 9}, { 5, 5}, {13, 5}, { 5,13}, {13,13}, { 1, 5}, { 9, 5}, { 1,13}, { 9,13}, { 5, 1}, {13, 1}, { 5, 9}, {13, 9}, { 3, 3}, {11, 3}, { 3,11}, {11,11}, { 7, 7}, {15, 7}, { 7,15}, {15,15}, { 3, 7}, {11, 7}, { 3,15}, {11,15}, { 7, 3}, {15, 3}, { 7,11}, {15,11}, { 1, 3}, { 9, 3}, { 1,11}, { 9,11}, { 5, 7}, {13, 7}, { 5,15}, {13,15}, { 1, 7}, { 9, 7}, { 1,15}, { 9,15}, { 5, 3}, {13, 3}, { 5,11}, {13,11}, { 3, 1}, {11, 1}, { 3, 9}, {11, 9}, { 7, 5}, {15, 5}, { 7,13}, {15,13}, { 3, 5}, {11, 5}, { 3,13}, {11,13}, { 7, 1}, {15, 1}, { 7, 9}, {15, 9}, { 0, 1}, { 8, 1}, { 0, 9}, { 8, 9}, { 4, 5}, {12, 5}, { 4,13}, {12,13}, { 0, 5}, { 8, 5}, { 0,13}, { 8,13}, { 4, 1}, {12, 1}, { 4, 9}, {12, 9}, { 2, 3}, {10, 3}, { 2,11}, {10,11}, { 6, 7}, {14, 7}, { 6,15}, {14,15}, { 2, 7}, {10, 7}, { 2,15}, {10,15}, { 6, 3}, {14, 3}, { 6,11}, {14,11}, { 0, 3}, { 8, 3}, { 0,11}, { 8,11}, { 4, 7}, {12, 7}, { 4,15}, {12,15}, { 0, 7}, { 8, 7}, { 0,15}, { 8,15}, { 4, 3}, {12, 3}, { 4,11}, {12,11}, { 2, 1}, {10, 1}, { 2, 9}, {10, 9}, { 6, 5}, {14, 5}, { 6,13}, {14,13}, { 2, 5}, {10, 5}, { 2,13}, {10,13}, { 6, 1}, {14, 1}, { 6, 9}, {14, 9}, { 1, 0}, { 9, 0}, { 1, 8}, { 9, 8}, { 5, 4}, {13, 4}, { 5,12}, {13,12}, { 1, 4}, { 9, 4}, { 1,12}, { 9,12}, { 5, 0}, {13, 0}, { 5, 8}, {13, 8}, { 3, 2}, {11, 2}, { 3,10}, {11,10}, { 7, 6}, {15, 6}, { 7,14}, {15,14}, { 3, 6}, {11, 6}, { 3,14}, {11,14}, { 7, 2}, {15, 2}, { 7,10}, {15,10}, { 1, 2}, { 9, 2}, { 1,10}, { 9,10}, { 5, 6}, {13, 6}, { 5,14}, {13,14}, { 1, 6}, { 9, 6}, { 1,14}, { 9,14}, { 5, 2}, {13, 2}, { 5,10}, {13,10}, { 3, 0}, {11, 0}, { 3, 8}, {11, 8}, { 7, 4}, {15, 4}, { 7,12}, {15,12}, { 3, 4}, {11, 4}, { 3,12}, {11,12}, { 7, 0}, {15, 0}, { 7, 8}, {15, 8}, | ||||
| { 0, 0}, { 8, 0}, { 0, 8}, { 8, 8}, { 4, 4}, {12, 4}, { 4,12}, {12,12}, { 0, 4}, { 8, 4}, { 0,12}, { 8,12}, { 4, 0}, {12, 0}, { 4, 8}, {12, 8}, { 2, 2}, {10, 2}, { 2,10}, {10,10}, { 6, 6}, {14, 6}, { 6,14}, {14,14}, { 2, 6}, {10, 6}, { 2,14}, {10,14}, { 6, 2}, {14, 2}, { 6,10}, {14,10}, { 0, 2}, { 8, 2}, { 0,10}, { 8,10}, { 4, 6}, {12, 6}, { 4,14}, {12,14}, { 0, 6}, { 8, 6}, { 0,14}, { 8,14}, { 4, 2}, {12, 2}, { 4,10}, {12,10}, { 2, 0}, {10, 0}, { 2, 8}, {10, 8}, { 6, 4}, {14, 4}, { 6,12}, {14,12}, { 2, 4}, {10, 4}, { 2,12}, {10,12}, { 6, 0}, {14, 0}, { 6, 8}, {14, 8}, { 1, 1}, { 9, 1}, { 1, 9}, { 9, 9}, { 5, 5}, {13, 5}, { 5,13}, {13,13}, { 1, 5}, { 9, 5}, { 1,13}, { 9,13}, { 5, 1}, {13, 1}, { 5, 9}, {13, 9}, { 3, 3}, {11, 3}, { 3,11}, {11,11}, { 7, 7}, {15, 7}, { 7,15}, {15,15}, { 3, 7}, {11, 7}, { 3,15}, {11,15}, { 7, 3}, {15, 3}, { 7,11}, {15,11}, { 1, 3}, { 9, 3}, { 1,11}, { 9,11}, { 5, 7}, {13, 7}, { 5,15}, {13,15}, { 1, 7}, { 9, 7}, { 1,15}, { 9,15}, { 5, 3}, {13, 3}, { 5,11}, {13,11}, { 3, 1}, {11,1}, { 3, 9}, {11, 9}, { 7, 5}, {15, 5}, { 7,13}, {15,13}, { 3, 5}, {11, 5}, { 3,13}, {11,13}, { 7, 1}, {15, 1}, { 7, 9}, {15, 9}, { 0, 1}, { 8, 1}, { 0, 9}, { 8, 9}, { 4, 5}, {12, 5}, { 4,13}, {12,13}, { 0, 5}, { 8, 5}, { 0,13}, { 8,13}, { 4, 1}, {12, 1}, { 4, 9}, {12, 9}, { 2, 3}, {10, 3}, { 2,11}, {10,11}, { 6, 7}, {14, 7}, { 6,15}, {14,15}, { 2, 7}, {10, 7}, { 2,15}, {10,15}, { 6, 3}, {14, 3}, { 6,11}, {14,11}, { 0, 3}, { 8, 3}, { 0,11}, { 8,11}, { 4, 7}, {12, 7}, { 4,15}, {12,15}, { 0, 7}, { 8, 7}, { 0,15}, { 8,15}, { 4, 3}, {12, 3}, { 4,11}, {12,11}, { 2, 1}, {10, 1}, { 2, 9}, {10, 9}, { 6, 5}, {14, 5}, { 6,13}, {14,13}, { 2, 5}, {10, 5}, { 2,13}, {10,13}, { 6, 1}, {14, 1}, { 6, 9}, {14, 9}, { 1, 0}, { 9, 0}, { 1, 8}, { 9, 8}, { 5, 4}, {13, 4}, { 5,12}, {13,12}, { 1, 4}, { 9, 4}, { 1,12}, { 9,12}, { 5, 0}, {13, 0}, { 5, 8}, {13, 8}, { 3, 2}, {11, 2}, { 3,10}, {11,10}, { 7, 6}, {15, 6}, { 7,14}, {15,14}, { 3, 6}, {11, 6}, { 3,14}, {11,14}, { 7, 2}, {15, 2}, { 7,10}, {15,10}, { 1, 2}, { 9, 2}, { 1,10}, {9,10}, { 5, 6}, {13, 6}, { 5,14}, {13,14}, { 1, 6}, { 9, 6}, { 1,14}, { 9,14}, { 5, 2}, {13, 2}, { 5,10}, {13,10}, { 3, 0}, {11, 0}, { 3, 8}, {11, 8}, { 7, 4}, {15, 4}, { 7,12}, {15,12}, { 3, 4}, {11, 4}, { 3,12}, {11,12}, { 7, 0}, {15, 0}, { 7, 8}, {15, 8}, | ||||
| }; | ||||
|  | ||||
| struct vf_priv_s { | ||||
|   | ||||
| @@ -127,7 +127,7 @@ int av_buffersink_get_buffer_ref(AVFilterContext *ctx, | ||||
|     if (!av_fifo_size(buf->fifo)) { | ||||
|         if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST) | ||||
|             return AVERROR(EAGAIN); | ||||
|         if ((ret = avfilter_request_frame(inlink)) < 0) | ||||
|         if ((ret = ff_request_frame(inlink)) < 0) | ||||
|             return ret; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -28,8 +28,8 @@ | ||||
|  | ||||
| #include "libavutil/avutil.h" | ||||
|  | ||||
| #define LIBAVFILTER_VERSION_MAJOR  2 | ||||
| #define LIBAVFILTER_VERSION_MINOR 82 | ||||
| #define LIBAVFILTER_VERSION_MAJOR  3 | ||||
| #define LIBAVFILTER_VERSION_MINOR  0 | ||||
| #define LIBAVFILTER_VERSION_MICRO 100 | ||||
|  | ||||
| #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ | ||||
|   | ||||
| @@ -136,7 +136,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|     AVFilterContext *ctx = outlink->src; | ||||
|     BlackDetectContext *blackdetect = ctx->priv; | ||||
|     AVFilterLink *inlink = ctx->inputs[0]; | ||||
|     int ret = avfilter_request_frame(inlink); | ||||
|     int ret = ff_request_frame(inlink); | ||||
|  | ||||
|     if (ret == AVERROR_EOF && blackdetect->black_started) { | ||||
|         // FIXME: black_end should be set to last_picref_pts + last_picref_duration | ||||
|   | ||||
| @@ -216,7 +216,7 @@ static int request_frame(AVFilterLink *link) | ||||
|     do { | ||||
|         int ret; | ||||
|  | ||||
|         if ((ret = avfilter_request_frame(link->src->inputs[0]))) | ||||
|         if ((ret = ff_request_frame(link->src->inputs[0]))) | ||||
|             return ret; | ||||
|     } while (!idet->cur); | ||||
|  | ||||
| @@ -231,7 +231,7 @@ static int poll_frame(AVFilterLink *link) | ||||
|     val = ff_poll_frame(link->src->inputs[0]); | ||||
|  | ||||
|     if (val >= 1 && !idet->next) { //FIXME change API to not requre this red tape | ||||
|         if ((ret = avfilter_request_frame(link->src->inputs[0])) < 0) | ||||
|         if ((ret = ff_request_frame(link->src->inputs[0])) < 0) | ||||
|             return ret; | ||||
|         val = ff_poll_frame(link->src->inputs[0]); | ||||
|     } | ||||
|   | ||||
| @@ -25,6 +25,9 @@ | ||||
|  */ | ||||
|  | ||||
| #include "avfilter.h" | ||||
| #include "video.h" | ||||
| #include "formats.h" | ||||
| #include "internal.h" | ||||
| #include "libavutil/avassert.h" | ||||
| #include "libavutil/pixdesc.h" | ||||
| #include "libavutil/intreadwrite.h" | ||||
| @@ -633,9 +636,9 @@ int vf_next_put_image(struct vf_instance *vf,mp_image_t *mpi, double pts){ | ||||
|     if(pts != MP_NOPTS_VALUE) | ||||
|         picref->pts= pts * av_q2d(outlink->time_base); | ||||
|  | ||||
|     avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0)); | ||||
|     avfilter_draw_slice(outlink, 0, picref->video->h, 1); | ||||
|     avfilter_end_frame(outlink); | ||||
|     ff_start_frame(outlink, avfilter_ref_buffer(picref, ~0)); | ||||
|     ff_draw_slice(outlink, 0, picref->video->h, 1); | ||||
|     ff_end_frame(outlink); | ||||
|     avfilter_unref_buffer(picref); | ||||
|     m->frame_returned++; | ||||
|  | ||||
| @@ -788,14 +791,14 @@ static int query_formats(AVFilterContext *ctx) | ||||
|         if(m->vf.query_format(&m->vf, conversion_map[i].fmt)){ | ||||
|             av_log(ctx, AV_LOG_DEBUG, "supported,adding\n"); | ||||
|             if (conversion_map[i].pix_fmt != lastpixfmt) { | ||||
|                 avfilter_add_format(&avfmts, conversion_map[i].pix_fmt); | ||||
|                 ff_add_format(&avfmts, conversion_map[i].pix_fmt); | ||||
|                 lastpixfmt = conversion_map[i].pix_fmt; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     //We assume all allowed input formats are also allowed output formats | ||||
|     avfilter_set_common_pixel_formats(ctx, avfmts); | ||||
|     ff_set_common_formats(ctx, avfmts); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -836,7 +839,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|     av_log(m->avfctx, AV_LOG_DEBUG, "mp request_frame\n"); | ||||
|  | ||||
|     for(m->frame_returned=0; !m->frame_returned;){ | ||||
|         ret=avfilter_request_frame(outlink->src->inputs[0]); | ||||
|         ret=ff_request_frame(outlink->src->inputs[0]); | ||||
|         if(ret<0) | ||||
|             break; | ||||
|     } | ||||
|   | ||||
| @@ -178,7 +178,7 @@ static int request_frame(AVFilterLink *link) | ||||
|     /* loop until a frame thumbnail is available (when a frame is queued, | ||||
|      * thumb->n is reset to zero) */ | ||||
|     do { | ||||
|         int ret = avfilter_request_frame(link->src->inputs[0]); | ||||
|         int ret = ff_request_frame(link->src->inputs[0]); | ||||
|         if (ret < 0) | ||||
|             return ret; | ||||
|     } while (thumb->n); | ||||
| @@ -203,7 +203,7 @@ static int poll_frame(AVFilterLink *link) | ||||
|  | ||||
|     /* we have some frame(s) available in the input link, but not yet enough to | ||||
|      * output a thumbnail, so we request more */ | ||||
|     ret = avfilter_request_frame(inlink); | ||||
|     ret = ff_request_frame(inlink); | ||||
|     return ret < 0 ? ret : 0; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,7 @@ | ||||
| #include "drawutils.h" | ||||
| #include "formats.h" | ||||
| #include "video.h" | ||||
| #include "internal.h" | ||||
|  | ||||
| typedef struct { | ||||
|     unsigned w, h; | ||||
| @@ -170,7 +171,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|     int r; | ||||
|  | ||||
|     while (1) { | ||||
|         r = avfilter_request_frame(inlink); | ||||
|         r = ff_request_frame(inlink); | ||||
|         if (r < 0) { | ||||
|             if (r == AVERROR_EOF && tile->current) | ||||
|                 end_last_frame(ctx); | ||||
|   | ||||
| @@ -334,7 +334,7 @@ static int poll_frame(AVFilterLink *outlink) | ||||
|     val = ff_poll_frame(inlink); | ||||
|  | ||||
|     if (val == 1 && !tinterlace->next) { | ||||
|         if ((ret = avfilter_request_frame(inlink)) < 0) | ||||
|         if ((ret = ff_request_frame(inlink)) < 0) | ||||
|             return ret; | ||||
|         val = ff_poll_frame(inlink); | ||||
|     } | ||||
| @@ -351,7 +351,7 @@ static int request_frame(AVFilterLink *outlink) | ||||
|     do { | ||||
|         int ret; | ||||
|  | ||||
|         if ((ret = avfilter_request_frame(inlink)) < 0) | ||||
|         if ((ret = ff_request_frame(inlink)) < 0) | ||||
|             return ret; | ||||
|     } while (!tinterlace->cur); | ||||
|  | ||||
|   | ||||
| @@ -321,6 +321,11 @@ void ff_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) | ||||
|     draw_slice(link, y, h, slice_dir); | ||||
| } | ||||
|  | ||||
| void avfilter_default_end_frame(AVFilterLink *inlink) | ||||
| { | ||||
|     default_end_frame(inlink); | ||||
| } | ||||
|  | ||||
| #if FF_API_FILTERS_PUBLIC | ||||
| AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, int perms, int w, int h) | ||||
| { | ||||
| @@ -330,10 +335,6 @@ void avfilter_default_start_frame(AVFilterLink *inlink, AVFilterBufferRef *picre | ||||
| { | ||||
|     default_start_frame(inlink, picref); | ||||
| } | ||||
| void avfilter_default_end_frame(AVFilterLink *inlink) | ||||
| { | ||||
|     default_end_frame(inlink); | ||||
| } | ||||
| void avfilter_default_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) | ||||
| { | ||||
|     default_draw_slice(inlink, y, h, slice_dir); | ||||
|   | ||||
| @@ -49,7 +49,7 @@ void av_set_cpu_flags_mask(int mask) | ||||
|  | ||||
| int av_parse_cpu_flags(const char *s) | ||||
| { | ||||
| #define CPUFLAG_MMX2     (AV_CPU_FLAG_MMX      | AV_CPU_FLAG_MMX2) | ||||
| #define CPUFLAG_MMX2     (AV_CPU_FLAG_MMX      | AV_CPU_FLAG_MMX2 | AV_CPU_FLAG_CMOV) | ||||
| #define CPUFLAG_3DNOW    (AV_CPU_FLAG_3DNOW    | AV_CPU_FLAG_MMX) | ||||
| #define CPUFLAG_3DNOWEXT (AV_CPU_FLAG_3DNOWEXT | CPUFLAG_3DNOW) | ||||
| #define CPUFLAG_SSE      (AV_CPU_FLAG_SSE      | CPUFLAG_MMX2) | ||||
| @@ -84,6 +84,7 @@ int av_parse_cpu_flags(const char *s) | ||||
|         { "fma4"    , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_FMA4         },    .unit = "flags" }, | ||||
|         { "3dnow"   , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOW        },    .unit = "flags" }, | ||||
|         { "3dnowext", NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOWEXT     },    .unit = "flags" }, | ||||
|         { "cmov",     NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_CMOV     },    .unit = "flags" }, | ||||
| #elif ARCH_ARM | ||||
|         { "armv5te",  NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_ARMV5TE  },    .unit = "flags" }, | ||||
|         { "armv6",    NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_ARMV6    },    .unit = "flags" }, | ||||
| @@ -188,6 +189,7 @@ static const struct { | ||||
|     { AV_CPU_FLAG_FMA4,      "fma4"       }, | ||||
|     { AV_CPU_FLAG_3DNOW,     "3dnow"      }, | ||||
|     { AV_CPU_FLAG_3DNOWEXT,  "3dnowext"   }, | ||||
|     { AV_CPU_FLAG_CMOV,      "cmov"       }, | ||||
| #endif | ||||
|     { 0 } | ||||
| }; | ||||
|   | ||||
| @@ -40,9 +40,14 @@ | ||||
| #define AV_CPU_FLAG_SSE4         0x0100 ///< Penryn SSE4.1 functions | ||||
| #define AV_CPU_FLAG_SSE42        0x0200 ///< Nehalem SSE4.2 functions | ||||
| #define AV_CPU_FLAG_AVX          0x4000 ///< AVX functions: requires OS support even if YMM registers aren't used | ||||
| #define AV_CPU_FLAG_CMOV      0x1000000 ///< supports cmov instruction | ||||
| #define AV_CPU_FLAG_XOP          0x0400 ///< Bulldozer XOP functions | ||||
| #define AV_CPU_FLAG_FMA4         0x0800 ///< Bulldozer FMA4 functions | ||||
| #if LIBAVUTIL_VERSION_MAJOR <52 | ||||
| #define AV_CPU_FLAG_CMOV      0x1001000 ///< supports cmov instruction | ||||
| #else | ||||
| #define AV_CPU_FLAG_CMOV         0x1000 ///< supports cmov instruction | ||||
| #endif | ||||
|  | ||||
| #define AV_CPU_FLAG_ALTIVEC      0x0001 ///< standard | ||||
|  | ||||
| #define AV_CPU_FLAG_ARMV5TE      (1 << 0) | ||||
|   | ||||
| @@ -83,7 +83,7 @@ int ff_get_cpu_flags_x86(void) | ||||
|         cpuid(1, eax, ebx, ecx, std_caps); | ||||
|         family = ((eax>>8)&0xf) + ((eax>>20)&0xff); | ||||
|         model  = ((eax>>4)&0xf) + ((eax>>12)&0xf0); | ||||
|         if (std_caps & (1<<15)) | ||||
|         if (std_caps & (1 << 15)) | ||||
|             rval |= AV_CPU_FLAG_CMOV; | ||||
|         if (std_caps & (1<<23)) | ||||
|             rval |= AV_CPU_FLAG_MMX; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user