mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +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:
commit
1c60088885
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user