From fa0662393096a1ece73ccc321cd5b8948e96e431 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sun, 17 Jun 2012 10:09:54 +0200 Subject: [PATCH 1/9] lavfi/audio: eliminate ff_default_filter_samples(). It currently does the following: 1) get a zeroed audio buffer 2) copy some properties (but not the data) of the input buffer to it 3) pass this buffer to the output filter This looks useless and is indeed not used by any filters, therefore delete it. Make ff_null_filter_samples() (just pass the buffer to the next filter) the new default. --- libavfilter/af_aformat.c | 3 +-- libavfilter/af_anull.c | 3 +-- libavfilter/audio.c | 26 +++----------------------- libavfilter/audio.h | 6 ------ 4 files changed, 5 insertions(+), 33 deletions(-) diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c index 3a75b925dd..5b00d7de9c 100644 --- a/libavfilter/af_aformat.c +++ b/libavfilter/af_aformat.c @@ -139,8 +139,7 @@ AVFilter avfilter_af_aformat = { .priv_size = sizeof(AFormatContext), .inputs = (AVFilterPad[]) {{ .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_samples = ff_null_filter_samples }, + .type = AVMEDIA_TYPE_AUDIO, }, { .name = NULL}}, .outputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO}, diff --git a/libavfilter/af_anull.c b/libavfilter/af_anull.c index 294586e1f3..231fc8c411 100644 --- a/libavfilter/af_anull.c +++ b/libavfilter/af_anull.c @@ -33,8 +33,7 @@ AVFilter avfilter_af_anull = { .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .get_audio_buffer = ff_null_get_audio_buffer, - .filter_samples = ff_null_filter_samples }, + .get_audio_buffer = ff_null_get_audio_buffer, }, { .name = NULL}}, .outputs = (AVFilterPad[]) {{ .name = "default", diff --git a/libavfilter/audio.c b/libavfilter/audio.c index 72e2ced478..14896081a3 100644 --- a/libavfilter/audio.c +++ b/libavfilter/audio.c @@ -146,32 +146,12 @@ fail: return NULL; } -void ff_null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) +static void default_filter_samples(AVFilterLink *link, + AVFilterBufferRef *samplesref) { ff_filter_samples(link->dst->outputs[0], samplesref); } -/* FIXME: samplesref is same as link->cur_buf. Need to consider removing the redundant parameter. */ -void ff_default_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *samplesref) -{ - AVFilterLink *outlink = NULL; - - if (inlink->dst->nb_outputs) - outlink = inlink->dst->outputs[0]; - - if (outlink) { - outlink->out_buf = ff_default_get_audio_buffer(inlink, AV_PERM_WRITE, - samplesref->audio->nb_samples); - outlink->out_buf->pts = samplesref->pts; - outlink->out_buf->audio->sample_rate = samplesref->audio->sample_rate; - ff_filter_samples(outlink, avfilter_ref_buffer(outlink->out_buf, ~0)); - avfilter_unref_buffer(outlink->out_buf); - outlink->out_buf = NULL; - } - avfilter_unref_buffer(samplesref); - inlink->cur_buf = NULL; -} - void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) { void (*filter_samples)(AVFilterLink *, AVFilterBufferRef *); @@ -181,7 +161,7 @@ void ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref) FF_DPRINTF_START(NULL, filter_samples); ff_dlog_link(NULL, link, 1); if (!(filter_samples = dst->filter_samples)) - filter_samples = ff_default_filter_samples; + filter_samples = default_filter_samples; /* prepare to copy the samples if the buffer has insufficient permissions */ if ((dst->min_perms & samplesref->perms) != dst->min_perms || diff --git a/libavfilter/audio.h b/libavfilter/audio.h index 935bec5c43..9af44f8a1c 100644 --- a/libavfilter/audio.h +++ b/libavfilter/audio.h @@ -42,12 +42,6 @@ AVFilterBufferRef *ff_null_get_audio_buffer(AVFilterLink *link, int perms, AVFilterBufferRef *ff_get_audio_buffer(AVFilterLink *link, int perms, int nb_samples); -/** default handler for filter_samples() for audio inputs */ -void ff_default_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref); - -/** filter_samples() handler for filters which simply pass audio along */ -void ff_null_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref); - /** * Send a buffer of audio samples to the next filter. * From 58b049f2fa4f192b00baadb5f1f32ca366f936ea Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 16 May 2012 09:19:46 +0200 Subject: [PATCH 2/9] lavfi: support automatically inserting the fifo filter when needed. This breaks libavfilter ABI. --- doc/APIchanges | 2 +- libavfilter/avfilter.h | 8 ++++ libavfilter/avfiltergraph.c | 40 ++++++++++++++++++ libavfilter/buffersink.c | 81 +++++++++---------------------------- libavfilter/internal.h | 8 ++++ libavfilter/version.h | 4 +- 6 files changed, 79 insertions(+), 64 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index b84c7ae85c..990e36e5c0 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -4,7 +4,7 @@ since the last major version increase. The last version increases were: libavcodec: 2012-01-27 libavdevice: 2011-04-18 -libavfilter: 2011-04-18 +libavfilter: 2012-06-22 libavformat: 2012-01-27 libavresample: 2012-04-24 libswscale: 2011-06-20 diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index aff662f8f6..c92f7e14d4 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -363,6 +363,14 @@ struct AVFilterPad { * and another value on error. */ int (*config_props)(AVFilterLink *link); + + /** + * The filter expects a fifo to be inserted on its input link, + * typically because it has a delay. + * + * input pads only. + */ + int needs_fifo; }; #endif diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 3d463a84d5..95e0a219af 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -591,12 +591,52 @@ static int graph_config_formats(AVFilterGraph *graph, AVClass *log_ctx) return 0; } +static int graph_insert_fifos(AVFilterGraph *graph, AVClass *log_ctx) +{ + AVFilterContext *f; + int i, j, ret; + int fifo_count = 0; + + for (i = 0; i < graph->filter_count; i++) { + f = graph->filters[i]; + + for (j = 0; j < f->nb_inputs; j++) { + AVFilterLink *link = f->inputs[j]; + AVFilterContext *fifo_ctx; + AVFilter *fifo; + char name[32]; + + if (!link->dstpad->needs_fifo) + continue; + + fifo = f->inputs[j]->type == AVMEDIA_TYPE_VIDEO ? + avfilter_get_by_name("fifo") : + avfilter_get_by_name("afifo"); + + snprintf(name, sizeof(name), "auto-inserted fifo %d", fifo_count++); + + ret = avfilter_graph_create_filter(&fifo_ctx, fifo, name, NULL, + NULL, graph); + if (ret < 0) + return ret; + + ret = avfilter_insert_filter(link, fifo_ctx, 0, 0); + if (ret < 0) + return ret; + } + } + + return 0; +} + int avfilter_graph_config(AVFilterGraph *graphctx, void *log_ctx) { int ret; if ((ret = graph_check_validity(graphctx, log_ctx))) return ret; + if ((ret = graph_insert_fifos(graphctx, log_ctx)) < 0) + return ret; if ((ret = graph_config_formats(graphctx, log_ctx))) return ret; if ((ret = graph_config_links(graphctx, log_ctx))) diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index 13fb2558c1..50d949b2ac 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -25,7 +25,7 @@ #include "libavutil/audio_fifo.h" #include "libavutil/audioconvert.h" -#include "libavutil/fifo.h" +#include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "audio.h" @@ -34,86 +34,45 @@ #include "internal.h" typedef struct { - AVFifoBuffer *fifo; ///< FIFO buffer of frame references - + AVFilterBufferRef *cur_buf; ///< last buffer delivered on the sink AVAudioFifo *audio_fifo; ///< FIFO for audio samples int64_t next_pts; ///< interpolating audio pts } BufferSinkContext; -#define FIFO_INIT_SIZE 8 - static av_cold void uninit(AVFilterContext *ctx) { BufferSinkContext *sink = ctx->priv; - while (sink->fifo && av_fifo_size(sink->fifo)) { - AVFilterBufferRef *buf; - av_fifo_generic_read(sink->fifo, &buf, sizeof(buf), NULL); - avfilter_unref_buffer(buf); - } - av_fifo_free(sink->fifo); - if (sink->audio_fifo) av_audio_fifo_free(sink->audio_fifo); } -static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) +static void start_frame(AVFilterLink *link, AVFilterBufferRef *buf) { - BufferSinkContext *sink = ctx->priv; + BufferSinkContext *s = link->dst->priv; - if (!(sink->fifo = av_fifo_alloc(FIFO_INIT_SIZE*sizeof(AVFilterBufferRef*)))) { - av_log(ctx, AV_LOG_ERROR, "Failed to allocate fifo\n"); - return AVERROR(ENOMEM); - } - - return 0; -} - -static void write_buf(AVFilterContext *ctx, AVFilterBufferRef *buf) -{ - BufferSinkContext *sink = ctx->priv; - - if (av_fifo_space(sink->fifo) < sizeof(AVFilterBufferRef *) && - (av_fifo_realloc2(sink->fifo, av_fifo_size(sink->fifo) * 2) < 0)) { - av_log(ctx, AV_LOG_ERROR, "Error reallocating the FIFO.\n"); - return; - } - - av_fifo_generic_write(sink->fifo, &buf, sizeof(buf), NULL); -} - -static void end_frame(AVFilterLink *link) -{ - write_buf(link->dst, link->cur_buf); + av_assert0(!s->cur_buf); + s->cur_buf = buf; link->cur_buf = NULL; -} - -static void filter_samples(AVFilterLink *link, AVFilterBufferRef *buf) -{ - write_buf(link->dst, buf); -} +}; int av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf) { - BufferSinkContext *sink = ctx->priv; + BufferSinkContext *s = ctx->priv; AVFilterLink *link = ctx->inputs[0]; int ret; - if (!buf) { - if (av_fifo_size(sink->fifo)) - return av_fifo_size(sink->fifo)/sizeof(*buf); - else - return ff_poll_frame(ctx->inputs[0]); - } + if (!buf) + return ff_poll_frame(ctx->inputs[0]); - if (!av_fifo_size(sink->fifo) && - (ret = ff_request_frame(link)) < 0) + if ((ret = ff_request_frame(link)) < 0) return ret; - if (!av_fifo_size(sink->fifo)) + if (!s->cur_buf) return AVERROR(EINVAL); - av_fifo_generic_read(sink->fifo, buf, sizeof(*buf), NULL); + *buf = s->cur_buf; + s->cur_buf = NULL; return 0; } @@ -182,13 +141,13 @@ AVFilter avfilter_vsink_buffer = { .name = "buffersink", .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), .priv_size = sizeof(BufferSinkContext), - .init = init, .uninit = uninit, .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .end_frame = end_frame, - .min_perms = AV_PERM_READ, }, + .start_frame = start_frame, + .min_perms = AV_PERM_READ, + .needs_fifo = 1 }, { .name = NULL }}, .outputs = (AVFilterPad[]) {{ .name = NULL }}, }; @@ -197,13 +156,13 @@ AVFilter avfilter_asink_abuffer = { .name = "abuffersink", .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), .priv_size = sizeof(BufferSinkContext), - .init = init, .uninit = uninit, .inputs = (AVFilterPad[]) {{ .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_samples = filter_samples, - .min_perms = AV_PERM_READ, }, + .filter_samples = start_frame, + .min_perms = AV_PERM_READ, + .needs_fifo = 1 }, { .name = NULL }}, .outputs = (AVFilterPad[]) {{ .name = NULL }}, }; diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 01b8f664f8..954610e3ca 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -149,6 +149,14 @@ struct AVFilterPad { * and another value on error. */ int (*config_props)(AVFilterLink *link); + + /** + * The filter expects a fifo to be inserted on its input link, + * typically because it has a delay. + * + * input pads only. + */ + int needs_fifo; }; #endif diff --git a/libavfilter/version.h b/libavfilter/version.h index 562147b657..b8f2749e12 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -28,8 +28,8 @@ #include "libavutil/avutil.h" -#define LIBAVFILTER_VERSION_MAJOR 2 -#define LIBAVFILTER_VERSION_MINOR 23 +#define LIBAVFILTER_VERSION_MAJOR 3 +#define LIBAVFILTER_VERSION_MINOR 0 #define LIBAVFILTER_VERSION_MICRO 0 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ From f75be9856a99739b2c22ed73a3c51df0f54a5ce9 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sun, 27 May 2012 14:18:49 +0200 Subject: [PATCH 3/9] lavfi: allow audio filters to request a given number of samples. This makes synchronization simpler for filters with multiple inputs. --- libavfilter/avfilter.h | 9 +++ libavfilter/fifo.c | 159 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 160 insertions(+), 8 deletions(-) diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index c92f7e14d4..f09f0869f4 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -595,6 +595,15 @@ struct AVFilterLink { AVFilterFormats *out_samplerates; struct AVFilterChannelLayouts *in_channel_layouts; struct AVFilterChannelLayouts *out_channel_layouts; + + /** + * Audio only, the destination filter sets this to a non-zero value to + * request that buffers with the given number of samples should be sent to + * it. AVFilterPad.needs_fifo must also be set on the corresponding input + * pad. + * Last buffer before EOF will be padded with silence. + */ + int request_samples; }; /** diff --git a/libavfilter/fifo.c b/libavfilter/fifo.c index 3fa4faab39..6d28757f4d 100644 --- a/libavfilter/fifo.c +++ b/libavfilter/fifo.c @@ -23,6 +23,11 @@ * FIFO buffering filter */ +#include "libavutil/avassert.h" +#include "libavutil/audioconvert.h" +#include "libavutil/mathematics.h" +#include "libavutil/samplefmt.h" + #include "audio.h" #include "avfilter.h" #include "internal.h" @@ -36,6 +41,13 @@ typedef struct Buf { typedef struct { Buf root; Buf *last; ///< last buffered frame + + /** + * When a specific number of output samples is requested, the partial + * buffer is stored here + */ + AVFilterBufferRef *buf_out; + int allocated_samples; ///< number of samples buf_out was allocated for } FifoContext; static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) @@ -57,6 +69,8 @@ static av_cold void uninit(AVFilterContext *ctx) avfilter_unref_buffer(buf->buf); av_free(buf); } + + avfilter_unref_buffer(fifo->buf_out); } static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) @@ -68,14 +82,143 @@ static void add_to_queue(AVFilterLink *inlink, AVFilterBufferRef *buf) fifo->last->buf = buf; } +static void queue_pop(FifoContext *s) +{ + Buf *tmp = s->root.next->next; + if (s->last == s->root.next) + s->last = &s->root; + av_freep(&s->root.next); + s->root.next = tmp; +} + static void end_frame(AVFilterLink *inlink) { } static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { } +/** + * Move data pointers and pts offset samples forward. + */ +static void buffer_offset(AVFilterLink *link, AVFilterBufferRef *buf, + int offset) +{ + int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); + int planar = av_sample_fmt_is_planar(link->format); + int planes = planar ? nb_channels : 1; + int block_align = av_get_bytes_per_sample(link->format) * (planar ? 1 : nb_channels); + int i; + + av_assert0(buf->audio->nb_samples > offset); + + for (i = 0; i < planes; i++) + buf->extended_data[i] += block_align*offset; + if (buf->data != buf->extended_data) + memcpy(buf->data, buf->extended_data, + FFMIN(planes, FF_ARRAY_ELEMS(buf->data)) * sizeof(*buf->data)); + buf->linesize[0] -= block_align*offset; + buf->audio->nb_samples -= offset; + + if (buf->pts != AV_NOPTS_VALUE) { + buf->pts += av_rescale_q(offset, (AVRational){1, link->sample_rate}, + link->time_base); + } +} + +static int calc_ptr_alignment(AVFilterBufferRef *buf) +{ + int planes = av_sample_fmt_is_planar(buf->format) ? + av_get_channel_layout_nb_channels(buf->audio->channel_layout) : 1; + int min_align = 128; + int p; + + for (p = 0; p < planes; p++) { + int cur_align = 128; + while ((intptr_t)buf->extended_data[p] % cur_align) + cur_align >>= 1; + if (cur_align < min_align) + min_align = cur_align; + } + return min_align; +} + +static int return_audio_frame(AVFilterContext *ctx) +{ + AVFilterLink *link = ctx->outputs[0]; + FifoContext *s = ctx->priv; + AVFilterBufferRef *head = s->root.next->buf; + AVFilterBufferRef *buf_out; + int ret; + + if (!s->buf_out && + head->audio->nb_samples >= link->request_samples && + calc_ptr_alignment(head) >= 32) { + if (head->audio->nb_samples == link->request_samples) { + buf_out = head; + queue_pop(s); + } else { + buf_out = avfilter_ref_buffer(head, AV_PERM_READ); + buf_out->audio->nb_samples = link->request_samples; + buffer_offset(link, head, link->request_samples); + } + } else { + int nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); + + if (!s->buf_out) { + s->buf_out = ff_get_audio_buffer(link, AV_PERM_WRITE, + link->request_samples); + if (!s->buf_out) + return AVERROR(ENOMEM); + + s->buf_out->audio->nb_samples = 0; + s->buf_out->pts = head->pts; + s->allocated_samples = link->request_samples; + } else if (link->request_samples != s->allocated_samples) { + av_log(ctx, AV_LOG_ERROR, "request_samples changed before the " + "buffer was returned.\n"); + return AVERROR(EINVAL); + } + + while (s->buf_out->audio->nb_samples < s->allocated_samples) { + int len = FFMIN(s->allocated_samples - s->buf_out->audio->nb_samples, + head->audio->nb_samples); + + av_samples_copy(s->buf_out->extended_data, head->extended_data, + s->buf_out->audio->nb_samples, 0, len, nb_channels, + link->format); + s->buf_out->audio->nb_samples += len; + + if (len == head->audio->nb_samples) { + avfilter_unref_buffer(head); + queue_pop(s); + + if (!s->root.next && + (ret = ff_request_frame(ctx->inputs[0])) < 0) { + if (ret == AVERROR_EOF) { + av_samples_set_silence(s->buf_out->extended_data, + s->buf_out->audio->nb_samples, + s->allocated_samples - + s->buf_out->audio->nb_samples, + nb_channels, link->format); + s->buf_out->audio->nb_samples = s->allocated_samples; + break; + } + return ret; + } + head = s->root.next->buf; + } else { + buffer_offset(link, head, len); + } + } + buf_out = s->buf_out; + s->buf_out = NULL; + } + ff_filter_samples(link, buf_out); + + return 0; +} + static int request_frame(AVFilterLink *outlink) { FifoContext *fifo = outlink->src->priv; - Buf *tmp; int ret; if (!fifo->root.next) { @@ -90,20 +233,20 @@ static int request_frame(AVFilterLink *outlink) ff_start_frame(outlink, fifo->root.next->buf); ff_draw_slice (outlink, 0, outlink->h, 1); ff_end_frame (outlink); + queue_pop(fifo); break; case AVMEDIA_TYPE_AUDIO: - ff_filter_samples(outlink, fifo->root.next->buf); + if (outlink->request_samples) { + return return_audio_frame(outlink->src); + } else { + ff_filter_samples(outlink, fifo->root.next->buf); + queue_pop(fifo); + } break; default: return AVERROR(EINVAL); } - if (fifo->last == fifo->root.next) - fifo->last = &fifo->root; - tmp = fifo->root.next->next; - av_free(fifo->root.next); - fifo->root.next = tmp; - return 0; } From dc07fb6f7b8fe0f7febdd89e21d23ac50ae920ec Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 28 May 2012 07:28:58 +0200 Subject: [PATCH 4/9] lavfi: add join audio filter. It joins multiple input streams into one multi-channel output. --- Changelog | 1 + doc/filters.texi | 37 +++ libavfilter/Makefile | 1 + libavfilter/af_join.c | 500 +++++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 5 files changed, 540 insertions(+) create mode 100644 libavfilter/af_join.c diff --git a/Changelog b/Changelog index 8ee9b8f30a..03287673e8 100644 --- a/Changelog +++ b/Changelog @@ -28,6 +28,7 @@ version : - RTMPT protocol support - iLBC encoding/decoding via libilbc - Microsoft Screen 1 decoder +- join audio filter version 0.8: diff --git a/doc/filters.texi b/doc/filters.texi index f17dad8051..e40f8798cc 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -232,6 +232,43 @@ front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]' side_right.wav @end example +@section join +Join multiple input streams into one multi-channel stream. + +The filter accepts the following named parameters: +@table @option + +@item inputs +Number of input streams. Defaults to 2. + +@item channel_layout +Desired output channel layout. Defaults to stereo. + +@item map +Map channels from inputs to output. The argument is a comma-separated list of +mappings, each in the @code{@var{input_idx}.@var{in_channel}-@var{out_channel}} +form. @var{input_idx} is the 0-based index of the input stream. @var{in_channel} +can be either the name of the input channel (e.g. FR for front left) or its +index in the specified input stream. @var{out_channel} is the name of the output +channel. +@end table + +The filter will attempt to guess the mappings when those are not specified +explicitly. It does so by first trying to find an unused matching input channel +and if that fails it picks the first unused input channel. + +E.g. to join 3 inputs (with properly set channel layouts) +@example +avconv -i INPUT1 -i INPUT2 -i INPUT3 -filter_complex join=inputs=3 OUTPUT +@end example + +To build a 5.1 output from 6 single-channel streams: +@example +avconv -i fl -i fr -i fc -i sl -i sr -i lfe -filter_complex +'join=inputs=6:channel_layout=5.1:map=0.0-FL\,1.0-FR\,2.0-FC\,3.0-SL\,4.0-SR\,5.0-LFE' +out +@end example + @section resample Convert the audio sample format, sample rate and channel layout. This filter is not meant to be used directly, it is inserted automatically by libavfilter diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 377956e6af..5fa85cebe4 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -32,6 +32,7 @@ OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o OBJS-$(CONFIG_ASPLIT_FILTER) += split.o OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o +OBJS-$(CONFIG_JOIN_FILTER) += af_join.o OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o OBJS-$(CONFIG_ANULLSRC_FILTER) += asrc_anullsrc.o diff --git a/libavfilter/af_join.c b/libavfilter/af_join.c new file mode 100644 index 0000000000..ab5e9055b3 --- /dev/null +++ b/libavfilter/af_join.c @@ -0,0 +1,500 @@ +/* + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Audio join filter + * + * Join multiple audio inputs as different channels in + * a single output + */ + +#include "libavutil/audioconvert.h" +#include "libavutil/avassert.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +typedef struct ChannelMap { + int input; ///< input stream index + int in_channel_idx; ///< index of in_channel in the input stream data + uint64_t in_channel; ///< layout describing the input channel + uint64_t out_channel; ///< layout describing the output channel +} ChannelMap; + +typedef struct JoinContext { + const AVClass *class; + + int inputs; + char *map; + char *channel_layout_str; + uint64_t channel_layout; + + int nb_channels; + ChannelMap *channels; + + /** + * Temporary storage for input frames, until we get one on each input. + */ + AVFilterBufferRef **input_frames; + + /** + * Temporary storage for data pointers, for assembling the output buffer. + */ + uint8_t **data; +} JoinContext; + +/** + * To avoid copying the data from input buffers, this filter creates + * a custom output buffer that stores references to all inputs and + * unrefs them on free. + */ +typedef struct JoinBufferPriv { + AVFilterBufferRef **in_buffers; + int nb_in_buffers; +} JoinBufferPriv; + +#define OFFSET(x) offsetof(JoinContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption join_options[] = { + { "inputs", "Number of input streams.", OFFSET(inputs), AV_OPT_TYPE_INT, { 2 }, 1, INT_MAX, A }, + { "channel_layout", "Channel layout of the " + "output stream.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, A }, + { "map", "A comma-separated list of channels maps in the format " + "'input_stream.input_channel-output_channel.", + OFFSET(map), AV_OPT_TYPE_STRING, .flags = A }, + { NULL }, +}; + +static const AVClass join_class = { + .class_name = "join filter", + .item_name = av_default_item_name, + .option = join_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static void filter_samples(AVFilterLink *link, AVFilterBufferRef *buf) +{ + AVFilterContext *ctx = link->dst; + JoinContext *s = ctx->priv; + int i; + + for (i = 0; i < ctx->nb_inputs; i++) + if (link == ctx->inputs[i]) + break; + av_assert0(i < ctx->nb_inputs); + av_assert0(!s->input_frames[i]); + s->input_frames[i] = buf; +} + +static int parse_maps(AVFilterContext *ctx) +{ + JoinContext *s = ctx->priv; + char *cur = s->map; + + while (cur && *cur) { + char *sep, *next, *p; + uint64_t in_channel = 0, out_channel = 0; + int input_idx, out_ch_idx, in_ch_idx; + + next = strchr(cur, ','); + if (next) + *next++ = 0; + + /* split the map into input and output parts */ + if (!(sep = strchr(cur, '-'))) { + av_log(ctx, AV_LOG_ERROR, "Missing separator '-' in channel " + "map '%s'\n", cur); + return AVERROR(EINVAL); + } + *sep++ = 0; + +#define PARSE_CHANNEL(str, var, inout) \ + if (!(var = av_get_channel_layout(str))) { \ + av_log(ctx, AV_LOG_ERROR, "Invalid " inout " channel: %s.\n", str);\ + return AVERROR(EINVAL); \ + } \ + if (av_get_channel_layout_nb_channels(var) != 1) { \ + av_log(ctx, AV_LOG_ERROR, "Channel map describes more than one " \ + inout " channel.\n"); \ + return AVERROR(EINVAL); \ + } + + /* parse output channel */ + PARSE_CHANNEL(sep, out_channel, "output"); + if (!(out_channel & s->channel_layout)) { + av_log(ctx, AV_LOG_ERROR, "Output channel '%s' is not present in " + "requested channel layout.\n", sep); + return AVERROR(EINVAL); + } + + out_ch_idx = av_get_channel_layout_channel_index(s->channel_layout, + out_channel); + if (s->channels[out_ch_idx].input >= 0) { + av_log(ctx, AV_LOG_ERROR, "Multiple maps for output channel " + "'%s'.\n", sep); + return AVERROR(EINVAL); + } + + /* parse input channel */ + input_idx = strtol(cur, &cur, 0); + if (input_idx < 0 || input_idx >= s->inputs) { + av_log(ctx, AV_LOG_ERROR, "Invalid input stream index: %d.\n", + input_idx); + return AVERROR(EINVAL); + } + + if (*cur) + cur++; + + in_ch_idx = strtol(cur, &p, 0); + if (p == cur) { + /* channel specifier is not a number, + * try to parse as channel name */ + PARSE_CHANNEL(cur, in_channel, "input"); + } + + s->channels[out_ch_idx].input = input_idx; + if (in_channel) + s->channels[out_ch_idx].in_channel = in_channel; + else + s->channels[out_ch_idx].in_channel_idx = in_ch_idx; + + cur = next; + } + return 0; +} + +static int join_init(AVFilterContext *ctx, const char *args, void *opaque) +{ + JoinContext *s = ctx->priv; + int ret, i; + + s->class = &join_class; + av_opt_set_defaults(s); + if ((ret = av_set_options_string(s, args, "=", ":")) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args); + return ret; + } + + if (!(s->channel_layout = av_get_channel_layout(s->channel_layout_str))) { + av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n", + s->channel_layout_str); + ret = AVERROR(EINVAL); + goto fail; + } + + s->nb_channels = av_get_channel_layout_nb_channels(s->channel_layout); + s->channels = av_mallocz(sizeof(*s->channels) * s->nb_channels); + s->data = av_mallocz(sizeof(*s->data) * s->nb_channels); + s->input_frames = av_mallocz(sizeof(*s->input_frames) * s->inputs); + if (!s->channels || !s->data || !s->input_frames) { + ret = AVERROR(ENOMEM); + goto fail; + } + + for (i = 0; i < s->nb_channels; i++) { + s->channels[i].out_channel = av_channel_layout_extract_channel(s->channel_layout, i); + s->channels[i].input = -1; + } + + if ((ret = parse_maps(ctx)) < 0) + goto fail; + + for (i = 0; i < s->inputs; i++) { + char name[32]; + AVFilterPad pad = { 0 }; + + snprintf(name, sizeof(name), "input%d", i); + pad.type = AVMEDIA_TYPE_AUDIO; + pad.name = av_strdup(name); + pad.filter_samples = filter_samples; + + pad.needs_fifo = 1; + + ff_insert_inpad(ctx, i, &pad); + } + +fail: + av_opt_free(s); + return ret; +} + +static void join_uninit(AVFilterContext *ctx) +{ + JoinContext *s = ctx->priv; + int i; + + for (i = 0; i < ctx->nb_inputs; i++) { + av_freep(&ctx->input_pads[i].name); + avfilter_unref_buffer(s->input_frames[i]); + } + + av_freep(&s->channels); + av_freep(&s->data); + av_freep(&s->input_frames); +} + +static int join_query_formats(AVFilterContext *ctx) +{ + JoinContext *s = ctx->priv; + AVFilterChannelLayouts *layouts = NULL; + int i; + + ff_add_channel_layout(&layouts, s->channel_layout); + ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts); + + for (i = 0; i < ctx->nb_inputs; i++) + ff_channel_layouts_ref(ff_all_channel_layouts(), + &ctx->inputs[i]->out_channel_layouts); + + ff_set_common_formats (ctx, ff_planar_sample_fmts()); + ff_set_common_samplerates(ctx, ff_all_samplerates()); + + return 0; +} + +static void guess_map_matching(AVFilterContext *ctx, ChannelMap *ch, + uint64_t *inputs) +{ + int i; + + for (i = 0; i < ctx->nb_inputs; i++) { + AVFilterLink *link = ctx->inputs[i]; + + if (ch->out_channel & link->channel_layout && + !(ch->out_channel & inputs[i])) { + ch->input = i; + ch->in_channel = ch->out_channel; + inputs[i] |= ch->out_channel; + return; + } + } +} + +static void guess_map_any(AVFilterContext *ctx, ChannelMap *ch, + uint64_t *inputs) +{ + int i; + + for (i = 0; i < ctx->nb_inputs; i++) { + AVFilterLink *link = ctx->inputs[i]; + + if ((inputs[i] & link->channel_layout) != link->channel_layout) { + uint64_t unused = link->channel_layout & ~inputs[i]; + + ch->input = i; + ch->in_channel = av_channel_layout_extract_channel(unused, 0); + inputs[i] |= ch->in_channel; + return; + } + } +} + +static int join_config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + JoinContext *s = ctx->priv; + uint64_t *inputs; // nth element tracks which channels are used from nth input + int i, ret = 0; + + /* initialize inputs to user-specified mappings */ + if (!(inputs = av_mallocz(sizeof(*inputs) * ctx->nb_inputs))) + return AVERROR(ENOMEM); + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + AVFilterLink *inlink; + + if (ch->input < 0) + continue; + + inlink = ctx->inputs[ch->input]; + + if (!ch->in_channel) + ch->in_channel = av_channel_layout_extract_channel(inlink->channel_layout, + ch->in_channel_idx); + + if (!(ch->in_channel & inlink->channel_layout)) { + av_log(ctx, AV_LOG_ERROR, "Requested channel %s is not present in " + "input stream #%d.\n", av_get_channel_name(ch->in_channel), + ch->input); + ret = AVERROR(EINVAL); + goto fail; + } + + inputs[ch->input] |= ch->in_channel; + } + + /* guess channel maps when not explicitly defined */ + /* first try unused matching channels */ + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + + if (ch->input < 0) + guess_map_matching(ctx, ch, inputs); + } + + /* if the above failed, try to find _any_ unused input channel */ + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + + if (ch->input < 0) + guess_map_any(ctx, ch, inputs); + + if (ch->input < 0) { + av_log(ctx, AV_LOG_ERROR, "Could not find input channel for " + "output channel '%s'.\n", + av_get_channel_name(ch->out_channel)); + goto fail; + } + + ch->in_channel_idx = av_get_channel_layout_channel_index(ctx->inputs[ch->input]->channel_layout, + ch->in_channel); + } + + /* print mappings */ + av_log(ctx, AV_LOG_VERBOSE, "mappings: "); + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + av_log(ctx, AV_LOG_VERBOSE, "%d.%s => %s ", ch->input, + av_get_channel_name(ch->in_channel), + av_get_channel_name(ch->out_channel)); + } + av_log(ctx, AV_LOG_VERBOSE, "\n"); + + for (i = 0; i < ctx->nb_inputs; i++) { + if (!inputs[i]) + av_log(ctx, AV_LOG_WARNING, "No channels are used from input " + "stream %d.\n", i); + } + +fail: + av_freep(&inputs); + return ret; +} + +static void join_free_buffer(AVFilterBuffer *buf) +{ + JoinBufferPriv *priv = buf->priv; + + if (priv) { + int i; + + for (i = 0; i < priv->nb_in_buffers; i++) + avfilter_unref_buffer(priv->in_buffers[i]); + + av_freep(&priv->in_buffers); + av_freep(&buf->priv); + } + + if (buf->extended_data != buf->data) + av_freep(&buf->extended_data); + av_freep(&buf); +} + +static int join_request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + JoinContext *s = ctx->priv; + AVFilterBufferRef *buf; + JoinBufferPriv *priv; + int linesize = INT_MAX; + int perms = ~0; + int nb_samples; + int i, j, ret; + + /* get a frame on each input */ + for (i = 0; i < ctx->nb_inputs; i++) { + AVFilterLink *inlink = ctx->inputs[i]; + + if (!s->input_frames[i] && + (ret = ff_request_frame(inlink)) < 0) + return ret; + + /* request the same number of samples on all inputs */ + if (i == 0) { + nb_samples = s->input_frames[0]->audio->nb_samples; + + for (j = 1; !i && j < ctx->nb_inputs; j++) + ctx->inputs[j]->request_samples = nb_samples; + } + } + + for (i = 0; i < s->nb_channels; i++) { + ChannelMap *ch = &s->channels[i]; + AVFilterBufferRef *cur_buf = s->input_frames[ch->input]; + + s->data[i] = cur_buf->extended_data[ch->in_channel_idx]; + linesize = FFMIN(linesize, cur_buf->linesize[0]); + perms &= cur_buf->perms; + } + + buf = avfilter_get_audio_buffer_ref_from_arrays(s->data, linesize, perms, + nb_samples, outlink->format, + outlink->channel_layout); + if (!buf) + return AVERROR(ENOMEM); + + buf->buf->free = join_free_buffer; + buf->pts = s->input_frames[0]->pts; + + if (!(priv = av_mallocz(sizeof(*priv)))) + goto fail; + if (!(priv->in_buffers = av_mallocz(sizeof(*priv->in_buffers) * ctx->nb_inputs))) + goto fail; + + for (i = 0; i < ctx->nb_inputs; i++) + priv->in_buffers[i] = s->input_frames[i]; + priv->nb_in_buffers = ctx->nb_inputs; + buf->buf->priv = priv; + + ff_filter_samples(outlink, buf); + + memset(s->input_frames, 0, sizeof(*s->input_frames) * ctx->nb_inputs); + + return 0; + +fail: + avfilter_unref_buffer(buf); + if (priv) + av_freep(&priv->in_buffers); + av_freep(&priv); + return AVERROR(ENOMEM); +} + +AVFilter avfilter_af_join = { + .name = "join", + .description = NULL_IF_CONFIG_SMALL("Join multiple audio streams into " + "multi-channel output"), + .priv_size = sizeof(JoinContext), + + .init = join_init, + .uninit = join_uninit, + .query_formats = join_query_formats, + + .inputs = (const AVFilterPad[]){{ NULL }}, + .outputs = (const AVFilterPad[]){{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = join_config_output, + .request_frame = join_request_frame, }, + { NULL }}, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 5a249c08c8..786c2dea7b 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -41,6 +41,7 @@ void avfilter_register_all(void) REGISTER_FILTER (ASPLIT, asplit, af); REGISTER_FILTER (ASYNCTS, asyncts, af); REGISTER_FILTER (CHANNELSPLIT,channelsplit,af); + REGISTER_FILTER (JOIN, join, af); REGISTER_FILTER (RESAMPLE, resample, af); REGISTER_FILTER (ANULLSRC, anullsrc, asrc); From 41e637e44902246efa17babf26e9be8a4a519e6f Mon Sep 17 00:00:00 2001 From: Alex Converse Date: Fri, 8 Jun 2012 18:42:53 -0700 Subject: [PATCH 5/9] lavfi: Add the af_channelmap audio channel mapping filter. Inspired by MPlayer's af_channels filter and SoX's remix effect. --- Changelog | 1 + doc/filters.texi | 33 +++ libavfilter/Makefile | 1 + libavfilter/af_channelmap.c | 402 ++++++++++++++++++++++++++++++++++++ libavfilter/allfilters.c | 1 + 5 files changed, 438 insertions(+) create mode 100644 libavfilter/af_channelmap.c diff --git a/Changelog b/Changelog index 03287673e8..95f79bcd6e 100644 --- a/Changelog +++ b/Changelog @@ -29,6 +29,7 @@ version : - iLBC encoding/decoding via libilbc - Microsoft Screen 1 decoder - join audio filter +- audio channel mapping filter version 0.8: diff --git a/doc/filters.texi b/doc/filters.texi index e40f8798cc..218cd600ee 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -232,6 +232,39 @@ front_center.wav -map '[LFE]' lfe.wav -map '[SL]' side_left.wav -map '[SR]' side_right.wav @end example +@section channelmap +Remap input channels to new locations. + +This filter accepts the following named parameters: +@table @option +@item channel_layout +Channel layout of the output stream. + +@item map +Map channels from input to output. The argument is a comma-separated list of +mappings, each in the @code{@var{in_channel}-@var{out_channel}} or +@var{in_channel} form. @var{in_channel} can be either the name of the input +channel (e.g. FL for front left) or its index in the input channel layout. +@var{out_channel} is the name of the output channel or its index in the output +channel layout. If @var{out_channel} is not given then it is implicitly an +index, starting with zero and increasing by one for each mapping. +@end table + +If no mapping is present, the filter will implicitly map input channels to +output channels preserving index. + +For example, assuming a 5.1+downmix input MOV file +@example +avconv -i in.mov -filter 'channelmap=map=DL-FL\,DR-FR' out.wav +@end example +will create an output WAV file tagged as stereo from the downmix channels of +the input. + +To fix a 5.1 WAV improperly encoded in AAC's native channel order +@example +avconv -i in.wav -filter 'channelmap=1\,2\,0\,5\,3\,4:channel_layout=5.1' out.wav +@end example + @section join Join multiple input streams into one multi-channel stream. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 5fa85cebe4..c77d450d7e 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -31,6 +31,7 @@ OBJS-$(CONFIG_AMIX_FILTER) += af_amix.o OBJS-$(CONFIG_ANULL_FILTER) += af_anull.o OBJS-$(CONFIG_ASPLIT_FILTER) += split.o OBJS-$(CONFIG_ASYNCTS_FILTER) += af_asyncts.o +OBJS-$(CONFIG_CHANNELMAP_FILTER) += af_channelmap.o OBJS-$(CONFIG_CHANNELSPLIT_FILTER) += af_channelsplit.o OBJS-$(CONFIG_JOIN_FILTER) += af_join.o OBJS-$(CONFIG_RESAMPLE_FILTER) += af_resample.o diff --git a/libavfilter/af_channelmap.c b/libavfilter/af_channelmap.c new file mode 100644 index 0000000000..14c2c0cb5a --- /dev/null +++ b/libavfilter/af_channelmap.c @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2012 Google, Inc. + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * audio channel mapping filter + */ + +#include + +#include "libavutil/audioconvert.h" +#include "libavutil/avstring.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "internal.h" + +struct ChannelMap { + uint64_t in_channel; + uint64_t out_channel; + int in_channel_idx; + int out_channel_idx; +}; + +enum MappingMode { + MAP_NONE, + MAP_ONE_INT, + MAP_ONE_STR, + MAP_PAIR_INT_INT, + MAP_PAIR_INT_STR, + MAP_PAIR_STR_INT, + MAP_PAIR_STR_STR +}; + +#define MAX_CH 64 +typedef struct ChannelMapContext { + const AVClass *class; + AVFilterChannelLayouts *channel_layouts; + char *mapping_str; + char *channel_layout_str; + uint64_t output_layout; + struct ChannelMap map[MAX_CH]; + int nch; + enum MappingMode mode; +} ChannelMapContext; + +#define OFFSET(x) offsetof(ChannelMapContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM +static const AVOption options[] = { + { "map", "A comma-separated list of input channel numbers in output order.", + OFFSET(mapping_str), AV_OPT_TYPE_STRING, .flags = A }, + { "channel_layout", "Output channel layout.", + OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A }, + { NULL }, +}; + +static const AVClass channelmap_class = { + .class_name = "channel map filter", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static char* split(char *message, char delim) { + char *next = strchr(message, delim); + if (next) + *next++ = '\0'; + return next; +} + +static int get_channel_idx(char **map, int *ch, char delim, int max_ch) +{ + char *next = split(*map, delim); + int len; + int n = 0; + if (!next && delim == '-') + return AVERROR(EINVAL); + len = strlen(*map); + sscanf(*map, "%d%n", ch, &n); + if (n != len) + return AVERROR(EINVAL); + if (*ch < 0 || *ch > max_ch) + return AVERROR(EINVAL); + *map = next; + return 0; +} + +static int get_channel(char **map, uint64_t *ch, char delim) +{ + char *next = split(*map, delim); + if (!next && delim == '-') + return AVERROR(EINVAL); + *ch = av_get_channel_layout(*map); + if (av_get_channel_layout_nb_channels(*ch) != 1) + return AVERROR(EINVAL); + *map = next; + return 0; +} + +static av_cold int channelmap_init(AVFilterContext *ctx, const char *args, + void *opaque) +{ + ChannelMapContext *s = ctx->priv; + int ret; + char *mapping; + enum mode; + int map_entries = 0; + char buf[256]; + enum MappingMode mode; + uint64_t out_ch_mask = 0; + int i; + + if (!args) { + av_log(ctx, AV_LOG_ERROR, "No parameters supplied.\n"); + return AVERROR(EINVAL); + } + + s->class = &channelmap_class; + av_opt_set_defaults(s); + + if ((ret = av_set_options_string(s, args, "=", ":")) < 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing options string '%s'.\n", args); + return ret; + } + + mapping = s->mapping_str; + + if (!mapping) { + mode = MAP_NONE; + } else { + char *dash = strchr(mapping, '-'); + if (!dash) { // short mapping + if (isdigit(*mapping)) + mode = MAP_ONE_INT; + else + mode = MAP_ONE_STR; + } else if (isdigit(*mapping)) { + if (isdigit(*(dash+1))) + mode = MAP_PAIR_INT_INT; + else + mode = MAP_PAIR_INT_STR; + } else { + if (isdigit(*(dash+1))) + mode = MAP_PAIR_STR_INT; + else + mode = MAP_PAIR_STR_STR; + } + } + + if (mode != MAP_NONE) { + char *comma = mapping; + map_entries = 1; + while ((comma = strchr(comma, ','))) { + if (*++comma) // Allow trailing comma + map_entries++; + } + } + + if (map_entries > MAX_CH) { + av_log(ctx, AV_LOG_ERROR, "Too many channels mapped: '%d'.\n", map_entries); + ret = AVERROR(EINVAL); + goto fail; + } + + for (i = 0; i < map_entries; i++) { + int in_ch_idx = -1, out_ch_idx = -1; + uint64_t in_ch = 0, out_ch = 0; + static const char err[] = "Failed to parse channel map\n"; + switch (mode) { + case MAP_ONE_INT: + if (get_channel_idx(&mapping, &in_ch_idx, ',', MAX_CH) < 0) { + ret = AVERROR(EINVAL); + av_log(ctx, AV_LOG_ERROR, err); + goto fail; + } + s->map[i].in_channel_idx = in_ch_idx; + s->map[i].out_channel_idx = i; + break; + case MAP_ONE_STR: + if (!get_channel(&mapping, &in_ch, ',')) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel = in_ch; + s->map[i].out_channel_idx = i; + break; + case MAP_PAIR_INT_INT: + if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 || + get_channel_idx(&mapping, &out_ch_idx, ',', MAX_CH) < 0) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel_idx = in_ch_idx; + s->map[i].out_channel_idx = out_ch_idx; + break; + case MAP_PAIR_INT_STR: + if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 || + get_channel(&mapping, &out_ch, ',') < 0 || + out_ch & out_ch_mask) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel_idx = in_ch_idx; + s->map[i].out_channel = out_ch; + out_ch_mask |= out_ch; + break; + case MAP_PAIR_STR_INT: + if (get_channel(&mapping, &in_ch, '-') < 0 || + get_channel_idx(&mapping, &out_ch_idx, ',', MAX_CH) < 0) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel = in_ch; + s->map[i].out_channel_idx = out_ch_idx; + break; + case MAP_PAIR_STR_STR: + if (get_channel(&mapping, &in_ch, '-') < 0 || + get_channel(&mapping, &out_ch, ',') < 0 || + out_ch & out_ch_mask) { + av_log(ctx, AV_LOG_ERROR, err); + ret = AVERROR(EINVAL); + goto fail; + } + s->map[i].in_channel = in_ch; + s->map[i].out_channel = out_ch; + out_ch_mask |= out_ch; + break; + } + } + s->mode = mode; + s->nch = map_entries; + s->output_layout = out_ch_mask ? out_ch_mask : + av_get_default_channel_layout(map_entries); + + if (s->channel_layout_str) { + uint64_t fmt; + if ((fmt = av_get_channel_layout(s->channel_layout_str)) == 0) { + av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: '%s'.\n", + s->channel_layout_str); + ret = AVERROR(EINVAL); + goto fail; + } + if (mode == MAP_NONE) { + int i; + s->nch = av_get_channel_layout_nb_channels(fmt); + for (i = 0; i < s->nch; i++) { + s->map[i].in_channel_idx = i; + s->map[i].out_channel_idx = i; + } + } else if (out_ch_mask && out_ch_mask != fmt) { + av_get_channel_layout_string(buf, sizeof(buf), 0, out_ch_mask); + av_log(ctx, AV_LOG_ERROR, + "Output channel layout '%s' does not match the list of channel mapped: '%s'.\n", + s->channel_layout_str, buf); + ret = AVERROR(EINVAL); + goto fail; + } else if (s->nch != av_get_channel_layout_nb_channels(fmt)) { + av_log(ctx, AV_LOG_ERROR, + "Output channel layout %s does not match the number of channels mapped %d.\n", + s->channel_layout_str, s->nch); + ret = AVERROR(EINVAL); + goto fail; + } + s->output_layout = fmt; + } + ff_add_channel_layout(&s->channel_layouts, s->output_layout); + + if (mode == MAP_PAIR_INT_STR || mode == MAP_PAIR_STR_STR) { + for (i = 0; i < s->nch; i++) { + s->map[i].out_channel_idx = av_get_channel_layout_channel_index( + s->output_layout, s->map[i].out_channel); + } + } + +fail: + av_opt_free(s); + return ret; +} + +static int channelmap_query_formats(AVFilterContext *ctx) +{ + ChannelMapContext *s = ctx->priv; + + ff_set_common_formats(ctx, ff_planar_sample_fmts()); + ff_set_common_samplerates(ctx, ff_all_samplerates()); + ff_channel_layouts_ref(ff_all_channel_layouts(), &ctx->inputs[0]->out_channel_layouts); + ff_channel_layouts_ref(s->channel_layouts, &ctx->outputs[0]->in_channel_layouts); + + return 0; +} + +static void channelmap_filter_samples(AVFilterLink *inlink, AVFilterBufferRef *buf) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + const ChannelMapContext *s = ctx->priv; + const int nch_in = av_get_channel_layout_nb_channels(inlink->channel_layout); + const int nch_out = s->nch; + int ch; + uint8_t *source_planes[MAX_CH]; + + memcpy(source_planes, buf->extended_data, + nch_in * sizeof(source_planes[0])); + + if (nch_out > nch_in) { + if (nch_out > FF_ARRAY_ELEMS(buf->data)) { + uint8_t **new_extended_data = + av_mallocz(nch_out * sizeof(*buf->extended_data)); + if (!new_extended_data) + return; + if (buf->extended_data == buf->data) { + buf->extended_data = new_extended_data; + } else { + buf->extended_data = new_extended_data; + av_free(buf->extended_data); + } + } else if (buf->extended_data != buf->data) { + av_free(buf->extended_data); + buf->extended_data = buf->data; + } + } + + for (ch = 0; ch < nch_out; ch++) { + buf->extended_data[s->map[ch].out_channel_idx] = + source_planes[s->map[ch].in_channel_idx]; + } + + if (buf->data != buf->extended_data) + memcpy(buf->data, buf->extended_data, + FFMIN(FF_ARRAY_ELEMS(buf->data), nch_out) * sizeof(buf->data[0])); + + ff_filter_samples(outlink, buf); +} + +static int channelmap_config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + ChannelMapContext *s = ctx->priv; + int i, err = 0; + const char *channel_name; + char layout_name[256]; + + if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR) { + for (i = 0; i < s->nch; i++) { + s->map[i].in_channel_idx = av_get_channel_layout_channel_index( + inlink->channel_layout, s->map[i].in_channel); + if (s->map[i].in_channel_idx < 0) { + channel_name = av_get_channel_name(s->map[i].in_channel); + av_get_channel_layout_string(layout_name, sizeof(layout_name), + 0, inlink->channel_layout); + av_log(ctx, AV_LOG_ERROR, + "input channel '%s' not available from input layout '%s'\n", + channel_name, layout_name); + err = AVERROR(EINVAL); + } + } + } + + return err; +} + +AVFilter avfilter_af_channelmap = { + .name = "channelmap", + .description = NULL_IF_CONFIG_SMALL("Remap audio channels."), + .init = channelmap_init, + .query_formats = channelmap_query_formats, + .priv_size = sizeof(ChannelMapContext), + + .inputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .filter_samples = channelmap_filter_samples, + .config_props = channelmap_config_input }, + { .name = NULL }}, + .outputs = (AVFilterPad[]) {{ .name = "default", + .type = AVMEDIA_TYPE_AUDIO }, + { .name = NULL }}, +}; diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 786c2dea7b..9c22b4fe9e 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -40,6 +40,7 @@ void avfilter_register_all(void) REGISTER_FILTER (ANULL, anull, af); REGISTER_FILTER (ASPLIT, asplit, af); REGISTER_FILTER (ASYNCTS, asyncts, af); + REGISTER_FILTER (CHANNELMAP, channelmap, af); REGISTER_FILTER (CHANNELSPLIT,channelsplit,af); REGISTER_FILTER (JOIN, join, af); REGISTER_FILTER (RESAMPLE, resample, af); From 685f5438bbbf99126856c165f64638e66a4ed355 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Fri, 22 Jun 2012 22:10:31 +0100 Subject: [PATCH 6/9] x86: move some inline asm macros to the only places they are used Signed-off-by: Mans Rullgard --- libavcodec/x86/cavsdsp_mmx.c | 6 ++++++ libavcodec/x86/dsputil_mmx.c | 28 ++++++++++++++++++++++++++++ libavcodec/x86/dsputil_mmx.h | 34 ---------------------------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/libavcodec/x86/cavsdsp_mmx.c b/libavcodec/x86/cavsdsp_mmx.c index 3bc62ea156..f56f85932a 100644 --- a/libavcodec/x86/cavsdsp_mmx.c +++ b/libavcodec/x86/cavsdsp_mmx.c @@ -29,6 +29,12 @@ #include "libavcodec/cavsdsp.h" #include "dsputil_mmx.h" +/* in/out: mma=mma+mmb, mmb=mmb-mma */ +#define SUMSUB_BA( a, b ) \ + "paddw "#b", "#a" \n\t"\ + "paddw "#b", "#b" \n\t"\ + "psubw "#a", "#b" \n\t" + /***************************************************************************** * * inverse transform diff --git a/libavcodec/x86/dsputil_mmx.c b/libavcodec/x86/dsputil_mmx.c index 434d1859ec..71a65e753b 100644 --- a/libavcodec/x86/dsputil_mmx.c +++ b/libavcodec/x86/dsputil_mmx.c @@ -630,6 +630,34 @@ static void add_hfyu_median_prediction_cmov(uint8_t *dst, const uint8_t *top, } #endif +static inline void transpose4x4(uint8_t *dst, uint8_t *src, x86_reg dst_stride, x86_reg src_stride){ + __asm__ volatile( //FIXME could save 1 instruction if done as 8x4 ... + "movd (%1), %%mm0 \n\t" + "add %3, %1 \n\t" + "movd (%1), %%mm1 \n\t" + "movd (%1,%3,1), %%mm2 \n\t" + "movd (%1,%3,2), %%mm3 \n\t" + "punpcklbw %%mm1, %%mm0 \n\t" + "punpcklbw %%mm3, %%mm2 \n\t" + "movq %%mm0, %%mm1 \n\t" + "punpcklwd %%mm2, %%mm0 \n\t" + "punpckhwd %%mm2, %%mm1 \n\t" + "movd %%mm0, (%0) \n\t" + "add %2, %0 \n\t" + "punpckhdq %%mm0, %%mm0 \n\t" + "movd %%mm0, (%0) \n\t" + "movd %%mm1, (%0,%2,1) \n\t" + "punpckhdq %%mm1, %%mm1 \n\t" + "movd %%mm1, (%0,%2,2) \n\t" + + : "+&r" (dst), + "+&r" (src) + : "r" (dst_stride), + "r" (src_stride) + : "memory" + ); +} + #define H263_LOOP_FILTER \ "pxor %%mm7, %%mm7 \n\t" \ "movq %0, %%mm0 \n\t" \ diff --git a/libavcodec/x86/dsputil_mmx.h b/libavcodec/x86/dsputil_mmx.h index 37f4581b9c..fa42be6469 100644 --- a/libavcodec/x86/dsputil_mmx.h +++ b/libavcodec/x86/dsputil_mmx.h @@ -78,12 +78,6 @@ extern const double ff_pd_2[2]; "movq "#c", 2*"#stride"+"#out"\n\t"\ "movq "#d", 3*"#stride"+"#out"\n\t" -/* in/out: mma=mma+mmb, mmb=mmb-mma */ -#define SUMSUB_BA( a, b ) \ - "paddw "#b", "#a" \n\t"\ - "paddw "#b", "#b" \n\t"\ - "psubw "#a", "#b" \n\t" - #define SBUTTERFLY(a,b,t,n,m)\ "mov" #m " " #a ", " #t " \n\t" /* abcd */\ "punpckl" #n " " #b ", " #a " \n\t" /* aebf */\ @@ -95,34 +89,6 @@ extern const double ff_pd_2[2]; SBUTTERFLY(a,c,d,dq,q) /* a=aeim d=bfjn */\ SBUTTERFLY(t,b,c,dq,q) /* t=cgko c=dhlp */ -static inline void transpose4x4(uint8_t *dst, uint8_t *src, x86_reg dst_stride, x86_reg src_stride){ - __asm__ volatile( //FIXME could save 1 instruction if done as 8x4 ... - "movd (%1), %%mm0 \n\t" - "add %3, %1 \n\t" - "movd (%1), %%mm1 \n\t" - "movd (%1,%3,1), %%mm2 \n\t" - "movd (%1,%3,2), %%mm3 \n\t" - "punpcklbw %%mm1, %%mm0 \n\t" - "punpcklbw %%mm3, %%mm2 \n\t" - "movq %%mm0, %%mm1 \n\t" - "punpcklwd %%mm2, %%mm0 \n\t" - "punpckhwd %%mm2, %%mm1 \n\t" - "movd %%mm0, (%0) \n\t" - "add %2, %0 \n\t" - "punpckhdq %%mm0, %%mm0 \n\t" - "movd %%mm0, (%0) \n\t" - "movd %%mm1, (%0,%2,1) \n\t" - "punpckhdq %%mm1, %%mm1 \n\t" - "movd %%mm1, (%0,%2,2) \n\t" - - : "+&r" (dst), - "+&r" (src) - : "r" (dst_stride), - "r" (src_stride) - : "memory" - ); -} - // e,f,g,h can be memory // out: a,d,t,c #define TRANSPOSE8x4(a,b,c,d,e,f,g,h,t)\ From 29686d6ea3521f8cc6ca3844c4320a84c7420fa9 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Fri, 22 Jun 2012 22:11:02 +0100 Subject: [PATCH 7/9] x86: remove unused inline asm macros from dsputil_mmx.h Signed-off-by: Mans Rullgard --- libavcodec/x86/dsputil_mmx.h | 68 ------------------------------------ 1 file changed, 68 deletions(-) diff --git a/libavcodec/x86/dsputil_mmx.h b/libavcodec/x86/dsputil_mmx.h index fa42be6469..f1db78d5c4 100644 --- a/libavcodec/x86/dsputil_mmx.h +++ b/libavcodec/x86/dsputil_mmx.h @@ -66,18 +66,6 @@ extern const xmm_reg ff_pb_FE; extern const double ff_pd_1[2]; extern const double ff_pd_2[2]; -#define LOAD4(stride,in,a,b,c,d)\ - "movq 0*"#stride"+"#in", "#a"\n\t"\ - "movq 1*"#stride"+"#in", "#b"\n\t"\ - "movq 2*"#stride"+"#in", "#c"\n\t"\ - "movq 3*"#stride"+"#in", "#d"\n\t" - -#define STORE4(stride,out,a,b,c,d)\ - "movq "#a", 0*"#stride"+"#out"\n\t"\ - "movq "#b", 1*"#stride"+"#out"\n\t"\ - "movq "#c", 2*"#stride"+"#out"\n\t"\ - "movq "#d", 3*"#stride"+"#out"\n\t" - #define SBUTTERFLY(a,b,t,n,m)\ "mov" #m " " #a ", " #t " \n\t" /* abcd */\ "punpckl" #n " " #b ", " #a " \n\t" /* aebf */\ @@ -89,62 +77,6 @@ extern const double ff_pd_2[2]; SBUTTERFLY(a,c,d,dq,q) /* a=aeim d=bfjn */\ SBUTTERFLY(t,b,c,dq,q) /* t=cgko c=dhlp */ -// e,f,g,h can be memory -// out: a,d,t,c -#define TRANSPOSE8x4(a,b,c,d,e,f,g,h,t)\ - "punpcklbw " #e ", " #a " \n\t" /* a0 e0 a1 e1 a2 e2 a3 e3 */\ - "punpcklbw " #f ", " #b " \n\t" /* b0 f0 b1 f1 b2 f2 b3 f3 */\ - "punpcklbw " #g ", " #c " \n\t" /* c0 g0 c1 g1 c2 g2 d3 g3 */\ - "punpcklbw " #h ", " #d " \n\t" /* d0 h0 d1 h1 d2 h2 d3 h3 */\ - SBUTTERFLY(a, b, t, bw, q) /* a= a0 b0 e0 f0 a1 b1 e1 f1 */\ - /* t= a2 b2 e2 f2 a3 b3 e3 f3 */\ - SBUTTERFLY(c, d, b, bw, q) /* c= c0 d0 g0 h0 c1 d1 g1 h1 */\ - /* b= c2 d2 g2 h2 c3 d3 g3 h3 */\ - SBUTTERFLY(a, c, d, wd, q) /* a= a0 b0 c0 d0 e0 f0 g0 h0 */\ - /* d= a1 b1 c1 d1 e1 f1 g1 h1 */\ - SBUTTERFLY(t, b, c, wd, q) /* t= a2 b2 c2 d2 e2 f2 g2 h2 */\ - /* c= a3 b3 c3 d3 e3 f3 g3 h3 */ - -#if ARCH_X86_64 -// permutes 01234567 -> 05736421 -#define TRANSPOSE8(a,b,c,d,e,f,g,h,t)\ - SBUTTERFLY(a,b,%%xmm8,wd,dqa)\ - SBUTTERFLY(c,d,b,wd,dqa)\ - SBUTTERFLY(e,f,d,wd,dqa)\ - SBUTTERFLY(g,h,f,wd,dqa)\ - SBUTTERFLY(a,c,h,dq,dqa)\ - SBUTTERFLY(%%xmm8,b,c,dq,dqa)\ - SBUTTERFLY(e,g,b,dq,dqa)\ - SBUTTERFLY(d,f,g,dq,dqa)\ - SBUTTERFLY(a,e,f,qdq,dqa)\ - SBUTTERFLY(%%xmm8,d,e,qdq,dqa)\ - SBUTTERFLY(h,b,d,qdq,dqa)\ - SBUTTERFLY(c,g,b,qdq,dqa)\ - "movdqa %%xmm8, "#g" \n\t" -#else -#define TRANSPOSE8(a,b,c,d,e,f,g,h,t)\ - "movdqa "#h", "#t" \n\t"\ - SBUTTERFLY(a,b,h,wd,dqa)\ - "movdqa "#h", 16"#t" \n\t"\ - "movdqa "#t", "#h" \n\t"\ - SBUTTERFLY(c,d,b,wd,dqa)\ - SBUTTERFLY(e,f,d,wd,dqa)\ - SBUTTERFLY(g,h,f,wd,dqa)\ - SBUTTERFLY(a,c,h,dq,dqa)\ - "movdqa "#h", "#t" \n\t"\ - "movdqa 16"#t", "#h" \n\t"\ - SBUTTERFLY(h,b,c,dq,dqa)\ - SBUTTERFLY(e,g,b,dq,dqa)\ - SBUTTERFLY(d,f,g,dq,dqa)\ - SBUTTERFLY(a,e,f,qdq,dqa)\ - SBUTTERFLY(h,d,e,qdq,dqa)\ - "movdqa "#h", 16"#t" \n\t"\ - "movdqa "#t", "#h" \n\t"\ - SBUTTERFLY(h,b,d,qdq,dqa)\ - SBUTTERFLY(c,g,b,qdq,dqa)\ - "movdqa 16"#t", "#g" \n\t" -#endif - #define MOVQ_WONE(regd) \ __asm__ volatile ( \ "pcmpeqd %%" #regd ", %%" #regd " \n\t" \ From 65345a5a30a0e866b6944c0e6184be3feca04335 Mon Sep 17 00:00:00 2001 From: Diego Biurrun Date: Tue, 19 Jun 2012 22:55:26 +0200 Subject: [PATCH 8/9] x86: Add CPU flag for the i686 cmov instruction --- libavutil/cpu.c | 4 +++- libavutil/cpu.h | 2 ++ libavutil/x86/cpu.c | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libavutil/cpu.c b/libavutil/cpu.c index 6532744d2b..c641106fff 100644 --- a/libavutil/cpu.c +++ b/libavutil/cpu.c @@ -47,7 +47,7 @@ void av_set_cpu_flags_mask(int mask) int av_parse_cpu_flags(const char *s) { -#define CPUFLAG_MMX2 (AV_CPU_FLAG_MMX | AV_CPU_FLAG_MMX2) +#define CPUFLAG_MMX2 (AV_CPU_FLAG_MMX | AV_CPU_FLAG_MMX2 | AV_CPU_FLAG_CMOV) #define CPUFLAG_3DNOW (AV_CPU_FLAG_3DNOW | AV_CPU_FLAG_MMX) #define CPUFLAG_3DNOWEXT (AV_CPU_FLAG_3DNOWEXT | CPUFLAG_3DNOW) #define CPUFLAG_SSE (AV_CPU_FLAG_SSE | CPUFLAG_MMX2) @@ -82,6 +82,7 @@ int av_parse_cpu_flags(const char *s) { "fma4" , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_FMA4 }, .unit = "flags" }, { "3dnow" , NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOW }, .unit = "flags" }, { "3dnowext", NULL, 0, AV_OPT_TYPE_CONST, { CPUFLAG_3DNOWEXT }, .unit = "flags" }, + { "cmov", NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_CMOV }, .unit = "flags" }, #elif ARCH_ARM { "armv5te", NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_ARMV5TE }, .unit = "flags" }, { "armv6", NULL, 0, AV_OPT_TYPE_CONST, { AV_CPU_FLAG_ARMV6 }, .unit = "flags" }, @@ -143,6 +144,7 @@ static const struct { { AV_CPU_FLAG_FMA4, "fma4" }, { AV_CPU_FLAG_3DNOW, "3dnow" }, { AV_CPU_FLAG_3DNOWEXT, "3dnowext" }, + { AV_CPU_FLAG_CMOV, "cmov" }, #endif { 0 } }; diff --git a/libavutil/cpu.h b/libavutil/cpu.h index 15c0088b7f..f477c83e13 100644 --- a/libavutil/cpu.h +++ b/libavutil/cpu.h @@ -40,6 +40,8 @@ #define AV_CPU_FLAG_AVX 0x4000 ///< AVX functions: requires OS support even if YMM registers aren't used #define AV_CPU_FLAG_XOP 0x0400 ///< Bulldozer XOP functions #define AV_CPU_FLAG_FMA4 0x0800 ///< Bulldozer FMA4 functions +#define AV_CPU_FLAG_CMOV 0x1000 ///< i686 cmov + #define AV_CPU_FLAG_ALTIVEC 0x0001 ///< standard #define AV_CPU_FLAG_ARMV5TE (1 << 0) diff --git a/libavutil/x86/cpu.c b/libavutil/x86/cpu.c index 2424fe4516..b87d3a3a92 100644 --- a/libavutil/x86/cpu.c +++ b/libavutil/x86/cpu.c @@ -83,6 +83,8 @@ int ff_get_cpu_flags_x86(void) cpuid(1, eax, ebx, ecx, std_caps); family = ((eax>>8)&0xf) + ((eax>>20)&0xff); model = ((eax>>4)&0xf) + ((eax>>12)&0xf0); + if (std_caps & (1 << 15)) + rval |= AV_CPU_FLAG_CMOV; if (std_caps & (1<<23)) rval |= AV_CPU_FLAG_MMX; if (std_caps & (1<<25)) From fe07c9c6b5a870b8f2ffcfac649228b4d76e9505 Mon Sep 17 00:00:00 2001 From: Diego Biurrun Date: Tue, 19 Jun 2012 12:55:10 +0200 Subject: [PATCH 9/9] x86: Only use optimizations with cmov if the CPU supports the instruction --- libavcodec/x86/dsputil_mmx.c | 3 ++- libavcodec/x86/h264_intrapred_init.c | 3 ++- libavcodec/x86/h264dsp_mmx.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libavcodec/x86/dsputil_mmx.c b/libavcodec/x86/dsputil_mmx.c index 71a65e753b..b695bd28ec 100644 --- a/libavcodec/x86/dsputil_mmx.c +++ b/libavcodec/x86/dsputil_mmx.c @@ -2829,7 +2829,8 @@ static void dsputil_init_3dnow(DSPContext *c, AVCodecContext *avctx, c->vorbis_inverse_coupling = vorbis_inverse_coupling_3dnow; #if HAVE_7REGS - c->add_hfyu_median_prediction = add_hfyu_median_prediction_cmov; + if (mm_flags & AV_CPU_FLAG_CMOV) + c->add_hfyu_median_prediction = add_hfyu_median_prediction_cmov; #endif } diff --git a/libavcodec/x86/h264_intrapred_init.c b/libavcodec/x86/h264_intrapred_init.c index 41e611ecd1..6bad9696c7 100644 --- a/libavcodec/x86/h264_intrapred_init.c +++ b/libavcodec/x86/h264_intrapred_init.c @@ -188,7 +188,8 @@ void ff_h264_pred_init_x86(H264PredContext *h, int codec_id, const int bit_depth if (chroma_format_idc == 1) h->pred8x8 [PLANE_PRED8x8] = ff_pred8x8_plane_mmx; if (codec_id == CODEC_ID_SVQ3) { - h->pred16x16[PLANE_PRED8x8] = ff_pred16x16_plane_svq3_mmx; + if (mm_flags & AV_CPU_FLAG_CMOV) + h->pred16x16[PLANE_PRED8x8] = ff_pred16x16_plane_svq3_mmx; } else if (codec_id == CODEC_ID_RV40) { h->pred16x16[PLANE_PRED8x8] = ff_pred16x16_plane_rv40_mmx; } else { diff --git a/libavcodec/x86/h264dsp_mmx.c b/libavcodec/x86/h264dsp_mmx.c index dcd918013c..c0a40c42d7 100644 --- a/libavcodec/x86/h264dsp_mmx.c +++ b/libavcodec/x86/h264dsp_mmx.c @@ -361,7 +361,8 @@ void ff_h264dsp_init_x86(H264DSPContext *c, const int bit_depth, const int chrom if (chroma_format_idc == 1) c->h264_idct_add8 = ff_h264_idct_add8_8_mmx; c->h264_idct_add16intra = ff_h264_idct_add16intra_8_mmx; - c->h264_luma_dc_dequant_idct= ff_h264_luma_dc_dequant_idct_mmx; + if (mm_flags & AV_CPU_FLAG_CMOV) + c->h264_luma_dc_dequant_idct = ff_h264_luma_dc_dequant_idct_mmx; if (mm_flags & AV_CPU_FLAG_MMX2) { c->h264_idct_dc_add = ff_h264_idct_dc_add_8_mmx2;