diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2012-05-16 02:27:31 +0200 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2012-05-16 02:27:31 +0200 |
commit | 1cbf7fb4345a3e5b7791d483241bf4759bde4ece (patch) | |
tree | d7acd8317309e051fb240e3505f77aabe2ea0437 /libavfilter | |
parent | a48abf5e263ad7f2e68821766e7cf4d29befb58e (diff) | |
parent | 0ff0af731ce4544f84b2f748dcc699717a2df8d6 (diff) | |
download | ffmpeg-1cbf7fb4345a3e5b7791d483241bf4759bde4ece.tar.gz |
Merge remote-tracking branch 'qatar/master'
* qatar/master: (26 commits)
fate: use diff -b in oneline comparison
Add missing version bumps and APIchanges/Changelog entries.
lavfi: move buffer management function to a separate file.
lavfi: move formats-related functions from default.c to formats.c
lavfi: move video-related functions to a separate file.
fate: make smjpeg a demux test
fate: separate sierra-vmd audio and video tests
fate: separate smacker audio and video tests
libmp3lame: set supported channel layouts.
avconv: automatically insert asyncts when -async is used.
avconv: add support for audio filters.
lavfi: add asyncts filter.
lavfi: add aformat filter
lavfi: add an audio buffer sink.
lavfi: add an audio buffer source.
buffersrc: add av_buffersrc_write_frame().
buffersrc: fix invalid read in uninit if the fifo hasn't been allocated
lavfi: rename vsrc_buffer.c to buffersrc.c
avfiltergraph: reindent
lavfi: add channel layout/sample rate negotiation.
...
Conflicts:
Changelog
doc/APIchanges
doc/filters.texi
ffmpeg.c
ffprobe.c
libavcodec/libmp3lame.c
libavfilter/Makefile
libavfilter/af_aformat.c
libavfilter/allfilters.c
libavfilter/avfilter.c
libavfilter/avfilter.h
libavfilter/avfiltergraph.c
libavfilter/buffersrc.c
libavfilter/defaults.c
libavfilter/formats.c
libavfilter/src_buffer.c
libavfilter/version.h
libavfilter/vf_yadif.c
libavfilter/vsrc_buffer.c
libavfilter/vsrc_buffer.h
libavutil/avutil.h
tests/fate/audio.mak
tests/fate/demux.mak
tests/fate/video.mak
Merged-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavfilter')
33 files changed, 2315 insertions, 835 deletions
diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 70f2c9e5ca..1f9d4117d1 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -2,6 +2,7 @@ include $(SUBDIR)../config.mak NAME = avfilter FFLIBS = avutil swscale +FFLIBS-$(CONFIG_ASYNCTS_FILTER) += avresample FFLIBS-$(CONFIG_RESAMPLE_FILTER) += avresample FFLIBS-$(CONFIG_ACONVERT_FILTER) += swresample @@ -25,6 +26,7 @@ OBJS = allfilters.o \ audio.o \ avfilter.o \ avfiltergraph.o \ + buffer.o \ buffersink.o \ defaults.o \ drawutils.o \ @@ -34,6 +36,8 @@ OBJS = allfilters.o \ src_buffer.o \ transform.o \ vf_scale.o \ + video.o \ + OBJS-$(CONFIG_AVCODEC) += avcodec.o OBJS-$(CONFIG_AVFORMAT) += lavfutils.o @@ -47,6 +51,7 @@ OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o OBJS-$(CONFIG_ASPLIT_FILTER) += af_asplit.o OBJS-$(CONFIG_ASTREAMSYNC_FILTER) += af_astreamsync.o +OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o OBJS-$(CONFIG_EARWAX_FILTER) += af_earwax.o OBJS-$(CONFIG_PAN_FILTER) += af_pan.o OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o diff --git a/libavfilter/af_aconvert.c b/libavfilter/af_aconvert.c index 9d420f84e4..2b3a330e3b 100644 --- a/libavfilter/af_aconvert.c +++ b/libavfilter/af_aconvert.c @@ -74,6 +74,7 @@ static int query_formats(AVFilterContext *ctx) AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; int out_packing = av_sample_fmt_is_planar(aconvert->out_sample_fmt); + AVFilterChannelLayouts *layouts; avfilter_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), &inlink->out_formats); @@ -85,15 +86,15 @@ static int query_formats(AVFilterContext *ctx) avfilter_formats_ref(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), &outlink->in_formats); - avfilter_formats_ref(avfilter_make_all_channel_layouts(), - &inlink->out_chlayouts); + ff_channel_layouts_ref(ff_all_channel_layouts(), + &inlink->out_channel_layouts); if (aconvert->out_chlayout != 0) { - formats = NULL; - avfilter_add_format(&formats, aconvert->out_chlayout); - avfilter_formats_ref(formats, &outlink->in_chlayouts); + layouts = NULL; + ff_add_channel_layout(&layouts, aconvert->out_chlayout); + ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts); } else - avfilter_formats_ref(avfilter_make_all_channel_layouts(), - &outlink->in_chlayouts); + ff_channel_layouts_ref(ff_all_channel_layouts(), + &outlink->in_channel_layouts); avfilter_formats_ref(avfilter_make_all_packing_formats(), &inlink->out_packing); diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c index 0a5857ec41..c602f5ddce 100644 --- a/libavfilter/af_aformat.c +++ b/libavfilter/af_aformat.c @@ -25,72 +25,110 @@ #include "libavutil/audioconvert.h" #include "libavutil/avstring.h" -#include "avfilter.h" +#include "libavutil/opt.h" + #include "audio.h" +#include "avfilter.h" +#include "formats.h" #include "internal.h" -typedef struct { - AVFilterFormats *formats, *chlayouts, *packing; +typedef struct AFormatContext { + const AVClass *class; + + AVFilterFormats *formats; + AVFilterFormats *sample_rates; + AVFilterChannelLayouts *channel_layouts; + + char *formats_str; + char *sample_rates_str; + char *channel_layouts_str; } AFormatContext; +#define OFFSET(x) offsetof(AFormatContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption options[] = { + { "sample_fmts", "A comma-separated list of sample formats.", OFFSET(formats_str), AV_OPT_TYPE_STRING, .flags = A }, + { "sample_rates", "A comma-separated list of sample rates.", OFFSET(sample_rates_str), AV_OPT_TYPE_STRING, .flags = A }, + { "channel_layouts", "A comma-separated list of channel layouts.", OFFSET(channel_layouts_str), AV_OPT_TYPE_STRING, .flags = A }, + { NULL }, +}; + +static const AVClass aformat_class = { + .class_name = "aformat filter", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +#define PARSE_FORMATS(str, type, list, add_to_list, get_fmt, none, desc) \ +do { \ + char *next, *cur = str; \ + while (cur) { \ + type fmt; \ + next = strchr(cur, ','); \ + if (next) \ + *next++ = 0; \ + \ + if ((fmt = get_fmt(cur)) == none) { \ + av_log(ctx, AV_LOG_ERROR, "Error parsing " desc ": %s.\n", cur);\ + ret = AVERROR(EINVAL); \ + goto fail; \ + } \ + add_to_list(&list, fmt); \ + \ + cur = next; \ + } \ +} while (0) + +static int get_sample_rate(const char *samplerate) +{ + int ret = strtol(samplerate, NULL, 0); + return FFMAX(ret, 0); +} + static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) { - AFormatContext * const aformat = ctx->priv; - char *fmts_str = NULL, *fmt_str, *ptr = NULL; - int64_t fmt; + AFormatContext *s = ctx->priv; int ret; - if (!args) - goto arg_fail; - -#define ADD_FORMATS(all_formats, fmt_name, fmt_type, fmts_list) do { \ - fmts_str = av_get_token(&args, ":"); \ - if (!fmts_str || !*fmts_str) \ - goto arg_fail; \ - if (!strcmp(fmts_str, "all")) { \ - aformat->fmts_list = all_formats; \ - } else { \ - for (fmt_str = fmts_str; \ - fmt_str = av_strtok(fmt_str, ",", &ptr); fmt_str = NULL) { \ - if ((ret = ff_parse_##fmt_name((fmt_type *)&fmt, \ - fmt_str, ctx)) < 0) { \ - av_freep(&fmts_str); \ - return ret; \ - } \ - avfilter_add_format(&aformat->fmts_list, fmt); \ - } \ - } \ - av_freep(&fmts_str); \ - if (*args) \ - args++; \ -} while (0) + if (!args) { + av_log(ctx, AV_LOG_ERROR, "No parameters supplied.\n"); + return AVERROR(EINVAL); + } - ADD_FORMATS(avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO), sample_format, int, formats); - ADD_FORMATS(avfilter_make_all_channel_layouts(), channel_layout, int64_t, chlayouts); - ADD_FORMATS(avfilter_make_all_packing_formats(), packing_format, int, packing); + s->class = &aformat_class; + av_opt_set_defaults(s); - return 0; + if ((ret = av_set_options_string(s, args, "=", ":")) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args); + return ret; + } -arg_fail: - av_log(ctx, AV_LOG_ERROR, "Invalid arguments, they must be of the form " - "sample_fmts:channel_layouts:packing_fmts\n"); - av_freep(&fmts_str); - return AVERROR(EINVAL); + PARSE_FORMATS(s->formats_str, enum AVSampleFormat, s->formats, + avfilter_add_format, av_get_sample_fmt, AV_SAMPLE_FMT_NONE, "sample format"); + PARSE_FORMATS(s->sample_rates_str, int, s->sample_rates, avfilter_add_format, + get_sample_rate, 0, "sample rate"); + PARSE_FORMATS(s->channel_layouts_str, uint64_t, s->channel_layouts, + ff_add_channel_layout, av_get_channel_layout, 0, + "channel layout"); + +fail: + av_opt_free(s); + return ret; } static int query_formats(AVFilterContext *ctx) { - AFormatContext * const aformat = ctx->priv; + AFormatContext *s = ctx->priv; - avfilter_set_common_sample_formats (ctx, aformat->formats); - avfilter_set_common_channel_layouts(ctx, aformat->chlayouts); - avfilter_set_common_packing_formats(ctx, aformat->packing); - return 0; -} + avfilter_set_common_formats(ctx, s->formats ? s->formats : + avfilter_all_formats(AVMEDIA_TYPE_AUDIO)); + ff_set_common_samplerates(ctx, s->sample_rates ? s->sample_rates : + ff_all_samplerates()); + ff_set_common_channel_layouts(ctx, s->channel_layouts ? s->channel_layouts : + ff_all_channel_layouts()); -static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamplesref) -{ - ff_filter_samples(inlink->dst->outputs[0], insamplesref); + return 0; } AVFilter avfilter_af_aformat = { @@ -100,11 +138,11 @@ AVFilter avfilter_af_aformat = { .query_formats = query_formats, .priv_size = sizeof(AFormatContext), - .inputs = (const AVFilterPad[]) {{ .name = "default", + .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_samples = filter_samples}, + .filter_samples = ff_null_filter_samples }, { .name = NULL}}, - .outputs = (const AVFilterPad[]) {{ .name = "default", + .outputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO}, { .name = NULL}}, }; diff --git a/libavfilter/af_amerge.c b/libavfilter/af_amerge.c index faa64e1977..fcc1089d6f 100644 --- a/libavfilter/af_amerge.c +++ b/libavfilter/af_amerge.c @@ -56,17 +56,18 @@ static int query_formats(AVFilterContext *ctx) int64_t inlayout[2], outlayout; const int packing_fmts[] = { AVFILTER_PACKED, -1 }; AVFilterFormats *formats; + AVFilterChannelLayouts *layouts; int i; for (i = 0; i < 2; i++) { - if (!ctx->inputs[i]->in_chlayouts || - !ctx->inputs[i]->in_chlayouts->format_count) { + if (!ctx->inputs[i]->in_channel_layouts || + !ctx->inputs[i]->in_channel_layouts->nb_channel_layouts) { av_log(ctx, AV_LOG_ERROR, "No channel layout for input %d\n", i + 1); return AVERROR(EINVAL); } - inlayout[i] = ctx->inputs[i]->in_chlayouts->formats[0]; - if (ctx->inputs[i]->in_chlayouts->format_count > 1) { + inlayout[i] = ctx->inputs[i]->in_channel_layouts->channel_layouts[0]; + if (ctx->inputs[i]->in_channel_layouts->nb_channel_layouts > 1) { char buf[256]; av_get_channel_layout_string(buf, sizeof(buf), 0, inlayout[i]); av_log(ctx, AV_LOG_INFO, "Using \"%s\" for input %d\n", buf, i + 1); @@ -101,13 +102,13 @@ static int query_formats(AVFilterContext *ctx) formats = avfilter_make_format_list(packing_fmts); avfilter_set_common_packing_formats(ctx, formats); for (i = 0; i < 2; i++) { - formats = NULL; - avfilter_add_format(&formats, inlayout[i]); - avfilter_formats_ref(formats, &ctx->inputs[i]->out_chlayouts); + layouts = NULL; + ff_add_channel_layout(&layouts, inlayout[i]); + ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts); } - formats = NULL; - avfilter_add_format(&formats, outlayout); - avfilter_formats_ref(formats, &ctx->outputs[0]->in_chlayouts); + layouts = NULL; + ff_add_channel_layout(&layouts, outlayout); + ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts); return 0; } diff --git a/libavfilter/af_aresample.c b/libavfilter/af_aresample.c index a2980d90cd..521ccf7f8d 100644 --- a/libavfilter/af_aresample.c +++ b/libavfilter/af_aresample.c @@ -56,6 +56,42 @@ static av_cold void uninit(AVFilterContext *ctx) swr_free(&aresample->swr); } +static int query_formats(AVFilterContext *ctx) +{ + AResampleContext *aresample = ctx->priv; + + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + + AVFilterFormats *in_formats = avfilter_all_formats(AVMEDIA_TYPE_AUDIO); + AVFilterFormats *out_formats = avfilter_all_formats(AVMEDIA_TYPE_AUDIO); + AVFilterFormats *in_samplerates = ff_all_samplerates(); + AVFilterFormats *out_samplerates; + + + AVFilterChannelLayouts *in_layouts = ff_all_channel_layouts(); + AVFilterChannelLayouts *out_layouts = ff_all_channel_layouts(); + + avfilter_formats_ref(in_formats, &inlink->out_formats); + avfilter_formats_ref(out_formats, &outlink->in_formats); + + avfilter_formats_ref(in_samplerates, &inlink->out_samplerates); + + ff_channel_layouts_ref(in_layouts, &inlink->out_channel_layouts); + ff_channel_layouts_ref(out_layouts, &outlink->in_channel_layouts); + + if(aresample->out_rate > 0) { + int sample_rates[] = { aresample->out_rate, -1 }; + ff_set_common_samplerates(ctx, avfilter_make_format_list(sample_rates)); + } else { + out_samplerates = ff_all_samplerates(); + avfilter_formats_ref(out_samplerates, &outlink->in_samplerates); + } + + return 0; +} + + static int config_output(AVFilterLink *outlink) { int ret; @@ -113,6 +149,7 @@ AVFilter avfilter_af_aresample = { .description = NULL_IF_CONFIG_SMALL("Resample audio data."), .init = init, .uninit = uninit, + .query_formats = query_formats, .priv_size = sizeof(AResampleContext), .inputs = (const AVFilterPad[]) {{ .name = "default", diff --git a/libavfilter/af_astreamsync.c b/libavfilter/af_astreamsync.c index faa3b7ce8a..9f040238d9 100644 --- a/libavfilter/af_astreamsync.c +++ b/libavfilter/af_astreamsync.c @@ -81,6 +81,7 @@ static int query_formats(AVFilterContext *ctx) { int i; AVFilterFormats *formats; + AVFilterChannelLayouts *layouts; for (i = 0; i < 2; i++) { formats = ctx->inputs[i]->in_formats; @@ -89,9 +90,9 @@ static int query_formats(AVFilterContext *ctx) formats = ctx->inputs[i]->in_packing; avfilter_formats_ref(formats, &ctx->inputs[i]->out_packing); avfilter_formats_ref(formats, &ctx->outputs[i]->in_packing); - formats = ctx->inputs[i]->in_chlayouts; - avfilter_formats_ref(formats, &ctx->inputs[i]->out_chlayouts); - avfilter_formats_ref(formats, &ctx->outputs[i]->in_chlayouts); + layouts = ctx->inputs[i]->in_channel_layouts; + ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts); + ff_channel_layouts_ref(layouts, &ctx->outputs[i]->in_channel_layouts); } return 0; } diff --git a/libavfilter/af_asyncts.c b/libavfilter/af_asyncts.c new file mode 100644 index 0000000000..5cde0bf00a --- /dev/null +++ b/libavfilter/af_asyncts.c @@ -0,0 +1,237 @@ +/* + * 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 + */ + +#include "libavresample/avresample.h" +#include "libavutil/audio_fifo.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" + +#include "audio.h" +#include "avfilter.h" + +typedef struct ASyncContext { + const AVClass *class; + + AVAudioResampleContext *avr; + int64_t pts; ///< timestamp in samples of the first sample in fifo + int min_delta; ///< pad/trim min threshold in samples + + /* options */ + int resample; + float min_delta_sec; + int max_comp; +} ASyncContext; + +#define OFFSET(x) offsetof(ASyncContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption options[] = { + { "compensate", "Stretch/squeeze the data to make it match the timestamps", OFFSET(resample), AV_OPT_TYPE_INT, { 0 }, 0, 1, A }, + { "min_delta", "Minimum difference between timestamps and audio data " + "(in seconds) to trigger padding/trimmin the data.", OFFSET(min_delta_sec), AV_OPT_TYPE_FLOAT, { 0.1 }, 0, INT_MAX, A }, + { "max_comp", "Maximum compensation in samples per second.", OFFSET(max_comp), AV_OPT_TYPE_INT, { 500 }, 0, INT_MAX, A }, + { NULL }, +}; + +static const AVClass async_class = { + .class_name = "asyncts filter", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int init(AVFilterContext *ctx, const char *args, void *opaque) +{ + ASyncContext *s = ctx->priv; + int ret; + + s->class = &async_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; + } + av_opt_free(s); + + s->pts = AV_NOPTS_VALUE; + + return 0; +} + +static void uninit(AVFilterContext *ctx) +{ + ASyncContext *s = ctx->priv; + + if (s->avr) { + avresample_close(s->avr); + avresample_free(&s->avr); + } +} + +static int config_props(AVFilterLink *link) +{ + ASyncContext *s = link->src->priv; + int ret; + + s->min_delta = s->min_delta_sec * link->sample_rate; + link->time_base = (AVRational){1, link->sample_rate}; + + s->avr = avresample_alloc_context(); + if (!s->avr) + return AVERROR(ENOMEM); + + av_opt_set_int(s->avr, "in_channel_layout", link->channel_layout, 0); + av_opt_set_int(s->avr, "out_channel_layout", link->channel_layout, 0); + av_opt_set_int(s->avr, "in_sample_fmt", link->format, 0); + av_opt_set_int(s->avr, "out_sample_fmt", link->format, 0); + av_opt_set_int(s->avr, "in_sample_rate", link->sample_rate, 0); + av_opt_set_int(s->avr, "out_sample_rate", link->sample_rate, 0); + + if (s->resample) + av_opt_set_int(s->avr, "force_resampling", 1, 0); + + if ((ret = avresample_open(s->avr)) < 0) + return ret; + + return 0; +} + +static int request_frame(AVFilterLink *link) +{ + AVFilterContext *ctx = link->src; + ASyncContext *s = ctx->priv; + int ret = avfilter_request_frame(ctx->inputs[0]); + int nb_samples; + + /* flush the fifo */ + if (ret == AVERROR_EOF && (nb_samples = avresample_get_delay(s->avr))) { + AVFilterBufferRef *buf = ff_get_audio_buffer(link, AV_PERM_WRITE, + nb_samples); + if (!buf) + return AVERROR(ENOMEM); + avresample_convert(s->avr, (void**)buf->extended_data, buf->linesize[0], + nb_samples, NULL, 0, 0); + buf->pts = s->pts; + ff_filter_samples(link, buf); + return 0; + } + + return ret; +} + +static void write_to_fifo(ASyncContext *s, AVFilterBufferRef *buf) +{ + avresample_convert(s->avr, NULL, 0, 0, (void**)buf->extended_data, + buf->linesize[0], buf->audio->nb_samples); + avfilter_unref_buffer(buf); +} + +/* get amount of data currently buffered, in samples */ +static int64_t get_delay(ASyncContext *s) +{ + return avresample_available(s->avr) + avresample_get_delay(s->avr); +} + +static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf) +{ + AVFilterContext *ctx = inlink->dst; + ASyncContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int nb_channels = av_get_channel_layout_nb_channels(buf->audio->channel_layout); + int64_t pts = (buf->pts == AV_NOPTS_VALUE) ? buf->pts : + av_rescale_q(buf->pts, inlink->time_base, outlink->time_base); + int out_size; + int64_t delta; + + /* buffer data until we get the first timestamp */ + if (s->pts == AV_NOPTS_VALUE) { + if (pts != AV_NOPTS_VALUE) { + s->pts = pts - get_delay(s); + } + write_to_fifo(s, buf); + return; + } + + /* now wait for the next timestamp */ + if (pts == AV_NOPTS_VALUE) { + write_to_fifo(s, buf); + return; + } + + /* when we have two timestamps, compute how many samples would we have + * to add/remove to get proper sync between data and timestamps */ + delta = pts - s->pts - get_delay(s); + out_size = avresample_available(s->avr); + + if (labs(delta) > s->min_delta) { + av_log(ctx, AV_LOG_VERBOSE, "Discontinuity - %"PRId64" samples.\n", delta); + out_size += delta; + } else if (s->resample) { + int comp = av_clip(delta, -s->max_comp, s->max_comp); + av_log(ctx, AV_LOG_VERBOSE, "Compensating %d samples per second.\n", comp); + avresample_set_compensation(s->avr, delta, inlink->sample_rate); + } + + if (out_size > 0) { + AVFilterBufferRef *buf_out = ff_get_audio_buffer(outlink, AV_PERM_WRITE, + out_size); + if (!buf_out) + return; + + avresample_read(s->avr, (void**)buf_out->extended_data, out_size); + buf_out->pts = s->pts; + + if (delta > 0) { + av_samples_set_silence(buf_out->extended_data, out_size - delta, + delta, nb_channels, buf->format); + } + ff_filter_samples(outlink, buf_out); + } else { + av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping " + "whole buffer.\n"); + } + + /* drain any remaining buffered data */ + avresample_read(s->avr, NULL, avresample_available(s->avr)); + + s->pts = pts - avresample_get_delay(s->avr); + avresample_convert(s->avr, NULL, 0, 0, (void**)buf->extended_data, + buf->linesize[0], buf->audio->nb_samples); + avfilter_unref_buffer(buf); +} + +AVFilter avfilter_af_asyncts = { + .name = "asyncts", + .description = NULL_IF_CONFIG_SMALL("Sync audio data to timestamps"), + + .init = init, + .uninit = uninit, + + .priv_size = sizeof(ASyncContext), + + .inputs = (const AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_samples = filter_samples }, + { NULL }}, + .outputs = (const AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_props, + .request_frame = request_frame }, + { NULL }}, +}; diff --git a/libavfilter/af_earwax.c b/libavfilter/af_earwax.c index 6afed72182..2ab248f390 100644 --- a/libavfilter/af_earwax.c +++ b/libavfilter/af_earwax.c @@ -32,6 +32,7 @@ #include "libavutil/audioconvert.h" #include "avfilter.h" #include "audio.h" +#include "formats.h" #define NUMTAPS 64 @@ -76,15 +77,19 @@ typedef struct { static int query_formats(AVFilterContext *ctx) { + int sample_rates[] = { 44100, -1 }; + AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layout = NULL; + avfilter_add_format(&formats, AV_SAMPLE_FMT_S16); avfilter_set_common_sample_formats(ctx, formats); - formats = NULL; - avfilter_add_format(&formats, AV_CH_LAYOUT_STEREO); - avfilter_set_common_channel_layouts(ctx, formats); + ff_add_channel_layout(&layout, AV_CH_LAYOUT_STEREO); + ff_set_common_channel_layouts(ctx, layout); formats = NULL; avfilter_add_format(&formats, AVFILTER_PACKED); avfilter_set_common_packing_formats(ctx, formats); + ff_set_common_samplerates(ctx, avfilter_make_format_list(sample_rates)); return 0; } diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c index fd65aac58c..3100a14881 100644 --- a/libavfilter/af_pan.c +++ b/libavfilter/af_pan.c @@ -33,6 +33,7 @@ #include "libswresample/swresample.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #define MAX_CHANNELS 63 @@ -212,7 +213,7 @@ static int query_formats(AVFilterContext *ctx) PanContext *pan = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; - AVFilterFormats *formats; + AVFilterChannelLayouts *layouts; pan->pure_gains = are_gains_pure(pan); /* libswr supports any sample and packing formats */ @@ -220,13 +221,13 @@ static int query_formats(AVFilterContext *ctx) avfilter_set_common_packing_formats(ctx, avfilter_make_all_packing_formats()); // inlink supports any channel layout - formats = avfilter_make_all_channel_layouts(); - avfilter_formats_ref(formats, &inlink->out_chlayouts); + layouts = ff_all_channel_layouts(); + ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts); // outlink supports only requested output channel layout - formats = NULL; - avfilter_add_format(&formats, pan->out_channel_layout); - avfilter_formats_ref(formats, &outlink->in_chlayouts); + layouts = NULL; + ff_add_channel_layout(&layouts, pan->out_channel_layout); + ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts); return 0; } diff --git a/libavfilter/af_resample.c b/libavfilter/af_resample.c index f46e24b1b6..4ad5a8c38e 100644 --- a/libavfilter/af_resample.c +++ b/libavfilter/af_resample.c @@ -31,6 +31,7 @@ #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct ResampleContext { @@ -56,10 +57,20 @@ static int query_formats(AVFilterContext *ctx) AVFilterFormats *in_formats = avfilter_all_formats(AVMEDIA_TYPE_AUDIO); AVFilterFormats *out_formats = avfilter_all_formats(AVMEDIA_TYPE_AUDIO); + AVFilterFormats *in_samplerates = ff_all_samplerates(); + AVFilterFormats *out_samplerates = ff_all_samplerates(); + AVFilterChannelLayouts *in_layouts = ff_all_channel_layouts(); + AVFilterChannelLayouts *out_layouts = ff_all_channel_layouts(); avfilter_formats_ref(in_formats, &inlink->out_formats); avfilter_formats_ref(out_formats, &outlink->in_formats); + avfilter_formats_ref(in_samplerates, &inlink->out_samplerates); + avfilter_formats_ref(out_samplerates, &outlink->in_samplerates); + + ff_channel_layouts_ref(in_layouts, &inlink->out_channel_layouts); + ff_channel_layouts_ref(out_layouts, &outlink->in_channel_layouts); + return 0; } diff --git a/libavfilter/af_silencedetect.c b/libavfilter/af_silencedetect.c index 093ca2b21f..4f5d8e0dd4 100644 --- a/libavfilter/af_silencedetect.c +++ b/libavfilter/af_silencedetect.c @@ -26,6 +26,7 @@ #include "libavutil/opt.h" #include "libavutil/timestamp.h" #include "audio.h" +#include "formats.h" #include "avfilter.h" typedef struct { @@ -130,16 +131,17 @@ static void filter_samples(AVFilterLink *inlink, AVFilterBufferRef *insamples) static int query_formats(AVFilterContext *ctx) { AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts = NULL; enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_NONE }; int packing_fmts[] = { AVFILTER_PACKED, -1 }; - formats = avfilter_make_all_channel_layouts(); - if (!formats) + layouts = ff_all_channel_layouts(); + if (!layouts) return AVERROR(ENOMEM); - avfilter_set_common_channel_layouts(ctx, formats); + ff_set_common_channel_layouts(ctx, layouts); formats = avfilter_make_format_list(sample_fmts); if (!formats) @@ -151,6 +153,11 @@ static int query_formats(AVFilterContext *ctx) return AVERROR(ENOMEM); avfilter_set_common_packing_formats(ctx, formats); + formats = ff_all_samplerates(); + if (!formats) + return AVERROR(ENOMEM); + ff_set_common_samplerates(ctx, formats); + return 0; } diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c index 8e2e37e730..50f3cbd002 100644 --- a/libavfilter/af_volume.c +++ b/libavfilter/af_volume.c @@ -28,6 +28,7 @@ #include "libavutil/eval.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" typedef struct { double volume; @@ -81,6 +82,7 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) static int query_formats(AVFilterContext *ctx) { AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts; enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16, @@ -91,10 +93,10 @@ static int query_formats(AVFilterContext *ctx) }; int packing_fmts[] = { AVFILTER_PACKED, -1 }; - formats = avfilter_make_all_channel_layouts(); - if (!formats) + layouts = ff_all_channel_layouts(); + if (!layouts) return AVERROR(ENOMEM); - avfilter_set_common_channel_layouts(ctx, formats); + ff_set_common_channel_layouts(ctx, layouts); formats = avfilter_make_format_list(sample_fmts); if (!formats) @@ -106,6 +108,11 @@ static int query_formats(AVFilterContext *ctx) return AVERROR(ENOMEM); avfilter_set_common_packing_formats(ctx, formats); + formats = ff_all_samplerates(); + if (!formats) + return AVERROR(ENOMEM); + ff_set_common_samplerates(ctx, formats); + return 0; } diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 4e4c5d37f4..29cd3e3402 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -42,13 +42,13 @@ void avfilter_register_all(void) REGISTER_FILTER (ASHOWINFO, ashowinfo, af); REGISTER_FILTER (ASPLIT, asplit, af); REGISTER_FILTER (ASTREAMSYNC, astreamsync, af); + REGISTER_FILTER (ASYNCTS, asyncts, af); REGISTER_FILTER (EARWAX, earwax, af); REGISTER_FILTER (PAN, pan, af); REGISTER_FILTER (SILENCEDETECT, silencedetect, af); REGISTER_FILTER (VOLUME, volume, af); REGISTER_FILTER (RESAMPLE, resample, af); - REGISTER_FILTER (ABUFFER, abuffer, asrc); REGISTER_FILTER (AEVALSRC, aevalsrc, asrc); REGISTER_FILTER (AMOVIE, amovie, asrc); REGISTER_FILTER (ANULLSRC, anullsrc, asrc); @@ -130,10 +130,18 @@ void avfilter_register_all(void) avfilter_register(&avfilter_vsrc_buffer); } { + extern AVFilter avfilter_asrc_abuffer; + avfilter_register(&avfilter_asrc_abuffer); + } + { extern AVFilter avfilter_vsink_buffer; avfilter_register(&avfilter_vsink_buffer); } { + extern AVFilter avfilter_asink_abuffer; + avfilter_register(&avfilter_asink_abuffer); + } + { extern AVFilter avfilter_vf_scale; avfilter_register(&avfilter_vf_scale); } diff --git a/libavfilter/asrc_aevalsrc.c b/libavfilter/asrc_aevalsrc.c index 4abcbc46aa..94197b2fd8 100644 --- a/libavfilter/asrc_aevalsrc.c +++ b/libavfilter/asrc_aevalsrc.c @@ -190,7 +190,7 @@ static int query_formats(AVFilterContext *ctx) int packing_fmts[] = { AVFILTER_PLANAR, -1 }; avfilter_set_common_sample_formats (ctx, avfilter_make_format_list(sample_fmts)); - avfilter_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts)); + ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts)); avfilter_set_common_packing_formats(ctx, avfilter_make_format_list(packing_fmts)); return 0; diff --git a/libavfilter/avcodec.c b/libavfilter/avcodec.c index d4c92f98cc..ce003abaa0 100644 --- a/libavfilter/avcodec.c +++ b/libavfilter/avcodec.c @@ -24,77 +24,6 @@ #include "avcodec.h" #include "libavutil/opt.h" -int avfilter_copy_frame_props(AVFilterBufferRef *dst, const AVFrame *src) -{ - dst->pts = src->pts; - dst->pos = src->pkt_pos; - dst->format = src->format; - - switch (dst->type) { - case AVMEDIA_TYPE_VIDEO: - dst->video->w = src->width; - dst->video->h = src->height; - dst->video->sample_aspect_ratio = src->sample_aspect_ratio; - dst->video->interlaced = src->interlaced_frame; - dst->video->top_field_first = src->top_field_first; - dst->video->key_frame = src->key_frame; - dst->video->pict_type = src->pict_type; - break; - case AVMEDIA_TYPE_AUDIO: - dst->audio->sample_rate = src->sample_rate; - dst->audio->channel_layout = src->channel_layout; - break; - default: - return AVERROR(EINVAL); - } - - return 0; -} - -int avfilter_copy_buf_props(AVFrame *dst, const AVFilterBufferRef *src) -{ - int planes, nb_channels; - - memcpy(dst->data, src->data, sizeof(dst->data)); - memcpy(dst->linesize, src->linesize, sizeof(dst->linesize)); - - dst->pts = src->pts; - dst->format = src->format; - - switch (src->type) { - case AVMEDIA_TYPE_VIDEO: - dst->width = src->video->w; - dst->height = src->video->h; - dst->sample_aspect_ratio = src->video->sample_aspect_ratio; - dst->interlaced_frame = src->video->interlaced; - dst->top_field_first = src->video->top_field_first; - dst->key_frame = src->video->key_frame; - dst->pict_type = src->video->pict_type; - break; - case AVMEDIA_TYPE_AUDIO: - nb_channels = av_get_channel_layout_nb_channels(src->audio->channel_layout); - planes = av_sample_fmt_is_planar(src->format) ? nb_channels : 1; - - if (planes > FF_ARRAY_ELEMS(dst->data)) { - dst->extended_data = av_mallocz(planes * sizeof(*dst->extended_data)); - if (!dst->extended_data) - return AVERROR(ENOMEM); - memcpy(dst->extended_data, src->extended_data, - planes * sizeof(dst->extended_data)); - } else - dst->extended_data = dst->data; - - dst->sample_rate = src->audio->sample_rate; - dst->channel_layout = src->audio->channel_layout; - dst->nb_samples = src->audio->nb_samples; - break; - default: - return AVERROR(EINVAL); - } - - return 0; -} - AVFilterBufferRef *avfilter_get_video_buffer_ref_from_frame(const AVFrame *frame, int perms) { diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index 4354f8ca03..9704dc0a96 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -24,11 +24,11 @@ #include "libavutil/pixdesc.h" #include "libavutil/rational.h" #include "libavutil/audioconvert.h" -#include "libavutil/imgutils.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" unsigned avfilter_version(void) { @@ -47,7 +47,7 @@ const char *avfilter_license(void) return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; } -static void command_queue_pop(AVFilterContext *filter) +void ff_command_queue_pop(AVFilterContext *filter) { AVFilterCommand *c= filter->command_queue; av_freep(&c->arg); @@ -56,133 +56,6 @@ static void command_queue_pop(AVFilterContext *filter) av_free(c); } -AVFilterBufferRef *avfilter_ref_buffer(AVFilterBufferRef *ref, int pmask) -{ - AVFilterBufferRef *ret = av_malloc(sizeof(AVFilterBufferRef)); - if (!ret) - return NULL; - *ret = *ref; - if (ref->type == AVMEDIA_TYPE_VIDEO) { - ret->video = av_malloc(sizeof(AVFilterBufferRefVideoProps)); - if (!ret->video) { - av_free(ret); - return NULL; - } - *ret->video = *ref->video; - ret->extended_data = ret->data; - } else if (ref->type == AVMEDIA_TYPE_AUDIO) { - ret->audio = av_malloc(sizeof(AVFilterBufferRefAudioProps)); - if (!ret->audio) { - av_free(ret); - return NULL; - } - *ret->audio = *ref->audio; - - if (ref->extended_data && ref->extended_data != ref->data) { - int nb_channels = av_get_channel_layout_nb_channels(ref->audio->channel_layout); - if (!(ret->extended_data = av_malloc(sizeof(*ret->extended_data) * - nb_channels))) { - av_freep(&ret->audio); - av_freep(&ret); - return NULL; - } - memcpy(ret->extended_data, ref->extended_data, - sizeof(*ret->extended_data) * nb_channels); - } else - ret->extended_data = ret->data; - } - ret->perms &= pmask; - ret->buf->refcount ++; - return ret; -} - -static void free_pool(AVFilterPool *pool) -{ - int i; - - av_assert0(pool->refcount > 0); - - for (i = 0; i < POOL_SIZE; i++) { - if (pool->pic[i]) { - AVFilterBufferRef *picref = pool->pic[i]; - /* free buffer: picrefs stored in the pool are not - * supposed to contain a free callback */ - av_assert0(!picref->buf->refcount); - av_freep(&picref->buf->data[0]); - av_freep(&picref->buf); - - av_freep(&picref->audio); - av_freep(&picref->video); - av_freep(&pool->pic[i]); - pool->count--; - } - } - pool->draining = 1; - - if (!--pool->refcount) { - av_assert0(!pool->count); - av_free(pool); - } -} - -static void store_in_pool(AVFilterBufferRef *ref) -{ - int i; - AVFilterPool *pool= ref->buf->priv; - - av_assert0(ref->buf->data[0]); - av_assert0(pool->refcount>0); - - if (pool->count == POOL_SIZE) { - AVFilterBufferRef *ref1 = pool->pic[0]; - av_freep(&ref1->video); - av_freep(&ref1->audio); - av_freep(&ref1->buf->data[0]); - av_freep(&ref1->buf); - av_free(ref1); - memmove(&pool->pic[0], &pool->pic[1], sizeof(void*)*(POOL_SIZE-1)); - pool->count--; - pool->pic[POOL_SIZE-1] = NULL; - } - - for (i = 0; i < POOL_SIZE; i++) { - if (!pool->pic[i]) { - pool->pic[i] = ref; - pool->count++; - break; - } - } - if (pool->draining) { - free_pool(pool); - } else - --pool->refcount; -} - -void avfilter_unref_buffer(AVFilterBufferRef *ref) -{ - if (!ref) - return; - av_assert0(ref->buf->refcount > 0); - if (!(--ref->buf->refcount)) { - if (!ref->buf->free) { - store_in_pool(ref); - return; - } - ref->buf->free(ref->buf); - } - if (ref->extended_data != ref->data) - av_freep(&ref->extended_data); - av_freep(&ref->video); - av_freep(&ref->audio); - av_free(ref); -} - -void avfilter_unref_bufferp(AVFilterBufferRef **ref) -{ - avfilter_unref_buffer(*ref); - *ref = NULL; -} - void avfilter_insert_pad(unsigned idx, unsigned *count, size_t padidx_off, AVFilterPad **pads, AVFilterLink ***links, AVFilterPad *newpad) @@ -240,7 +113,7 @@ void avfilter_link_free(AVFilterLink **link) return; if ((*link)->pool) - free_pool((*link)->pool); + ff_free_pool((*link)->pool); av_freep(link); } @@ -272,12 +145,15 @@ int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt, if (link->out_formats) avfilter_formats_changeref(&link->out_formats, &filt->outputs[filt_dstpad_idx]->out_formats); - if (link->out_chlayouts) - avfilter_formats_changeref(&link->out_chlayouts, - &filt->outputs[filt_dstpad_idx]->out_chlayouts); + if (link->out_channel_layouts) + ff_channel_layouts_changeref(&link->out_channel_layouts, + &filt->outputs[filt_dstpad_idx]->out_channel_layouts); if (link->out_packing) avfilter_formats_changeref(&link->out_packing, &filt->outputs[filt_dstpad_idx]->out_packing); + if (link->out_samplerates) + avfilter_formats_changeref(&link->out_samplerates, + &filt->outputs[filt_dstpad_idx]->out_samplerates); return 0; } @@ -329,6 +205,7 @@ int avfilter_config_links(AVFilterContext *filter) link->sample_aspect_ratio = inlink ? inlink->sample_aspect_ratio : (AVRational){1,1}; +#if 1 if (inlink) { if (!link->w) link->w = inlink->w; @@ -359,6 +236,7 @@ int avfilter_config_links(AVFilterContext *filter) link->time_base = (AVRational) {1, link->sample_rate}; } +#endif if ((config_link = link->dstpad->config_props)) if ((ret = config_link(link)) < 0) return ret; @@ -370,47 +248,6 @@ int avfilter_config_links(AVFilterContext *filter) return 0; } -static char *ff_get_ref_perms_string(char *buf, size_t buf_size, int perms) -{ - snprintf(buf, buf_size, "%s%s%s%s%s%s", - perms & AV_PERM_READ ? "r" : "", - perms & AV_PERM_WRITE ? "w" : "", - perms & AV_PERM_PRESERVE ? "p" : "", - perms & AV_PERM_REUSE ? "u" : "", - perms & AV_PERM_REUSE2 ? "U" : "", - perms & AV_PERM_NEG_LINESIZES ? "n" : ""); - return buf; -} - -static void ff_dlog_ref(void *ctx, AVFilterBufferRef *ref, int end) -{ - av_unused char buf[16]; - av_dlog(ctx, - "ref[%p buf:%p refcount:%d perms:%s data:%p linesize[%d, %d, %d, %d] pts:%"PRId64" pos:%"PRId64, - ref, ref->buf, ref->buf->refcount, ff_get_ref_perms_string(buf, sizeof(buf), ref->perms), ref->data[0], - ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3], - ref->pts, ref->pos); - - if (ref->video) { - av_dlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c", - ref->video->sample_aspect_ratio.num, ref->video->sample_aspect_ratio.den, - ref->video->w, ref->video->h, - !ref->video->interlaced ? 'P' : /* Progressive */ - ref->video->top_field_first ? 'T' : 'B', /* Top / Bottom */ - ref->video->key_frame, - av_get_picture_type_char(ref->video->pict_type)); - } - if (ref->audio) { - av_dlog(ctx, " cl:%"PRId64"d n:%d r:%d p:%d", - ref->audio->channel_layout, - ref->audio->nb_samples, - ref->audio->sample_rate, - ref->audio->planar); - } - - av_dlog(ctx, "]%s", end ? "\n" : ""); -} - void ff_dlog_link(void *ctx, AVFilterLink *link, int end) { if (link->type == AVMEDIA_TYPE_VIDEO) { @@ -435,71 +272,6 @@ void ff_dlog_link(void *ctx, AVFilterLink *link, int end) } } -AVFilterBufferRef *avfilter_get_video_buffer(AVFilterLink *link, int perms, int w, int h) -{ - AVFilterBufferRef *ret = NULL; - - av_unused char buf[16]; - FF_DPRINTF_START(NULL, get_video_buffer); ff_dlog_link(NULL, link, 0); - av_dlog(NULL, " perms:%s w:%d h:%d\n", ff_get_ref_perms_string(buf, sizeof(buf), perms), w, h); - - if (link->dstpad->get_video_buffer) - ret = link->dstpad->get_video_buffer(link, perms, w, h); - - if (!ret) - ret = avfilter_default_get_video_buffer(link, perms, w, h); - - if (ret) - ret->type = AVMEDIA_TYPE_VIDEO; - - FF_DPRINTF_START(NULL, get_video_buffer); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " returning "); ff_dlog_ref(NULL, ret, 1); - - return ret; -} - -AVFilterBufferRef * -avfilter_get_video_buffer_ref_from_arrays(uint8_t * const data[4], const int linesize[4], int perms, - int w, int h, enum PixelFormat format) -{ - AVFilterBuffer *pic = av_mallocz(sizeof(AVFilterBuffer)); - AVFilterBufferRef *picref = av_mallocz(sizeof(AVFilterBufferRef)); - - if (!pic || !picref) - goto fail; - - picref->buf = pic; - picref->buf->free = ff_avfilter_default_free_buffer; - if (!(picref->video = av_mallocz(sizeof(AVFilterBufferRefVideoProps)))) - goto fail; - - pic->w = picref->video->w = w; - pic->h = picref->video->h = h; - - /* make sure the buffer gets read permission or it's useless for output */ - picref->perms = perms | AV_PERM_READ; - - pic->refcount = 1; - picref->type = AVMEDIA_TYPE_VIDEO; - pic->format = picref->format = format; - - memcpy(pic->data, data, 4*sizeof(data[0])); - memcpy(pic->linesize, linesize, 4*sizeof(linesize[0])); - memcpy(picref->data, pic->data, sizeof(picref->data)); - memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize)); - - pic-> extended_data = pic->data; - picref->extended_data = picref->data; - - return picref; - -fail: - if (picref && picref->video) - av_free(picref->video); - av_free(picref); - av_free(pic); - return NULL; -} - int avfilter_request_frame(AVFilterLink *link) { FF_DPRINTF_START(NULL, request_frame); ff_dlog_link(NULL, link, 1); @@ -538,107 +310,6 @@ void ff_update_link_current_pts(AVFilterLink *link, int64_t pts) ff_avfilter_graph_update_heap(link->graph, link); } -/* XXX: should we do the duplicating of the picture ref here, instead of - * forcing the source filter to do it? */ -void avfilter_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) -{ - void (*start_frame)(AVFilterLink *, AVFilterBufferRef *); - AVFilterPad *dst = link->dstpad; - int perms = picref->perms; - AVFilterCommand *cmd= link->dst->command_queue; - - FF_DPRINTF_START(NULL, start_frame); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " "); ff_dlog_ref(NULL, picref, 1); - - if (!(start_frame = dst->start_frame)) - start_frame = avfilter_default_start_frame; - - if (picref->linesize[0] < 0) - perms |= AV_PERM_NEG_LINESIZES; - /* prepare to copy the picture if it has insufficient permissions */ - if ((dst->min_perms & perms) != dst->min_perms || dst->rej_perms & perms) { - av_log(link->dst, AV_LOG_DEBUG, - "frame copy needed (have perms %x, need %x, reject %x)\n", - picref->perms, - link->dstpad->min_perms, link->dstpad->rej_perms); - - link->cur_buf = avfilter_get_video_buffer(link, dst->min_perms, link->w, link->h); - link->src_buf = picref; - avfilter_copy_buffer_ref_props(link->cur_buf, link->src_buf); - } - else - link->cur_buf = picref; - - while(cmd && cmd->time <= picref->pts * av_q2d(link->time_base)){ - av_log(link->dst, AV_LOG_DEBUG, - "Processing command time:%f command:%s arg:%s\n", - cmd->time, cmd->command, cmd->arg); - avfilter_process_command(link->dst, cmd->command, cmd->arg, 0, 0, cmd->flags); - command_queue_pop(link->dst); - cmd= link->dst->command_queue; - } - - start_frame(link, link->cur_buf); - ff_update_link_current_pts(link, link->cur_buf->pts); -} - -void avfilter_end_frame(AVFilterLink *link) -{ - void (*end_frame)(AVFilterLink *); - - if (!(end_frame = link->dstpad->end_frame)) - end_frame = avfilter_default_end_frame; - - end_frame(link); - - /* unreference the source picture if we're feeding the destination filter - * a copied version dues to permission issues */ - if (link->src_buf) { - avfilter_unref_buffer(link->src_buf); - link->src_buf = NULL; - } -} - -void avfilter_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) -{ - uint8_t *src[4], *dst[4]; - int i, j, vsub; - void (*draw_slice)(AVFilterLink *, int, int, int); - - FF_DPRINTF_START(NULL, draw_slice); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " y:%d h:%d dir:%d\n", y, h, slice_dir); - - /* copy the slice if needed for permission reasons */ - if (link->src_buf) { - vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h; - - for (i = 0; i < 4; i++) { - if (link->src_buf->data[i]) { - src[i] = link->src_buf-> data[i] + - (y >> (i==1 || i==2 ? vsub : 0)) * link->src_buf-> linesize[i]; - dst[i] = link->cur_buf->data[i] + - (y >> (i==1 || i==2 ? vsub : 0)) * link->cur_buf->linesize[i]; - } else - src[i] = dst[i] = NULL; - } - - for (i = 0; i < 4; i++) { - int planew = - av_image_get_linesize(link->format, link->cur_buf->video->w, i); - - if (!src[i]) continue; - - for (j = 0; j < h >> (i==1 || i==2 ? vsub : 0); j++) { - memcpy(dst[i], src[i], planew); - src[i] += link->src_buf->linesize[i]; - dst[i] += link->cur_buf->linesize[i]; - } - } - } - - if (!(draw_slice = link->dstpad->draw_slice)) - draw_slice = avfilter_default_draw_slice; - draw_slice(link, y, h, slice_dir); -} - int avfilter_process_command(AVFilterContext *filter, const char *cmd, const char *arg, char *res, int res_len, int flags) { if(!strcmp(cmd, "ping")){ @@ -788,6 +459,10 @@ void avfilter_free(AVFilterContext *filter) link->src->outputs[link->srcpad - link->src->output_pads] = NULL; avfilter_formats_unref(&link->in_formats); avfilter_formats_unref(&link->out_formats); + avfilter_formats_unref(&link->in_samplerates); + avfilter_formats_unref(&link->out_samplerates); + ff_channel_layouts_unref(&link->in_channel_layouts); + ff_channel_layouts_unref(&link->out_channel_layouts); } avfilter_link_free(&link); } @@ -797,6 +472,10 @@ void avfilter_free(AVFilterContext *filter) link->dst->inputs[link->dstpad - link->dst->input_pads] = NULL; avfilter_formats_unref(&link->in_formats); avfilter_formats_unref(&link->out_formats); + avfilter_formats_unref(&link->in_samplerates); + avfilter_formats_unref(&link->out_samplerates); + ff_channel_layouts_unref(&link->in_channel_layouts); + ff_channel_layouts_unref(&link->out_channel_layouts); } avfilter_link_free(&link); } @@ -808,7 +487,7 @@ void avfilter_free(AVFilterContext *filter) av_freep(&filter->outputs); av_freep(&filter->priv); while(filter->command_queue){ - command_queue_pop(filter); + ff_command_queue_pop(filter); } av_free(filter); } @@ -821,16 +500,3 @@ int avfilter_init_filter(AVFilterContext *filter, const char *args, void *opaque ret = filter->filter->init(filter, args, opaque); return ret; } - -void avfilter_copy_buffer_ref_props(AVFilterBufferRef *dst, AVFilterBufferRef *src) -{ - // copy common properties - dst->pts = src->pts; - dst->pos = src->pos; - - switch (src->type) { - case AVMEDIA_TYPE_VIDEO: *dst->video = *src->video; break; - case AVMEDIA_TYPE_AUDIO: *dst->audio = *src->audio; break; - default: break; - } -} diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index 06146cfc2d..c76c637e8d 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -258,8 +258,8 @@ void avfilter_unref_bufferp(AVFilterBufferRef **ref); * pointer to each of the pointers to itself. */ typedef struct AVFilterFormats { - unsigned format_count; ///< number of formats int64_t *formats; ///< list of media formats + unsigned format_count; ///< number of formats unsigned refcount; ///< number of references to this list struct AVFilterFormats ***refs; ///< references to this list @@ -274,7 +274,6 @@ typedef struct AVFilterFormats { * @return the format list, with no existing references */ AVFilterFormats *avfilter_make_format_list(const int *fmts); -AVFilterFormats *avfilter_make_format64_list(const int64_t *fmts); /** * Add fmt to the list of media formats contained in *avff. @@ -305,11 +304,6 @@ AVFilterFormats *avfilter_make_all_formats(enum AVMediaType type); extern const int64_t avfilter_all_channel_layouts[]; /** - * Return a list of all channel layouts supported by FFmpeg. - */ -AVFilterFormats *avfilter_make_all_channel_layouts(void); - -/** * Return a list of all audio packing formats. */ AVFilterFormats *avfilter_make_all_packing_formats(void); @@ -521,6 +515,7 @@ AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, * formats/layouts. If there are no links hooked to this filter, the list * of formats is freed. */ +void avfilter_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats); void avfilter_set_common_pixel_formats(AVFilterContext *ctx, AVFilterFormats *formats); void avfilter_set_common_sample_formats(AVFilterContext *ctx, AVFilterFormats *formats); void avfilter_set_common_channel_layouts(AVFilterContext *ctx, AVFilterFormats *formats); @@ -674,8 +669,6 @@ struct AVFilterLink { AVFilterFormats *in_formats; AVFilterFormats *out_formats; - AVFilterFormats *in_chlayouts; - AVFilterFormats *out_chlayouts; AVFilterFormats *in_packing; AVFilterFormats *out_packing; @@ -713,17 +706,26 @@ struct AVFilterLink { */ int64_t current_pts; - /** - * Private fields - * - * The following fields are for internal use only. - * Their type, offset, number and semantic can change without notice. + /***************************************************************** + * All fields below this line are not part of the public API. They + * may not be used outside of libavfilter and can be changed and + * removed at will. + * New public fields should be added right above. + ***************************************************************** */ - /** * Index in the age array. */ int age_index; + + /** + * Lists of channel layouts and sample rates used for automatic + * negotiation. + */ + AVFilterFormats *in_samplerates; + AVFilterFormats *out_samplerates; + struct AVFilterChannelLayouts *in_channel_layouts; + struct AVFilterChannelLayouts *out_channel_layouts; }; /** diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 3bc10f9586..90d7560d6e 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -28,8 +28,10 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "avfiltergraph.h" +#include "formats.h" #include "internal.h" +#include "libavutil/audioconvert.h" #include "libavutil/log.h" static const AVClass filtergraph_class = { @@ -198,10 +200,10 @@ static int insert_conv_filter(AVFilterGraph *graph, AVFilterLink *link, if (link->type == AVMEDIA_TYPE_AUDIO && (((link = filt_ctx-> inputs[0]) && - (!avfilter_merge_formats(link->in_chlayouts, link->out_chlayouts) || + (!ff_merge_channel_layouts(link->in_channel_layouts, link->out_channel_layouts) || !avfilter_merge_formats(link->in_packing, link->out_packing))) || ((link = filt_ctx->outputs[0]) && - (!avfilter_merge_formats(link->in_chlayouts, link->out_chlayouts) || + (!ff_merge_channel_layouts(link->in_channel_layouts, link->out_channel_layouts) || !avfilter_merge_formats(link->in_packing, link->out_packing)))) ) { av_log(NULL, AV_LOG_ERROR, @@ -217,7 +219,10 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) { int i, j, ret; char filt_args[128]; - AVFilterFormats *formats, *chlayouts, *packing; + AVFilterFormats *formats, *packing; + AVFilterChannelLayouts *chlayouts; + AVFilterFormats *samplerates; + int scaler_count = 0, resampler_count = 0; /* ask all the sub-filters for their supported media formats */ for (i = 0; i < graph->filter_count; i++) { @@ -233,6 +238,7 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) for (j = 0; j < filter->input_count; j++) { AVFilterLink *link = filter->inputs[j]; +#if 0 if (!link) continue; if (!link->in_formats || !link->out_formats) @@ -248,7 +254,7 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) return ret; } else if (link->type == AVMEDIA_TYPE_AUDIO) { - if (!link->in_chlayouts || !link->out_chlayouts || + if (!link->in_channel_layouts || !link->out_channel_layouts || !link->in_packing || !link->out_packing) return AVERROR(EINVAL); @@ -256,11 +262,100 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) * three categories, aconvert will use a common format * whenever possible. */ formats = avfilter_merge_formats(link->in_formats, link->out_formats); - chlayouts = avfilter_merge_formats(link->in_chlayouts, link->out_chlayouts); + chlayouts = ff_merge_channel_layouts(link->in_channel_layouts , link->out_channel_layouts); + samplerates = ff_merge_samplerates (link->in_samplerates, link->out_samplerates); packing = avfilter_merge_formats(link->in_packing, link->out_packing); - if (!formats || !chlayouts || !packing) + + if (!formats || !chlayouts || !packing || !samplerates) if (ret = insert_conv_filter(graph, link, "aconvert", NULL)) return ret; +#else + int convert_needed = 0; + + if (!link) + continue; + + if (link->in_formats != link->out_formats && + !avfilter_merge_formats(link->in_formats, + link->out_formats)) + convert_needed = 1; + if (link->type == AVMEDIA_TYPE_AUDIO) { + if (link->in_channel_layouts != link->out_channel_layouts && + !ff_merge_channel_layouts(link->in_channel_layouts, + link->out_channel_layouts)) + convert_needed = 1; + if (link->in_samplerates != link->out_samplerates && + !ff_merge_samplerates(link->in_samplerates, + link->out_samplerates)) + convert_needed = 1; + } + + if (convert_needed) { + AVFilterContext *convert; + AVFilter *filter; + AVFilterLink *inlink, *outlink; + char scale_args[256]; + char inst_name[30]; + + /* couldn't merge format lists. auto-insert conversion filter */ + switch (link->type) { + case AVMEDIA_TYPE_VIDEO: + snprintf(inst_name, sizeof(inst_name), "auto-inserted scaler %d", + scaler_count++); + snprintf(scale_args, sizeof(scale_args), "0:0:%s", graph->scale_sws_opts); + if ((ret = avfilter_graph_create_filter(&convert, + avfilter_get_by_name("scale"), + inst_name, scale_args, NULL, + graph)) < 0) + return ret; + break; + case AVMEDIA_TYPE_AUDIO: + if (!(filter = avfilter_get_by_name("resample"))) { + av_log(log_ctx, AV_LOG_ERROR, "'resample' filter " + "not present, cannot convert audio formats.\n"); + return AVERROR(EINVAL); + } + + snprintf(inst_name, sizeof(inst_name), "auto-inserted resampler %d", + resampler_count++); + if ((ret = avfilter_graph_create_filter(&convert, + avfilter_get_by_name("resample"), + inst_name, NULL, NULL, graph)) < 0) + return ret; + break; + default: + return AVERROR(EINVAL); + } + + if ((ret = avfilter_insert_filter(link, convert, 0, 0)) < 0) + return ret; + + convert->filter->query_formats(convert); + inlink = convert->inputs[0]; + outlink = convert->outputs[0]; + if (!avfilter_merge_formats( inlink->in_formats, inlink->out_formats) || + !avfilter_merge_formats(outlink->in_formats, outlink->out_formats)) + ret |= AVERROR(ENOSYS); + if (inlink->type == AVMEDIA_TYPE_AUDIO && + (!ff_merge_samplerates(inlink->in_samplerates, + inlink->out_samplerates) || + !ff_merge_channel_layouts(inlink->in_channel_layouts, + inlink->out_channel_layouts))) + ret |= AVERROR(ENOSYS); + if (outlink->type == AVMEDIA_TYPE_AUDIO && + (!ff_merge_samplerates(outlink->in_samplerates, + outlink->out_samplerates) || + !ff_merge_channel_layouts(outlink->in_channel_layouts, + outlink->out_channel_layouts))) + ret |= AVERROR(ENOSYS); + + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Impossible to convert between the formats supported by the filter " + "'%s' and the filter '%s'\n", link->src->name, link->dst->name); + return ret; + } +#endif } } } @@ -268,10 +363,10 @@ static int query_formats(AVFilterGraph *graph, AVClass *log_ctx) return 0; } -static void pick_format(AVFilterLink *link, AVFilterLink *ref) +static int pick_format(AVFilterLink *link, AVFilterLink *ref) { if (!link || !link->in_formats) - return; + return 0; if (link->type == AVMEDIA_TYPE_VIDEO) { if(ref && ref->type == AVMEDIA_TYPE_VIDEO){ @@ -288,50 +383,83 @@ static void pick_format(AVFilterLink *link, AVFilterLink *ref) link->in_formats->format_count = 1; link->format = link->in_formats->formats[0]; - avfilter_formats_unref(&link->in_formats); - avfilter_formats_unref(&link->out_formats); if (link->type == AVMEDIA_TYPE_AUDIO) { - link->in_chlayouts->format_count = 1; - link->channel_layout = link->in_chlayouts->formats[0]; - avfilter_formats_unref(&link->in_chlayouts); - avfilter_formats_unref(&link->out_chlayouts); - - link->in_packing->format_count = 1; - link->planar = link->in_packing->formats[0] == AVFILTER_PLANAR; - avfilter_formats_unref(&link->in_packing); - avfilter_formats_unref(&link->out_packing); + if (!link->in_samplerates->format_count) { + av_log(link->src, AV_LOG_ERROR, "Cannot select sample rate for" + " the link between filters %s and %s.\n", link->src->name, + link->dst->name); + return AVERROR(EINVAL); + } + link->in_samplerates->format_count = 1; + link->sample_rate = link->in_samplerates->formats[0]; + + if (!link->in_channel_layouts->nb_channel_layouts) { + av_log(link->src, AV_LOG_ERROR, "Cannot select channel layout for" + "the link between filters %s and %s.\n", link->src->name, + link->dst->name); + return AVERROR(EINVAL); + } + link->in_channel_layouts->nb_channel_layouts = 1; + link->channel_layout = link->in_channel_layouts->channel_layouts[0]; } + + avfilter_formats_unref(&link->in_formats); + avfilter_formats_unref(&link->out_formats); + avfilter_formats_unref(&link->in_samplerates); + avfilter_formats_unref(&link->out_samplerates); + ff_channel_layouts_unref(&link->in_channel_layouts); + ff_channel_layouts_unref(&link->out_channel_layouts); + + return 0; } +#define REDUCE_FORMATS(fmt_type, list_type, list, var, nb, add_format) \ +do { \ + for (i = 0; i < filter->input_count; i++) { \ + AVFilterLink *link = filter->inputs[i]; \ + fmt_type fmt; \ + \ + if (!link->out_ ## list || link->out_ ## list->nb != 1) \ + continue; \ + fmt = link->out_ ## list->var[0]; \ + \ + for (j = 0; j < filter->output_count; j++) { \ + AVFilterLink *out_link = filter->outputs[j]; \ + list_type *fmts; \ + \ + if (link->type != out_link->type || \ + out_link->in_ ## list->nb == 1) \ + continue; \ + fmts = out_link->in_ ## list; \ + \ + if (!out_link->in_ ## list->nb) { \ + add_format(&out_link->in_ ##list, fmt); \ + break; \ + } \ + \ + for (k = 0; k < out_link->in_ ## list->nb; k++) \ + if (fmts->var[k] == fmt) { \ + fmts->var[0] = fmt; \ + fmts->nb = 1; \ + ret = 1; \ + break; \ + } \ + } \ + } \ +} while (0) + static int reduce_formats_on_filter(AVFilterContext *filter) { int i, j, k, ret = 0; - for (i = 0; i < filter->input_count; i++) { - AVFilterLink *link = filter->inputs[i]; - int format = link->out_formats->formats[0]; - - if (link->out_formats->format_count != 1) - continue; - - for (j = 0; j < filter->output_count; j++) { - AVFilterLink *out_link = filter->outputs[j]; - AVFilterFormats *fmts = out_link->in_formats; - - if (link->type != out_link->type || - out_link->in_formats->format_count == 1) - continue; + REDUCE_FORMATS(int, AVFilterFormats, formats, formats, + format_count, avfilter_add_format); + REDUCE_FORMATS(int, AVFilterFormats, samplerates, formats, + format_count, avfilter_add_format); + REDUCE_FORMATS(uint64_t, AVFilterChannelLayouts, channel_layouts, + channel_layouts, nb_channel_layouts, ff_add_channel_layout); - for (k = 0; k < out_link->in_formats->format_count; k++) - if (fmts->formats[k] == format) { - fmts->formats[0] = format; - fmts->format_count = 1; - ret = 1; - break; - } - } - } return ret; } @@ -347,9 +475,109 @@ static void reduce_formats(AVFilterGraph *graph) } while (reduced); } -static void pick_formats(AVFilterGraph *graph) +static void swap_samplerates_on_filter(AVFilterContext *filter) +{ + AVFilterLink *link = NULL; + int sample_rate; + int i, j; + + for (i = 0; i < filter->input_count; i++) { + link = filter->inputs[i]; + + if (link->type == AVMEDIA_TYPE_AUDIO && + link->out_samplerates->format_count == 1) + break; + } + if (i == filter->input_count) + return; + + sample_rate = link->out_samplerates->formats[0]; + + for (i = 0; i < filter->output_count; i++) { + AVFilterLink *outlink = filter->outputs[i]; + int best_idx, best_diff = INT_MAX; + + if (outlink->type != AVMEDIA_TYPE_AUDIO || + outlink->in_samplerates->format_count < 2) + continue; + + for (j = 0; j < outlink->in_samplerates->format_count; j++) { + int diff = abs(sample_rate - outlink->in_samplerates->formats[j]); + + if (diff < best_diff) { + best_diff = diff; + best_idx = j; + } + } + FFSWAP(int, outlink->in_samplerates->formats[0], + outlink->in_samplerates->formats[best_idx]); + } +} + +static void swap_samplerates(AVFilterGraph *graph) { + int i; + + for (i = 0; i < graph->filter_count; i++) + swap_samplerates_on_filter(graph->filters[i]); +} + +static void swap_channel_layouts_on_filter(AVFilterContext *filter) +{ + AVFilterLink *link = NULL; + uint64_t chlayout; int i, j; + + for (i = 0; i < filter->input_count; i++) { + link = filter->inputs[i]; + + if (link->type == AVMEDIA_TYPE_AUDIO && + link->out_channel_layouts->nb_channel_layouts == 1) + break; + } + if (i == filter->input_count) + return; + + chlayout = link->out_channel_layouts->channel_layouts[0]; + + for (i = 0; i < filter->output_count; i++) { + AVFilterLink *outlink = filter->outputs[i]; + int best_idx, best_score = INT_MIN; + + if (outlink->type != AVMEDIA_TYPE_AUDIO || + outlink->in_channel_layouts->nb_channel_layouts < 2) + continue; + + for (j = 0; j < outlink->in_channel_layouts->nb_channel_layouts; j++) { + uint64_t out_chlayout = outlink->in_channel_layouts->channel_layouts[j]; + int matched_channels = av_get_channel_layout_nb_channels(chlayout & + out_chlayout); + int extra_channels = av_get_channel_layout_nb_channels(out_chlayout & + (~chlayout)); + int score = matched_channels - extra_channels; + + if (score > best_score) { + best_score = score; + best_idx = j; + } + } + FFSWAP(uint64_t, outlink->in_channel_layouts->channel_layouts[0], + outlink->in_channel_layouts->channel_layouts[best_idx]); + } + +} + +static void swap_channel_layouts(AVFilterGraph *graph) +{ + int i; + + for (i = 0; i < graph->filter_count; i++) + swap_channel_layouts_on_filter(graph->filters[i]); +} + +static int pick_formats(AVFilterGraph *graph) +{ + int i, j, ret; int change; do{ @@ -385,13 +613,15 @@ static void pick_formats(AVFilterGraph *graph) for (i = 0; i < graph->filter_count; i++) { AVFilterContext *filter = graph->filters[i]; - if (1) { - for (j = 0; j < filter->input_count; j++) - pick_format(filter->inputs[j], NULL); - for (j = 0; j < filter->output_count; j++) - pick_format(filter->outputs[j], NULL); - } + + for (j = 0; j < filter->input_count; j++) + if ((ret = pick_format(filter->inputs[j], NULL)) < 0) + return ret; + for (j = 0; j < filter->output_count; j++) + if ((ret = pick_format(filter->outputs[j], NULL)) < 0) + return ret; } + return 0; } int ff_avfilter_graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) @@ -407,7 +637,13 @@ int ff_avfilter_graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) * of format conversion inside filters */ reduce_formats(graph); - pick_formats(graph); + /* for audio filters, ensure the best sample rate and channel layout + * is selected */ + swap_samplerates(graph); + swap_channel_layouts(graph); + + if ((ret = pick_formats(graph)) < 0) + return ret; return 0; } diff --git a/libavfilter/buffer.c b/libavfilter/buffer.c new file mode 100644 index 0000000000..32431c6d95 --- /dev/null +++ b/libavfilter/buffer.c @@ -0,0 +1,244 @@ +/* + * 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 + */ + +#include "libavutil/audioconvert.h" +#include "libavutil/avassert.h" +#include "libavcodec/avcodec.h" + +#include "avfilter.h" +#include "internal.h" +#include "avcodec.h" + +void ff_avfilter_default_free_buffer(AVFilterBuffer *ptr) +{ + if (ptr->extended_data != ptr->data) + av_freep(&ptr->extended_data); + av_free(ptr->data[0]); + av_free(ptr); +} + +AVFilterBufferRef *avfilter_ref_buffer(AVFilterBufferRef *ref, int pmask) +{ + AVFilterBufferRef *ret = av_malloc(sizeof(AVFilterBufferRef)); + if (!ret) + return NULL; + *ret = *ref; + if (ref->type == AVMEDIA_TYPE_VIDEO) { + ret->video = av_malloc(sizeof(AVFilterBufferRefVideoProps)); + if (!ret->video) { + av_free(ret); + return NULL; + } + *ret->video = *ref->video; + ret->extended_data = ret->data; + } else if (ref->type == AVMEDIA_TYPE_AUDIO) { + ret->audio = av_malloc(sizeof(AVFilterBufferRefAudioProps)); + if (!ret->audio) { + av_free(ret); + return NULL; + } + *ret->audio = *ref->audio; + + if (ref->extended_data && ref->extended_data != ref->data) { + int nb_channels = av_get_channel_layout_nb_channels(ref->audio->channel_layout); + if (!(ret->extended_data = av_malloc(sizeof(*ret->extended_data) * + nb_channels))) { + av_freep(&ret->audio); + av_freep(&ret); + return NULL; + } + memcpy(ret->extended_data, ref->extended_data, + sizeof(*ret->extended_data) * nb_channels); + } else + ret->extended_data = ret->data; + } + ret->perms &= pmask; + ret->buf->refcount ++; + return ret; +} + +void ff_free_pool(AVFilterPool *pool) +{ + int i; + + av_assert0(pool->refcount > 0); + + for (i = 0; i < POOL_SIZE; i++) { + if (pool->pic[i]) { + AVFilterBufferRef *picref = pool->pic[i]; + /* free buffer: picrefs stored in the pool are not + * supposed to contain a free callback */ + av_assert0(!picref->buf->refcount); + av_freep(&picref->buf->data[0]); + av_freep(&picref->buf); + + av_freep(&picref->audio); + av_freep(&picref->video); + av_freep(&pool->pic[i]); + pool->count--; + } + } + pool->draining = 1; + + if (!--pool->refcount) { + av_assert0(!pool->count); + av_free(pool); + } +} + +static void store_in_pool(AVFilterBufferRef *ref) +{ + int i; + AVFilterPool *pool= ref->buf->priv; + + av_assert0(ref->buf->data[0]); + av_assert0(pool->refcount>0); + + if (pool->count == POOL_SIZE) { + AVFilterBufferRef *ref1 = pool->pic[0]; + av_freep(&ref1->video); + av_freep(&ref1->audio); + av_freep(&ref1->buf->data[0]); + av_freep(&ref1->buf); + av_free(ref1); + memmove(&pool->pic[0], &pool->pic[1], sizeof(void*)*(POOL_SIZE-1)); + pool->count--; + pool->pic[POOL_SIZE-1] = NULL; + } + + for (i = 0; i < POOL_SIZE; i++) { + if (!pool->pic[i]) { + pool->pic[i] = ref; + pool->count++; + break; + } + } + if (pool->draining) { + ff_free_pool(pool); + } else + --pool->refcount; +} + +void avfilter_unref_buffer(AVFilterBufferRef *ref) +{ + if (!ref) + return; + av_assert0(ref->buf->refcount > 0); + if (!(--ref->buf->refcount)) { + if (!ref->buf->free) { + store_in_pool(ref); + return; + } + ref->buf->free(ref->buf); + } + if (ref->extended_data != ref->data) + av_freep(&ref->extended_data); + av_freep(&ref->video); + av_freep(&ref->audio); + av_free(ref); +} + +void avfilter_unref_bufferp(AVFilterBufferRef **ref) +{ + avfilter_unref_buffer(*ref); + *ref = NULL; +} + +int avfilter_copy_frame_props(AVFilterBufferRef *dst, const AVFrame *src) +{ + dst->pts = src->pts; + dst->pos = src->pkt_pos; + dst->format = src->format; + + switch (dst->type) { + case AVMEDIA_TYPE_VIDEO: + dst->video->w = src->width; + dst->video->h = src->height; + dst->video->sample_aspect_ratio = src->sample_aspect_ratio; + dst->video->interlaced = src->interlaced_frame; + dst->video->top_field_first = src->top_field_first; + dst->video->key_frame = src->key_frame; + dst->video->pict_type = src->pict_type; + break; + case AVMEDIA_TYPE_AUDIO: + dst->audio->sample_rate = src->sample_rate; + dst->audio->channel_layout = src->channel_layout; + break; + default: + return AVERROR(EINVAL); + } + + return 0; +} + +int avfilter_copy_buf_props(AVFrame *dst, const AVFilterBufferRef *src) +{ + int planes, nb_channels; + + memcpy(dst->data, src->data, sizeof(dst->data)); + memcpy(dst->linesize, src->linesize, sizeof(dst->linesize)); + + dst->pts = src->pts; + dst->format = src->format; + + switch (src->type) { + case AVMEDIA_TYPE_VIDEO: + dst->width = src->video->w; + dst->height = src->video->h; + dst->sample_aspect_ratio = src->video->sample_aspect_ratio; + dst->interlaced_frame = src->video->interlaced; + dst->top_field_first = src->video->top_field_first; + dst->key_frame = src->video->key_frame; + dst->pict_type = src->video->pict_type; + break; + case AVMEDIA_TYPE_AUDIO: + nb_channels = av_get_channel_layout_nb_channels(src->audio->channel_layout); + planes = av_sample_fmt_is_planar(src->format) ? nb_channels : 1; + + if (planes > FF_ARRAY_ELEMS(dst->data)) { + dst->extended_data = av_mallocz(planes * sizeof(*dst->extended_data)); + if (!dst->extended_data) + return AVERROR(ENOMEM); + memcpy(dst->extended_data, src->extended_data, + planes * sizeof(dst->extended_data)); + } else + dst->extended_data = dst->data; + + dst->sample_rate = src->audio->sample_rate; + dst->channel_layout = src->audio->channel_layout; + dst->nb_samples = src->audio->nb_samples; + break; + default: + return AVERROR(EINVAL); + } + + return 0; +} + +void avfilter_copy_buffer_ref_props(AVFilterBufferRef *dst, AVFilterBufferRef *src) +{ + // copy common properties + dst->pts = src->pts; + dst->pos = src->pos; + + switch (src->type) { + case AVMEDIA_TYPE_VIDEO: *dst->video = *src->video; break; + case AVMEDIA_TYPE_AUDIO: *dst->audio = *src->audio; break; + default: break; + } +} diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index f0fcab553e..90ebd93908 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -23,13 +23,20 @@ * buffer sink */ +#include "libavutil/audio_fifo.h" +#include "libavutil/audioconvert.h" #include "libavutil/fifo.h" +#include "libavutil/mathematics.h" +#include "audio.h" #include "avfilter.h" #include "buffersink.h" typedef struct { - AVFifoBuffer *fifo; ///< FIFO buffer of video frame references + AVFifoBuffer *fifo; ///< FIFO buffer of frame references + + AVAudioFifo *audio_fifo; ///< FIFO for audio samples + int64_t next_pts; ///< interpolating audio pts } BufferSinkContext; #define FIFO_INIT_SIZE 8 @@ -44,6 +51,9 @@ static av_cold void uninit(AVFilterContext *ctx) 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) @@ -58,9 +68,8 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) return 0; } -static void end_frame(AVFilterLink *link) +static void write_buf(AVFilterContext *ctx, AVFilterBufferRef *buf) { - AVFilterContext *ctx = link->dst; BufferSinkContext *sink = ctx->priv; if (av_fifo_space(sink->fifo) < sizeof(AVFilterBufferRef *) && @@ -69,10 +78,20 @@ static void end_frame(AVFilterLink *link) return; } - av_fifo_generic_write(sink->fifo, &link->cur_buf, sizeof(link->cur_buf), NULL); + av_fifo_generic_write(sink->fifo, &buf, sizeof(buf), NULL); +} + +static void end_frame(AVFilterLink *link) +{ + write_buf(link->dst, link->cur_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; @@ -98,6 +117,66 @@ int av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf) return 0; } +static int read_from_fifo(AVFilterContext *ctx, AVFilterBufferRef **pbuf, + int nb_samples) +{ + BufferSinkContext *s = ctx->priv; + AVFilterLink *link = ctx->inputs[0]; + AVFilterBufferRef *buf; + + if (!(buf = ff_get_audio_buffer(link, AV_PERM_WRITE, nb_samples))) + return AVERROR(ENOMEM); + av_audio_fifo_read(s->audio_fifo, (void**)buf->extended_data, nb_samples); + + buf->pts = s->next_pts; + s->next_pts += av_rescale_q(nb_samples, (AVRational){1, link->sample_rate}, + link->time_base); + + *pbuf = buf; + return 0; + +} + +int av_buffersink_read_samples(AVFilterContext *ctx, AVFilterBufferRef **pbuf, + int nb_samples) +{ + BufferSinkContext *s = ctx->priv; + AVFilterLink *link = ctx->inputs[0]; + int ret = 0; + + if (!s->audio_fifo) { + int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); + if (!(s->audio_fifo = av_audio_fifo_alloc(link->format, nb_channels, nb_samples))) + return AVERROR(ENOMEM); + } + + while (ret >= 0) { + AVFilterBufferRef *buf; + + if (av_audio_fifo_size(s->audio_fifo) >= nb_samples) + return read_from_fifo(ctx, pbuf, nb_samples); + + ret = av_buffersink_read(ctx, &buf); + if (ret == AVERROR_EOF && av_audio_fifo_size(s->audio_fifo)) + return read_from_fifo(ctx, pbuf, av_audio_fifo_size(s->audio_fifo)); + else if (ret < 0) + return ret; + + if (buf->pts != AV_NOPTS_VALUE) { + s->next_pts = buf->pts - + av_rescale_q(av_audio_fifo_size(s->audio_fifo), + (AVRational){ 1, link->sample_rate }, + link->time_base); + } + + ret = av_audio_fifo_write(s->audio_fifo, (void**)buf->extended_data, + buf->audio->nb_samples); + avfilter_unref_buffer(buf); + } + + return ret; +} + 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."), @@ -112,3 +191,18 @@ AVFilter avfilter_vsink_buffer = { { .name = NULL }}, .outputs = (AVFilterPad[]) {{ .name = NULL }}, }; + +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, }, + { .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = NULL }}, +}; diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h index 86a4b2462b..2ee5294b26 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -101,7 +101,7 @@ int av_vsink_buffer_get_video_buffer_ref(AVFilterContext *buffer_sink, /** * Get a buffer with filtered data from sink and put it in buf. * - * @param sink pointer to a context of a buffersink AVFilter. + * @param sink pointer to a context of a buffersink or abuffersink AVFilter. * @param buf pointer to the buffer will be written here if buf is non-NULL. buf * must be freed by the caller using avfilter_unref_buffer(). * Buf may also be NULL to query whether a buffer is ready to be @@ -112,4 +112,23 @@ int av_vsink_buffer_get_video_buffer_ref(AVFilterContext *buffer_sink, */ int av_buffersink_read(AVFilterContext *sink, AVFilterBufferRef **buf); +/** + * Same as av_buffersink_read, but with the ability to specify the number of + * samples read. This function is less efficient than av_buffersink_read(), + * because it copies the data around. + * + * @param sink pointer to a context of the abuffersink AVFilter. + * @param buf pointer to the buffer will be written here if buf is non-NULL. buf + * must be freed by the caller using avfilter_unref_buffer(). buf + * will contain exactly nb_samples audio samples, except at the end + * of stream, when it can contain less than nb_samples. + * Buf may also be NULL to query whether a buffer is ready to be + * output. + * + * @warning do not mix this function with av_buffersink_read(). Use only one or + * the other with a single sink, not both. + */ +int av_buffersink_read_samples(AVFilterContext *ctx, AVFilterBufferRef **buf, + int nb_samples); + #endif /* AVFILTER_BUFFERSINK_H */ diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c new file mode 100644 index 0000000000..305d1c49d6 --- /dev/null +++ b/libavfilter/buffersrc.c @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2008 Vitor Sessak + * + * 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 + * memory buffer source filter + */ + +#include "audio.h" +#include "avfilter.h" +#include "buffersrc.h" +#include "formats.h" +#include "vsrc_buffer.h" +#include "avcodec.h" + +#include "libavutil/audioconvert.h" +#include "libavutil/fifo.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" + +typedef struct { + const AVClass *class; + AVFifoBuffer *fifo; + AVRational time_base; ///< time_base to set in the output link + + /* video only */ + int h, w; + enum PixelFormat pix_fmt; + AVRational pixel_aspect; + + /* audio only */ + int sample_rate; + enum AVSampleFormat sample_fmt; + char *sample_fmt_str; + uint64_t channel_layout; + char *channel_layout_str; + + int eof; +} BufferSourceContext; + +#define CHECK_VIDEO_PARAM_CHANGE(s, c, width, height, format)\ + if (c->w != width || c->h != height || c->pix_fmt != format) {\ + av_log(s, AV_LOG_ERROR, "Changing frame properties on the fly is not supported.\n");\ + return AVERROR(EINVAL);\ + } + +#define CHECK_AUDIO_PARAM_CHANGE(s, c, srate, ch_layout, format)\ + if (c->sample_fmt != format || c->sample_rate != srate ||\ + c->channel_layout != ch_layout) {\ + av_log(s, AV_LOG_ERROR, "Changing frame properties on the fly is not supported.\n");\ + return AVERROR(EINVAL);\ + } + +#if FF_API_VSRC_BUFFER_ADD_FRAME +static int av_vsrc_buffer_add_frame_alt(AVFilterContext *buffer_filter, AVFrame *frame, + int64_t pts, AVRational pixel_aspect) +{ + int64_t orig_pts = frame->pts; + AVRational orig_sar = frame->sample_aspect_ratio; + int ret; + + frame->pts = pts; + frame->sample_aspect_ratio = pixel_aspect; + if ((ret = av_buffersrc_write_frame(buffer_filter, frame)) < 0) + return ret; + frame->pts = orig_pts; + frame->sample_aspect_ratio = orig_sar; + + return 0; +} +#endif + +int av_buffersrc_write_frame(AVFilterContext *buffer_filter, AVFrame *frame) +{ + BufferSourceContext *c = buffer_filter->priv; + AVFilterBufferRef *buf; + int ret; + + if (!frame) { + c->eof = 1; + return 0; + } else if (c->eof) + return AVERROR(EINVAL); + + if (!av_fifo_space(c->fifo) && + (ret = av_fifo_realloc2(c->fifo, av_fifo_size(c->fifo) + + sizeof(buf))) < 0) + return ret; + + switch (buffer_filter->outputs[0]->type) { + case AVMEDIA_TYPE_VIDEO: + CHECK_VIDEO_PARAM_CHANGE(buffer_filter, c, frame->width, frame->height, + frame->format); + buf = avfilter_get_video_buffer(buffer_filter->outputs[0], AV_PERM_WRITE, + c->w, c->h); + av_image_copy(buf->data, buf->linesize, frame->data, frame->linesize, + c->pix_fmt, c->w, c->h); + break; + case AVMEDIA_TYPE_AUDIO: + CHECK_AUDIO_PARAM_CHANGE(buffer_filter, c, frame->sample_rate, frame->channel_layout, + frame->format); + buf = ff_get_audio_buffer(buffer_filter->outputs[0], AV_PERM_WRITE, + frame->nb_samples); + av_samples_copy(buf->extended_data, frame->extended_data, + 0, 0, frame->nb_samples, + av_get_channel_layout_nb_channels(frame->channel_layout), + frame->format); + break; + default: + return AVERROR(EINVAL); + } + + avfilter_copy_frame_props(buf, frame); + + if ((ret = av_fifo_generic_write(c->fifo, &buf, sizeof(buf), NULL)) < 0) { + avfilter_unref_buffer(buf); + return ret; + } + + return 0; +} + +static int av_buffersrc_buffer(AVFilterContext *s, AVFilterBufferRef *buf) +{ + BufferSourceContext *c = s->priv; + int ret; + + if (!buf) { + c->eof = 1; + return 0; + } else if (c->eof) + return AVERROR(EINVAL); + + if (!av_fifo_space(c->fifo) && + (ret = av_fifo_realloc2(c->fifo, av_fifo_size(c->fifo) + + sizeof(buf))) < 0) + return ret; + + switch (s->outputs[0]->type) { + case AVMEDIA_TYPE_VIDEO: + CHECK_VIDEO_PARAM_CHANGE(s, c, buf->video->w, buf->video->h, buf->format); + break; + case AVMEDIA_TYPE_AUDIO: + CHECK_AUDIO_PARAM_CHANGE(s, c, buf->audio->sample_rate, buf->audio->channel_layout, + buf->format); + break; + default: + return AVERROR(EINVAL); + } + + if ((ret = av_fifo_generic_write(c->fifo, &buf, sizeof(buf), NULL)) < 0) + return ret; + + return 0; +} + +static av_cold int init_video(AVFilterContext *ctx, const char *args, void *opaque) +{ + BufferSourceContext *c = ctx->priv; + char pix_fmt_str[128]; + int n = 0; + + if (!args || + (n = sscanf(args, "%d:%d:%127[^:]:%d:%d:%d:%d", &c->w, &c->h, pix_fmt_str, + &c->time_base.num, &c->time_base.den, + &c->pixel_aspect.num, &c->pixel_aspect.den)) != 7) { + av_log(ctx, AV_LOG_ERROR, "Expected 7 arguments, but %d found in '%s'\n", n, args); + return AVERROR(EINVAL); + } + if ((c->pix_fmt = av_get_pix_fmt(pix_fmt_str)) == PIX_FMT_NONE) { + char *tail; + c->pix_fmt = strtol(pix_fmt_str, &tail, 10); + if (*tail || c->pix_fmt < 0 || c->pix_fmt >= PIX_FMT_NB) { + av_log(ctx, AV_LOG_ERROR, "Invalid pixel format string '%s'\n", pix_fmt_str); + return AVERROR(EINVAL); + } + } + + if (!(c->fifo = av_fifo_alloc(sizeof(AVFilterBufferRef*)))) + return AVERROR(ENOMEM); + + av_log(ctx, AV_LOG_INFO, "w:%d h:%d pixfmt:%s\n", c->w, c->h, av_pix_fmt_descriptors[c->pix_fmt].name); + return 0; +} + +#define OFFSET(x) offsetof(BufferSourceContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption audio_options[] = { + { "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { 0 }, 0, INT_MAX, A }, + { "sample_rate", NULL, OFFSET(sample_rate), AV_OPT_TYPE_INT, { 0 }, 0, INT_MAX, A }, + { "sample_fmt", NULL, OFFSET(sample_fmt_str), AV_OPT_TYPE_STRING, .flags = A }, + { "channel_layout", NULL, OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A }, + { NULL }, +}; + +static const AVClass abuffer_class = { + .class_name = "abuffer source", + .item_name = av_default_item_name, + .option = audio_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static av_cold int init_audio(AVFilterContext *ctx, const char *args, void *opaque) +{ + BufferSourceContext *s = ctx->priv; + int ret = 0; + + s->class = &abuffer_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); + goto fail; + } + + s->sample_fmt = av_get_sample_fmt(s->sample_fmt_str); + if (s->sample_fmt == AV_SAMPLE_FMT_NONE) { + av_log(ctx, AV_LOG_ERROR, "Invalid sample format %s.\n", + s->sample_fmt_str); + ret = AVERROR(EINVAL); + goto fail; + } + + s->channel_layout = av_get_channel_layout(s->channel_layout_str); + if (!s->channel_layout) { + av_log(ctx, AV_LOG_ERROR, "Invalid channel layout %s.\n", + s->channel_layout_str); + ret = AVERROR(EINVAL); + goto fail; + } + + if (!(s->fifo = av_fifo_alloc(sizeof(AVFilterBufferRef*)))) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if (!s->time_base.num) + s->time_base = (AVRational){1, s->sample_rate}; + + av_log(ctx, AV_LOG_VERBOSE, "tb:%d/%d samplefmt:%s samplerate: %d " + "ch layout:%s\n", s->time_base.num, s->time_base.den, s->sample_fmt_str, + s->sample_rate, s->channel_layout_str); + +fail: + av_opt_free(s); + return ret; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + BufferSourceContext *s = ctx->priv; + while (s->fifo && av_fifo_size(s->fifo)) { + AVFilterBufferRef *buf; + av_fifo_generic_read(s->fifo, &buf, sizeof(buf), NULL); + avfilter_unref_buffer(buf); + } + av_fifo_free(s->fifo); + s->fifo = NULL; +} + +static int query_formats(AVFilterContext *ctx) +{ + BufferSourceContext *c = ctx->priv; + AVFilterChannelLayouts *channel_layouts = NULL; + AVFilterFormats *formats = NULL; + AVFilterFormats *samplerates = NULL; + + switch (ctx->outputs[0]->type) { + case AVMEDIA_TYPE_VIDEO: + avfilter_add_format(&formats, c->pix_fmt); + avfilter_set_common_formats(ctx, formats); + break; + case AVMEDIA_TYPE_AUDIO: + avfilter_add_format(&formats, c->sample_fmt); + avfilter_set_common_formats(ctx, formats); + + avfilter_add_format(&samplerates, c->sample_rate); + ff_set_common_samplerates(ctx, samplerates); + + ff_add_channel_layout(&channel_layouts, c->channel_layout); + ff_set_common_channel_layouts(ctx, channel_layouts); + break; + default: + return AVERROR(EINVAL); + } + + return 0; +} + +static int config_props(AVFilterLink *link) +{ + BufferSourceContext *c = link->src->priv; + + switch (link->type) { + case AVMEDIA_TYPE_VIDEO: + link->w = c->w; + link->h = c->h; + link->sample_aspect_ratio = c->pixel_aspect; + break; + case AVMEDIA_TYPE_AUDIO: + link->channel_layout = c->channel_layout; + link->sample_rate = c->sample_rate; + break; + default: + return AVERROR(EINVAL); + } + + link->time_base = c->time_base; + return 0; +} + +static int request_frame(AVFilterLink *link) +{ + BufferSourceContext *c = link->src->priv; + AVFilterBufferRef *buf; + + if (!av_fifo_size(c->fifo)) { + if (c->eof) + return AVERROR_EOF; + return AVERROR(EAGAIN); + } + av_fifo_generic_read(c->fifo, &buf, sizeof(buf), NULL); + + switch (link->type) { + case AVMEDIA_TYPE_VIDEO: + avfilter_start_frame(link, avfilter_ref_buffer(buf, ~0)); + avfilter_draw_slice(link, 0, link->h, 1); + avfilter_end_frame(link); + break; + case AVMEDIA_TYPE_AUDIO: + ff_filter_samples(link, avfilter_ref_buffer(buf, ~0)); + break; + default: + return AVERROR(EINVAL); + } + + avfilter_unref_buffer(buf); + + return 0; +} + +static int poll_frame(AVFilterLink *link) +{ + BufferSourceContext *c = link->src->priv; + int size = av_fifo_size(c->fifo); + if (!size && c->eof) + return AVERROR_EOF; + return size/sizeof(AVFilterBufferRef*); +} + +AVFilter avfilter_vsrc_buffer = { + .name = "buffer", + .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them accessible to the filterchain."), + .priv_size = sizeof(BufferSourceContext), + .query_formats = query_formats, + + .init = init_video, + .uninit = uninit, + + .inputs = (AVFilterPad[]) {{ .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .poll_frame = poll_frame, + .config_props = config_props, }, + { .name = NULL}}, +}; + +AVFilter avfilter_asrc_abuffer = { + .name = "abuffer", + .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."), + .priv_size = sizeof(BufferSourceContext), + .query_formats = query_formats, + + .init = init_audio, + .uninit = uninit, + + .inputs = (AVFilterPad[]) {{ .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .request_frame = request_frame, + .poll_frame = poll_frame, + .config_props = config_props, }, + { .name = NULL}}, +}; diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h index f1f566b46e..25e2fbf6dc 100644 --- a/libavfilter/buffersrc.h +++ b/libavfilter/buffersrc.h @@ -62,4 +62,15 @@ int av_buffersrc_add_ref(AVFilterContext *buffer_src, */ unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src); +/** + * Add a frame to the buffer source. + * + * @param s an instance of the buffersrc filter. + * @param frame frame to be added. + * + * @warning frame data will be memcpy()ed, which may be a big performance + * hit. Use av_buffersrc_buffer() to avoid copying the data. + */ +int av_buffersrc_write_frame(AVFilterContext *s, AVFrame *frame); + #endif /* AVFILTER_BUFFERSRC_H */ diff --git a/libavfilter/defaults.c b/libavfilter/defaults.c index d2e01499be..31dc18d9f7 100644 --- a/libavfilter/defaults.c +++ b/libavfilter/defaults.c @@ -26,111 +26,7 @@ #include "avfilter.h" #include "internal.h" - -void ff_avfilter_default_free_buffer(AVFilterBuffer *ptr) -{ - if (ptr->extended_data != ptr->data) - av_freep(&ptr->extended_data); - av_free(ptr->data[0]); - av_free(ptr); -} - -/* TODO: set the buffer's priv member to a context structure for the whole - * filter chain. This will allow for a buffer pool instead of the constant - * alloc & free cycle currently implemented. */ -AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, int perms, int w, int h) -{ - int linesize[4]; - uint8_t *data[4]; - int i; - AVFilterBufferRef *picref = NULL; - AVFilterPool *pool = link->pool; - - if (pool) { - for (i = 0; i < POOL_SIZE; i++) { - picref = pool->pic[i]; - if (picref && picref->buf->format == link->format && picref->buf->w == w && picref->buf->h == h) { - AVFilterBuffer *pic = picref->buf; - pool->pic[i] = NULL; - pool->count--; - picref->video->w = w; - picref->video->h = h; - picref->perms = perms | AV_PERM_READ; - picref->format = link->format; - pic->refcount = 1; - memcpy(picref->data, pic->data, sizeof(picref->data)); - memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize)); - pool->refcount++; - return picref; - } - } - } else { - pool = link->pool = av_mallocz(sizeof(AVFilterPool)); - pool->refcount = 1; - } - - // align: +2 is needed for swscaler, +16 to be SIMD-friendly - if ((i = av_image_alloc(data, linesize, w, h, link->format, 32)) < 0) - return NULL; - - picref = avfilter_get_video_buffer_ref_from_arrays(data, linesize, - perms, w, h, link->format); - if (!picref) { - av_free(data[0]); - return NULL; - } - memset(data[0], 128, i); - - picref->buf->priv = pool; - picref->buf->free = NULL; - pool->refcount++; - - return picref; -} - -void avfilter_default_start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref) -{ - AVFilterLink *outlink = NULL; - - if (inlink->dst->output_count) - outlink = inlink->dst->outputs[0]; - - if (outlink) { - outlink->out_buf = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h); - avfilter_copy_buffer_ref_props(outlink->out_buf, picref); - avfilter_start_frame(outlink, avfilter_ref_buffer(outlink->out_buf, ~0)); - } -} - -void avfilter_default_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) -{ - AVFilterLink *outlink = NULL; - - if (inlink->dst->output_count) - outlink = inlink->dst->outputs[0]; - - if (outlink) - avfilter_draw_slice(outlink, y, h, slice_dir); -} - -void avfilter_default_end_frame(AVFilterLink *inlink) -{ - AVFilterLink *outlink = NULL; - - if (inlink->dst->output_count) - outlink = inlink->dst->outputs[0]; - - avfilter_unref_buffer(inlink->cur_buf); - inlink->cur_buf = NULL; - - if (outlink) { - if (outlink->out_buf) { - avfilter_unref_buffer(outlink->out_buf); - outlink->out_buf = NULL; - } - avfilter_end_frame(outlink); - } -} +#include "formats.h" static void set_common_formats(AVFilterContext *ctx, AVFilterFormats *fmts, enum AVMediaType type, int offin, int offout) @@ -170,8 +66,8 @@ void avfilter_set_common_sample_formats(AVFilterContext *ctx, AVFilterFormats *f void avfilter_set_common_channel_layouts(AVFilterContext *ctx, AVFilterFormats *formats) { set_common_formats(ctx, formats, AVMEDIA_TYPE_AUDIO, - offsetof(AVFilterLink, in_chlayouts), - offsetof(AVFilterLink, out_chlayouts)); + offsetof(AVFilterLink, in_channel_layouts), + offsetof(AVFilterLink, out_channel_layouts)); } void avfilter_set_common_packing_formats(AVFilterContext *ctx, AVFilterFormats *formats) @@ -180,33 +76,3 @@ void avfilter_set_common_packing_formats(AVFilterContext *ctx, AVFilterFormats * offsetof(AVFilterLink, in_packing), offsetof(AVFilterLink, out_packing)); } - -int avfilter_default_query_formats(AVFilterContext *ctx) -{ - avfilter_set_common_pixel_formats(ctx, avfilter_make_all_formats(AVMEDIA_TYPE_VIDEO)); - avfilter_set_common_sample_formats(ctx, avfilter_make_all_formats(AVMEDIA_TYPE_AUDIO)); - avfilter_set_common_channel_layouts(ctx, avfilter_make_all_channel_layouts()); - avfilter_set_common_packing_formats(ctx, avfilter_make_all_packing_formats()); - - return 0; -} - -void avfilter_null_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) -{ - avfilter_start_frame(link->dst->outputs[0], picref); -} - -void avfilter_null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) -{ - avfilter_draw_slice(link->dst->outputs[0], y, h, slice_dir); -} - -void avfilter_null_end_frame(AVFilterLink *link) -{ - avfilter_end_frame(link->dst->outputs[0]); -} - -AVFilterBufferRef *avfilter_null_get_video_buffer(AVFilterLink *link, int perms, int w, int h) -{ - return avfilter_get_video_buffer(link->dst->outputs[0], perms, w, h); -} diff --git a/libavfilter/formats.c b/libavfilter/formats.c index 569178fca8..41c6da92b7 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -24,63 +24,138 @@ #include "libavutil/audioconvert.h" #include "avfilter.h" #include "internal.h" +#include "formats.h" /** * Add all refs from a to ret and destroy a. */ -static void merge_ref(AVFilterFormats *ret, AVFilterFormats *a) -{ - int i; - - for (i = 0; i < a->refcount; i++) { - ret->refs[ret->refcount] = a->refs[i]; - *ret->refs[ret->refcount++] = ret; - } +#define MERGE_REF(ret, a, fmts, type, fail) \ +do { \ + type ***tmp; \ + int i; \ + \ + if (!(tmp = av_realloc(ret->refs, \ + sizeof(*tmp) * (ret->refcount + a->refcount)))) \ + goto fail; \ + ret->refs = tmp; \ + \ + for (i = 0; i < a->refcount; i ++) { \ + ret->refs[ret->refcount] = a->refs[i]; \ + *ret->refs[ret->refcount++] = ret; \ + } \ + \ + av_freep(&a->refs); \ + av_freep(&a->fmts); \ + av_freep(&a); \ +} while (0) - av_free(a->refs); - av_free(a->formats); - av_free(a); -} +/** + * Add all formats common for a and b to ret, copy the refs and destroy + * a and b. + */ +#define MERGE_FORMATS(ret, a, b, fmts, nb, type, fail) \ +do { \ + int i, j, k = 0, count = FFMIN(a->nb, b->nb); \ + \ + if (!(ret = av_mallocz(sizeof(*ret)))) \ + goto fail; \ + \ + if (count) { \ + if (!(ret->fmts = av_malloc(sizeof(*ret->fmts) * count))) \ + goto fail; \ + for (i = 0; i < a->nb; i++) \ + for (j = 0; j < b->nb; j++) \ + if (a->fmts[i] == b->fmts[j]) { \ + if(k >= FFMIN(a->nb, b->nb)){ \ + av_log(0, AV_LOG_ERROR, "Duplicate formats in avfilter_merge_formats() detected\n"); \ + av_free(ret->fmts); \ + av_free(ret); \ + return NULL; \ + } \ + ret->fmts[k++] = a->fmts[i]; \ + } \ + } \ + ret->nb = k; \ + /* check that there was at least one common format */ \ + if (!ret->nb) \ + goto fail; \ + \ + MERGE_REF(ret, a, fmts, type, fail); \ + MERGE_REF(ret, b, fmts, type, fail); \ +} while (0) AVFilterFormats *avfilter_merge_formats(AVFilterFormats *a, AVFilterFormats *b) { - AVFilterFormats *ret; - unsigned i, j, k = 0; + AVFilterFormats *ret = NULL; if (a == b) return a; - ret = av_mallocz(sizeof(*ret)); - - /* merge list of formats */ - ret->formats = av_malloc(sizeof(*ret->formats) * FFMIN(a->format_count, - b->format_count)); - for (i = 0; i < a->format_count; i++) - for (j = 0; j < b->format_count; j++) - if (a->formats[i] == b->formats[j]){ - if(k >= FFMIN(a->format_count, b->format_count)){ - av_log(0, AV_LOG_ERROR, "Duplicate formats in avfilter_merge_formats() detected\n"); - av_free(ret->formats); - av_free(ret); - return NULL; - } - ret->formats[k++] = a->formats[i]; - } - - ret->format_count = k; - /* check that there was at least one common format */ - if (!ret->format_count) { - av_free(ret->formats); - av_free(ret); - return NULL; + MERGE_FORMATS(ret, a, b, formats, format_count, AVFilterFormats, fail); + + return ret; +fail: + if (ret) { + av_freep(&ret->refs); + av_freep(&ret->formats); } + av_freep(&ret); + return NULL; +} + +AVFilterFormats *ff_merge_samplerates(AVFilterFormats *a, + AVFilterFormats *b) +{ + AVFilterFormats *ret = NULL; - ret->refs = av_malloc(sizeof(*ret->refs) * (a->refcount + b->refcount)); + if (a == b) return a; - merge_ref(ret, a); - merge_ref(ret, b); + if (a->format_count && b->format_count) { + MERGE_FORMATS(ret, a, b, formats, format_count, AVFilterFormats, fail); + } else if (a->format_count) { + MERGE_REF(a, b, formats, AVFilterFormats, fail); + ret = a; + } else { + MERGE_REF(b, a, formats, AVFilterFormats, fail); + ret = b; + } return ret; +fail: + if (ret) { + av_freep(&ret->refs); + av_freep(&ret->formats); + } + av_freep(&ret); + return NULL; +} + +AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a, + AVFilterChannelLayouts *b) +{ + AVFilterChannelLayouts *ret = NULL; + + if (a == b) return a; + + if (a->nb_channel_layouts && b->nb_channel_layouts) { + MERGE_FORMATS(ret, a, b, channel_layouts, nb_channel_layouts, + AVFilterChannelLayouts, fail); + } else if (a->nb_channel_layouts) { + MERGE_REF(a, b, channel_layouts, AVFilterChannelLayouts, fail); + ret = a; + } else { + MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, fail); + ret = b; + } + + return ret; +fail: + if (ret) { + av_freep(&ret->refs); + av_freep(&ret->channel_layouts); + } + av_freep(&ret); + return NULL; } int ff_fmt_is_in(int fmt, const int *fmts) @@ -146,30 +221,40 @@ AVFilterFormats *avfilter_make_format_list(const int *fmts) return formats; } -AVFilterFormats *avfilter_make_format64_list(const int64_t *fmts) +AVFilterChannelLayouts *avfilter_make_format64_list(const int64_t *fmts) { MAKE_FORMAT_LIST(); if (count) memcpy(formats->formats, fmts, sizeof(*formats->formats) * count); - return formats; + return (AVFilterChannelLayouts*)formats; } +#define ADD_FORMAT(f, fmt, type, list, nb) \ +do { \ + type *fmts; \ + \ + if (!(*f) && !(*f = av_mallocz(sizeof(**f)))) \ + return AVERROR(ENOMEM); \ + \ + fmts = av_realloc((*f)->list, \ + sizeof(*(*f)->list) * ((*f)->nb + 1));\ + if (!fmts) \ + return AVERROR(ENOMEM); \ + \ + (*f)->list = fmts; \ + (*f)->list[(*f)->nb++] = fmt; \ + return 0; \ +} while (0) + int avfilter_add_format(AVFilterFormats **avff, int64_t fmt) { - int64_t *fmts; - - if (!(*avff) && !(*avff = av_mallocz(sizeof(**avff)))) - return AVERROR(ENOMEM); - - fmts = av_realloc((*avff)->formats, - sizeof(*(*avff)->formats) * ((*avff)->format_count+1)); - if (!fmts) - return AVERROR(ENOMEM); + ADD_FORMAT(avff, fmt, int64_t, formats, format_count); +} - (*avff)->formats = fmts; - (*avff)->formats[(*avff)->format_count++] = fmt; - return 0; +int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout) +{ + ADD_FORMAT(l, channel_layout, uint64_t, channel_layouts, nb_channel_layouts); } #if FF_API_OLD_ALL_FORMATS_API @@ -199,10 +284,10 @@ const int64_t avfilter_all_channel_layouts[] = { -1 }; -AVFilterFormats *avfilter_make_all_channel_layouts(void) -{ - return avfilter_make_format64_list(avfilter_all_channel_layouts); -} +// AVFilterFormats *avfilter_make_all_channel_layouts(void) +// { +// return avfilter_make_format64_list(avfilter_all_channel_layouts); +// } AVFilterFormats *avfilter_make_all_packing_formats(void) { @@ -215,53 +300,163 @@ AVFilterFormats *avfilter_make_all_packing_formats(void) return avfilter_make_format_list(packing); } -void avfilter_formats_ref(AVFilterFormats *f, AVFilterFormats **ref) +AVFilterFormats *ff_all_samplerates(void) { - *ref = f; - f->refs = av_realloc(f->refs, sizeof(*f->refs) * ++f->refcount); - f->refs[f->refcount-1] = ref; + AVFilterFormats *ret = av_mallocz(sizeof(*ret)); + return ret; } -static int find_ref_index(AVFilterFormats **ref) +AVFilterChannelLayouts *ff_all_channel_layouts(void) { - int i; - for (i = 0; i < (*ref)->refcount; i++) - if ((*ref)->refs[i] == ref) - return i; - return -1; + AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret)); + return ret; } -void avfilter_formats_unref(AVFilterFormats **ref) +#define FORMATS_REF(f, ref) \ +do { \ + *ref = f; \ + f->refs = av_realloc(f->refs, sizeof(*f->refs) * ++f->refcount); \ + f->refs[f->refcount-1] = ref; \ +} while (0) + +void ff_channel_layouts_ref(AVFilterChannelLayouts *f, AVFilterChannelLayouts **ref) +{ + FORMATS_REF(f, ref); +} + +void avfilter_formats_ref(AVFilterFormats *f, AVFilterFormats **ref) { - int idx; + FORMATS_REF(f, ref); +} - if (!*ref) - return; +#define FIND_REF_INDEX(ref, idx) \ +do { \ + int i; \ + for (i = 0; i < (*ref)->refcount; i ++) \ + if((*ref)->refs[i] == ref) { \ + idx = i; \ + break; \ + } \ +} while (0) + +#define FORMATS_UNREF(ref, list) \ +do { \ + int idx = -1; \ + \ + if (!*ref) \ + return; \ + \ + FIND_REF_INDEX(ref, idx); \ + \ + if (idx >= 0) \ + memmove((*ref)->refs + idx, (*ref)->refs + idx + 1, \ + sizeof(*(*ref)->refs) * ((*ref)->refcount - idx - 1)); \ + \ + if(!--(*ref)->refcount) { \ + av_free((*ref)->list); \ + av_free((*ref)->refs); \ + av_free(*ref); \ + } \ + *ref = NULL; \ +} while (0) - idx = find_ref_index(ref); +void avfilter_formats_unref(AVFilterFormats **ref) +{ + FORMATS_UNREF(ref, formats); +} - if(idx >= 0) - memmove((*ref)->refs + idx, (*ref)->refs + idx + 1, - sizeof(*(*ref)->refs) * ((*ref)->refcount - idx - 1)); +void ff_channel_layouts_unref(AVFilterChannelLayouts **ref) +{ + FORMATS_UNREF(ref, channel_layouts); +} - if (!--(*ref)->refcount) { - av_free((*ref)->formats); - av_free((*ref)->refs); - av_free(*ref); - } - *ref = NULL; +#define FORMATS_CHANGEREF(oldref, newref) \ +do { \ + int idx = -1; \ + \ + FIND_REF_INDEX(oldref, idx); \ + \ + if (idx >= 0) { \ + (*oldref)->refs[idx] = newref; \ + *newref = *oldref; \ + *oldref = NULL; \ + } \ +} while (0) + +void ff_channel_layouts_changeref(AVFilterChannelLayouts **oldref, + AVFilterChannelLayouts **newref) +{ + FORMATS_CHANGEREF(oldref, newref); } void avfilter_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref) { - int idx = find_ref_index(oldref); + FORMATS_CHANGEREF(oldref, newref); +} + +#define SET_COMMON_FORMATS(ctx, fmts, in_fmts, out_fmts, ref, list) \ +{ \ + int count = 0, i; \ + \ + for (i = 0; i < ctx->input_count; i++) { \ + if (ctx->inputs[i]) { \ + ref(fmts, &ctx->inputs[i]->out_fmts); \ + count++; \ + } \ + } \ + for (i = 0; i < ctx->output_count; i++) { \ + if (ctx->outputs[i]) { \ + ref(fmts, &ctx->outputs[i]->in_fmts); \ + count++; \ + } \ + } \ + \ + if (!count) { \ + av_freep(&fmts->list); \ + av_freep(&fmts->refs); \ + av_freep(&fmts); \ + } \ +} + +void ff_set_common_channel_layouts(AVFilterContext *ctx, + AVFilterChannelLayouts *layouts) +{ + SET_COMMON_FORMATS(ctx, layouts, in_channel_layouts, out_channel_layouts, + ff_channel_layouts_ref, channel_layouts); +} + +void ff_set_common_samplerates(AVFilterContext *ctx, + AVFilterFormats *samplerates) +{ + SET_COMMON_FORMATS(ctx, samplerates, in_samplerates, out_samplerates, + avfilter_formats_ref, formats); +} + +/** + * A helper for query_formats() which sets all links to the same list of + * formats. If there are no links hooked to this filter, the list of formats is + * freed. + */ +void avfilter_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats) +{ + SET_COMMON_FORMATS(ctx, formats, in_formats, out_formats, + avfilter_formats_ref, formats); +} - if (idx >= 0) { - (*oldref)->refs[idx] = newref; - *newref = *oldref; - *oldref = NULL; +int avfilter_default_query_formats(AVFilterContext *ctx) +{ + enum AVMediaType type = ctx->inputs && ctx->inputs [0] ? ctx->inputs [0]->type : + ctx->outputs && ctx->outputs[0] ? ctx->outputs[0]->type : + AVMEDIA_TYPE_VIDEO; + + avfilter_set_common_formats(ctx, avfilter_all_formats(type)); + if (type == AVMEDIA_TYPE_AUDIO) { + ff_set_common_channel_layouts(ctx, ff_all_channel_layouts()); + ff_set_common_samplerates(ctx, ff_all_samplerates()); } + + return 0; } /* internal functions for parsing audio format arguments */ diff --git a/libavfilter/formats.h b/libavfilter/formats.h new file mode 100644 index 0000000000..7b0eab5dea --- /dev/null +++ b/libavfilter/formats.h @@ -0,0 +1,81 @@ +/* + * 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 + */ + +#ifndef AVFILTER_FORMATS_H +#define AVFILTER_FORMATS_H + +#include "avfilter.h" + +typedef struct AVFilterChannelLayouts { + uint64_t *channel_layouts; ///< list of channel layouts + int nb_channel_layouts; ///< number of channel layouts + + unsigned refcount; ///< number of references to this list + struct AVFilterChannelLayouts ***refs; ///< references to this list +} AVFilterChannelLayouts; + +/** + * Return a channel layouts/samplerates list which contains the intersection of + * the layouts/samplerates of a and b. Also, all the references of a, all the + * references of b, and a and b themselves will be deallocated. + * + * If a and b do not share any common elements, neither is modified, and NULL + * is returned. + */ +AVFilterChannelLayouts *ff_merge_channel_layouts(AVFilterChannelLayouts *a, + AVFilterChannelLayouts *b); +AVFilterFormats *ff_merge_samplerates(AVFilterFormats *a, + AVFilterFormats *b); + +/** + * Construct an empty AVFilterChannelLayouts/AVFilterFormats struct -- + * representing any channel layout/sample rate. + */ +AVFilterChannelLayouts *ff_all_channel_layouts(void); +AVFilterFormats *ff_all_samplerates(void); + +AVFilterChannelLayouts *avfilter_make_format64_list(const int64_t *fmts); + + +/** + * A helper for query_formats() which sets all links to the same list of channel + * layouts/sample rates. If there are no links hooked to this filter, the list + * is freed. + */ +void ff_set_common_channel_layouts(AVFilterContext *ctx, + AVFilterChannelLayouts *layouts); +void ff_set_common_samplerates(AVFilterContext *ctx, + AVFilterFormats *samplerates); + +int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout); + +/** + * Add *ref as a new reference to f. + */ +void ff_channel_layouts_ref(AVFilterChannelLayouts *f, + AVFilterChannelLayouts **ref); + +/** + * Remove a reference to a channel layouts list. + */ +void ff_channel_layouts_unref(AVFilterChannelLayouts **ref); + +void ff_channel_layouts_changeref(AVFilterChannelLayouts **oldref, + AVFilterChannelLayouts **newref); + +#endif // AVFILTER_FORMATS_H diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 09d605541f..034a6c9cab 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -26,6 +26,7 @@ #include "avfilter.h" #include "avfiltergraph.h" +#include "formats.h" #define POOL_SIZE 32 typedef struct AVFilterPool { @@ -151,6 +152,10 @@ static inline void ff_null_start_frame_keep_ref(AVFilterLink *inlink, void ff_update_link_current_pts(AVFilterLink *link, int64_t pts); +void ff_free_pool(AVFilterPool *pool); + +void ff_command_queue_pop(AVFilterContext *filter); + #define FF_DPRINTF_START(ctx, func) av_dlog(NULL, "%-16s: ", #func) void ff_dlog_link(void *ctx, AVFilterLink *link, int end); diff --git a/libavfilter/sink_buffer.c b/libavfilter/sink_buffer.c index b0cc519217..ab7aa4f5a8 100644 --- a/libavfilter/sink_buffer.c +++ b/libavfilter/sink_buffer.c @@ -269,19 +269,22 @@ static int asink_query_formats(AVFilterContext *ctx) { BufferSinkContext *buf = ctx->priv; AVFilterFormats *formats = NULL; + AVFilterChannelLayouts *layouts = NULL; if (!(formats = avfilter_make_format_list(buf->sample_fmts))) return AVERROR(ENOMEM); avfilter_set_common_sample_formats(ctx, formats); - if (!(formats = avfilter_make_format64_list(buf->channel_layouts))) + if (!(layouts = avfilter_make_format64_list(buf->channel_layouts))) return AVERROR(ENOMEM); - avfilter_set_common_channel_layouts(ctx, formats); + ff_set_common_channel_layouts(ctx, layouts); if (!(formats = avfilter_make_format_list(buf->packing_fmts))) return AVERROR(ENOMEM); avfilter_set_common_packing_formats(ctx, formats); + ff_set_common_samplerates (ctx, ff_all_samplerates()); + return 0; } diff --git a/libavfilter/src_buffer.c b/libavfilter/src_buffer.c index f8d572d085..cadc4ee2a4 100644 --- a/libavfilter/src_buffer.c +++ b/libavfilter/src_buffer.c @@ -507,14 +507,15 @@ static int query_formats_audio(AVFilterContext *ctx) { BufferSourceContext *abuffer = ctx->priv; AVFilterFormats *formats; + AVFilterChannelLayouts *layouts; formats = NULL; avfilter_add_format(&formats, abuffer->sample_format); avfilter_set_common_sample_formats(ctx, formats); - formats = NULL; - avfilter_add_format(&formats, abuffer->channel_layout); - avfilter_set_common_channel_layouts(ctx, formats); + layouts = NULL; + ff_add_channel_layout(&layouts, abuffer->channel_layout); + ff_set_common_channel_layouts(ctx, layouts); formats = NULL; avfilter_add_format(&formats, abuffer->packing_format); @@ -653,8 +654,6 @@ AVFilter avfilter_vsrc_buffer = { { .name = NULL}}, }; -#if CONFIG_ABUFFER_FILTER - AVFilter avfilter_asrc_abuffer = { .name = "abuffer", .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."), @@ -673,4 +672,3 @@ AVFilter avfilter_asrc_abuffer = { { .name = NULL}}, }; -#endif diff --git a/libavfilter/src_movie.c b/libavfilter/src_movie.c index a00d30d9cf..30624559fc 100644 --- a/libavfilter/src_movie.c +++ b/libavfilter/src_movie.c @@ -38,6 +38,7 @@ #include "audio.h" #include "avcodec.h" #include "avfilter.h" +#include "formats.h" typedef struct { /* common A/V fields */ @@ -370,12 +371,14 @@ static int amovie_query_formats(AVFilterContext *ctx) enum AVSampleFormat sample_fmts[] = { c->sample_fmt, -1 }; int packing_fmts[] = { AVFILTER_PACKED, -1 }; + int sample_rates[] = { c->sample_rate, -1 }; int64_t chlayouts[] = { c->channel_layout ? c->channel_layout : av_get_default_channel_layout(c->channels), -1 }; avfilter_set_common_sample_formats (ctx, avfilter_make_format_list(sample_fmts)); avfilter_set_common_packing_formats(ctx, avfilter_make_format_list(packing_fmts)); - avfilter_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts)); + ff_set_common_samplerates (ctx, avfilter_make_format_list(sample_rates)); + ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts)); return 0; } diff --git a/libavfilter/version.h b/libavfilter/version.h index 05318047f4..a8e4f893d4 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -29,7 +29,7 @@ #include "libavutil/avutil.h" #define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 73 +#define LIBAVFILTER_VERSION_MINOR 74 #define LIBAVFILTER_VERSION_MICRO 100 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ @@ -50,5 +50,8 @@ #ifndef FF_API_SAMPLERATE64 #define FF_API_SAMPLERATE64 (LIBAVFILTER_VERSION_MAJOR < 3) #endif +#ifndef FF_API_VSRC_BUFFER_ADD_FRAME +#define FF_API_VSRC_BUFFER_ADD_FRAME (LIBAVFILTER_VERSION_MAJOR < 3) +#endif #endif // AVFILTER_VERSION_H diff --git a/libavfilter/vf_yadif.c b/libavfilter/vf_yadif.c index 684f9099fa..fb4862d0be 100644 --- a/libavfilter/vf_yadif.c +++ b/libavfilter/vf_yadif.c @@ -59,6 +59,7 @@ typedef struct { int w, int prefs, int mrefs, int parity, int mode); const AVPixFmtDescriptor *csp; + int eof; } YADIFContext; #define CHECK(j)\ @@ -216,22 +217,11 @@ static void return_frame(AVFilterContext *ctx, int is_second) filter(ctx, yadif->out, tff ^ !is_second, tff); if (is_second) { - if (yadif->next->pts != AV_NOPTS_VALUE && - yadif->cur->pts != AV_NOPTS_VALUE) { - uint64_t next_pts = yadif->next->pts; - uint64_t cur_pts = yadif->cur->pts; - uint64_t prev_pts = yadif->prev->pts; - - uint64_t ft = FFMIN3( cur_pts-prev_pts, - next_pts-cur_pts, - (next_pts-prev_pts)/2); - - if(next_pts - cur_pts < 2*ft) - yadif->out->pts = - (next_pts&cur_pts) + - ((next_pts^cur_pts)>>1); - else - yadif->out->pts = cur_pts + ft/2; + int64_t cur_pts = yadif->cur->pts; + int64_t next_pts = yadif->next->pts; + + if (next_pts != AV_NOPTS_VALUE && cur_pts != AV_NOPTS_VALUE) { + yadif->out->pts = cur_pts + next_pts; } else { yadif->out->pts = AV_NOPTS_VALUE; } @@ -264,6 +254,8 @@ static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref) yadif->out = avfilter_ref_buffer(yadif->cur, AV_PERM_READ); avfilter_unref_buffer(yadif->prev); yadif->prev = NULL; + if (yadif->out->pts != AV_NOPTS_VALUE) + yadif->out->pts *= 2; avfilter_start_frame(ctx->outputs[0], yadif->out); return; } @@ -276,6 +268,8 @@ static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref) avfilter_copy_buffer_ref_props(yadif->out, yadif->cur); yadif->out->video->interlaced = 0; + if (yadif->out->pts != AV_NOPTS_VALUE) + yadif->out->pts *= 2; avfilter_start_frame(ctx->outputs[0], yadif->out); } @@ -309,8 +303,21 @@ static int request_frame(AVFilterLink *link) do { int ret; - if ((ret = avfilter_request_frame(link->src->inputs[0]))) + if (yadif->eof) + return AVERROR_EOF; + + ret = avfilter_request_frame(link->src->inputs[0]); + + if (ret == AVERROR_EOF && yadif->next) { + AVFilterBufferRef *next = avfilter_ref_buffer(yadif->next, AV_PERM_READ); + next->pts = yadif->next->pts * 2 - yadif->cur->pts; + + start_frame(link->src->inputs[0], next); + end_frame(link->src->inputs[0]); + yadif->eof = 1; + } else if (ret < 0) { return ret; + } } while (!yadif->cur); return 0; @@ -411,6 +418,16 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { } +static int config_props(AVFilterLink *link) +{ + link->time_base.num = link->src->inputs[0]->time_base.num; + link->time_base.den = link->src->inputs[0]->time_base.den * 2; + link->w = link->src->inputs[0]->w; + link->h = link->src->inputs[0]->h; + + return 0; +} + AVFilter avfilter_vf_yadif = { .name = "yadif", .description = NULL_IF_CONFIG_SMALL("Deinterlace the input image."), @@ -432,6 +449,7 @@ AVFilter avfilter_vf_yadif = { .outputs = (const AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, .poll_frame = poll_frame, - .request_frame = request_frame, }, + .request_frame = request_frame, + .config_props = config_props, }, { .name = NULL}}, }; diff --git a/libavfilter/video.c b/libavfilter/video.c new file mode 100644 index 0000000000..86dfd901f8 --- /dev/null +++ b/libavfilter/video.c @@ -0,0 +1,345 @@ +/* + * 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 + */ + +#include "libavutil/imgutils.h" + +#include "avfilter.h" +#include "internal.h" + +static char *ff_get_ref_perms_string(char *buf, size_t buf_size, int perms) +{ + snprintf(buf, buf_size, "%s%s%s%s%s%s", + perms & AV_PERM_READ ? "r" : "", + perms & AV_PERM_WRITE ? "w" : "", + perms & AV_PERM_PRESERVE ? "p" : "", + perms & AV_PERM_REUSE ? "u" : "", + perms & AV_PERM_REUSE2 ? "U" : "", + perms & AV_PERM_NEG_LINESIZES ? "n" : ""); + return buf; +} + +static void ff_dlog_ref(void *ctx, AVFilterBufferRef *ref, int end) +{ + av_unused char buf[16]; + av_dlog(ctx, + "ref[%p buf:%p refcount:%d perms:%s data:%p linesize[%d, %d, %d, %d] pts:%"PRId64" pos:%"PRId64, + ref, ref->buf, ref->buf->refcount, ff_get_ref_perms_string(buf, sizeof(buf), ref->perms), ref->data[0], + ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3], + ref->pts, ref->pos); + + if (ref->video) { + av_dlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c", + ref->video->sample_aspect_ratio.num, ref->video->sample_aspect_ratio.den, + ref->video->w, ref->video->h, + !ref->video->interlaced ? 'P' : /* Progressive */ + ref->video->top_field_first ? 'T' : 'B', /* Top / Bottom */ + ref->video->key_frame, + av_get_picture_type_char(ref->video->pict_type)); + } + if (ref->audio) { + av_dlog(ctx, " cl:%"PRId64"d n:%d r:%d p:%d", + ref->audio->channel_layout, + ref->audio->nb_samples, + ref->audio->sample_rate, + ref->audio->planar); + } + + av_dlog(ctx, "]%s", end ? "\n" : ""); +} + +AVFilterBufferRef *avfilter_null_get_video_buffer(AVFilterLink *link, int perms, int w, int h) +{ + return avfilter_get_video_buffer(link->dst->outputs[0], perms, w, h); +} + +AVFilterBufferRef *avfilter_default_get_video_buffer(AVFilterLink *link, int perms, int w, int h) +{ + int linesize[4]; + uint8_t *data[4]; + int i; + AVFilterBufferRef *picref = NULL; + AVFilterPool *pool = link->pool; + + if (pool) { + for (i = 0; i < POOL_SIZE; i++) { + picref = pool->pic[i]; + if (picref && picref->buf->format == link->format && picref->buf->w == w && picref->buf->h == h) { + AVFilterBuffer *pic = picref->buf; + pool->pic[i] = NULL; + pool->count--; + picref->video->w = w; + picref->video->h = h; + picref->perms = perms | AV_PERM_READ; + picref->format = link->format; + pic->refcount = 1; + memcpy(picref->data, pic->data, sizeof(picref->data)); + memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize)); + pool->refcount++; + return picref; + } + } + } else { + pool = link->pool = av_mallocz(sizeof(AVFilterPool)); + pool->refcount = 1; + } + + // align: +2 is needed for swscaler, +16 to be SIMD-friendly + if ((i = av_image_alloc(data, linesize, w, h, link->format, 32)) < 0) + return NULL; + + picref = avfilter_get_video_buffer_ref_from_arrays(data, linesize, + perms, w, h, link->format); + if (!picref) { + av_free(data[0]); + return NULL; + } + + memset(data[0], 128, i); + + picref->buf->priv = pool; + picref->buf->free = NULL; + pool->refcount++; + + return picref; +} + +AVFilterBufferRef * +avfilter_get_video_buffer_ref_from_arrays(uint8_t * const data[4], const int linesize[4], int perms, + int w, int h, enum PixelFormat format) +{ + AVFilterBuffer *pic = av_mallocz(sizeof(AVFilterBuffer)); + AVFilterBufferRef *picref = av_mallocz(sizeof(AVFilterBufferRef)); + + if (!pic || !picref) + goto fail; + + picref->buf = pic; + picref->buf->free = ff_avfilter_default_free_buffer; + if (!(picref->video = av_mallocz(sizeof(AVFilterBufferRefVideoProps)))) + goto fail; + + pic->w = picref->video->w = w; + pic->h = picref->video->h = h; + + /* make sure the buffer gets read permission or it's useless for output */ + picref->perms = perms | AV_PERM_READ; + + pic->refcount = 1; + picref->type = AVMEDIA_TYPE_VIDEO; + pic->format = picref->format = format; + + memcpy(pic->data, data, 4*sizeof(data[0])); + memcpy(pic->linesize, linesize, 4*sizeof(linesize[0])); + memcpy(picref->data, pic->data, sizeof(picref->data)); + memcpy(picref->linesize, pic->linesize, sizeof(picref->linesize)); + + pic-> extended_data = pic->data; + picref->extended_data = picref->data; + + return picref; + +fail: + if (picref && picref->video) + av_free(picref->video); + av_free(picref); + av_free(pic); + return NULL; +} + +AVFilterBufferRef *avfilter_get_video_buffer(AVFilterLink *link, int perms, int w, int h) +{ + AVFilterBufferRef *ret = NULL; + + av_unused char buf[16]; + FF_DPRINTF_START(NULL, get_video_buffer); ff_dlog_link(NULL, link, 0); + av_dlog(NULL, " perms:%s w:%d h:%d\n", ff_get_ref_perms_string(buf, sizeof(buf), perms), w, h); + + if (link->dstpad->get_video_buffer) + ret = link->dstpad->get_video_buffer(link, perms, w, h); + + if (!ret) + ret = avfilter_default_get_video_buffer(link, perms, w, h); + + if (ret) + ret->type = AVMEDIA_TYPE_VIDEO; + + FF_DPRINTF_START(NULL, get_video_buffer); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " returning "); ff_dlog_ref(NULL, ret, 1); + + return ret; +} + +void avfilter_null_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) +{ + avfilter_start_frame(link->dst->outputs[0], picref); +} + +void avfilter_default_start_frame(AVFilterLink *inlink, AVFilterBufferRef *picref) +{ + AVFilterLink *outlink = NULL; + + if (inlink->dst->output_count) + outlink = inlink->dst->outputs[0]; + + if (outlink) { + outlink->out_buf = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h); + avfilter_copy_buffer_ref_props(outlink->out_buf, picref); + avfilter_start_frame(outlink, avfilter_ref_buffer(outlink->out_buf, ~0)); + } +} + +/* XXX: should we do the duplicating of the picture ref here, instead of + * forcing the source filter to do it? */ +void avfilter_start_frame(AVFilterLink *link, AVFilterBufferRef *picref) +{ + void (*start_frame)(AVFilterLink *, AVFilterBufferRef *); + AVFilterPad *dst = link->dstpad; + int perms = picref->perms; + AVFilterCommand *cmd= link->dst->command_queue; + + FF_DPRINTF_START(NULL, start_frame); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " "); ff_dlog_ref(NULL, picref, 1); + + if (!(start_frame = dst->start_frame)) + start_frame = avfilter_default_start_frame; + + if (picref->linesize[0] < 0) + perms |= AV_PERM_NEG_LINESIZES; + /* prepare to copy the picture if it has insufficient permissions */ + if ((dst->min_perms & perms) != dst->min_perms || dst->rej_perms & perms) { + av_log(link->dst, AV_LOG_DEBUG, + "frame copy needed (have perms %x, need %x, reject %x)\n", + picref->perms, + link->dstpad->min_perms, link->dstpad->rej_perms); + + link->cur_buf = avfilter_get_video_buffer(link, dst->min_perms, link->w, link->h); + link->src_buf = picref; + avfilter_copy_buffer_ref_props(link->cur_buf, link->src_buf); + } + else + link->cur_buf = picref; + + while(cmd && cmd->time <= picref->pts * av_q2d(link->time_base)){ + av_log(link->dst, AV_LOG_DEBUG, + "Processing command time:%f command:%s arg:%s\n", + cmd->time, cmd->command, cmd->arg); + avfilter_process_command(link->dst, cmd->command, cmd->arg, 0, 0, cmd->flags); + ff_command_queue_pop(link->dst); + cmd= link->dst->command_queue; + } + + start_frame(link, link->cur_buf); + ff_update_link_current_pts(link, link->cur_buf->pts); +} + +void avfilter_null_end_frame(AVFilterLink *link) +{ + avfilter_end_frame(link->dst->outputs[0]); +} + +void avfilter_default_end_frame(AVFilterLink *inlink) +{ + AVFilterLink *outlink = NULL; + + if (inlink->dst->output_count) + outlink = inlink->dst->outputs[0]; + + avfilter_unref_buffer(inlink->cur_buf); + inlink->cur_buf = NULL; + + if (outlink) { + if (outlink->out_buf) { + avfilter_unref_buffer(outlink->out_buf); + outlink->out_buf = NULL; + } + avfilter_end_frame(outlink); + } +} + +void avfilter_end_frame(AVFilterLink *link) +{ + void (*end_frame)(AVFilterLink *); + + if (!(end_frame = link->dstpad->end_frame)) + end_frame = avfilter_default_end_frame; + + end_frame(link); + + /* unreference the source picture if we're feeding the destination filter + * a copied version dues to permission issues */ + if (link->src_buf) { + avfilter_unref_buffer(link->src_buf); + link->src_buf = NULL; + } +} + +void avfilter_null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) +{ + avfilter_draw_slice(link->dst->outputs[0], y, h, slice_dir); +} + +void avfilter_default_draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) +{ + AVFilterLink *outlink = NULL; + + if (inlink->dst->output_count) + outlink = inlink->dst->outputs[0]; + + if (outlink) + avfilter_draw_slice(outlink, y, h, slice_dir); +} + +void avfilter_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) +{ + uint8_t *src[4], *dst[4]; + int i, j, vsub; + void (*draw_slice)(AVFilterLink *, int, int, int); + + FF_DPRINTF_START(NULL, draw_slice); ff_dlog_link(NULL, link, 0); av_dlog(NULL, " y:%d h:%d dir:%d\n", y, h, slice_dir); + + /* copy the slice if needed for permission reasons */ + if (link->src_buf) { + vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h; + + for (i = 0; i < 4; i++) { + if (link->src_buf->data[i]) { + src[i] = link->src_buf-> data[i] + + (y >> (i==1 || i==2 ? vsub : 0)) * link->src_buf-> linesize[i]; + dst[i] = link->cur_buf->data[i] + + (y >> (i==1 || i==2 ? vsub : 0)) * link->cur_buf->linesize[i]; + } else + src[i] = dst[i] = NULL; + } + + for (i = 0; i < 4; i++) { + int planew = + av_image_get_linesize(link->format, link->cur_buf->video->w, i); + + if (!src[i]) continue; + + for (j = 0; j < h >> (i==1 || i==2 ? vsub : 0); j++) { + memcpy(dst[i], src[i], planew); + src[i] += link->src_buf->linesize[i]; + dst[i] += link->cur_buf->linesize[i]; + } + } + } + + if (!(draw_slice = link->dstpad->draw_slice)) + draw_slice = avfilter_default_draw_slice; + draw_slice(link, y, h, slice_dir); +} + |