Bundling ffmpeg, initial attempt. This builds on MinGW; Linux is untested as of yet.

This commit is contained in:
Ben "root" Anderson
2014-02-09 17:23:19 -06:00
parent 7341280df8
commit f56df1c44b
4045 changed files with 962454 additions and 11 deletions
+196
View File
@@ -0,0 +1,196 @@
/*
* Copyright (c) 2010 S.N. Hemanth Meenakshisundaram <[email protected]>
* Copyright (c) 2011 Stefano Sabatini
* Copyright (c) 2011 Mina Nagy Zaki
*
* 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
* sample format and channel layout conversion audio filter
*/
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
typedef struct {
const AVClass *class;
enum AVSampleFormat out_sample_fmt;
int64_t out_chlayout;
struct SwrContext *swr;
char *format_str;
char *channel_layout_str;
} AConvertContext;
#define OFFSET(x) offsetof(AConvertContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption aconvert_options[] = {
{ "sample_fmt", "", OFFSET(format_str), AV_OPT_TYPE_STRING, .flags = A|F },
{ "channel_layout", "", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(aconvert);
static av_cold int init(AVFilterContext *ctx)
{
AConvertContext *aconvert = ctx->priv;
int ret = 0;
av_log(ctx, AV_LOG_WARNING, "This filter is deprecated, use aformat instead\n");
aconvert->out_sample_fmt = AV_SAMPLE_FMT_NONE;
aconvert->out_chlayout = 0;
if (aconvert->format_str && strcmp(aconvert->format_str, "auto") &&
(ret = ff_parse_sample_format(&aconvert->out_sample_fmt, aconvert->format_str, ctx)) < 0)
return ret;
if (aconvert->channel_layout_str && strcmp(aconvert->channel_layout_str, "auto"))
return ff_parse_channel_layout(&aconvert->out_chlayout, NULL, aconvert->channel_layout_str, ctx);
return ret;
}
static av_cold void uninit(AVFilterContext *ctx)
{
AConvertContext *aconvert = ctx->priv;
swr_free(&aconvert->swr);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
AConvertContext *aconvert = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
AVFilterChannelLayouts *layouts;
ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO),
&inlink->out_formats);
if (aconvert->out_sample_fmt != AV_SAMPLE_FMT_NONE) {
formats = NULL;
ff_add_format(&formats, aconvert->out_sample_fmt);
ff_formats_ref(formats, &outlink->in_formats);
} else
ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_AUDIO),
&outlink->in_formats);
ff_channel_layouts_ref(ff_all_channel_layouts(),
&inlink->out_channel_layouts);
if (aconvert->out_chlayout != 0) {
layouts = NULL;
ff_add_channel_layout(&layouts, aconvert->out_chlayout);
ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
} else
ff_channel_layouts_ref(ff_all_channel_layouts(),
&outlink->in_channel_layouts);
return 0;
}
static int config_output(AVFilterLink *outlink)
{
int ret;
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink = ctx->inputs[0];
AConvertContext *aconvert = ctx->priv;
char buf1[64], buf2[64];
/* if not specified in args, use the format and layout of the output */
if (aconvert->out_sample_fmt == AV_SAMPLE_FMT_NONE)
aconvert->out_sample_fmt = outlink->format;
if (aconvert->out_chlayout == 0)
aconvert->out_chlayout = outlink->channel_layout;
aconvert->swr = swr_alloc_set_opts(aconvert->swr,
aconvert->out_chlayout, aconvert->out_sample_fmt, inlink->sample_rate,
inlink->channel_layout, inlink->format, inlink->sample_rate,
0, ctx);
if (!aconvert->swr)
return AVERROR(ENOMEM);
ret = swr_init(aconvert->swr);
if (ret < 0)
return ret;
av_get_channel_layout_string(buf1, sizeof(buf1),
-1, inlink ->channel_layout);
av_get_channel_layout_string(buf2, sizeof(buf2),
-1, outlink->channel_layout);
av_log(ctx, AV_LOG_VERBOSE,
"fmt:%s cl:%s -> fmt:%s cl:%s\n",
av_get_sample_fmt_name(inlink ->format), buf1,
av_get_sample_fmt_name(outlink->format), buf2);
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamplesref)
{
AConvertContext *aconvert = inlink->dst->priv;
const int n = insamplesref->nb_samples;
AVFilterLink *const outlink = inlink->dst->outputs[0];
AVFrame *outsamplesref = ff_get_audio_buffer(outlink, n);
int ret;
if (!outsamplesref)
return AVERROR(ENOMEM);
swr_convert(aconvert->swr, outsamplesref->extended_data, n,
(void *)insamplesref->extended_data, n);
av_frame_copy_props(outsamplesref, insamplesref);
av_frame_set_channels(outsamplesref, outlink->channels);
outsamplesref->channel_layout = outlink->channel_layout;
ret = ff_filter_frame(outlink, outsamplesref);
av_frame_free(&insamplesref);
return ret;
}
static const AVFilterPad aconvert_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad aconvert_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
},
{ NULL }
};
AVFilter avfilter_af_aconvert = {
.name = "aconvert",
.description = NULL_IF_CONFIG_SMALL("Convert the input audio to sample_fmt:channel_layout."),
.priv_size = sizeof(AConvertContext),
.priv_class = &aconvert_class,
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = aconvert_inputs,
.outputs = aconvert_outputs,
};
+283
View File
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2013 Paul B Mahol
*
* 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/avstring.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
typedef struct ChanDelay {
int delay;
unsigned delay_index;
unsigned index;
uint8_t *samples;
} ChanDelay;
typedef struct AudioDelayContext {
const AVClass *class;
char *delays;
ChanDelay *chandelay;
int nb_delays;
int block_align;
unsigned max_delay;
int64_t next_pts;
void (*delay_channel)(ChanDelay *d, int nb_samples,
const uint8_t *src, uint8_t *dst);
} AudioDelayContext;
#define OFFSET(x) offsetof(AudioDelayContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption adelay_options[] = {
{ "delays", "set list of delays for each channel", OFFSET(delays), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
{ NULL }
};
AVFILTER_DEFINE_CLASS(adelay);
static int query_formats(AVFilterContext *ctx)
{
AVFilterChannelLayouts *layouts;
AVFilterFormats *formats;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
#define DELAY(name, type, fill) \
static void delay_channel_## name ##p(ChanDelay *d, int nb_samples, \
const uint8_t *ssrc, uint8_t *ddst) \
{ \
const type *src = (type *)ssrc; \
type *dst = (type *)ddst; \
type *samples = (type *)d->samples; \
\
while (nb_samples) { \
if (d->delay_index < d->delay) { \
const int len = FFMIN(nb_samples, d->delay - d->delay_index); \
\
memcpy(&samples[d->delay_index], src, len * sizeof(type)); \
memset(dst, fill, len * sizeof(type)); \
d->delay_index += len; \
src += len; \
dst += len; \
nb_samples -= len; \
} else { \
*dst = samples[d->index]; \
samples[d->index] = *src; \
nb_samples--; \
d->index++; \
src++, dst++; \
d->index = d->index >= d->delay ? 0 : d->index; \
} \
} \
}
DELAY(u8, uint8_t, 0x80)
DELAY(s16, int16_t, 0)
DELAY(s32, int32_t, 0)
DELAY(flt, float, 0)
DELAY(dbl, double, 0)
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
AudioDelayContext *s = ctx->priv;
char *p, *arg, *saveptr = NULL;
int i;
s->chandelay = av_calloc(inlink->channels, sizeof(*s->chandelay));
if (!s->chandelay)
return AVERROR(ENOMEM);
s->nb_delays = inlink->channels;
s->block_align = av_get_bytes_per_sample(inlink->format);
p = s->delays;
for (i = 0; i < s->nb_delays; i++) {
ChanDelay *d = &s->chandelay[i];
float delay;
if (!(arg = av_strtok(p, "|", &saveptr)))
break;
p = NULL;
sscanf(arg, "%f", &delay);
d->delay = delay * inlink->sample_rate / 1000.0;
if (d->delay < 0) {
av_log(ctx, AV_LOG_ERROR, "Delay must be non negative number.\n");
return AVERROR(EINVAL);
}
}
for (i = 0; i < s->nb_delays; i++) {
ChanDelay *d = &s->chandelay[i];
if (!d->delay)
continue;
d->samples = av_malloc_array(d->delay, s->block_align);
if (!d->samples)
return AVERROR(ENOMEM);
s->max_delay = FFMAX(s->max_delay, d->delay);
}
if (!s->max_delay) {
av_log(ctx, AV_LOG_ERROR, "At least one delay >0 must be specified.\n");
return AVERROR(EINVAL);
}
switch (inlink->format) {
case AV_SAMPLE_FMT_U8P : s->delay_channel = delay_channel_u8p ; break;
case AV_SAMPLE_FMT_S16P: s->delay_channel = delay_channel_s16p; break;
case AV_SAMPLE_FMT_S32P: s->delay_channel = delay_channel_s32p; break;
case AV_SAMPLE_FMT_FLTP: s->delay_channel = delay_channel_fltp; break;
case AV_SAMPLE_FMT_DBLP: s->delay_channel = delay_channel_dblp; break;
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
AudioDelayContext *s = ctx->priv;
AVFrame *out_frame;
int i;
if (ctx->is_disabled || !s->delays)
return ff_filter_frame(ctx->outputs[0], frame);
out_frame = ff_get_audio_buffer(inlink, frame->nb_samples);
if (!out_frame)
return AVERROR(ENOMEM);
av_frame_copy_props(out_frame, frame);
for (i = 0; i < s->nb_delays; i++) {
ChanDelay *d = &s->chandelay[i];
const uint8_t *src = frame->extended_data[i];
uint8_t *dst = out_frame->extended_data[i];
if (!d->delay)
memcpy(dst, src, frame->nb_samples * s->block_align);
else
s->delay_channel(d, frame->nb_samples, src, dst);
}
s->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, inlink->sample_rate}, inlink->time_base);
av_frame_free(&frame);
return ff_filter_frame(ctx->outputs[0], out_frame);
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AudioDelayContext *s = ctx->priv;
int ret;
ret = ff_request_frame(ctx->inputs[0]);
if (ret == AVERROR_EOF && !ctx->is_disabled && s->max_delay) {
int nb_samples = FFMIN(s->max_delay, 2048);
AVFrame *frame;
frame = ff_get_audio_buffer(outlink, nb_samples);
if (!frame)
return AVERROR(ENOMEM);
s->max_delay -= nb_samples;
av_samples_set_silence(frame->extended_data, 0,
frame->nb_samples,
outlink->channels,
frame->format);
frame->pts = s->next_pts;
if (s->next_pts != AV_NOPTS_VALUE)
s->next_pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
ret = filter_frame(ctx->inputs[0], frame);
}
return ret;
}
static av_cold void uninit(AVFilterContext *ctx)
{
AudioDelayContext *s = ctx->priv;
int i;
for (i = 0; i < s->nb_delays; i++)
av_free(s->chandelay[i].samples);
av_freep(&s->chandelay);
}
static const AVFilterPad adelay_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_input,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad adelay_outputs[] = {
{
.name = "default",
.request_frame = request_frame,
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_adelay = {
.name = "adelay",
.description = NULL_IF_CONFIG_SMALL("Delay one or more audio channels."),
.query_formats = query_formats,
.priv_size = sizeof(AudioDelayContext),
.priv_class = &adelay_class,
.uninit = uninit,
.inputs = adelay_inputs,
.outputs = adelay_outputs,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
};
+359
View File
@@ -0,0 +1,359 @@
/*
* Copyright (c) 2013 Paul B Mahol
*
* 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/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
typedef struct AudioEchoContext {
const AVClass *class;
float in_gain, out_gain;
char *delays, *decays;
float *delay, *decay;
int nb_echoes;
int delay_index;
uint8_t **delayptrs;
int max_samples, fade_out;
int *samples;
int64_t next_pts;
void (*echo_samples)(struct AudioEchoContext *ctx, uint8_t **delayptrs,
uint8_t * const *src, uint8_t **dst,
int nb_samples, int channels);
} AudioEchoContext;
#define OFFSET(x) offsetof(AudioEchoContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption aecho_options[] = {
{ "in_gain", "set signal input gain", OFFSET(in_gain), AV_OPT_TYPE_FLOAT, {.dbl=0.6}, 0, 1, A },
{ "out_gain", "set signal output gain", OFFSET(out_gain), AV_OPT_TYPE_FLOAT, {.dbl=0.3}, 0, 1, A },
{ "delays", "set list of signal delays", OFFSET(delays), AV_OPT_TYPE_STRING, {.str="1000"}, 0, 0, A },
{ "decays", "set list of signal decays", OFFSET(decays), AV_OPT_TYPE_STRING, {.str="0.5"}, 0, 0, A },
{ NULL }
};
AVFILTER_DEFINE_CLASS(aecho);
static void count_items(char *item_str, int *nb_items)
{
char *p;
*nb_items = 1;
for (p = item_str; *p; p++) {
if (*p == '|')
(*nb_items)++;
}
}
static void fill_items(char *item_str, int *nb_items, float *items)
{
char *p, *saveptr = NULL;
int i, new_nb_items = 0;
p = item_str;
for (i = 0; i < *nb_items; i++) {
char *tstr = av_strtok(p, "|", &saveptr);
p = NULL;
new_nb_items += sscanf(tstr, "%f", &items[i]) == 1;
}
*nb_items = new_nb_items;
}
static av_cold void uninit(AVFilterContext *ctx)
{
AudioEchoContext *s = ctx->priv;
av_freep(&s->delay);
av_freep(&s->decay);
av_freep(&s->samples);
if (s->delayptrs)
av_freep(&s->delayptrs[0]);
av_freep(&s->delayptrs);
}
static av_cold int init(AVFilterContext *ctx)
{
AudioEchoContext *s = ctx->priv;
int nb_delays, nb_decays, i;
if (!s->delays || !s->decays) {
av_log(ctx, AV_LOG_ERROR, "Missing delays and/or decays.\n");
return AVERROR(EINVAL);
}
count_items(s->delays, &nb_delays);
count_items(s->decays, &nb_decays);
s->delay = av_realloc_f(s->delay, nb_delays, sizeof(*s->delay));
s->decay = av_realloc_f(s->decay, nb_decays, sizeof(*s->decay));
if (!s->delay || !s->decay)
return AVERROR(ENOMEM);
fill_items(s->delays, &nb_delays, s->delay);
fill_items(s->decays, &nb_decays, s->decay);
if (nb_delays != nb_decays) {
av_log(ctx, AV_LOG_ERROR, "Number of delays %d differs from number of decays %d.\n", nb_delays, nb_decays);
return AVERROR(EINVAL);
}
s->nb_echoes = nb_delays;
if (!s->nb_echoes) {
av_log(ctx, AV_LOG_ERROR, "At least one decay & delay must be set.\n");
return AVERROR(EINVAL);
}
s->samples = av_realloc_f(s->samples, nb_delays, sizeof(*s->samples));
if (!s->samples)
return AVERROR(ENOMEM);
for (i = 0; i < nb_delays; i++) {
if (s->delay[i] <= 0 || s->delay[i] > 90000) {
av_log(ctx, AV_LOG_ERROR, "delay[%d]: %f is out of allowed range: (0, 90000]\n", i, s->delay[i]);
return AVERROR(EINVAL);
}
if (s->decay[i] <= 0 || s->decay[i] > 1) {
av_log(ctx, AV_LOG_ERROR, "decay[%d]: %f is out of allowed range: (0, 1]\n", i, s->decay[i]);
return AVERROR(EINVAL);
}
}
s->next_pts = AV_NOPTS_VALUE;
av_log(ctx, AV_LOG_DEBUG, "nb_echoes:%d\n", s->nb_echoes);
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterChannelLayouts *layouts;
AVFilterFormats *formats;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
#define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a))
#define ECHO(name, type, min, max) \
static void echo_samples_## name ##p(AudioEchoContext *ctx, \
uint8_t **delayptrs, \
uint8_t * const *src, uint8_t **dst, \
int nb_samples, int channels) \
{ \
const double out_gain = ctx->out_gain; \
const double in_gain = ctx->in_gain; \
const int nb_echoes = ctx->nb_echoes; \
const int max_samples = ctx->max_samples; \
int i, j, chan, av_uninit(index); \
\
av_assert1(channels > 0); /* would corrupt delay_index */ \
\
for (chan = 0; chan < channels; chan++) { \
const type *s = (type *)src[chan]; \
type *d = (type *)dst[chan]; \
type *dbuf = (type *)delayptrs[chan]; \
\
index = ctx->delay_index; \
for (i = 0; i < nb_samples; i++, s++, d++) { \
double out, in; \
\
in = *s; \
out = in * in_gain; \
for (j = 0; j < nb_echoes; j++) { \
int ix = index + max_samples - ctx->samples[j]; \
ix = MOD(ix, max_samples); \
out += dbuf[ix] * ctx->decay[j]; \
} \
out *= out_gain; \
\
*d = av_clipd(out, min, max); \
dbuf[index] = in; \
\
index = MOD(index + 1, max_samples); \
} \
} \
ctx->delay_index = index; \
}
ECHO(dbl, double, -1.0, 1.0 )
ECHO(flt, float, -1.0, 1.0 )
ECHO(s16, int16_t, INT16_MIN, INT16_MAX)
ECHO(s32, int32_t, INT32_MIN, INT32_MAX)
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AudioEchoContext *s = ctx->priv;
float volume = 1.0;
int i;
for (i = 0; i < s->nb_echoes; i++) {
s->samples[i] = s->delay[i] * outlink->sample_rate / 1000.0;
s->max_samples = FFMAX(s->max_samples, s->samples[i]);
volume += s->decay[i];
}
if (s->max_samples <= 0) {
av_log(ctx, AV_LOG_ERROR, "Nothing to echo - missing delay samples.\n");
return AVERROR(EINVAL);
}
s->fade_out = s->max_samples;
if (volume * s->in_gain * s->out_gain > 1.0)
av_log(ctx, AV_LOG_WARNING,
"out_gain %f can cause saturation of output\n", s->out_gain);
switch (outlink->format) {
case AV_SAMPLE_FMT_DBLP: s->echo_samples = echo_samples_dblp; break;
case AV_SAMPLE_FMT_FLTP: s->echo_samples = echo_samples_fltp; break;
case AV_SAMPLE_FMT_S16P: s->echo_samples = echo_samples_s16p; break;
case AV_SAMPLE_FMT_S32P: s->echo_samples = echo_samples_s32p; break;
}
if (s->delayptrs)
av_freep(&s->delayptrs[0]);
av_freep(&s->delayptrs);
return av_samples_alloc_array_and_samples(&s->delayptrs, NULL,
outlink->channels,
s->max_samples,
outlink->format, 0);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
AudioEchoContext *s = ctx->priv;
AVFrame *out_frame;
if (av_frame_is_writable(frame)) {
out_frame = frame;
} else {
out_frame = ff_get_audio_buffer(inlink, frame->nb_samples);
if (!out_frame)
return AVERROR(ENOMEM);
av_frame_copy_props(out_frame, frame);
}
s->echo_samples(s, s->delayptrs, frame->extended_data, out_frame->extended_data,
frame->nb_samples, inlink->channels);
if (frame != out_frame)
av_frame_free(&frame);
s->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, inlink->sample_rate}, inlink->time_base);
return ff_filter_frame(ctx->outputs[0], out_frame);
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AudioEchoContext *s = ctx->priv;
int ret;
ret = ff_request_frame(ctx->inputs[0]);
if (ret == AVERROR_EOF && !ctx->is_disabled && s->fade_out) {
int nb_samples = FFMIN(s->fade_out, 2048);
AVFrame *frame;
frame = ff_get_audio_buffer(outlink, nb_samples);
if (!frame)
return AVERROR(ENOMEM);
s->fade_out -= nb_samples;
av_samples_set_silence(frame->extended_data, 0,
frame->nb_samples,
outlink->channels,
frame->format);
s->echo_samples(s, s->delayptrs, frame->extended_data, frame->extended_data,
frame->nb_samples, outlink->channels);
frame->pts = s->next_pts;
if (s->next_pts != AV_NOPTS_VALUE)
s->next_pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
return ff_filter_frame(outlink, frame);
}
return ret;
}
static const AVFilterPad aecho_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad aecho_outputs[] = {
{
.name = "default",
.request_frame = request_frame,
.config_props = config_output,
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_aecho = {
.name = "aecho",
.description = NULL_IF_CONFIG_SMALL("Add echoing to the audio."),
.query_formats = query_formats,
.priv_size = sizeof(AudioEchoContext),
.priv_class = &aecho_class,
.init = init,
.uninit = uninit,
.inputs = aecho_inputs,
.outputs = aecho_outputs,
};
+300
View File
@@ -0,0 +1,300 @@
/*
* Copyright (c) 2013 Paul B Mahol
*
* 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
* fade audio filter
*/
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct {
const AVClass *class;
int type;
int curve;
int nb_samples;
int64_t start_sample;
int64_t duration;
int64_t start_time;
void (*fade_samples)(uint8_t **dst, uint8_t * const *src,
int nb_samples, int channels, int direction,
int64_t start, int range, int curve);
} AudioFadeContext;
enum CurveType { TRI, QSIN, ESIN, HSIN, LOG, PAR, QUA, CUB, SQU, CBR };
#define OFFSET(x) offsetof(AudioFadeContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption afade_options[] = {
{ "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "type" },
{ "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "type" },
{ "in", "fade-in", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "type" },
{ "out", "fade-out", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "type" },
{ "start_sample", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, FLAGS },
{ "ss", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, FLAGS },
{ "nb_samples", "set number of samples for fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX, FLAGS },
{ "ns", "set number of samples for fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT32_MAX, FLAGS },
{ "start_time", "set time to start fading", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "st", "set time to start fading", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "duration", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "d", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "curve", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, TRI, CBR, FLAGS, "curve" },
{ "c", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, TRI, CBR, FLAGS, "curve" },
{ "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, FLAGS, "curve" },
{ "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, FLAGS, "curve" },
{ "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, FLAGS, "curve" },
{ "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, FLAGS, "curve" },
{ "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, FLAGS, "curve" },
{ "par", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, FLAGS, "curve" },
{ "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, FLAGS, "curve" },
{ "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, FLAGS, "curve" },
{ "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, FLAGS, "curve" },
{ "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, FLAGS, "curve" },
{ NULL }
};
AVFILTER_DEFINE_CLASS(afade);
static av_cold int init(AVFilterContext *ctx)
{
AudioFadeContext *s = ctx->priv;
if (INT64_MAX - s->nb_samples < s->start_sample)
return AVERROR(EINVAL);
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats;
AVFilterChannelLayouts *layouts;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
static double fade_gain(int curve, int64_t index, int range)
{
double gain;
gain = FFMAX(0.0, FFMIN(1.0, 1.0 * index / range));
switch (curve) {
case QSIN:
gain = sin(gain * M_PI / 2.0);
break;
case ESIN:
gain = 1.0 - cos(M_PI / 4.0 * (pow(2.0*gain - 1, 3) + 1));
break;
case HSIN:
gain = (1.0 - cos(gain * M_PI)) / 2.0;
break;
case LOG:
gain = pow(0.1, (1 - gain) * 5.0);
break;
case PAR:
gain = (1 - (1 - gain) * (1 - gain));
break;
case QUA:
gain *= gain;
break;
case CUB:
gain = gain * gain * gain;
break;
case SQU:
gain = sqrt(gain);
break;
case CBR:
gain = cbrt(gain);
break;
}
return gain;
}
#define FADE_PLANAR(name, type) \
static void fade_samples_## name ##p(uint8_t **dst, uint8_t * const *src, \
int nb_samples, int channels, int dir, \
int64_t start, int range, int curve) \
{ \
int i, c; \
\
for (i = 0; i < nb_samples; i++) { \
double gain = fade_gain(curve, start + i * dir, range); \
for (c = 0; c < channels; c++) { \
type *d = (type *)dst[c]; \
const type *s = (type *)src[c]; \
\
d[i] = s[i] * gain; \
} \
} \
}
#define FADE(name, type) \
static void fade_samples_## name (uint8_t **dst, uint8_t * const *src, \
int nb_samples, int channels, int dir, \
int64_t start, int range, int curve) \
{ \
type *d = (type *)dst[0]; \
const type *s = (type *)src[0]; \
int i, c, k = 0; \
\
for (i = 0; i < nb_samples; i++) { \
double gain = fade_gain(curve, start + i * dir, range); \
for (c = 0; c < channels; c++, k++) \
d[k] = s[k] * gain; \
} \
}
FADE_PLANAR(dbl, double)
FADE_PLANAR(flt, float)
FADE_PLANAR(s16, int16_t)
FADE_PLANAR(s32, int32_t)
FADE(dbl, double)
FADE(flt, float)
FADE(s16, int16_t)
FADE(s32, int32_t)
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
AudioFadeContext *s = ctx->priv;
switch (inlink->format) {
case AV_SAMPLE_FMT_DBL: s->fade_samples = fade_samples_dbl; break;
case AV_SAMPLE_FMT_DBLP: s->fade_samples = fade_samples_dblp; break;
case AV_SAMPLE_FMT_FLT: s->fade_samples = fade_samples_flt; break;
case AV_SAMPLE_FMT_FLTP: s->fade_samples = fade_samples_fltp; break;
case AV_SAMPLE_FMT_S16: s->fade_samples = fade_samples_s16; break;
case AV_SAMPLE_FMT_S16P: s->fade_samples = fade_samples_s16p; break;
case AV_SAMPLE_FMT_S32: s->fade_samples = fade_samples_s32; break;
case AV_SAMPLE_FMT_S32P: s->fade_samples = fade_samples_s32p; break;
}
if (s->duration)
s->nb_samples = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE);
if (s->start_time)
s->start_sample = av_rescale(s->start_time, inlink->sample_rate, AV_TIME_BASE);
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
AudioFadeContext *s = inlink->dst->priv;
AVFilterLink *outlink = inlink->dst->outputs[0];
int nb_samples = buf->nb_samples;
AVFrame *out_buf;
int64_t cur_sample = av_rescale_q(buf->pts, (AVRational){1, outlink->sample_rate}, outlink->time_base);
if ((!s->type && (s->start_sample + s->nb_samples < cur_sample)) ||
( s->type && (cur_sample + s->nb_samples < s->start_sample)))
return ff_filter_frame(outlink, buf);
if (av_frame_is_writable(buf)) {
out_buf = buf;
} else {
out_buf = ff_get_audio_buffer(inlink, nb_samples);
if (!out_buf)
return AVERROR(ENOMEM);
av_frame_copy_props(out_buf, buf);
}
if ((!s->type && (cur_sample + nb_samples < s->start_sample)) ||
( s->type && (s->start_sample + s->nb_samples < cur_sample))) {
av_samples_set_silence(out_buf->extended_data, 0, nb_samples,
av_frame_get_channels(out_buf), out_buf->format);
} else {
int64_t start;
if (!s->type)
start = cur_sample - s->start_sample;
else
start = s->start_sample + s->nb_samples - cur_sample;
s->fade_samples(out_buf->extended_data, buf->extended_data,
nb_samples, av_frame_get_channels(buf),
s->type ? -1 : 1, start,
s->nb_samples, s->curve);
}
if (buf != out_buf)
av_frame_free(&buf);
return ff_filter_frame(outlink, out_buf);
}
static const AVFilterPad avfilter_af_afade_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
.config_props = config_input,
},
{ NULL }
};
static const AVFilterPad avfilter_af_afade_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_afade = {
.name = "afade",
.description = NULL_IF_CONFIG_SMALL("Fade in/out input audio."),
.query_formats = query_formats,
.priv_size = sizeof(AudioFadeContext),
.init = init,
.inputs = avfilter_af_afade_inputs,
.outputs = avfilter_af_afade_outputs,
.priv_class = &afade_class,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
};
+147
View File
@@ -0,0 +1,147 @@
/*
* Copyright (c) 2011 Mina Nagy Zaki
*
* 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
* format audio filter
*/
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
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
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption aformat_options[] = {
{ "sample_fmts", "A comma-separated list of sample formats.", OFFSET(formats_str), AV_OPT_TYPE_STRING, .flags = A|F },
{ "sample_rates", "A comma-separated list of sample rates.", OFFSET(sample_rates_str), AV_OPT_TYPE_STRING, .flags = A|F },
{ "channel_layouts", "A comma-separated list of channel layouts.", OFFSET(channel_layouts_str), AV_OPT_TYPE_STRING, .flags = A|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(aformat);
#define PARSE_FORMATS(str, type, list, add_to_list, get_fmt, none, desc) \
do { \
char *next, *cur = str, sep; \
\
if (str && strchr(str, ',')) { \
av_log(ctx, AV_LOG_WARNING, "This syntax is deprecated, use '|' to "\
"separate %s.\n", desc); \
sep = ','; \
} else \
sep = '|'; \
\
while (cur) { \
type fmt; \
next = strchr(cur, sep); \
if (next) \
*next++ = 0; \
\
if ((fmt = get_fmt(cur)) == none) { \
av_log(ctx, AV_LOG_ERROR, "Error parsing " desc ": %s.\n", cur);\
return AVERROR(EINVAL); \
} \
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)
{
AFormatContext *s = ctx->priv;
PARSE_FORMATS(s->formats_str, enum AVSampleFormat, s->formats,
ff_add_format, av_get_sample_fmt, AV_SAMPLE_FMT_NONE, "sample format");
PARSE_FORMATS(s->sample_rates_str, int, s->sample_rates, ff_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");
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
AFormatContext *s = ctx->priv;
ff_set_common_formats(ctx, s->formats ? s->formats :
ff_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_counts());
return 0;
}
static const AVFilterPad avfilter_af_aformat_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
static const AVFilterPad avfilter_af_aformat_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO
},
{ NULL }
};
AVFilter avfilter_af_aformat = {
.name = "aformat",
.description = NULL_IF_CONFIG_SMALL("Convert the input audio to one of the specified formats."),
.init = init,
.query_formats = query_formats,
.priv_size = sizeof(AFormatContext),
.priv_class = &aformat_class,
.inputs = avfilter_af_aformat_inputs,
.outputs = avfilter_af_aformat_outputs,
};
+350
View File
@@ -0,0 +1,350 @@
/*
* Copyright (c) 2011 Nicolas George <[email protected]>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Audio merging filter
*/
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h" // only for SWR_CH_MAX
#include "avfilter.h"
#include "audio.h"
#include "bufferqueue.h"
#include "internal.h"
typedef struct {
const AVClass *class;
int nb_inputs;
int route[SWR_CH_MAX]; /**< channels routing, see copy_samples */
int bps;
struct amerge_input {
struct FFBufQueue queue;
int nb_ch; /**< number of channels for the input */
int nb_samples;
int pos;
} *in;
} AMergeContext;
#define OFFSET(x) offsetof(AMergeContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption amerge_options[] = {
{ "inputs", "specify the number of inputs", OFFSET(nb_inputs),
AV_OPT_TYPE_INT, { .i64 = 2 }, 2, SWR_CH_MAX, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(amerge);
static av_cold void uninit(AVFilterContext *ctx)
{
AMergeContext *am = ctx->priv;
int i;
for (i = 0; i < am->nb_inputs; i++) {
if (am->in)
ff_bufqueue_discard_all(&am->in[i].queue);
if (ctx->input_pads)
av_freep(&ctx->input_pads[i].name);
}
av_freep(&am->in);
}
static int query_formats(AVFilterContext *ctx)
{
AMergeContext *am = ctx->priv;
int64_t inlayout[SWR_CH_MAX], outlayout = 0;
AVFilterFormats *formats;
AVFilterChannelLayouts *layouts;
int i, overlap = 0, nb_ch = 0;
for (i = 0; i < am->nb_inputs; i++) {
if (!ctx->inputs[i]->in_channel_layouts ||
!ctx->inputs[i]->in_channel_layouts->nb_channel_layouts) {
av_log(ctx, AV_LOG_WARNING,
"No channel layout for input %d\n", i + 1);
return AVERROR(EAGAIN);
}
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);
}
am->in[i].nb_ch = av_get_channel_layout_nb_channels(inlayout[i]);
if (outlayout & inlayout[i])
overlap++;
outlayout |= inlayout[i];
nb_ch += am->in[i].nb_ch;
}
if (nb_ch > SWR_CH_MAX) {
av_log(ctx, AV_LOG_ERROR, "Too many channels (max %d)\n", SWR_CH_MAX);
return AVERROR(EINVAL);
}
if (overlap) {
av_log(ctx, AV_LOG_WARNING,
"Input channel layouts overlap: "
"output layout will be determined by the number of distinct input channels\n");
for (i = 0; i < nb_ch; i++)
am->route[i] = i;
outlayout = av_get_default_channel_layout(nb_ch);
if (!outlayout)
outlayout = ((int64_t)1 << nb_ch) - 1;
} else {
int *route[SWR_CH_MAX];
int c, out_ch_number = 0;
route[0] = am->route;
for (i = 1; i < am->nb_inputs; i++)
route[i] = route[i - 1] + am->in[i - 1].nb_ch;
for (c = 0; c < 64; c++)
for (i = 0; i < am->nb_inputs; i++)
if ((inlayout[i] >> c) & 1)
*(route[i]++) = out_ch_number++;
}
formats = ff_make_format_list(ff_packed_sample_fmts_array);
ff_set_common_formats(ctx, formats);
for (i = 0; i < am->nb_inputs; i++) {
layouts = NULL;
ff_add_channel_layout(&layouts, inlayout[i]);
ff_channel_layouts_ref(layouts, &ctx->inputs[i]->out_channel_layouts);
}
layouts = NULL;
ff_add_channel_layout(&layouts, outlayout);
ff_channel_layouts_ref(layouts, &ctx->outputs[0]->in_channel_layouts);
ff_set_common_samplerates(ctx, ff_all_samplerates());
return 0;
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AMergeContext *am = ctx->priv;
AVBPrint bp;
int i;
for (i = 1; i < am->nb_inputs; i++) {
if (ctx->inputs[i]->sample_rate != ctx->inputs[0]->sample_rate) {
av_log(ctx, AV_LOG_ERROR,
"Inputs must have the same sample rate "
"%d for in%d vs %d\n",
ctx->inputs[i]->sample_rate, i, ctx->inputs[0]->sample_rate);
return AVERROR(EINVAL);
}
}
am->bps = av_get_bytes_per_sample(ctx->outputs[0]->format);
outlink->sample_rate = ctx->inputs[0]->sample_rate;
outlink->time_base = ctx->inputs[0]->time_base;
av_bprint_init(&bp, 0, 1);
for (i = 0; i < am->nb_inputs; i++) {
av_bprintf(&bp, "%sin%d:", i ? " + " : "", i);
av_bprint_channel_layout(&bp, -1, ctx->inputs[i]->channel_layout);
}
av_bprintf(&bp, " -> out:");
av_bprint_channel_layout(&bp, -1, ctx->outputs[0]->channel_layout);
av_log(ctx, AV_LOG_VERBOSE, "%s\n", bp.str);
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AMergeContext *am = ctx->priv;
int i, ret;
for (i = 0; i < am->nb_inputs; i++)
if (!am->in[i].nb_samples)
if ((ret = ff_request_frame(ctx->inputs[i])) < 0)
return ret;
return 0;
}
/**
* Copy samples from several input streams to one output stream.
* @param nb_inputs number of inputs
* @param in inputs; used only for the nb_ch field;
* @param route routing values;
* input channel i goes to output channel route[i];
* i < in[0].nb_ch are the channels from the first output;
* i >= in[0].nb_ch are the channels from the second output
* @param ins pointer to the samples of each inputs, in packed format;
* will be left at the end of the copied samples
* @param outs pointer to the samples of the output, in packet format;
* must point to a buffer big enough;
* will be left at the end of the copied samples
* @param ns number of samples to copy
* @param bps bytes per sample
*/
static inline void copy_samples(int nb_inputs, struct amerge_input in[],
int *route, uint8_t *ins[],
uint8_t **outs, int ns, int bps)
{
int *route_cur;
int i, c, nb_ch = 0;
for (i = 0; i < nb_inputs; i++)
nb_ch += in[i].nb_ch;
while (ns--) {
route_cur = route;
for (i = 0; i < nb_inputs; i++) {
for (c = 0; c < in[i].nb_ch; c++) {
memcpy((*outs) + bps * *(route_cur++), ins[i], bps);
ins[i] += bps;
}
}
*outs += nb_ch * bps;
}
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
AVFilterContext *ctx = inlink->dst;
AMergeContext *am = ctx->priv;
AVFilterLink *const outlink = ctx->outputs[0];
int input_number;
int nb_samples, ns, i;
AVFrame *outbuf, *inbuf[SWR_CH_MAX];
uint8_t *ins[SWR_CH_MAX], *outs;
for (input_number = 0; input_number < am->nb_inputs; input_number++)
if (inlink == ctx->inputs[input_number])
break;
av_assert1(input_number < am->nb_inputs);
if (ff_bufqueue_is_full(&am->in[input_number].queue)) {
av_frame_free(&insamples);
return AVERROR(ENOMEM);
}
ff_bufqueue_add(ctx, &am->in[input_number].queue, av_frame_clone(insamples));
am->in[input_number].nb_samples += insamples->nb_samples;
av_frame_free(&insamples);
nb_samples = am->in[0].nb_samples;
for (i = 1; i < am->nb_inputs; i++)
nb_samples = FFMIN(nb_samples, am->in[i].nb_samples);
if (!nb_samples)
return 0;
outbuf = ff_get_audio_buffer(ctx->outputs[0], nb_samples);
if (!outbuf)
return AVERROR(ENOMEM);
outs = outbuf->data[0];
for (i = 0; i < am->nb_inputs; i++) {
inbuf[i] = ff_bufqueue_peek(&am->in[i].queue, 0);
ins[i] = inbuf[i]->data[0] +
am->in[i].pos * am->in[i].nb_ch * am->bps;
}
av_frame_copy_props(outbuf, inbuf[0]);
outbuf->pts = inbuf[0]->pts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE :
inbuf[0]->pts +
av_rescale_q(am->in[0].pos,
(AVRational){ 1, ctx->inputs[0]->sample_rate },
ctx->outputs[0]->time_base);
outbuf->nb_samples = nb_samples;
outbuf->channel_layout = outlink->channel_layout;
av_frame_set_channels(outbuf, outlink->channels);
while (nb_samples) {
ns = nb_samples;
for (i = 0; i < am->nb_inputs; i++)
ns = FFMIN(ns, inbuf[i]->nb_samples - am->in[i].pos);
/* Unroll the most common sample formats: speed +~350% for the loop,
+~13% overall (including two common decoders) */
switch (am->bps) {
case 1:
copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 1);
break;
case 2:
copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 2);
break;
case 4:
copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, 4);
break;
default:
copy_samples(am->nb_inputs, am->in, am->route, ins, &outs, ns, am->bps);
break;
}
nb_samples -= ns;
for (i = 0; i < am->nb_inputs; i++) {
am->in[i].nb_samples -= ns;
am->in[i].pos += ns;
if (am->in[i].pos == inbuf[i]->nb_samples) {
am->in[i].pos = 0;
av_frame_free(&inbuf[i]);
ff_bufqueue_get(&am->in[i].queue);
inbuf[i] = ff_bufqueue_peek(&am->in[i].queue, 0);
ins[i] = inbuf[i] ? inbuf[i]->data[0] : NULL;
}
}
}
return ff_filter_frame(ctx->outputs[0], outbuf);
}
static av_cold int init(AVFilterContext *ctx)
{
AMergeContext *am = ctx->priv;
int i;
am->in = av_calloc(am->nb_inputs, sizeof(*am->in));
if (!am->in)
return AVERROR(ENOMEM);
for (i = 0; i < am->nb_inputs; i++) {
char *name = av_asprintf("in%d", i);
AVFilterPad pad = {
.name = name,
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
};
if (!name)
return AVERROR(ENOMEM);
ff_insert_inpad(ctx, i, &pad);
}
return 0;
}
static const AVFilterPad amerge_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_af_amerge = {
.name = "amerge",
.description = NULL_IF_CONFIG_SMALL("Merge two or more audio streams into "
"a single multi-channel stream."),
.priv_size = sizeof(AMergeContext),
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = NULL,
.outputs = amerge_outputs,
.priv_class = &amerge_class,
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
};
+560
View File
@@ -0,0 +1,560 @@
/*
* Audio Mix Filter
* Copyright (c) 2012 Justin Ruggles <[email protected]>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Audio Mix Filter
*
* Mixes audio from multiple sources into a single output. The channel layout,
* sample rate, and sample format will be the same for all inputs and the
* output.
*/
#include "libavutil/attributes.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/float_dsp.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"
#define INPUT_OFF 0 /**< input has reached EOF */
#define INPUT_ON 1 /**< input is active */
#define INPUT_INACTIVE 2 /**< input is on, but is currently inactive */
#define DURATION_LONGEST 0
#define DURATION_SHORTEST 1
#define DURATION_FIRST 2
typedef struct FrameInfo {
int nb_samples;
int64_t pts;
struct FrameInfo *next;
} FrameInfo;
/**
* Linked list used to store timestamps and frame sizes of all frames in the
* FIFO for the first input.
*
* This is needed to keep timestamps synchronized for the case where multiple
* input frames are pushed to the filter for processing before a frame is
* requested by the output link.
*/
typedef struct FrameList {
int nb_frames;
int nb_samples;
FrameInfo *list;
FrameInfo *end;
} FrameList;
static void frame_list_clear(FrameList *frame_list)
{
if (frame_list) {
while (frame_list->list) {
FrameInfo *info = frame_list->list;
frame_list->list = info->next;
av_free(info);
}
frame_list->nb_frames = 0;
frame_list->nb_samples = 0;
frame_list->end = NULL;
}
}
static int frame_list_next_frame_size(FrameList *frame_list)
{
if (!frame_list->list)
return 0;
return frame_list->list->nb_samples;
}
static int64_t frame_list_next_pts(FrameList *frame_list)
{
if (!frame_list->list)
return AV_NOPTS_VALUE;
return frame_list->list->pts;
}
static void frame_list_remove_samples(FrameList *frame_list, int nb_samples)
{
if (nb_samples >= frame_list->nb_samples) {
frame_list_clear(frame_list);
} else {
int samples = nb_samples;
while (samples > 0) {
FrameInfo *info = frame_list->list;
av_assert0(info != NULL);
if (info->nb_samples <= samples) {
samples -= info->nb_samples;
frame_list->list = info->next;
if (!frame_list->list)
frame_list->end = NULL;
frame_list->nb_frames--;
frame_list->nb_samples -= info->nb_samples;
av_free(info);
} else {
info->nb_samples -= samples;
info->pts += samples;
frame_list->nb_samples -= samples;
samples = 0;
}
}
}
}
static int frame_list_add_frame(FrameList *frame_list, int nb_samples, int64_t pts)
{
FrameInfo *info = av_malloc(sizeof(*info));
if (!info)
return AVERROR(ENOMEM);
info->nb_samples = nb_samples;
info->pts = pts;
info->next = NULL;
if (!frame_list->list) {
frame_list->list = info;
frame_list->end = info;
} else {
av_assert0(frame_list->end != NULL);
frame_list->end->next = info;
frame_list->end = info;
}
frame_list->nb_frames++;
frame_list->nb_samples += nb_samples;
return 0;
}
typedef struct MixContext {
const AVClass *class; /**< class for AVOptions */
AVFloatDSPContext fdsp;
int nb_inputs; /**< number of inputs */
int active_inputs; /**< number of input currently active */
int duration_mode; /**< mode for determining duration */
float dropout_transition; /**< transition time when an input drops out */
int nb_channels; /**< number of channels */
int sample_rate; /**< sample rate */
int planar;
AVAudioFifo **fifos; /**< audio fifo for each input */
uint8_t *input_state; /**< current state of each input */
float *input_scale; /**< mixing scale factor for each input */
float scale_norm; /**< normalization factor for all inputs */
int64_t next_pts; /**< calculated pts for next output frame */
FrameList *frame_list; /**< list of frame info for the first input */
} MixContext;
#define OFFSET(x) offsetof(MixContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption amix_options[] = {
{ "inputs", "Number of inputs.",
OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, 32, A|F },
{ "duration", "How to determine the end-of-stream.",
OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, A|F, "duration" },
{ "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, INT_MIN, INT_MAX, A|F, "duration" },
{ "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, INT_MIN, INT_MAX, A|F, "duration" },
{ "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, INT_MIN, INT_MAX, A|F, "duration" },
{ "dropout_transition", "Transition time, in seconds, for volume "
"renormalization when an input stream ends.",
OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(amix);
/**
* Update the scaling factors to apply to each input during mixing.
*
* This balances the full volume range between active inputs and handles
* volume transitions when EOF is encountered on an input but mixing continues
* with the remaining inputs.
*/
static void calculate_scales(MixContext *s, int nb_samples)
{
int i;
if (s->scale_norm > s->active_inputs) {
s->scale_norm -= nb_samples / (s->dropout_transition * s->sample_rate);
s->scale_norm = FFMAX(s->scale_norm, s->active_inputs);
}
for (i = 0; i < s->nb_inputs; i++) {
if (s->input_state[i] == INPUT_ON)
s->input_scale[i] = 1.0f / s->scale_norm;
else
s->input_scale[i] = 0.0f;
}
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
MixContext *s = ctx->priv;
int i;
char buf[64];
s->planar = av_sample_fmt_is_planar(outlink->format);
s->sample_rate = outlink->sample_rate;
outlink->time_base = (AVRational){ 1, outlink->sample_rate };
s->next_pts = AV_NOPTS_VALUE;
s->frame_list = av_mallocz(sizeof(*s->frame_list));
if (!s->frame_list)
return AVERROR(ENOMEM);
s->fifos = av_mallocz(s->nb_inputs * sizeof(*s->fifos));
if (!s->fifos)
return AVERROR(ENOMEM);
s->nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout);
for (i = 0; i < s->nb_inputs; i++) {
s->fifos[i] = av_audio_fifo_alloc(outlink->format, s->nb_channels, 1024);
if (!s->fifos[i])
return AVERROR(ENOMEM);
}
s->input_state = av_malloc(s->nb_inputs);
if (!s->input_state)
return AVERROR(ENOMEM);
memset(s->input_state, INPUT_ON, s->nb_inputs);
s->active_inputs = s->nb_inputs;
s->input_scale = av_mallocz(s->nb_inputs * sizeof(*s->input_scale));
if (!s->input_scale)
return AVERROR(ENOMEM);
s->scale_norm = s->active_inputs;
calculate_scales(s, 0);
av_get_channel_layout_string(buf, sizeof(buf), -1, outlink->channel_layout);
av_log(ctx, AV_LOG_VERBOSE,
"inputs:%d fmt:%s srate:%d cl:%s\n", s->nb_inputs,
av_get_sample_fmt_name(outlink->format), outlink->sample_rate, buf);
return 0;
}
/**
* Read samples from the input FIFOs, mix, and write to the output link.
*/
static int output_frame(AVFilterLink *outlink, int nb_samples)
{
AVFilterContext *ctx = outlink->src;
MixContext *s = ctx->priv;
AVFrame *out_buf, *in_buf;
int i;
calculate_scales(s, nb_samples);
out_buf = ff_get_audio_buffer(outlink, nb_samples);
if (!out_buf)
return AVERROR(ENOMEM);
in_buf = ff_get_audio_buffer(outlink, nb_samples);
if (!in_buf) {
av_frame_free(&out_buf);
return AVERROR(ENOMEM);
}
for (i = 0; i < s->nb_inputs; i++) {
if (s->input_state[i] == INPUT_ON) {
int planes, plane_size, p;
av_audio_fifo_read(s->fifos[i], (void **)in_buf->extended_data,
nb_samples);
planes = s->planar ? s->nb_channels : 1;
plane_size = nb_samples * (s->planar ? 1 : s->nb_channels);
plane_size = FFALIGN(plane_size, 16);
for (p = 0; p < planes; p++) {
s->fdsp.vector_fmac_scalar((float *)out_buf->extended_data[p],
(float *) in_buf->extended_data[p],
s->input_scale[i], plane_size);
}
}
}
av_frame_free(&in_buf);
out_buf->pts = s->next_pts;
if (s->next_pts != AV_NOPTS_VALUE)
s->next_pts += nb_samples;
return ff_filter_frame(outlink, out_buf);
}
/**
* Returns the smallest number of samples available in the input FIFOs other
* than that of the first input.
*/
static int get_available_samples(MixContext *s)
{
int i;
int available_samples = INT_MAX;
av_assert0(s->nb_inputs > 1);
for (i = 1; i < s->nb_inputs; i++) {
int nb_samples;
if (s->input_state[i] == INPUT_OFF)
continue;
nb_samples = av_audio_fifo_size(s->fifos[i]);
available_samples = FFMIN(available_samples, nb_samples);
}
if (available_samples == INT_MAX)
return 0;
return available_samples;
}
/**
* Requests a frame, if needed, from each input link other than the first.
*/
static int request_samples(AVFilterContext *ctx, int min_samples)
{
MixContext *s = ctx->priv;
int i, ret;
av_assert0(s->nb_inputs > 1);
for (i = 1; i < s->nb_inputs; i++) {
ret = 0;
if (s->input_state[i] == INPUT_OFF)
continue;
while (!ret && av_audio_fifo_size(s->fifos[i]) < min_samples)
ret = ff_request_frame(ctx->inputs[i]);
if (ret == AVERROR_EOF) {
if (av_audio_fifo_size(s->fifos[i]) == 0) {
s->input_state[i] = INPUT_OFF;
continue;
}
} else if (ret < 0)
return ret;
}
return 0;
}
/**
* Calculates the number of active inputs and determines EOF based on the
* duration option.
*
* @return 0 if mixing should continue, or AVERROR_EOF if mixing should stop.
*/
static int calc_active_inputs(MixContext *s)
{
int i;
int active_inputs = 0;
for (i = 0; i < s->nb_inputs; i++)
active_inputs += !!(s->input_state[i] != INPUT_OFF);
s->active_inputs = active_inputs;
if (!active_inputs ||
(s->duration_mode == DURATION_FIRST && s->input_state[0] == INPUT_OFF) ||
(s->duration_mode == DURATION_SHORTEST && active_inputs != s->nb_inputs))
return AVERROR_EOF;
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
MixContext *s = ctx->priv;
int ret;
int wanted_samples, available_samples;
ret = calc_active_inputs(s);
if (ret < 0)
return ret;
if (s->input_state[0] == INPUT_OFF) {
ret = request_samples(ctx, 1);
if (ret < 0)
return ret;
ret = calc_active_inputs(s);
if (ret < 0)
return ret;
available_samples = get_available_samples(s);
if (!available_samples)
return AVERROR(EAGAIN);
return output_frame(outlink, available_samples);
}
if (s->frame_list->nb_frames == 0) {
ret = ff_request_frame(ctx->inputs[0]);
if (ret == AVERROR_EOF) {
s->input_state[0] = INPUT_OFF;
if (s->nb_inputs == 1)
return AVERROR_EOF;
else
return AVERROR(EAGAIN);
} else if (ret < 0)
return ret;
}
av_assert0(s->frame_list->nb_frames > 0);
wanted_samples = frame_list_next_frame_size(s->frame_list);
if (s->active_inputs > 1) {
ret = request_samples(ctx, wanted_samples);
if (ret < 0)
return ret;
ret = calc_active_inputs(s);
if (ret < 0)
return ret;
}
if (s->active_inputs > 1) {
available_samples = get_available_samples(s);
if (!available_samples)
return AVERROR(EAGAIN);
available_samples = FFMIN(available_samples, wanted_samples);
} else {
available_samples = wanted_samples;
}
s->next_pts = frame_list_next_pts(s->frame_list);
frame_list_remove_samples(s->frame_list, available_samples);
return output_frame(outlink, available_samples);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
AVFilterContext *ctx = inlink->dst;
MixContext *s = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
int i, ret = 0;
for (i = 0; i < ctx->nb_inputs; i++)
if (ctx->inputs[i] == inlink)
break;
if (i >= ctx->nb_inputs) {
av_log(ctx, AV_LOG_ERROR, "unknown input link\n");
ret = AVERROR(EINVAL);
goto fail;
}
if (i == 0) {
int64_t pts = av_rescale_q(buf->pts, inlink->time_base,
outlink->time_base);
ret = frame_list_add_frame(s->frame_list, buf->nb_samples, pts);
if (ret < 0)
goto fail;
}
ret = av_audio_fifo_write(s->fifos[i], (void **)buf->extended_data,
buf->nb_samples);
fail:
av_frame_free(&buf);
return ret;
}
static av_cold int init(AVFilterContext *ctx)
{
MixContext *s = ctx->priv;
int i;
for (i = 0; i < s->nb_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_frame = filter_frame;
ff_insert_inpad(ctx, i, &pad);
}
avpriv_float_dsp_init(&s->fdsp, 0);
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
int i;
MixContext *s = ctx->priv;
if (s->fifos) {
for (i = 0; i < s->nb_inputs; i++)
av_audio_fifo_free(s->fifos[i]);
av_freep(&s->fifos);
}
frame_list_clear(s->frame_list);
av_freep(&s->frame_list);
av_freep(&s->input_state);
av_freep(&s->input_scale);
for (i = 0; i < ctx->nb_inputs; i++)
av_freep(&ctx->input_pads[i].name);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
ff_add_format(&formats, AV_SAMPLE_FMT_FLT);
ff_add_format(&formats, AV_SAMPLE_FMT_FLTP);
ff_set_common_formats(ctx, formats);
ff_set_common_channel_layouts(ctx, ff_all_channel_layouts());
ff_set_common_samplerates(ctx, ff_all_samplerates());
return 0;
}
static const AVFilterPad avfilter_af_amix_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
.request_frame = request_frame
},
{ NULL }
};
AVFilter avfilter_af_amix = {
.name = "amix",
.description = NULL_IF_CONFIG_SMALL("Audio mixing."),
.priv_size = sizeof(MixContext),
.priv_class = &amix_class,
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = NULL,
.outputs = avfilter_af_amix_outputs,
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
};
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2010 S.N. Hemanth Meenakshisundaram <[email protected]>
* 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
* null audio filter
*/
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
#include "libavutil/internal.h"
static const AVFilterPad avfilter_af_anull_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
static const AVFilterPad avfilter_af_anull_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_anull = {
.name = "anull",
.description = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."),
.query_formats = ff_query_formats_all,
.inputs = avfilter_af_anull_inputs,
.outputs = avfilter_af_anull_outputs,
};
+156
View File
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2012 Michael Niedermayer
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/**
* @file
* audio pad filter.
*
* Based on af_aresample.c
*/
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "libavutil/avassert.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
typedef struct {
const AVClass *class;
int64_t next_pts;
int packet_size;
int64_t pad_len;
int64_t whole_len;
} APadContext;
#define OFFSET(x) offsetof(APadContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption apad_options[] = {
{ "packet_size", "set silence packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, { .i64 = 4096 }, 0, INT_MAX, A },
{ "pad_len", "number of samples of silence to add", OFFSET(pad_len), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, A },
{ "whole_len", "target number of samples in the audio stream", OFFSET(whole_len), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, A },
{ NULL }
};
AVFILTER_DEFINE_CLASS(apad);
static av_cold int init(AVFilterContext *ctx)
{
APadContext *apad = ctx->priv;
apad->next_pts = AV_NOPTS_VALUE;
if (apad->whole_len && apad->pad_len) {
av_log(ctx, AV_LOG_ERROR, "Both whole and pad length are set, this is not possible\n");
return AVERROR(EINVAL);
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
APadContext *apad = ctx->priv;
if (apad->whole_len)
apad->whole_len -= frame->nb_samples;
apad->next_pts = frame->pts + av_rescale_q(frame->nb_samples, (AVRational){1, inlink->sample_rate}, inlink->time_base);
return ff_filter_frame(ctx->outputs[0], frame);
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
APadContext *apad = ctx->priv;
int ret;
ret = ff_request_frame(ctx->inputs[0]);
if (ret == AVERROR_EOF && !ctx->is_disabled) {
int n_out = apad->packet_size;
AVFrame *outsamplesref;
if (apad->whole_len > 0) {
apad->pad_len = apad->whole_len;
apad->whole_len = 0;
}
if (apad->pad_len > 0) {
n_out = FFMIN(n_out, apad->pad_len);
apad->pad_len -= n_out;
}
if(!n_out)
return AVERROR_EOF;
outsamplesref = ff_get_audio_buffer(outlink, n_out);
if (!outsamplesref)
return AVERROR(ENOMEM);
av_assert0(outsamplesref->sample_rate == outlink->sample_rate);
av_assert0(outsamplesref->nb_samples == n_out);
av_samples_set_silence(outsamplesref->extended_data, 0,
n_out,
av_frame_get_channels(outsamplesref),
outsamplesref->format);
outsamplesref->pts = apad->next_pts;
if (apad->next_pts != AV_NOPTS_VALUE)
apad->next_pts += av_rescale_q(n_out, (AVRational){1, outlink->sample_rate}, outlink->time_base);
return ff_filter_frame(outlink, outsamplesref);
}
return ret;
}
static const AVFilterPad apad_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad apad_outputs[] = {
{
.name = "default",
.request_frame = request_frame,
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_apad = {
.name = "apad",
.description = NULL_IF_CONFIG_SMALL("Pad audio with silence."),
.init = init,
.priv_size = sizeof(APadContext),
.inputs = apad_inputs,
.outputs = apad_outputs,
.priv_class = &apad_class,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL,
};
+358
View File
@@ -0,0 +1,358 @@
/*
* Copyright (c) 2013 Paul B Mahol
*
* 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
* phaser audio filter
*/
#include "libavutil/avassert.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
enum WaveType {
WAVE_SIN,
WAVE_TRI,
WAVE_NB,
};
typedef struct AudioPhaserContext {
const AVClass *class;
double in_gain, out_gain;
double delay;
double decay;
double speed;
enum WaveType type;
int delay_buffer_length;
double *delay_buffer;
int modulation_buffer_length;
int32_t *modulation_buffer;
int delay_pos, modulation_pos;
void (*phaser)(struct AudioPhaserContext *p,
uint8_t * const *src, uint8_t **dst,
int nb_samples, int channels);
} AudioPhaserContext;
#define OFFSET(x) offsetof(AudioPhaserContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption aphaser_options[] = {
{ "in_gain", "set input gain", OFFSET(in_gain), AV_OPT_TYPE_DOUBLE, {.dbl=.4}, 0, 1, FLAGS },
{ "out_gain", "set output gain", OFFSET(out_gain), AV_OPT_TYPE_DOUBLE, {.dbl=.74}, 0, 1e9, FLAGS },
{ "delay", "set delay in milliseconds", OFFSET(delay), AV_OPT_TYPE_DOUBLE, {.dbl=3.}, 0, 5, FLAGS },
{ "decay", "set decay", OFFSET(decay), AV_OPT_TYPE_DOUBLE, {.dbl=.4}, 0, .99, FLAGS },
{ "speed", "set modulation speed", OFFSET(speed), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, .1, 2, FLAGS },
{ "type", "set modulation type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=WAVE_TRI}, 0, WAVE_NB-1, FLAGS, "type" },
{ "triangular", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, FLAGS, "type" },
{ "t", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, FLAGS, "type" },
{ "sinusoidal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, FLAGS, "type" },
{ "s", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, FLAGS, "type" },
{ NULL }
};
AVFILTER_DEFINE_CLASS(aphaser);
static av_cold int init(AVFilterContext *ctx)
{
AudioPhaserContext *p = ctx->priv;
if (p->in_gain > (1 - p->decay * p->decay))
av_log(ctx, AV_LOG_WARNING, "in_gain may cause clipping\n");
if (p->in_gain / (1 - p->decay) > 1 / p->out_gain)
av_log(ctx, AV_LOG_WARNING, "out_gain may cause clipping\n");
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats;
AVFilterChannelLayouts *layouts;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
static void generate_wave_table(enum WaveType wave_type, enum AVSampleFormat sample_fmt,
void *table, int table_size,
double min, double max, double phase)
{
uint32_t i, phase_offset = phase / M_PI / 2 * table_size + 0.5;
for (i = 0; i < table_size; i++) {
uint32_t point = (i + phase_offset) % table_size;
double d;
switch (wave_type) {
case WAVE_SIN:
d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2;
break;
case WAVE_TRI:
d = (double)point * 2 / table_size;
switch (4 * point / table_size) {
case 0: d = d + 0.5; break;
case 1:
case 2: d = 1.5 - d; break;
case 3: d = d - 1.5; break;
}
break;
default:
av_assert0(0);
}
d = d * (max - min) + min;
switch (sample_fmt) {
case AV_SAMPLE_FMT_FLT: {
float *fp = (float *)table;
*fp++ = (float)d;
table = fp;
continue; }
case AV_SAMPLE_FMT_DBL: {
double *dp = (double *)table;
*dp++ = d;
table = dp;
continue; }
}
d += d < 0 ? -0.5 : 0.5;
switch (sample_fmt) {
case AV_SAMPLE_FMT_S16: {
int16_t *sp = table;
*sp++ = (int16_t)d;
table = sp;
continue; }
case AV_SAMPLE_FMT_S32: {
int32_t *ip = table;
*ip++ = (int32_t)d;
table = ip;
continue; }
default:
av_assert0(0);
}
}
}
#define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a))
#define PHASER_PLANAR(name, type) \
static void phaser_## name ##p(AudioPhaserContext *p, \
uint8_t * const *src, uint8_t **dst, \
int nb_samples, int channels) \
{ \
int i, c, delay_pos, modulation_pos; \
\
av_assert0(channels > 0); \
for (c = 0; c < channels; c++) { \
type *s = (type *)src[c]; \
type *d = (type *)dst[c]; \
double *buffer = p->delay_buffer + \
c * p->delay_buffer_length; \
\
delay_pos = p->delay_pos; \
modulation_pos = p->modulation_pos; \
\
for (i = 0; i < nb_samples; i++, s++, d++) { \
double v = *s * p->in_gain + buffer[ \
MOD(delay_pos + p->modulation_buffer[ \
modulation_pos], \
p->delay_buffer_length)] * p->decay; \
\
modulation_pos = MOD(modulation_pos + 1, \
p->modulation_buffer_length); \
delay_pos = MOD(delay_pos + 1, p->delay_buffer_length); \
buffer[delay_pos] = v; \
\
*d = v * p->out_gain; \
} \
} \
\
p->delay_pos = delay_pos; \
p->modulation_pos = modulation_pos; \
}
#define PHASER(name, type) \
static void phaser_## name (AudioPhaserContext *p, \
uint8_t * const *src, uint8_t **dst, \
int nb_samples, int channels) \
{ \
int i, c, delay_pos, modulation_pos; \
type *s = (type *)src[0]; \
type *d = (type *)dst[0]; \
double *buffer = p->delay_buffer; \
\
delay_pos = p->delay_pos; \
modulation_pos = p->modulation_pos; \
\
for (i = 0; i < nb_samples; i++) { \
int pos = MOD(delay_pos + p->modulation_buffer[modulation_pos], \
p->delay_buffer_length) * channels; \
int npos; \
\
delay_pos = MOD(delay_pos + 1, p->delay_buffer_length); \
npos = delay_pos * channels; \
for (c = 0; c < channels; c++, s++, d++) { \
double v = *s * p->in_gain + buffer[pos + c] * p->decay; \
\
buffer[npos + c] = v; \
\
*d = v * p->out_gain; \
} \
\
modulation_pos = MOD(modulation_pos + 1, \
p->modulation_buffer_length); \
} \
\
p->delay_pos = delay_pos; \
p->modulation_pos = modulation_pos; \
}
PHASER_PLANAR(dbl, double)
PHASER_PLANAR(flt, float)
PHASER_PLANAR(s16, int16_t)
PHASER_PLANAR(s32, int32_t)
PHASER(dbl, double)
PHASER(flt, float)
PHASER(s16, int16_t)
PHASER(s32, int32_t)
static int config_output(AVFilterLink *outlink)
{
AudioPhaserContext *p = outlink->src->priv;
AVFilterLink *inlink = outlink->src->inputs[0];
p->delay_buffer_length = p->delay * 0.001 * inlink->sample_rate + 0.5;
p->delay_buffer = av_calloc(p->delay_buffer_length, sizeof(*p->delay_buffer) * inlink->channels);
p->modulation_buffer_length = inlink->sample_rate / p->speed + 0.5;
p->modulation_buffer = av_malloc(p->modulation_buffer_length * sizeof(*p->modulation_buffer));
if (!p->modulation_buffer || !p->delay_buffer)
return AVERROR(ENOMEM);
generate_wave_table(p->type, AV_SAMPLE_FMT_S32,
p->modulation_buffer, p->modulation_buffer_length,
1., p->delay_buffer_length, M_PI / 2.0);
p->delay_pos = p->modulation_pos = 0;
switch (inlink->format) {
case AV_SAMPLE_FMT_DBL: p->phaser = phaser_dbl; break;
case AV_SAMPLE_FMT_DBLP: p->phaser = phaser_dblp; break;
case AV_SAMPLE_FMT_FLT: p->phaser = phaser_flt; break;
case AV_SAMPLE_FMT_FLTP: p->phaser = phaser_fltp; break;
case AV_SAMPLE_FMT_S16: p->phaser = phaser_s16; break;
case AV_SAMPLE_FMT_S16P: p->phaser = phaser_s16p; break;
case AV_SAMPLE_FMT_S32: p->phaser = phaser_s32; break;
case AV_SAMPLE_FMT_S32P: p->phaser = phaser_s32p; break;
default: av_assert0(0);
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *inbuf)
{
AudioPhaserContext *p = inlink->dst->priv;
AVFilterLink *outlink = inlink->dst->outputs[0];
AVFrame *outbuf;
if (av_frame_is_writable(inbuf)) {
outbuf = inbuf;
} else {
outbuf = ff_get_audio_buffer(inlink, inbuf->nb_samples);
if (!outbuf)
return AVERROR(ENOMEM);
av_frame_copy_props(outbuf, inbuf);
}
p->phaser(p, inbuf->extended_data, outbuf->extended_data,
outbuf->nb_samples, av_frame_get_channels(outbuf));
if (inbuf != outbuf)
av_frame_free(&inbuf);
return ff_filter_frame(outlink, outbuf);
}
static av_cold void uninit(AVFilterContext *ctx)
{
AudioPhaserContext *p = ctx->priv;
av_freep(&p->delay_buffer);
av_freep(&p->modulation_buffer);
}
static const AVFilterPad aphaser_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad aphaser_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
},
{ NULL }
};
AVFilter avfilter_af_aphaser = {
.name = "aphaser",
.description = NULL_IF_CONFIG_SMALL("Add a phasing effect to the audio."),
.query_formats = query_formats,
.priv_size = sizeof(AudioPhaserContext),
.init = init,
.uninit = uninit,
.inputs = aphaser_inputs,
.outputs = aphaser_outputs,
.priv_class = &aphaser_class,
};
+309
View File
@@ -0,0 +1,309 @@
/*
* Copyright (c) 2011 Stefano Sabatini
* Copyright (c) 2011 Mina Nagy Zaki
*
* 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
* resampling audio filter
*/
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "libavutil/avassert.h"
#include "libswresample/swresample.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
typedef struct {
const AVClass *class;
int sample_rate_arg;
double ratio;
struct SwrContext *swr;
int64_t next_pts;
int req_fullfilled;
} AResampleContext;
static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts)
{
AResampleContext *aresample = ctx->priv;
int ret = 0;
aresample->next_pts = AV_NOPTS_VALUE;
aresample->swr = swr_alloc();
if (!aresample->swr) {
ret = AVERROR(ENOMEM);
goto end;
}
if (opts) {
AVDictionaryEntry *e = NULL;
while ((e = av_dict_get(*opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
if ((ret = av_opt_set(aresample->swr, e->key, e->value, 0)) < 0)
goto end;
}
av_dict_free(opts);
}
if (aresample->sample_rate_arg > 0)
av_opt_set_int(aresample->swr, "osr", aresample->sample_rate_arg, 0);
end:
return ret;
}
static av_cold void uninit(AVFilterContext *ctx)
{
AResampleContext *aresample = ctx->priv;
swr_free(&aresample->swr);
}
static int query_formats(AVFilterContext *ctx)
{
AResampleContext *aresample = ctx->priv;
int out_rate = av_get_int(aresample->swr, "osr", NULL);
uint64_t out_layout = av_get_int(aresample->swr, "ocl", NULL);
enum AVSampleFormat out_format = av_get_int(aresample->swr, "osf", NULL);
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
AVFilterFormats *in_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO);
AVFilterFormats *out_formats;
AVFilterFormats *in_samplerates = ff_all_samplerates();
AVFilterFormats *out_samplerates;
AVFilterChannelLayouts *in_layouts = ff_all_channel_counts();
AVFilterChannelLayouts *out_layouts;
ff_formats_ref (in_formats, &inlink->out_formats);
ff_formats_ref (in_samplerates, &inlink->out_samplerates);
ff_channel_layouts_ref(in_layouts, &inlink->out_channel_layouts);
if(out_rate > 0) {
out_samplerates = ff_make_format_list((int[]){ out_rate, -1 });
} else {
out_samplerates = ff_all_samplerates();
}
ff_formats_ref(out_samplerates, &outlink->in_samplerates);
if(out_format != AV_SAMPLE_FMT_NONE) {
out_formats = ff_make_format_list((int[]){ out_format, -1 });
} else
out_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO);
ff_formats_ref(out_formats, &outlink->in_formats);
if(out_layout) {
out_layouts = avfilter_make_format64_list((int64_t[]){ out_layout, -1 });
} else
out_layouts = ff_all_channel_counts();
ff_channel_layouts_ref(out_layouts, &outlink->in_channel_layouts);
return 0;
}
static int config_output(AVFilterLink *outlink)
{
int ret;
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink = ctx->inputs[0];
AResampleContext *aresample = ctx->priv;
int out_rate;
uint64_t out_layout;
enum AVSampleFormat out_format;
char inchl_buf[128], outchl_buf[128];
aresample->swr = swr_alloc_set_opts(aresample->swr,
outlink->channel_layout, outlink->format, outlink->sample_rate,
inlink->channel_layout, inlink->format, inlink->sample_rate,
0, ctx);
if (!aresample->swr)
return AVERROR(ENOMEM);
if (!inlink->channel_layout)
av_opt_set_int(aresample->swr, "ich", inlink->channels, 0);
if (!outlink->channel_layout)
av_opt_set_int(aresample->swr, "och", outlink->channels, 0);
ret = swr_init(aresample->swr);
if (ret < 0)
return ret;
out_rate = av_get_int(aresample->swr, "osr", NULL);
out_layout = av_get_int(aresample->swr, "ocl", NULL);
out_format = av_get_int(aresample->swr, "osf", NULL);
outlink->time_base = (AVRational) {1, out_rate};
av_assert0(outlink->sample_rate == out_rate);
av_assert0(outlink->channel_layout == out_layout || !outlink->channel_layout);
av_assert0(outlink->format == out_format);
aresample->ratio = (double)outlink->sample_rate / inlink->sample_rate;
av_get_channel_layout_string(inchl_buf, sizeof(inchl_buf), inlink ->channels, inlink ->channel_layout);
av_get_channel_layout_string(outchl_buf, sizeof(outchl_buf), outlink->channels, outlink->channel_layout);
av_log(ctx, AV_LOG_VERBOSE, "ch:%d chl:%s fmt:%s r:%dHz -> ch:%d chl:%s fmt:%s r:%dHz\n",
inlink ->channels, inchl_buf, av_get_sample_fmt_name(inlink->format), inlink->sample_rate,
outlink->channels, outchl_buf, av_get_sample_fmt_name(outlink->format), outlink->sample_rate);
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamplesref)
{
AResampleContext *aresample = inlink->dst->priv;
const int n_in = insamplesref->nb_samples;
int n_out = n_in * aresample->ratio * 2 + 256;
AVFilterLink *const outlink = inlink->dst->outputs[0];
AVFrame *outsamplesref = ff_get_audio_buffer(outlink, n_out);
int ret;
if(!outsamplesref)
return AVERROR(ENOMEM);
av_frame_copy_props(outsamplesref, insamplesref);
outsamplesref->format = outlink->format;
av_frame_set_channels(outsamplesref, outlink->channels);
outsamplesref->channel_layout = outlink->channel_layout;
outsamplesref->sample_rate = outlink->sample_rate;
if(insamplesref->pts != AV_NOPTS_VALUE) {
int64_t inpts = av_rescale(insamplesref->pts, inlink->time_base.num * (int64_t)outlink->sample_rate * inlink->sample_rate, inlink->time_base.den);
int64_t outpts= swr_next_pts(aresample->swr, inpts);
aresample->next_pts =
outsamplesref->pts = ROUNDED_DIV(outpts, inlink->sample_rate);
} else {
outsamplesref->pts = AV_NOPTS_VALUE;
}
n_out = swr_convert(aresample->swr, outsamplesref->extended_data, n_out,
(void *)insamplesref->extended_data, n_in);
if (n_out <= 0) {
av_frame_free(&outsamplesref);
av_frame_free(&insamplesref);
return 0;
}
outsamplesref->nb_samples = n_out;
ret = ff_filter_frame(outlink, outsamplesref);
aresample->req_fullfilled= 1;
av_frame_free(&insamplesref);
return ret;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AResampleContext *aresample = ctx->priv;
AVFilterLink *const inlink = outlink->src->inputs[0];
int ret;
aresample->req_fullfilled = 0;
do{
ret = ff_request_frame(ctx->inputs[0]);
}while(!aresample->req_fullfilled && ret>=0);
if (ret == AVERROR_EOF) {
AVFrame *outsamplesref;
int n_out = 4096;
outsamplesref = ff_get_audio_buffer(outlink, n_out);
if (!outsamplesref)
return AVERROR(ENOMEM);
n_out = swr_convert(aresample->swr, outsamplesref->extended_data, n_out, 0, 0);
if (n_out <= 0) {
av_frame_free(&outsamplesref);
return (n_out == 0) ? AVERROR_EOF : n_out;
}
outsamplesref->sample_rate = outlink->sample_rate;
outsamplesref->nb_samples = n_out;
#if 0
outsamplesref->pts = aresample->next_pts;
if(aresample->next_pts != AV_NOPTS_VALUE)
aresample->next_pts += av_rescale_q(n_out, (AVRational){1 ,outlink->sample_rate}, outlink->time_base);
#else
outsamplesref->pts = swr_next_pts(aresample->swr, INT64_MIN);
outsamplesref->pts = ROUNDED_DIV(outsamplesref->pts, inlink->sample_rate);
#endif
return ff_filter_frame(outlink, outsamplesref);
}
return ret;
}
static const AVClass *resample_child_class_next(const AVClass *prev)
{
return prev ? NULL : swr_get_class();
}
static void *resample_child_next(void *obj, void *prev)
{
AResampleContext *s = obj;
return prev ? NULL : s->swr;
}
#define OFFSET(x) offsetof(AResampleContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption options[] = {
{"sample_rate", NULL, OFFSET(sample_rate_arg), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
{NULL}
};
static const AVClass aresample_class = {
.class_name = "aresample",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
.child_class_next = resample_child_class_next,
.child_next = resample_child_next,
};
static const AVFilterPad aresample_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad aresample_outputs[] = {
{
.name = "default",
.config_props = config_output,
.request_frame = request_frame,
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_aresample = {
.name = "aresample",
.description = NULL_IF_CONFIG_SMALL("Resample audio data."),
.init_dict = init_dict,
.uninit = uninit,
.query_formats = query_formats,
.priv_size = sizeof(AResampleContext),
.priv_class = &aresample_class,
.inputs = aresample_inputs,
.outputs = aresample_outputs,
};
+196
View File
@@ -0,0 +1,196 @@
/*
* Copyright (c) 2012 Andrey Utkin
* Copyright (c) 2012 Stefano Sabatini
*
* 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
* Filter that changes number of samples on single output operation
*/
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
#include "formats.h"
typedef struct {
const AVClass *class;
int nb_out_samples; ///< how many samples to output
AVAudioFifo *fifo; ///< samples are queued here
int64_t next_out_pts;
int pad;
} ASNSContext;
#define OFFSET(x) offsetof(ASNSContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption asetnsamples_options[] = {
{ "nb_out_samples", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
{ "n", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
{ "pad", "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
{ "p", "pad last frame with zeros", OFFSET(pad), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(asetnsamples);
static av_cold int init(AVFilterContext *ctx)
{
ASNSContext *asns = ctx->priv;
asns->next_out_pts = AV_NOPTS_VALUE;
av_log(ctx, AV_LOG_VERBOSE, "nb_out_samples:%d pad:%d\n", asns->nb_out_samples, asns->pad);
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
ASNSContext *asns = ctx->priv;
av_audio_fifo_free(asns->fifo);
}
static int config_props_output(AVFilterLink *outlink)
{
ASNSContext *asns = outlink->src->priv;
asns->fifo = av_audio_fifo_alloc(outlink->format, outlink->channels, asns->nb_out_samples);
if (!asns->fifo)
return AVERROR(ENOMEM);
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
return 0;
}
static int push_samples(AVFilterLink *outlink)
{
ASNSContext *asns = outlink->src->priv;
AVFrame *outsamples = NULL;
int ret, nb_out_samples, nb_pad_samples;
if (asns->pad) {
nb_out_samples = av_audio_fifo_size(asns->fifo) ? asns->nb_out_samples : 0;
nb_pad_samples = nb_out_samples - FFMIN(nb_out_samples, av_audio_fifo_size(asns->fifo));
} else {
nb_out_samples = FFMIN(asns->nb_out_samples, av_audio_fifo_size(asns->fifo));
nb_pad_samples = 0;
}
if (!nb_out_samples)
return 0;
outsamples = ff_get_audio_buffer(outlink, nb_out_samples);
if (!outsamples)
return AVERROR(ENOMEM);
av_audio_fifo_read(asns->fifo,
(void **)outsamples->extended_data, nb_out_samples);
if (nb_pad_samples)
av_samples_set_silence(outsamples->extended_data, nb_out_samples - nb_pad_samples,
nb_pad_samples, outlink->channels,
outlink->format);
outsamples->nb_samples = nb_out_samples;
outsamples->channel_layout = outlink->channel_layout;
outsamples->sample_rate = outlink->sample_rate;
outsamples->pts = asns->next_out_pts;
if (asns->next_out_pts != AV_NOPTS_VALUE)
asns->next_out_pts += nb_out_samples;
ret = ff_filter_frame(outlink, outsamples);
if (ret < 0)
return ret;
return nb_out_samples;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
AVFilterContext *ctx = inlink->dst;
ASNSContext *asns = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
int ret;
int nb_samples = insamples->nb_samples;
if (av_audio_fifo_space(asns->fifo) < nb_samples) {
av_log(ctx, AV_LOG_DEBUG, "No space for %d samples, stretching audio fifo\n", nb_samples);
ret = av_audio_fifo_realloc(asns->fifo, av_audio_fifo_size(asns->fifo) + nb_samples);
if (ret < 0) {
av_log(ctx, AV_LOG_ERROR,
"Stretching audio fifo failed, discarded %d samples\n", nb_samples);
return -1;
}
}
av_audio_fifo_write(asns->fifo, (void **)insamples->extended_data, nb_samples);
if (asns->next_out_pts == AV_NOPTS_VALUE)
asns->next_out_pts = insamples->pts;
av_frame_free(&insamples);
while (av_audio_fifo_size(asns->fifo) >= asns->nb_out_samples)
push_samples(outlink);
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterLink *inlink = outlink->src->inputs[0];
int ret;
ret = ff_request_frame(inlink);
if (ret == AVERROR_EOF) {
ret = push_samples(outlink);
return ret < 0 ? ret : ret > 0 ? 0 : AVERROR_EOF;
}
return ret;
}
static const AVFilterPad asetnsamples_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad asetnsamples_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.request_frame = request_frame,
.config_props = config_props_output,
},
{ NULL }
};
AVFilter avfilter_af_asetnsamples = {
.name = "asetnsamples",
.description = NULL_IF_CONFIG_SMALL("Set the number of samples for each output audio frames."),
.priv_size = sizeof(ASNSContext),
.priv_class = &asetnsamples_class,
.init = init,
.uninit = uninit,
.inputs = asetnsamples_inputs,
.outputs = asetnsamples_outputs,
};
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2013 Nicolas George
*
* 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/opt.h"
#include "avfilter.h"
#include "internal.h"
typedef struct {
const AVClass *class;
int sample_rate;
int rescale_pts;
} ASetRateContext;
#define CONTEXT ASetRateContext
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
#define OPT_GENERIC(name, field, def, min, max, descr, type, deffield, ...) \
{ name, descr, offsetof(CONTEXT, field), AV_OPT_TYPE_ ## type, \
{ .deffield = def }, min, max, FLAGS, __VA_ARGS__ }
#define OPT_INT(name, field, def, min, max, descr, ...) \
OPT_GENERIC(name, field, def, min, max, descr, INT, i64, __VA_ARGS__)
static const AVOption asetrate_options[] = {
OPT_INT("sample_rate", sample_rate, 44100, 1, INT_MAX, "set the sample rate"),
OPT_INT("r", sample_rate, 44100, 1, INT_MAX, "set the sample rate"),
{NULL},
};
AVFILTER_DEFINE_CLASS(asetrate);
static av_cold int query_formats(AVFilterContext *ctx)
{
ASetRateContext *sr = ctx->priv;
int sample_rates[] = { sr->sample_rate, -1 };
ff_formats_ref(ff_make_format_list(sample_rates),
&ctx->outputs[0]->in_samplerates);
return 0;
}
static av_cold int config_props(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
ASetRateContext *sr = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
AVRational intb = ctx->inputs[0]->time_base;
int inrate = inlink->sample_rate;
if (intb.num == 1 && intb.den == inrate) {
outlink->time_base.num = 1;
outlink->time_base.den = outlink->sample_rate;
} else {
outlink->time_base = intb;
sr->rescale_pts = 1;
if (av_q2d(intb) > 1.0 / FFMAX(inrate, outlink->sample_rate))
av_log(ctx, AV_LOG_WARNING, "Time base is inaccurate\n");
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
ASetRateContext *sr = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
frame->sample_rate = outlink->sample_rate;
if (sr->rescale_pts)
frame->pts = av_rescale(frame->pts, inlink->sample_rate,
outlink->sample_rate);
return ff_filter_frame(outlink, frame);
}
static const AVFilterPad asetrate_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad asetrate_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_props,
},
{ NULL }
};
AVFilter avfilter_af_asetrate = {
.name = "asetrate",
.description = NULL_IF_CONFIG_SMALL("Change the sample rate without "
"altering the data."),
.query_formats = query_formats,
.priv_size = sizeof(ASetRateContext),
.inputs = asetrate_inputs,
.outputs = asetrate_outputs,
.priv_class = &asetrate_class,
};
+126
View File
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2011 Stefano Sabatini
*
* 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
* filter for showing textual audio frame information
*/
#include <inttypes.h>
#include <stddef.h>
#include "libavutil/adler32.h"
#include "libavutil/attributes.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/mem.h"
#include "libavutil/timestamp.h"
#include "libavutil/samplefmt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct AShowInfoContext {
/**
* Scratch space for individual plane checksums for planar audio
*/
uint32_t *plane_checksums;
} AShowInfoContext;
static av_cold void uninit(AVFilterContext *ctx)
{
AShowInfoContext *s = ctx->priv;
av_freep(&s->plane_checksums);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
AVFilterContext *ctx = inlink->dst;
AShowInfoContext *s = ctx->priv;
char chlayout_str[128];
uint32_t checksum = 0;
int channels = inlink->channels;
int planar = av_sample_fmt_is_planar(buf->format);
int block_align = av_get_bytes_per_sample(buf->format) * (planar ? 1 : channels);
int data_size = buf->nb_samples * block_align;
int planes = planar ? channels : 1;
int i;
void *tmp_ptr = av_realloc(s->plane_checksums, channels * sizeof(*s->plane_checksums));
if (!tmp_ptr)
return AVERROR(ENOMEM);
s->plane_checksums = tmp_ptr;
for (i = 0; i < planes; i++) {
uint8_t *data = buf->extended_data[i];
s->plane_checksums[i] = av_adler32_update(0, data, data_size);
checksum = i ? av_adler32_update(checksum, data, data_size) :
s->plane_checksums[0];
}
av_get_channel_layout_string(chlayout_str, sizeof(chlayout_str), -1,
buf->channel_layout);
av_log(ctx, AV_LOG_INFO,
"n:%"PRId64" pts:%s pts_time:%s pos:%"PRId64" "
"fmt:%s channels:%d chlayout:%s rate:%d nb_samples:%d "
"checksum:%08X ",
inlink->frame_count,
av_ts2str(buf->pts), av_ts2timestr(buf->pts, &inlink->time_base),
av_frame_get_pkt_pos(buf),
av_get_sample_fmt_name(buf->format), av_frame_get_channels(buf), chlayout_str,
buf->sample_rate, buf->nb_samples,
checksum);
av_log(ctx, AV_LOG_INFO, "plane_checksums: [ ");
for (i = 0; i < planes; i++)
av_log(ctx, AV_LOG_INFO, "%08X ", s->plane_checksums[i]);
av_log(ctx, AV_LOG_INFO, "]\n");
return ff_filter_frame(inlink->dst->outputs[0], buf);
}
static const AVFilterPad inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_ashowinfo = {
.name = "ashowinfo",
.description = NULL_IF_CONFIG_SMALL("Show textual information for each audio frame."),
.priv_size = sizeof(AShowInfoContext),
.uninit = uninit,
.inputs = inputs,
.outputs = outputs,
};
+274
View File
@@ -0,0 +1,274 @@
/*
* Copyright (c) 2009 Rob Sykes <[email protected]>
* Copyright (c) 2013 Paul B Mahol
*
* 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 <float.h>
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct ChannelStats {
double last;
double sigma_x, sigma_x2;
double avg_sigma_x2, min_sigma_x2, max_sigma_x2;
double min, max;
double min_run, max_run;
double min_runs, max_runs;
uint64_t min_count, max_count;
uint64_t nb_samples;
} ChannelStats;
typedef struct {
const AVClass *class;
ChannelStats *chstats;
int nb_channels;
uint64_t tc_samples;
double time_constant;
double mult;
} AudioStatsContext;
#define OFFSET(x) offsetof(AudioStatsContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption astats_options[] = {
{ "length", "set the window length", OFFSET(time_constant), AV_OPT_TYPE_DOUBLE, {.dbl=.05}, .01, 10, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(astats);
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats;
AVFilterChannelLayouts *layouts;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
static int config_output(AVFilterLink *outlink)
{
AudioStatsContext *s = outlink->src->priv;
int c;
s->chstats = av_calloc(sizeof(*s->chstats), outlink->channels);
if (!s->chstats)
return AVERROR(ENOMEM);
s->nb_channels = outlink->channels;
s->mult = exp((-1 / s->time_constant / outlink->sample_rate));
s->tc_samples = 5 * s->time_constant * outlink->sample_rate + .5;
for (c = 0; c < s->nb_channels; c++) {
ChannelStats *p = &s->chstats[c];
p->min = p->min_sigma_x2 = DBL_MAX;
p->max = p->max_sigma_x2 = DBL_MIN;
}
return 0;
}
static inline void update_stat(AudioStatsContext *s, ChannelStats *p, double d)
{
if (d < p->min) {
p->min = d;
p->min_run = 1;
p->min_runs = 0;
p->min_count = 1;
} else if (d == p->min) {
p->min_count++;
p->min_run = d == p->last ? p->min_run + 1 : 1;
} else if (p->last == p->min) {
p->min_runs += p->min_run * p->min_run;
}
if (d > p->max) {
p->max = d;
p->max_run = 1;
p->max_runs = 0;
p->max_count = 1;
} else if (d == p->max) {
p->max_count++;
p->max_run = d == p->last ? p->max_run + 1 : 1;
} else if (p->last == p->max) {
p->max_runs += p->max_run * p->max_run;
}
p->sigma_x += d;
p->sigma_x2 += d * d;
p->avg_sigma_x2 = p->avg_sigma_x2 * s->mult + (1.0 - s->mult) * d * d;
p->last = d;
if (p->nb_samples >= s->tc_samples) {
p->max_sigma_x2 = FFMAX(p->max_sigma_x2, p->avg_sigma_x2);
p->min_sigma_x2 = FFMIN(p->min_sigma_x2, p->avg_sigma_x2);
}
p->nb_samples++;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
AudioStatsContext *s = inlink->dst->priv;
const int channels = s->nb_channels;
const double *src;
int i, c;
switch (inlink->format) {
case AV_SAMPLE_FMT_DBLP:
for (c = 0; c < channels; c++) {
ChannelStats *p = &s->chstats[c];
src = (const double *)buf->extended_data[c];
for (i = 0; i < buf->nb_samples; i++, src++)
update_stat(s, p, *src);
}
break;
case AV_SAMPLE_FMT_DBL:
src = (const double *)buf->extended_data[0];
for (i = 0; i < buf->nb_samples; i++) {
for (c = 0; c < channels; c++, src++)
update_stat(s, &s->chstats[c], *src);
}
break;
}
return ff_filter_frame(inlink->dst->outputs[0], buf);
}
#define LINEAR_TO_DB(x) (log10(x) * 20)
static void print_stats(AVFilterContext *ctx)
{
AudioStatsContext *s = ctx->priv;
uint64_t min_count = 0, max_count = 0, nb_samples = 0;
double min_runs = 0, max_runs = 0,
min = DBL_MAX, max = DBL_MIN,
max_sigma_x = 0,
sigma_x = 0,
sigma_x2 = 0,
min_sigma_x2 = DBL_MAX,
max_sigma_x2 = DBL_MIN;
int c;
for (c = 0; c < s->nb_channels; c++) {
ChannelStats *p = &s->chstats[c];
if (p->nb_samples < s->tc_samples)
p->min_sigma_x2 = p->max_sigma_x2 = p->sigma_x2 / p->nb_samples;
min = FFMIN(min, p->min);
max = FFMAX(max, p->max);
min_sigma_x2 = FFMIN(min_sigma_x2, p->min_sigma_x2);
max_sigma_x2 = FFMAX(max_sigma_x2, p->max_sigma_x2);
sigma_x += p->sigma_x;
sigma_x2 += p->sigma_x2;
min_count += p->min_count;
max_count += p->max_count;
min_runs += p->min_runs;
max_runs += p->max_runs;
nb_samples += p->nb_samples;
if (fabs(p->sigma_x) > fabs(max_sigma_x))
max_sigma_x = p->sigma_x;
av_log(ctx, AV_LOG_INFO, "Channel: %d\n", c + 1);
av_log(ctx, AV_LOG_INFO, "DC offset: %f\n", p->sigma_x / p->nb_samples);
av_log(ctx, AV_LOG_INFO, "Min level: %f\n", p->min);
av_log(ctx, AV_LOG_INFO, "Max level: %f\n", p->max);
av_log(ctx, AV_LOG_INFO, "Peak level dB: %f\n", LINEAR_TO_DB(FFMAX(-p->min, p->max)));
av_log(ctx, AV_LOG_INFO, "RMS level dB: %f\n", LINEAR_TO_DB(sqrt(p->sigma_x2 / p->nb_samples)));
av_log(ctx, AV_LOG_INFO, "RMS peak dB: %f\n", LINEAR_TO_DB(sqrt(p->max_sigma_x2)));
if (p->min_sigma_x2 != 1)
av_log(ctx, AV_LOG_INFO, "RMS trough dB: %f\n",LINEAR_TO_DB(sqrt(p->min_sigma_x2)));
av_log(ctx, AV_LOG_INFO, "Crest factor: %f\n", p->sigma_x2 ? FFMAX(-p->min, p->max) / sqrt(p->sigma_x2 / p->nb_samples) : 1);
av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((p->min_runs + p->max_runs) / (p->min_count + p->max_count)));
av_log(ctx, AV_LOG_INFO, "Peak count: %"PRId64"\n", p->min_count + p->max_count);
}
av_log(ctx, AV_LOG_INFO, "Overall\n");
av_log(ctx, AV_LOG_INFO, "DC offset: %f\n", max_sigma_x / (nb_samples / s->nb_channels));
av_log(ctx, AV_LOG_INFO, "Min level: %f\n", min);
av_log(ctx, AV_LOG_INFO, "Max level: %f\n", max);
av_log(ctx, AV_LOG_INFO, "Peak level dB: %f\n", LINEAR_TO_DB(FFMAX(-min, max)));
av_log(ctx, AV_LOG_INFO, "RMS level dB: %f\n", LINEAR_TO_DB(sqrt(sigma_x2 / nb_samples)));
av_log(ctx, AV_LOG_INFO, "RMS peak dB: %f\n", LINEAR_TO_DB(sqrt(max_sigma_x2)));
if (min_sigma_x2 != 1)
av_log(ctx, AV_LOG_INFO, "RMS trough dB: %f\n", LINEAR_TO_DB(sqrt(min_sigma_x2)));
av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((min_runs + max_runs) / (min_count + max_count)));
av_log(ctx, AV_LOG_INFO, "Peak count: %f\n", (min_count + max_count) / (double)s->nb_channels);
av_log(ctx, AV_LOG_INFO, "Number of samples: %"PRId64"\n", nb_samples / s->nb_channels);
}
static av_cold void uninit(AVFilterContext *ctx)
{
AudioStatsContext *s = ctx->priv;
print_stats(ctx);
av_freep(&s->chstats);
}
static const AVFilterPad astats_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad astats_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
},
{ NULL }
};
AVFilter avfilter_af_astats = {
.name = "astats",
.description = NULL_IF_CONFIG_SMALL("Show time domain statistics about audio frames."),
.query_formats = query_formats,
.priv_size = sizeof(AudioStatsContext),
.priv_class = &astats_class,
.uninit = uninit,
.inputs = astats_inputs,
.outputs = astats_outputs,
};
+240
View File
@@ -0,0 +1,240 @@
/*
* Copyright (c) 2011 Nicolas George <[email protected]>
*
* 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
* Stream (de)synchronization filter
*/
#include "libavutil/eval.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
#define QUEUE_SIZE 16
static const char * const var_names[] = {
"b1", "b2",
"s1", "s2",
"t1", "t2",
NULL
};
enum var_name {
VAR_B1, VAR_B2,
VAR_S1, VAR_S2,
VAR_T1, VAR_T2,
VAR_NB
};
typedef struct {
const AVClass *class;
AVExpr *expr;
char *expr_str;
double var_values[VAR_NB];
struct buf_queue {
AVFrame *buf[QUEUE_SIZE];
unsigned tail, nb;
/* buf[tail] is the oldest,
buf[(tail + nb) % QUEUE_SIZE] is where the next is added */
} queue[2];
int req[2];
int next_out;
int eof; /* bitmask, one bit for each stream */
} AStreamSyncContext;
#define OFFSET(x) offsetof(AStreamSyncContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption astreamsync_options[] = {
{ "expr", "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
{ "e", "set stream selection expression", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "t1-t2" }, .flags = FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(astreamsync);
static av_cold int init(AVFilterContext *ctx)
{
AStreamSyncContext *as = ctx->priv;
int r, i;
r = av_expr_parse(&as->expr, as->expr_str, var_names,
NULL, NULL, NULL, NULL, 0, ctx);
if (r < 0) {
av_log(ctx, AV_LOG_ERROR, "Error in expression \"%s\"\n", as->expr_str);
return r;
}
for (i = 0; i < 42; i++)
av_expr_eval(as->expr, as->var_values, NULL); /* exercize prng */
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
int i;
AVFilterFormats *formats, *rates;
AVFilterChannelLayouts *layouts;
for (i = 0; i < 2; i++) {
formats = ctx->inputs[i]->in_formats;
ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
ff_formats_ref(formats, &ctx->outputs[i]->in_formats);
rates = ff_all_samplerates();
ff_formats_ref(rates, &ctx->inputs[i]->out_samplerates);
ff_formats_ref(rates, &ctx->outputs[i]->in_samplerates);
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;
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
int id = outlink == ctx->outputs[1];
outlink->sample_rate = ctx->inputs[id]->sample_rate;
outlink->time_base = ctx->inputs[id]->time_base;
return 0;
}
static int send_out(AVFilterContext *ctx, int out_id)
{
AStreamSyncContext *as = ctx->priv;
struct buf_queue *queue = &as->queue[out_id];
AVFrame *buf = queue->buf[queue->tail];
int ret;
queue->buf[queue->tail] = NULL;
as->var_values[VAR_B1 + out_id]++;
as->var_values[VAR_S1 + out_id] += buf->nb_samples;
if (buf->pts != AV_NOPTS_VALUE)
as->var_values[VAR_T1 + out_id] =
av_q2d(ctx->outputs[out_id]->time_base) * buf->pts;
as->var_values[VAR_T1 + out_id] += buf->nb_samples /
(double)ctx->inputs[out_id]->sample_rate;
ret = ff_filter_frame(ctx->outputs[out_id], buf);
queue->nb--;
queue->tail = (queue->tail + 1) % QUEUE_SIZE;
if (as->req[out_id])
as->req[out_id]--;
return ret;
}
static void send_next(AVFilterContext *ctx)
{
AStreamSyncContext *as = ctx->priv;
int i;
while (1) {
if (!as->queue[as->next_out].nb)
break;
send_out(ctx, as->next_out);
if (!as->eof)
as->next_out = av_expr_eval(as->expr, as->var_values, NULL) >= 0;
}
for (i = 0; i < 2; i++)
if (as->queue[i].nb == QUEUE_SIZE)
send_out(ctx, i);
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AStreamSyncContext *as = ctx->priv;
int id = outlink == ctx->outputs[1];
as->req[id]++;
while (as->req[id] && !(as->eof & (1 << id))) {
if (as->queue[as->next_out].nb) {
send_next(ctx);
} else {
as->eof |= 1 << as->next_out;
ff_request_frame(ctx->inputs[as->next_out]);
if (as->eof & (1 << as->next_out))
as->next_out = !as->next_out;
}
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
AVFilterContext *ctx = inlink->dst;
AStreamSyncContext *as = ctx->priv;
int id = inlink == ctx->inputs[1];
as->queue[id].buf[(as->queue[id].tail + as->queue[id].nb++) % QUEUE_SIZE] =
insamples;
as->eof &= ~(1 << id);
send_next(ctx);
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
AStreamSyncContext *as = ctx->priv;
av_expr_free(as->expr);
as->expr = NULL;
}
static const AVFilterPad astreamsync_inputs[] = {
{
.name = "in1",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},{
.name = "in2",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad astreamsync_outputs[] = {
{
.name = "out1",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
.request_frame = request_frame,
},{
.name = "out2",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_af_astreamsync = {
.name = "astreamsync",
.description = NULL_IF_CONFIG_SMALL("Copy two streams of audio data "
"in a configurable order."),
.priv_size = sizeof(AStreamSyncContext),
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = astreamsync_inputs,
.outputs = astreamsync_outputs,
.priv_class = &astreamsync_class,
};
+321
View File
@@ -0,0 +1,321 @@
/*
* 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 "libavresample/avresample.h"
#include "libavutil/attributes.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/common.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.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
int first_frame; ///< 1 until filter_frame() has processed at least 1 frame with a pts != AV_NOPTS_VALUE
int64_t first_pts; ///< user-specified first expected pts, in samples
int comp; ///< current resample compensation
/* options */
int resample;
float min_delta_sec;
int max_comp;
/* set by filter_frame() to signal an output frame to request_frame() */
int got_output;
} ASyncContext;
#define OFFSET(x) offsetof(ASyncContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption asyncts_options[] = {
{ "compensate", "Stretch/squeeze the data to make it match the timestamps", OFFSET(resample), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, A|F },
{ "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, { .dbl = 0.1 }, 0, INT_MAX, A|F },
{ "max_comp", "Maximum compensation in samples per second.", OFFSET(max_comp), AV_OPT_TYPE_INT, { .i64 = 500 }, 0, INT_MAX, A|F },
{ "first_pts", "Assume the first pts should be this value.", OFFSET(first_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, A|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(asyncts);
static av_cold int init(AVFilterContext *ctx)
{
ASyncContext *s = ctx->priv;
s->pts = AV_NOPTS_VALUE;
s->first_frame = 1;
return 0;
}
static av_cold 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;
}
/* 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 handle_trimming(AVFilterContext *ctx)
{
ASyncContext *s = ctx->priv;
if (s->pts < s->first_pts) {
int delta = FFMIN(s->first_pts - s->pts, avresample_available(s->avr));
av_log(ctx, AV_LOG_VERBOSE, "Trimming %d samples from start\n",
delta);
avresample_read(s->avr, NULL, delta);
s->pts += delta;
} else if (s->first_frame)
s->pts = s->first_pts;
}
static int request_frame(AVFilterLink *link)
{
AVFilterContext *ctx = link->src;
ASyncContext *s = ctx->priv;
int ret = 0;
int nb_samples;
s->got_output = 0;
while (ret >= 0 && !s->got_output)
ret = ff_request_frame(ctx->inputs[0]);
/* flush the fifo */
if (ret == AVERROR_EOF) {
if (s->first_pts != AV_NOPTS_VALUE)
handle_trimming(ctx);
if (nb_samples = get_delay(s)) {
AVFrame *buf = ff_get_audio_buffer(link, nb_samples);
if (!buf)
return AVERROR(ENOMEM);
ret = avresample_convert(s->avr, buf->extended_data,
buf->linesize[0], nb_samples, NULL, 0, 0);
if (ret <= 0) {
av_frame_free(&buf);
return (ret < 0) ? ret : AVERROR_EOF;
}
buf->pts = s->pts;
return ff_filter_frame(link, buf);
}
}
return ret;
}
static int write_to_fifo(ASyncContext *s, AVFrame *buf)
{
int ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data,
buf->linesize[0], buf->nb_samples);
av_frame_free(&buf);
return ret;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
AVFilterContext *ctx = inlink->dst;
ASyncContext *s = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
int nb_channels = av_get_channel_layout_nb_channels(buf->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, ret;
int64_t delta;
int64_t new_pts;
/* buffer data until we get the next timestamp */
if (s->pts == AV_NOPTS_VALUE || pts == AV_NOPTS_VALUE) {
if (pts != AV_NOPTS_VALUE) {
s->pts = pts - get_delay(s);
}
return write_to_fifo(s, buf);
}
if (s->first_pts != AV_NOPTS_VALUE) {
handle_trimming(ctx);
if (!avresample_available(s->avr))
return write_to_fifo(s, buf);
}
/* 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 ||
(s->first_frame && delta && s->first_pts != AV_NOPTS_VALUE)) {
av_log(ctx, AV_LOG_VERBOSE, "Discontinuity - %"PRId64" samples.\n", delta);
out_size = av_clipl_int32((int64_t)out_size + delta);
} else {
if (s->resample) {
// adjust the compensation if delta is non-zero
int delay = get_delay(s);
int comp = s->comp + av_clip(delta * inlink->sample_rate / delay,
-s->max_comp, s->max_comp);
if (comp != s->comp) {
av_log(ctx, AV_LOG_VERBOSE, "Compensating %d samples per second.\n", comp);
if (avresample_set_compensation(s->avr, comp, inlink->sample_rate) == 0) {
s->comp = comp;
}
}
}
// adjust PTS to avoid monotonicity errors with input PTS jitter
pts -= delta;
delta = 0;
}
if (out_size > 0) {
AVFrame *buf_out = ff_get_audio_buffer(outlink, out_size);
if (!buf_out) {
ret = AVERROR(ENOMEM);
goto fail;
}
if (s->first_frame && delta > 0) {
int planar = av_sample_fmt_is_planar(buf_out->format);
int planes = planar ? nb_channels : 1;
int block_size = av_get_bytes_per_sample(buf_out->format) *
(planar ? 1 : nb_channels);
int ch;
av_samples_set_silence(buf_out->extended_data, 0, delta,
nb_channels, buf->format);
for (ch = 0; ch < planes; ch++)
buf_out->extended_data[ch] += delta * block_size;
avresample_read(s->avr, buf_out->extended_data, out_size);
for (ch = 0; ch < planes; ch++)
buf_out->extended_data[ch] -= delta * block_size;
} else {
avresample_read(s->avr, buf_out->extended_data, out_size);
if (delta > 0) {
av_samples_set_silence(buf_out->extended_data, out_size - delta,
delta, nb_channels, buf->format);
}
}
buf_out->pts = s->pts;
ret = ff_filter_frame(outlink, buf_out);
if (ret < 0)
goto fail;
s->got_output = 1;
} else if (avresample_available(s->avr)) {
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));
new_pts = pts - avresample_get_delay(s->avr);
/* check for s->pts monotonicity */
if (new_pts > s->pts) {
s->pts = new_pts;
ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data,
buf->linesize[0], buf->nb_samples);
} else {
av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping "
"whole buffer.\n");
ret = 0;
}
s->first_frame = 0;
fail:
av_frame_free(&buf);
return ret;
}
static const AVFilterPad avfilter_af_asyncts_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame
},
{ NULL }
};
static const AVFilterPad avfilter_af_asyncts_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_props,
.request_frame = request_frame
},
{ NULL }
};
AVFilter avfilter_af_asyncts = {
.name = "asyncts",
.description = NULL_IF_CONFIG_SMALL("Sync audio data to timestamps"),
.init = init,
.uninit = uninit,
.priv_size = sizeof(ASyncContext),
.priv_class = &asyncts_class,
.inputs = avfilter_af_asyncts_inputs,
.outputs = avfilter_af_asyncts_outputs,
};
File diff suppressed because it is too large Load Diff
+620
View File
@@ -0,0 +1,620 @@
/*
* Copyright (c) 2013 Paul B Mahol
* Copyright (c) 2006-2008 Rob Sykes <[email protected]>
*
* 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
*/
/*
* 2-pole filters designed by Robert Bristow-Johnson <[email protected]>
* see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
*
* 1-pole filters based on code (c) 2000 Chris Bagwell <[email protected]>
* Algorithms: Recursive single pole low/high pass filter
* Reference: The Scientist and Engineer's Guide to Digital Signal Processing
*
* low-pass: output[N] = input[N] * A + output[N-1] * B
* X = exp(-2.0 * pi * Fc)
* A = 1 - X
* B = X
* Fc = cutoff freq / sample rate
*
* Mimics an RC low-pass filter:
*
* ---/\/\/\/\----------->
* |
* --- C
* ---
* |
* |
* V
*
* high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
* X = exp(-2.0 * pi * Fc)
* A0 = (1 + X) / 2
* A1 = -(1 + X) / 2
* B1 = X
* Fc = cutoff freq / sample rate
*
* Mimics an RC high-pass filter:
*
* || C
* ----||--------->
* || |
* <
* > R
* <
* |
* V
*/
#include "libavutil/avassert.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
enum FilterType {
biquad,
equalizer,
bass,
treble,
band,
bandpass,
bandreject,
allpass,
highpass,
lowpass,
};
enum WidthType {
NONE,
HERTZ,
OCTAVE,
QFACTOR,
SLOPE,
};
typedef struct ChanCache {
double i1, i2;
double o1, o2;
} ChanCache;
typedef struct {
const AVClass *class;
enum FilterType filter_type;
enum WidthType width_type;
int poles;
int csg;
double gain;
double frequency;
double width;
double a0, a1, a2;
double b0, b1, b2;
ChanCache *cache;
void (*filter)(const void *ibuf, void *obuf, int len,
double *i1, double *i2, double *o1, double *o2,
double b0, double b1, double b2, double a1, double a2);
} BiquadsContext;
static av_cold int init(AVFilterContext *ctx)
{
BiquadsContext *p = ctx->priv;
if (p->filter_type != biquad) {
if (p->frequency <= 0 || p->width <= 0) {
av_log(ctx, AV_LOG_ERROR, "Invalid frequency %f and/or width %f <= 0\n",
p->frequency, p->width);
return AVERROR(EINVAL);
}
}
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats;
AVFilterChannelLayouts *layouts;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_FLTP,
AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
#define BIQUAD_FILTER(name, type, min, max) \
static void biquad_## name (const void *input, void *output, int len, \
double *in1, double *in2, \
double *out1, double *out2, \
double b0, double b1, double b2, \
double a1, double a2) \
{ \
const type *ibuf = input; \
type *obuf = output; \
double i1 = *in1; \
double i2 = *in2; \
double o1 = *out1; \
double o2 = *out2; \
int i; \
a1 = -a1; \
a2 = -a2; \
\
for (i = 0; i+1 < len; i++) { \
o2 = i2 * b2 + i1 * b1 + ibuf[i] * b0 + o2 * a2 + o1 * a1; \
i2 = ibuf[i]; \
if (o2 < min) { \
av_log(NULL, AV_LOG_WARNING, "clipping\n"); \
obuf[i] = min; \
} else if (o2 > max) { \
av_log(NULL, AV_LOG_WARNING, "clipping\n"); \
obuf[i] = max; \
} else { \
obuf[i] = o2; \
} \
i++; \
o1 = i1 * b2 + i2 * b1 + ibuf[i] * b0 + o1 * a2 + o2 * a1; \
i1 = ibuf[i]; \
if (o1 < min) { \
av_log(NULL, AV_LOG_WARNING, "clipping\n"); \
obuf[i] = min; \
} else if (o1 > max) { \
av_log(NULL, AV_LOG_WARNING, "clipping\n"); \
obuf[i] = max; \
} else { \
obuf[i] = o1; \
} \
} \
if (i < len) { \
double o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \
i2 = i1; \
i1 = ibuf[i]; \
o2 = o1; \
o1 = o0; \
if (o0 < min) { \
av_log(NULL, AV_LOG_WARNING, "clipping\n"); \
obuf[i] = min; \
} else if (o0 > max) { \
av_log(NULL, AV_LOG_WARNING, "clipping\n"); \
obuf[i] = max; \
} else { \
obuf[i] = o0; \
} \
} \
*in1 = i1; \
*in2 = i2; \
*out1 = o1; \
*out2 = o2; \
}
BIQUAD_FILTER(s16, int16_t, INT16_MIN, INT16_MAX)
BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX)
BIQUAD_FILTER(flt, float, -1., 1.)
BIQUAD_FILTER(dbl, double, -1., 1.)
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
BiquadsContext *p = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
double A = exp(p->gain / 40 * log(10.));
double w0 = 2 * M_PI * p->frequency / inlink->sample_rate;
double alpha;
if (w0 > M_PI) {
av_log(ctx, AV_LOG_ERROR,
"Invalid frequency %f. Frequency must be less than half the sample-rate %d.\n",
p->frequency, inlink->sample_rate);
return AVERROR(EINVAL);
}
switch (p->width_type) {
case NONE:
alpha = 0.0;
break;
case HERTZ:
alpha = sin(w0) / (2 * p->frequency / p->width);
break;
case OCTAVE:
alpha = sin(w0) * sinh(log(2.) / 2 * p->width * w0 / sin(w0));
break;
case QFACTOR:
alpha = sin(w0) / (2 * p->width);
break;
case SLOPE:
alpha = sin(w0) / 2 * sqrt((A + 1 / A) * (1 / p->width - 1) + 2);
break;
default:
av_assert0(0);
}
switch (p->filter_type) {
case biquad:
break;
case equalizer:
p->a0 = 1 + alpha / A;
p->a1 = -2 * cos(w0);
p->a2 = 1 - alpha / A;
p->b0 = 1 + alpha * A;
p->b1 = -2 * cos(w0);
p->b2 = 1 - alpha * A;
break;
case bass:
p->a0 = (A + 1) + (A - 1) * cos(w0) + 2 * sqrt(A) * alpha;
p->a1 = -2 * ((A - 1) + (A + 1) * cos(w0));
p->a2 = (A + 1) + (A - 1) * cos(w0) - 2 * sqrt(A) * alpha;
p->b0 = A * ((A + 1) - (A - 1) * cos(w0) + 2 * sqrt(A) * alpha);
p->b1 = 2 * A * ((A - 1) - (A + 1) * cos(w0));
p->b2 = A * ((A + 1) - (A - 1) * cos(w0) - 2 * sqrt(A) * alpha);
break;
case treble:
p->a0 = (A + 1) - (A - 1) * cos(w0) + 2 * sqrt(A) * alpha;
p->a1 = 2 * ((A - 1) - (A + 1) * cos(w0));
p->a2 = (A + 1) - (A - 1) * cos(w0) - 2 * sqrt(A) * alpha;
p->b0 = A * ((A + 1) + (A - 1) * cos(w0) + 2 * sqrt(A) * alpha);
p->b1 =-2 * A * ((A - 1) + (A + 1) * cos(w0));
p->b2 = A * ((A + 1) + (A - 1) * cos(w0) - 2 * sqrt(A) * alpha);
break;
case bandpass:
if (p->csg) {
p->a0 = 1 + alpha;
p->a1 = -2 * cos(w0);
p->a2 = 1 - alpha;
p->b0 = sin(w0) / 2;
p->b1 = 0;
p->b2 = -sin(w0) / 2;
} else {
p->a0 = 1 + alpha;
p->a1 = -2 * cos(w0);
p->a2 = 1 - alpha;
p->b0 = alpha;
p->b1 = 0;
p->b2 = -alpha;
}
break;
case bandreject:
p->a0 = 1 + alpha;
p->a1 = -2 * cos(w0);
p->a2 = 1 - alpha;
p->b0 = 1;
p->b1 = -2 * cos(w0);
p->b2 = 1;
break;
case lowpass:
if (p->poles == 1) {
p->a0 = 1;
p->a1 = -exp(-w0);
p->a2 = 0;
p->b0 = 1 + p->a1;
p->b1 = 0;
p->b2 = 0;
} else {
p->a0 = 1 + alpha;
p->a1 = -2 * cos(w0);
p->a2 = 1 - alpha;
p->b0 = (1 - cos(w0)) / 2;
p->b1 = 1 - cos(w0);
p->b2 = (1 - cos(w0)) / 2;
}
break;
case highpass:
if (p->poles == 1) {
p->a0 = 1;
p->a1 = -exp(-w0);
p->a2 = 0;
p->b0 = (1 - p->a1) / 2;
p->b1 = -p->b0;
p->b2 = 0;
} else {
p->a0 = 1 + alpha;
p->a1 = -2 * cos(w0);
p->a2 = 1 - alpha;
p->b0 = (1 + cos(w0)) / 2;
p->b1 = -(1 + cos(w0));
p->b2 = (1 + cos(w0)) / 2;
}
break;
case allpass:
p->a0 = 1 + alpha;
p->a1 = -2 * cos(w0);
p->a2 = 1 - alpha;
p->b0 = 1 - alpha;
p->b1 = -2 * cos(w0);
p->b2 = 1 + alpha;
break;
default:
av_assert0(0);
}
p->a1 /= p->a0;
p->a2 /= p->a0;
p->b0 /= p->a0;
p->b1 /= p->a0;
p->b2 /= p->a0;
p->cache = av_realloc_f(p->cache, sizeof(ChanCache), inlink->channels);
if (!p->cache)
return AVERROR(ENOMEM);
memset(p->cache, 0, sizeof(ChanCache) * inlink->channels);
switch (inlink->format) {
case AV_SAMPLE_FMT_S16P: p->filter = biquad_s16; break;
case AV_SAMPLE_FMT_S32P: p->filter = biquad_s32; break;
case AV_SAMPLE_FMT_FLTP: p->filter = biquad_flt; break;
case AV_SAMPLE_FMT_DBLP: p->filter = biquad_dbl; break;
default: av_assert0(0);
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
BiquadsContext *p = inlink->dst->priv;
AVFilterLink *outlink = inlink->dst->outputs[0];
AVFrame *out_buf;
int nb_samples = buf->nb_samples;
int ch;
if (av_frame_is_writable(buf)) {
out_buf = buf;
} else {
out_buf = ff_get_audio_buffer(inlink, nb_samples);
if (!out_buf)
return AVERROR(ENOMEM);
av_frame_copy_props(out_buf, buf);
}
for (ch = 0; ch < av_frame_get_channels(buf); ch++)
p->filter(buf->extended_data[ch],
out_buf->extended_data[ch], nb_samples,
&p->cache[ch].i1, &p->cache[ch].i2,
&p->cache[ch].o1, &p->cache[ch].o2,
p->b0, p->b1, p->b2, p->a1, p->a2);
if (buf != out_buf)
av_frame_free(&buf);
return ff_filter_frame(outlink, out_buf);
}
static av_cold void uninit(AVFilterContext *ctx)
{
BiquadsContext *p = ctx->priv;
av_freep(&p->cache);
}
static const AVFilterPad inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
},
{ NULL }
};
#define OFFSET(x) offsetof(BiquadsContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
#define DEFINE_BIQUAD_FILTER(name_, description_) \
AVFILTER_DEFINE_CLASS(name_); \
static av_cold int name_##_init(AVFilterContext *ctx) \
{ \
BiquadsContext *p = ctx->priv; \
p->class = &name_##_class; \
p->filter_type = name_; \
return init(ctx); \
} \
\
AVFilter avfilter_af_##name_ = { \
.name = #name_, \
.description = NULL_IF_CONFIG_SMALL(description_), \
.priv_size = sizeof(BiquadsContext), \
.init = name_##_init, \
.uninit = uninit, \
.query_formats = query_formats, \
.inputs = inputs, \
.outputs = outputs, \
.priv_class = &name_##_class, \
}
#if CONFIG_EQUALIZER_FILTER
static const AVOption equalizer_options[] = {
{"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 999999, FLAGS},
{"f", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 999, FLAGS},
{"w", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 999, FLAGS},
{"gain", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
{"g", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(equalizer, "Apply two-pole peaking equalization (EQ) filter.");
#endif /* CONFIG_EQUALIZER_FILTER */
#if CONFIG_BASS_FILTER
static const AVOption bass_options[] = {
{"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=100}, 0, 999999, FLAGS},
{"f", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=100}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
{"w", "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
{"gain", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
{"g", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(bass, "Boost or cut lower frequencies.");
#endif /* CONFIG_BASS_FILTER */
#if CONFIG_TREBLE_FILTER
static const AVOption treble_options[] = {
{"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"f", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
{"w", "set shelf transition steep", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 99999, FLAGS},
{"gain", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
{"g", "set gain", OFFSET(gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(treble, "Boost or cut upper frequencies.");
#endif /* CONFIG_TREBLE_FILTER */
#if CONFIG_BANDPASS_FILTER
static const AVOption bandpass_options[] = {
{"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"f", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
{"w", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
{"csg", "use constant skirt gain", OFFSET(csg), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(bandpass, "Apply a two-pole Butterworth band-pass filter.");
#endif /* CONFIG_BANDPASS_FILTER */
#if CONFIG_BANDREJECT_FILTER
static const AVOption bandreject_options[] = {
{"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"f", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
{"w", "set band-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 999, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(bandreject, "Apply a two-pole Butterworth band-reject filter.");
#endif /* CONFIG_BANDREJECT_FILTER */
#if CONFIG_LOWPASS_FILTER
static const AVOption lowpass_options[] = {
{"frequency", "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=500}, 0, 999999, FLAGS},
{"f", "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=500}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
{"w", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
{"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
{"p", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(lowpass, "Apply a low-pass filter with 3dB point frequency.");
#endif /* CONFIG_LOWPASS_FILTER */
#if CONFIG_HIGHPASS_FILTER
static const AVOption highpass_options[] = {
{"frequency", "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"f", "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=QFACTOR}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
{"w", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=0.707}, 0, 99999, FLAGS},
{"poles", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
{"p", "set number of poles", OFFSET(poles), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(highpass, "Apply a high-pass filter with 3dB point frequency.");
#endif /* CONFIG_HIGHPASS_FILTER */
#if CONFIG_ALLPASS_FILTER
static const AVOption allpass_options[] = {
{"frequency", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"f", "set central frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=3000}, 0, 999999, FLAGS},
{"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=HERTZ}, HERTZ, SLOPE, FLAGS, "width_type"},
{"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"},
{"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"},
{"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"},
{"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"},
{"width", "set filter-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=707.1}, 0, 99999, FLAGS},
{"w", "set filter-width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=707.1}, 0, 99999, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(allpass, "Apply a two-pole all-pass filter.");
#endif /* CONFIG_ALLPASS_FILTER */
#if CONFIG_BIQUAD_FILTER
static const AVOption biquad_options[] = {
{"a0", NULL, OFFSET(a0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
{"a1", NULL, OFFSET(a1), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
{"a2", NULL, OFFSET(a2), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
{"b0", NULL, OFFSET(b0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
{"b1", NULL, OFFSET(b1), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
{"b2", NULL, OFFSET(b2), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT16_MIN, INT16_MAX, FLAGS},
{NULL}
};
DEFINE_BIQUAD_FILTER(biquad, "Apply a biquad IIR filter with the given coefficients.");
#endif /* CONFIG_BIQUAD_FILTER */
+409
View File
@@ -0,0 +1,409 @@
/*
* Copyright (c) 2012 Google, Inc.
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio channel mapping filter
*/
#include <ctype.h>
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.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
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption channelmap_options[] = {
{ "map", "A comma-separated list of input channel numbers in output order.",
OFFSET(mapping_str), AV_OPT_TYPE_STRING, .flags = A|F },
{ "channel_layout", "Output channel layout.",
OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(channelmap);
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)
{
ChannelMapContext *s = ctx->priv;
char *mapping, separator = '|';
int map_entries = 0;
char buf[256];
enum MappingMode mode;
uint64_t out_ch_mask = 0;
int i;
mapping = s->mapping_str;
if (!mapping) {
mode = MAP_NONE;
} else {
char *dash = strchr(mapping, '-');
if (!dash) { // short mapping
if (av_isdigit(*mapping))
mode = MAP_ONE_INT;
else
mode = MAP_ONE_STR;
} else if (av_isdigit(*mapping)) {
if (av_isdigit(*(dash+1)))
mode = MAP_PAIR_INT_INT;
else
mode = MAP_PAIR_INT_STR;
} else {
if (av_isdigit(*(dash+1)))
mode = MAP_PAIR_STR_INT;
else
mode = MAP_PAIR_STR_STR;
}
#if FF_API_OLD_FILTER_OPTS
if (strchr(mapping, ',')) {
av_log(ctx, AV_LOG_WARNING, "This syntax is deprecated, use "
"'|' to separate the mappings.\n");
separator = ',';
}
#endif
}
if (mode != MAP_NONE) {
char *sep = mapping;
map_entries = 1;
while ((sep = strchr(sep, separator))) {
if (*++sep) // Allow trailing comma
map_entries++;
}
}
if (map_entries > MAX_CH) {
av_log(ctx, AV_LOG_ERROR, "Too many channels mapped: '%d'.\n", map_entries);
return AVERROR(EINVAL);
}
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, separator, MAX_CH) < 0) {
av_log(ctx, AV_LOG_ERROR, err);
return AVERROR(EINVAL);
}
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, separator)) {
av_log(ctx, AV_LOG_ERROR, err);
return AVERROR(EINVAL);
}
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, separator, MAX_CH) < 0) {
av_log(ctx, AV_LOG_ERROR, err);
return AVERROR(EINVAL);
}
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, separator) < 0 ||
out_ch & out_ch_mask) {
av_log(ctx, AV_LOG_ERROR, err);
return AVERROR(EINVAL);
}
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, separator, MAX_CH) < 0) {
av_log(ctx, AV_LOG_ERROR, err);
return AVERROR(EINVAL);
}
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, separator) < 0 ||
out_ch & out_ch_mask) {
av_log(ctx, AV_LOG_ERROR, err);
return AVERROR(EINVAL);
}
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);
return AVERROR(EINVAL);
}
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);
return AVERROR(EINVAL);
} 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);
return AVERROR(EINVAL);
}
s->output_layout = fmt;
}
if (!s->output_layout) {
av_log(ctx, AV_LOG_ERROR, "Output channel layout is not set and "
"cannot be guessed from the maps.\n");
return AVERROR(EINVAL);
}
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);
}
}
return 0;
}
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 int channelmap_filter_frame(AVFilterLink *inlink, AVFrame *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) {
av_frame_free(&buf);
return AVERROR(ENOMEM);
}
if (buf->extended_data == buf->data) {
buf->extended_data = new_extended_data;
} else {
av_free(buf->extended_data);
buf->extended_data = new_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]));
return ff_filter_frame(outlink, buf);
}
static int channelmap_config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
ChannelMapContext *s = ctx->priv;
int nb_channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
int i, err = 0;
const char *channel_name;
char layout_name[256];
for (i = 0; i < s->nch; i++) {
struct ChannelMap *m = &s->map[i];
if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR) {
m->in_channel_idx = av_get_channel_layout_channel_index(
inlink->channel_layout, m->in_channel);
}
if (m->in_channel_idx < 0 || m->in_channel_idx >= nb_channels) {
av_get_channel_layout_string(layout_name, sizeof(layout_name),
0, inlink->channel_layout);
if (m->in_channel) {
channel_name = av_get_channel_name(m->in_channel);
av_log(ctx, AV_LOG_ERROR,
"input channel '%s' not available from input layout '%s'\n",
channel_name, layout_name);
} else {
av_log(ctx, AV_LOG_ERROR,
"input channel #%d not available from input layout '%s'\n",
m->in_channel_idx, layout_name);
}
err = AVERROR(EINVAL);
}
}
return err;
}
static const AVFilterPad avfilter_af_channelmap_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = channelmap_filter_frame,
.config_props = channelmap_config_input,
.needs_writable = 1,
},
{ NULL }
};
static const AVFilterPad avfilter_af_channelmap_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO
},
{ NULL }
};
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),
.priv_class = &channelmap_class,
.inputs = avfilter_af_channelmap_inputs,
.outputs = avfilter_af_channelmap_outputs,
};
+149
View File
@@ -0,0 +1,149 @@
/*
* 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
* Channel split filter
*
* Split an audio stream into per-channel streams.
*/
#include "libavutil/attributes.h"
#include "libavutil/channel_layout.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
typedef struct ChannelSplitContext {
const AVClass *class;
uint64_t channel_layout;
char *channel_layout_str;
} ChannelSplitContext;
#define OFFSET(x) offsetof(ChannelSplitContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption channelsplit_options[] = {
{ "channel_layout", "Input channel layout.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, { .str = "stereo" }, .flags = A|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(channelsplit);
static av_cold int init(AVFilterContext *ctx)
{
ChannelSplitContext *s = ctx->priv;
int nb_channels;
int ret = 0, i;
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;
}
nb_channels = av_get_channel_layout_nb_channels(s->channel_layout);
for (i = 0; i < nb_channels; i++) {
uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, i);
AVFilterPad pad = { 0 };
pad.type = AVMEDIA_TYPE_AUDIO;
pad.name = av_get_channel_name(channel);
ff_insert_outpad(ctx, i, &pad);
}
fail:
return ret;
}
static int query_formats(AVFilterContext *ctx)
{
ChannelSplitContext *s = ctx->priv;
AVFilterChannelLayouts *in_layouts = NULL;
int i;
ff_set_common_formats (ctx, ff_planar_sample_fmts());
ff_set_common_samplerates(ctx, ff_all_samplerates());
ff_add_channel_layout(&in_layouts, s->channel_layout);
ff_channel_layouts_ref(in_layouts, &ctx->inputs[0]->out_channel_layouts);
for (i = 0; i < ctx->nb_outputs; i++) {
AVFilterChannelLayouts *out_layouts = NULL;
uint64_t channel = av_channel_layout_extract_channel(s->channel_layout, i);
ff_add_channel_layout(&out_layouts, channel);
ff_channel_layouts_ref(out_layouts, &ctx->outputs[i]->in_channel_layouts);
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
AVFilterContext *ctx = inlink->dst;
int i, ret = 0;
for (i = 0; i < ctx->nb_outputs; i++) {
AVFrame *buf_out = av_frame_clone(buf);
if (!buf_out) {
ret = AVERROR(ENOMEM);
break;
}
buf_out->data[0] = buf_out->extended_data[0] = buf_out->extended_data[i];
buf_out->channel_layout =
av_channel_layout_extract_channel(buf->channel_layout, i);
av_frame_set_channels(buf_out, 1);
ret = ff_filter_frame(ctx->outputs[i], buf_out);
if (ret < 0)
break;
}
av_frame_free(&buf);
return ret;
}
static const AVFilterPad avfilter_af_channelsplit_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
AVFilter avfilter_af_channelsplit = {
.name = "channelsplit",
.description = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."),
.priv_size = sizeof(ChannelSplitContext),
.priv_class = &channelsplit_class,
.init = init,
.query_formats = query_formats,
.inputs = avfilter_af_channelsplit_inputs,
.outputs = NULL,
.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
};
+518
View File
@@ -0,0 +1,518 @@
/*
* Copyright (c) 1999 Chris Bagwell
* Copyright (c) 1999 Nick Bailey
* Copyright (c) 2007 Rob Sykes <[email protected]>
* Copyright (c) 2013 Paul B Mahol
*
* 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/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
typedef struct ChanParam {
double attack;
double decay;
double volume;
} ChanParam;
typedef struct CompandSegment {
double x, y;
double a, b;
} CompandSegment;
typedef struct CompandContext {
const AVClass *class;
char *attacks, *decays, *points;
CompandSegment *segments;
ChanParam *channels;
double in_min_lin;
double out_min_lin;
double curve_dB;
double gain_dB;
double initial_volume;
double delay;
uint8_t **delayptrs;
int delay_samples;
int delay_count;
int delay_index;
int64_t pts;
int (*compand)(AVFilterContext *ctx, AVFrame *frame);
} CompandContext;
#define OFFSET(x) offsetof(CompandContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption compand_options[] = {
{ "attacks", "set time over which increase of volume is determined", OFFSET(attacks), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
{ "decays", "set time over which decrease of volume is determined", OFFSET(decays), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
{ "points", "set points of transfer function", OFFSET(points), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, A },
{ "soft-knee", "set soft-knee", OFFSET(curve_dB), AV_OPT_TYPE_DOUBLE, {.dbl=0.01}, 0.01, 900, A },
{ "gain", "set output gain", OFFSET(gain_dB), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 900, A },
{ "volume", "set initial volume", OFFSET(initial_volume), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -900, 0, A },
{ "delay", "set delay for samples before sending them to volume adjuster", OFFSET(delay), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 20, A },
{ NULL }
};
AVFILTER_DEFINE_CLASS(compand);
static av_cold int init(AVFilterContext *ctx)
{
CompandContext *s = ctx->priv;
if (!s->attacks || !s->decays || !s->points) {
av_log(ctx, AV_LOG_ERROR, "Missing attacks and/or decays and/or points.\n");
return AVERROR(EINVAL);
}
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
CompandContext *s = ctx->priv;
av_freep(&s->channels);
av_freep(&s->segments);
if (s->delayptrs)
av_freep(&s->delayptrs[0]);
av_freep(&s->delayptrs);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterChannelLayouts *layouts;
AVFilterFormats *formats;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
static void count_items(char *item_str, int *nb_items)
{
char *p;
*nb_items = 1;
for (p = item_str; *p; p++) {
if (*p == ' ')
(*nb_items)++;
}
}
static void update_volume(ChanParam *cp, double in)
{
double delta = in - cp->volume;
if (delta > 0.0)
cp->volume += delta * cp->attack;
else
cp->volume += delta * cp->decay;
}
static double get_volume(CompandContext *s, double in_lin)
{
CompandSegment *cs;
double in_log, out_log;
int i;
if (in_lin < s->in_min_lin)
return s->out_min_lin;
in_log = log(in_lin);
for (i = 1;; i++)
if (in_log <= s->segments[i + 1].x)
break;
cs = &s->segments[i];
in_log -= cs->x;
out_log = cs->y + in_log * (cs->a * in_log + cs->b);
return exp(out_log);
}
static int compand_nodelay(AVFilterContext *ctx, AVFrame *frame)
{
CompandContext *s = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
const int channels = inlink->channels;
const int nb_samples = frame->nb_samples;
AVFrame *out_frame;
int chan, i;
if (av_frame_is_writable(frame)) {
out_frame = frame;
} else {
out_frame = ff_get_audio_buffer(inlink, nb_samples);
if (!out_frame)
return AVERROR(ENOMEM);
av_frame_copy_props(out_frame, frame);
}
for (chan = 0; chan < channels; chan++) {
const double *src = (double *)frame->extended_data[chan];
double *dst = (double *)out_frame->extended_data[chan];
ChanParam *cp = &s->channels[chan];
for (i = 0; i < nb_samples; i++) {
update_volume(cp, fabs(src[i]));
dst[i] = av_clipd(src[i] * get_volume(s, cp->volume), -1, 1);
}
}
if (frame != out_frame)
av_frame_free(&frame);
return ff_filter_frame(ctx->outputs[0], out_frame);
}
#define MOD(a, b) (((a) >= (b)) ? (a) - (b) : (a))
static int compand_delay(AVFilterContext *ctx, AVFrame *frame)
{
CompandContext *s = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
const int channels = inlink->channels;
const int nb_samples = frame->nb_samples;
int chan, i, av_uninit(dindex), oindex, av_uninit(count);
AVFrame *out_frame = NULL;
av_assert1(channels > 0); /* would corrupt delay_count and delay_index */
for (chan = 0; chan < channels; chan++) {
const double *src = (double *)frame->extended_data[chan];
double *dbuf = (double *)s->delayptrs[chan];
ChanParam *cp = &s->channels[chan];
double *dst;
count = s->delay_count;
dindex = s->delay_index;
for (i = 0, oindex = 0; i < nb_samples; i++) {
const double in = src[i];
update_volume(cp, fabs(in));
if (count >= s->delay_samples) {
if (!out_frame) {
out_frame = ff_get_audio_buffer(inlink, nb_samples - i);
if (!out_frame)
return AVERROR(ENOMEM);
av_frame_copy_props(out_frame, frame);
out_frame->pts = s->pts;
s->pts += av_rescale_q(nb_samples - i, (AVRational){1, inlink->sample_rate}, inlink->time_base);
}
dst = (double *)out_frame->extended_data[chan];
dst[oindex++] = av_clipd(dbuf[dindex] * get_volume(s, cp->volume), -1, 1);
} else {
count++;
}
dbuf[dindex] = in;
dindex = MOD(dindex + 1, s->delay_samples);
}
}
s->delay_count = count;
s->delay_index = dindex;
av_frame_free(&frame);
return out_frame ? ff_filter_frame(ctx->outputs[0], out_frame) : 0;
}
static int compand_drain(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
CompandContext *s = ctx->priv;
const int channels = outlink->channels;
int chan, i, dindex;
AVFrame *frame = NULL;
frame = ff_get_audio_buffer(outlink, FFMIN(2048, s->delay_count));
if (!frame)
return AVERROR(ENOMEM);
frame->pts = s->pts;
s->pts += av_rescale_q(frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base);
for (chan = 0; chan < channels; chan++) {
double *dbuf = (double *)s->delayptrs[chan];
double *dst = (double *)frame->extended_data[chan];
ChanParam *cp = &s->channels[chan];
dindex = s->delay_index;
for (i = 0; i < frame->nb_samples; i++) {
dst[i] = av_clipd(dbuf[dindex] * get_volume(s, cp->volume), -1, 1);
dindex = MOD(dindex + 1, s->delay_samples);
}
}
s->delay_count -= frame->nb_samples;
s->delay_index = dindex;
return ff_filter_frame(outlink, frame);
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
CompandContext *s = ctx->priv;
const int sample_rate = outlink->sample_rate;
double radius = s->curve_dB * M_LN10 / 20;
int nb_attacks, nb_decays, nb_points;
char *p, *saveptr = NULL;
int new_nb_items, num;
int i;
count_items(s->attacks, &nb_attacks);
count_items(s->decays, &nb_decays);
count_items(s->points, &nb_points);
if ((nb_attacks > outlink->channels) || (nb_decays > outlink->channels)) {
av_log(ctx, AV_LOG_ERROR, "Number of attacks/decays bigger than number of channels.\n");
return AVERROR(EINVAL);
}
uninit(ctx);
s->channels = av_mallocz_array(outlink->channels, sizeof(*s->channels));
s->segments = av_mallocz_array((nb_points + 4) * 2, sizeof(*s->segments));
if (!s->channels || !s->segments)
return AVERROR(ENOMEM);
p = s->attacks;
for (i = 0, new_nb_items = 0; i < nb_attacks; i++) {
char *tstr = av_strtok(p, " ", &saveptr);
p = NULL;
new_nb_items += sscanf(tstr, "%lf", &s->channels[i].attack) == 1;
if (s->channels[i].attack < 0)
return AVERROR(EINVAL);
}
nb_attacks = new_nb_items;
p = s->decays;
for (i = 0, new_nb_items = 0; i < nb_decays; i++) {
char *tstr = av_strtok(p, " ", &saveptr);
p = NULL;
new_nb_items += sscanf(tstr, "%lf", &s->channels[i].decay) == 1;
if (s->channels[i].decay < 0)
return AVERROR(EINVAL);
}
nb_decays = new_nb_items;
if (nb_attacks != nb_decays) {
av_log(ctx, AV_LOG_ERROR, "Number of attacks %d differs from number of decays %d.\n", nb_attacks, nb_decays);
return AVERROR(EINVAL);
}
#define S(x) s->segments[2 * ((x) + 1)]
p = s->points;
for (i = 0, new_nb_items = 0; i < nb_points; i++) {
char *tstr = av_strtok(p, " ", &saveptr);
p = NULL;
if (sscanf(tstr, "%lf/%lf", &S(i).x, &S(i).y) != 2) {
av_log(ctx, AV_LOG_ERROR, "Invalid and/or missing input/output value.\n");
return AVERROR(EINVAL);
}
if (i && S(i - 1).x > S(i).x) {
av_log(ctx, AV_LOG_ERROR, "Transfer function input values must be increasing.\n");
return AVERROR(EINVAL);
}
S(i).y -= S(i).x;
av_log(ctx, AV_LOG_DEBUG, "%d: x=%f y=%f\n", i, S(i).x, S(i).y);
new_nb_items++;
}
num = new_nb_items;
/* Add 0,0 if necessary */
if (num == 0 || S(num - 1).x)
num++;
#undef S
#define S(x) s->segments[2 * (x)]
/* Add a tail off segment at the start */
S(0).x = S(1).x - 2 * s->curve_dB;
S(0).y = S(1).y;
num++;
/* Join adjacent colinear segments */
for (i = 2; i < num; i++) {
double g1 = (S(i - 1).y - S(i - 2).y) * (S(i - 0).x - S(i - 1).x);
double g2 = (S(i - 0).y - S(i - 1).y) * (S(i - 1).x - S(i - 2).x);
int j;
if (fabs(g1 - g2))
continue;
num--;
for (j = --i; j < num; j++)
S(j) = S(j + 1);
}
for (i = 0; !i || s->segments[i - 2].x; i += 2) {
s->segments[i].y += s->gain_dB;
s->segments[i].x *= M_LN10 / 20;
s->segments[i].y *= M_LN10 / 20;
}
#define L(x) s->segments[i - (x)]
for (i = 4; s->segments[i - 2].x; i += 2) {
double x, y, cx, cy, in1, in2, out1, out2, theta, len, r;
L(4).a = 0;
L(4).b = (L(2).y - L(4).y) / (L(2).x - L(4).x);
L(2).a = 0;
L(2).b = (L(0).y - L(2).y) / (L(0).x - L(2).x);
theta = atan2(L(2).y - L(4).y, L(2).x - L(4).x);
len = sqrt(pow(L(2).x - L(4).x, 2.) + pow(L(2).y - L(4).y, 2.));
r = FFMIN(radius, len);
L(3).x = L(2).x - r * cos(theta);
L(3).y = L(2).y - r * sin(theta);
theta = atan2(L(0).y - L(2).y, L(0).x - L(2).x);
len = sqrt(pow(L(0).x - L(2).x, 2.) + pow(L(0).y - L(2).y, 2.));
r = FFMIN(radius, len / 2);
x = L(2).x + r * cos(theta);
y = L(2).y + r * sin(theta);
cx = (L(3).x + L(2).x + x) / 3;
cy = (L(3).y + L(2).y + y) / 3;
L(2).x = x;
L(2).y = y;
in1 = cx - L(3).x;
out1 = cy - L(3).y;
in2 = L(2).x - L(3).x;
out2 = L(2).y - L(3).y;
L(3).a = (out2 / in2 - out1 / in1) / (in2-in1);
L(3).b = out1 / in1 - L(3).a * in1;
}
L(3).x = 0;
L(3).y = L(2).y;
s->in_min_lin = exp(s->segments[1].x);
s->out_min_lin = exp(s->segments[1].y);
for (i = 0; i < outlink->channels; i++) {
ChanParam *cp = &s->channels[i];
if (cp->attack > 1.0 / sample_rate)
cp->attack = 1.0 - exp(-1.0 / (sample_rate * cp->attack));
else
cp->attack = 1.0;
if (cp->decay > 1.0 / sample_rate)
cp->decay = 1.0 - exp(-1.0 / (sample_rate * cp->decay));
else
cp->decay = 1.0;
cp->volume = pow(10.0, s->initial_volume / 20);
}
s->delay_samples = s->delay * sample_rate;
if (s->delay_samples > 0) {
int ret;
if ((ret = av_samples_alloc_array_and_samples(&s->delayptrs, NULL,
outlink->channels,
s->delay_samples,
outlink->format, 0)) < 0)
return ret;
s->compand = compand_delay;
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
} else {
s->compand = compand_nodelay;
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
CompandContext *s = ctx->priv;
return s->compand(ctx, frame);
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
CompandContext *s = ctx->priv;
int ret;
ret = ff_request_frame(ctx->inputs[0]);
if (ret == AVERROR_EOF && !ctx->is_disabled && s->delay_count)
ret = compand_drain(outlink);
return ret;
}
static const AVFilterPad compand_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad compand_outputs[] = {
{
.name = "default",
.request_frame = request_frame,
.config_props = config_output,
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_compand = {
.name = "compand",
.description = NULL_IF_CONFIG_SMALL("Compress or expand audio dynamic range."),
.query_formats = query_formats,
.priv_size = sizeof(CompandContext),
.priv_class = &compand_class,
.init = init,
.uninit = uninit,
.inputs = compand_inputs,
.outputs = compand_outputs,
};
+172
View File
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2011 Mina Nagy Zaki
* Copyright (c) 2000 Edward Beingessner And Sundry Contributors.
* This source code is freely redistributable and may be used for any purpose.
* This copyright notice must be maintained. Edward Beingessner And Sundry
* Contributors are not responsible for the consequences of using this
* software.
*
* 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
* Stereo Widening Effect. Adds audio cues to move stereo image in
* front of the listener. Adapted from the libsox earwax effect.
*/
#include "libavutil/channel_layout.h"
#include "avfilter.h"
#include "audio.h"
#include "formats.h"
#define NUMTAPS 64
static const int8_t filt[NUMTAPS] = {
/* 30° 330° */
4, -6, /* 32 tap stereo FIR filter. */
4, -11, /* One side filters as if the */
-1, -5, /* signal was from 30 degrees */
3, 3, /* from the ear, the other as */
-2, 5, /* if 330 degrees. */
-5, 0,
9, 1,
6, 3, /* Input */
-4, -1, /* Left Right */
-5, -3, /* __________ __________ */
-2, -5, /* | | | | */
-7, 1, /* .---| Hh,0(f) | | Hh,0(f) |---. */
6, -7, /* / |__________| |__________| \ */
30, -29, /* / \ / \ */
12, -3, /* / X \ */
-11, 4, /* / / \ \ */
-3, 7, /* ____V_____ __________V V__________ _____V____ */
-20, 23, /* | | | | | | | | */
2, 0, /* | Hh,30(f) | | Hh,330(f)| | Hh,330(f)| | Hh,30(f) | */
1, -6, /* |__________| |__________| |__________| |__________| */
-14, -5, /* \ ___ / \ ___ / */
15, -18, /* \ / \ / _____ \ / \ / */
6, 7, /* `->| + |<--' / \ `-->| + |<-' */
15, -10, /* \___/ _/ \_ \___/ */
-14, 22, /* \ / \ / \ / */
-7, -2, /* `--->| | | |<---' */
-4, 9, /* \_/ \_/ */
6, -12, /* */
6, -6, /* Headphones */
0, -11,
0, -5,
4, 0};
typedef struct {
int16_t taps[NUMTAPS * 2];
} EarwaxContext;
static int query_formats(AVFilterContext *ctx)
{
static const int sample_rates[] = { 44100, -1 };
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layout = NULL;
ff_add_format(&formats, AV_SAMPLE_FMT_S16);
ff_set_common_formats(ctx, formats);
ff_add_channel_layout(&layout, AV_CH_LAYOUT_STEREO);
ff_set_common_channel_layouts(ctx, layout);
ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates));
return 0;
}
//FIXME: replace with DSPContext.scalarproduct_int16
static inline int16_t *scalarproduct(const int16_t *in, const int16_t *endin, int16_t *out)
{
int32_t sample;
int16_t j;
while (in < endin) {
sample = 0;
for (j = 0; j < NUMTAPS; j++)
sample += in[j] * filt[j];
*out = av_clip_int16(sample >> 6);
out++;
in++;
}
return out;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
AVFilterLink *outlink = inlink->dst->outputs[0];
int16_t *taps, *endin, *in, *out;
AVFrame *outsamples = ff_get_audio_buffer(inlink, insamples->nb_samples);
int len;
if (!outsamples) {
av_frame_free(&insamples);
return AVERROR(ENOMEM);
}
av_frame_copy_props(outsamples, insamples);
taps = ((EarwaxContext *)inlink->dst->priv)->taps;
out = (int16_t *)outsamples->data[0];
in = (int16_t *)insamples ->data[0];
len = FFMIN(NUMTAPS, 2*insamples->nb_samples);
// copy part of new input and process with saved input
memcpy(taps+NUMTAPS, in, len * sizeof(*taps));
out = scalarproduct(taps, taps + len, out);
// process current input
if (2*insamples->nb_samples >= NUMTAPS ){
endin = in + insamples->nb_samples * 2 - NUMTAPS;
scalarproduct(in, endin, out);
// save part of input for next round
memcpy(taps, endin, NUMTAPS * sizeof(*taps));
} else
memmove(taps, taps + 2*insamples->nb_samples, NUMTAPS * sizeof(*taps));
av_frame_free(&insamples);
return ff_filter_frame(outlink, outsamples);
}
static const AVFilterPad earwax_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad earwax_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_earwax = {
.name = "earwax",
.description = NULL_IF_CONFIG_SMALL("Widen the stereo image."),
.query_formats = query_formats,
.priv_size = sizeof(EarwaxContext),
.inputs = earwax_inputs,
.outputs = earwax_outputs,
};
+516
View File
@@ -0,0 +1,516 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Audio join filter
*
* Join multiple audio inputs as different channels in
* a single output
*/
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.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.
*/
AVFrame **input_frames;
/**
* Temporary storage for buffer references, for assembling the output frame.
*/
AVBufferRef **buffers;
} JoinContext;
#define OFFSET(x) offsetof(JoinContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption join_options[] = {
{ "inputs", "Number of input streams.", OFFSET(inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT_MAX, A|F },
{ "channel_layout", "Channel layout of the "
"output stream.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, A|F },
{ "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|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(join);
static int filter_frame(AVFilterLink *link, AVFrame *frame)
{
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] = frame;
return 0;
}
static int parse_maps(AVFilterContext *ctx)
{
JoinContext *s = ctx->priv;
char separator = '|';
char *cur = s->map;
#if FF_API_OLD_FILTER_OPTS
if (cur && strchr(cur, ',')) {
av_log(ctx, AV_LOG_WARNING, "This syntax is deprecated, use '|' to "
"separate the mappings.\n");
separator = ',';
}
#endif
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, separator);
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 av_cold int join_init(AVFilterContext *ctx)
{
JoinContext *s = ctx->priv;
int ret, i;
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);
return AVERROR(EINVAL);
}
s->nb_channels = av_get_channel_layout_nb_channels(s->channel_layout);
s->channels = av_mallocz(sizeof(*s->channels) * s->nb_channels);
s->buffers = av_mallocz(sizeof(*s->buffers) * s->nb_channels);
s->input_frames = av_mallocz(sizeof(*s->input_frames) * s->inputs);
if (!s->channels || !s->buffers|| !s->input_frames)
return AVERROR(ENOMEM);
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)
return ret;
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_frame = filter_frame;
pad.needs_fifo = 1;
ff_insert_inpad(ctx, i, &pad);
}
return 0;
}
static av_cold 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);
av_frame_free(&s->input_frames[i]);
}
av_freep(&s->channels);
av_freep(&s->buffers);
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 int join_request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
JoinContext *s = ctx->priv;
AVFrame *frame;
int linesize = INT_MAX;
int nb_samples = 0;
int nb_buffers = 0;
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]->nb_samples;
for (j = 1; !i && j < ctx->nb_inputs; j++)
ctx->inputs[j]->request_samples = nb_samples;
}
}
/* setup the output frame */
frame = av_frame_alloc();
if (!frame)
return AVERROR(ENOMEM);
if (s->nb_channels > FF_ARRAY_ELEMS(frame->data)) {
frame->extended_data = av_mallocz(s->nb_channels *
sizeof(*frame->extended_data));
if (!frame->extended_data) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
/* copy the data pointers */
for (i = 0; i < s->nb_channels; i++) {
ChannelMap *ch = &s->channels[i];
AVFrame *cur = s->input_frames[ch->input];
AVBufferRef *buf;
frame->extended_data[i] = cur->extended_data[ch->in_channel_idx];
linesize = FFMIN(linesize, cur->linesize[0]);
/* add the buffer where this plan is stored to the list if it's
* not already there */
buf = av_frame_get_plane_buffer(cur, ch->in_channel_idx);
if (!buf) {
ret = AVERROR(EINVAL);
goto fail;
}
for (j = 0; j < nb_buffers; j++)
if (s->buffers[j]->buffer == buf->buffer)
break;
if (j == i)
s->buffers[nb_buffers++] = buf;
}
/* create references to the buffers we copied to output */
if (nb_buffers > FF_ARRAY_ELEMS(frame->buf)) {
frame->nb_extended_buf = nb_buffers - FF_ARRAY_ELEMS(frame->buf);
frame->extended_buf = av_mallocz(sizeof(*frame->extended_buf) *
frame->nb_extended_buf);
if (!frame->extended_buf) {
frame->nb_extended_buf = 0;
ret = AVERROR(ENOMEM);
goto fail;
}
}
for (i = 0; i < FFMIN(FF_ARRAY_ELEMS(frame->buf), nb_buffers); i++) {
frame->buf[i] = av_buffer_ref(s->buffers[i]);
if (!frame->buf[i]) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
for (i = 0; i < frame->nb_extended_buf; i++) {
frame->extended_buf[i] = av_buffer_ref(s->buffers[i +
FF_ARRAY_ELEMS(frame->buf)]);
if (!frame->extended_buf[i]) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
frame->nb_samples = nb_samples;
frame->channel_layout = outlink->channel_layout;
av_frame_set_channels(frame, outlink->channels);
frame->format = outlink->format;
frame->sample_rate = outlink->sample_rate;
frame->pts = s->input_frames[0]->pts;
frame->linesize[0] = linesize;
if (frame->data != frame->extended_data) {
memcpy(frame->data, frame->extended_data, sizeof(*frame->data) *
FFMIN(FF_ARRAY_ELEMS(frame->data), s->nb_channels));
}
ret = ff_filter_frame(outlink, frame);
for (i = 0; i < ctx->nb_inputs; i++)
av_frame_free(&s->input_frames[i]);
return ret;
fail:
av_frame_free(&frame);
return ret;
}
static const AVFilterPad avfilter_af_join_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = join_config_output,
.request_frame = join_request_frame,
},
{ NULL }
};
AVFilter avfilter_af_join = {
.name = "join",
.description = NULL_IF_CONFIG_SMALL("Join multiple audio streams into "
"multi-channel output."),
.priv_size = sizeof(JoinContext),
.priv_class = &join_class,
.init = join_init,
.uninit = join_uninit,
.query_formats = join_query_formats,
.inputs = NULL,
.outputs = avfilter_af_join_outputs,
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
};
+703
View File
@@ -0,0 +1,703 @@
/*
* Copyright (c) 2013 Paul B Mahol
* Copyright (c) 2011 Mina Nagy Zaki
*
* 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
* LADSPA wrapper
*/
#include <dlfcn.h>
#include <ladspa.h>
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct LADSPAContext {
const AVClass *class;
char *dl_name;
char *plugin;
char *options;
void *dl_handle;
unsigned long nb_inputs;
unsigned long *ipmap; /* map input number to port number */
unsigned long nb_inputcontrols;
unsigned long *icmap; /* map input control number to port number */
LADSPA_Data *ictlv; /* input controls values */
unsigned long nb_outputs;
unsigned long *opmap; /* map output number to port number */
unsigned long nb_outputcontrols;
unsigned long *ocmap; /* map output control number to port number */
LADSPA_Data *octlv; /* output controls values */
const LADSPA_Descriptor *desc;
int *ctl_needs_value;
int nb_handles;
LADSPA_Handle *handles;
int sample_rate;
int nb_samples;
int64_t pts;
int64_t duration;
} LADSPAContext;
#define OFFSET(x) offsetof(LADSPAContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM
static const AVOption ladspa_options[] = {
{ "file", "set library name or full path", OFFSET(dl_name), AV_OPT_TYPE_STRING, .flags = FLAGS },
{ "f", "set library name or full path", OFFSET(dl_name), AV_OPT_TYPE_STRING, .flags = FLAGS },
{ "plugin", "set plugin name", OFFSET(plugin), AV_OPT_TYPE_STRING, .flags = FLAGS },
{ "p", "set plugin name", OFFSET(plugin), AV_OPT_TYPE_STRING, .flags = FLAGS },
{ "controls", "set plugin options", OFFSET(options), AV_OPT_TYPE_STRING, .flags = FLAGS },
{ "c", "set plugin options", OFFSET(options), AV_OPT_TYPE_STRING, .flags = FLAGS },
{ "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT32_MAX, FLAGS },
{ "s", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT32_MAX, FLAGS },
{ "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
{ "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS },
{ "duration", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=-1}, -1, INT64_MAX, FLAGS },
{ "d", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=-1}, -1, INT64_MAX, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(ladspa);
static void print_ctl_info(AVFilterContext *ctx, int level,
LADSPAContext *s, int ctl, unsigned long *map,
LADSPA_Data *values, int print)
{
const LADSPA_PortRangeHint *h = s->desc->PortRangeHints + map[ctl];
av_log(ctx, level, "c%i: %s [", ctl, s->desc->PortNames[map[ctl]]);
if (LADSPA_IS_HINT_TOGGLED(h->HintDescriptor)) {
av_log(ctx, level, "toggled (1 or 0)");
if (LADSPA_IS_HINT_HAS_DEFAULT(h->HintDescriptor))
av_log(ctx, level, " (default %i)", (int)values[ctl]);
} else {
if (LADSPA_IS_HINT_INTEGER(h->HintDescriptor)) {
av_log(ctx, level, "<int>");
if (LADSPA_IS_HINT_BOUNDED_BELOW(h->HintDescriptor))
av_log(ctx, level, ", min: %i", (int)h->LowerBound);
if (LADSPA_IS_HINT_BOUNDED_ABOVE(h->HintDescriptor))
av_log(ctx, level, ", max: %i", (int)h->UpperBound);
if (print)
av_log(ctx, level, " (value %d)", (int)values[ctl]);
else if (LADSPA_IS_HINT_HAS_DEFAULT(h->HintDescriptor))
av_log(ctx, level, " (default %d)", (int)values[ctl]);
} else {
av_log(ctx, level, "<float>");
if (LADSPA_IS_HINT_BOUNDED_BELOW(h->HintDescriptor))
av_log(ctx, level, ", min: %f", h->LowerBound);
if (LADSPA_IS_HINT_BOUNDED_ABOVE(h->HintDescriptor))
av_log(ctx, level, ", max: %f", h->UpperBound);
if (print)
av_log(ctx, level, " (value %f)", values[ctl]);
else if (LADSPA_IS_HINT_HAS_DEFAULT(h->HintDescriptor))
av_log(ctx, level, " (default %f)", values[ctl]);
}
if (LADSPA_IS_HINT_SAMPLE_RATE(h->HintDescriptor))
av_log(ctx, level, ", multiple of sample rate");
if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
av_log(ctx, level, ", logarithmic scale");
}
av_log(ctx, level, "]\n");
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
LADSPAContext *s = ctx->priv;
AVFrame *out;
int i, h;
if (!s->nb_outputs ||
(av_frame_is_writable(in) && s->nb_inputs == s->nb_outputs &&
!(s->desc->Properties & LADSPA_PROPERTY_INPLACE_BROKEN))) {
out = in;
} else {
out = ff_get_audio_buffer(ctx->outputs[0], in->nb_samples);
if (!out) {
av_frame_free(&in);
return AVERROR(ENOMEM);
}
av_frame_copy_props(out, in);
}
for (h = 0; h < s->nb_handles; h++) {
for (i = 0; i < s->nb_inputs; i++) {
s->desc->connect_port(s->handles[h], s->ipmap[i],
(LADSPA_Data*)in->extended_data[i]);
}
for (i = 0; i < s->nb_outputs; i++) {
s->desc->connect_port(s->handles[h], s->opmap[i],
(LADSPA_Data*)out->extended_data[i]);
}
s->desc->run(s->handles[h], in->nb_samples);
}
for (i = 0; i < s->nb_outputcontrols; i++)
print_ctl_info(ctx, AV_LOG_VERBOSE, s, i, s->ocmap, s->octlv, 1);
if (out != in)
av_frame_free(&in);
return ff_filter_frame(ctx->outputs[0], out);
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
LADSPAContext *s = ctx->priv;
AVFrame *out;
int64_t t;
int i;
if (ctx->nb_inputs)
return ff_request_frame(ctx->inputs[0]);
t = av_rescale(s->pts, AV_TIME_BASE, s->sample_rate);
if (s->duration >= 0 && t >= s->duration)
return AVERROR_EOF;
out = ff_get_audio_buffer(outlink, s->nb_samples);
if (!out)
return AVERROR(ENOMEM);
for (i = 0; i < s->nb_outputs; i++)
s->desc->connect_port(s->handles[0], s->opmap[i],
(LADSPA_Data*)out->extended_data[i]);
s->desc->run(s->handles[0], s->nb_samples);
for (i = 0; i < s->nb_outputcontrols; i++)
print_ctl_info(ctx, AV_LOG_INFO, s, i, s->ocmap, s->octlv, 1);
out->sample_rate = s->sample_rate;
out->pts = s->pts;
s->pts += s->nb_samples;
return ff_filter_frame(outlink, out);
}
static void set_default_ctl_value(LADSPAContext *s, int ctl,
unsigned long *map, LADSPA_Data *values)
{
const LADSPA_PortRangeHint *h = s->desc->PortRangeHints + map[ctl];
const LADSPA_Data lower = h->LowerBound;
const LADSPA_Data upper = h->UpperBound;
if (LADSPA_IS_HINT_DEFAULT_MINIMUM(h->HintDescriptor)) {
values[ctl] = lower;
} else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(h->HintDescriptor)) {
values[ctl] = upper;
} else if (LADSPA_IS_HINT_DEFAULT_0(h->HintDescriptor)) {
values[ctl] = 0.0;
} else if (LADSPA_IS_HINT_DEFAULT_1(h->HintDescriptor)) {
values[ctl] = 1.0;
} else if (LADSPA_IS_HINT_DEFAULT_100(h->HintDescriptor)) {
values[ctl] = 100.0;
} else if (LADSPA_IS_HINT_DEFAULT_440(h->HintDescriptor)) {
values[ctl] = 440.0;
} else if (LADSPA_IS_HINT_DEFAULT_LOW(h->HintDescriptor)) {
if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
values[ctl] = exp(log(lower) * 0.75 + log(upper) * 0.25);
else
values[ctl] = lower * 0.75 + upper * 0.25;
} else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(h->HintDescriptor)) {
if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
values[ctl] = exp(log(lower) * 0.5 + log(upper) * 0.5);
else
values[ctl] = lower * 0.5 + upper * 0.5;
} else if (LADSPA_IS_HINT_DEFAULT_HIGH(h->HintDescriptor)) {
if (LADSPA_IS_HINT_LOGARITHMIC(h->HintDescriptor))
values[ctl] = exp(log(lower) * 0.25 + log(upper) * 0.75);
else
values[ctl] = lower * 0.25 + upper * 0.75;
}
}
static int connect_ports(AVFilterContext *ctx, AVFilterLink *link)
{
LADSPAContext *s = ctx->priv;
int i, j;
s->nb_handles = s->nb_inputs == 1 && s->nb_outputs == 1 ? link->channels : 1;
s->handles = av_calloc(s->nb_handles, sizeof(*s->handles));
if (!s->handles)
return AVERROR(ENOMEM);
for (i = 0; i < s->nb_handles; i++) {
s->handles[i] = s->desc->instantiate(s->desc, link->sample_rate);
if (!s->handles[i]) {
av_log(ctx, AV_LOG_ERROR, "Could not instantiate plugin.\n");
return AVERROR_EXTERNAL;
}
// Connect the input control ports
for (j = 0; j < s->nb_inputcontrols; j++)
s->desc->connect_port(s->handles[i], s->icmap[j], s->ictlv + j);
// Connect the output control ports
for (j = 0; j < s->nb_outputcontrols; j++)
s->desc->connect_port(s->handles[i], s->ocmap[j], &s->octlv[j]);
if (s->desc->activate)
s->desc->activate(s->handles[i]);
}
av_log(ctx, AV_LOG_DEBUG, "handles: %d\n", s->nb_handles);
return 0;
}
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
return connect_ports(ctx, inlink);
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
int ret;
if (ctx->nb_inputs) {
AVFilterLink *inlink = ctx->inputs[0];
outlink->format = inlink->format;
outlink->sample_rate = inlink->sample_rate;
ret = 0;
} else {
LADSPAContext *s = ctx->priv;
outlink->sample_rate = s->sample_rate;
outlink->time_base = (AVRational){1, s->sample_rate};
ret = connect_ports(ctx, outlink);
}
return ret;
}
static void count_ports(const LADSPA_Descriptor *desc,
unsigned long *nb_inputs, unsigned long *nb_outputs)
{
LADSPA_PortDescriptor pd;
int i;
for (i = 0; i < desc->PortCount; i++) {
pd = desc->PortDescriptors[i];
if (LADSPA_IS_PORT_AUDIO(pd)) {
if (LADSPA_IS_PORT_INPUT(pd)) {
(*nb_inputs)++;
} else if (LADSPA_IS_PORT_OUTPUT(pd)) {
(*nb_outputs)++;
}
}
}
}
static void *try_load(const char *dir, const char *soname)
{
char *path = av_asprintf("%s/%s.so", dir, soname);
void *ret = NULL;
if (path) {
ret = dlopen(path, RTLD_LOCAL|RTLD_NOW);
av_free(path);
}
return ret;
}
static int set_control(AVFilterContext *ctx, unsigned long port, LADSPA_Data value)
{
LADSPAContext *s = ctx->priv;
const char *label = s->desc->Label;
LADSPA_PortRangeHint *h = (LADSPA_PortRangeHint *)s->desc->PortRangeHints +
s->icmap[port];
if (port >= s->nb_inputcontrols) {
av_log(ctx, AV_LOG_ERROR, "Control c%ld is out of range [0 - %lu].\n",
port, s->nb_inputcontrols);
return AVERROR(EINVAL);
}
if (LADSPA_IS_HINT_BOUNDED_BELOW(h->HintDescriptor) &&
value < h->LowerBound) {
av_log(ctx, AV_LOG_ERROR,
"%s: input control c%ld is below lower boundary of %0.4f.\n",
label, port, h->LowerBound);
return AVERROR(EINVAL);
}
if (LADSPA_IS_HINT_BOUNDED_ABOVE(h->HintDescriptor) &&
value > h->UpperBound) {
av_log(ctx, AV_LOG_ERROR,
"%s: input control c%ld is above upper boundary of %0.4f.\n",
label, port, h->UpperBound);
return AVERROR(EINVAL);
}
s->ictlv[port] = value;
return 0;
}
static av_cold int init(AVFilterContext *ctx)
{
LADSPAContext *s = ctx->priv;
LADSPA_Descriptor_Function descriptor_fn;
const LADSPA_Descriptor *desc;
LADSPA_PortDescriptor pd;
AVFilterPad pad = { NULL };
char *p, *arg, *saveptr = NULL;
unsigned long nb_ports;
int i;
if (!s->dl_name) {
av_log(ctx, AV_LOG_ERROR, "No plugin name provided\n");
return AVERROR(EINVAL);
}
if (s->dl_name[0] == '/' || s->dl_name[0] == '.') {
// argument is a path
s->dl_handle = dlopen(s->dl_name, RTLD_LOCAL|RTLD_NOW);
} else {
// argument is a shared object name
char *paths = av_strdup(getenv("LADSPA_PATH"));
const char *separator = ":";
if (paths) {
p = paths;
while ((arg = av_strtok(p, separator, &saveptr)) && !s->dl_handle) {
s->dl_handle = try_load(arg, s->dl_name);
p = NULL;
}
}
av_free(paths);
if (!s->dl_handle && (paths = av_asprintf("%s/.ladspa/lib", getenv("HOME")))) {
s->dl_handle = try_load(paths, s->dl_name);
av_free(paths);
}
if (!s->dl_handle)
s->dl_handle = try_load("/usr/local/lib/ladspa", s->dl_name);
if (!s->dl_handle)
s->dl_handle = try_load("/usr/lib/ladspa", s->dl_name);
}
if (!s->dl_handle) {
av_log(ctx, AV_LOG_ERROR, "Failed to load '%s'\n", s->dl_name);
return AVERROR(EINVAL);
}
descriptor_fn = dlsym(s->dl_handle, "ladspa_descriptor");
if (!descriptor_fn) {
av_log(ctx, AV_LOG_ERROR, "Could not find ladspa_descriptor: %s\n", dlerror());
return AVERROR(EINVAL);
}
// Find the requested plugin, or list plugins
if (!s->plugin) {
av_log(ctx, AV_LOG_INFO, "The '%s' library contains the following plugins:\n", s->dl_name);
av_log(ctx, AV_LOG_INFO, "I = Input Channels\n");
av_log(ctx, AV_LOG_INFO, "O = Output Channels\n");
av_log(ctx, AV_LOG_INFO, "I:O %-25s %s\n", "Plugin", "Description");
av_log(ctx, AV_LOG_INFO, "\n");
for (i = 0; desc = descriptor_fn(i); i++) {
unsigned long inputs = 0, outputs = 0;
count_ports(desc, &inputs, &outputs);
av_log(ctx, AV_LOG_INFO, "%lu:%lu %-25s %s\n", inputs, outputs, desc->Label,
av_x_if_null(desc->Name, "?"));
av_log(ctx, AV_LOG_VERBOSE, "Maker: %s\n", av_x_if_null(desc->Maker, "?"));
av_log(ctx, AV_LOG_VERBOSE, "Copyright: %s\n", av_x_if_null(desc->Copyright, "?"));
}
return AVERROR_EXIT;
} else {
for (i = 0;; i++) {
desc = descriptor_fn(i);
if (!desc) {
av_log(ctx, AV_LOG_ERROR, "Could not find plugin: %s\n", s->plugin);
return AVERROR(EINVAL);
}
if (desc->Label && !strcmp(desc->Label, s->plugin))
break;
}
}
s->desc = desc;
nb_ports = desc->PortCount;
s->ipmap = av_calloc(nb_ports, sizeof(*s->ipmap));
s->opmap = av_calloc(nb_ports, sizeof(*s->opmap));
s->icmap = av_calloc(nb_ports, sizeof(*s->icmap));
s->ocmap = av_calloc(nb_ports, sizeof(*s->ocmap));
s->ictlv = av_calloc(nb_ports, sizeof(*s->ictlv));
s->octlv = av_calloc(nb_ports, sizeof(*s->octlv));
s->ctl_needs_value = av_calloc(nb_ports, sizeof(*s->ctl_needs_value));
if (!s->ipmap || !s->opmap || !s->icmap ||
!s->ocmap || !s->ictlv || !s->octlv || !s->ctl_needs_value)
return AVERROR(ENOMEM);
for (i = 0; i < nb_ports; i++) {
pd = desc->PortDescriptors[i];
if (LADSPA_IS_PORT_AUDIO(pd)) {
if (LADSPA_IS_PORT_INPUT(pd)) {
s->ipmap[s->nb_inputs] = i;
s->nb_inputs++;
} else if (LADSPA_IS_PORT_OUTPUT(pd)) {
s->opmap[s->nb_outputs] = i;
s->nb_outputs++;
}
} else if (LADSPA_IS_PORT_CONTROL(pd)) {
if (LADSPA_IS_PORT_INPUT(pd)) {
s->icmap[s->nb_inputcontrols] = i;
if (LADSPA_IS_HINT_HAS_DEFAULT(desc->PortRangeHints[i].HintDescriptor))
set_default_ctl_value(s, s->nb_inputcontrols, s->icmap, s->ictlv);
else
s->ctl_needs_value[s->nb_inputcontrols] = 1;
s->nb_inputcontrols++;
} else if (LADSPA_IS_PORT_OUTPUT(pd)) {
s->ocmap[s->nb_outputcontrols] = i;
s->nb_outputcontrols++;
}
}
}
// List Control Ports if "help" is specified
if (s->options && !strcmp(s->options, "help")) {
if (!s->nb_inputcontrols) {
av_log(ctx, AV_LOG_INFO,
"The '%s' plugin does not have any input controls.\n",
desc->Label);
} else {
av_log(ctx, AV_LOG_INFO,
"The '%s' plugin has the following input controls:\n",
desc->Label);
for (i = 0; i < s->nb_inputcontrols; i++)
print_ctl_info(ctx, AV_LOG_INFO, s, i, s->icmap, s->ictlv, 0);
}
return AVERROR_EXIT;
}
// Parse control parameters
p = s->options;
while (s->options) {
LADSPA_Data val;
int ret;
if (!(arg = av_strtok(p, "|", &saveptr)))
break;
p = NULL;
if (sscanf(arg, "c%d=%f", &i, &val) != 2) {
av_log(ctx, AV_LOG_ERROR, "Invalid syntax.\n");
return AVERROR(EINVAL);
}
if ((ret = set_control(ctx, i, val)) < 0)
return ret;
s->ctl_needs_value[i] = 0;
}
// Check if any controls are not set
for (i = 0; i < s->nb_inputcontrols; i++) {
if (s->ctl_needs_value[i]) {
av_log(ctx, AV_LOG_ERROR, "Control c%d must be set.\n", i);
print_ctl_info(ctx, AV_LOG_ERROR, s, i, s->icmap, s->ictlv, 0);
return AVERROR(EINVAL);
}
}
pad.type = AVMEDIA_TYPE_AUDIO;
if (s->nb_inputs) {
pad.name = av_asprintf("in0:%s%lu", desc->Label, s->nb_inputs);
if (!pad.name)
return AVERROR(ENOMEM);
pad.filter_frame = filter_frame;
pad.config_props = config_input;
if (ff_insert_inpad(ctx, ctx->nb_inputs, &pad) < 0) {
av_freep(&pad.name);
return AVERROR(ENOMEM);
}
}
av_log(ctx, AV_LOG_DEBUG, "ports: %lu\n", nb_ports);
av_log(ctx, AV_LOG_DEBUG, "inputs: %lu outputs: %lu\n",
s->nb_inputs, s->nb_outputs);
av_log(ctx, AV_LOG_DEBUG, "input controls: %lu output controls: %lu\n",
s->nb_inputcontrols, s->nb_outputcontrols);
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
LADSPAContext *s = ctx->priv;
AVFilterFormats *formats;
AVFilterChannelLayouts *layouts;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE };
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
if (s->nb_inputs) {
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
} else {
int sample_rates[] = { s->sample_rate, -1 };
ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates));
}
if (s->nb_inputs == 1 && s->nb_outputs == 1) {
// We will instantiate multiple LADSPA_Handle, one over each channel
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
} else {
AVFilterLink *outlink = ctx->outputs[0];
if (s->nb_inputs >= 1) {
AVFilterLink *inlink = ctx->inputs[0];
int64_t inlayout = FF_COUNT2LAYOUT(s->nb_inputs);
layouts = NULL;
ff_add_channel_layout(&layouts, inlayout);
ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
if (!s->nb_outputs)
ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
}
if (s->nb_outputs >= 1) {
int64_t outlayout = FF_COUNT2LAYOUT(s->nb_outputs);
layouts = NULL;
ff_add_channel_layout(&layouts, outlayout);
ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
}
}
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
LADSPAContext *s = ctx->priv;
int i;
for (i = 0; i < s->nb_handles; i++) {
if (s->desc->deactivate)
s->desc->deactivate(s->handles[i]);
if (s->desc->cleanup)
s->desc->cleanup(s->handles[i]);
}
if (s->dl_handle)
dlclose(s->dl_handle);
av_freep(&s->ipmap);
av_freep(&s->opmap);
av_freep(&s->icmap);
av_freep(&s->ocmap);
av_freep(&s->ictlv);
av_freep(&s->octlv);
av_freep(&s->handles);
av_freep(&s->ctl_needs_value);
if (ctx->nb_inputs)
av_freep(&ctx->input_pads[0].name);
}
static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
char *res, int res_len, int flags)
{
LADSPA_Data value;
unsigned long port;
if (sscanf(cmd, "c%ld", &port) + sscanf(args, "%f", &value) != 2)
return AVERROR(EINVAL);
return set_control(ctx, port, value);
}
static const AVFilterPad ladspa_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_af_ladspa = {
.name = "ladspa",
.description = NULL_IF_CONFIG_SMALL("Apply LADSPA effect."),
.priv_size = sizeof(LADSPAContext),
.priv_class = &ladspa_class,
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.process_command = process_command,
.inputs = 0,
.outputs = ladspa_outputs,
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
};
+426
View File
@@ -0,0 +1,426 @@
/*
* Copyright (c) 2002 Anders Johansson <[email protected]>
* Copyright (c) 2011 Clément Bœsch <u pkh me>
* Copyright (c) 2011 Nicolas George <[email protected]>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Audio panning filter (channels mixing)
* Original code written by Anders Johansson for MPlayer,
* reimplemented for FFmpeg.
*/
#include <stdio.h>
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h"
#include "audio.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
#define MAX_CHANNELS 63
typedef struct PanContext {
const AVClass *class;
char *args;
int64_t out_channel_layout;
double gain[MAX_CHANNELS][MAX_CHANNELS];
int64_t need_renorm;
int need_renumber;
int nb_output_channels;
int pure_gains;
/* channel mapping specific */
int channel_map[SWR_CH_MAX];
struct SwrContext *swr;
} PanContext;
static void skip_spaces(char **arg)
{
int len = 0;
sscanf(*arg, " %n", &len);
*arg += len;
}
static int parse_channel_name(char **arg, int *rchannel, int *rnamed)
{
char buf[8];
int len, i, channel_id = 0;
int64_t layout, layout0;
skip_spaces(arg);
/* try to parse a channel name, e.g. "FL" */
if (sscanf(*arg, "%7[A-Z]%n", buf, &len)) {
layout0 = layout = av_get_channel_layout(buf);
/* channel_id <- first set bit in layout */
for (i = 32; i > 0; i >>= 1) {
if (layout >= (int64_t)1 << i) {
channel_id += i;
layout >>= i;
}
}
/* reject layouts that are not a single channel */
if (channel_id >= MAX_CHANNELS || layout0 != (int64_t)1 << channel_id)
return AVERROR(EINVAL);
*rchannel = channel_id;
*rnamed = 1;
*arg += len;
return 0;
}
/* try to parse a channel number, e.g. "c2" */
if (sscanf(*arg, "c%d%n", &channel_id, &len) &&
channel_id >= 0 && channel_id < MAX_CHANNELS) {
*rchannel = channel_id;
*rnamed = 0;
*arg += len;
return 0;
}
return AVERROR(EINVAL);
}
static av_cold int init(AVFilterContext *ctx)
{
PanContext *const pan = ctx->priv;
char *arg, *arg0, *tokenizer, *args = av_strdup(pan->args);
int out_ch_id, in_ch_id, len, named, ret;
int nb_in_channels[2] = { 0, 0 }; // number of unnamed and named input channels
double gain;
if (!pan->args) {
av_log(ctx, AV_LOG_ERROR,
"pan filter needs a channel layout and a set "
"of channels definitions as parameter\n");
return AVERROR(EINVAL);
}
if (!args)
return AVERROR(ENOMEM);
arg = av_strtok(args, "|", &tokenizer);
ret = ff_parse_channel_layout(&pan->out_channel_layout,
&pan->nb_output_channels, arg, ctx);
if (ret < 0)
goto fail;
/* parse channel specifications */
while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) {
/* channel name */
if (parse_channel_name(&arg, &out_ch_id, &named)) {
av_log(ctx, AV_LOG_ERROR,
"Expected out channel name, got \"%.8s\"\n", arg);
ret = AVERROR(EINVAL);
goto fail;
}
if (named) {
if (!((pan->out_channel_layout >> out_ch_id) & 1)) {
av_log(ctx, AV_LOG_ERROR,
"Channel \"%.8s\" does not exist in the chosen layout\n", arg0);
ret = AVERROR(EINVAL);
goto fail;
}
/* get the channel number in the output channel layout:
* out_channel_layout & ((1 << out_ch_id) - 1) are all the
* channels that come before out_ch_id,
* so their count is the index of out_ch_id */
out_ch_id = av_get_channel_layout_nb_channels(pan->out_channel_layout & (((int64_t)1 << out_ch_id) - 1));
}
if (out_ch_id < 0 || out_ch_id >= pan->nb_output_channels) {
av_log(ctx, AV_LOG_ERROR,
"Invalid out channel name \"%.8s\"\n", arg0);
ret = AVERROR(EINVAL);
goto fail;
}
skip_spaces(&arg);
if (*arg == '=') {
arg++;
} else if (*arg == '<') {
pan->need_renorm |= (int64_t)1 << out_ch_id;
arg++;
} else {
av_log(ctx, AV_LOG_ERROR,
"Syntax error after channel name in \"%.8s\"\n", arg0);
ret = AVERROR(EINVAL);
goto fail;
}
/* gains */
while (1) {
gain = 1;
if (sscanf(arg, "%lf%n *%n", &gain, &len, &len))
arg += len;
if (parse_channel_name(&arg, &in_ch_id, &named)){
av_log(ctx, AV_LOG_ERROR,
"Expected in channel name, got \"%.8s\"\n", arg);
ret = AVERROR(EINVAL);
goto fail;
}
nb_in_channels[named]++;
if (nb_in_channels[!named]) {
av_log(ctx, AV_LOG_ERROR,
"Can not mix named and numbered channels\n");
ret = AVERROR(EINVAL);
goto fail;
}
pan->gain[out_ch_id][in_ch_id] = gain;
skip_spaces(&arg);
if (!*arg)
break;
if (*arg != '+') {
av_log(ctx, AV_LOG_ERROR, "Syntax error near \"%.8s\"\n", arg);
ret = AVERROR(EINVAL);
goto fail;
}
arg++;
}
}
pan->need_renumber = !!nb_in_channels[1];
ret = 0;
fail:
av_free(args);
return ret;
}
static int are_gains_pure(const PanContext *pan)
{
int i, j;
for (i = 0; i < MAX_CHANNELS; i++) {
int nb_gain = 0;
for (j = 0; j < MAX_CHANNELS; j++) {
double gain = pan->gain[i][j];
/* channel mapping is effective only if 0% or 100% of a channel is
* selected... */
if (gain != 0. && gain != 1.)
return 0;
/* ...and if the output channel is only composed of one input */
if (gain && nb_gain++)
return 0;
}
}
return 1;
}
static int query_formats(AVFilterContext *ctx)
{
PanContext *pan = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layouts;
pan->pure_gains = are_gains_pure(pan);
/* libswr supports any sample and packing formats */
ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO));
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
// inlink supports any channel layout
layouts = ff_all_channel_counts();
ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
// outlink supports only requested output channel layout
layouts = NULL;
ff_add_channel_layout(&layouts,
pan->out_channel_layout ? pan->out_channel_layout :
FF_COUNT2LAYOUT(pan->nb_output_channels));
ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
return 0;
}
static int config_props(AVFilterLink *link)
{
AVFilterContext *ctx = link->dst;
PanContext *pan = ctx->priv;
char buf[1024], *cur;
int i, j, k, r;
double t;
if (pan->need_renumber) {
// input channels were given by their name: renumber them
for (i = j = 0; i < MAX_CHANNELS; i++) {
if ((link->channel_layout >> i) & 1) {
for (k = 0; k < pan->nb_output_channels; k++)
pan->gain[k][j] = pan->gain[k][i];
j++;
}
}
}
// sanity check; can't be done in query_formats since the inlink
// channel layout is unknown at that time
if (link->channels > SWR_CH_MAX ||
pan->nb_output_channels > SWR_CH_MAX) {
av_log(ctx, AV_LOG_ERROR,
"libswresample support a maximum of %d channels. "
"Feel free to ask for a higher limit.\n", SWR_CH_MAX);
return AVERROR_PATCHWELCOME;
}
// init libswresample context
pan->swr = swr_alloc_set_opts(pan->swr,
pan->out_channel_layout, link->format, link->sample_rate,
link->channel_layout, link->format, link->sample_rate,
0, ctx);
if (!pan->swr)
return AVERROR(ENOMEM);
if (!link->channel_layout)
av_opt_set_int(pan->swr, "ich", link->channels, 0);
if (!pan->out_channel_layout)
av_opt_set_int(pan->swr, "och", pan->nb_output_channels, 0);
// gains are pure, init the channel mapping
if (pan->pure_gains) {
// get channel map from the pure gains
for (i = 0; i < pan->nb_output_channels; i++) {
int ch_id = -1;
for (j = 0; j < link->channels; j++) {
if (pan->gain[i][j]) {
ch_id = j;
break;
}
}
pan->channel_map[i] = ch_id;
}
av_opt_set_int(pan->swr, "icl", pan->out_channel_layout, 0);
av_opt_set_int(pan->swr, "uch", pan->nb_output_channels, 0);
swr_set_channel_mapping(pan->swr, pan->channel_map);
} else {
// renormalize
for (i = 0; i < pan->nb_output_channels; i++) {
if (!((pan->need_renorm >> i) & 1))
continue;
t = 0;
for (j = 0; j < link->channels; j++)
t += pan->gain[i][j];
if (t > -1E-5 && t < 1E-5) {
// t is almost 0 but not exactly, this is probably a mistake
if (t)
av_log(ctx, AV_LOG_WARNING,
"Degenerate coefficients while renormalizing\n");
continue;
}
for (j = 0; j < link->channels; j++)
pan->gain[i][j] /= t;
}
av_opt_set_int(pan->swr, "icl", link->channel_layout, 0);
av_opt_set_int(pan->swr, "ocl", pan->out_channel_layout, 0);
swr_set_matrix(pan->swr, pan->gain[0], pan->gain[1] - pan->gain[0]);
}
r = swr_init(pan->swr);
if (r < 0)
return r;
// summary
for (i = 0; i < pan->nb_output_channels; i++) {
cur = buf;
for (j = 0; j < link->channels; j++) {
r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d",
j ? " + " : "", pan->gain[i][j], j);
cur += FFMIN(buf + sizeof(buf) - cur, r);
}
av_log(ctx, AV_LOG_VERBOSE, "o%d = %s\n", i, buf);
}
// add channel mapping summary if possible
if (pan->pure_gains) {
av_log(ctx, AV_LOG_INFO, "Pure channel mapping detected:");
for (i = 0; i < pan->nb_output_channels; i++)
if (pan->channel_map[i] < 0)
av_log(ctx, AV_LOG_INFO, " M");
else
av_log(ctx, AV_LOG_INFO, " %d", pan->channel_map[i]);
av_log(ctx, AV_LOG_INFO, "\n");
return 0;
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
int ret;
int n = insamples->nb_samples;
AVFilterLink *const outlink = inlink->dst->outputs[0];
AVFrame *outsamples = ff_get_audio_buffer(outlink, n);
PanContext *pan = inlink->dst->priv;
if (!outsamples)
return AVERROR(ENOMEM);
swr_convert(pan->swr, outsamples->data, n, (void *)insamples->data, n);
av_frame_copy_props(outsamples, insamples);
outsamples->channel_layout = outlink->channel_layout;
av_frame_set_channels(outsamples, outlink->channels);
ret = ff_filter_frame(outlink, outsamples);
av_frame_free(&insamples);
return ret;
}
static av_cold void uninit(AVFilterContext *ctx)
{
PanContext *pan = ctx->priv;
swr_free(&pan->swr);
}
#define OFFSET(x) offsetof(PanContext, x)
static const AVOption pan_options[] = {
{ "args", NULL, OFFSET(args), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM },
{ NULL }
};
AVFILTER_DEFINE_CLASS(pan);
static const AVFilterPad pan_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_props,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad pan_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_pan = {
.name = "pan",
.description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."),
.priv_size = sizeof(PanContext),
.priv_class = &pan_class,
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = pan_inputs,
.outputs = pan_outputs,
};
+613
View File
@@ -0,0 +1,613 @@
/*
* Copyright (c) 1998 - 2009 Conifer Software
*
* 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
* ReplayGain scanner
*/
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
#define HISTOGRAM_SLOTS 12000
#define BUTTER_ORDER 2
#define YULE_ORDER 10
typedef struct ReplayGainFreqInfo {
int sample_rate;
double BYule[YULE_ORDER + 1];
double AYule[YULE_ORDER + 1];
double BButter[BUTTER_ORDER + 1];
double AButter[BUTTER_ORDER + 1];
} ReplayGainFreqInfo;
static const ReplayGainFreqInfo freqinfos[] =
{
{
192000,
{ 0.01184742123123, -0.04631092400086, 0.06584226961238,
-0.02165588522478, -0.05656260778952, 0.08607493592760,
-0.03375544339786, -0.04216579932754, 0.06416711490648,
-0.03444708260844, 0.00697275872241 },
{ 1.00000000000000, -5.24727318348167, 10.60821585192244,
-8.74127665810413, -1.33906071371683, 8.07972882096606,
-5.46179918950847, 0.54318070652536, 0.87450969224280,
-0.34656083539754, 0.03034796843589 },
{ 0.99653501465135, -1.99307002930271, 0.99653501465135 },
{ 1.00000000000000, -1.99305802314321, 0.99308203546221 },
},
{
176400,
{ 0.00268568524529, -0.00852379426080, 0.00852704191347,
0.00146116310295, -0.00950855828762, 0.00625449515499,
0.00116183868722, -0.00362461417136, 0.00203961000134,
-0.00050664587933, 0.00004327455427 },
{ 1.00000000000000, -5.57512782763045, 12.44291056065794,
-12.87462799681221, 3.08554846961576, 6.62493459880692,
-7.07662766313248, 2.51175542736441, 0.06731510802735,
-0.24567753819213, 0.03961404162376 },
{ 0.99622916581118, -1.99245833162236, 0.99622916581118 },
{ 1.00000000000000, -1.99244411238133, 0.99247255086339 },
},
{
144000,
{ 0.00639682359450, -0.02556437970955, 0.04230854400938,
-0.03722462201267, 0.01718514827295, 0.00610592243009,
-0.03065965747365, 0.04345745003539, -0.03298592681309,
0.01320937236809, -0.00220304127757 },
{ 1.00000000000000, -6.14814623523425, 15.80002457141566,
-20.78487587686937, 11.98848552310315, 3.36462015062606,
-10.22419868359470, 6.65599702146473, -1.67141861110485,
-0.05417956536718, 0.07374767867406 },
{ 0.99538268958706, -1.99076537917413, 0.99538268958706 },
{ 1.00000000000000, -1.99074405950505, 0.99078669884321 },
},
{
128000,
{ 0.00553120584305, -0.02112620545016, 0.03549076243117,
-0.03362498312306, 0.01425867248183, 0.01344686928787,
-0.03392770787836, 0.03464136459530, -0.02039116051549,
0.00667420794705, -0.00093763762995 },
{ 1.00000000000000, -6.14581710839925, 16.04785903675838,
-22.19089131407749, 15.24756471580286, -0.52001440400238,
-8.00488641699940, 6.60916094768855, -2.37856022810923,
0.33106947986101, 0.00459820832036 },
{ 0.99480702681278, -1.98961405362557, 0.99480702681278 },
{ 1.00000000000000, -1.98958708647324, 0.98964102077790 },
},
{
112000,
{ 0.00528778718259, -0.01893240907245, 0.03185982561867,
-0.02926260297838, 0.00715743034072, 0.01985743355827,
-0.03222614850941, 0.02565681978192, -0.01210662313473,
0.00325436284541, -0.00044173593001 },
{ 1.00000000000000, -6.24932108456288, 17.42344320538476,
-27.86819709054896, 26.79087344681326,-13.43711081485123,
-0.66023612948173, 6.03658091814935, -4.24926577030310,
1.40829268709186, -0.19480852628112 },
{ 0.99406737810867, -1.98813475621734, 0.99406737810867 },
{ 1.00000000000000, -1.98809955990514, 0.98816995252954 },
},
{
96000,
{ 0.00588138296683, -0.01613559730421, 0.02184798954216,
-0.01742490405317, 0.00464635643780, 0.01117772513205,
-0.02123865824368, 0.01959354413350, -0.01079720643523,
0.00352183686289, -0.00063124341421 },
{ 1.00000000000000, -5.97808823642008, 16.21362507964068,
-25.72923730652599, 25.40470663139513,-14.66166287771134,
2.81597484359752, 2.51447125969733, -2.23575306985286,
0.75788151036791, -0.10078025199029 },
{ 0.99308203517541, -1.98616407035082, 0.99308203517541 },
{ 1.00000000000000, -1.98611621154089, 0.98621192916075 },
},
{
88200,
{ 0.02667482047416, -0.11377479336097, 0.23063167910965,
-0.30726477945593, 0.33188520686529, -0.33862680249063,
0.31807161531340, -0.23730796929880, 0.12273894790371,
-0.03840017967282, 0.00549673387936 },
{ 1.00000000000000, -6.31836451657302, 18.31351310801799,
-31.88210014815921, 36.53792146976740,-28.23393036467559,
14.24725258227189, -4.04670980012854, 0.18865757280515,
0.25420333563908, -0.06012333531065 },
{ 0.99247255046129, -1.98494510092259, 0.99247255046129 },
{ 1.00000000000000, -1.98488843762335, 0.98500176422183 },
},
{
64000,
{ 0.02613056568174, -0.08128786488109, 0.14937282347325,
-0.21695711675126, 0.25010286673402, -0.23162283619278,
0.17424041833052, -0.10299599216680, 0.04258696481981,
-0.00977952936493, 0.00105325558889 },
{ 1.00000000000000, -5.73625477092119, 16.15249794355035,
-29.68654912464508, 39.55706155674083,-39.82524556246253,
30.50605345013009,-17.43051772821245, 7.05154573908017,
-1.80783839720514, 0.22127840210813 },
{ 0.98964101933472, -1.97928203866944, 0.98964101933472 },
{ 1.00000000000000, -1.97917472731009, 0.97938935002880 },
},
{
56000,
{ 0.03144914734085, -0.06151729206963, 0.08066788708145,
-0.09737939921516, 0.08943210803999, -0.06989984672010,
0.04926972841044, -0.03161257848451, 0.01456837493506,
-0.00316015108496, 0.00132807215875 },
{ 1.00000000000000, -4.87377313090032, 12.03922160140209,
-20.10151118381395, 25.10388534415171,-24.29065560815903,
18.27158469090663,-10.45249552560593, 4.30319491872003,
-1.13716992070185, 0.14510733527035 },
{ 0.98816995007392, -1.97633990014784, 0.98816995007392 },
{ 1.00000000000000, -1.97619994516973, 0.97647985512594 },
},
{
48000,
{ 0.03857599435200, -0.02160367184185, -0.00123395316851,
-0.00009291677959, -0.01655260341619, 0.02161526843274,
-0.02074045215285, 0.00594298065125, 0.00306428023191,
0.00012025322027, 0.00288463683916 },
{ 1.00000000000000, -3.84664617118067, 7.81501653005538,
-11.34170355132042, 13.05504219327545,-12.28759895145294,
9.48293806319790, -5.87257861775999, 2.75465861874613,
-0.86984376593551, 0.13919314567432 },
{ 0.98621192462708, -1.97242384925416, 0.98621192462708 },
{ 1.00000000000000, -1.97223372919527, 0.97261396931306 },
},
{
44100,
{ 0.05418656406430, -0.02911007808948, -0.00848709379851,
-0.00851165645469, -0.00834990904936, 0.02245293253339,
-0.02596338512915, 0.01624864962975, -0.00240879051584,
0.00674613682247, -0.00187763777362 },
{ 1.00000000000000, -3.47845948550071, 6.36317777566148,
-8.54751527471874, 9.47693607801280, -8.81498681370155,
6.85401540936998, -4.39470996079559, 2.19611684890774,
-0.75104302451432, 0.13149317958808 },
{ 0.98500175787242, -1.97000351574484, 0.98500175787242 },
{ 1.00000000000000, -1.96977855582618, 0.97022847566350 },
},
{
37800,
{ 0.08717879977844, -0.01000374016172, -0.06265852122368,
-0.01119328800950, -0.00114279372960, 0.02081333954769,
-0.01603261863207, 0.01936763028546, 0.00760044736442,
-0.00303979112271, -0.00075088605788 },
{ 1.00000000000000, -2.62816311472146, 3.53734535817992,
-3.81003448678921, 3.91291636730132, -3.53518605896288,
2.71356866157873, -1.86723311846592, 1.12075382367659,
-0.48574086886890, 0.11330544663849 },
{ 0.98252400815195, -1.96504801630391, 0.98252400815195 },
{ 1.00000000000000, -1.96474258269041, 0.96535344991740 },
},
{
32000,
{ 0.15457299681924, -0.09331049056315, -0.06247880153653,
0.02163541888798, -0.05588393329856, 0.04781476674921,
0.00222312597743, 0.03174092540049, -0.01390589421898,
0.00651420667831, -0.00881362733839 },
{ 1.00000000000000, -2.37898834973084, 2.84868151156327,
-2.64577170229825, 2.23697657451713, -1.67148153367602,
1.00595954808547, -0.45953458054983, 0.16378164858596,
-0.05032077717131, 0.02347897407020 },
{ 0.97938932735214, -1.95877865470428, 0.97938932735214 },
{ 1.00000000000000, -1.95835380975398, 0.95920349965459 },
},
{
24000,
{ 0.30296907319327, -0.22613988682123, -0.08587323730772,
0.03282930172664, -0.00915702933434, -0.02364141202522,
-0.00584456039913, 0.06276101321749, -0.00000828086748,
0.00205861885564, -0.02950134983287 },
{ 1.00000000000000, -1.61273165137247, 1.07977492259970,
-0.25656257754070, -0.16276719120440, -0.22638893773906,
0.39120800788284, -0.22138138954925, 0.04500235387352,
0.02005851806501, 0.00302439095741 },
{ 0.97531843204928, -1.95063686409857, 0.97531843204928 },
{ 1.00000000000000, -1.95002759149878, 0.95124613669835 },
},
{
22050,
{ 0.33642304856132, -0.25572241425570, -0.11828570177555,
0.11921148675203, -0.07834489609479, -0.00469977914380,
-0.00589500224440, 0.05724228140351, 0.00832043980773,
-0.01635381384540, -0.01760176568150 },
{ 1.00000000000000, -1.49858979367799, 0.87350271418188,
0.12205022308084, -0.80774944671438, 0.47854794562326,
-0.12453458140019, -0.04067510197014, 0.08333755284107,
-0.04237348025746, 0.02977207319925 },
{ 0.97316523498161, -1.94633046996323, 0.97316523498161 },
{ 1.00000000000000, -1.94561023566527, 0.94705070426118 },
},
{
18900,
{ 0.38524531015142, -0.27682212062067, -0.09980181488805,
0.09951486755646, -0.08934020156622, -0.00322369330199,
-0.00110329090689, 0.03784509844682, 0.01683906213303,
-0.01147039862572, -0.01941767987192 },
{ 1.00000000000000, -1.29708918404534, 0.90399339674203,
-0.29613799017877, -0.42326645916207, 0.37934887402200,
-0.37919795944938, 0.23410283284785, -0.03892971758879,
0.00403009552351, 0.03640166626278 },
{ 0.96535326815829, -1.93070653631658, 0.96535326815829 },
{ 1.00000000000000, -1.92950577983524, 0.93190729279793 },
},
{
16000,
{ 0.44915256608450, -0.14351757464547, -0.22784394429749,
-0.01419140100551, 0.04078262797139, -0.12398163381748,
0.04097565135648, 0.10478503600251, -0.01863887810927,
-0.03193428438915, 0.00541907748707 },
{ 1.00000000000000, -0.62820619233671, 0.29661783706366,
-0.37256372942400, 0.00213767857124, -0.42029820170918,
0.22199650564824, 0.00613424350682, 0.06747620744683,
0.05784820375801, 0.03222754072173 },
{ 0.96454515552826, -1.92909031105652, 0.96454515552826 },
{ 1.00000000000000, -1.92783286977036, 0.93034775234268 },
},
{
12000,
{ 0.56619470757641, -0.75464456939302, 0.16242137742230,
0.16744243493672, -0.18901604199609, 0.30931782841830,
-0.27562961986224, 0.00647310677246, 0.08647503780351,
-0.03788984554840, -0.00588215443421 },
{ 1.00000000000000, -1.04800335126349, 0.29156311971249,
-0.26806001042947, 0.00819999645858, 0.45054734505008,
-0.33032403314006, 0.06739368333110, -0.04784254229033,
0.01639907836189, 0.01807364323573 },
{ 0.96009142950541, -1.92018285901082, 0.96009142950541 },
{ 1.00000000000000, -1.91858953033784, 0.92177618768381 },
},
{
11025,
{ 0.58100494960553, -0.53174909058578, -0.14289799034253,
0.17520704835522, 0.02377945217615, 0.15558449135573,
-0.25344790059353, 0.01628462406333, 0.06920467763959,
-0.03721611395801, -0.00749618797172 },
{ 1.00000000000000, -0.51035327095184, -0.31863563325245,
-0.20256413484477, 0.14728154134330, 0.38952639978999,
-0.23313271880868, -0.05246019024463, -0.02505961724053,
0.02442357316099, 0.01818801111503 },
{ 0.95856916599601, -1.91713833199203, 0.95856916599601 },
{ 1.00000000000000, -1.91542108074780, 0.91885558323625 },
},
{
8000,
{ 0.53648789255105, -0.42163034350696, -0.00275953611929,
0.04267842219415, -0.10214864179676, 0.14590772289388,
-0.02459864859345, -0.11202315195388, -0.04060034127000,
0.04788665548180, -0.02217936801134 },
{ 1.00000000000000, -0.25049871956020, -0.43193942311114,
-0.03424681017675, -0.04678328784242, 0.26408300200955,
0.15113130533216, -0.17556493366449, -0.18823009262115,
0.05477720428674, 0.04704409688120 },
{ 0.94597685600279, -1.89195371200558, 0.94597685600279 },
{ 1.00000000000000, -1.88903307939452, 0.89487434461664 },
},
};
typedef struct ReplayGainContext {
uint32_t histogram[HISTOGRAM_SLOTS];
float peak;
int yule_hist_i, butter_hist_i;
const double *yule_coeff_a;
const double *yule_coeff_b;
const double *butter_coeff_a;
const double *butter_coeff_b;
float yule_hist_a[256];
float yule_hist_b[256];
float butter_hist_a[256];
float butter_hist_b[256];
} ReplayGainContext;
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layout = NULL;
int i;
ff_add_format(&formats, AV_SAMPLE_FMT_FLT);
ff_set_common_formats(ctx, formats);
ff_add_channel_layout(&layout, AV_CH_LAYOUT_STEREO);
ff_set_common_channel_layouts(ctx, layout);
formats = NULL;
for (i = 0; i < FF_ARRAY_ELEMS(freqinfos); i++)
ff_add_format(&formats, freqinfos[i].sample_rate);
ff_set_common_samplerates(ctx, formats);
return 0;
}
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
ReplayGainContext *s = ctx->priv;
int i;
for (i = 0; i < FF_ARRAY_ELEMS(freqinfos); i++) {
if (freqinfos[i].sample_rate == inlink->sample_rate)
break;
}
av_assert0(i < FF_ARRAY_ELEMS(freqinfos));
s->yule_coeff_a = freqinfos[i].AYule;
s->yule_coeff_b = freqinfos[i].BYule;
s->butter_coeff_a = freqinfos[i].AButter;
s->butter_coeff_b = freqinfos[i].BButter;
s->yule_hist_i = 20;
s->butter_hist_i = 4;
inlink->partial_buf_size =
inlink->min_samples =
inlink->max_samples = inlink->sample_rate / 20;
return 0;
}
/*
* Update largest absolute sample value.
*/
static void calc_stereo_peak(const float *samples, int nb_samples,
float *peak_p)
{
float peak = 0.0;
while (nb_samples--) {
if (samples[0] > peak)
peak = samples[0];
else if (-samples[0] > peak)
peak = -samples[0];
if (samples[1] > peak)
peak = samples[1];
else if (-samples[1] > peak)
peak = -samples[1];
samples += 2;
}
*peak_p = FFMAX(peak, *peak_p);
}
/*
* Calculate stereo RMS level. Minimum value is about -100 dB for
* digital silence. The 90 dB offset is to compensate for the
* normalized float range and 3 dB is for stereo samples.
*/
static double calc_stereo_rms(const float *samples, int nb_samples)
{
int count = nb_samples;
double sum = 1e-16;
while (count--) {
sum += samples[0] * samples[0] + samples[1] * samples[1];
samples += 2;
}
return 10 * log10 (sum / nb_samples) + 90.0 - 3.0;
}
/*
* Optimized implementation of 2nd-order IIR stereo filter.
*/
static void butter_filter_stereo_samples(ReplayGainContext *s,
float *samples, int nb_samples)
{
const double *coeff_a = s->butter_coeff_a;
const double *coeff_b = s->butter_coeff_b;
float *hist_a = s->butter_hist_a;
float *hist_b = s->butter_hist_b;
double left, right;
int i, j;
i = s->butter_hist_i;
// If filter history is very small magnitude, clear it completely
// to prevent denormals from rattling around in there forever
// (slowing us down).
for (j = -4; j < 0; ++j)
if (fabs(hist_a[i + j]) > 1e-10 || fabs(hist_b[i + j]) > 1e-10)
break;
if (!j) {
memset(s->butter_hist_a, 0, sizeof(s->butter_hist_a));
memset(s->butter_hist_b, 0, sizeof(s->butter_hist_b));
}
while (nb_samples--) {
left = (hist_b[i ] = samples[0]) * coeff_b[0];
right = (hist_b[i + 1] = samples[1]) * coeff_b[0];
left += hist_b[i - 2] * coeff_b[1] - hist_a[i - 2] * coeff_a[1];
right += hist_b[i - 1] * coeff_b[1] - hist_a[i - 1] * coeff_a[1];
left += hist_b[i - 4] * coeff_b[2] - hist_a[i - 4] * coeff_a[2];
right += hist_b[i - 3] * coeff_b[2] - hist_a[i - 3] * coeff_a[2];
samples[0] = hist_a[i ] = (float) left;
samples[1] = hist_a[i + 1] = (float) right;
samples += 2;
if ((i += 2) == 256) {
memcpy(hist_a, hist_a + 252, sizeof(*hist_a) * 4);
memcpy(hist_b, hist_b + 252, sizeof(*hist_b) * 4);
i = 4;
}
}
s->butter_hist_i = i;
}
/*
* Optimized implementation of 10th-order IIR stereo filter.
*/
static void yule_filter_stereo_samples(ReplayGainContext *s, const float *src,
float *dst, int nb_samples)
{
const double *coeff_a = s->yule_coeff_a;
const double *coeff_b = s->yule_coeff_b;
float *hist_a = s->yule_hist_a;
float *hist_b = s->yule_hist_b;
double left, right;
int i, j;
i = s->yule_hist_i;
// If filter history is very small magnitude, clear it completely to
// prevent denormals from rattling around in there forever
// (slowing us down).
for (j = -20; j < 0; ++j)
if (fabs(hist_a[i + j]) > 1e-10 || fabs(hist_b[i + j]) > 1e-10)
break;
if (!j) {
memset(s->yule_hist_a, 0, sizeof(s->yule_hist_a));
memset(s->yule_hist_b, 0, sizeof(s->yule_hist_b));
}
while (nb_samples--) {
left = (hist_b[i] = src[0]) * coeff_b[0];
right = (hist_b[i + 1] = src[1]) * coeff_b[0];
left += hist_b[i - 2] * coeff_b[ 1] - hist_a[i - 2] * coeff_a[1 ];
right += hist_b[i - 1] * coeff_b[ 1] - hist_a[i - 1] * coeff_a[1 ];
left += hist_b[i - 4] * coeff_b[ 2] - hist_a[i - 4] * coeff_a[2 ];
right += hist_b[i - 3] * coeff_b[ 2] - hist_a[i - 3] * coeff_a[2 ];
left += hist_b[i - 6] * coeff_b[ 3] - hist_a[i - 6] * coeff_a[3 ];
right += hist_b[i - 5] * coeff_b[ 3] - hist_a[i - 5] * coeff_a[3 ];
left += hist_b[i - 8] * coeff_b[ 4] - hist_a[i - 8] * coeff_a[4 ];
right += hist_b[i - 7] * coeff_b[ 4] - hist_a[i - 7] * coeff_a[4 ];
left += hist_b[i - 10] * coeff_b[ 5] - hist_a[i - 10] * coeff_a[5 ];
right += hist_b[i - 9] * coeff_b[ 5] - hist_a[i - 9] * coeff_a[5 ];
left += hist_b[i - 12] * coeff_b[ 6] - hist_a[i - 12] * coeff_a[6 ];
right += hist_b[i - 11] * coeff_b[ 6] - hist_a[i - 11] * coeff_a[6 ];
left += hist_b[i - 14] * coeff_b[ 7] - hist_a[i - 14] * coeff_a[7 ];
right += hist_b[i - 13] * coeff_b[ 7] - hist_a[i - 13] * coeff_a[7 ];
left += hist_b[i - 16] * coeff_b[ 8] - hist_a[i - 16] * coeff_a[8 ];
right += hist_b[i - 15] * coeff_b[ 8] - hist_a[i - 15] * coeff_a[8 ];
left += hist_b[i - 18] * coeff_b[ 9] - hist_a[i - 18] * coeff_a[9 ];
right += hist_b[i - 17] * coeff_b[ 9] - hist_a[i - 17] * coeff_a[9 ];
left += hist_b[i - 20] * coeff_b[10] - hist_a[i - 20] * coeff_a[10];
right += hist_b[i - 19] * coeff_b[10] - hist_a[i - 19] * coeff_a[10];
dst[0] = hist_a[i ] = (float)left;
dst[1] = hist_a[i + 1] = (float)right;
src += 2;
dst += 2;
if ((i += 2) == 256) {
memcpy(hist_a, hist_a + 236, sizeof(*hist_a) * 20);
memcpy(hist_b, hist_b + 236, sizeof(*hist_b) * 20);
i = 20;
}
}
s->yule_hist_i = i;
}
/*
* Calculate the ReplayGain value from the specified loudness histogram;
* clip to -24 / +64 dB.
*/
static float calc_replaygain(uint32_t *histogram)
{
uint32_t loud_count = 0, total_windows = 0;
float gain;
int i;
for (i = 0; i < HISTOGRAM_SLOTS; i++)
total_windows += histogram [i];
while (i--)
if ((loud_count += histogram [i]) * 20 >= total_windows)
break;
gain = (float)(64.54 - i / 100.0);
return av_clipf(gain, -24.0, 64.0);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
ReplayGainContext *s = ctx->priv;
uint32_t level;
AVFrame *out;
out = ff_get_audio_buffer(inlink, in->nb_samples);
if (!out) {
av_frame_free(&in);
return AVERROR(ENOMEM);
}
calc_stereo_peak((float *)in->data[0],
in->nb_samples, &s->peak);
yule_filter_stereo_samples(s, (const float *)in->data[0],
(float *)out->data[0],
out->nb_samples);
butter_filter_stereo_samples(s, (float *)out->data[0],
out->nb_samples);
level = (uint32_t)floor(100 * calc_stereo_rms((float *)out->data[0],
out->nb_samples));
level = av_clip(level, 0, HISTOGRAM_SLOTS - 1);
s->histogram[level]++;
av_frame_free(&out);
return ff_filter_frame(outlink, in);
}
static av_cold void uninit(AVFilterContext *ctx)
{
ReplayGainContext *s = ctx->priv;
float gain = calc_replaygain(s->histogram);
av_log(ctx, AV_LOG_INFO, "track_gain = %+.2f dB\n", gain);
av_log(ctx, AV_LOG_INFO, "track_peak = %.6f\n", s->peak);
}
static const AVFilterPad replaygain_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
.config_props = config_input,
},
{ NULL }
};
static const AVFilterPad replaygain_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_replaygain = {
.name = "replaygain",
.description = NULL_IF_CONFIG_SMALL("ReplayGain scanner."),
.query_formats = query_formats,
.uninit = uninit,
.priv_size = sizeof(ReplayGainContext),
.inputs = replaygain_inputs,
.outputs = replaygain_outputs,
};
+327
View File
@@ -0,0 +1,327 @@
/*
* 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
* sample format and channel layout conversion audio filter
*/
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/common.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavresample/avresample.h"
#include "audio.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
typedef struct ResampleContext {
const AVClass *class;
AVAudioResampleContext *avr;
AVDictionary *options;
int64_t next_pts;
/* set by filter_frame() to signal an output frame to request_frame() */
int got_output;
} ResampleContext;
static av_cold int init(AVFilterContext *ctx, AVDictionary **opts)
{
ResampleContext *s = ctx->priv;
const AVClass *avr_class = avresample_get_class();
AVDictionaryEntry *e = NULL;
while ((e = av_dict_get(*opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
if (av_opt_find(&avr_class, e->key, NULL, 0,
AV_OPT_SEARCH_FAKE_OBJ | AV_OPT_SEARCH_CHILDREN))
av_dict_set(&s->options, e->key, e->value, 0);
}
e = NULL;
while ((e = av_dict_get(s->options, "", e, AV_DICT_IGNORE_SUFFIX)))
av_dict_set(opts, e->key, NULL, 0);
/* do not allow the user to override basic format options */
av_dict_set(&s->options, "in_channel_layout", NULL, 0);
av_dict_set(&s->options, "out_channel_layout", NULL, 0);
av_dict_set(&s->options, "in_sample_fmt", NULL, 0);
av_dict_set(&s->options, "out_sample_fmt", NULL, 0);
av_dict_set(&s->options, "in_sample_rate", NULL, 0);
av_dict_set(&s->options, "out_sample_rate", NULL, 0);
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
ResampleContext *s = ctx->priv;
if (s->avr) {
avresample_close(s->avr);
avresample_free(&s->avr);
}
av_dict_free(&s->options);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
AVFilterFormats *in_formats = ff_all_formats(AVMEDIA_TYPE_AUDIO);
AVFilterFormats *out_formats = ff_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();
ff_formats_ref(in_formats, &inlink->out_formats);
ff_formats_ref(out_formats, &outlink->in_formats);
ff_formats_ref(in_samplerates, &inlink->out_samplerates);
ff_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;
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink = ctx->inputs[0];
ResampleContext *s = ctx->priv;
char buf1[64], buf2[64];
int ret;
if (s->avr) {
avresample_close(s->avr);
avresample_free(&s->avr);
}
if (inlink->channel_layout == outlink->channel_layout &&
inlink->sample_rate == outlink->sample_rate &&
(inlink->format == outlink->format ||
(av_get_channel_layout_nb_channels(inlink->channel_layout) == 1 &&
av_get_channel_layout_nb_channels(outlink->channel_layout) == 1 &&
av_get_planar_sample_fmt(inlink->format) ==
av_get_planar_sample_fmt(outlink->format))))
return 0;
if (!(s->avr = avresample_alloc_context()))
return AVERROR(ENOMEM);
if (s->options) {
AVDictionaryEntry *e = NULL;
while ((e = av_dict_get(s->options, "", e, AV_DICT_IGNORE_SUFFIX)))
av_log(ctx, AV_LOG_VERBOSE, "lavr option: %s=%s\n", e->key, e->value);
av_opt_set_dict(s->avr, &s->options);
}
av_opt_set_int(s->avr, "in_channel_layout", inlink ->channel_layout, 0);
av_opt_set_int(s->avr, "out_channel_layout", outlink->channel_layout, 0);
av_opt_set_int(s->avr, "in_sample_fmt", inlink ->format, 0);
av_opt_set_int(s->avr, "out_sample_fmt", outlink->format, 0);
av_opt_set_int(s->avr, "in_sample_rate", inlink ->sample_rate, 0);
av_opt_set_int(s->avr, "out_sample_rate", outlink->sample_rate, 0);
if ((ret = avresample_open(s->avr)) < 0)
return ret;
outlink->time_base = (AVRational){ 1, outlink->sample_rate };
s->next_pts = AV_NOPTS_VALUE;
av_get_channel_layout_string(buf1, sizeof(buf1),
-1, inlink ->channel_layout);
av_get_channel_layout_string(buf2, sizeof(buf2),
-1, outlink->channel_layout);
av_log(ctx, AV_LOG_VERBOSE,
"fmt:%s srate:%d cl:%s -> fmt:%s srate:%d cl:%s\n",
av_get_sample_fmt_name(inlink ->format), inlink ->sample_rate, buf1,
av_get_sample_fmt_name(outlink->format), outlink->sample_rate, buf2);
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
ResampleContext *s = ctx->priv;
int ret = 0;
s->got_output = 0;
while (ret >= 0 && !s->got_output)
ret = ff_request_frame(ctx->inputs[0]);
/* flush the lavr delay buffer */
if (ret == AVERROR_EOF && s->avr) {
AVFrame *frame;
int nb_samples = av_rescale_rnd(avresample_get_delay(s->avr),
outlink->sample_rate,
ctx->inputs[0]->sample_rate,
AV_ROUND_UP);
if (!nb_samples)
return ret;
frame = ff_get_audio_buffer(outlink, nb_samples);
if (!frame)
return AVERROR(ENOMEM);
ret = avresample_convert(s->avr, frame->extended_data,
frame->linesize[0], nb_samples,
NULL, 0, 0);
if (ret <= 0) {
av_frame_free(&frame);
return (ret == 0) ? AVERROR_EOF : ret;
}
frame->pts = s->next_pts;
return ff_filter_frame(outlink, frame);
}
return ret;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
{
AVFilterContext *ctx = inlink->dst;
ResampleContext *s = ctx->priv;
AVFilterLink *outlink = ctx->outputs[0];
int ret;
if (s->avr) {
AVFrame *out;
int delay, nb_samples;
/* maximum possible samples lavr can output */
delay = avresample_get_delay(s->avr);
nb_samples = av_rescale_rnd(in->nb_samples + delay,
outlink->sample_rate, inlink->sample_rate,
AV_ROUND_UP);
out = ff_get_audio_buffer(outlink, nb_samples);
if (!out) {
ret = AVERROR(ENOMEM);
goto fail;
}
ret = avresample_convert(s->avr, out->extended_data, out->linesize[0],
nb_samples, in->extended_data, in->linesize[0],
in->nb_samples);
if (ret <= 0) {
av_frame_free(&out);
if (ret < 0)
goto fail;
}
av_assert0(!avresample_available(s->avr));
if (s->next_pts == AV_NOPTS_VALUE) {
if (in->pts == AV_NOPTS_VALUE) {
av_log(ctx, AV_LOG_WARNING, "First timestamp is missing, "
"assuming 0.\n");
s->next_pts = 0;
} else
s->next_pts = av_rescale_q(in->pts, inlink->time_base,
outlink->time_base);
}
if (ret > 0) {
out->nb_samples = ret;
if (in->pts != AV_NOPTS_VALUE) {
out->pts = av_rescale_q(in->pts, inlink->time_base,
outlink->time_base) -
av_rescale(delay, outlink->sample_rate,
inlink->sample_rate);
} else
out->pts = s->next_pts;
s->next_pts = out->pts + out->nb_samples;
ret = ff_filter_frame(outlink, out);
s->got_output = 1;
}
fail:
av_frame_free(&in);
} else {
in->format = outlink->format;
ret = ff_filter_frame(outlink, in);
s->got_output = 1;
}
return ret;
}
static const AVClass *resample_child_class_next(const AVClass *prev)
{
return prev ? NULL : avresample_get_class();
}
static void *resample_child_next(void *obj, void *prev)
{
ResampleContext *s = obj;
return prev ? NULL : s->avr;
}
static const AVClass resample_class = {
.class_name = "resample",
.item_name = av_default_item_name,
.version = LIBAVUTIL_VERSION_INT,
.child_class_next = resample_child_class_next,
.child_next = resample_child_next,
};
static const AVFilterPad avfilter_af_resample_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad avfilter_af_resample_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
.request_frame = request_frame
},
{ NULL }
};
AVFilter avfilter_af_resample = {
.name = "resample",
.description = NULL_IF_CONFIG_SMALL("Audio resampling and conversion."),
.priv_size = sizeof(ResampleContext),
.priv_class = &resample_class,
.init_dict = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = avfilter_af_resample_inputs,
.outputs = avfilter_af_resample_outputs,
};
@@ -0,0 +1,212 @@
/*
* Copyright (c) 2012 Clément Bœsch <u pkh me>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Audio silence detector
*/
#include <float.h> /* DBL_MAX */
#include "libavutil/opt.h"
#include "libavutil/timestamp.h"
#include "audio.h"
#include "formats.h"
#include "avfilter.h"
#include "internal.h"
typedef struct SilenceDetectContext {
const AVClass *class;
double noise; ///< noise amplitude ratio
double duration; ///< minimum duration of silence until notification
int64_t nb_null_samples; ///< current number of continuous zero samples
int64_t start; ///< if silence is detected, this value contains the time of the first zero sample
int last_sample_rate; ///< last sample rate to check for sample rate changes
void (*silencedetect)(struct SilenceDetectContext *s, AVFrame *insamples,
int nb_samples, int64_t nb_samples_notify,
AVRational time_base);
} SilenceDetectContext;
#define OFFSET(x) offsetof(SilenceDetectContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
static const AVOption silencedetect_options[] = {
{ "n", "set noise tolerance", OFFSET(noise), AV_OPT_TYPE_DOUBLE, {.dbl=0.001}, 0, DBL_MAX, FLAGS },
{ "noise", "set noise tolerance", OFFSET(noise), AV_OPT_TYPE_DOUBLE, {.dbl=0.001}, 0, DBL_MAX, FLAGS },
{ "d", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DOUBLE, {.dbl=2.}, 0, 24*60*60, FLAGS },
{ "duration", "set minimum duration in seconds", OFFSET(duration), AV_OPT_TYPE_DOUBLE, {.dbl=2.}, 0, 24*60*60, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(silencedetect);
static char *get_metadata_val(AVFrame *insamples, const char *key)
{
AVDictionaryEntry *e = av_dict_get(insamples->metadata, key, NULL, 0);
return e && e->value ? e->value : NULL;
}
static av_always_inline void update(SilenceDetectContext *s, AVFrame *insamples,
int is_silence, int64_t nb_samples_notify,
AVRational time_base)
{
if (is_silence) {
if (!s->start) {
s->nb_null_samples++;
if (s->nb_null_samples >= nb_samples_notify) {
s->start = insamples->pts - (int64_t)(s->duration / av_q2d(time_base) + .5);
av_dict_set(&insamples->metadata, "lavfi.silence_start",
av_ts2timestr(s->start, &time_base), 0);
av_log(s, AV_LOG_INFO, "silence_start: %s\n",
get_metadata_val(insamples, "lavfi.silence_start"));
}
}
} else {
if (s->start) {
av_dict_set(&insamples->metadata, "lavfi.silence_end",
av_ts2timestr(insamples->pts, &time_base), 0);
av_dict_set(&insamples->metadata, "lavfi.silence_duration",
av_ts2timestr(insamples->pts - s->start, &time_base), 0);
av_log(s, AV_LOG_INFO,
"silence_end: %s | silence_duration: %s\n",
get_metadata_val(insamples, "lavfi.silence_end"),
get_metadata_val(insamples, "lavfi.silence_duration"));
}
s->nb_null_samples = s->start = 0;
}
}
#define SILENCE_DETECT(name, type) \
static void silencedetect_##name(SilenceDetectContext *s, AVFrame *insamples, \
int nb_samples, int64_t nb_samples_notify, \
AVRational time_base) \
{ \
const type *p = (const type *)insamples->data[0]; \
const type noise = s->noise; \
int i; \
\
for (i = 0; i < nb_samples; i++, p++) \
update(s, insamples, *p < noise && *p > -noise, \
nb_samples_notify, time_base); \
}
SILENCE_DETECT(dbl, double)
SILENCE_DETECT(flt, float)
SILENCE_DETECT(s32, int32_t)
SILENCE_DETECT(s16, int16_t)
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
SilenceDetectContext *s = ctx->priv;
switch (inlink->format) {
case AV_SAMPLE_FMT_DBL: s->silencedetect = silencedetect_dbl; break;
case AV_SAMPLE_FMT_FLT: s->silencedetect = silencedetect_flt; break;
case AV_SAMPLE_FMT_S32:
s->noise *= INT32_MAX;
s->silencedetect = silencedetect_s32;
break;
case AV_SAMPLE_FMT_S16:
s->noise *= INT16_MAX;
s->silencedetect = silencedetect_s16;
break;
}
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
SilenceDetectContext *s = inlink->dst->priv;
const int nb_channels = inlink->channels;
const int srate = inlink->sample_rate;
const int nb_samples = insamples->nb_samples * nb_channels;
const int64_t nb_samples_notify = srate * s->duration * nb_channels;
// scale number of null samples to the new sample rate
if (s->last_sample_rate && s->last_sample_rate != srate)
s->nb_null_samples = srate * s->nb_null_samples / s->last_sample_rate;
s->last_sample_rate = srate;
// TODO: document metadata
s->silencedetect(s, insamples, nb_samples, nb_samples_notify,
inlink->time_base);
return ff_filter_frame(inlink->dst->outputs[0], insamples);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layouts = NULL;
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_DBL,
AV_SAMPLE_FMT_FLT,
AV_SAMPLE_FMT_S32,
AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_NONE
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
static const AVFilterPad silencedetect_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_input,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad silencedetect_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_silencedetect = {
.name = "silencedetect",
.description = NULL_IF_CONFIG_SMALL("Detect silence."),
.priv_size = sizeof(SilenceDetectContext),
.query_formats = query_formats,
.inputs = silencedetect_inputs,
.outputs = silencedetect_outputs,
.priv_class = &silencedetect_class,
};
+300
View File
@@ -0,0 +1,300 @@
/*
* Copyright (c) 2011 Stefano Sabatini
* Copyright (c) 2012 Justin Ruggles <[email protected]>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio volume filter
*/
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/eval.h"
#include "libavutil/float_dsp.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
#include "af_volume.h"
static const char *precision_str[] = {
"fixed", "float", "double"
};
#define OFFSET(x) offsetof(VolumeContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption volume_options[] = {
{ "volume", "set volume adjustment",
OFFSET(volume), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 0, 0x7fffff, A|F },
{ "precision", "select mathematical precision",
OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, PRECISION_FIXED, PRECISION_DOUBLE, A|F, "precision" },
{ "fixed", "select 8-bit fixed-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FIXED }, INT_MIN, INT_MAX, A|F, "precision" },
{ "float", "select 32-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FLOAT }, INT_MIN, INT_MAX, A|F, "precision" },
{ "double", "select 64-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_DOUBLE }, INT_MIN, INT_MAX, A|F, "precision" },
{ NULL }
};
AVFILTER_DEFINE_CLASS(volume);
static av_cold int init(AVFilterContext *ctx)
{
VolumeContext *vol = ctx->priv;
if (vol->precision == PRECISION_FIXED) {
vol->volume_i = (int)(vol->volume * 256 + 0.5);
vol->volume = vol->volume_i / 256.0;
av_log(ctx, AV_LOG_VERBOSE, "volume:(%d/256)(%f)(%1.2fdB) precision:fixed\n",
vol->volume_i, vol->volume, 20.0*log(vol->volume)/M_LN10);
} else {
av_log(ctx, AV_LOG_VERBOSE, "volume:(%f)(%1.2fdB) precision:%s\n",
vol->volume, 20.0*log(vol->volume)/M_LN10,
precision_str[vol->precision]);
}
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
VolumeContext *vol = ctx->priv;
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layouts;
static const enum AVSampleFormat sample_fmts[][7] = {
[PRECISION_FIXED] = {
AV_SAMPLE_FMT_U8,
AV_SAMPLE_FMT_U8P,
AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_S32,
AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_NONE
},
[PRECISION_FLOAT] = {
AV_SAMPLE_FMT_FLT,
AV_SAMPLE_FMT_FLTP,
AV_SAMPLE_FMT_NONE
},
[PRECISION_DOUBLE] = {
AV_SAMPLE_FMT_DBL,
AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
}
};
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_set_common_channel_layouts(ctx, layouts);
formats = ff_make_format_list(sample_fmts[vol->precision]);
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_set_common_samplerates(ctx, formats);
return 0;
}
static inline void scale_samples_u8(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
for (i = 0; i < nb_samples; i++)
dst[i] = av_clip_uint8(((((int64_t)src[i] - 128) * volume + 128) >> 8) + 128);
}
static inline void scale_samples_u8_small(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
for (i = 0; i < nb_samples; i++)
dst[i] = av_clip_uint8((((src[i] - 128) * volume + 128) >> 8) + 128);
}
static inline void scale_samples_s16(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
int16_t *smp_dst = (int16_t *)dst;
const int16_t *smp_src = (const int16_t *)src;
for (i = 0; i < nb_samples; i++)
smp_dst[i] = av_clip_int16(((int64_t)smp_src[i] * volume + 128) >> 8);
}
static inline void scale_samples_s16_small(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
int16_t *smp_dst = (int16_t *)dst;
const int16_t *smp_src = (const int16_t *)src;
for (i = 0; i < nb_samples; i++)
smp_dst[i] = av_clip_int16((smp_src[i] * volume + 128) >> 8);
}
static inline void scale_samples_s32(uint8_t *dst, const uint8_t *src,
int nb_samples, int volume)
{
int i;
int32_t *smp_dst = (int32_t *)dst;
const int32_t *smp_src = (const int32_t *)src;
for (i = 0; i < nb_samples; i++)
smp_dst[i] = av_clipl_int32((((int64_t)smp_src[i] * volume + 128) >> 8));
}
static av_cold void volume_init(VolumeContext *vol)
{
vol->samples_align = 1;
switch (av_get_packed_sample_fmt(vol->sample_fmt)) {
case AV_SAMPLE_FMT_U8:
if (vol->volume_i < 0x1000000)
vol->scale_samples = scale_samples_u8_small;
else
vol->scale_samples = scale_samples_u8;
break;
case AV_SAMPLE_FMT_S16:
if (vol->volume_i < 0x10000)
vol->scale_samples = scale_samples_s16_small;
else
vol->scale_samples = scale_samples_s16;
break;
case AV_SAMPLE_FMT_S32:
vol->scale_samples = scale_samples_s32;
break;
case AV_SAMPLE_FMT_FLT:
avpriv_float_dsp_init(&vol->fdsp, 0);
vol->samples_align = 4;
break;
case AV_SAMPLE_FMT_DBL:
avpriv_float_dsp_init(&vol->fdsp, 0);
vol->samples_align = 8;
break;
}
if (ARCH_X86)
ff_volume_init_x86(vol);
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
VolumeContext *vol = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
vol->sample_fmt = inlink->format;
vol->channels = av_get_channel_layout_nb_channels(inlink->channel_layout);
vol->planes = av_sample_fmt_is_planar(inlink->format) ? vol->channels : 1;
volume_init(vol);
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
VolumeContext *vol = inlink->dst->priv;
AVFilterLink *outlink = inlink->dst->outputs[0];
int nb_samples = buf->nb_samples;
AVFrame *out_buf;
if (vol->volume == 1.0 || vol->volume_i == 256)
return ff_filter_frame(outlink, buf);
/* do volume scaling in-place if input buffer is writable */
if (av_frame_is_writable(buf)) {
out_buf = buf;
} else {
out_buf = ff_get_audio_buffer(inlink, nb_samples);
if (!out_buf)
return AVERROR(ENOMEM);
av_frame_copy_props(out_buf, buf);
}
if (vol->precision != PRECISION_FIXED || vol->volume_i > 0) {
int p, plane_samples;
if (av_sample_fmt_is_planar(buf->format))
plane_samples = FFALIGN(nb_samples, vol->samples_align);
else
plane_samples = FFALIGN(nb_samples * vol->channels, vol->samples_align);
if (vol->precision == PRECISION_FIXED) {
for (p = 0; p < vol->planes; p++) {
vol->scale_samples(out_buf->extended_data[p],
buf->extended_data[p], plane_samples,
vol->volume_i);
}
} else if (av_get_packed_sample_fmt(vol->sample_fmt) == AV_SAMPLE_FMT_FLT) {
for (p = 0; p < vol->planes; p++) {
vol->fdsp.vector_fmul_scalar((float *)out_buf->extended_data[p],
(const float *)buf->extended_data[p],
vol->volume, plane_samples);
}
} else {
for (p = 0; p < vol->planes; p++) {
vol->fdsp.vector_dmul_scalar((double *)out_buf->extended_data[p],
(const double *)buf->extended_data[p],
vol->volume, plane_samples);
}
}
}
if (buf != out_buf)
av_frame_free(&buf);
return ff_filter_frame(outlink, out_buf);
}
static const AVFilterPad avfilter_af_volume_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad avfilter_af_volume_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
},
{ NULL }
};
AVFilter avfilter_af_volume = {
.name = "volume",
.description = NULL_IF_CONFIG_SMALL("Change input volume."),
.query_formats = query_formats,
.priv_size = sizeof(VolumeContext),
.priv_class = &volume_class,
.init = init,
.inputs = avfilter_af_volume_inputs,
.outputs = avfilter_af_volume_outputs,
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
};
+55
View File
@@ -0,0 +1,55 @@
/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio volume filter
*/
#ifndef AVFILTER_AF_VOLUME_H
#define AVFILTER_AF_VOLUME_H
#include "libavutil/common.h"
#include "libavutil/float_dsp.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
enum PrecisionType {
PRECISION_FIXED = 0,
PRECISION_FLOAT,
PRECISION_DOUBLE,
};
typedef struct VolumeContext {
const AVClass *class;
AVFloatDSPContext fdsp;
enum PrecisionType precision;
double volume;
int volume_i;
int channels;
int planes;
enum AVSampleFormat sample_fmt;
void (*scale_samples)(uint8_t *dst, const uint8_t *src, int nb_samples,
int volume);
int samples_align;
} VolumeContext;
void ff_volume_init_x86(VolumeContext *vol);
#endif /* AVFILTER_AF_VOLUME_H */
+159
View File
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2012 Nicolas George
*
* 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/channel_layout.h"
#include "libavutil/avassert.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct {
/**
* Number of samples at each PCM value.
* histogram[0x8000 + i] is the number of samples at value i.
* The extra element is there for symmetry.
*/
uint64_t histogram[0x10001];
} VolDetectContext;
static int query_formats(AVFilterContext *ctx)
{
static const enum AVSampleFormat sample_fmts[] = {
AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_NONE
};
AVFilterFormats *formats;
if (!(formats = ff_make_format_list(sample_fmts)))
return AVERROR(ENOMEM);
ff_set_common_formats(ctx, formats);
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *samples)
{
AVFilterContext *ctx = inlink->dst;
VolDetectContext *vd = ctx->priv;
int64_t layout = samples->channel_layout;
int nb_samples = samples->nb_samples;
int nb_channels = av_get_channel_layout_nb_channels(layout);
int nb_planes = nb_channels;
int plane, i;
int16_t *pcm;
if (!av_sample_fmt_is_planar(samples->format)) {
nb_samples *= nb_channels;
nb_planes = 1;
}
for (plane = 0; plane < nb_planes; plane++) {
pcm = (int16_t *)samples->extended_data[plane];
for (i = 0; i < nb_samples; i++)
vd->histogram[pcm[i] + 0x8000]++;
}
return ff_filter_frame(inlink->dst->outputs[0], samples);
}
#define MAX_DB 91
static inline double logdb(uint64_t v)
{
double d = v / (double)(0x8000 * 0x8000);
if (!v)
return MAX_DB;
return log(d) * -4.3429448190325182765112891891660508229; /* -10/log(10) */
}
static void print_stats(AVFilterContext *ctx)
{
VolDetectContext *vd = ctx->priv;
int i, max_volume, shift;
uint64_t nb_samples = 0, power = 0, nb_samples_shift = 0, sum = 0;
uint64_t histdb[MAX_DB + 1] = { 0 };
for (i = 0; i < 0x10000; i++)
nb_samples += vd->histogram[i];
av_log(ctx, AV_LOG_INFO, "n_samples: %"PRId64"\n", nb_samples);
if (!nb_samples)
return;
/* If nb_samples > 1<<34, there is a risk of overflow in the
multiplication or the sum: shift all histogram values to avoid that.
The total number of samples must be recomputed to avoid rounding
errors. */
shift = av_log2(nb_samples >> 33);
for (i = 0; i < 0x10000; i++) {
nb_samples_shift += vd->histogram[i] >> shift;
power += (i - 0x8000) * (i - 0x8000) * (vd->histogram[i] >> shift);
}
if (!nb_samples_shift)
return;
power = (power + nb_samples_shift / 2) / nb_samples_shift;
av_assert0(power <= 0x8000 * 0x8000);
av_log(ctx, AV_LOG_INFO, "mean_volume: %.1f dB\n", -logdb(power));
max_volume = 0x8000;
while (max_volume > 0 && !vd->histogram[0x8000 + max_volume] &&
!vd->histogram[0x8000 - max_volume])
max_volume--;
av_log(ctx, AV_LOG_INFO, "max_volume: %.1f dB\n", -logdb(max_volume * max_volume));
for (i = 0; i < 0x10000; i++)
histdb[(int)logdb((i - 0x8000) * (i - 0x8000))] += vd->histogram[i];
for (i = 0; i <= MAX_DB && !histdb[i]; i++);
for (; i <= MAX_DB && sum < nb_samples / 1000; i++) {
av_log(ctx, AV_LOG_INFO, "histogram_%ddb: %"PRId64"\n", i, histdb[i]);
sum += histdb[i];
}
}
static av_cold void uninit(AVFilterContext *ctx)
{
print_stats(ctx);
}
static const AVFilterPad volumedetect_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad volumedetect_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_volumedetect = {
.name = "volumedetect",
.description = NULL_IF_CONFIG_SMALL("Detect audio volume."),
.priv_size = sizeof(VolDetectContext),
.query_formats = query_formats,
.uninit = uninit,
.inputs = volumedetect_inputs,
.outputs = volumedetect_outputs,
};
@@ -0,0 +1,68 @@
AV_CH_FRONT_CENTER,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY,
AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER,
AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_CENTER|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_LOW_FREQUENCY|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|AV_CH_BACK_CENTER|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT|AV_CH_STEREO_LEFT|AV_CH_STEREO_RIGHT,
+249
View File
@@ -0,0 +1,249 @@
/*
* filter registration
* 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
*/
#include "avfilter.h"
#include "config.h"
#include "opencl_allkernels.h"
#define REGISTER_FILTER(X, x, y) \
{ \
extern AVFilter avfilter_##y##_##x; \
if (CONFIG_##X##_FILTER) \
avfilter_register(&avfilter_##y##_##x); \
}
#define REGISTER_FILTER_UNCONDITIONAL(x) \
{ \
extern AVFilter avfilter_##x; \
avfilter_register(&avfilter_##x); \
}
void avfilter_register_all(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;
#if FF_API_ACONVERT_FILTER
REGISTER_FILTER(ACONVERT, aconvert, af);
#endif
REGISTER_FILTER(ADELAY, adelay, af);
REGISTER_FILTER(AECHO, aecho, af);
REGISTER_FILTER(AFADE, afade, af);
REGISTER_FILTER(AFORMAT, aformat, af);
REGISTER_FILTER(AINTERLEAVE, ainterleave, af);
REGISTER_FILTER(ALLPASS, allpass, af);
REGISTER_FILTER(AMERGE, amerge, af);
REGISTER_FILTER(AMIX, amix, af);
REGISTER_FILTER(ANULL, anull, af);
REGISTER_FILTER(APAD, apad, af);
REGISTER_FILTER(APERMS, aperms, af);
REGISTER_FILTER(APHASER, aphaser, af);
REGISTER_FILTER(ARESAMPLE, aresample, af);
REGISTER_FILTER(ASELECT, aselect, af);
REGISTER_FILTER(ASENDCMD, asendcmd, af);
REGISTER_FILTER(ASETNSAMPLES, asetnsamples, af);
REGISTER_FILTER(ASETPTS, asetpts, af);
REGISTER_FILTER(ASETRATE, asetrate, af);
REGISTER_FILTER(ASETTB, asettb, af);
REGISTER_FILTER(ASHOWINFO, ashowinfo, af);
REGISTER_FILTER(ASPLIT, asplit, af);
REGISTER_FILTER(ASTATS, astats, af);
REGISTER_FILTER(ASTREAMSYNC, astreamsync, af);
REGISTER_FILTER(ASYNCTS, asyncts, af);
REGISTER_FILTER(ATEMPO, atempo, af);
REGISTER_FILTER(ATRIM, atrim, af);
REGISTER_FILTER(AZMQ, azmq, af);
REGISTER_FILTER(BANDPASS, bandpass, af);
REGISTER_FILTER(BANDREJECT, bandreject, af);
REGISTER_FILTER(BASS, bass, af);
REGISTER_FILTER(BIQUAD, biquad, af);
REGISTER_FILTER(CHANNELMAP, channelmap, af);
REGISTER_FILTER(CHANNELSPLIT, channelsplit, af);
REGISTER_FILTER(COMPAND, compand, af);
REGISTER_FILTER(EARWAX, earwax, af);
REGISTER_FILTER(EBUR128, ebur128, af);
REGISTER_FILTER(EQUALIZER, equalizer, af);
REGISTER_FILTER(HIGHPASS, highpass, af);
REGISTER_FILTER(JOIN, join, af);
REGISTER_FILTER(LADSPA, ladspa, af);
REGISTER_FILTER(LOWPASS, lowpass, af);
REGISTER_FILTER(PAN, pan, af);
REGISTER_FILTER(REPLAYGAIN, replaygain, af);
REGISTER_FILTER(RESAMPLE, resample, af);
REGISTER_FILTER(SILENCEDETECT, silencedetect, af);
REGISTER_FILTER(TREBLE, treble, af);
REGISTER_FILTER(VOLUME, volume, af);
REGISTER_FILTER(VOLUMEDETECT, volumedetect, af);
REGISTER_FILTER(AEVALSRC, aevalsrc, asrc);
REGISTER_FILTER(ANULLSRC, anullsrc, asrc);
REGISTER_FILTER(FLITE, flite, asrc);
REGISTER_FILTER(SINE, sine, asrc);
REGISTER_FILTER(ANULLSINK, anullsink, asink);
REGISTER_FILTER(ALPHAEXTRACT, alphaextract, vf);
REGISTER_FILTER(ALPHAMERGE, alphamerge, vf);
REGISTER_FILTER(ASS, ass, vf);
REGISTER_FILTER(BBOX, bbox, vf);
REGISTER_FILTER(BLACKDETECT, blackdetect, vf);
REGISTER_FILTER(BLACKFRAME, blackframe, vf);
REGISTER_FILTER(BLEND, blend, vf);
REGISTER_FILTER(BOXBLUR, boxblur, vf);
REGISTER_FILTER(COLORBALANCE, colorbalance, vf);
REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf);
REGISTER_FILTER(COLORMATRIX, colormatrix, vf);
REGISTER_FILTER(COPY, copy, vf);
REGISTER_FILTER(CROP, crop, vf);
REGISTER_FILTER(CROPDETECT, cropdetect, vf);
REGISTER_FILTER(CURVES, curves, vf);
REGISTER_FILTER(DCTDNOIZ, dctdnoiz, vf);
REGISTER_FILTER(DECIMATE, decimate, vf);
REGISTER_FILTER(DELOGO, delogo, vf);
REGISTER_FILTER(DESHAKE, deshake, vf);
REGISTER_FILTER(DRAWBOX, drawbox, vf);
REGISTER_FILTER(DRAWGRID, drawgrid, vf);
REGISTER_FILTER(DRAWTEXT, drawtext, vf);
REGISTER_FILTER(EDGEDETECT, edgedetect, vf);
REGISTER_FILTER(EXTRACTPLANES, extractplanes, vf);
REGISTER_FILTER(FADE, fade, vf);
REGISTER_FILTER(FIELD, field, vf);
REGISTER_FILTER(FIELDMATCH, fieldmatch, vf);
REGISTER_FILTER(FIELDORDER, fieldorder, vf);
REGISTER_FILTER(FORMAT, format, vf);
REGISTER_FILTER(FPS, fps, vf);
REGISTER_FILTER(FRAMESTEP, framestep, vf);
REGISTER_FILTER(FREI0R, frei0r, vf);
REGISTER_FILTER(GEQ, geq, vf);
REGISTER_FILTER(GRADFUN, gradfun, vf);
REGISTER_FILTER(HALDCLUT, haldclut, vf);
REGISTER_FILTER(HFLIP, hflip, vf);
REGISTER_FILTER(HISTEQ, histeq, vf);
REGISTER_FILTER(HISTOGRAM, histogram, vf);
REGISTER_FILTER(HQDN3D, hqdn3d, vf);
REGISTER_FILTER(HUE, hue, vf);
REGISTER_FILTER(IDET, idet, vf);
REGISTER_FILTER(IL, il, vf);
REGISTER_FILTER(INTERLACE, interlace, vf);
REGISTER_FILTER(INTERLEAVE, interleave, vf);
REGISTER_FILTER(KERNDEINT, kerndeint, vf);
REGISTER_FILTER(LUT3D, lut3d, vf);
REGISTER_FILTER(LUT, lut, vf);
REGISTER_FILTER(LUTRGB, lutrgb, vf);
REGISTER_FILTER(LUTYUV, lutyuv, vf);
REGISTER_FILTER(MCDEINT, mcdeint, vf);
REGISTER_FILTER(MERGEPLANES, mergeplanes, vf);
REGISTER_FILTER(MP, mp, vf);
REGISTER_FILTER(MPDECIMATE, mpdecimate, vf);
REGISTER_FILTER(NEGATE, negate, vf);
REGISTER_FILTER(NOFORMAT, noformat, vf);
REGISTER_FILTER(NOISE, noise, vf);
REGISTER_FILTER(NULL, null, vf);
REGISTER_FILTER(OCV, ocv, vf);
REGISTER_FILTER(OVERLAY, overlay, vf);
REGISTER_FILTER(OWDENOISE, owdenoise, vf);
REGISTER_FILTER(PAD, pad, vf);
REGISTER_FILTER(PERMS, perms, vf);
REGISTER_FILTER(PERSPECTIVE, perspective, vf);
REGISTER_FILTER(PHASE, phase, vf);
REGISTER_FILTER(PIXDESCTEST, pixdesctest, vf);
REGISTER_FILTER(PP, pp, vf);
REGISTER_FILTER(PSNR, psnr, vf);
REGISTER_FILTER(PULLUP, pullup, vf);
REGISTER_FILTER(REMOVELOGO, removelogo, vf);
REGISTER_FILTER(ROTATE, rotate, vf);
REGISTER_FILTER(SAB, sab, vf);
REGISTER_FILTER(SCALE, scale, vf);
REGISTER_FILTER(SELECT, select, vf);
REGISTER_FILTER(SENDCMD, sendcmd, vf);
REGISTER_FILTER(SEPARATEFIELDS, separatefields, vf);
REGISTER_FILTER(SETDAR, setdar, vf);
REGISTER_FILTER(SETFIELD, setfield, vf);
REGISTER_FILTER(SETPTS, setpts, vf);
REGISTER_FILTER(SETSAR, setsar, vf);
REGISTER_FILTER(SETTB, settb, vf);
REGISTER_FILTER(SHOWINFO, showinfo, vf);
REGISTER_FILTER(SMARTBLUR, smartblur, vf);
REGISTER_FILTER(SPLIT, split, vf);
REGISTER_FILTER(SPP, spp, vf);
REGISTER_FILTER(STEREO3D, stereo3d, vf);
REGISTER_FILTER(SUBTITLES, subtitles, vf);
REGISTER_FILTER(SUPER2XSAI, super2xsai, vf);
REGISTER_FILTER(SWAPUV, swapuv, vf);
REGISTER_FILTER(TELECINE, telecine, vf);
REGISTER_FILTER(THUMBNAIL, thumbnail, vf);
REGISTER_FILTER(TILE, tile, vf);
REGISTER_FILTER(TINTERLACE, tinterlace, vf);
REGISTER_FILTER(TRANSPOSE, transpose, vf);
REGISTER_FILTER(TRIM, trim, vf);
REGISTER_FILTER(UNSHARP, unsharp, vf);
REGISTER_FILTER(VFLIP, vflip, vf);
REGISTER_FILTER(VIDSTABDETECT, vidstabdetect, vf);
REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
REGISTER_FILTER(VIGNETTE, vignette, vf);
REGISTER_FILTER(W3FDIF, w3fdif, vf);
REGISTER_FILTER(YADIF, yadif, vf);
REGISTER_FILTER(ZMQ, zmq, vf);
REGISTER_FILTER(CELLAUTO, cellauto, vsrc);
REGISTER_FILTER(COLOR, color, vsrc);
REGISTER_FILTER(FREI0R, frei0r_src, vsrc);
REGISTER_FILTER(HALDCLUTSRC, haldclutsrc, vsrc);
REGISTER_FILTER(LIFE, life, vsrc);
REGISTER_FILTER(MANDELBROT, mandelbrot, vsrc);
REGISTER_FILTER(MPTESTSRC, mptestsrc, vsrc);
REGISTER_FILTER(NULLSRC, nullsrc, vsrc);
REGISTER_FILTER(RGBTESTSRC, rgbtestsrc, vsrc);
REGISTER_FILTER(SMPTEBARS, smptebars, vsrc);
REGISTER_FILTER(SMPTEHDBARS, smptehdbars, vsrc);
REGISTER_FILTER(TESTSRC, testsrc, vsrc);
REGISTER_FILTER(NULLSINK, nullsink, vsink);
/* multimedia filters */
REGISTER_FILTER(AVECTORSCOPE, avectorscope, avf);
REGISTER_FILTER(CONCAT, concat, avf);
REGISTER_FILTER(SHOWSPECTRUM, showspectrum, avf);
REGISTER_FILTER(SHOWWAVES, showwaves, avf);
/* multimedia sources */
REGISTER_FILTER(AMOVIE, amovie, avsrc);
REGISTER_FILTER(MOVIE, movie, avsrc);
#if FF_API_AVFILTERBUFFER
REGISTER_FILTER_UNCONDITIONAL(vsink_ffbuffersink);
REGISTER_FILTER_UNCONDITIONAL(asink_ffabuffersink);
#endif
/* those filters are part of public or internal API => registered
* unconditionally */
REGISTER_FILTER_UNCONDITIONAL(asrc_abuffer);
REGISTER_FILTER_UNCONDITIONAL(vsrc_buffer);
REGISTER_FILTER_UNCONDITIONAL(asink_abuffer);
REGISTER_FILTER_UNCONDITIONAL(vsink_buffer);
REGISTER_FILTER_UNCONDITIONAL(af_afifo);
REGISTER_FILTER_UNCONDITIONAL(vf_fifo);
ff_opencl_register_filter_kernel_code_all();
}
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2010 S.N. Hemanth Meenakshisundaram <[email protected]>
*
* 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/internal.h"
#include "avfilter.h"
#include "internal.h"
static int null_filter_frame(AVFilterLink *link, AVFrame *frame)
{
av_frame_free(&frame);
return 0;
}
static const AVFilterPad avfilter_asink_anullsink_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = null_filter_frame,
},
{ NULL },
};
AVFilter avfilter_asink_anullsink = {
.name = "anullsink",
.description = NULL_IF_CONFIG_SMALL("Do absolutely nothing with the input audio."),
.priv_size = 0,
.inputs = avfilter_asink_anullsink_inputs,
.outputs = NULL,
};
+91
View File
@@ -0,0 +1,91 @@
/*
* 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_ASRC_ABUFFER_H
#define AVFILTER_ASRC_ABUFFER_H
#include "avfilter.h"
/**
* @file
* memory buffer source for audio
*
* @deprecated use buffersrc.h instead.
*/
/**
* Queue an audio buffer to the audio buffer source.
*
* @param abuffersrc audio source buffer context
* @param data pointers to the samples planes
* @param linesize linesizes of each audio buffer plane
* @param nb_samples number of samples per channel
* @param sample_fmt sample format of the audio data
* @param ch_layout channel layout of the audio data
* @param planar flag to indicate if audio data is planar or packed
* @param pts presentation timestamp of the audio buffer
* @param flags unused
*
* @deprecated use av_buffersrc_add_ref() instead.
*/
attribute_deprecated
int av_asrc_buffer_add_samples(AVFilterContext *abuffersrc,
uint8_t *data[8], int linesize[8],
int nb_samples, int sample_rate,
int sample_fmt, int64_t ch_layout, int planar,
int64_t pts, int av_unused flags);
/**
* Queue an audio buffer to the audio buffer source.
*
* This is similar to av_asrc_buffer_add_samples(), but the samples
* are stored in a buffer with known size.
*
* @param abuffersrc audio source buffer context
* @param buf pointer to the samples data, packed is assumed
* @param size the size in bytes of the buffer, it must contain an
* integer number of samples
* @param sample_fmt sample format of the audio data
* @param ch_layout channel layout of the audio data
* @param pts presentation timestamp of the audio buffer
* @param flags unused
*
* @deprecated use av_buffersrc_add_ref() instead.
*/
attribute_deprecated
int av_asrc_buffer_add_buffer(AVFilterContext *abuffersrc,
uint8_t *buf, int buf_size,
int sample_rate,
int sample_fmt, int64_t ch_layout, int planar,
int64_t pts, int av_unused flags);
/**
* Queue an audio buffer to the audio buffer source.
*
* @param abuffersrc audio source buffer context
* @param samplesref buffer ref to queue
* @param flags unused
*
* @deprecated use av_buffersrc_add_ref() instead.
*/
attribute_deprecated
int av_asrc_buffer_add_audio_buffer_ref(AVFilterContext *abuffersrc,
AVFilterBufferRef *samplesref,
int av_unused flags);
#endif /* AVFILTER_ASRC_ABUFFER_H */
+242
View File
@@ -0,0 +1,242 @@
/*
* Copyright (c) 2011 Stefano Sabatini
*
* 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
* eval audio source
*/
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/eval.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "avfilter.h"
#include "audio.h"
#include "internal.h"
static const char * const var_names[] = {
"n", ///< number of frame
"t", ///< timestamp expressed in seconds
"s", ///< sample rate
NULL
};
enum var_name {
VAR_N,
VAR_T,
VAR_S,
VAR_VARS_NB
};
typedef struct {
const AVClass *class;
char *sample_rate_str;
int sample_rate;
int64_t chlayout;
char *chlayout_str;
int nb_channels;
int64_t pts;
AVExpr **expr;
char *exprs;
int nb_samples; ///< number of samples per requested frame
int64_t duration;
uint64_t n;
double var_values[VAR_VARS_NB];
} EvalContext;
#define OFFSET(x) offsetof(EvalContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption aevalsrc_options[]= {
{ "exprs", "set the '|'-separated list of channels expressions", OFFSET(exprs), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS },
{ "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS },
{ "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS },
{ "sample_rate", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX, FLAGS },
{ "s", "set the sample rate", OFFSET(sample_rate_str), AV_OPT_TYPE_STRING, {.str = "44100"}, CHAR_MIN, CHAR_MAX, FLAGS },
{ "duration", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },
{ "d", "set audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS },
{ "channel_layout", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ "c", "set channel layout", OFFSET(chlayout_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(aevalsrc);
static av_cold int init(AVFilterContext *ctx)
{
EvalContext *eval = ctx->priv;
char *args1 = av_strdup(eval->exprs);
char *expr, *buf;
int ret;
if (!args1) {
av_log(ctx, AV_LOG_ERROR, "Channels expressions list is empty\n");
ret = eval->exprs ? AVERROR(ENOMEM) : AVERROR(EINVAL);
goto end;
}
/* parse expressions */
buf = args1;
while (expr = av_strtok(buf, "|", &buf)) {
if (!av_dynarray2_add((void **)&eval->expr, &eval->nb_channels, sizeof(*eval->expr), NULL)) {
ret = AVERROR(ENOMEM);
goto end;
}
ret = av_expr_parse(&eval->expr[eval->nb_channels - 1], expr, var_names,
NULL, NULL, NULL, NULL, 0, ctx);
if (ret < 0)
goto end;
}
if (eval->chlayout_str) {
int n;
ret = ff_parse_channel_layout(&eval->chlayout, NULL, eval->chlayout_str, ctx);
if (ret < 0)
goto end;
n = av_get_channel_layout_nb_channels(eval->chlayout);
if (n != eval->nb_channels) {
av_log(ctx, AV_LOG_ERROR,
"Mismatch between the specified number of channels '%d' "
"and the number of channels '%d' in the specified channel layout '%s'\n",
eval->nb_channels, n, eval->chlayout_str);
ret = AVERROR(EINVAL);
goto end;
}
} else {
/* guess channel layout from nb expressions/channels */
eval->chlayout = av_get_default_channel_layout(eval->nb_channels);
if (!eval->chlayout && eval->nb_channels <= 0) {
av_log(ctx, AV_LOG_ERROR, "Invalid number of channels '%d' provided\n",
eval->nb_channels);
ret = AVERROR(EINVAL);
goto end;
}
}
if ((ret = ff_parse_sample_rate(&eval->sample_rate, eval->sample_rate_str, ctx)))
goto end;
eval->n = 0;
end:
av_free(args1);
return ret;
}
static av_cold void uninit(AVFilterContext *ctx)
{
EvalContext *eval = ctx->priv;
int i;
for (i = 0; i < eval->nb_channels; i++) {
av_expr_free(eval->expr[i]);
eval->expr[i] = NULL;
}
av_freep(&eval->expr);
}
static int config_props(AVFilterLink *outlink)
{
EvalContext *eval = outlink->src->priv;
char buf[128];
outlink->time_base = (AVRational){1, eval->sample_rate};
outlink->sample_rate = eval->sample_rate;
eval->var_values[VAR_S] = eval->sample_rate;
av_get_channel_layout_string(buf, sizeof(buf), 0, eval->chlayout);
av_log(outlink->src, AV_LOG_VERBOSE,
"sample_rate:%d chlayout:%s duration:%"PRId64"\n",
eval->sample_rate, buf, eval->duration);
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
EvalContext *eval = ctx->priv;
static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE };
int64_t chlayouts[] = { eval->chlayout ? eval->chlayout : FF_COUNT2LAYOUT(eval->nb_channels) , -1 };
int sample_rates[] = { eval->sample_rate, -1 };
ff_set_common_formats (ctx, ff_make_format_list(sample_fmts));
ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts));
ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates));
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
EvalContext *eval = outlink->src->priv;
AVFrame *samplesref;
int i, j;
int64_t t = av_rescale(eval->n, AV_TIME_BASE, eval->sample_rate);
if (eval->duration >= 0 && t >= eval->duration)
return AVERROR_EOF;
samplesref = ff_get_audio_buffer(outlink, eval->nb_samples);
if (!samplesref)
return AVERROR(ENOMEM);
/* evaluate expression for each single sample and for each channel */
for (i = 0; i < eval->nb_samples; i++, eval->n++) {
eval->var_values[VAR_N] = eval->n;
eval->var_values[VAR_T] = eval->var_values[VAR_N] * (double)1/eval->sample_rate;
for (j = 0; j < eval->nb_channels; j++) {
*((double *) samplesref->extended_data[j] + i) =
av_expr_eval(eval->expr[j], eval->var_values, NULL);
}
}
samplesref->pts = eval->pts;
samplesref->sample_rate = eval->sample_rate;
eval->pts += eval->nb_samples;
return ff_filter_frame(outlink, samplesref);
}
static const AVFilterPad aevalsrc_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_props,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_asrc_aevalsrc = {
.name = "aevalsrc",
.description = NULL_IF_CONFIG_SMALL("Generate an audio signal generated by an expression."),
.query_formats = query_formats,
.init = init,
.uninit = uninit,
.priv_size = sizeof(EvalContext),
.inputs = NULL,
.outputs = aevalsrc_outputs,
.priv_class = &aevalsrc_class,
};
+146
View File
@@ -0,0 +1,146 @@
/*
* Copyright 2010 S.N. Hemanth Meenakshisundaram <smeenaks ucsd edu>
* Copyright 2010 Stefano Sabatini <stefano.sabatini-lala poste it>
*
* 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
* null audio source
*/
#include <inttypes.h>
#include <stdio.h>
#include "libavutil/channel_layout.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct {
const AVClass *class;
char *channel_layout_str;
uint64_t channel_layout;
char *sample_rate_str;
int sample_rate;
int nb_samples; ///< number of samples per requested frame
int64_t pts;
} ANullContext;
#define OFFSET(x) offsetof(ANullContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption anullsrc_options[]= {
{ "channel_layout", "set channel_layout", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, FLAGS },
{ "cl", "set channel_layout", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, FLAGS },
{ "sample_rate", "set sample rate", OFFSET(sample_rate_str) , AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS },
{ "r", "set sample rate", OFFSET(sample_rate_str) , AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS },
{ "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS },
{ "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 0, INT_MAX, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(anullsrc);
static av_cold int init(AVFilterContext *ctx)
{
ANullContext *null = ctx->priv;
int ret;
if ((ret = ff_parse_sample_rate(&null->sample_rate,
null->sample_rate_str, ctx)) < 0)
return ret;
if ((ret = ff_parse_channel_layout(&null->channel_layout, NULL,
null->channel_layout_str, ctx)) < 0)
return ret;
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
ANullContext *null = ctx->priv;
int64_t chlayouts[] = { null->channel_layout, -1 };
int sample_rates[] = { null->sample_rate, -1 };
ff_set_common_formats (ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO));
ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts));
ff_set_common_samplerates (ctx, ff_make_format_list(sample_rates));
return 0;
}
static int config_props(AVFilterLink *outlink)
{
ANullContext *null = outlink->src->priv;
char buf[128];
av_get_channel_layout_string(buf, sizeof(buf), 0, null->channel_layout);
av_log(outlink->src, AV_LOG_VERBOSE,
"sample_rate:%d channel_layout:'%s' nb_samples:%d\n",
null->sample_rate, buf, null->nb_samples);
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
int ret;
ANullContext *null = outlink->src->priv;
AVFrame *samplesref;
samplesref = ff_get_audio_buffer(outlink, null->nb_samples);
if (!samplesref)
return AVERROR(ENOMEM);
samplesref->pts = null->pts;
samplesref->channel_layout = null->channel_layout;
samplesref->sample_rate = outlink->sample_rate;
ret = ff_filter_frame(outlink, av_frame_clone(samplesref));
av_frame_free(&samplesref);
if (ret < 0)
return ret;
null->pts += null->nb_samples;
return ret;
}
static const AVFilterPad avfilter_asrc_anullsrc_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_props,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_asrc_anullsrc = {
.name = "anullsrc",
.description = NULL_IF_CONFIG_SMALL("Null audio source, return empty audio frames."),
.init = init,
.query_formats = query_formats,
.priv_size = sizeof(ANullContext),
.inputs = NULL,
.outputs = avfilter_asrc_anullsrc_outputs,
.priv_class = &anullsrc_class,
};
+283
View File
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2012 Stefano Sabatini
*
* 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
* flite voice synth source
*/
#include <flite/flite.h>
#include "libavutil/channel_layout.h"
#include "libavutil/file.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "audio.h"
#include "formats.h"
#include "internal.h"
typedef struct {
const AVClass *class;
char *voice_str;
char *textfile;
char *text;
cst_wave *wave;
int16_t *wave_samples;
int wave_nb_samples;
int list_voices;
cst_voice *voice;
struct voice_entry *voice_entry;
int64_t pts;
int frame_nb_samples; ///< number of samples per frame
} FliteContext;
#define OFFSET(x) offsetof(FliteContext, x)
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption flite_options[] = {
{ "list_voices", "list voices and exit", OFFSET(list_voices), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
{ "nb_samples", "set number of samples per frame", OFFSET(frame_nb_samples), AV_OPT_TYPE_INT, {.i64=512}, 0, INT_MAX, FLAGS },
{ "n", "set number of samples per frame", OFFSET(frame_nb_samples), AV_OPT_TYPE_INT, {.i64=512}, 0, INT_MAX, FLAGS },
{ "text", "set text to speak", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
{ "textfile", "set filename of the text to speak", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
{ "v", "set voice", OFFSET(voice_str), AV_OPT_TYPE_STRING, {.str="kal"}, CHAR_MIN, CHAR_MAX, FLAGS },
{ "voice", "set voice", OFFSET(voice_str), AV_OPT_TYPE_STRING, {.str="kal"}, CHAR_MIN, CHAR_MAX, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(flite);
static volatile int flite_inited = 0;
/* declare functions for all the supported voices */
#define DECLARE_REGISTER_VOICE_FN(name) \
cst_voice *register_cmu_us_## name(const char *); \
void unregister_cmu_us_## name(cst_voice *);
DECLARE_REGISTER_VOICE_FN(awb);
DECLARE_REGISTER_VOICE_FN(kal);
DECLARE_REGISTER_VOICE_FN(kal16);
DECLARE_REGISTER_VOICE_FN(rms);
DECLARE_REGISTER_VOICE_FN(slt);
struct voice_entry {
const char *name;
cst_voice * (*register_fn)(const char *);
void (*unregister_fn)(cst_voice *);
cst_voice *voice;
unsigned usage_count;
} voice_entry;
#define MAKE_VOICE_STRUCTURE(voice_name) { \
.name = #voice_name, \
.register_fn = register_cmu_us_ ## voice_name, \
.unregister_fn = unregister_cmu_us_ ## voice_name, \
}
static struct voice_entry voice_entries[] = {
MAKE_VOICE_STRUCTURE(awb),
MAKE_VOICE_STRUCTURE(kal),
MAKE_VOICE_STRUCTURE(kal16),
MAKE_VOICE_STRUCTURE(rms),
MAKE_VOICE_STRUCTURE(slt),
};
static void list_voices(void *log_ctx, const char *sep)
{
int i, n = FF_ARRAY_ELEMS(voice_entries);
for (i = 0; i < n; i++)
av_log(log_ctx, AV_LOG_INFO, "%s%s",
voice_entries[i].name, i < (n-1) ? sep : "\n");
}
static int select_voice(struct voice_entry **entry_ret, const char *voice_name, void *log_ctx)
{
int i;
for (i = 0; i < FF_ARRAY_ELEMS(voice_entries); i++) {
struct voice_entry *entry = &voice_entries[i];
if (!strcmp(entry->name, voice_name)) {
if (!entry->voice)
entry->voice = entry->register_fn(NULL);
if (!entry->voice) {
av_log(log_ctx, AV_LOG_ERROR,
"Could not register voice '%s'\n", voice_name);
return AVERROR_UNKNOWN;
}
entry->usage_count++;
*entry_ret = entry;
return 0;
}
}
av_log(log_ctx, AV_LOG_ERROR, "Could not find voice '%s'\n", voice_name);
av_log(log_ctx, AV_LOG_INFO, "Choose between the voices: ");
list_voices(log_ctx, ", ");
return AVERROR(EINVAL);
}
static av_cold int init(AVFilterContext *ctx)
{
FliteContext *flite = ctx->priv;
int ret = 0;
if (flite->list_voices) {
list_voices(ctx, "\n");
return AVERROR_EXIT;
}
if (!flite_inited) {
if (flite_init() < 0) {
av_log(ctx, AV_LOG_ERROR, "flite initialization failed\n");
return AVERROR_UNKNOWN;
}
flite_inited++;
}
if ((ret = select_voice(&flite->voice_entry, flite->voice_str, ctx)) < 0)
return ret;
flite->voice = flite->voice_entry->voice;
if (flite->textfile && flite->text) {
av_log(ctx, AV_LOG_ERROR,
"Both text and textfile options set: only one must be specified\n");
return AVERROR(EINVAL);
}
if (flite->textfile) {
uint8_t *textbuf;
size_t textbuf_size;
if ((ret = av_file_map(flite->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) {
av_log(ctx, AV_LOG_ERROR,
"The text file '%s' could not be read: %s\n",
flite->textfile, av_err2str(ret));
return ret;
}
if (!(flite->text = av_malloc(textbuf_size+1)))
return AVERROR(ENOMEM);
memcpy(flite->text, textbuf, textbuf_size);
flite->text[textbuf_size] = 0;
av_file_unmap(textbuf, textbuf_size);
}
if (!flite->text) {
av_log(ctx, AV_LOG_ERROR,
"No speech text specified, specify the 'text' or 'textfile' option\n");
return AVERROR(EINVAL);
}
/* synth all the file data in block */
flite->wave = flite_text_to_wave(flite->text, flite->voice);
flite->wave_samples = flite->wave->samples;
flite->wave_nb_samples = flite->wave->num_samples;
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
FliteContext *flite = ctx->priv;
if (!--flite->voice_entry->usage_count)
flite->voice_entry->unregister_fn(flite->voice);
flite->voice = NULL;
flite->voice_entry = NULL;
delete_wave(flite->wave);
flite->wave = NULL;
}
static int query_formats(AVFilterContext *ctx)
{
FliteContext *flite = ctx->priv;
AVFilterChannelLayouts *chlayouts = NULL;
int64_t chlayout = av_get_default_channel_layout(flite->wave->num_channels);
AVFilterFormats *sample_formats = NULL;
AVFilterFormats *sample_rates = NULL;
ff_add_channel_layout(&chlayouts, chlayout);
ff_set_common_channel_layouts(ctx, chlayouts);
ff_add_format(&sample_formats, AV_SAMPLE_FMT_S16);
ff_set_common_formats(ctx, sample_formats);
ff_add_format(&sample_rates, flite->wave->sample_rate);
ff_set_common_samplerates (ctx, sample_rates);
return 0;
}
static int config_props(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
FliteContext *flite = ctx->priv;
outlink->sample_rate = flite->wave->sample_rate;
outlink->time_base = (AVRational){1, flite->wave->sample_rate};
av_log(ctx, AV_LOG_VERBOSE, "voice:%s fmt:%s sample_rate:%d\n",
flite->voice_str,
av_get_sample_fmt_name(outlink->format), outlink->sample_rate);
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFrame *samplesref;
FliteContext *flite = outlink->src->priv;
int nb_samples = FFMIN(flite->wave_nb_samples, flite->frame_nb_samples);
if (!nb_samples)
return AVERROR_EOF;
samplesref = ff_get_audio_buffer(outlink, nb_samples);
if (!samplesref)
return AVERROR(ENOMEM);
memcpy(samplesref->data[0], flite->wave_samples,
nb_samples * flite->wave->num_channels * 2);
samplesref->pts = flite->pts;
av_frame_set_pkt_pos(samplesref, -1);
av_frame_set_sample_rate(samplesref, flite->wave->sample_rate);
flite->pts += nb_samples;
flite->wave_samples += nb_samples * flite->wave->num_channels;
flite->wave_nb_samples -= nb_samples;
return ff_filter_frame(outlink, samplesref);
}
static const AVFilterPad flite_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_props,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_asrc_flite = {
.name = "flite",
.description = NULL_IF_CONFIG_SMALL("Synthesize voice from text using libflite."),
.query_formats = query_formats,
.init = init,
.uninit = uninit,
.priv_size = sizeof(FliteContext),
.inputs = NULL,
.outputs = flite_outputs,
.priv_class = &flite_class,
};
+223
View File
@@ -0,0 +1,223 @@
/*
* Copyright (c) 2013 Nicolas George
*
* 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 <float.h>
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
typedef struct {
const AVClass *class;
double frequency;
double beep_factor;
int samples_per_frame;
int sample_rate;
int64_t duration;
int16_t *sin;
int64_t pts;
uint32_t phi; ///< current phase of the sine (2pi = 1<<32)
uint32_t dphi; ///< phase increment between two samples
unsigned beep_period;
unsigned beep_index;
unsigned beep_length;
uint32_t phi_beep; ///< current phase of the beep
uint32_t dphi_beep; ///< phase increment of the beep
} SineContext;
#define CONTEXT SineContext
#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
#define OPT_GENERIC(name, field, def, min, max, descr, type, deffield, ...) \
{ name, descr, offsetof(CONTEXT, field), AV_OPT_TYPE_ ## type, \
{ .deffield = def }, min, max, FLAGS, __VA_ARGS__ }
#define OPT_INT(name, field, def, min, max, descr, ...) \
OPT_GENERIC(name, field, def, min, max, descr, INT, i64, __VA_ARGS__)
#define OPT_DBL(name, field, def, min, max, descr, ...) \
OPT_GENERIC(name, field, def, min, max, descr, DOUBLE, dbl, __VA_ARGS__)
#define OPT_DUR(name, field, def, min, max, descr, ...) \
OPT_GENERIC(name, field, def, min, max, descr, DURATION, str, __VA_ARGS__)
static const AVOption sine_options[] = {
OPT_DBL("frequency", frequency, 440, 0, DBL_MAX, "set the sine frequency"),
OPT_DBL("f", frequency, 440, 0, DBL_MAX, "set the sine frequency"),
OPT_DBL("beep_factor", beep_factor, 0, 0, DBL_MAX, "set the beep fequency factor"),
OPT_DBL("b", beep_factor, 0, 0, DBL_MAX, "set the beep fequency factor"),
OPT_INT("sample_rate", sample_rate, 44100, 1, INT_MAX, "set the sample rate"),
OPT_INT("r", sample_rate, 44100, 1, INT_MAX, "set the sample rate"),
OPT_DUR("duration", duration, 0, 0, INT64_MAX, "set the audio duration"),
OPT_DUR("d", duration, 0, 0, INT64_MAX, "set the audio duration"),
OPT_INT("samples_per_frame", samples_per_frame, 1024, 0, INT_MAX, "set the number of samples per frame"),
{NULL}
};
AVFILTER_DEFINE_CLASS(sine);
#define LOG_PERIOD 15
#define AMPLITUDE 4095
#define AMPLITUDE_SHIFT 3
static void make_sin_table(int16_t *sin)
{
unsigned half_pi = 1 << (LOG_PERIOD - 2);
unsigned ampls = AMPLITUDE << AMPLITUDE_SHIFT;
uint64_t unit2 = (uint64_t)(ampls * ampls) << 32;
unsigned step, i, c, s, k, new_k, n2;
/* Principle: if u = exp(i*a1) and v = exp(i*a2), then
exp(i*(a1+a2)/2) = (u+v) / length(u+v) */
sin[0] = 0;
sin[half_pi] = ampls;
for (step = half_pi; step > 1; step /= 2) {
/* k = (1 << 16) * amplitude / length(u+v)
In exact values, k is constant at a given step */
k = 0x10000;
for (i = 0; i < half_pi / 2; i += step) {
s = sin[i] + sin[i + step];
c = sin[half_pi - i] + sin[half_pi - i - step];
n2 = s * s + c * c;
/* Newton's method to solve n² * k² = unit² */
while (1) {
new_k = (k + unit2 / ((uint64_t)k * n2) + 1) >> 1;
if (k == new_k)
break;
k = new_k;
}
sin[i + step / 2] = (k * s + 0x7FFF) >> 16;
sin[half_pi - i - step / 2] = (k * c + 0x8000) >> 16;
}
}
/* Unshift amplitude */
for (i = 0; i <= half_pi; i++)
sin[i] = (sin[i] + (1 << (AMPLITUDE_SHIFT - 1))) >> AMPLITUDE_SHIFT;
/* Use symmetries to fill the other three quarters */
for (i = 0; i < half_pi; i++)
sin[half_pi * 2 - i] = sin[i];
for (i = 0; i < 2 * half_pi; i++)
sin[i + 2 * half_pi] = -sin[i];
}
static av_cold int init(AVFilterContext *ctx)
{
SineContext *sine = ctx->priv;
if (!(sine->sin = av_malloc(sizeof(*sine->sin) << LOG_PERIOD)))
return AVERROR(ENOMEM);
sine->dphi = ldexp(sine->frequency, 32) / sine->sample_rate + 0.5;
make_sin_table(sine->sin);
if (sine->beep_factor) {
sine->beep_period = sine->sample_rate;
sine->beep_length = sine->beep_period / 25;
sine->dphi_beep = ldexp(sine->beep_factor * sine->frequency, 32) /
sine->sample_rate + 0.5;
}
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
SineContext *sine = ctx->priv;
av_freep(&sine->sin);
}
static av_cold int query_formats(AVFilterContext *ctx)
{
SineContext *sine = ctx->priv;
static const int64_t chlayouts[] = { AV_CH_LAYOUT_MONO, -1 };
int sample_rates[] = { sine->sample_rate, -1 };
static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_NONE };
ff_set_common_formats (ctx, ff_make_format_list(sample_fmts));
ff_set_common_channel_layouts(ctx, avfilter_make_format64_list(chlayouts));
ff_set_common_samplerates(ctx, ff_make_format_list(sample_rates));
return 0;
}
static av_cold int config_props(AVFilterLink *outlink)
{
SineContext *sine = outlink->src->priv;
sine->duration = av_rescale(sine->duration, sine->sample_rate, AV_TIME_BASE);
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
SineContext *sine = outlink->src->priv;
AVFrame *frame;
int i, nb_samples = sine->samples_per_frame;
int16_t *samples;
if (sine->duration) {
nb_samples = FFMIN(nb_samples, sine->duration - sine->pts);
av_assert1(nb_samples >= 0);
if (!nb_samples)
return AVERROR_EOF;
}
if (!(frame = ff_get_audio_buffer(outlink, nb_samples)))
return AVERROR(ENOMEM);
samples = (int16_t *)frame->data[0];
for (i = 0; i < nb_samples; i++) {
samples[i] = sine->sin[sine->phi >> (32 - LOG_PERIOD)];
sine->phi += sine->dphi;
if (sine->beep_index < sine->beep_length) {
samples[i] += sine->sin[sine->phi_beep >> (32 - LOG_PERIOD)] << 1;
sine->phi_beep += sine->dphi_beep;
}
if (++sine->beep_index == sine->beep_period)
sine->beep_index = 0;
}
frame->pts = sine->pts;
sine->pts += nb_samples;
return ff_filter_frame(outlink, frame);
}
static const AVFilterPad sine_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.request_frame = request_frame,
.config_props = config_props,
},
{ NULL }
};
AVFilter avfilter_asrc_sine = {
.name = "sine",
.description = NULL_IF_CONFIG_SMALL("Generate sine wave audio signal."),
.query_formats = query_formats,
.init = init,
.uninit = uninit,
.priv_size = sizeof(SineContext),
.inputs = NULL,
.outputs = sine_outputs,
.priv_class = &sine_class,
};
+170
View File
@@ -0,0 +1,170 @@
/*
* Copyright (c) Stefano Sabatini | stefasab at gmail.com
* Copyright (c) S.N. Hemanth Meenakshisundaram | smeenaks at ucsd.edu
*
* 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/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavcodec/avcodec.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
int avfilter_ref_get_channels(AVFilterBufferRef *ref)
{
return ref->audio ? ref->audio->channels : 0;
}
AVFrame *ff_null_get_audio_buffer(AVFilterLink *link, int nb_samples)
{
return ff_get_audio_buffer(link->dst->outputs[0], nb_samples);
}
AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples)
{
AVFrame *frame = av_frame_alloc();
int channels = link->channels;
int ret;
av_assert0(channels == av_get_channel_layout_nb_channels(link->channel_layout) || !av_get_channel_layout_nb_channels(link->channel_layout));
if (!frame)
return NULL;
frame->nb_samples = nb_samples;
frame->format = link->format;
av_frame_set_channels(frame, link->channels);
frame->channel_layout = link->channel_layout;
frame->sample_rate = link->sample_rate;
ret = av_frame_get_buffer(frame, 0);
if (ret < 0) {
av_frame_free(&frame);
return NULL;
}
av_samples_set_silence(frame->extended_data, 0, nb_samples, channels,
link->format);
return frame;
}
AVFrame *ff_get_audio_buffer(AVFilterLink *link, int nb_samples)
{
AVFrame *ret = NULL;
if (link->dstpad->get_audio_buffer)
ret = link->dstpad->get_audio_buffer(link, nb_samples);
if (!ret)
ret = ff_default_get_audio_buffer(link, nb_samples);
return ret;
}
#if FF_API_AVFILTERBUFFER
AVFilterBufferRef* avfilter_get_audio_buffer_ref_from_arrays_channels(uint8_t **data,
int linesize,int perms,
int nb_samples,
enum AVSampleFormat sample_fmt,
int channels,
uint64_t channel_layout)
{
int planes;
AVFilterBuffer *samples = av_mallocz(sizeof(*samples));
AVFilterBufferRef *samplesref = av_mallocz(sizeof(*samplesref));
if (!samples || !samplesref)
goto fail;
av_assert0(channels);
av_assert0(channel_layout == 0 ||
channels == av_get_channel_layout_nb_channels(channel_layout));
samplesref->buf = samples;
samplesref->buf->free = ff_avfilter_default_free_buffer;
if (!(samplesref->audio = av_mallocz(sizeof(*samplesref->audio))))
goto fail;
samplesref->audio->nb_samples = nb_samples;
samplesref->audio->channel_layout = channel_layout;
samplesref->audio->channels = channels;
planes = av_sample_fmt_is_planar(sample_fmt) ? channels : 1;
/* make sure the buffer gets read permission or it's useless for output */
samplesref->perms = perms | AV_PERM_READ;
samples->refcount = 1;
samplesref->type = AVMEDIA_TYPE_AUDIO;
samplesref->format = sample_fmt;
memcpy(samples->data, data,
FFMIN(FF_ARRAY_ELEMS(samples->data), planes)*sizeof(samples->data[0]));
memcpy(samplesref->data, samples->data, sizeof(samples->data));
samples->linesize[0] = samplesref->linesize[0] = linesize;
if (planes > FF_ARRAY_ELEMS(samples->data)) {
samples-> extended_data = av_mallocz(sizeof(*samples->extended_data) *
planes);
samplesref->extended_data = av_mallocz(sizeof(*samplesref->extended_data) *
planes);
if (!samples->extended_data || !samplesref->extended_data)
goto fail;
memcpy(samples-> extended_data, data, sizeof(*data)*planes);
memcpy(samplesref->extended_data, data, sizeof(*data)*planes);
} else {
samples->extended_data = samples->data;
samplesref->extended_data = samplesref->data;
}
samplesref->pts = AV_NOPTS_VALUE;
return samplesref;
fail:
if (samples && samples->extended_data != samples->data)
av_freep(&samples->extended_data);
if (samplesref) {
av_freep(&samplesref->audio);
if (samplesref->extended_data != samplesref->data)
av_freep(&samplesref->extended_data);
}
av_freep(&samplesref);
av_freep(&samples);
return NULL;
}
AVFilterBufferRef* avfilter_get_audio_buffer_ref_from_arrays(uint8_t **data,
int linesize,int perms,
int nb_samples,
enum AVSampleFormat sample_fmt,
uint64_t channel_layout)
{
int channels = av_get_channel_layout_nb_channels(channel_layout);
return avfilter_get_audio_buffer_ref_from_arrays_channels(data, linesize, perms,
nb_samples, sample_fmt,
channels, channel_layout);
}
#endif
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (c) Stefano Sabatini | stefasab at gmail.com
* Copyright (c) S.N. Hemanth Meenakshisundaram | smeenaks at ucsd.edu
*
* 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_AUDIO_H
#define AVFILTER_AUDIO_H
#include "avfilter.h"
#include "internal.h"
static const enum AVSampleFormat ff_packed_sample_fmts_array[] = {
AV_SAMPLE_FMT_U8,
AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_S32,
AV_SAMPLE_FMT_FLT,
AV_SAMPLE_FMT_DBL,
AV_SAMPLE_FMT_NONE
};
static const enum AVSampleFormat ff_planar_sample_fmts_array[] = {
AV_SAMPLE_FMT_U8P,
AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_FLTP,
AV_SAMPLE_FMT_DBLP,
AV_SAMPLE_FMT_NONE
};
/** default handler for get_audio_buffer() for audio inputs */
AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples);
/** get_audio_buffer() handler for filters which simply pass audio along */
AVFrame *ff_null_get_audio_buffer(AVFilterLink *link, int nb_samples);
/**
* Request an audio samples buffer with a specific set of permissions.
*
* @param link the output link to the filter from which the buffer will
* be requested
* @param nb_samples the number of samples per channel
* @return A reference to the samples. This must be unreferenced with
* avfilter_unref_buffer when you are finished with it.
*/
AVFrame *ff_get_audio_buffer(AVFilterLink *link, int nb_samples);
/**
* Send a buffer of audio samples to the next filter.
*
* @param link the output link over which the audio samples are being sent
* @param samplesref a reference to the buffer of audio samples being sent. The
* receiving filter will free this reference when it no longer
* needs it or pass it on to the next filter.
*
* @return >= 0 on success, a negative AVERROR on error. The receiving filter
* is responsible for unreferencing samplesref in case of error.
*/
int ff_filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref);
/**
* Send a buffer of audio samples to the next link, without checking
* min_samples.
*/
int ff_filter_samples_framed(AVFilterLink *link,
AVFilterBufferRef *samplesref);
#endif /* AVFILTER_AUDIO_H */
+157
View File
@@ -0,0 +1,157 @@
/*
* Copyright 2011 Stefano Sabatini | stefasab at gmail.com
*
* 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
* libavcodec/libavfilter gluing utilities
*/
#include "avcodec.h"
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#if FF_API_AVFILTERBUFFER
AVFilterBufferRef *avfilter_get_video_buffer_ref_from_frame(const AVFrame *frame,
int perms)
{
AVFilterBufferRef *picref =
avfilter_get_video_buffer_ref_from_arrays(frame->data, frame->linesize, perms,
frame->width, frame->height,
frame->format);
if (!picref)
return NULL;
if (avfilter_copy_frame_props(picref, frame) < 0) {
picref->buf->data[0] = NULL;
avfilter_unref_bufferp(&picref);
}
return picref;
}
AVFilterBufferRef *avfilter_get_audio_buffer_ref_from_frame(const AVFrame *frame,
int perms)
{
AVFilterBufferRef *samplesref;
int channels = av_frame_get_channels(frame);
int64_t layout = av_frame_get_channel_layout(frame);
if (layout && av_get_channel_layout_nb_channels(layout) != av_frame_get_channels(frame)) {
av_log(0, AV_LOG_ERROR, "Layout indicates a different number of channels than actually present\n");
return NULL;
}
samplesref = avfilter_get_audio_buffer_ref_from_arrays_channels(
(uint8_t **)frame->extended_data, frame->linesize[0], perms,
frame->nb_samples, frame->format, channels, layout);
if (!samplesref)
return NULL;
if (avfilter_copy_frame_props(samplesref, frame) < 0) {
samplesref->buf->data[0] = NULL;
avfilter_unref_bufferp(&samplesref);
}
return samplesref;
}
AVFilterBufferRef *avfilter_get_buffer_ref_from_frame(enum AVMediaType type,
const AVFrame *frame,
int perms)
{
switch (type) {
case AVMEDIA_TYPE_VIDEO:
return avfilter_get_video_buffer_ref_from_frame(frame, perms);
case AVMEDIA_TYPE_AUDIO:
return avfilter_get_audio_buffer_ref_from_frame(frame, perms);
default:
return NULL;
}
}
int avfilter_copy_buf_props(AVFrame *dst, const AVFilterBufferRef *src)
{
int planes, nb_channels;
if (!dst)
return AVERROR(EINVAL);
/* abort in case the src is NULL and dst is not, avoid inconsistent state in dst */
av_assert0(src);
memcpy(dst->data, src->data, sizeof(dst->data));
memcpy(dst->linesize, src->linesize, sizeof(dst->linesize));
dst->pts = src->pts;
dst->format = src->format;
av_frame_set_pkt_pos(dst, src->pos);
switch (src->type) {
case AVMEDIA_TYPE_VIDEO:
av_assert0(src->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:
av_assert0(src->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->nb_samples = src->audio->nb_samples;
av_frame_set_sample_rate (dst, src->audio->sample_rate);
av_frame_set_channel_layout(dst, src->audio->channel_layout);
av_frame_set_channels (dst, src->audio->channels);
break;
default:
return AVERROR(EINVAL);
}
return 0;
}
#endif
#if FF_API_FILL_FRAME
int avfilter_fill_frame_from_audio_buffer_ref(AVFrame *frame,
const AVFilterBufferRef *samplesref)
{
return avfilter_copy_buf_props(frame, samplesref);
}
int avfilter_fill_frame_from_video_buffer_ref(AVFrame *frame,
const AVFilterBufferRef *picref)
{
return avfilter_copy_buf_props(frame, picref);
}
int avfilter_fill_frame_from_buffer_ref(AVFrame *frame,
const AVFilterBufferRef *ref)
{
return avfilter_copy_buf_props(frame, ref);
}
#endif
+110
View File
@@ -0,0 +1,110 @@
/*
* 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_AVCODEC_H
#define AVFILTER_AVCODEC_H
/**
* @file
* libavcodec/libavfilter gluing utilities
*
* This should be included in an application ONLY if the installed
* libavfilter has been compiled with libavcodec support, otherwise
* symbols defined below will not be available.
*/
#include "avfilter.h"
#if FF_API_AVFILTERBUFFER
/**
* Create and return a picref reference from the data and properties
* contained in frame.
*
* @param perms permissions to assign to the new buffer reference
* @deprecated avfilter APIs work natively with AVFrame instead.
*/
attribute_deprecated
AVFilterBufferRef *avfilter_get_video_buffer_ref_from_frame(const AVFrame *frame, int perms);
/**
* Create and return a picref reference from the data and properties
* contained in frame.
*
* @param perms permissions to assign to the new buffer reference
* @deprecated avfilter APIs work natively with AVFrame instead.
*/
attribute_deprecated
AVFilterBufferRef *avfilter_get_audio_buffer_ref_from_frame(const AVFrame *frame,
int perms);
/**
* Create and return a buffer reference from the data and properties
* contained in frame.
*
* @param perms permissions to assign to the new buffer reference
* @deprecated avfilter APIs work natively with AVFrame instead.
*/
attribute_deprecated
AVFilterBufferRef *avfilter_get_buffer_ref_from_frame(enum AVMediaType type,
const AVFrame *frame,
int perms);
#endif
#if FF_API_FILL_FRAME
/**
* Fill an AVFrame with the information stored in samplesref.
*
* @param frame an already allocated AVFrame
* @param samplesref an audio buffer reference
* @return >= 0 in case of success, a negative AVERROR code in case of
* failure
* @deprecated Use avfilter_copy_buf_props() instead.
*/
attribute_deprecated
int avfilter_fill_frame_from_audio_buffer_ref(AVFrame *frame,
const AVFilterBufferRef *samplesref);
/**
* Fill an AVFrame with the information stored in picref.
*
* @param frame an already allocated AVFrame
* @param picref a video buffer reference
* @return >= 0 in case of success, a negative AVERROR code in case of
* failure
* @deprecated Use avfilter_copy_buf_props() instead.
*/
attribute_deprecated
int avfilter_fill_frame_from_video_buffer_ref(AVFrame *frame,
const AVFilterBufferRef *picref);
/**
* Fill an AVFrame with information stored in ref.
*
* @param frame an already allocated AVFrame
* @param ref a video or audio buffer reference
* @return >= 0 in case of success, a negative AVERROR code in case of
* failure
* @deprecated Use avfilter_copy_buf_props() instead.
*/
attribute_deprecated
int avfilter_fill_frame_from_buffer_ref(AVFrame *frame,
const AVFilterBufferRef *ref);
#endif
#endif /* AVFILTER_AVCODEC_H */
@@ -0,0 +1,273 @@
/*
* Copyright (c) 2013 Paul B Mahol
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio to video multimedia vectorscope filter
*/
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "avfilter.h"
#include "formats.h"
#include "audio.h"
#include "video.h"
#include "internal.h"
enum VectorScopeMode {
LISSAJOUS,
LISSAJOUS_XY,
MODE_NB,
};
typedef struct AudioVectorScopeContext {
const AVClass *class;
AVFrame *outpicref;
int w, h;
int hw, hh;
enum VectorScopeMode mode;
int contrast[3];
int fade[3];
double zoom;
AVRational frame_rate;
} AudioVectorScopeContext;
#define OFFSET(x) offsetof(AudioVectorScopeContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption avectorscope_options[] = {
{ "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=LISSAJOUS}, 0, MODE_NB-1, FLAGS, "mode" },
{ "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=LISSAJOUS}, 0, MODE_NB-1, FLAGS, "mode" },
{ "lissajous", "", 0, AV_OPT_TYPE_CONST, {.i64=LISSAJOUS}, 0, 0, FLAGS, "mode" },
{ "lissajous_xy", "", 0, AV_OPT_TYPE_CONST, {.i64=LISSAJOUS_XY}, 0, 0, FLAGS, "mode" },
{ "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
{ "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, 0, FLAGS },
{ "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="400x400"}, 0, 0, FLAGS },
{ "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="400x400"}, 0, 0, FLAGS },
{ "rc", "set red contrast", OFFSET(contrast[0]), AV_OPT_TYPE_INT, {.i64=40}, 0, 255, FLAGS },
{ "gc", "set green contrast", OFFSET(contrast[1]), AV_OPT_TYPE_INT, {.i64=160}, 0, 255, FLAGS },
{ "bc", "set blue contrast", OFFSET(contrast[2]), AV_OPT_TYPE_INT, {.i64=80}, 0, 255, FLAGS },
{ "rf", "set red fade", OFFSET(fade[0]), AV_OPT_TYPE_INT, {.i64=15}, 0, 255, FLAGS },
{ "gf", "set green fade", OFFSET(fade[1]), AV_OPT_TYPE_INT, {.i64=10}, 0, 255, FLAGS },
{ "bf", "set blue fade", OFFSET(fade[2]), AV_OPT_TYPE_INT, {.i64=5}, 0, 255, FLAGS },
{ "zoom", "set zoom factor", OFFSET(zoom), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 1, 10, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(avectorscope);
static void draw_dot(AudioVectorScopeContext *p, unsigned x, unsigned y)
{
const int linesize = p->outpicref->linesize[0];
uint8_t *dst;
if (p->zoom > 1) {
if (y >= p->h || x >= p->w)
return;
} else {
y = FFMIN(y, p->h - 1);
x = FFMIN(x, p->w - 1);
}
dst = &p->outpicref->data[0][y * linesize + x * 4];
dst[0] = FFMIN(dst[0] + p->contrast[0], 255);
dst[1] = FFMIN(dst[1] + p->contrast[1], 255);
dst[2] = FFMIN(dst[2] + p->contrast[2], 255);
}
static void fade(AudioVectorScopeContext *p)
{
const int linesize = p->outpicref->linesize[0];
int i, j;
if (p->fade[0] || p->fade[1] || p->fade[2]) {
uint8_t *d = p->outpicref->data[0];
for (i = 0; i < p->h; i++) {
for (j = 0; j < p->w*4; j+=4) {
d[j+0] = FFMAX(d[j+0] - p->fade[0], 0);
d[j+1] = FFMAX(d[j+1] - p->fade[1], 0);
d[j+2] = FFMAX(d[j+2] - p->fade[2], 0);
}
d += linesize;
}
}
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layout = NULL;
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE };
static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE };
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_formats);
ff_add_channel_layout(&layout, AV_CH_LAYOUT_STEREO);
ff_channel_layouts_ref(layout, &inlink->out_channel_layouts);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_samplerates);
formats = ff_make_format_list(pix_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &outlink->in_formats);
return 0;
}
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
AudioVectorScopeContext *p = ctx->priv;
int nb_samples;
nb_samples = FFMAX(1024, ((double)inlink->sample_rate / av_q2d(p->frame_rate)) + 0.5);
inlink->partial_buf_size =
inlink->min_samples =
inlink->max_samples = nb_samples;
return 0;
}
static int config_output(AVFilterLink *outlink)
{
AudioVectorScopeContext *p = outlink->src->priv;
outlink->w = p->w;
outlink->h = p->h;
outlink->sample_aspect_ratio = (AVRational){1,1};
outlink->frame_rate = p->frame_rate;
p->hw = p->w / 2;
p->hh = p->h / 2;
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
AudioVectorScopeContext *p = ctx->priv;
const int hw = p->hw;
const int hh = p->hh;
unsigned x, y;
const double zoom = p->zoom;
int i;
if (!p->outpicref || p->outpicref->width != outlink->w ||
p->outpicref->height != outlink->h) {
av_frame_free(&p->outpicref);
p->outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h);
if (!p->outpicref)
av_frame_free(&insamples);
return AVERROR(ENOMEM);
for (i = 0; i < outlink->h; i++)
memset(p->outpicref->data[0] + i * p->outpicref->linesize[0], 0, outlink->w * 4);
}
p->outpicref->pts = insamples->pts;
fade(p);
switch (insamples->format) {
case AV_SAMPLE_FMT_S16:
for (i = 0; i < insamples->nb_samples; i++) {
int16_t *src = (int16_t *)insamples->data[0] + i * 2;
if (p->mode == LISSAJOUS) {
x = ((src[1] - src[0]) * zoom / (float)(UINT16_MAX) + 1) * hw;
y = (1.0 - (src[0] + src[1]) * zoom / (float)UINT16_MAX) * hh;
} else {
x = (src[1] * zoom / (float)INT16_MAX + 1) * hw;
y = (src[0] * zoom / (float)INT16_MAX + 1) * hh;
}
draw_dot(p, x, y);
}
break;
case AV_SAMPLE_FMT_FLT:
for (i = 0; i < insamples->nb_samples; i++) {
float *src = (float *)insamples->data[0] + i * 2;
if (p->mode == LISSAJOUS) {
x = ((src[1] - src[0]) * zoom / 2 + 1) * hw;
y = (1.0 - (src[0] + src[1]) * zoom / 2) * hh;
} else {
x = (src[1] * zoom + 1) * hw;
y = (src[0] * zoom + 1) * hh;
}
draw_dot(p, x, y);
}
break;
}
av_frame_free(&insamples);
return ff_filter_frame(outlink, av_frame_clone(p->outpicref));
}
static av_cold void uninit(AVFilterContext *ctx)
{
AudioVectorScopeContext *p = ctx->priv;
av_frame_free(&p->outpicref);
}
static const AVFilterPad audiovectorscope_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_input,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad audiovectorscope_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_output,
},
{ NULL }
};
AVFilter avfilter_avf_avectorscope = {
.name = "avectorscope",
.description = NULL_IF_CONFIG_SMALL("Convert input audio to vectorscope video output."),
.uninit = uninit,
.query_formats = query_formats,
.priv_size = sizeof(AudioVectorScopeContext),
.inputs = audiovectorscope_inputs,
.outputs = audiovectorscope_outputs,
.priv_class = &avectorscope_class,
};
+426
View File
@@ -0,0 +1,426 @@
/*
* Copyright (c) 2012 Nicolas George
*
* 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
* concat audio-video filter
*/
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#define FF_BUFQUEUE_SIZE 256
#include "bufferqueue.h"
#include "internal.h"
#include "video.h"
#include "audio.h"
#define TYPE_ALL 2
typedef struct {
const AVClass *class;
unsigned nb_streams[TYPE_ALL]; /**< number of out streams of each type */
unsigned nb_segments;
unsigned cur_idx; /**< index of the first input of current segment */
int64_t delta_ts; /**< timestamp to add to produce output timestamps */
unsigned nb_in_active; /**< number of active inputs in current segment */
unsigned unsafe;
struct concat_in {
int64_t pts;
int64_t nb_frames;
unsigned eof;
struct FFBufQueue queue;
} *in;
} ConcatContext;
#define OFFSET(x) offsetof(ConcatContext, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
#define V AV_OPT_FLAG_VIDEO_PARAM
static const AVOption concat_options[] = {
{ "n", "specify the number of segments", OFFSET(nb_segments),
AV_OPT_TYPE_INT, { .i64 = 2 }, 2, INT_MAX, V|A|F},
{ "v", "specify the number of video streams",
OFFSET(nb_streams[AVMEDIA_TYPE_VIDEO]),
AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, V|F },
{ "a", "specify the number of audio streams",
OFFSET(nb_streams[AVMEDIA_TYPE_AUDIO]),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A|F},
{ "unsafe", "enable unsafe mode",
OFFSET(unsafe),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V|A|F},
{ NULL }
};
AVFILTER_DEFINE_CLASS(concat);
static int query_formats(AVFilterContext *ctx)
{
ConcatContext *cat = ctx->priv;
unsigned type, nb_str, idx0 = 0, idx, str, seg;
AVFilterFormats *formats, *rates = NULL;
AVFilterChannelLayouts *layouts = NULL;
for (type = 0; type < TYPE_ALL; type++) {
nb_str = cat->nb_streams[type];
for (str = 0; str < nb_str; str++) {
idx = idx0;
/* Set the output formats */
formats = ff_all_formats(type);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &ctx->outputs[idx]->in_formats);
if (type == AVMEDIA_TYPE_AUDIO) {
rates = ff_all_samplerates();
if (!rates)
return AVERROR(ENOMEM);
ff_formats_ref(rates, &ctx->outputs[idx]->in_samplerates);
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_channel_layouts_ref(layouts, &ctx->outputs[idx]->in_channel_layouts);
}
/* Set the same formats for each corresponding input */
for (seg = 0; seg < cat->nb_segments; seg++) {
ff_formats_ref(formats, &ctx->inputs[idx]->out_formats);
if (type == AVMEDIA_TYPE_AUDIO) {
ff_formats_ref(rates, &ctx->inputs[idx]->out_samplerates);
ff_channel_layouts_ref(layouts, &ctx->inputs[idx]->out_channel_layouts);
}
idx += ctx->nb_outputs;
}
idx0++;
}
}
return 0;
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
ConcatContext *cat = ctx->priv;
unsigned out_no = FF_OUTLINK_IDX(outlink);
unsigned in_no = out_no, seg;
AVFilterLink *inlink = ctx->inputs[in_no];
/* enhancement: find a common one */
outlink->time_base = AV_TIME_BASE_Q;
outlink->w = inlink->w;
outlink->h = inlink->h;
outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
outlink->format = inlink->format;
for (seg = 1; seg < cat->nb_segments; seg++) {
inlink = ctx->inputs[in_no += ctx->nb_outputs];
if (!outlink->sample_aspect_ratio.num)
outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
/* possible enhancement: unsafe mode, do not check */
if (outlink->w != inlink->w ||
outlink->h != inlink->h ||
outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num &&
inlink->sample_aspect_ratio.num ||
outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) {
av_log(ctx, AV_LOG_ERROR, "Input link %s parameters "
"(size %dx%d, SAR %d:%d) do not match the corresponding "
"output link %s parameters (%dx%d, SAR %d:%d)\n",
ctx->input_pads[in_no].name, inlink->w, inlink->h,
inlink->sample_aspect_ratio.num,
inlink->sample_aspect_ratio.den,
ctx->input_pads[out_no].name, outlink->w, outlink->h,
outlink->sample_aspect_ratio.num,
outlink->sample_aspect_ratio.den);
if (!cat->unsafe)
return AVERROR(EINVAL);
}
}
return 0;
}
static int push_frame(AVFilterContext *ctx, unsigned in_no, AVFrame *buf)
{
ConcatContext *cat = ctx->priv;
unsigned out_no = in_no % ctx->nb_outputs;
AVFilterLink * inlink = ctx-> inputs[ in_no];
AVFilterLink *outlink = ctx->outputs[out_no];
struct concat_in *in = &cat->in[in_no];
buf->pts = av_rescale_q(buf->pts, inlink->time_base, outlink->time_base);
in->pts = buf->pts;
in->nb_frames++;
/* add duration to input PTS */
if (inlink->sample_rate)
/* use number of audio samples */
in->pts += av_rescale_q(buf->nb_samples,
(AVRational){ 1, inlink->sample_rate },
outlink->time_base);
else if (in->nb_frames >= 2)
/* use mean duration */
in->pts = av_rescale(in->pts, in->nb_frames, in->nb_frames - 1);
buf->pts += cat->delta_ts;
return ff_filter_frame(outlink, buf);
}
static int process_frame(AVFilterLink *inlink, AVFrame *buf)
{
AVFilterContext *ctx = inlink->dst;
ConcatContext *cat = ctx->priv;
unsigned in_no = FF_INLINK_IDX(inlink);
if (in_no < cat->cur_idx) {
av_log(ctx, AV_LOG_ERROR, "Frame after EOF on input %s\n",
ctx->input_pads[in_no].name);
av_frame_free(&buf);
} else if (in_no >= cat->cur_idx + ctx->nb_outputs) {
ff_bufqueue_add(ctx, &cat->in[in_no].queue, buf);
} else {
return push_frame(ctx, in_no, buf);
}
return 0;
}
static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
{
AVFilterContext *ctx = inlink->dst;
unsigned in_no = FF_INLINK_IDX(inlink);
AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
return ff_get_video_buffer(outlink, w, h);
}
static AVFrame *get_audio_buffer(AVFilterLink *inlink, int nb_samples)
{
AVFilterContext *ctx = inlink->dst;
unsigned in_no = FF_INLINK_IDX(inlink);
AVFilterLink *outlink = ctx->outputs[in_no % ctx->nb_outputs];
return ff_get_audio_buffer(outlink, nb_samples);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
{
return process_frame(inlink, buf);
}
static void close_input(AVFilterContext *ctx, unsigned in_no)
{
ConcatContext *cat = ctx->priv;
cat->in[in_no].eof = 1;
cat->nb_in_active--;
av_log(ctx, AV_LOG_VERBOSE, "EOF on %s, %d streams left in segment.\n",
ctx->input_pads[in_no].name, cat->nb_in_active);
}
static void find_next_delta_ts(AVFilterContext *ctx, int64_t *seg_delta)
{
ConcatContext *cat = ctx->priv;
unsigned i = cat->cur_idx;
unsigned imax = i + ctx->nb_outputs;
int64_t pts;
pts = cat->in[i++].pts;
for (; i < imax; i++)
pts = FFMAX(pts, cat->in[i].pts);
cat->delta_ts += pts;
*seg_delta = pts;
}
static int send_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
int64_t seg_delta)
{
ConcatContext *cat = ctx->priv;
AVFilterLink *outlink = ctx->outputs[out_no];
int64_t base_pts = cat->in[in_no].pts + cat->delta_ts - seg_delta;
int64_t nb_samples, sent = 0;
int frame_nb_samples, ret;
AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };
AVFrame *buf;
int nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout);
if (!rate_tb.den)
return AVERROR_BUG;
nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
outlink->time_base, rate_tb);
frame_nb_samples = FFMAX(9600, rate_tb.den / 5); /* arbitrary */
while (nb_samples) {
frame_nb_samples = FFMIN(frame_nb_samples, nb_samples);
buf = ff_get_audio_buffer(outlink, frame_nb_samples);
if (!buf)
return AVERROR(ENOMEM);
av_samples_set_silence(buf->extended_data, 0, frame_nb_samples,
nb_channels, outlink->format);
buf->pts = base_pts + av_rescale_q(sent, rate_tb, outlink->time_base);
ret = ff_filter_frame(outlink, buf);
if (ret < 0)
return ret;
sent += frame_nb_samples;
nb_samples -= frame_nb_samples;
}
return 0;
}
static int flush_segment(AVFilterContext *ctx)
{
int ret;
ConcatContext *cat = ctx->priv;
unsigned str, str_max;
int64_t seg_delta;
find_next_delta_ts(ctx, &seg_delta);
cat->cur_idx += ctx->nb_outputs;
cat->nb_in_active = ctx->nb_outputs;
av_log(ctx, AV_LOG_VERBOSE, "Segment finished at pts=%"PRId64"\n",
cat->delta_ts);
if (cat->cur_idx < ctx->nb_inputs) {
/* pad audio streams with silence */
str = cat->nb_streams[AVMEDIA_TYPE_VIDEO];
str_max = str + cat->nb_streams[AVMEDIA_TYPE_AUDIO];
for (; str < str_max; str++) {
ret = send_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str,
seg_delta);
if (ret < 0)
return ret;
}
/* flush queued buffers */
/* possible enhancement: flush in PTS order */
str_max = cat->cur_idx + ctx->nb_outputs;
for (str = cat->cur_idx; str < str_max; str++) {
while (cat->in[str].queue.available) {
ret = push_frame(ctx, str, ff_bufqueue_get(&cat->in[str].queue));
if (ret < 0)
return ret;
}
}
}
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
ConcatContext *cat = ctx->priv;
unsigned out_no = FF_OUTLINK_IDX(outlink);
unsigned in_no = out_no + cat->cur_idx;
unsigned str, str_max;
int ret;
while (1) {
if (in_no >= ctx->nb_inputs)
return AVERROR_EOF;
if (!cat->in[in_no].eof) {
ret = ff_request_frame(ctx->inputs[in_no]);
if (ret != AVERROR_EOF)
return ret;
close_input(ctx, in_no);
}
/* cycle on all inputs to finish the segment */
/* possible enhancement: request in PTS order */
str_max = cat->cur_idx + ctx->nb_outputs - 1;
for (str = cat->cur_idx; cat->nb_in_active;
str = str == str_max ? cat->cur_idx : str + 1) {
if (cat->in[str].eof)
continue;
ret = ff_request_frame(ctx->inputs[str]);
if (ret == AVERROR_EOF)
close_input(ctx, str);
else if (ret < 0)
return ret;
}
ret = flush_segment(ctx);
if (ret < 0)
return ret;
in_no += ctx->nb_outputs;
}
}
static av_cold int init(AVFilterContext *ctx)
{
ConcatContext *cat = ctx->priv;
unsigned seg, type, str;
/* create input pads */
for (seg = 0; seg < cat->nb_segments; seg++) {
for (type = 0; type < TYPE_ALL; type++) {
for (str = 0; str < cat->nb_streams[type]; str++) {
AVFilterPad pad = {
.type = type,
.get_video_buffer = get_video_buffer,
.get_audio_buffer = get_audio_buffer,
.filter_frame = filter_frame,
};
pad.name = av_asprintf("in%d:%c%d", seg, "va"[type], str);
ff_insert_inpad(ctx, ctx->nb_inputs, &pad);
}
}
}
/* create output pads */
for (type = 0; type < TYPE_ALL; type++) {
for (str = 0; str < cat->nb_streams[type]; str++) {
AVFilterPad pad = {
.type = type,
.config_props = config_output,
.request_frame = request_frame,
};
pad.name = av_asprintf("out:%c%d", "va"[type], str);
ff_insert_outpad(ctx, ctx->nb_outputs, &pad);
}
}
cat->in = av_calloc(ctx->nb_inputs, sizeof(*cat->in));
if (!cat->in)
return AVERROR(ENOMEM);
cat->nb_in_active = ctx->nb_outputs;
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
ConcatContext *cat = ctx->priv;
unsigned i;
for (i = 0; i < ctx->nb_inputs; i++) {
av_freep(&ctx->input_pads[i].name);
ff_bufqueue_discard_all(&cat->in[i].queue);
}
for (i = 0; i < ctx->nb_outputs; i++)
av_freep(&ctx->output_pads[i].name);
av_free(cat->in);
}
AVFilter avfilter_avf_concat = {
.name = "concat",
.description = NULL_IF_CONFIG_SMALL("Concatenate audio and video streams."),
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.priv_size = sizeof(ConcatContext),
.inputs = NULL,
.outputs = NULL,
.priv_class = &concat_class,
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS | AVFILTER_FLAG_DYNAMIC_OUTPUTS,
};
@@ -0,0 +1,502 @@
/*
* Copyright (c) 2012 Clément Bœsch
* Copyright (c) 2013 Rudolf Polzer <[email protected]>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio to spectrum (video) transmedia filter, based on ffplay rdft showmode
* (by Michael Niedermayer) and lavfi/avf_showwaves (by Stefano Sabatini).
*/
#include <math.h>
#include "libavcodec/avfft.h"
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "internal.h"
enum DisplayMode { COMBINED, SEPARATE, NB_MODES };
enum DisplayScale { LINEAR, SQRT, CBRT, LOG, NB_SCALES };
enum ColorMode { CHANNEL, INTENSITY, NB_CLMODES };
typedef struct {
const AVClass *class;
int w, h;
AVFrame *outpicref;
int req_fullfilled;
int nb_display_channels;
int channel_height;
int sliding; ///< 1 if sliding mode, 0 otherwise
enum DisplayMode mode; ///< channel display mode
enum ColorMode color_mode; ///< display color scheme
enum DisplayScale scale;
float saturation; ///< color saturation multiplier
int xpos; ///< x position (current column)
RDFTContext *rdft; ///< Real Discrete Fourier Transform context
int rdft_bits; ///< number of bits (RDFT window size = 1<<rdft_bits)
FFTSample **rdft_data; ///< bins holder for each (displayed) channels
int filled; ///< number of samples (per channel) filled in current rdft_buffer
int consumed; ///< number of samples (per channel) consumed from the input frame
float *window_func_lut; ///< Window function LUT
float *combine_buffer; ///< color combining buffer (3 * h items)
} ShowSpectrumContext;
#define OFFSET(x) offsetof(ShowSpectrumContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption showspectrum_options[] = {
{ "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x512"}, 0, 0, FLAGS },
{ "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x512"}, 0, 0, FLAGS },
{ "slide", "set sliding mode", OFFSET(sliding), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS },
{ "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, COMBINED, NB_MODES-1, FLAGS, "mode" },
{ "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, "mode" },
{ "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "mode" },
{ "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=CHANNEL}, CHANNEL, NB_CLMODES-1, FLAGS, "color" },
{ "channel", "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL}, 0, 0, FLAGS, "color" },
{ "intensity", "intensity based coloring", 0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, "color" },
{ "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=SQRT}, LINEAR, NB_SCALES-1, FLAGS, "scale" },
{ "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, "scale" },
{ "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, "scale" },
{ "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, "scale" },
{ "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" },
{ "saturation", "color saturation multiplier", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl = 1}, -10, 10, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(showspectrum);
static const struct {
float a, y, u, v;
} intensity_color_table[] = {
{ 0, 0, 0, 0 },
{ 0.13, .03587126228984074, .1573300977624594, -.02548747583751842 },
{ 0.30, .18572281794568020, .1772436246393981, .17475554840414750 },
{ 0.60, .28184980583656130, -.1593064119945782, .47132074554608920 },
{ 0.73, .65830621175547810, -.3716070802232764, .24352759331252930 },
{ 0.78, .76318535758242900, -.4307467689263783, .16866496622310430 },
{ 0.91, .95336363636363640, -.2045454545454546, .03313636363636363 },
{ 1, 1, 0, 0 }
};
static av_cold void uninit(AVFilterContext *ctx)
{
ShowSpectrumContext *s = ctx->priv;
int i;
av_freep(&s->combine_buffer);
av_rdft_end(s->rdft);
for (i = 0; i < s->nb_display_channels; i++)
av_freep(&s->rdft_data[i]);
av_freep(&s->rdft_data);
av_freep(&s->window_func_lut);
av_frame_free(&s->outpicref);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layouts = NULL;
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE };
static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE };
/* set input audio formats */
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_formats);
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_samplerates);
/* set output video format */
formats = ff_make_format_list(pix_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &outlink->in_formats);
return 0;
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink = ctx->inputs[0];
ShowSpectrumContext *s = ctx->priv;
int i, rdft_bits, win_size, h;
outlink->w = s->w;
outlink->h = s->h;
h = (s->mode == COMBINED) ? outlink->h : outlink->h / inlink->channels;
s->channel_height = h;
/* RDFT window size (precision) according to the requested output frame height */
for (rdft_bits = 1; 1 << rdft_bits < 2 * h; rdft_bits++);
win_size = 1 << rdft_bits;
/* (re-)configuration if the video output changed (or first init) */
if (rdft_bits != s->rdft_bits) {
size_t rdft_size, rdft_listsize;
AVFrame *outpicref;
av_rdft_end(s->rdft);
s->rdft = av_rdft_init(rdft_bits, DFT_R2C);
s->rdft_bits = rdft_bits;
/* RDFT buffers: x2 for each (display) channel buffer.
* Note: we use free and malloc instead of a realloc-like function to
* make sure the buffer is aligned in memory for the FFT functions. */
for (i = 0; i < s->nb_display_channels; i++)
av_freep(&s->rdft_data[i]);
av_freep(&s->rdft_data);
s->nb_display_channels = inlink->channels;
if (av_size_mult(sizeof(*s->rdft_data),
s->nb_display_channels, &rdft_listsize) < 0)
return AVERROR(EINVAL);
if (av_size_mult(sizeof(**s->rdft_data),
win_size, &rdft_size) < 0)
return AVERROR(EINVAL);
s->rdft_data = av_malloc(rdft_listsize);
if (!s->rdft_data)
return AVERROR(ENOMEM);
for (i = 0; i < s->nb_display_channels; i++) {
s->rdft_data[i] = av_malloc(rdft_size);
if (!s->rdft_data[i])
return AVERROR(ENOMEM);
}
s->filled = 0;
/* pre-calc windowing function (hann here) */
s->window_func_lut =
av_realloc_f(s->window_func_lut, win_size,
sizeof(*s->window_func_lut));
if (!s->window_func_lut)
return AVERROR(ENOMEM);
for (i = 0; i < win_size; i++)
s->window_func_lut[i] = .5f * (1 - cos(2*M_PI*i / (win_size-1)));
/* prepare the initial picref buffer (black frame) */
av_frame_free(&s->outpicref);
s->outpicref = outpicref =
ff_get_video_buffer(outlink, outlink->w, outlink->h);
if (!outpicref)
return AVERROR(ENOMEM);
outlink->sample_aspect_ratio = (AVRational){1,1};
for (i = 0; i < outlink->h; i++) {
memset(outpicref->data[0] + i * outpicref->linesize[0], 0, outlink->w);
memset(outpicref->data[1] + i * outpicref->linesize[1], 128, outlink->w);
memset(outpicref->data[2] + i * outpicref->linesize[2], 128, outlink->w);
}
}
if (s->xpos >= outlink->w)
s->xpos = 0;
s->combine_buffer =
av_realloc_f(s->combine_buffer, outlink->h * 3,
sizeof(*s->combine_buffer));
av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d RDFT window size:%d\n",
s->w, s->h, win_size);
return 0;
}
inline static int push_frame(AVFilterLink *outlink)
{
ShowSpectrumContext *s = outlink->src->priv;
s->xpos++;
if (s->xpos >= outlink->w)
s->xpos = 0;
s->filled = 0;
s->req_fullfilled = 1;
return ff_filter_frame(outlink, av_frame_clone(s->outpicref));
}
static int request_frame(AVFilterLink *outlink)
{
ShowSpectrumContext *s = outlink->src->priv;
AVFilterLink *inlink = outlink->src->inputs[0];
int ret;
s->req_fullfilled = 0;
do {
ret = ff_request_frame(inlink);
} while (!s->req_fullfilled && ret >= 0);
if (ret == AVERROR_EOF && s->outpicref)
push_frame(outlink);
return ret;
}
static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples, int nb_samples)
{
int ret;
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
ShowSpectrumContext *s = ctx->priv;
AVFrame *outpicref = s->outpicref;
/* nb_freq contains the power of two superior or equal to the output image
* height (or half the RDFT window size) */
const int nb_freq = 1 << (s->rdft_bits - 1);
const int win_size = nb_freq << 1;
const double w = 1. / (sqrt(nb_freq) * 32768.);
int ch, plane, n, y;
const int start = s->filled;
const int add_samples = FFMIN(win_size - start, nb_samples);
/* fill RDFT input with the number of samples available */
for (ch = 0; ch < s->nb_display_channels; ch++) {
const int16_t *p = (int16_t *)insamples->extended_data[ch];
p += s->consumed;
for (n = 0; n < add_samples; n++)
s->rdft_data[ch][start + n] = p[n] * s->window_func_lut[start + n];
}
s->filled += add_samples;
/* complete RDFT window size? */
if (s->filled == win_size) {
/* channel height */
int h = s->channel_height;
/* run RDFT on each samples set */
for (ch = 0; ch < s->nb_display_channels; ch++)
av_rdft_calc(s->rdft, s->rdft_data[ch]);
/* fill a new spectrum column */
#define RE(y, ch) s->rdft_data[ch][2 * y + 0]
#define IM(y, ch) s->rdft_data[ch][2 * y + 1]
#define MAGNITUDE(y, ch) hypot(RE(y, ch), IM(y, ch))
/* initialize buffer for combining to black */
for (y = 0; y < outlink->h; y++) {
s->combine_buffer[3 * y ] = 0;
s->combine_buffer[3 * y + 1] = 127.5;
s->combine_buffer[3 * y + 2] = 127.5;
}
for (ch = 0; ch < s->nb_display_channels; ch++) {
float yf, uf, vf;
/* decide color range */
switch (s->mode) {
case COMBINED:
// reduce range by channel count
yf = 256.0f / s->nb_display_channels;
switch (s->color_mode) {
case INTENSITY:
uf = yf;
vf = yf;
break;
case CHANNEL:
/* adjust saturation for mixed UV coloring */
/* this factor is correct for infinite channels, an approximation otherwise */
uf = yf * M_PI;
vf = yf * M_PI;
break;
default:
av_assert0(0);
}
break;
case SEPARATE:
// full range
yf = 256.0f;
uf = 256.0f;
vf = 256.0f;
break;
default:
av_assert0(0);
}
if (s->color_mode == CHANNEL) {
if (s->nb_display_channels > 1) {
uf *= 0.5 * sin((2 * M_PI * ch) / s->nb_display_channels);
vf *= 0.5 * cos((2 * M_PI * ch) / s->nb_display_channels);
} else {
uf = 0.0f;
vf = 0.0f;
}
}
uf *= s->saturation;
vf *= s->saturation;
/* draw the channel */
for (y = 0; y < h; y++) {
int row = (s->mode == COMBINED) ? y : ch * h + y;
float *out = &s->combine_buffer[3 * row];
/* get magnitude */
float a = w * MAGNITUDE(y, ch);
/* apply scale */
switch (s->scale) {
case LINEAR:
break;
case SQRT:
a = sqrt(a);
break;
case CBRT:
a = cbrt(a);
break;
case LOG:
a = 1 - log(FFMAX(FFMIN(1, a), 1e-6)) / log(1e-6); // zero = -120dBFS
break;
default:
av_assert0(0);
}
if (s->color_mode == INTENSITY) {
float y, u, v;
int i;
for (i = 1; i < sizeof(intensity_color_table) / sizeof(*intensity_color_table) - 1; i++)
if (intensity_color_table[i].a >= a)
break;
// i now is the first item >= the color
// now we know to interpolate between item i - 1 and i
if (a <= intensity_color_table[i - 1].a) {
y = intensity_color_table[i - 1].y;
u = intensity_color_table[i - 1].u;
v = intensity_color_table[i - 1].v;
} else if (a >= intensity_color_table[i].a) {
y = intensity_color_table[i].y;
u = intensity_color_table[i].u;
v = intensity_color_table[i].v;
} else {
float start = intensity_color_table[i - 1].a;
float end = intensity_color_table[i].a;
float lerpfrac = (a - start) / (end - start);
y = intensity_color_table[i - 1].y * (1.0f - lerpfrac)
+ intensity_color_table[i].y * lerpfrac;
u = intensity_color_table[i - 1].u * (1.0f - lerpfrac)
+ intensity_color_table[i].u * lerpfrac;
v = intensity_color_table[i - 1].v * (1.0f - lerpfrac)
+ intensity_color_table[i].v * lerpfrac;
}
out[0] += y * yf;
out[1] += u * uf;
out[2] += v * vf;
} else {
out[0] += a * yf;
out[1] += a * uf;
out[2] += a * vf;
}
}
}
/* copy to output */
if (s->sliding) {
for (plane = 0; plane < 3; plane++) {
for (y = 0; y < outlink->h; y++) {
uint8_t *p = outpicref->data[plane] +
y * outpicref->linesize[plane];
memmove(p, p + 1, outlink->w - 1);
}
}
s->xpos = outlink->w - 1;
}
for (plane = 0; plane < 3; plane++) {
uint8_t *p = outpicref->data[plane] +
(outlink->h - 1) * outpicref->linesize[plane] +
s->xpos;
for (y = 0; y < outlink->h; y++) {
*p = rint(FFMAX(0, FFMIN(s->combine_buffer[3 * y + plane], 255)));
p -= outpicref->linesize[plane];
}
}
outpicref->pts = insamples->pts +
av_rescale_q(s->consumed,
(AVRational){ 1, inlink->sample_rate },
outlink->time_base);
ret = push_frame(outlink);
if (ret < 0)
return ret;
}
return add_samples;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
AVFilterContext *ctx = inlink->dst;
ShowSpectrumContext *s = ctx->priv;
int ret = 0, left_samples = insamples->nb_samples;
s->consumed = 0;
while (left_samples) {
int ret = plot_spectrum_column(inlink, insamples, left_samples);
if (ret < 0)
break;
s->consumed += ret;
left_samples -= ret;
}
av_frame_free(&insamples);
return ret;
}
static const AVFilterPad showspectrum_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad showspectrum_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_output,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_avf_showspectrum = {
.name = "showspectrum",
.description = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output."),
.uninit = uninit,
.query_formats = query_formats,
.priv_size = sizeof(ShowSpectrumContext),
.inputs = showspectrum_inputs,
.outputs = showspectrum_outputs,
.priv_class = &showspectrum_class,
};
+256
View File
@@ -0,0 +1,256 @@
/*
* Copyright (c) 2012 Stefano Sabatini
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio to video multimedia filter
*/
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "avfilter.h"
#include "formats.h"
#include "audio.h"
#include "video.h"
#include "internal.h"
enum ShowWavesMode {
MODE_POINT,
MODE_LINE,
MODE_NB,
};
typedef struct {
const AVClass *class;
int w, h;
AVRational rate;
int buf_idx;
AVFrame *outpicref;
int req_fullfilled;
int n;
int sample_count_mod;
enum ShowWavesMode mode;
} ShowWavesContext;
#define OFFSET(x) offsetof(ShowWavesContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption showwaves_options[] = {
{ "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
{ "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS },
{ "mode", "select display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_POINT}, 0, MODE_NB-1, FLAGS, "mode"},
{ "point", "draw a point for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_POINT}, .flags=FLAGS, .unit="mode"},
{ "line", "draw a line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_LINE}, .flags=FLAGS, .unit="mode"},
{ "n", "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS },
{ "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },
{ "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(showwaves);
static av_cold void uninit(AVFilterContext *ctx)
{
ShowWavesContext *showwaves = ctx->priv;
av_frame_free(&showwaves->outpicref);
}
static int query_formats(AVFilterContext *ctx)
{
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layouts = NULL;
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE };
static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE };
/* set input audio formats */
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_formats);
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
formats = ff_all_samplerates();
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_samplerates);
/* set output video format */
formats = ff_make_format_list(pix_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &outlink->in_formats);
return 0;
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink = ctx->inputs[0];
ShowWavesContext *showwaves = ctx->priv;
if (!showwaves->n)
showwaves->n = FFMAX(1, ((double)inlink->sample_rate / (showwaves->w * av_q2d(showwaves->rate))) + 0.5);
showwaves->buf_idx = 0;
outlink->w = showwaves->w;
outlink->h = showwaves->h;
outlink->sample_aspect_ratio = (AVRational){1,1};
outlink->frame_rate = av_div_q((AVRational){inlink->sample_rate,showwaves->n},
(AVRational){showwaves->w,1});
av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%d\n",
showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), showwaves->n);
return 0;
}
inline static int push_frame(AVFilterLink *outlink)
{
ShowWavesContext *showwaves = outlink->src->priv;
int ret;
if ((ret = ff_filter_frame(outlink, showwaves->outpicref)) >= 0)
showwaves->req_fullfilled = 1;
showwaves->outpicref = NULL;
showwaves->buf_idx = 0;
return ret;
}
static int request_frame(AVFilterLink *outlink)
{
ShowWavesContext *showwaves = outlink->src->priv;
AVFilterLink *inlink = outlink->src->inputs[0];
int ret;
showwaves->req_fullfilled = 0;
do {
ret = ff_request_frame(inlink);
} while (!showwaves->req_fullfilled && ret >= 0);
if (ret == AVERROR_EOF && showwaves->outpicref)
push_frame(outlink);
return ret;
}
#define MAX_INT16 ((1<<15) -1)
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
ShowWavesContext *showwaves = ctx->priv;
const int nb_samples = insamples->nb_samples;
AVFrame *outpicref = showwaves->outpicref;
int linesize = outpicref ? outpicref->linesize[0] : 0;
int16_t *p = (int16_t *)insamples->data[0];
int nb_channels = inlink->channels;
int i, j, k, h, ret = 0;
const int n = showwaves->n;
const int x = 255 / (nb_channels * n); /* multiplication factor, pre-computed to avoid in-loop divisions */
/* draw data in the buffer */
for (i = 0; i < nb_samples; i++) {
if (!showwaves->outpicref) {
showwaves->outpicref = outpicref =
ff_get_video_buffer(outlink, outlink->w, outlink->h);
if (!outpicref)
return AVERROR(ENOMEM);
outpicref->width = outlink->w;
outpicref->height = outlink->h;
outpicref->pts = insamples->pts +
av_rescale_q((p - (int16_t *)insamples->data[0]) / nb_channels,
(AVRational){ 1, inlink->sample_rate },
outlink->time_base);
linesize = outpicref->linesize[0];
for (j = 0; j < outlink->h; j++)
memset(outpicref->data[0] + j * linesize, 0, outlink->w);
}
for (j = 0; j < nb_channels; j++) {
h = showwaves->h/2 - av_rescale(*p++, showwaves->h/2, MAX_INT16);
switch (showwaves->mode) {
case MODE_POINT:
if (h >= 0 && h < outlink->h)
*(outpicref->data[0] + showwaves->buf_idx + h * linesize) += x;
break;
case MODE_LINE:
{
int start = showwaves->h/2, end = av_clip(h, 0, outlink->h-1);
if (start > end) FFSWAP(int16_t, start, end);
for (k = start; k < end; k++)
*(outpicref->data[0] + showwaves->buf_idx + k * linesize) += x;
break;
}
}
}
showwaves->sample_count_mod++;
if (showwaves->sample_count_mod == n) {
showwaves->sample_count_mod = 0;
showwaves->buf_idx++;
}
if (showwaves->buf_idx == showwaves->w)
if ((ret = push_frame(outlink)) < 0)
break;
outpicref = showwaves->outpicref;
}
av_frame_free(&insamples);
return ret;
}
static const AVFilterPad showwaves_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad showwaves_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_output,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_avf_showwaves = {
.name = "showwaves",
.description = NULL_IF_CONFIG_SMALL("Convert input audio to a video output."),
.uninit = uninit,
.query_formats = query_formats,
.priv_size = sizeof(ShowWavesContext),
.inputs = showwaves_inputs,
.outputs = showwaves_outputs,
.priv_class = &showwaves_class,
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
/*
* Filter graphs
* copyright (c) 2007 Bobby Bingham
*
* 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_AVFILTERGRAPH_H
#define AVFILTER_AVFILTERGRAPH_H
#include "avfilter.h"
#include "libavutil/log.h"
#endif /* AVFILTER_AVFILTERGRAPH_H */
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2005 Robert Edele <[email protected]>
*
* 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 "bbox.h"
int ff_calculate_bounding_box(FFBoundingBox *bbox,
const uint8_t *data, int linesize, int w, int h,
int min_val)
{
int x, y;
int start_x;
int start_y;
int end_x;
int end_y;
const uint8_t *line;
/* left bound */
for (start_x = 0; start_x < w; start_x++)
for (y = 0; y < h; y++)
if ((data[y * linesize + start_x] > min_val))
goto outl;
outl:
if (start_x == w) /* no points found */
return 0;
/* right bound */
for (end_x = w - 1; end_x >= start_x; end_x--)
for (y = 0; y < h; y++)
if ((data[y * linesize + end_x] > min_val))
goto outr;
outr:
/* top bound */
line = data;
for (start_y = 0; start_y < h; start_y++) {
for (x = 0; x < w; x++)
if (line[x] > min_val)
goto outt;
line += linesize;
}
outt:
/* bottom bound */
line = data + (h-1)*linesize;
for (end_y = h - 1; end_y >= start_y; end_y--) {
for (x = 0; x < w; x++)
if (line[x] > min_val)
goto outb;
line -= linesize;
}
outb:
bbox->x1 = start_x;
bbox->y1 = start_y;
bbox->x2 = end_x;
bbox->y2 = end_y;
return 1;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2005 Robert Edele <[email protected]>
*
* 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_BBOX_H
#define AVFILTER_BBOX_H
#include <stdint.h>
typedef struct {
int x1, x2, y1, y2;
} FFBoundingBox;
/**
* Calculate the smallest rectangle that will encompass the
* region with values > min_val.
*
* @param bbox bounding box structure which is updated with the found values.
* If no pixels could be found with value > min_val, the
* structure is not modified.
* @return 1 in case at least one pixel with value > min_val was found,
* 0 otherwise
*/
int ff_calculate_bounding_box(FFBoundingBox *bbox,
const uint8_t *data, int linesize,
int w, int h, int min_val);
#endif /* AVFILTER_BBOX_H */
+170
View File
@@ -0,0 +1,170 @@
/*
* Copyright Stefano Sabatini <stefasab gmail com>
* Copyright Anton Khirnov <anton khirnov net>
* Copyright Michael Niedermayer <michaelni gmx at>
*
* 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/channel_layout.h"
#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/imgutils.h"
#include "libavcodec/avcodec.h"
#include "avfilter.h"
#include "internal.h"
#include "audio.h"
#include "avcodec.h"
#include "version.h"
#if FF_API_AVFILTERBUFFER
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);
}
static void copy_video_props(AVFilterBufferRefVideoProps *dst, AVFilterBufferRefVideoProps *src) {
*dst = *src;
if (src->qp_table) {
int qsize = src->qp_table_size;
dst->qp_table = av_malloc(qsize);
memcpy(dst->qp_table, src->qp_table, qsize);
}
}
AVFilterBufferRef *avfilter_ref_buffer(AVFilterBufferRef *ref, int pmask)
{
AVFilterBufferRef *ret = av_malloc(sizeof(AVFilterBufferRef));
if (!ret)
return NULL;
*ret = *ref;
ret->metadata = NULL;
av_dict_copy(&ret->metadata, ref->metadata, 0);
if (ref->type == AVMEDIA_TYPE_VIDEO) {
ret->video = av_malloc(sizeof(AVFilterBufferRefVideoProps));
if (!ret->video) {
av_free(ret);
return NULL;
}
copy_video_props(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 avfilter_unref_buffer(AVFilterBufferRef *ref)
{
if (!ref)
return;
av_assert0(ref->buf->refcount > 0);
if (!(--ref->buf->refcount))
ref->buf->free(ref->buf);
if (ref->extended_data != ref->data)
av_freep(&ref->extended_data);
if (ref->video)
av_freep(&ref->video->qp_table);
av_freep(&ref->video);
av_freep(&ref->audio);
av_dict_free(&ref->metadata);
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 = av_frame_get_pkt_pos(src);
dst->format = src->format;
av_dict_free(&dst->metadata);
av_dict_copy(&dst->metadata, av_frame_get_metadata(src), 0);
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;
}
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: {
if (dst->video->qp_table)
av_freep(&dst->video->qp_table);
copy_video_props(dst->video, src->video);
break;
}
case AVMEDIA_TYPE_AUDIO: *dst->audio = *src->audio; break;
default: break;
}
av_dict_free(&dst->metadata);
av_dict_copy(&dst->metadata, src->metadata, 0);
}
#endif /* FF_API_AVFILTERBUFFER */
+121
View File
@@ -0,0 +1,121 @@
/*
* Generic buffer queue
* Copyright (c) 2012 Nicolas George
*
* 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_BUFFERQUEUE_H
#define AVFILTER_BUFFERQUEUE_H
/**
* FFBufQueue: simple AVFrame queue API
*
* Note: this API is not thread-safe. Concurrent access to the same queue
* must be protected by a mutex or any synchronization mechanism.
*/
/**
* Maximum size of the queue.
*
* This value can be overridden by definying it before including this
* header.
* Powers of 2 are recommended.
*/
#ifndef FF_BUFQUEUE_SIZE
#define FF_BUFQUEUE_SIZE 32
#endif
#include "avfilter.h"
#include "libavutil/avassert.h"
/**
* Structure holding the queue
*/
struct FFBufQueue {
AVFrame *queue[FF_BUFQUEUE_SIZE];
unsigned short head;
unsigned short available; /**< number of available buffers */
};
#define BUCKET(i) queue->queue[(queue->head + (i)) % FF_BUFQUEUE_SIZE]
/**
* Test if a buffer queue is full.
*/
static inline int ff_bufqueue_is_full(struct FFBufQueue *queue)
{
return queue->available == FF_BUFQUEUE_SIZE;
}
/**
* Add a buffer to the queue.
*
* If the queue is already full, then the current last buffer is dropped
* (and unrefed) with a warning before adding the new buffer.
*/
static inline void ff_bufqueue_add(void *log, struct FFBufQueue *queue,
AVFrame *buf)
{
if (ff_bufqueue_is_full(queue)) {
av_log(log, AV_LOG_WARNING, "Buffer queue overflow, dropping.\n");
av_frame_free(&BUCKET(--queue->available));
}
BUCKET(queue->available++) = buf;
}
/**
* Get a buffer from the queue without altering it.
*
* Buffer with index 0 is the first buffer in the queue.
* Return NULL if the queue has not enough buffers.
*/
static inline AVFrame *ff_bufqueue_peek(struct FFBufQueue *queue,
unsigned index)
{
return index < queue->available ? BUCKET(index) : NULL;
}
/**
* Get the first buffer from the queue and remove it.
*
* Do not use on an empty queue.
*/
static inline AVFrame *ff_bufqueue_get(struct FFBufQueue *queue)
{
AVFrame *ret = queue->queue[queue->head];
av_assert0(queue->available);
queue->available--;
queue->queue[queue->head] = NULL;
queue->head = (queue->head + 1) % FF_BUFQUEUE_SIZE;
return ret;
}
/**
* Unref and remove all buffers from the queue.
*/
static inline void ff_bufqueue_discard_all(struct FFBufQueue *queue)
{
while (queue->available) {
AVFrame *buf = ff_bufqueue_get(queue);
av_frame_free(&buf);
}
}
#undef BUCKET
#endif /* AVFILTER_BUFFERQUEUE_H */
+609
View File
@@ -0,0 +1,609 @@
/*
* Copyright (c) 2011 Stefano Sabatini
*
* 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
* buffer sink
*/
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/internal.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "audio.h"
#include "avfilter.h"
#include "buffersink.h"
#include "internal.h"
typedef struct {
const AVClass *class;
AVFifoBuffer *fifo; ///< FIFO buffer of video frame references
unsigned warning_limit;
/* only used for video */
enum AVPixelFormat *pixel_fmts; ///< list of accepted pixel formats, must be terminated with -1
int pixel_fmts_size;
/* only used for audio */
enum AVSampleFormat *sample_fmts; ///< list of accepted sample formats, terminated by AV_SAMPLE_FMT_NONE
int sample_fmts_size;
int64_t *channel_layouts; ///< list of accepted channel layouts, terminated by -1
int channel_layouts_size;
int *channel_counts; ///< list of accepted channel counts, terminated by -1
int channel_counts_size;
int all_channel_counts;
int *sample_rates; ///< list of accepted sample rates, terminated by -1
int sample_rates_size;
/* only used for compat API */
AVAudioFifo *audio_fifo; ///< FIFO for audio samples
int64_t next_pts; ///< interpolating audio pts
} BufferSinkContext;
#define NB_ITEMS(list) (list ## _size / sizeof(*list))
static av_cold void uninit(AVFilterContext *ctx)
{
BufferSinkContext *sink = ctx->priv;
AVFrame *frame;
if (sink->audio_fifo)
av_audio_fifo_free(sink->audio_fifo);
if (sink->fifo) {
while (av_fifo_size(sink->fifo) >= sizeof(AVFilterBufferRef *)) {
av_fifo_generic_read(sink->fifo, &frame, sizeof(frame), NULL);
av_frame_free(&frame);
}
av_fifo_free(sink->fifo);
sink->fifo = NULL;
}
}
static int add_buffer_ref(AVFilterContext *ctx, AVFrame *ref)
{
BufferSinkContext *buf = ctx->priv;
if (av_fifo_space(buf->fifo) < sizeof(AVFilterBufferRef *)) {
/* realloc fifo size */
if (av_fifo_realloc2(buf->fifo, av_fifo_size(buf->fifo) * 2) < 0) {
av_log(ctx, AV_LOG_ERROR,
"Cannot buffer more frames. Consume some available frames "
"before adding new ones.\n");
return AVERROR(ENOMEM);
}
}
/* cache frame */
av_fifo_generic_write(buf->fifo, &ref, sizeof(AVFilterBufferRef *), NULL);
return 0;
}
static int filter_frame(AVFilterLink *link, AVFrame *frame)
{
AVFilterContext *ctx = link->dst;
BufferSinkContext *buf = link->dst->priv;
int ret;
if ((ret = add_buffer_ref(ctx, frame)) < 0)
return ret;
if (buf->warning_limit &&
av_fifo_size(buf->fifo) / sizeof(AVFilterBufferRef *) >= buf->warning_limit) {
av_log(ctx, AV_LOG_WARNING,
"%d buffers queued in %s, something may be wrong.\n",
buf->warning_limit,
(char *)av_x_if_null(ctx->name, ctx->filter->name));
buf->warning_limit *= 10;
}
return 0;
}
int attribute_align_arg av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame)
{
return av_buffersink_get_frame_flags(ctx, frame, 0);
}
int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
{
BufferSinkContext *buf = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
int ret;
AVFrame *cur_frame;
/* no picref available, fetch it from the filterchain */
if (!av_fifo_size(buf->fifo)) {
if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST)
return AVERROR(EAGAIN);
if ((ret = ff_request_frame(inlink)) < 0)
return ret;
}
if (!av_fifo_size(buf->fifo))
return AVERROR(EINVAL);
if (flags & AV_BUFFERSINK_FLAG_PEEK) {
cur_frame = *((AVFrame **)av_fifo_peek2(buf->fifo, 0));
if ((ret = av_frame_ref(frame, cur_frame)) < 0)
return ret;
} else {
av_fifo_generic_read(buf->fifo, &cur_frame, sizeof(cur_frame), NULL);
av_frame_move_ref(frame, cur_frame);
av_frame_free(&cur_frame);
}
return 0;
}
static int read_from_fifo(AVFilterContext *ctx, AVFrame *frame,
int nb_samples)
{
BufferSinkContext *s = ctx->priv;
AVFilterLink *link = ctx->inputs[0];
AVFrame *tmp;
if (!(tmp = ff_get_audio_buffer(link, nb_samples)))
return AVERROR(ENOMEM);
av_audio_fifo_read(s->audio_fifo, (void**)tmp->extended_data, nb_samples);
tmp->pts = s->next_pts;
if (s->next_pts != AV_NOPTS_VALUE)
s->next_pts += av_rescale_q(nb_samples, (AVRational){1, link->sample_rate},
link->time_base);
av_frame_move_ref(frame, tmp);
av_frame_free(&tmp);
return 0;
}
int attribute_align_arg av_buffersink_get_samples(AVFilterContext *ctx,
AVFrame *frame, int nb_samples)
{
BufferSinkContext *s = ctx->priv;
AVFilterLink *link = ctx->inputs[0];
AVFrame *cur_frame;
int ret = 0;
if (!s->audio_fifo) {
int nb_channels = link->channels;
if (!(s->audio_fifo = av_audio_fifo_alloc(link->format, nb_channels, nb_samples)))
return AVERROR(ENOMEM);
}
while (ret >= 0) {
if (av_audio_fifo_size(s->audio_fifo) >= nb_samples)
return read_from_fifo(ctx, frame, nb_samples);
if (!(cur_frame = av_frame_alloc()))
return AVERROR(ENOMEM);
ret = av_buffersink_get_frame_flags(ctx, cur_frame, 0);
if (ret == AVERROR_EOF && av_audio_fifo_size(s->audio_fifo)) {
av_frame_free(&cur_frame);
return read_from_fifo(ctx, frame, av_audio_fifo_size(s->audio_fifo));
} else if (ret < 0) {
av_frame_free(&cur_frame);
return ret;
}
if (cur_frame->pts != AV_NOPTS_VALUE) {
s->next_pts = cur_frame->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**)cur_frame->extended_data,
cur_frame->nb_samples);
av_frame_free(&cur_frame);
}
return ret;
}
AVBufferSinkParams *av_buffersink_params_alloc(void)
{
static const int pixel_fmts[] = { AV_PIX_FMT_NONE };
AVBufferSinkParams *params = av_malloc(sizeof(AVBufferSinkParams));
if (!params)
return NULL;
params->pixel_fmts = pixel_fmts;
return params;
}
AVABufferSinkParams *av_abuffersink_params_alloc(void)
{
AVABufferSinkParams *params = av_mallocz(sizeof(AVABufferSinkParams));
if (!params)
return NULL;
return params;
}
#define FIFO_INIT_SIZE 8
static av_cold int common_init(AVFilterContext *ctx)
{
BufferSinkContext *buf = ctx->priv;
buf->fifo = av_fifo_alloc(FIFO_INIT_SIZE*sizeof(AVFilterBufferRef *));
if (!buf->fifo) {
av_log(ctx, AV_LOG_ERROR, "Failed to allocate fifo\n");
return AVERROR(ENOMEM);
}
buf->warning_limit = 100;
buf->next_pts = AV_NOPTS_VALUE;
return 0;
}
void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size)
{
AVFilterLink *inlink = ctx->inputs[0];
inlink->min_samples = inlink->max_samples =
inlink->partial_buf_size = frame_size;
}
#if FF_API_AVFILTERBUFFER
FF_DISABLE_DEPRECATION_WARNINGS
static void compat_free_buffer(AVFilterBuffer *buf)
{
AVFrame *frame = buf->priv;
av_frame_free(&frame);
av_free(buf);
}
static int compat_read(AVFilterContext *ctx,
AVFilterBufferRef **pbuf, int nb_samples, int flags)
{
AVFilterBufferRef *buf;
AVFrame *frame;
int ret;
if (!pbuf)
return ff_poll_frame(ctx->inputs[0]);
frame = av_frame_alloc();
if (!frame)
return AVERROR(ENOMEM);
if (!nb_samples)
ret = av_buffersink_get_frame_flags(ctx, frame, flags);
else
ret = av_buffersink_get_samples(ctx, frame, nb_samples);
if (ret < 0)
goto fail;
AV_NOWARN_DEPRECATED(
if (ctx->inputs[0]->type == AVMEDIA_TYPE_VIDEO) {
buf = avfilter_get_video_buffer_ref_from_arrays(frame->data, frame->linesize,
AV_PERM_READ,
frame->width, frame->height,
frame->format);
} else {
buf = avfilter_get_audio_buffer_ref_from_arrays(frame->extended_data,
frame->linesize[0], AV_PERM_READ,
frame->nb_samples,
frame->format,
frame->channel_layout);
}
if (!buf) {
ret = AVERROR(ENOMEM);
goto fail;
}
avfilter_copy_frame_props(buf, frame);
)
buf->buf->priv = frame;
buf->buf->free = compat_free_buffer;
*pbuf = buf;
return 0;
fail:
av_frame_free(&frame);
return ret;
}
int attribute_align_arg av_buffersink_read(AVFilterContext *ctx, AVFilterBufferRef **buf)
{
return compat_read(ctx, buf, 0, 0);
}
int attribute_align_arg av_buffersink_read_samples(AVFilterContext *ctx, AVFilterBufferRef **buf,
int nb_samples)
{
return compat_read(ctx, buf, nb_samples, 0);
}
int attribute_align_arg av_buffersink_get_buffer_ref(AVFilterContext *ctx,
AVFilterBufferRef **bufref, int flags)
{
*bufref = NULL;
av_assert0( !strcmp(ctx->filter->name, "buffersink")
|| !strcmp(ctx->filter->name, "abuffersink")
|| !strcmp(ctx->filter->name, "ffbuffersink")
|| !strcmp(ctx->filter->name, "ffabuffersink"));
return compat_read(ctx, bufref, 0, flags);
}
FF_ENABLE_DEPRECATION_WARNINGS
#endif
AVRational av_buffersink_get_frame_rate(AVFilterContext *ctx)
{
av_assert0( !strcmp(ctx->filter->name, "buffersink")
|| !strcmp(ctx->filter->name, "ffbuffersink"));
return ctx->inputs[0]->frame_rate;
}
int attribute_align_arg av_buffersink_poll_frame(AVFilterContext *ctx)
{
BufferSinkContext *buf = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
av_assert0( !strcmp(ctx->filter->name, "buffersink")
|| !strcmp(ctx->filter->name, "abuffersink")
|| !strcmp(ctx->filter->name, "ffbuffersink")
|| !strcmp(ctx->filter->name, "ffabuffersink"));
return av_fifo_size(buf->fifo)/sizeof(AVFilterBufferRef *) + ff_poll_frame(inlink);
}
static av_cold int vsink_init(AVFilterContext *ctx, void *opaque)
{
BufferSinkContext *buf = ctx->priv;
AVBufferSinkParams *params = opaque;
int ret;
if (params) {
if ((ret = av_opt_set_int_list(buf, "pix_fmts", params->pixel_fmts, AV_PIX_FMT_NONE, 0)) < 0)
return ret;
}
return common_init(ctx);
}
#define CHECK_LIST_SIZE(field) \
if (buf->field ## _size % sizeof(*buf->field)) { \
av_log(ctx, AV_LOG_ERROR, "Invalid size for " #field ": %d, " \
"should be multiple of %d\n", \
buf->field ## _size, (int)sizeof(*buf->field)); \
return AVERROR(EINVAL); \
}
static int vsink_query_formats(AVFilterContext *ctx)
{
BufferSinkContext *buf = ctx->priv;
AVFilterFormats *formats = NULL;
unsigned i;
int ret;
CHECK_LIST_SIZE(pixel_fmts)
if (buf->pixel_fmts_size) {
for (i = 0; i < NB_ITEMS(buf->pixel_fmts); i++)
if ((ret = ff_add_format(&formats, buf->pixel_fmts[i])) < 0) {
ff_formats_unref(&formats);
return ret;
}
ff_set_common_formats(ctx, formats);
} else {
ff_default_query_formats(ctx);
}
return 0;
}
static av_cold int asink_init(AVFilterContext *ctx, void *opaque)
{
BufferSinkContext *buf = ctx->priv;
AVABufferSinkParams *params = opaque;
int ret;
if (params) {
if ((ret = av_opt_set_int_list(buf, "sample_fmts", params->sample_fmts, AV_SAMPLE_FMT_NONE, 0)) < 0 ||
(ret = av_opt_set_int_list(buf, "sample_rates", params->sample_rates, -1, 0)) < 0 ||
(ret = av_opt_set_int_list(buf, "channel_layouts", params->channel_layouts, -1, 0)) < 0 ||
(ret = av_opt_set_int_list(buf, "channel_counts", params->channel_counts, -1, 0)) < 0 ||
(ret = av_opt_set_int(buf, "all_channel_counts", params->all_channel_counts, 0)) < 0)
return ret;
}
return common_init(ctx);
}
static int asink_query_formats(AVFilterContext *ctx)
{
BufferSinkContext *buf = ctx->priv;
AVFilterFormats *formats = NULL;
AVFilterChannelLayouts *layouts = NULL;
unsigned i;
int ret;
CHECK_LIST_SIZE(sample_fmts)
CHECK_LIST_SIZE(sample_rates)
CHECK_LIST_SIZE(channel_layouts)
CHECK_LIST_SIZE(channel_counts)
if (buf->sample_fmts_size) {
for (i = 0; i < NB_ITEMS(buf->sample_fmts); i++)
if ((ret = ff_add_format(&formats, buf->sample_fmts[i])) < 0) {
ff_formats_unref(&formats);
return ret;
}
ff_set_common_formats(ctx, formats);
}
if (buf->channel_layouts_size || buf->channel_counts_size ||
buf->all_channel_counts) {
for (i = 0; i < NB_ITEMS(buf->channel_layouts); i++)
if ((ret = ff_add_channel_layout(&layouts, buf->channel_layouts[i])) < 0) {
ff_channel_layouts_unref(&layouts);
return ret;
}
for (i = 0; i < NB_ITEMS(buf->channel_counts); i++)
if ((ret = ff_add_channel_layout(&layouts, FF_COUNT2LAYOUT(buf->channel_counts[i]))) < 0) {
ff_channel_layouts_unref(&layouts);
return ret;
}
if (buf->all_channel_counts) {
if (layouts)
av_log(ctx, AV_LOG_WARNING,
"Conflicting all_channel_counts and list in options\n");
else if (!(layouts = ff_all_channel_counts()))
return AVERROR(ENOMEM);
}
ff_set_common_channel_layouts(ctx, layouts);
}
if (buf->sample_rates_size) {
formats = NULL;
for (i = 0; i < NB_ITEMS(buf->sample_rates); i++)
if ((ret = ff_add_format(&formats, buf->sample_rates[i])) < 0) {
ff_formats_unref(&formats);
return ret;
}
ff_set_common_samplerates(ctx, formats);
}
return 0;
}
#define OFFSET(x) offsetof(BufferSinkContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption buffersink_options[] = {
{ "pix_fmts", "set the supported pixel formats", OFFSET(pixel_fmts), AV_OPT_TYPE_BINARY, .flags = FLAGS },
{ NULL },
};
#undef FLAGS
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption abuffersink_options[] = {
{ "sample_fmts", "set the supported sample formats", OFFSET(sample_fmts), AV_OPT_TYPE_BINARY, .flags = FLAGS },
{ "sample_rates", "set the supported sample rates", OFFSET(sample_rates), AV_OPT_TYPE_BINARY, .flags = FLAGS },
{ "channel_layouts", "set the supported channel layouts", OFFSET(channel_layouts), AV_OPT_TYPE_BINARY, .flags = FLAGS },
{ "channel_counts", "set the supported channel counts", OFFSET(channel_counts), AV_OPT_TYPE_BINARY, .flags = FLAGS },
{ "all_channel_counts", "accept all channel counts", OFFSET(all_channel_counts), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS },
{ NULL },
};
#undef FLAGS
AVFILTER_DEFINE_CLASS(buffersink);
AVFILTER_DEFINE_CLASS(abuffersink);
#if FF_API_AVFILTERBUFFER
#define ffbuffersink_options buffersink_options
#define ffabuffersink_options abuffersink_options
AVFILTER_DEFINE_CLASS(ffbuffersink);
AVFILTER_DEFINE_CLASS(ffabuffersink);
static const AVFilterPad ffbuffersink_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
},
{ NULL },
};
AVFilter avfilter_vsink_ffbuffersink = {
.name = "ffbuffersink",
.description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."),
.priv_size = sizeof(BufferSinkContext),
.priv_class = &ffbuffersink_class,
.init_opaque = vsink_init,
.uninit = uninit,
.query_formats = vsink_query_formats,
.inputs = ffbuffersink_inputs,
.outputs = NULL,
};
static const AVFilterPad ffabuffersink_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL },
};
AVFilter avfilter_asink_ffabuffersink = {
.name = "ffabuffersink",
.description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."),
.init_opaque = asink_init,
.uninit = uninit,
.priv_size = sizeof(BufferSinkContext),
.priv_class = &ffabuffersink_class,
.query_formats = asink_query_formats,
.inputs = ffabuffersink_inputs,
.outputs = NULL,
};
#endif /* FF_API_AVFILTERBUFFER */
static const AVFilterPad avfilter_vsink_buffer_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
},
{ NULL }
};
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),
.priv_class = &buffersink_class,
.init_opaque = vsink_init,
.uninit = uninit,
.query_formats = vsink_query_formats,
.inputs = avfilter_vsink_buffer_inputs,
.outputs = NULL,
};
static const AVFilterPad avfilter_asink_abuffer_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
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_class = &abuffersink_class,
.priv_size = sizeof(BufferSinkContext),
.init_opaque = asink_init,
.uninit = uninit,
.query_formats = asink_query_formats,
.inputs = avfilter_asink_abuffer_inputs,
.outputs = NULL,
};
+186
View File
@@ -0,0 +1,186 @@
/*
* 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_BUFFERSINK_H
#define AVFILTER_BUFFERSINK_H
/**
* @file
* memory buffer sink API for audio and video
*/
#include "avfilter.h"
#if FF_API_AVFILTERBUFFER
/**
* Get an audio/video buffer data from buffer_sink and put it in bufref.
*
* This function works with both audio and video buffer sinks.
*
* @param buffer_sink pointer to a buffersink or abuffersink context
* @param flags a combination of AV_BUFFERSINK_FLAG_* flags
* @return >= 0 in case of success, a negative AVERROR code in case of
* failure
*/
attribute_deprecated
int av_buffersink_get_buffer_ref(AVFilterContext *buffer_sink,
AVFilterBufferRef **bufref, int flags);
/**
* Get the number of immediately available frames.
*/
attribute_deprecated
int av_buffersink_poll_frame(AVFilterContext *ctx);
/**
* Get a buffer with filtered data from sink and put it in buf.
*
* @param ctx 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
* output.
*
* @return >= 0 in case of success, a negative AVERROR code in case of
* failure.
*/
attribute_deprecated
int av_buffersink_read(AVFilterContext *ctx, 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 ctx 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.
*/
attribute_deprecated
int av_buffersink_read_samples(AVFilterContext *ctx, AVFilterBufferRef **buf,
int nb_samples);
#endif
/**
* Get a frame with filtered data from sink and put it in frame.
*
* @param ctx pointer to a buffersink or abuffersink filter context.
* @param frame pointer to an allocated frame that will be filled with data.
* The data must be freed using av_frame_unref() / av_frame_free()
* @param flags a combination of AV_BUFFERSINK_FLAG_* flags
*
* @return >= 0 in for success, a negative AVERROR code for failure.
*/
int av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags);
/**
* Tell av_buffersink_get_buffer_ref() to read video/samples buffer
* reference, but not remove it from the buffer. This is useful if you
* need only to read a video/samples buffer, without to fetch it.
*/
#define AV_BUFFERSINK_FLAG_PEEK 1
/**
* Tell av_buffersink_get_buffer_ref() not to request a frame from its input.
* If a frame is already buffered, it is read (and removed from the buffer),
* but if no frame is present, return AVERROR(EAGAIN).
*/
#define AV_BUFFERSINK_FLAG_NO_REQUEST 2
/**
* Struct to use for initializing a buffersink context.
*/
typedef struct {
const enum AVPixelFormat *pixel_fmts; ///< list of allowed pixel formats, terminated by AV_PIX_FMT_NONE
} AVBufferSinkParams;
/**
* Create an AVBufferSinkParams structure.
*
* Must be freed with av_free().
*/
AVBufferSinkParams *av_buffersink_params_alloc(void);
/**
* Struct to use for initializing an abuffersink context.
*/
typedef struct {
const enum AVSampleFormat *sample_fmts; ///< list of allowed sample formats, terminated by AV_SAMPLE_FMT_NONE
const int64_t *channel_layouts; ///< list of allowed channel layouts, terminated by -1
const int *channel_counts; ///< list of allowed channel counts, terminated by -1
int all_channel_counts; ///< if not 0, accept any channel count or layout
int *sample_rates; ///< list of allowed sample rates, terminated by -1
} AVABufferSinkParams;
/**
* Create an AVABufferSinkParams structure.
*
* Must be freed with av_free().
*/
AVABufferSinkParams *av_abuffersink_params_alloc(void);
/**
* Set the frame size for an audio buffer sink.
*
* All calls to av_buffersink_get_buffer_ref will return a buffer with
* exactly the specified number of samples, or AVERROR(EAGAIN) if there is
* not enough. The last buffer at EOF will be padded with 0.
*/
void av_buffersink_set_frame_size(AVFilterContext *ctx, unsigned frame_size);
/**
* Get the frame rate of the input.
*/
AVRational av_buffersink_get_frame_rate(AVFilterContext *ctx);
/**
* Get a frame with filtered data from sink and put it in frame.
*
* @param ctx pointer to a context of a buffersink or abuffersink AVFilter.
* @param frame pointer to an allocated frame that will be filled with data.
* The data must be freed using av_frame_unref() / av_frame_free()
*
* @return >= 0 in case of success, a negative AVERROR code in case of
* failure.
*/
int av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame);
/**
* Same as av_buffersink_get_frame(), but with the ability to specify the number
* of samples read. This function is less efficient than
* av_buffersink_get_frame(), because it copies the data around.
*
* @param ctx pointer to a context of the abuffersink AVFilter.
* @param frame pointer to an allocated frame that will be filled with data.
* The data must be freed using av_frame_unref() / av_frame_free()
* frame will contain exactly nb_samples audio samples, except at
* the end of stream, when it can contain less than nb_samples.
*
* @warning do not mix this function with av_buffersink_get_frame(). Use only one or
* the other with a single sink, not both.
*/
int av_buffersink_get_samples(AVFilterContext *ctx, AVFrame *frame, int nb_samples);
#endif /* AVFILTER_BUFFERSINK_H */
+551
View File
@@ -0,0 +1,551 @@
/*
* 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 <float.h>
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/fifo.h"
#include "libavutil/frame.h"
#include "libavutil/imgutils.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
#include "audio.h"
#include "avfilter.h"
#include "buffersrc.h"
#include "formats.h"
#include "internal.h"
#include "video.h"
#include "avcodec.h"
typedef struct {
const AVClass *class;
AVFifoBuffer *fifo;
AVRational time_base; ///< time_base to set in the output link
AVRational frame_rate; ///< frame_rate to set in the output link
unsigned nb_failed_requests;
unsigned warning_limit;
/* video only */
int w, h;
enum AVPixelFormat pix_fmt;
AVRational pixel_aspect;
char *sws_param;
/* audio only */
int sample_rate;
enum AVSampleFormat sample_fmt;
char *sample_fmt_str;
int channels;
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_INFO, "Changing frame properties on the fly is not supported by all filters.\n");\
}
#define CHECK_AUDIO_PARAM_CHANGE(s, c, srate, ch_layout, ch_count, format)\
if (c->sample_fmt != format || c->sample_rate != srate ||\
c->channel_layout != ch_layout || c->channels != ch_count) {\
av_log(s, AV_LOG_ERROR, "Changing frame properties on the fly is not supported.\n");\
return AVERROR(EINVAL);\
}
int attribute_align_arg av_buffersrc_write_frame(AVFilterContext *ctx, const AVFrame *frame)
{
return av_buffersrc_add_frame_flags(ctx, (AVFrame *)frame,
AV_BUFFERSRC_FLAG_KEEP_REF);
}
int attribute_align_arg av_buffersrc_add_frame(AVFilterContext *ctx, AVFrame *frame)
{
return av_buffersrc_add_frame_flags(ctx, frame, 0);
}
static int av_buffersrc_add_frame_internal(AVFilterContext *ctx,
AVFrame *frame, int flags);
int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFrame *frame, int flags)
{
AVFrame *copy = NULL;
int ret = 0;
if (frame && frame->channel_layout &&
av_get_channel_layout_nb_channels(frame->channel_layout) != av_frame_get_channels(frame)) {
av_log(0, AV_LOG_ERROR, "Layout indicates a different number of channels than actually present\n");
return AVERROR(EINVAL);
}
if (!(flags & AV_BUFFERSRC_FLAG_KEEP_REF) || !frame)
return av_buffersrc_add_frame_internal(ctx, frame, flags);
if (!(copy = av_frame_alloc()))
return AVERROR(ENOMEM);
ret = av_frame_ref(copy, frame);
if (ret >= 0)
ret = av_buffersrc_add_frame_internal(ctx, copy, flags);
av_frame_free(&copy);
return ret;
}
static int av_buffersrc_add_frame_internal(AVFilterContext *ctx,
AVFrame *frame, int flags)
{
BufferSourceContext *s = ctx->priv;
AVFrame *copy;
int ret;
s->nb_failed_requests = 0;
if (!frame) {
s->eof = 1;
return 0;
} else if (s->eof)
return AVERROR(EINVAL);
if (!(flags & AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT)) {
switch (ctx->outputs[0]->type) {
case AVMEDIA_TYPE_VIDEO:
CHECK_VIDEO_PARAM_CHANGE(ctx, s, frame->width, frame->height,
frame->format);
break;
case AVMEDIA_TYPE_AUDIO:
/* For layouts unknown on input but known on link after negotiation. */
if (!frame->channel_layout)
frame->channel_layout = s->channel_layout;
CHECK_AUDIO_PARAM_CHANGE(ctx, s, frame->sample_rate, frame->channel_layout,
av_frame_get_channels(frame), frame->format);
break;
default:
return AVERROR(EINVAL);
}
}
if (!av_fifo_space(s->fifo) &&
(ret = av_fifo_realloc2(s->fifo, av_fifo_size(s->fifo) +
sizeof(copy))) < 0)
return ret;
if (!(copy = av_frame_alloc()))
return AVERROR(ENOMEM);
av_frame_move_ref(copy, frame);
if ((ret = av_fifo_generic_write(s->fifo, &copy, sizeof(copy), NULL)) < 0) {
av_frame_move_ref(frame, copy);
av_frame_free(&copy);
return ret;
}
if ((flags & AV_BUFFERSRC_FLAG_PUSH))
if ((ret = ctx->output_pads[0].request_frame(ctx->outputs[0])) < 0)
return ret;
return 0;
}
#if FF_API_AVFILTERBUFFER
FF_DISABLE_DEPRECATION_WARNINGS
static void compat_free_buffer(void *opaque, uint8_t *data)
{
AVFilterBufferRef *buf = opaque;
AV_NOWARN_DEPRECATED(
avfilter_unref_buffer(buf);
)
}
static void compat_unref_buffer(void *opaque, uint8_t *data)
{
AVBufferRef *buf = opaque;
AV_NOWARN_DEPRECATED(
av_buffer_unref(&buf);
)
}
int av_buffersrc_add_ref(AVFilterContext *ctx, AVFilterBufferRef *buf,
int flags)
{
BufferSourceContext *s = ctx->priv;
AVFrame *frame = NULL;
AVBufferRef *dummy_buf = NULL;
int ret = 0, planes, i;
if (!buf) {
s->eof = 1;
return 0;
} else if (s->eof)
return AVERROR(EINVAL);
frame = av_frame_alloc();
if (!frame)
return AVERROR(ENOMEM);
dummy_buf = av_buffer_create(NULL, 0, compat_free_buffer, buf,
(buf->perms & AV_PERM_WRITE) ? 0 : AV_BUFFER_FLAG_READONLY);
if (!dummy_buf) {
ret = AVERROR(ENOMEM);
goto fail;
}
AV_NOWARN_DEPRECATED(
if ((ret = avfilter_copy_buf_props(frame, buf)) < 0)
goto fail;
)
#define WRAP_PLANE(ref_out, data, data_size) \
do { \
AVBufferRef *dummy_ref = av_buffer_ref(dummy_buf); \
if (!dummy_ref) { \
ret = AVERROR(ENOMEM); \
goto fail; \
} \
ref_out = av_buffer_create(data, data_size, compat_unref_buffer, \
dummy_ref, (buf->perms & AV_PERM_WRITE) ? 0 : AV_BUFFER_FLAG_READONLY); \
if (!ref_out) { \
av_frame_unref(frame); \
ret = AVERROR(ENOMEM); \
goto fail; \
} \
} while (0)
if (ctx->outputs[0]->type == AVMEDIA_TYPE_VIDEO) {
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
planes = av_pix_fmt_count_planes(frame->format);
if (!desc || planes <= 0) {
ret = AVERROR(EINVAL);
goto fail;
}
for (i = 0; i < planes; i++) {
int v_shift = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;
int plane_size = (frame->height >> v_shift) * frame->linesize[i];
WRAP_PLANE(frame->buf[i], frame->data[i], plane_size);
}
} else {
int planar = av_sample_fmt_is_planar(frame->format);
int channels = av_get_channel_layout_nb_channels(frame->channel_layout);
planes = planar ? channels : 1;
if (planes > FF_ARRAY_ELEMS(frame->buf)) {
frame->nb_extended_buf = planes - FF_ARRAY_ELEMS(frame->buf);
frame->extended_buf = av_mallocz(sizeof(*frame->extended_buf) *
frame->nb_extended_buf);
if (!frame->extended_buf) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
for (i = 0; i < FFMIN(planes, FF_ARRAY_ELEMS(frame->buf)); i++)
WRAP_PLANE(frame->buf[i], frame->extended_data[i], frame->linesize[0]);
for (i = 0; i < planes - FF_ARRAY_ELEMS(frame->buf); i++)
WRAP_PLANE(frame->extended_buf[i],
frame->extended_data[i + FF_ARRAY_ELEMS(frame->buf)],
frame->linesize[0]);
}
ret = av_buffersrc_add_frame_flags(ctx, frame, flags);
fail:
av_buffer_unref(&dummy_buf);
av_frame_free(&frame);
return ret;
}
FF_ENABLE_DEPRECATION_WARNINGS
int av_buffersrc_buffer(AVFilterContext *ctx, AVFilterBufferRef *buf)
{
return av_buffersrc_add_ref(ctx, buf, 0);
}
#endif
static av_cold int init_video(AVFilterContext *ctx)
{
BufferSourceContext *c = ctx->priv;
if (c->pix_fmt == AV_PIX_FMT_NONE || !c->w || !c->h || av_q2d(c->time_base) <= 0) {
av_log(ctx, AV_LOG_ERROR, "Invalid parameters provided.\n");
return AVERROR(EINVAL);
}
if (!(c->fifo = av_fifo_alloc(sizeof(AVFrame*))))
return AVERROR(ENOMEM);
av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d sws_param:%s\n",
c->w, c->h, av_get_pix_fmt_name(c->pix_fmt),
c->time_base.num, c->time_base.den, c->frame_rate.num, c->frame_rate.den,
c->pixel_aspect.num, c->pixel_aspect.den, (char *)av_x_if_null(c->sws_param, ""));
c->warning_limit = 100;
return 0;
}
unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src)
{
return ((BufferSourceContext *)buffer_src->priv)->nb_failed_requests;
}
#define OFFSET(x) offsetof(BufferSourceContext, x)
#define A AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM
#define V AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption buffer_options[] = {
{ "width", NULL, OFFSET(w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V },
{ "video_size", NULL, OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, .flags = V },
{ "height", NULL, OFFSET(h), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V },
{ "pix_fmt", NULL, OFFSET(pix_fmt), AV_OPT_TYPE_PIXEL_FMT, .flags = V },
#if FF_API_OLD_FILTER_OPTS
/* those 4 are for compatibility with the old option passing system where each filter
* did its own parsing */
{ "time_base_num", "deprecated, do not use", OFFSET(time_base.num), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V },
{ "time_base_den", "deprecated, do not use", OFFSET(time_base.den), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V },
{ "sar_num", "deprecated, do not use", OFFSET(pixel_aspect.num), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V },
{ "sar_den", "deprecated, do not use", OFFSET(pixel_aspect.den), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, V },
#endif
{ "sar", "sample aspect ratio", OFFSET(pixel_aspect), AV_OPT_TYPE_RATIONAL, { .dbl = 1 }, 0, DBL_MAX, V },
{ "pixel_aspect", "sample aspect ratio", OFFSET(pixel_aspect), AV_OPT_TYPE_RATIONAL, { .dbl = 1 }, 0, DBL_MAX, V },
{ "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V },
{ "frame_rate", NULL, OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V },
{ "sws_param", NULL, OFFSET(sws_param), AV_OPT_TYPE_STRING, .flags = V },
{ NULL },
};
AVFILTER_DEFINE_CLASS(buffer);
static const AVOption abuffer_options[] = {
{ "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, INT_MAX, A },
{ "sample_rate", NULL, OFFSET(sample_rate), AV_OPT_TYPE_INT, { .i64 = 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 },
{ "channels", NULL, OFFSET(channels), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, A },
{ NULL },
};
AVFILTER_DEFINE_CLASS(abuffer);
static av_cold int init_audio(AVFilterContext *ctx)
{
BufferSourceContext *s = ctx->priv;
int ret = 0;
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);
return AVERROR(EINVAL);
}
if (s->channel_layout_str) {
int n;
/* TODO reindent */
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);
return AVERROR(EINVAL);
}
n = av_get_channel_layout_nb_channels(s->channel_layout);
if (s->channels) {
if (n != s->channels) {
av_log(ctx, AV_LOG_ERROR,
"Mismatching channel count %d and layout '%s' "
"(%d channels)\n",
s->channels, s->channel_layout_str, n);
return AVERROR(EINVAL);
}
}
s->channels = n;
} else if (!s->channels) {
av_log(ctx, AV_LOG_ERROR, "Neither number of channels nor "
"channel layout specified\n");
return AVERROR(EINVAL);
}
if (!(s->fifo = av_fifo_alloc(sizeof(AVFrame*))))
return AVERROR(ENOMEM);
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 chlayout:%s\n",
s->time_base.num, s->time_base.den, s->sample_fmt_str,
s->sample_rate, s->channel_layout_str);
s->warning_limit = 100;
return ret;
}
static av_cold void uninit(AVFilterContext *ctx)
{
BufferSourceContext *s = ctx->priv;
while (s->fifo && av_fifo_size(s->fifo)) {
AVFrame *frame;
av_fifo_generic_read(s->fifo, &frame, sizeof(frame), NULL);
av_frame_free(&frame);
}
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:
ff_add_format(&formats, c->pix_fmt);
ff_set_common_formats(ctx, formats);
break;
case AVMEDIA_TYPE_AUDIO:
ff_add_format(&formats, c->sample_fmt);
ff_set_common_formats(ctx, formats);
ff_add_format(&samplerates, c->sample_rate);
ff_set_common_samplerates(ctx, samplerates);
ff_add_channel_layout(&channel_layouts,
c->channel_layout ? c->channel_layout :
FF_COUNT2LAYOUT(c->channels));
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:
if (!c->channel_layout)
c->channel_layout = link->channel_layout;
break;
default:
return AVERROR(EINVAL);
}
link->time_base = c->time_base;
link->frame_rate = c->frame_rate;
return 0;
}
static int request_frame(AVFilterLink *link)
{
BufferSourceContext *c = link->src->priv;
AVFrame *frame;
if (!av_fifo_size(c->fifo)) {
if (c->eof)
return AVERROR_EOF;
c->nb_failed_requests++;
return AVERROR(EAGAIN);
}
av_fifo_generic_read(c->fifo, &frame, sizeof(frame), NULL);
return ff_filter_frame(link, frame);
}
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(AVFrame*);
}
static const AVFilterPad avfilter_vsrc_buffer_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.request_frame = request_frame,
.poll_frame = poll_frame,
.config_props = config_props,
},
{ NULL }
};
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 = NULL,
.outputs = avfilter_vsrc_buffer_outputs,
.priv_class = &buffer_class,
};
static const AVFilterPad avfilter_asrc_abuffer_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.request_frame = request_frame,
.poll_frame = poll_frame,
.config_props = config_props,
},
{ 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 = NULL,
.outputs = avfilter_asrc_abuffer_outputs,
.priv_class = &abuffer_class,
};
+148
View File
@@ -0,0 +1,148 @@
/*
*
* 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_BUFFERSRC_H
#define AVFILTER_BUFFERSRC_H
/**
* @file
* Memory buffer source API.
*/
#include "libavcodec/avcodec.h"
#include "avfilter.h"
enum {
/**
* Do not check for format changes.
*/
AV_BUFFERSRC_FLAG_NO_CHECK_FORMAT = 1,
#if FF_API_AVFILTERBUFFER
/**
* Ignored
*/
AV_BUFFERSRC_FLAG_NO_COPY = 2,
#endif
/**
* Immediately push the frame to the output.
*/
AV_BUFFERSRC_FLAG_PUSH = 4,
/**
* Keep a reference to the frame.
* If the frame if reference-counted, create a new reference; otherwise
* copy the frame data.
*/
AV_BUFFERSRC_FLAG_KEEP_REF = 8,
};
/**
* Add buffer data in picref to buffer_src.
*
* @param buffer_src pointer to a buffer source context
* @param picref a buffer reference, or NULL to mark EOF
* @param flags a combination of AV_BUFFERSRC_FLAG_*
* @return >= 0 in case of success, a negative AVERROR code
* in case of failure
*/
int av_buffersrc_add_ref(AVFilterContext *buffer_src,
AVFilterBufferRef *picref, int flags);
/**
* Get the number of failed requests.
*
* A failed request is when the request_frame method is called while no
* frame is present in the buffer.
* The number is reset when a frame is added.
*/
unsigned av_buffersrc_get_nb_failed_requests(AVFilterContext *buffer_src);
#if FF_API_AVFILTERBUFFER
/**
* Add a buffer to the filtergraph s.
*
* @param buf buffer containing frame data to be passed down the filtergraph.
* This function will take ownership of buf, the user must not free it.
* A NULL buf signals EOF -- i.e. no more frames will be sent to this filter.
*
* @deprecated use av_buffersrc_write_frame() or av_buffersrc_add_frame()
*/
attribute_deprecated
int av_buffersrc_buffer(AVFilterContext *s, AVFilterBufferRef *buf);
#endif
/**
* Add a frame to the buffer source.
*
* @param s an instance of the buffersrc filter.
* @param frame frame to be added. If the frame is reference counted, this
* function will make a new reference to it. Otherwise the frame data will be
* copied.
*
* @return 0 on success, a negative AVERROR on error
*
* This function is equivalent to av_buffersrc_add_frame_flags() with the
* AV_BUFFERSRC_FLAG_KEEP_REF flag.
*/
int av_buffersrc_write_frame(AVFilterContext *s, const AVFrame *frame);
/**
* Add a frame to the buffer source.
*
* @param s an instance of the buffersrc filter.
* @param frame frame to be added. If the frame is reference counted, this
* function will take ownership of the reference(s) and reset the frame.
* Otherwise the frame data will be copied. If this function returns an error,
* the input frame is not touched.
*
* @return 0 on success, a negative AVERROR on error.
*
* @note the difference between this function and av_buffersrc_write_frame() is
* that av_buffersrc_write_frame() creates a new reference to the input frame,
* while this function takes ownership of the reference passed to it.
*
* This function is equivalent to av_buffersrc_add_frame_flags() without the
* AV_BUFFERSRC_FLAG_KEEP_REF flag.
*/
int av_buffersrc_add_frame(AVFilterContext *ctx, AVFrame *frame);
/**
* Add a frame to the buffer source.
*
* By default, if the frame is reference-counted, this function will take
* ownership of the reference(s) and reset the frame. This can be controled
* using the flags.
*
* If this function returns an error, the input frame is not touched.
*
* @param buffer_src pointer to a buffer source context
* @param frame a frame, or NULL to mark EOF
* @param flags a combination of AV_BUFFERSRC_FLAG_*
* @return >= 0 in case of success, a negative AVERROR code
* in case of failure
*/
int av_buffersrc_add_frame_flags(AVFilterContext *buffer_src,
AVFrame *frame, int flags);
#endif /* AVFILTER_BUFFERSRC_H */
+104
View File
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2013 Wei Gao <[email protected]>
*
* 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_DESHAKE_H
#define AVFILTER_DESHAKE_H
#include "config.h"
#include "avfilter.h"
#include "libavcodec/dsputil.h"
#include "transform.h"
#if CONFIG_OPENCL
#include "libavutil/opencl.h"
#endif
enum SearchMethod {
EXHAUSTIVE, ///< Search all possible positions
SMART_EXHAUSTIVE, ///< Search most possible positions (faster)
SEARCH_COUNT
};
typedef struct {
int x; ///< Horizontal shift
int y; ///< Vertical shift
} IntMotionVector;
typedef struct {
double x; ///< Horizontal shift
double y; ///< Vertical shift
} MotionVector;
typedef struct {
MotionVector vector; ///< Motion vector
double angle; ///< Angle of rotation
double zoom; ///< Zoom percentage
} Transform;
#if CONFIG_OPENCL
typedef struct {
size_t matrix_size;
float matrix_y[9];
float matrix_uv[9];
cl_mem cl_matrix_y;
cl_mem cl_matrix_uv;
int in_plane_size[8];
int out_plane_size[8];
int plane_num;
cl_mem cl_inbuf;
size_t cl_inbuf_size;
cl_mem cl_outbuf;
size_t cl_outbuf_size;
AVOpenCLKernelEnv kernel_env;
} DeshakeOpenclContext;
#endif
typedef struct {
const AVClass *class;
AVFrame *ref; ///< Previous frame
int rx; ///< Maximum horizontal shift
int ry; ///< Maximum vertical shift
int edge; ///< Edge fill method
int blocksize; ///< Size of blocks to compare
int contrast; ///< Contrast threshold
int search; ///< Motion search method
AVCodecContext *avctx;
DSPContext c; ///< Context providing optimized SAD methods
Transform last; ///< Transform from last frame
int refcount; ///< Number of reference frames (defines averaging window)
FILE *fp;
Transform avg;
int cw; ///< Crop motion search to this box
int ch;
int cx;
int cy;
char *filename; ///< Motion search detailed log filename
int opencl;
#if CONFIG_OPENCL
DeshakeOpenclContext opencl_ctx;
#endif
int (* transform)(AVFilterContext *ctx, int width, int height, int cw, int ch,
const float *matrix_y, const float *matrix_uv, enum InterpolateMethod interpolate,
enum FillMethod fill, AVFrame *in, AVFrame *out);
} DeshakeContext;
#endif /* AVFILTER_DESHAKE_H */
+176
View File
@@ -0,0 +1,176 @@
/*
* Copyright (C) 2013 Wei Gao <[email protected]>
*
* 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
* transform input video
*/
#include "libavutil/common.h"
#include "libavutil/dict.h"
#include "libavutil/pixdesc.h"
#include "deshake_opencl.h"
#include "libavutil/opencl_internal.h"
#define MATRIX_SIZE 6
#define PLANE_NUM 3
int ff_opencl_transform(AVFilterContext *ctx,
int width, int height, int cw, int ch,
const float *matrix_y, const float *matrix_uv,
enum InterpolateMethod interpolate,
enum FillMethod fill, AVFrame *in, AVFrame *out)
{
int ret = 0;
const size_t global_work_size = width * height + 2 * ch * cw;
cl_int status;
DeshakeContext *deshake = ctx->priv;
FFOpenclParam opencl_param = {0};
opencl_param.ctx = ctx;
opencl_param.kernel = deshake->opencl_ctx.kernel_env.kernel;
ret = av_opencl_buffer_write(deshake->opencl_ctx.cl_matrix_y, (uint8_t *)matrix_y, deshake->opencl_ctx.matrix_size * sizeof(cl_float));
if (ret < 0)
return ret;
ret = av_opencl_buffer_write(deshake->opencl_ctx.cl_matrix_uv, (uint8_t *)matrix_uv, deshake->opencl_ctx.matrix_size * sizeof(cl_float));
if (ret < 0)
return ret;
if ((unsigned int)interpolate > INTERPOLATE_BIQUADRATIC) {
av_log(ctx, AV_LOG_ERROR, "Selected interpolate method is invalid\n");
return AVERROR(EINVAL);
}
ret = ff_opencl_set_parameter(&opencl_param,
FF_OPENCL_PARAM_INFO(deshake->opencl_ctx.cl_inbuf),
FF_OPENCL_PARAM_INFO(deshake->opencl_ctx.cl_outbuf),
FF_OPENCL_PARAM_INFO(deshake->opencl_ctx.cl_matrix_y),
FF_OPENCL_PARAM_INFO(deshake->opencl_ctx.cl_matrix_uv),
FF_OPENCL_PARAM_INFO(interpolate),
FF_OPENCL_PARAM_INFO(fill),
FF_OPENCL_PARAM_INFO(in->linesize[0]),
FF_OPENCL_PARAM_INFO(out->linesize[0]),
FF_OPENCL_PARAM_INFO(in->linesize[1]),
FF_OPENCL_PARAM_INFO(out->linesize[1]),
FF_OPENCL_PARAM_INFO(height),
FF_OPENCL_PARAM_INFO(width),
FF_OPENCL_PARAM_INFO(ch),
FF_OPENCL_PARAM_INFO(cw),
NULL);
if (ret < 0)
return ret;
status = clEnqueueNDRangeKernel(deshake->opencl_ctx.kernel_env.command_queue,
deshake->opencl_ctx.kernel_env.kernel, 1, NULL,
&global_work_size, NULL, 0, NULL, NULL);
if (status != CL_SUCCESS) {
av_log(ctx, AV_LOG_ERROR, "OpenCL run kernel error occurred: %s\n", av_opencl_errstr(status));
return AVERROR_EXTERNAL;
}
clFinish(deshake->opencl_ctx.kernel_env.command_queue);
ret = av_opencl_buffer_read_image(out->data, deshake->opencl_ctx.out_plane_size,
deshake->opencl_ctx.plane_num, deshake->opencl_ctx.cl_outbuf,
deshake->opencl_ctx.cl_outbuf_size);
if (ret < 0)
return ret;
return ret;
}
int ff_opencl_deshake_init(AVFilterContext *ctx)
{
int ret = 0;
DeshakeContext *deshake = ctx->priv;
ret = av_opencl_init(NULL);
if (ret < 0)
return ret;
deshake->opencl_ctx.matrix_size = MATRIX_SIZE;
deshake->opencl_ctx.plane_num = PLANE_NUM;
ret = av_opencl_buffer_create(&deshake->opencl_ctx.cl_matrix_y,
deshake->opencl_ctx.matrix_size*sizeof(cl_float), CL_MEM_READ_ONLY, NULL);
if (ret < 0)
return ret;
ret = av_opencl_buffer_create(&deshake->opencl_ctx.cl_matrix_uv,
deshake->opencl_ctx.matrix_size*sizeof(cl_float), CL_MEM_READ_ONLY, NULL);
if (ret < 0)
return ret;
if (!deshake->opencl_ctx.kernel_env.kernel) {
ret = av_opencl_create_kernel(&deshake->opencl_ctx.kernel_env, "avfilter_transform");
if (ret < 0) {
av_log(ctx, AV_LOG_ERROR, "OpenCL failed to create kernel for name 'avfilter_transform'\n");
return ret;
}
}
return ret;
}
void ff_opencl_deshake_uninit(AVFilterContext *ctx)
{
DeshakeContext *deshake = ctx->priv;
av_opencl_buffer_release(&deshake->opencl_ctx.cl_inbuf);
av_opencl_buffer_release(&deshake->opencl_ctx.cl_outbuf);
av_opencl_buffer_release(&deshake->opencl_ctx.cl_matrix_y);
av_opencl_buffer_release(&deshake->opencl_ctx.cl_matrix_uv);
av_opencl_release_kernel(&deshake->opencl_ctx.kernel_env);
av_opencl_uninit();
}
int ff_opencl_deshake_process_inout_buf(AVFilterContext *ctx, AVFrame *in, AVFrame *out)
{
int ret = 0;
AVFilterLink *link = ctx->inputs[0];
DeshakeContext *deshake = ctx->priv;
const int hshift = av_pix_fmt_desc_get(link->format)->log2_chroma_h;
int chroma_height = FF_CEIL_RSHIFT(link->h, hshift);
if ((!deshake->opencl_ctx.cl_inbuf) || (!deshake->opencl_ctx.cl_outbuf)) {
deshake->opencl_ctx.in_plane_size[0] = (in->linesize[0] * in->height);
deshake->opencl_ctx.in_plane_size[1] = (in->linesize[1] * chroma_height);
deshake->opencl_ctx.in_plane_size[2] = (in->linesize[2] * chroma_height);
deshake->opencl_ctx.out_plane_size[0] = (out->linesize[0] * out->height);
deshake->opencl_ctx.out_plane_size[1] = (out->linesize[1] * chroma_height);
deshake->opencl_ctx.out_plane_size[2] = (out->linesize[2] * chroma_height);
deshake->opencl_ctx.cl_inbuf_size = deshake->opencl_ctx.in_plane_size[0] +
deshake->opencl_ctx.in_plane_size[1] +
deshake->opencl_ctx.in_plane_size[2];
deshake->opencl_ctx.cl_outbuf_size = deshake->opencl_ctx.out_plane_size[0] +
deshake->opencl_ctx.out_plane_size[1] +
deshake->opencl_ctx.out_plane_size[2];
if (!deshake->opencl_ctx.cl_inbuf) {
ret = av_opencl_buffer_create(&deshake->opencl_ctx.cl_inbuf,
deshake->opencl_ctx.cl_inbuf_size,
CL_MEM_READ_ONLY, NULL);
if (ret < 0)
return ret;
}
if (!deshake->opencl_ctx.cl_outbuf) {
ret = av_opencl_buffer_create(&deshake->opencl_ctx.cl_outbuf,
deshake->opencl_ctx.cl_outbuf_size,
CL_MEM_READ_WRITE, NULL);
if (ret < 0)
return ret;
}
}
ret = av_opencl_buffer_write_image(deshake->opencl_ctx.cl_inbuf,
deshake->opencl_ctx.cl_inbuf_size,
0, in->data,deshake->opencl_ctx.in_plane_size,
deshake->opencl_ctx.plane_num);
if(ret < 0)
return ret;
return ret;
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2013 Wei Gao <[email protected]>
*
* 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_DESHAKE_OPENCL_H
#define AVFILTER_DESHAKE_OPENCL_H
#include "deshake.h"
int ff_opencl_deshake_init(AVFilterContext *ctx);
void ff_opencl_deshake_uninit(AVFilterContext *ctx);
int ff_opencl_deshake_process_inout_buf(AVFilterContext *ctx, AVFrame *in, AVFrame *out);
int ff_opencl_transform(AVFilterContext *ctx,
int width, int height, int cw, int ch,
const float *matrix_y, const float *matrix_uv,
enum InterpolateMethod interpolate,
enum FillMethod fill, AVFrame *in, AVFrame *out);
#endif /* AVFILTER_DESHAKE_OPENCL_H */
@@ -0,0 +1,217 @@
/*
* Copyright (C) 2013 Wei Gao <[email protected]>
*
*
* 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_DESHAKE_OPENCL_KERNEL_H
#define AVFILTER_DESHAKE_OPENCL_KERNEL_H
#include "libavutil/opencl.h"
const char *ff_kernel_deshake_opencl = AV_OPENCL_KERNEL(
inline unsigned char pixel(global const unsigned char *src, float x, float y,
int w, int h,int stride, unsigned char def)
{
return (x < 0 || y < 0 || x >= w || y >= h) ? def : src[(int)x + (int)y * stride];
}
unsigned char interpolate_nearest(float x, float y, global const unsigned char *src,
int width, int height, int stride, unsigned char def)
{
return pixel(src, (int)(x + 0.5), (int)(y + 0.5), width, height, stride, def);
}
unsigned char interpolate_bilinear(float x, float y, global const unsigned char *src,
int width, int height, int stride, unsigned char def)
{
int x_c, x_f, y_c, y_f;
int v1, v2, v3, v4;
if (x < -1 || x > width || y < -1 || y > height) {
return def;
} else {
x_f = (int)x;
x_c = x_f + 1;
y_f = (int)y;
y_c = y_f + 1;
v1 = pixel(src, x_c, y_c, width, height, stride, def);
v2 = pixel(src, x_c, y_f, width, height, stride, def);
v3 = pixel(src, x_f, y_c, width, height, stride, def);
v4 = pixel(src, x_f, y_f, width, height, stride, def);
return (v1*(x - x_f)*(y - y_f) + v2*((x - x_f)*(y_c - y)) +
v3*(x_c - x)*(y - y_f) + v4*((x_c - x)*(y_c - y)));
}
}
unsigned char interpolate_biquadratic(float x, float y, global const unsigned char *src,
int width, int height, int stride, unsigned char def)
{
int x_c, x_f, y_c, y_f;
unsigned char v1, v2, v3, v4;
float f1, f2, f3, f4;
if (x < - 1 || x > width || y < -1 || y > height)
return def;
else {
x_f = (int)x;
x_c = x_f + 1;
y_f = (int)y;
y_c = y_f + 1;
v1 = pixel(src, x_c, y_c, width, height, stride, def);
v2 = pixel(src, x_c, y_f, width, height, stride, def);
v3 = pixel(src, x_f, y_c, width, height, stride, def);
v4 = pixel(src, x_f, y_f, width, height, stride, def);
f1 = 1 - sqrt((x_c - x) * (y_c - y));
f2 = 1 - sqrt((x_c - x) * (y - y_f));
f3 = 1 - sqrt((x - x_f) * (y_c - y));
f4 = 1 - sqrt((x - x_f) * (y - y_f));
return (v1 * f1 + v2 * f2 + v3 * f3 + v4 * f4) / (f1 + f2 + f3 + f4);
}
}
inline const float clipf(float a, float amin, float amax)
{
if (a < amin) return amin;
else if (a > amax) return amax;
else return a;
}
inline int mirror(int v, int m)
{
while ((unsigned)v > (unsigned)m) {
v = -v;
if (v < 0)
v += 2 * m;
}
return v;
}
kernel void avfilter_transform(global unsigned char *src,
global unsigned char *dst,
global float *matrix,
global float *matrix2,
int interpolate,
int fillmethod,
int src_stride_lu,
int dst_stride_lu,
int src_stride_ch,
int dst_stride_ch,
int height,
int width,
int ch,
int cw)
{
int global_id = get_global_id(0);
global unsigned char *dst_y = dst;
global unsigned char *dst_u = dst_y + height * dst_stride_lu;
global unsigned char *dst_v = dst_u + ch * dst_stride_ch;
global unsigned char *src_y = src;
global unsigned char *src_u = src_y + height * src_stride_lu;
global unsigned char *src_v = src_u + ch * src_stride_ch;
global unsigned char *tempdst;
global unsigned char *tempsrc;
int x;
int y;
float x_s;
float y_s;
int tempsrc_stride;
int tempdst_stride;
int temp_height;
int temp_width;
int curpos;
unsigned char def = 0;
if (global_id < width*height) {
y = global_id/width;
x = global_id%width;
x_s = x * matrix[0] + y * matrix[1] + matrix[2];
y_s = x * matrix[3] + y * matrix[4] + matrix[5];
tempdst = dst_y;
tempsrc = src_y;
tempsrc_stride = src_stride_lu;
tempdst_stride = dst_stride_lu;
temp_height = height;
temp_width = width;
} else if ((global_id >= width*height)&&(global_id < width*height + ch*cw)) {
y = (global_id - width*height)/cw;
x = (global_id - width*height)%cw;
x_s = x * matrix2[0] + y * matrix2[1] + matrix2[2];
y_s = x * matrix2[3] + y * matrix2[4] + matrix2[5];
tempdst = dst_u;
tempsrc = src_u;
tempsrc_stride = src_stride_ch;
tempdst_stride = dst_stride_ch;
temp_height = ch;
temp_width = cw;
} else {
y = (global_id - width*height - ch*cw)/cw;
x = (global_id - width*height - ch*cw)%cw;
x_s = x * matrix2[0] + y * matrix2[1] + matrix2[2];
y_s = x * matrix2[3] + y * matrix2[4] + matrix2[5];
tempdst = dst_v;
tempsrc = src_v;
tempsrc_stride = src_stride_ch;
tempdst_stride = dst_stride_ch;
temp_height = ch;
temp_width = cw;
}
curpos = y * tempdst_stride + x;
switch (fillmethod) {
case 0: //FILL_BLANK
def = 0;
break;
case 1: //FILL_ORIGINAL
def = tempsrc[y*tempsrc_stride+x];
break;
case 2: //FILL_CLAMP
y_s = clipf(y_s, 0, temp_height - 1);
x_s = clipf(x_s, 0, temp_width - 1);
def = tempsrc[(int)y_s * tempsrc_stride + (int)x_s];
break;
case 3: //FILL_MIRROR
y_s = mirror(y_s,temp_height - 1);
x_s = mirror(x_s,temp_width - 1);
def = tempsrc[(int)y_s * tempsrc_stride + (int)x_s];
break;
}
switch (interpolate) {
case 0: //INTERPOLATE_NEAREST
tempdst[curpos] = interpolate_nearest(x_s, y_s, tempsrc, temp_width, temp_height, tempsrc_stride, def);
break;
case 1: //INTERPOLATE_BILINEAR
tempdst[curpos] = interpolate_bilinear(x_s, y_s, tempsrc, temp_width, temp_height, tempsrc_stride, def);
break;
case 2: //INTERPOLATE_BIQUADRATIC
tempdst[curpos] = interpolate_biquadratic(x_s, y_s, tempsrc, temp_width, temp_height, tempsrc_stride, def);
break;
default:
return;
}
}
);
#endif /* AVFILTER_DESHAKE_OPENCL_KERNEL_H */
+569
View File
@@ -0,0 +1,569 @@
/*
* Copyright 2011 Stefano Sabatini <stefano.sabatini-lala poste it>
* Copyright 2012 Nicolas George <nicolas.george normalesup org>
*
* 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 <string.h>
#include "libavutil/avutil.h"
#include "libavutil/colorspace.h"
#include "libavutil/mem.h"
#include "libavutil/pixdesc.h"
#include "drawutils.h"
#include "formats.h"
enum { RED = 0, GREEN, BLUE, ALPHA };
int ff_fill_rgba_map(uint8_t *rgba_map, enum AVPixelFormat pix_fmt)
{
switch (pix_fmt) {
case AV_PIX_FMT_0RGB:
case AV_PIX_FMT_ARGB: rgba_map[ALPHA] = 0; rgba_map[RED ] = 1; rgba_map[GREEN] = 2; rgba_map[BLUE ] = 3; break;
case AV_PIX_FMT_0BGR:
case AV_PIX_FMT_ABGR: rgba_map[ALPHA] = 0; rgba_map[BLUE ] = 1; rgba_map[GREEN] = 2; rgba_map[RED ] = 3; break;
case AV_PIX_FMT_RGB48LE:
case AV_PIX_FMT_RGB48BE:
case AV_PIX_FMT_RGBA64BE:
case AV_PIX_FMT_RGBA64LE:
case AV_PIX_FMT_RGB0:
case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_RGB24: rgba_map[RED ] = 0; rgba_map[GREEN] = 1; rgba_map[BLUE ] = 2; rgba_map[ALPHA] = 3; break;
case AV_PIX_FMT_BGR48LE:
case AV_PIX_FMT_BGR48BE:
case AV_PIX_FMT_BGRA64BE:
case AV_PIX_FMT_BGRA64LE:
case AV_PIX_FMT_BGRA:
case AV_PIX_FMT_BGR0:
case AV_PIX_FMT_BGR24: rgba_map[BLUE ] = 0; rgba_map[GREEN] = 1; rgba_map[RED ] = 2; rgba_map[ALPHA] = 3; break;
case AV_PIX_FMT_GBRAP:
case AV_PIX_FMT_GBRP: rgba_map[GREEN] = 0; rgba_map[BLUE ] = 1; rgba_map[RED ] = 2; rgba_map[ALPHA] = 3; break;
default: /* unsupported */
return AVERROR(EINVAL);
}
return 0;
}
int ff_fill_line_with_color(uint8_t *line[4], int pixel_step[4], int w, uint8_t dst_color[4],
enum AVPixelFormat pix_fmt, uint8_t rgba_color[4],
int *is_packed_rgba, uint8_t rgba_map_ptr[4])
{
uint8_t rgba_map[4] = {0};
int i;
const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(pix_fmt);
int hsub = pix_desc->log2_chroma_w;
*is_packed_rgba = ff_fill_rgba_map(rgba_map, pix_fmt) >= 0;
if (*is_packed_rgba) {
pixel_step[0] = (av_get_bits_per_pixel(pix_desc))>>3;
for (i = 0; i < 4; i++)
dst_color[rgba_map[i]] = rgba_color[i];
line[0] = av_malloc(w * pixel_step[0]);
for (i = 0; i < w; i++)
memcpy(line[0] + i * pixel_step[0], dst_color, pixel_step[0]);
if (rgba_map_ptr)
memcpy(rgba_map_ptr, rgba_map, sizeof(rgba_map[0]) * 4);
} else {
int plane;
dst_color[0] = RGB_TO_Y_CCIR(rgba_color[0], rgba_color[1], rgba_color[2]);
dst_color[1] = RGB_TO_U_CCIR(rgba_color[0], rgba_color[1], rgba_color[2], 0);
dst_color[2] = RGB_TO_V_CCIR(rgba_color[0], rgba_color[1], rgba_color[2], 0);
dst_color[3] = rgba_color[3];
for (plane = 0; plane < 4; plane++) {
int line_size;
int hsub1 = (plane == 1 || plane == 2) ? hsub : 0;
pixel_step[plane] = 1;
line_size = FF_CEIL_RSHIFT(w, hsub1) * pixel_step[plane];
line[plane] = av_malloc(line_size);
memset(line[plane], dst_color[plane], line_size);
}
}
return 0;
}
void ff_draw_rectangle(uint8_t *dst[4], int dst_linesize[4],
uint8_t *src[4], int pixelstep[4],
int hsub, int vsub, int x, int y, int w, int h)
{
int i, plane;
uint8_t *p;
for (plane = 0; plane < 4 && dst[plane]; plane++) {
int hsub1 = plane == 1 || plane == 2 ? hsub : 0;
int vsub1 = plane == 1 || plane == 2 ? vsub : 0;
int width = FF_CEIL_RSHIFT(w, hsub1);
int height = FF_CEIL_RSHIFT(h, vsub1);
p = dst[plane] + (y >> vsub1) * dst_linesize[plane];
for (i = 0; i < height; i++) {
memcpy(p + (x >> hsub1) * pixelstep[plane],
src[plane], width * pixelstep[plane]);
p += dst_linesize[plane];
}
}
}
void ff_copy_rectangle(uint8_t *dst[4], int dst_linesize[4],
uint8_t *src[4], int src_linesize[4], int pixelstep[4],
int hsub, int vsub, int x, int y, int y2, int w, int h)
{
int i, plane;
uint8_t *p;
for (plane = 0; plane < 4 && dst[plane]; plane++) {
int hsub1 = plane == 1 || plane == 2 ? hsub : 0;
int vsub1 = plane == 1 || plane == 2 ? vsub : 0;
int width = FF_CEIL_RSHIFT(w, hsub1);
int height = FF_CEIL_RSHIFT(h, vsub1);
p = dst[plane] + (y >> vsub1) * dst_linesize[plane];
for (i = 0; i < height; i++) {
memcpy(p + (x >> hsub1) * pixelstep[plane],
src[plane] + src_linesize[plane]*(i+(y2>>vsub1)), width * pixelstep[plane]);
p += dst_linesize[plane];
}
}
}
int ff_draw_init(FFDrawContext *draw, enum AVPixelFormat format, unsigned flags)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format);
const AVComponentDescriptor *c;
unsigned i, nb_planes = 0;
int pixelstep[MAX_PLANES] = { 0 };
if (!desc->name)
return AVERROR(EINVAL);
if (desc->flags & ~(AV_PIX_FMT_FLAG_PLANAR | AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_PSEUDOPAL | AV_PIX_FMT_FLAG_ALPHA))
return AVERROR(ENOSYS);
for (i = 0; i < desc->nb_components; i++) {
c = &desc->comp[i];
/* for now, only 8-bits formats */
if (c->depth_minus1 != 8 - 1)
return AVERROR(ENOSYS);
if (c->plane >= MAX_PLANES)
return AVERROR(ENOSYS);
/* strange interleaving */
if (pixelstep[c->plane] != 0 &&
pixelstep[c->plane] != c->step_minus1 + 1)
return AVERROR(ENOSYS);
pixelstep[c->plane] = c->step_minus1 + 1;
if (pixelstep[c->plane] >= 8)
return AVERROR(ENOSYS);
nb_planes = FFMAX(nb_planes, c->plane + 1);
}
if ((desc->log2_chroma_w || desc->log2_chroma_h) && nb_planes < 3)
return AVERROR(ENOSYS); /* exclude NV12 and NV21 */
memset(draw, 0, sizeof(*draw));
draw->desc = desc;
draw->format = format;
draw->nb_planes = nb_planes;
memcpy(draw->pixelstep, pixelstep, sizeof(draw->pixelstep));
draw->hsub[1] = draw->hsub[2] = draw->hsub_max = desc->log2_chroma_w;
draw->vsub[1] = draw->vsub[2] = draw->vsub_max = desc->log2_chroma_h;
for (i = 0; i < ((desc->nb_components - 1) | 1); i++)
draw->comp_mask[desc->comp[i].plane] |=
1 << (desc->comp[i].offset_plus1 - 1);
return 0;
}
void ff_draw_color(FFDrawContext *draw, FFDrawColor *color, const uint8_t rgba[4])
{
unsigned i;
uint8_t rgba_map[4];
if (rgba != color->rgba)
memcpy(color->rgba, rgba, sizeof(color->rgba));
if ((draw->desc->flags & AV_PIX_FMT_FLAG_RGB) &&
ff_fill_rgba_map(rgba_map, draw->format) >= 0) {
if (draw->nb_planes == 1) {
for (i = 0; i < 4; i++)
color->comp[0].u8[rgba_map[i]] = rgba[i];
} else {
for (i = 0; i < 4; i++)
color->comp[rgba_map[i]].u8[0] = rgba[i];
}
} else if (draw->nb_planes == 3 || draw->nb_planes == 4) {
/* assume YUV */
color->comp[0].u8[0] = RGB_TO_Y_CCIR(rgba[0], rgba[1], rgba[2]);
color->comp[1].u8[0] = RGB_TO_U_CCIR(rgba[0], rgba[1], rgba[2], 0);
color->comp[2].u8[0] = RGB_TO_V_CCIR(rgba[0], rgba[1], rgba[2], 0);
color->comp[3].u8[0] = rgba[3];
} else if (draw->format == AV_PIX_FMT_GRAY8 || draw->format == AV_PIX_FMT_GRAY8A) {
color->comp[0].u8[0] = RGB_TO_Y_CCIR(rgba[0], rgba[1], rgba[2]);
color->comp[1].u8[0] = rgba[3];
} else {
av_log(NULL, AV_LOG_WARNING,
"Color conversion not implemented for %s\n", draw->desc->name);
memset(color, 128, sizeof(*color));
}
}
static uint8_t *pointer_at(FFDrawContext *draw, uint8_t *data[], int linesize[],
int plane, int x, int y)
{
return data[plane] +
(y >> draw->vsub[plane]) * linesize[plane] +
(x >> draw->hsub[plane]) * draw->pixelstep[plane];
}
void ff_copy_rectangle2(FFDrawContext *draw,
uint8_t *dst[], int dst_linesize[],
uint8_t *src[], int src_linesize[],
int dst_x, int dst_y, int src_x, int src_y,
int w, int h)
{
int plane, y, wp, hp;
uint8_t *p, *q;
for (plane = 0; plane < draw->nb_planes; plane++) {
p = pointer_at(draw, src, src_linesize, plane, src_x, src_y);
q = pointer_at(draw, dst, dst_linesize, plane, dst_x, dst_y);
wp = FF_CEIL_RSHIFT(w, draw->hsub[plane]) * draw->pixelstep[plane];
hp = FF_CEIL_RSHIFT(h, draw->vsub[plane]);
for (y = 0; y < hp; y++) {
memcpy(q, p, wp);
p += src_linesize[plane];
q += dst_linesize[plane];
}
}
}
void ff_fill_rectangle(FFDrawContext *draw, FFDrawColor *color,
uint8_t *dst[], int dst_linesize[],
int dst_x, int dst_y, int w, int h)
{
int plane, x, y, wp, hp;
uint8_t *p0, *p;
for (plane = 0; plane < draw->nb_planes; plane++) {
p0 = pointer_at(draw, dst, dst_linesize, plane, dst_x, dst_y);
wp = FF_CEIL_RSHIFT(w, draw->hsub[plane]);
hp = FF_CEIL_RSHIFT(h, draw->vsub[plane]);
if (!hp)
return;
p = p0;
/* copy first line from color */
for (x = 0; x < wp; x++) {
memcpy(p, color->comp[plane].u8, draw->pixelstep[plane]);
p += draw->pixelstep[plane];
}
wp *= draw->pixelstep[plane];
/* copy next lines from first line */
p = p0 + dst_linesize[plane];
for (y = 1; y < hp; y++) {
memcpy(p, p0, wp);
p += dst_linesize[plane];
}
}
}
/**
* Clip interval [x; x+w[ within [0; wmax[.
* The resulting w may be negative if the final interval is empty.
* dx, if not null, return the difference between in and out value of x.
*/
static void clip_interval(int wmax, int *x, int *w, int *dx)
{
if (dx)
*dx = 0;
if (*x < 0) {
if (dx)
*dx = -*x;
*w += *x;
*x = 0;
}
if (*x + *w > wmax)
*w = wmax - *x;
}
/**
* Decompose w pixels starting at x
* into start + (w starting at x) + end
* with x and w aligned on multiples of 1<<sub.
*/
static void subsampling_bounds(int sub, int *x, int *w, int *start, int *end)
{
int mask = (1 << sub) - 1;
*start = (-*x) & mask;
*x += *start;
*start = FFMIN(*start, *w);
*w -= *start;
*end = *w & mask;
*w >>= sub;
}
static int component_used(FFDrawContext *draw, int plane, int comp)
{
return (draw->comp_mask[plane] >> comp) & 1;
}
/* If alpha is in the [ 0 ; 0x1010101 ] range,
then alpha * value is in the [ 0 ; 0xFFFFFFFF ] range,
and >> 24 gives a correct rounding. */
static void blend_line(uint8_t *dst, unsigned src, unsigned alpha,
int dx, int w, unsigned hsub, int left, int right)
{
unsigned asrc = alpha * src;
unsigned tau = 0x1010101 - alpha;
int x;
if (left) {
unsigned suba = (left * alpha) >> hsub;
*dst = (*dst * (0x1010101 - suba) + src * suba) >> 24;
dst += dx;
}
for (x = 0; x < w; x++) {
*dst = (*dst * tau + asrc) >> 24;
dst += dx;
}
if (right) {
unsigned suba = (right * alpha) >> hsub;
*dst = (*dst * (0x1010101 - suba) + src * suba) >> 24;
}
}
void ff_blend_rectangle(FFDrawContext *draw, FFDrawColor *color,
uint8_t *dst[], int dst_linesize[],
int dst_w, int dst_h,
int x0, int y0, int w, int h)
{
unsigned alpha, nb_planes, nb_comp, plane, comp;
int w_sub, h_sub, x_sub, y_sub, left, right, top, bottom, y;
uint8_t *p0, *p;
/* TODO optimize if alpha = 0xFF */
clip_interval(dst_w, &x0, &w, NULL);
clip_interval(dst_h, &y0, &h, NULL);
if (w <= 0 || h <= 0 || !color->rgba[3])
return;
/* 0x10203 * alpha + 2 is in the [ 2 ; 0x1010101 - 2 ] range */
alpha = 0x10203 * color->rgba[3] + 0x2;
nb_planes = (draw->nb_planes - 1) | 1; /* eliminate alpha */
for (plane = 0; plane < nb_planes; plane++) {
nb_comp = draw->pixelstep[plane];
p0 = pointer_at(draw, dst, dst_linesize, plane, x0, y0);
w_sub = w;
h_sub = h;
x_sub = x0;
y_sub = y0;
subsampling_bounds(draw->hsub[plane], &x_sub, &w_sub, &left, &right);
subsampling_bounds(draw->vsub[plane], &y_sub, &h_sub, &top, &bottom);
for (comp = 0; comp < nb_comp; comp++) {
if (!component_used(draw, plane, comp))
continue;
p = p0 + comp;
if (top) {
blend_line(p, color->comp[plane].u8[comp], alpha >> 1,
draw->pixelstep[plane], w_sub,
draw->hsub[plane], left, right);
p += dst_linesize[plane];
}
for (y = 0; y < h_sub; y++) {
blend_line(p, color->comp[plane].u8[comp], alpha,
draw->pixelstep[plane], w_sub,
draw->hsub[plane], left, right);
p += dst_linesize[plane];
}
if (bottom)
blend_line(p, color->comp[plane].u8[comp], alpha >> 1,
draw->pixelstep[plane], w_sub,
draw->hsub[plane], left, right);
}
}
}
static void blend_pixel(uint8_t *dst, unsigned src, unsigned alpha,
uint8_t *mask, int mask_linesize, int l2depth,
unsigned w, unsigned h, unsigned shift, unsigned xm0)
{
unsigned xm, x, y, t = 0;
unsigned xmshf = 3 - l2depth;
unsigned xmmod = 7 >> l2depth;
unsigned mbits = (1 << (1 << l2depth)) - 1;
unsigned mmult = 255 / mbits;
for (y = 0; y < h; y++) {
xm = xm0;
for (x = 0; x < w; x++) {
t += ((mask[xm >> xmshf] >> ((~xm & xmmod) << l2depth)) & mbits)
* mmult;
xm++;
}
mask += mask_linesize;
}
alpha = (t >> shift) * alpha;
*dst = ((0x1010101 - alpha) * *dst + alpha * src) >> 24;
}
static void blend_line_hv(uint8_t *dst, int dst_delta,
unsigned src, unsigned alpha,
uint8_t *mask, int mask_linesize, int l2depth, int w,
unsigned hsub, unsigned vsub,
int xm, int left, int right, int hband)
{
int x;
if (left) {
blend_pixel(dst, src, alpha, mask, mask_linesize, l2depth,
left, hband, hsub + vsub, xm);
dst += dst_delta;
xm += left;
}
for (x = 0; x < w; x++) {
blend_pixel(dst, src, alpha, mask, mask_linesize, l2depth,
1 << hsub, hband, hsub + vsub, xm);
dst += dst_delta;
xm += 1 << hsub;
}
if (right)
blend_pixel(dst, src, alpha, mask, mask_linesize, l2depth,
right, hband, hsub + vsub, xm);
}
void ff_blend_mask(FFDrawContext *draw, FFDrawColor *color,
uint8_t *dst[], int dst_linesize[], int dst_w, int dst_h,
uint8_t *mask, int mask_linesize, int mask_w, int mask_h,
int l2depth, unsigned endianness, int x0, int y0)
{
unsigned alpha, nb_planes, nb_comp, plane, comp;
int xm0, ym0, w_sub, h_sub, x_sub, y_sub, left, right, top, bottom, y;
uint8_t *p0, *p, *m;
clip_interval(dst_w, &x0, &mask_w, &xm0);
clip_interval(dst_h, &y0, &mask_h, &ym0);
mask += ym0 * mask_linesize;
if (mask_w <= 0 || mask_h <= 0 || !color->rgba[3])
return;
/* alpha is in the [ 0 ; 0x10203 ] range,
alpha * mask is in the [ 0 ; 0x1010101 - 4 ] range */
alpha = (0x10307 * color->rgba[3] + 0x3) >> 8;
nb_planes = (draw->nb_planes - 1) | 1; /* eliminate alpha */
for (plane = 0; plane < nb_planes; plane++) {
nb_comp = draw->pixelstep[plane];
p0 = pointer_at(draw, dst, dst_linesize, plane, x0, y0);
w_sub = mask_w;
h_sub = mask_h;
x_sub = x0;
y_sub = y0;
subsampling_bounds(draw->hsub[plane], &x_sub, &w_sub, &left, &right);
subsampling_bounds(draw->vsub[plane], &y_sub, &h_sub, &top, &bottom);
for (comp = 0; comp < nb_comp; comp++) {
if (!component_used(draw, plane, comp))
continue;
p = p0 + comp;
m = mask;
if (top) {
blend_line_hv(p, draw->pixelstep[plane],
color->comp[plane].u8[comp], alpha,
m, mask_linesize, l2depth, w_sub,
draw->hsub[plane], draw->vsub[plane],
xm0, left, right, top);
p += dst_linesize[plane];
m += top * mask_linesize;
}
for (y = 0; y < h_sub; y++) {
blend_line_hv(p, draw->pixelstep[plane],
color->comp[plane].u8[comp], alpha,
m, mask_linesize, l2depth, w_sub,
draw->hsub[plane], draw->vsub[plane],
xm0, left, right, 1 << draw->vsub[plane]);
p += dst_linesize[plane];
m += mask_linesize << draw->vsub[plane];
}
if (bottom)
blend_line_hv(p, draw->pixelstep[plane],
color->comp[plane].u8[comp], alpha,
m, mask_linesize, l2depth, w_sub,
draw->hsub[plane], draw->vsub[plane],
xm0, left, right, bottom);
}
}
}
int ff_draw_round_to_sub(FFDrawContext *draw, int sub_dir, int round_dir,
int value)
{
unsigned shift = sub_dir ? draw->vsub_max : draw->hsub_max;
if (!shift)
return value;
if (round_dir >= 0)
value += round_dir ? (1 << shift) - 1 : 1 << (shift - 1);
return (value >> shift) << shift;
}
AVFilterFormats *ff_draw_supported_pixel_formats(unsigned flags)
{
enum AVPixelFormat i, pix_fmts[AV_PIX_FMT_NB + 1];
unsigned n = 0;
FFDrawContext draw;
for (i = 0; i < AV_PIX_FMT_NB; i++)
if (ff_draw_init(&draw, i, flags) >= 0)
pix_fmts[n++] = i;
pix_fmts[n++] = AV_PIX_FMT_NONE;
return ff_make_format_list(pix_fmts);
}
#ifdef TEST
#undef printf
int main(void)
{
enum AVPixelFormat f;
const AVPixFmtDescriptor *desc;
FFDrawContext draw;
FFDrawColor color;
int r, i;
for (f = 0; f < AV_PIX_FMT_NB; f++) {
desc = av_pix_fmt_desc_get(f);
if (!desc->name)
continue;
printf("Testing %s...%*s", desc->name,
(int)(16 - strlen(desc->name)), "");
r = ff_draw_init(&draw, f, 0);
if (r < 0) {
char buf[128];
av_strerror(r, buf, sizeof(buf));
printf("no: %s\n", buf);
continue;
}
ff_draw_color(&draw, &color, (uint8_t[]) { 1, 0, 0, 1 });
for (i = 0; i < sizeof(color); i++)
if (((uint8_t *)&color)[i] != 128)
break;
if (i == sizeof(color)) {
printf("fallback color\n");
continue;
}
printf("ok\n");
}
return 0;
}
#endif
+155
View File
@@ -0,0 +1,155 @@
/*
* 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_DRAWUTILS_H
#define AVFILTER_DRAWUTILS_H
/**
* @file
* misc drawing utilities
*/
#include <stdint.h>
#include "avfilter.h"
#include "libavutil/pixfmt.h"
int ff_fill_rgba_map(uint8_t *rgba_map, enum AVPixelFormat pix_fmt);
int ff_fill_line_with_color(uint8_t *line[4], int pixel_step[4], int w,
uint8_t dst_color[4],
enum AVPixelFormat pix_fmt, uint8_t rgba_color[4],
int *is_packed_rgba, uint8_t rgba_map[4]);
void ff_draw_rectangle(uint8_t *dst[4], int dst_linesize[4],
uint8_t *src[4], int pixelstep[4],
int hsub, int vsub, int x, int y, int w, int h);
void ff_copy_rectangle(uint8_t *dst[4], int dst_linesize[4],
uint8_t *src[4], int src_linesize[4], int pixelstep[4],
int hsub, int vsub, int x, int y, int y2, int w, int h);
#define MAX_PLANES 4
typedef struct FFDrawContext {
const struct AVPixFmtDescriptor *desc;
enum AVPixelFormat format;
unsigned nb_planes;
int pixelstep[MAX_PLANES]; /*< offset between pixels */
uint8_t comp_mask[MAX_PLANES]; /*< bitmask of used non-alpha components */
uint8_t hsub[MAX_PLANES]; /*< horizontal subsampling */
uint8_t vsub[MAX_PLANES]; /*< vertical subsampling */
uint8_t hsub_max;
uint8_t vsub_max;
} FFDrawContext;
typedef struct FFDrawColor {
uint8_t rgba[4];
union {
uint32_t u32;
uint16_t u16;
uint8_t u8[4];
} comp[MAX_PLANES];
} FFDrawColor;
/**
* Init a draw context.
*
* Only a limited number of pixel formats are supported, if format is not
* supported the function will return an error.
* No flags currently defined.
* @return 0 for success, < 0 for error
*/
int ff_draw_init(FFDrawContext *draw, enum AVPixelFormat format, unsigned flags);
/**
* Prepare a color.
*/
void ff_draw_color(FFDrawContext *draw, FFDrawColor *color, const uint8_t rgba[4]);
/**
* Copy a rectangle from an image to another.
*
* The coordinates must be as even as the subsampling requires.
*/
void ff_copy_rectangle2(FFDrawContext *draw,
uint8_t *dst[], int dst_linesize[],
uint8_t *src[], int src_linesize[],
int dst_x, int dst_y, int src_x, int src_y,
int w, int h);
/**
* Fill a rectangle with an uniform color.
*
* The coordinates must be as even as the subsampling requires.
* The color needs to be inited with ff_draw_color.
*/
void ff_fill_rectangle(FFDrawContext *draw, FFDrawColor *color,
uint8_t *dst[], int dst_linesize[],
int dst_x, int dst_y, int w, int h);
/**
* Blend a rectangle with an uniform color.
*/
void ff_blend_rectangle(FFDrawContext *draw, FFDrawColor *color,
uint8_t *dst[], int dst_linesize[],
int dst_w, int dst_h,
int x0, int y0, int w, int h);
/**
* Blend an alpha mask with an uniform color.
*
* @param draw draw context
* @param color color for the overlay;
* @param dst destination image
* @param dst_linesize line stride of the destination
* @param dst_w width of the destination image
* @param dst_h height of the destination image
* @param mask mask
* @param mask_linesize line stride of the mask
* @param mask_w width of the mask
* @param mask_h height of the mask
* @param l2depth log2 of depth of the mask (0 for 1bpp, 3 for 8bpp)
* @param endianness bit order of the mask (0: MSB to the left)
* @param x0 horizontal position of the overlay
* @param y0 vertical position of the overlay
*/
void ff_blend_mask(FFDrawContext *draw, FFDrawColor *color,
uint8_t *dst[], int dst_linesize[], int dst_w, int dst_h,
uint8_t *mask, int mask_linesize, int mask_w, int mask_h,
int l2depth, unsigned endianness, int x0, int y0);
/**
* Round a dimension according to subsampling.
*
* @param draw draw context
* @param sub_dir 0 for horizontal, 1 for vertical
* @param round_dir 0 nearest, -1 round down, +1 round up
* @param value value to round
* @return the rounded value
*/
int ff_draw_round_to_sub(FFDrawContext *draw, int sub_dir, int round_dir,
int value);
/**
* Return the list of pixel formats supported by the draw functions.
*
* The flags are the same as ff_draw_init, i.e., none currently.
*/
AVFilterFormats *ff_draw_supported_pixel_formats(unsigned flags);
#endif /* AVFILTER_DRAWUTILS_H */
+83
View File
@@ -0,0 +1,83 @@
/*
* 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 "dualinput.h"
#include "libavutil/timestamp.h"
static int process_frame(FFFrameSync *fs)
{
AVFilterContext *ctx = fs->parent;
FFDualInputContext *s = fs->opaque;
AVFrame *mainpic = NULL, *secondpic = NULL;
int ret = 0;
if ((ret = ff_framesync_get_frame(&s->fs, 0, &mainpic, 1)) < 0 ||
(ret = ff_framesync_get_frame(&s->fs, 1, &secondpic, 0)) < 0) {
av_frame_free(&mainpic);
return ret;
}
av_assert0(mainpic);
mainpic->pts = av_rescale_q(mainpic->pts, s->fs.time_base, ctx->outputs[0]->time_base);
if (secondpic && !ctx->is_disabled)
mainpic = s->process(ctx, mainpic, secondpic);
ret = ff_filter_frame(ctx->outputs[0], mainpic);
av_assert1(ret != AVERROR(EAGAIN));
return ret;
}
int ff_dualinput_init(AVFilterContext *ctx, FFDualInputContext *s)
{
FFFrameSyncIn *in = s->fs.in;
ff_framesync_init(&s->fs, ctx, 2);
s->fs.opaque = s;
s->fs.on_event = process_frame;
in[0].time_base = ctx->inputs[0]->time_base;
in[1].time_base = ctx->inputs[1]->time_base;
in[0].sync = 2;
in[0].before = EXT_STOP;
in[0].after = EXT_INFINITY;
in[1].sync = 1;
in[1].before = EXT_NULL;
in[1].after = EXT_INFINITY;
if (s->shortest)
in[1].after = EXT_STOP;
if (!s->repeatlast) {
in[0].after = EXT_STOP;
in[1].sync = 0;
}
return ff_framesync_configure(&s->fs);
}
int ff_dualinput_filter_frame(FFDualInputContext *s,
AVFilterLink *inlink, AVFrame *in)
{
return ff_framesync_filter_frame(&s->fs, inlink, in);
}
int ff_dualinput_request_frame(FFDualInputContext *s, AVFilterLink *outlink)
{
return ff_framesync_request_frame(&s->fs, outlink);
}
void ff_dualinput_uninit(FFDualInputContext *s)
{
ff_framesync_uninit(&s->fs);
}
+46
View File
@@ -0,0 +1,46 @@
/*
* 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
* Double input streams helper for filters
*/
#ifndef AVFILTER_DUALINPUT_H
#define AVFILTER_DUALINPUT_H
#include <stdint.h>
#include "bufferqueue.h"
#include "framesync.h"
#include "internal.h"
typedef struct {
FFFrameSync fs;
FFFrameSyncIn second_input; /* must be immediately after fs */
AVFrame *(*process)(AVFilterContext *ctx, AVFrame *main, const AVFrame *second);
int shortest; ///< terminate stream when the second input terminates
int repeatlast; ///< repeat last second frame
} FFDualInputContext;
int ff_dualinput_init(AVFilterContext *ctx, FFDualInputContext *s);
int ff_dualinput_filter_frame(FFDualInputContext *s, AVFilterLink *inlink, AVFrame *in);
int ff_dualinput_request_frame(FFDualInputContext *s, AVFilterLink *outlink);
void ff_dualinput_uninit(FFDualInputContext *s);
#endif /* AVFILTER_DUALINPUT_H */
+799
View File
@@ -0,0 +1,799 @@
/*
* Copyright (c) 2012 Clément Bœsch
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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
* EBU R.128 implementation
* @see http://tech.ebu.ch/loudness
* @see https://www.youtube.com/watch?v=iuEtQqC-Sqo "EBU R128 Introduction - Florian Camerer"
* @todo True Peak
* @todo implement start/stop/reset through filter command injection
* @todo support other frequencies to avoid resampling
*/
#include <math.h>
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/channel_layout.h"
#include "libavutil/dict.h"
#include "libavutil/xga_font_data.h"
#include "libavutil/opt.h"
#include "libavutil/timestamp.h"
#include "audio.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
#define MAX_CHANNELS 63
/* pre-filter coefficients */
#define PRE_B0 1.53512485958697
#define PRE_B1 -2.69169618940638
#define PRE_B2 1.19839281085285
#define PRE_A1 -1.69065929318241
#define PRE_A2 0.73248077421585
/* RLB-filter coefficients */
#define RLB_B0 1.0
#define RLB_B1 -2.0
#define RLB_B2 1.0
#define RLB_A1 -1.99004745483398
#define RLB_A2 0.99007225036621
#define ABS_THRES -70 ///< silence gate: we discard anything below this absolute (LUFS) threshold
#define ABS_UP_THRES 10 ///< upper loud limit to consider (ABS_THRES being the minimum)
#define HIST_GRAIN 100 ///< defines histogram precision
#define HIST_SIZE ((ABS_UP_THRES - ABS_THRES) * HIST_GRAIN + 1)
/**
* A histogram is an array of HIST_SIZE hist_entry storing all the energies
* recorded (with an accuracy of 1/HIST_GRAIN) of the loudnesses from ABS_THRES
* (at 0) to ABS_UP_THRES (at HIST_SIZE-1).
* This fixed-size system avoids the need of a list of energies growing
* infinitely over the time and is thus more scalable.
*/
struct hist_entry {
int count; ///< how many times the corresponding value occurred
double energy; ///< E = 10^((L + 0.691) / 10)
double loudness; ///< L = -0.691 + 10 * log10(E)
};
struct integrator {
double *cache[MAX_CHANNELS]; ///< window of filtered samples (N ms)
int cache_pos; ///< focus on the last added bin in the cache array
double sum[MAX_CHANNELS]; ///< sum of the last N ms filtered samples (cache content)
int filled; ///< 1 if the cache is completely filled, 0 otherwise
double rel_threshold; ///< relative threshold
double sum_kept_powers; ///< sum of the powers (weighted sums) above absolute threshold
int nb_kept_powers; ///< number of sum above absolute threshold
struct hist_entry *histogram; ///< histogram of the powers, used to compute LRA and I
};
struct rect { int x, y, w, h; };
typedef struct {
const AVClass *class; ///< AVClass context for log and options purpose
/* video */
int do_video; ///< 1 if video output enabled, 0 otherwise
int w, h; ///< size of the video output
struct rect text; ///< rectangle for the LU legend on the left
struct rect graph; ///< rectangle for the main graph in the center
struct rect gauge; ///< rectangle for the gauge on the right
AVFrame *outpicref; ///< output picture reference, updated regularly
int meter; ///< select a EBU mode between +9 and +18
int scale_range; ///< the range of LU values according to the meter
int y_zero_lu; ///< the y value (pixel position) for 0 LU
int *y_line_ref; ///< y reference values for drawing the LU lines in the graph and the gauge
/* audio */
int nb_channels; ///< number of channels in the input
double *ch_weighting; ///< channel weighting mapping
int sample_count; ///< sample count used for refresh frequency, reset at refresh
/* Filter caches.
* The mult by 3 in the following is for X[i], X[i-1] and X[i-2] */
double x[MAX_CHANNELS * 3]; ///< 3 input samples cache for each channel
double y[MAX_CHANNELS * 3]; ///< 3 pre-filter samples cache for each channel
double z[MAX_CHANNELS * 3]; ///< 3 RLB-filter samples cache for each channel
#define I400_BINS (48000 * 4 / 10)
#define I3000_BINS (48000 * 3)
struct integrator i400; ///< 400ms integrator, used for Momentary loudness (M), and Integrated loudness (I)
struct integrator i3000; ///< 3s integrator, used for Short term loudness (S), and Loudness Range (LRA)
/* I and LRA specific */
double integrated_loudness; ///< integrated loudness in LUFS (I)
double loudness_range; ///< loudness range in LU (LRA)
double lra_low, lra_high; ///< low and high LRA values
/* misc */
int loglevel; ///< log level for frame logging
int metadata; ///< whether or not to inject loudness results in frames
} EBUR128Context;
#define OFFSET(x) offsetof(EBUR128Context, x)
#define A AV_OPT_FLAG_AUDIO_PARAM
#define V AV_OPT_FLAG_VIDEO_PARAM
#define F AV_OPT_FLAG_FILTERING_PARAM
static const AVOption ebur128_options[] = {
{ "video", "set video output", OFFSET(do_video), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, V|F },
{ "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0, V|F },
{ "meter", "set scale meter (+9 to +18)", OFFSET(meter), AV_OPT_TYPE_INT, {.i64 = 9}, 9, 18, V|F },
{ "framelog", "force frame logging level", OFFSET(loglevel), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, A|V|F, "level" },
{ "info", "information logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_INFO}, INT_MIN, INT_MAX, A|V|F, "level" },
{ "verbose", "verbose logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_VERBOSE}, INT_MIN, INT_MAX, A|V|F, "level" },
{ "metadata", "inject metadata in the filtergraph", OFFSET(metadata), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, A|V|F },
{ NULL }
};
AVFILTER_DEFINE_CLASS(ebur128);
static const uint8_t graph_colors[] = {
0xdd, 0x66, 0x66, // value above 0LU non reached
0x66, 0x66, 0xdd, // value below 0LU non reached
0x96, 0x33, 0x33, // value above 0LU reached
0x33, 0x33, 0x96, // value below 0LU reached
0xdd, 0x96, 0x96, // value above 0LU line non reached
0x96, 0x96, 0xdd, // value below 0LU line non reached
0xdd, 0x33, 0x33, // value above 0LU line reached
0x33, 0x33, 0xdd, // value below 0LU line reached
};
static const uint8_t *get_graph_color(const EBUR128Context *ebur128, int v, int y)
{
const int below0 = y > ebur128->y_zero_lu;
const int reached = y >= v;
const int line = ebur128->y_line_ref[y] || y == ebur128->y_zero_lu;
const int colorid = 4*line + 2*reached + below0;
return graph_colors + 3*colorid;
}
static inline int lu_to_y(const EBUR128Context *ebur128, double v)
{
v += 2 * ebur128->meter; // make it in range [0;...]
v = av_clipf(v, 0, ebur128->scale_range); // make sure it's in the graph scale
v = ebur128->scale_range - v; // invert value (y=0 is on top)
return v * ebur128->graph.h / ebur128->scale_range; // rescale from scale range to px height
}
#define FONT8 0
#define FONT16 1
static const uint8_t font_colors[] = {
0xdd, 0xdd, 0x00,
0x00, 0x96, 0x96,
};
static void drawtext(AVFrame *pic, int x, int y, int ftid, const uint8_t *color, const char *fmt, ...)
{
int i;
char buf[128] = {0};
const uint8_t *font;
int font_height;
va_list vl;
if (ftid == FONT16) font = avpriv_vga16_font, font_height = 16;
else if (ftid == FONT8) font = avpriv_cga_font, font_height = 8;
else return;
va_start(vl, fmt);
vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
for (i = 0; buf[i]; i++) {
int char_y, mask;
uint8_t *p = pic->data[0] + y*pic->linesize[0] + (x + i*8)*3;
for (char_y = 0; char_y < font_height; char_y++) {
for (mask = 0x80; mask; mask >>= 1) {
if (font[buf[i] * font_height + char_y] & mask)
memcpy(p, color, 3);
else
memcpy(p, "\x00\x00\x00", 3);
p += 3;
}
p += pic->linesize[0] - 8*3;
}
}
}
static void drawline(AVFrame *pic, int x, int y, int len, int step)
{
int i;
uint8_t *p = pic->data[0] + y*pic->linesize[0] + x*3;
for (i = 0; i < len; i++) {
memcpy(p, "\x00\xff\x00", 3);
p += step;
}
}
static int config_video_output(AVFilterLink *outlink)
{
int i, x, y;
uint8_t *p;
AVFilterContext *ctx = outlink->src;
EBUR128Context *ebur128 = ctx->priv;
AVFrame *outpicref;
/* check if there is enough space to represent everything decently */
if (ebur128->w < 640 || ebur128->h < 480) {
av_log(ctx, AV_LOG_ERROR, "Video size %dx%d is too small, "
"minimum size is 640x480\n", ebur128->w, ebur128->h);
return AVERROR(EINVAL);
}
outlink->w = ebur128->w;
outlink->h = ebur128->h;
#define PAD 8
/* configure text area position and size */
ebur128->text.x = PAD;
ebur128->text.y = 40;
ebur128->text.w = 3 * 8; // 3 characters
ebur128->text.h = ebur128->h - PAD - ebur128->text.y;
/* configure gauge position and size */
ebur128->gauge.w = 20;
ebur128->gauge.h = ebur128->text.h;
ebur128->gauge.x = ebur128->w - PAD - ebur128->gauge.w;
ebur128->gauge.y = ebur128->text.y;
/* configure graph position and size */
ebur128->graph.x = ebur128->text.x + ebur128->text.w + PAD;
ebur128->graph.y = ebur128->gauge.y;
ebur128->graph.w = ebur128->gauge.x - ebur128->graph.x - PAD;
ebur128->graph.h = ebur128->gauge.h;
/* graph and gauge share the LU-to-pixel code */
av_assert0(ebur128->graph.h == ebur128->gauge.h);
/* prepare the initial picref buffer */
av_frame_free(&ebur128->outpicref);
ebur128->outpicref = outpicref =
ff_get_video_buffer(outlink, outlink->w, outlink->h);
if (!outpicref)
return AVERROR(ENOMEM);
outlink->sample_aspect_ratio = (AVRational){1,1};
/* init y references values (to draw LU lines) */
ebur128->y_line_ref = av_calloc(ebur128->graph.h + 1, sizeof(*ebur128->y_line_ref));
if (!ebur128->y_line_ref)
return AVERROR(ENOMEM);
/* black background */
memset(outpicref->data[0], 0, ebur128->h * outpicref->linesize[0]);
/* draw LU legends */
drawtext(outpicref, PAD, PAD+16, FONT8, font_colors+3, " LU");
for (i = ebur128->meter; i >= -ebur128->meter * 2; i--) {
y = lu_to_y(ebur128, i);
x = PAD + (i < 10 && i > -10) * 8;
ebur128->y_line_ref[y] = i;
y -= 4; // -4 to center vertically
drawtext(outpicref, x, y + ebur128->graph.y, FONT8, font_colors+3,
"%c%d", i < 0 ? '-' : i > 0 ? '+' : ' ', FFABS(i));
}
/* draw graph */
ebur128->y_zero_lu = lu_to_y(ebur128, 0);
p = outpicref->data[0] + ebur128->graph.y * outpicref->linesize[0]
+ ebur128->graph.x * 3;
for (y = 0; y < ebur128->graph.h; y++) {
const uint8_t *c = get_graph_color(ebur128, INT_MAX, y);
for (x = 0; x < ebur128->graph.w; x++)
memcpy(p + x*3, c, 3);
p += outpicref->linesize[0];
}
/* draw fancy rectangles around the graph and the gauge */
#define DRAW_RECT(r) do { \
drawline(outpicref, r.x, r.y - 1, r.w, 3); \
drawline(outpicref, r.x, r.y + r.h, r.w, 3); \
drawline(outpicref, r.x - 1, r.y, r.h, outpicref->linesize[0]); \
drawline(outpicref, r.x + r.w, r.y, r.h, outpicref->linesize[0]); \
} while (0)
DRAW_RECT(ebur128->graph);
DRAW_RECT(ebur128->gauge);
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
return 0;
}
static int config_audio_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
EBUR128Context *ebur128 = ctx->priv;
/* force 100ms framing in case of metadata injection: the frames must have
* a granularity of the window overlap to be accurately exploited */
if (ebur128->metadata)
inlink->min_samples =
inlink->max_samples =
inlink->partial_buf_size = inlink->sample_rate / 10;
return 0;
}
static int config_audio_output(AVFilterLink *outlink)
{
int i;
int idx_bitposn = 0;
AVFilterContext *ctx = outlink->src;
EBUR128Context *ebur128 = ctx->priv;
const int nb_channels = av_get_channel_layout_nb_channels(outlink->channel_layout);
#define BACK_MASK (AV_CH_BACK_LEFT |AV_CH_BACK_CENTER |AV_CH_BACK_RIGHT| \
AV_CH_TOP_BACK_LEFT|AV_CH_TOP_BACK_CENTER|AV_CH_TOP_BACK_RIGHT| \
AV_CH_SIDE_LEFT |AV_CH_SIDE_RIGHT| \
AV_CH_SURROUND_DIRECT_LEFT |AV_CH_SURROUND_DIRECT_RIGHT)
ebur128->nb_channels = nb_channels;
ebur128->ch_weighting = av_calloc(nb_channels, sizeof(*ebur128->ch_weighting));
if (!ebur128->ch_weighting)
return AVERROR(ENOMEM);
for (i = 0; i < nb_channels; i++) {
/* find the next bit that is set starting from the right */
while ((outlink->channel_layout & 1ULL<<idx_bitposn) == 0 && idx_bitposn < 63)
idx_bitposn++;
/* channel weighting */
if ((1ULL<<idx_bitposn & AV_CH_LOW_FREQUENCY) ||
(1ULL<<idx_bitposn & AV_CH_LOW_FREQUENCY_2)) {
ebur128->ch_weighting[i] = 0;
} else if (1ULL<<idx_bitposn & BACK_MASK) {
ebur128->ch_weighting[i] = 1.41;
} else {
ebur128->ch_weighting[i] = 1.0;
}
idx_bitposn++;
if (!ebur128->ch_weighting[i])
continue;
/* bins buffer for the two integration window (400ms and 3s) */
ebur128->i400.cache[i] = av_calloc(I400_BINS, sizeof(*ebur128->i400.cache[0]));
ebur128->i3000.cache[i] = av_calloc(I3000_BINS, sizeof(*ebur128->i3000.cache[0]));
if (!ebur128->i400.cache[i] || !ebur128->i3000.cache[i])
return AVERROR(ENOMEM);
}
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
return 0;
}
#define ENERGY(loudness) (pow(10, ((loudness) + 0.691) / 10.))
#define LOUDNESS(energy) (-0.691 + 10 * log10(energy))
static struct hist_entry *get_histogram(void)
{
int i;
struct hist_entry *h = av_calloc(HIST_SIZE, sizeof(*h));
if (!h)
return NULL;
for (i = 0; i < HIST_SIZE; i++) {
h[i].loudness = i / (double)HIST_GRAIN + ABS_THRES;
h[i].energy = ENERGY(h[i].loudness);
}
return h;
}
static av_cold int init(AVFilterContext *ctx)
{
EBUR128Context *ebur128 = ctx->priv;
AVFilterPad pad;
if (ebur128->loglevel != AV_LOG_INFO &&
ebur128->loglevel != AV_LOG_VERBOSE) {
if (ebur128->do_video || ebur128->metadata)
ebur128->loglevel = AV_LOG_VERBOSE;
else
ebur128->loglevel = AV_LOG_INFO;
}
// if meter is +9 scale, scale range is from -18 LU to +9 LU (or 3*9)
// if meter is +18 scale, scale range is from -36 LU to +18 LU (or 3*18)
ebur128->scale_range = 3 * ebur128->meter;
ebur128->i400.histogram = get_histogram();
ebur128->i3000.histogram = get_histogram();
if (!ebur128->i400.histogram || !ebur128->i3000.histogram)
return AVERROR(ENOMEM);
ebur128->integrated_loudness = ABS_THRES;
ebur128->loudness_range = 0;
/* insert output pads */
if (ebur128->do_video) {
pad = (AVFilterPad){
.name = av_strdup("out0"),
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_video_output,
};
if (!pad.name)
return AVERROR(ENOMEM);
ff_insert_outpad(ctx, 0, &pad);
}
pad = (AVFilterPad){
.name = av_asprintf("out%d", ebur128->do_video),
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_audio_output,
};
if (!pad.name)
return AVERROR(ENOMEM);
ff_insert_outpad(ctx, ebur128->do_video, &pad);
/* summary */
av_log(ctx, AV_LOG_VERBOSE, "EBU +%d scale\n", ebur128->meter);
return 0;
}
#define HIST_POS(power) (int)(((power) - ABS_THRES) * HIST_GRAIN)
/* loudness and power should be set such as loudness = -0.691 +
* 10*log10(power), we just avoid doing that calculus two times */
static int gate_update(struct integrator *integ, double power,
double loudness, int gate_thres)
{
int ipower;
double relative_threshold;
int gate_hist_pos;
/* update powers histograms by incrementing current power count */
ipower = av_clip(HIST_POS(loudness), 0, HIST_SIZE - 1);
integ->histogram[ipower].count++;
/* compute relative threshold and get its position in the histogram */
integ->sum_kept_powers += power;
integ->nb_kept_powers++;
relative_threshold = integ->sum_kept_powers / integ->nb_kept_powers;
if (!relative_threshold)
relative_threshold = 1e-12;
integ->rel_threshold = LOUDNESS(relative_threshold) + gate_thres;
gate_hist_pos = av_clip(HIST_POS(integ->rel_threshold), 0, HIST_SIZE - 1);
return gate_hist_pos;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *insamples)
{
int i, ch, idx_insample;
AVFilterContext *ctx = inlink->dst;
EBUR128Context *ebur128 = ctx->priv;
const int nb_channels = ebur128->nb_channels;
const int nb_samples = insamples->nb_samples;
const double *samples = (double *)insamples->data[0];
AVFrame *pic = ebur128->outpicref;
for (idx_insample = 0; idx_insample < nb_samples; idx_insample++) {
const int bin_id_400 = ebur128->i400.cache_pos;
const int bin_id_3000 = ebur128->i3000.cache_pos;
#define MOVE_TO_NEXT_CACHED_ENTRY(time) do { \
ebur128->i##time.cache_pos++; \
if (ebur128->i##time.cache_pos == I##time##_BINS) { \
ebur128->i##time.filled = 1; \
ebur128->i##time.cache_pos = 0; \
} \
} while (0)
MOVE_TO_NEXT_CACHED_ENTRY(400);
MOVE_TO_NEXT_CACHED_ENTRY(3000);
for (ch = 0; ch < nb_channels; ch++) {
double bin;
ebur128->x[ch * 3] = *samples++; // set X[i]
if (!ebur128->ch_weighting[ch])
continue;
/* Y[i] = X[i]*b0 + X[i-1]*b1 + X[i-2]*b2 - Y[i-1]*a1 - Y[i-2]*a2 */
#define FILTER(Y, X, name) do { \
double *dst = ebur128->Y + ch*3; \
double *src = ebur128->X + ch*3; \
dst[2] = dst[1]; \
dst[1] = dst[0]; \
dst[0] = src[0]*name##_B0 + src[1]*name##_B1 + src[2]*name##_B2 \
- dst[1]*name##_A1 - dst[2]*name##_A2; \
} while (0)
// TODO: merge both filters in one?
FILTER(y, x, PRE); // apply pre-filter
ebur128->x[ch * 3 + 2] = ebur128->x[ch * 3 + 1];
ebur128->x[ch * 3 + 1] = ebur128->x[ch * 3 ];
FILTER(z, y, RLB); // apply RLB-filter
bin = ebur128->z[ch * 3] * ebur128->z[ch * 3];
/* add the new value, and limit the sum to the cache size (400ms or 3s)
* by removing the oldest one */
ebur128->i400.sum [ch] = ebur128->i400.sum [ch] + bin - ebur128->i400.cache [ch][bin_id_400];
ebur128->i3000.sum[ch] = ebur128->i3000.sum[ch] + bin - ebur128->i3000.cache[ch][bin_id_3000];
/* override old cache entry with the new value */
ebur128->i400.cache [ch][bin_id_400 ] = bin;
ebur128->i3000.cache[ch][bin_id_3000] = bin;
}
/* For integrated loudness, gating blocks are 400ms long with 75%
* overlap (see BS.1770-2 p5), so a re-computation is needed each 100ms
* (4800 samples at 48kHz). */
if (++ebur128->sample_count == 4800) {
double loudness_400, loudness_3000;
double power_400 = 1e-12, power_3000 = 1e-12;
AVFilterLink *outlink = ctx->outputs[0];
const int64_t pts = insamples->pts +
av_rescale_q(idx_insample, (AVRational){ 1, inlink->sample_rate },
outlink->time_base);
ebur128->sample_count = 0;
#define COMPUTE_LOUDNESS(m, time) do { \
if (ebur128->i##time.filled) { \
/* weighting sum of the last <time> ms */ \
for (ch = 0; ch < nb_channels; ch++) \
power_##time += ebur128->ch_weighting[ch] * ebur128->i##time.sum[ch]; \
power_##time /= I##time##_BINS; \
} \
loudness_##time = LOUDNESS(power_##time); \
} while (0)
COMPUTE_LOUDNESS(M, 400);
COMPUTE_LOUDNESS(S, 3000);
/* Integrated loudness */
#define I_GATE_THRES -10 // initially defined to -8 LU in the first EBU standard
if (loudness_400 >= ABS_THRES) {
double integrated_sum = 0;
int nb_integrated = 0;
int gate_hist_pos = gate_update(&ebur128->i400, power_400,
loudness_400, I_GATE_THRES);
/* compute integrated loudness by summing the histogram values
* above the relative threshold */
for (i = gate_hist_pos; i < HIST_SIZE; i++) {
const int nb_v = ebur128->i400.histogram[i].count;
nb_integrated += nb_v;
integrated_sum += nb_v * ebur128->i400.histogram[i].energy;
}
if (nb_integrated)
ebur128->integrated_loudness = LOUDNESS(integrated_sum / nb_integrated);
}
/* LRA */
#define LRA_GATE_THRES -20
#define LRA_LOWER_PRC 10
#define LRA_HIGHER_PRC 95
/* XXX: example code in EBU 3342 is ">=" but formula in BS.1770
* specs is ">" */
if (loudness_3000 >= ABS_THRES) {
int nb_powers = 0;
int gate_hist_pos = gate_update(&ebur128->i3000, power_3000,
loudness_3000, LRA_GATE_THRES);
for (i = gate_hist_pos; i < HIST_SIZE; i++)
nb_powers += ebur128->i3000.histogram[i].count;
if (nb_powers) {
int n, nb_pow;
/* get lower loudness to consider */
n = 0;
nb_pow = LRA_LOWER_PRC * nb_powers / 100. + 0.5;
for (i = gate_hist_pos; i < HIST_SIZE; i++) {
n += ebur128->i3000.histogram[i].count;
if (n >= nb_pow) {
ebur128->lra_low = ebur128->i3000.histogram[i].loudness;
break;
}
}
/* get higher loudness to consider */
n = nb_powers;
nb_pow = LRA_HIGHER_PRC * nb_powers / 100. + 0.5;
for (i = HIST_SIZE - 1; i >= 0; i--) {
n -= ebur128->i3000.histogram[i].count;
if (n < nb_pow) {
ebur128->lra_high = ebur128->i3000.histogram[i].loudness;
break;
}
}
// XXX: show low & high on the graph?
ebur128->loudness_range = ebur128->lra_high - ebur128->lra_low;
}
}
#define LOG_FMT "M:%6.1f S:%6.1f I:%6.1f LUFS LRA:%6.1f LU"
/* push one video frame */
if (ebur128->do_video) {
int x, y, ret;
uint8_t *p;
const int y_loudness_lu_graph = lu_to_y(ebur128, loudness_3000 + 23);
const int y_loudness_lu_gauge = lu_to_y(ebur128, loudness_400 + 23);
/* draw the graph using the short-term loudness */
p = pic->data[0] + ebur128->graph.y*pic->linesize[0] + ebur128->graph.x*3;
for (y = 0; y < ebur128->graph.h; y++) {
const uint8_t *c = get_graph_color(ebur128, y_loudness_lu_graph, y);
memmove(p, p + 3, (ebur128->graph.w - 1) * 3);
memcpy(p + (ebur128->graph.w - 1) * 3, c, 3);
p += pic->linesize[0];
}
/* draw the gauge using the momentary loudness */
p = pic->data[0] + ebur128->gauge.y*pic->linesize[0] + ebur128->gauge.x*3;
for (y = 0; y < ebur128->gauge.h; y++) {
const uint8_t *c = get_graph_color(ebur128, y_loudness_lu_gauge, y);
for (x = 0; x < ebur128->gauge.w; x++)
memcpy(p + x*3, c, 3);
p += pic->linesize[0];
}
/* draw textual info */
drawtext(pic, PAD, PAD - PAD/2, FONT16, font_colors,
LOG_FMT " ", // padding to erase trailing characters
loudness_400, loudness_3000,
ebur128->integrated_loudness, ebur128->loudness_range);
/* set pts and push frame */
pic->pts = pts;
ret = ff_filter_frame(outlink, av_frame_clone(pic));
if (ret < 0)
return ret;
}
if (ebur128->metadata) { /* happens only once per filter_frame call */
char metabuf[128];
#define SET_META(name, var) do { \
snprintf(metabuf, sizeof(metabuf), "%.3f", var); \
av_dict_set(&insamples->metadata, "lavfi.r128." name, metabuf, 0); \
} while (0)
SET_META("M", loudness_400);
SET_META("S", loudness_3000);
SET_META("I", ebur128->integrated_loudness);
SET_META("LRA", ebur128->loudness_range);
SET_META("LRA.low", ebur128->lra_low);
SET_META("LRA.high", ebur128->lra_high);
}
av_log(ctx, ebur128->loglevel, "t: %-10s " LOG_FMT "\n",
av_ts2timestr(pts, &outlink->time_base),
loudness_400, loudness_3000,
ebur128->integrated_loudness, ebur128->loudness_range);
}
}
return ff_filter_frame(ctx->outputs[ebur128->do_video], insamples);
}
static int query_formats(AVFilterContext *ctx)
{
EBUR128Context *ebur128 = ctx->priv;
AVFilterFormats *formats;
AVFilterChannelLayouts *layouts;
AVFilterLink *inlink = ctx->inputs[0];
AVFilterLink *outlink = ctx->outputs[0];
static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_NONE };
static const int input_srate[] = {48000, -1}; // ITU-R BS.1770 provides coeff only for 48kHz
static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE };
/* set optional output video format */
if (ebur128->do_video) {
formats = ff_make_format_list(pix_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &outlink->in_formats);
outlink = ctx->outputs[1];
}
/* set input and output audio formats
* Note: ff_set_common_* functions are not used because they affect all the
* links, and thus break the video format negotiation */
formats = ff_make_format_list(sample_fmts);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_formats);
ff_formats_ref(formats, &outlink->in_formats);
layouts = ff_all_channel_layouts();
if (!layouts)
return AVERROR(ENOMEM);
ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts);
ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts);
formats = ff_make_format_list(input_srate);
if (!formats)
return AVERROR(ENOMEM);
ff_formats_ref(formats, &inlink->out_samplerates);
ff_formats_ref(formats, &outlink->in_samplerates);
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
int i;
EBUR128Context *ebur128 = ctx->priv;
av_log(ctx, AV_LOG_INFO, "Summary:\n\n"
" Integrated loudness:\n"
" I: %5.1f LUFS\n"
" Threshold: %5.1f LUFS\n\n"
" Loudness range:\n"
" LRA: %5.1f LU\n"
" Threshold: %5.1f LUFS\n"
" LRA low: %5.1f LUFS\n"
" LRA high: %5.1f LUFS\n",
ebur128->integrated_loudness, ebur128->i400.rel_threshold,
ebur128->loudness_range, ebur128->i3000.rel_threshold,
ebur128->lra_low, ebur128->lra_high);
av_freep(&ebur128->y_line_ref);
av_freep(&ebur128->ch_weighting);
av_freep(&ebur128->i400.histogram);
av_freep(&ebur128->i3000.histogram);
for (i = 0; i < ebur128->nb_channels; i++) {
av_freep(&ebur128->i400.cache[i]);
av_freep(&ebur128->i3000.cache[i]);
}
for (i = 0; i < ctx->nb_outputs; i++)
av_freep(&ctx->output_pads[i].name);
av_frame_free(&ebur128->outpicref);
}
static const AVFilterPad ebur128_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
.config_props = config_audio_input,
},
{ NULL }
};
AVFilter avfilter_af_ebur128 = {
.name = "ebur128",
.description = NULL_IF_CONFIG_SMALL("EBU R128 scanner."),
.priv_size = sizeof(EBUR128Context),
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = ebur128_inputs,
.outputs = NULL,
.priv_class = &ebur128_class,
.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
};
+259
View File
@@ -0,0 +1,259 @@
/*
* Copyright (c) 2013 Stefano Sabatini
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* audio and video interleaver
*/
#include "libavutil/avassert.h"
#include "libavutil/avstring.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "bufferqueue.h"
#include "formats.h"
#include "internal.h"
#include "audio.h"
#include "video.h"
typedef struct {
const AVClass *class;
int nb_inputs;
struct FFBufQueue *queues;
} InterleaveContext;
#define OFFSET(x) offsetof(InterleaveContext, x)
#define DEFINE_OPTIONS(filt_name, flags_) \
static const AVOption filt_name##_options[] = { \
{ "nb_inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 2}, 1, INT_MAX, .flags = flags_ }, \
{ "n", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 2}, 1, INT_MAX, .flags = flags_ }, \
{ NULL } \
}
inline static int push_frame(AVFilterContext *ctx)
{
InterleaveContext *s = ctx->priv;
AVFrame *frame;
int i, queue_idx = -1;
int64_t pts_min = INT64_MAX;
/* look for oldest frame */
for (i = 0; i < ctx->nb_inputs; i++) {
struct FFBufQueue *q = &s->queues[i];
if (!q->available && !ctx->inputs[i]->closed)
return 0;
if (q->available) {
frame = ff_bufqueue_peek(q, 0);
if (frame->pts < pts_min) {
pts_min = frame->pts;
queue_idx = i;
}
}
}
/* all inputs are closed */
if (queue_idx < 0)
return AVERROR_EOF;
frame = ff_bufqueue_get(&s->queues[queue_idx]);
av_log(ctx, AV_LOG_DEBUG, "queue:%d -> frame time:%f\n",
queue_idx, frame->pts * av_q2d(AV_TIME_BASE_Q));
return ff_filter_frame(ctx->outputs[0], frame);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
InterleaveContext *s = ctx->priv;
unsigned in_no = FF_INLINK_IDX(inlink);
if (frame->pts == AV_NOPTS_VALUE) {
av_log(ctx, AV_LOG_WARNING,
"NOPTS value for input frame cannot be accepted, frame discarded\n");
av_frame_free(&frame);
return AVERROR_INVALIDDATA;
}
/* queue frame */
frame->pts = av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q);
av_log(ctx, AV_LOG_DEBUG, "frame pts:%f -> queue idx:%d available:%d\n",
frame->pts * av_q2d(AV_TIME_BASE_Q), in_no, s->queues[in_no].available);
ff_bufqueue_add(ctx, &s->queues[in_no], frame);
return push_frame(ctx);
}
static av_cold int init(AVFilterContext *ctx)
{
InterleaveContext *s = ctx->priv;
const AVFilterPad *outpad = &ctx->filter->outputs[0];
int i;
s->queues = av_calloc(s->nb_inputs, sizeof(s->queues[0]));
if (!s->queues)
return AVERROR(ENOMEM);
for (i = 0; i < s->nb_inputs; i++) {
AVFilterPad inpad = { 0 };
inpad.name = av_asprintf("input%d", i);
if (!inpad.name)
return AVERROR(ENOMEM);
inpad.type = outpad->type;
inpad.filter_frame = filter_frame;
switch (outpad->type) {
case AVMEDIA_TYPE_VIDEO:
inpad.get_video_buffer = ff_null_get_video_buffer; break;
case AVMEDIA_TYPE_AUDIO:
inpad.get_audio_buffer = ff_null_get_audio_buffer; break;
default:
av_assert0(0);
}
ff_insert_inpad(ctx, i, &inpad);
}
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
InterleaveContext *s = ctx->priv;
int i;
for (i = 0; i < ctx->nb_inputs; i++) {
ff_bufqueue_discard_all(&s->queues[i]);
av_freep(&s->queues[i]);
av_freep(&ctx->input_pads[i].name);
}
}
static int config_output(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
AVFilterLink *inlink0 = ctx->inputs[0];
int i;
if (outlink->type == AVMEDIA_TYPE_VIDEO) {
outlink->time_base = AV_TIME_BASE_Q;
outlink->w = inlink0->w;
outlink->h = inlink0->h;
outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
outlink->format = inlink0->format;
outlink->frame_rate = (AVRational) {1, 0};
for (i = 1; i < ctx->nb_inputs; i++) {
AVFilterLink *inlink = ctx->inputs[i];
if (outlink->w != inlink->w ||
outlink->h != inlink->h ||
outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num ||
outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) {
av_log(ctx, AV_LOG_ERROR, "Parameters for input link %s "
"(size %dx%d, SAR %d:%d) do not match the corresponding "
"output link parameters (%dx%d, SAR %d:%d)\n",
ctx->input_pads[i].name, inlink->w, inlink->h,
inlink->sample_aspect_ratio.num,
inlink->sample_aspect_ratio.den,
outlink->w, outlink->h,
outlink->sample_aspect_ratio.num,
outlink->sample_aspect_ratio.den);
return AVERROR(EINVAL);
}
}
}
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
InterleaveContext *s = ctx->priv;
int i, ret;
for (i = 0; i < ctx->nb_inputs; i++) {
if (!s->queues[i].available && !ctx->inputs[i]->closed) {
ret = ff_request_frame(ctx->inputs[i]);
if (ret != AVERROR_EOF)
return ret;
}
}
return push_frame(ctx);
}
#if CONFIG_INTERLEAVE_FILTER
DEFINE_OPTIONS(interleave, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM);
AVFILTER_DEFINE_CLASS(interleave);
static const AVFilterPad interleave_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_output,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_vf_interleave = {
.name = "interleave",
.description = NULL_IF_CONFIG_SMALL("Temporally interleave video inputs."),
.priv_size = sizeof(InterleaveContext),
.init = init,
.uninit = uninit,
.outputs = interleave_outputs,
.priv_class = &interleave_class,
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
};
#endif
#if CONFIG_AINTERLEAVE_FILTER
DEFINE_OPTIONS(ainterleave, AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM);
AVFILTER_DEFINE_CLASS(ainterleave);
static const AVFilterPad ainterleave_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_af_ainterleave = {
.name = "ainterleave",
.description = NULL_IF_CONFIG_SMALL("Temporally interleave audio inputs."),
.priv_size = sizeof(InterleaveContext),
.init = init,
.uninit = uninit,
.outputs = ainterleave_outputs,
.priv_class = &ainterleave_class,
.flags = AVFILTER_FLAG_DYNAMIC_INPUTS,
};
#endif
+178
View File
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2013 Clément Bœsch
*
* 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/lfg.h"
#include "libavutil/opt.h"
#include "libavutil/random_seed.h"
#include "audio.h"
#include "video.h"
enum mode {
MODE_NONE,
MODE_RO,
MODE_RW,
MODE_TOGGLE,
MODE_RANDOM,
NB_MODES
};
typedef struct {
const AVClass *class;
AVLFG lfg;
int64_t random_seed;
enum mode mode;
} PermsContext;
#define OFFSET(x) offsetof(PermsContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM
static const AVOption options[] = {
{ "mode", "select permissions mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_NONE}, MODE_NONE, NB_MODES-1, FLAGS, "mode" },
{ "none", "do nothing", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_NONE}, INT_MIN, INT_MAX, FLAGS, "mode" },
{ "ro", "set all output frames read-only", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RO}, INT_MIN, INT_MAX, FLAGS, "mode" },
{ "rw", "set all output frames writable", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RW}, INT_MIN, INT_MAX, FLAGS, "mode" },
{ "toggle", "switch permissions", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_TOGGLE}, INT_MIN, INT_MAX, FLAGS, "mode" },
{ "random", "set permissions randomly", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RANDOM}, INT_MIN, INT_MAX, FLAGS, "mode" },
{ "seed", "set the seed for the random mode", OFFSET(random_seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT32_MAX, FLAGS },
{ NULL }
};
static av_cold int init(AVFilterContext *ctx)
{
PermsContext *perms = ctx->priv;
if (perms->mode == MODE_RANDOM) {
uint32_t seed;
if (perms->random_seed == -1)
perms->random_seed = av_get_random_seed();
seed = perms->random_seed;
av_log(ctx, AV_LOG_INFO, "random seed: 0x%08x\n", seed);
av_lfg_init(&perms->lfg, seed);
}
return 0;
}
enum perm { RO, RW };
static const char *perm_str[2] = { "RO", "RW" };
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
int ret;
AVFilterContext *ctx = inlink->dst;
PermsContext *perms = ctx->priv;
AVFrame *out = frame;
enum perm in_perm = av_frame_is_writable(frame) ? RW : RO;
enum perm out_perm;
switch (perms->mode) {
case MODE_TOGGLE: out_perm = in_perm == RO ? RW : RO; break;
case MODE_RANDOM: out_perm = av_lfg_get(&perms->lfg) & 1 ? RW : RO; break;
case MODE_RO: out_perm = RO; break;
case MODE_RW: out_perm = RW; break;
default: out_perm = in_perm; break;
}
av_log(ctx, AV_LOG_VERBOSE, "%s -> %s%s\n",
perm_str[in_perm], perm_str[out_perm],
in_perm == out_perm ? " (no-op)" : "");
if (in_perm == RO && out_perm == RW) {
if ((ret = av_frame_make_writable(frame)) < 0)
return ret;
} else if (in_perm == RW && out_perm == RO) {
out = av_frame_clone(frame);
if (!out)
return AVERROR(ENOMEM);
}
ret = ff_filter_frame(ctx->outputs[0], out);
if (in_perm == RW && out_perm == RO)
av_frame_free(&frame);
return ret;
}
#if CONFIG_APERMS_FILTER
#define aperms_options options
AVFILTER_DEFINE_CLASS(aperms);
static const AVFilterPad aperms_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad aperms_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_aperms = {
.name = "aperms",
.description = NULL_IF_CONFIG_SMALL("Set permissions for the output audio frame."),
.init = init,
.priv_size = sizeof(PermsContext),
.inputs = aperms_inputs,
.outputs = aperms_outputs,
.priv_class = &aperms_class,
};
#endif /* CONFIG_APERMS_FILTER */
#if CONFIG_PERMS_FILTER
#define perms_options options
AVFILTER_DEFINE_CLASS(perms);
static const AVFilterPad perms_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad perms_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
},
{ NULL }
};
AVFilter avfilter_vf_perms = {
.name = "perms",
.description = NULL_IF_CONFIG_SMALL("Set permissions for the output video frame."),
.init = init,
.priv_size = sizeof(PermsContext),
.inputs = perms_inputs,
.outputs = perms_outputs,
.priv_class = &perms_class,
};
#endif /* CONFIG_PERMS_FILTER */
+533
View File
@@ -0,0 +1,533 @@
/*
* Copyright (c) 2011 Stefano Sabatini
*
* 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
* filter for selecting which frame passes in the filterchain
*/
#include "libavutil/avstring.h"
#include "libavutil/eval.h"
#include "libavutil/fifo.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "audio.h"
#include "formats.h"
#include "internal.h"
#include "video.h"
#if CONFIG_AVCODEC
#include "libavcodec/dsputil.h"
#endif
static const char *const var_names[] = {
"TB", ///< timebase
"pts", ///< original pts in the file of the frame
"start_pts", ///< first PTS in the stream, expressed in TB units
"prev_pts", ///< previous frame PTS
"prev_selected_pts", ///< previous selected frame PTS
"t", ///< first PTS in seconds
"start_t", ///< first PTS in the stream, expressed in seconds
"prev_t", ///< previous frame time
"prev_selected_t", ///< previously selected time
"pict_type", ///< the type of picture in the movie
"I",
"P",
"B",
"S",
"SI",
"SP",
"BI",
"PICT_TYPE_I",
"PICT_TYPE_P",
"PICT_TYPE_B",
"PICT_TYPE_S",
"PICT_TYPE_SI",
"PICT_TYPE_SP",
"PICT_TYPE_BI",
"interlace_type", ///< the frame interlace type
"PROGRESSIVE",
"TOPFIRST",
"BOTTOMFIRST",
"consumed_samples_n",///< number of samples consumed by the filter (only audio)
"samples_n", ///< number of samples in the current frame (only audio)
"sample_rate", ///< sample rate (only audio)
"n", ///< frame number (starting from zero)
"selected_n", ///< selected frame number (starting from zero)
"prev_selected_n", ///< number of the last selected frame
"key", ///< tell if the frame is a key frame
"pos", ///< original position in the file of the frame
"scene",
NULL
};
enum var_name {
VAR_TB,
VAR_PTS,
VAR_START_PTS,
VAR_PREV_PTS,
VAR_PREV_SELECTED_PTS,
VAR_T,
VAR_START_T,
VAR_PREV_T,
VAR_PREV_SELECTED_T,
VAR_PICT_TYPE,
VAR_I,
VAR_P,
VAR_B,
VAR_S,
VAR_SI,
VAR_SP,
VAR_BI,
VAR_PICT_TYPE_I,
VAR_PICT_TYPE_P,
VAR_PICT_TYPE_B,
VAR_PICT_TYPE_S,
VAR_PICT_TYPE_SI,
VAR_PICT_TYPE_SP,
VAR_PICT_TYPE_BI,
VAR_INTERLACE_TYPE,
VAR_INTERLACE_TYPE_P,
VAR_INTERLACE_TYPE_T,
VAR_INTERLACE_TYPE_B,
VAR_CONSUMED_SAMPLES_N,
VAR_SAMPLES_N,
VAR_SAMPLE_RATE,
VAR_N,
VAR_SELECTED_N,
VAR_PREV_SELECTED_N,
VAR_KEY,
VAR_POS,
VAR_SCENE,
VAR_VARS_NB
};
typedef struct {
const AVClass *class;
char *expr_str;
AVExpr *expr;
double var_values[VAR_VARS_NB];
int do_scene_detect; ///< 1 if the expression requires scene detection variables, 0 otherwise
#if CONFIG_AVCODEC
AVCodecContext *avctx; ///< codec context required for the DSPContext (scene detect only)
DSPContext c; ///< context providing optimized SAD methods (scene detect only)
double prev_mafd; ///< previous MAFD (scene detect only)
#endif
AVFrame *prev_picref; ///< previous frame (scene detect only)
double select;
int select_out; ///< mark the selected output pad index
int nb_outputs;
} SelectContext;
#define OFFSET(x) offsetof(SelectContext, x)
#define DEFINE_OPTIONS(filt_name, FLAGS) \
static const AVOption filt_name##_options[] = { \
{ "expr", "set an expression to use for selecting frames", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "1" }, .flags=FLAGS }, \
{ "e", "set an expression to use for selecting frames", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "1" }, .flags=FLAGS }, \
{ "outputs", "set the number of outputs", OFFSET(nb_outputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, .flags=FLAGS }, \
{ "n", "set the number of outputs", OFFSET(nb_outputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, .flags=FLAGS }, \
{ NULL } \
}
static int request_frame(AVFilterLink *outlink);
static av_cold int init(AVFilterContext *ctx)
{
SelectContext *select = ctx->priv;
int i, ret;
if ((ret = av_expr_parse(&select->expr, select->expr_str,
var_names, NULL, NULL, NULL, NULL, 0, ctx)) < 0) {
av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n",
select->expr_str);
return ret;
}
select->do_scene_detect = !!strstr(select->expr_str, "scene");
for (i = 0; i < select->nb_outputs; i++) {
AVFilterPad pad = { 0 };
pad.name = av_asprintf("output%d", i);
if (!pad.name)
return AVERROR(ENOMEM);
pad.type = ctx->filter->inputs[0].type;
pad.request_frame = request_frame;
ff_insert_outpad(ctx, i, &pad);
}
return 0;
}
#define INTERLACE_TYPE_P 0
#define INTERLACE_TYPE_T 1
#define INTERLACE_TYPE_B 2
static int config_input(AVFilterLink *inlink)
{
SelectContext *select = inlink->dst->priv;
select->var_values[VAR_N] = 0.0;
select->var_values[VAR_SELECTED_N] = 0.0;
select->var_values[VAR_TB] = av_q2d(inlink->time_base);
select->var_values[VAR_PREV_PTS] = NAN;
select->var_values[VAR_PREV_SELECTED_PTS] = NAN;
select->var_values[VAR_PREV_SELECTED_T] = NAN;
select->var_values[VAR_PREV_T] = NAN;
select->var_values[VAR_START_PTS] = NAN;
select->var_values[VAR_START_T] = NAN;
select->var_values[VAR_I] = AV_PICTURE_TYPE_I;
select->var_values[VAR_P] = AV_PICTURE_TYPE_P;
select->var_values[VAR_B] = AV_PICTURE_TYPE_B;
select->var_values[VAR_SI] = AV_PICTURE_TYPE_SI;
select->var_values[VAR_SP] = AV_PICTURE_TYPE_SP;
select->var_values[VAR_BI] = AV_PICTURE_TYPE_BI;
select->var_values[VAR_PICT_TYPE_I] = AV_PICTURE_TYPE_I;
select->var_values[VAR_PICT_TYPE_P] = AV_PICTURE_TYPE_P;
select->var_values[VAR_PICT_TYPE_B] = AV_PICTURE_TYPE_B;
select->var_values[VAR_PICT_TYPE_SI] = AV_PICTURE_TYPE_SI;
select->var_values[VAR_PICT_TYPE_SP] = AV_PICTURE_TYPE_SP;
select->var_values[VAR_PICT_TYPE_BI] = AV_PICTURE_TYPE_BI;
select->var_values[VAR_INTERLACE_TYPE_P] = INTERLACE_TYPE_P;
select->var_values[VAR_INTERLACE_TYPE_T] = INTERLACE_TYPE_T;
select->var_values[VAR_INTERLACE_TYPE_B] = INTERLACE_TYPE_B;
select->var_values[VAR_PICT_TYPE] = NAN;
select->var_values[VAR_INTERLACE_TYPE] = NAN;
select->var_values[VAR_SCENE] = NAN;
select->var_values[VAR_CONSUMED_SAMPLES_N] = NAN;
select->var_values[VAR_SAMPLES_N] = NAN;
select->var_values[VAR_SAMPLE_RATE] =
inlink->type == AVMEDIA_TYPE_AUDIO ? inlink->sample_rate : NAN;
#if CONFIG_AVCODEC
if (select->do_scene_detect) {
select->avctx = avcodec_alloc_context3(NULL);
if (!select->avctx)
return AVERROR(ENOMEM);
avpriv_dsputil_init(&select->c, select->avctx);
}
#endif
return 0;
}
#if CONFIG_AVCODEC
static double get_scene_score(AVFilterContext *ctx, AVFrame *frame)
{
double ret = 0;
SelectContext *select = ctx->priv;
AVFrame *prev_picref = select->prev_picref;
if (prev_picref &&
frame->height == prev_picref->height &&
frame->width == prev_picref->width &&
frame->linesize[0] == prev_picref->linesize[0]) {
int x, y, nb_sad = 0;
int64_t sad = 0;
double mafd, diff;
uint8_t *p1 = frame->data[0];
uint8_t *p2 = prev_picref->data[0];
const int linesize = frame->linesize[0];
for (y = 0; y < frame->height - 8; y += 8) {
for (x = 0; x < frame->width*3 - 8; x += 8) {
sad += select->c.sad[1](select, p1 + x, p2 + x,
linesize, 8);
nb_sad += 8 * 8;
}
p1 += 8 * linesize;
p2 += 8 * linesize;
}
emms_c();
mafd = nb_sad ? sad / nb_sad : 0;
diff = fabs(mafd - select->prev_mafd);
ret = av_clipf(FFMIN(mafd, diff) / 100., 0, 1);
select->prev_mafd = mafd;
av_frame_free(&prev_picref);
}
select->prev_picref = av_frame_clone(frame);
return ret;
}
#endif
#define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d))
#define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts))
static void select_frame(AVFilterContext *ctx, AVFrame *frame)
{
SelectContext *select = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
double res;
if (isnan(select->var_values[VAR_START_PTS]))
select->var_values[VAR_START_PTS] = TS2D(frame->pts);
if (isnan(select->var_values[VAR_START_T]))
select->var_values[VAR_START_T] = TS2D(frame->pts) * av_q2d(inlink->time_base);
select->var_values[VAR_N ] = inlink->frame_count;
select->var_values[VAR_PTS] = TS2D(frame->pts);
select->var_values[VAR_T ] = TS2D(frame->pts) * av_q2d(inlink->time_base);
select->var_values[VAR_POS] = av_frame_get_pkt_pos(frame) == -1 ? NAN : av_frame_get_pkt_pos(frame);
switch (inlink->type) {
case AVMEDIA_TYPE_AUDIO:
select->var_values[VAR_SAMPLES_N] = frame->nb_samples;
break;
case AVMEDIA_TYPE_VIDEO:
select->var_values[VAR_INTERLACE_TYPE] =
!frame->interlaced_frame ? INTERLACE_TYPE_P :
frame->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B;
select->var_values[VAR_PICT_TYPE] = frame->pict_type;
#if CONFIG_AVCODEC
if (select->do_scene_detect) {
char buf[32];
select->var_values[VAR_SCENE] = get_scene_score(ctx, frame);
// TODO: document metadata
snprintf(buf, sizeof(buf), "%f", select->var_values[VAR_SCENE]);
av_dict_set(avpriv_frame_get_metadatap(frame), "lavfi.scene_score", buf, 0);
}
#endif
break;
}
select->select = res = av_expr_eval(select->expr, select->var_values, NULL);
av_log(inlink->dst, AV_LOG_DEBUG,
"n:%f pts:%f t:%f key:%d",
select->var_values[VAR_N],
select->var_values[VAR_PTS],
select->var_values[VAR_T],
(int)select->var_values[VAR_KEY]);
switch (inlink->type) {
case AVMEDIA_TYPE_VIDEO:
av_log(inlink->dst, AV_LOG_DEBUG, " interlace_type:%c pict_type:%c scene:%f",
select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_P ? 'P' :
select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_T ? 'T' :
select->var_values[VAR_INTERLACE_TYPE] == INTERLACE_TYPE_B ? 'B' : '?',
av_get_picture_type_char(select->var_values[VAR_PICT_TYPE]),
select->var_values[VAR_SCENE]);
break;
case AVMEDIA_TYPE_AUDIO:
av_log(inlink->dst, AV_LOG_DEBUG, " samples_n:%d consumed_samples_n:%d",
(int)select->var_values[VAR_SAMPLES_N],
(int)select->var_values[VAR_CONSUMED_SAMPLES_N]);
break;
}
if (res == 0) {
select->select_out = -1; /* drop */
} else if (isnan(res) || res < 0) {
select->select_out = 0; /* first output */
} else {
select->select_out = FFMIN(ceilf(res)-1, select->nb_outputs-1); /* other outputs */
}
av_log(inlink->dst, AV_LOG_DEBUG, " -> select:%f select_out:%d\n", res, select->select_out);
if (res) {
select->var_values[VAR_PREV_SELECTED_N] = select->var_values[VAR_N];
select->var_values[VAR_PREV_SELECTED_PTS] = select->var_values[VAR_PTS];
select->var_values[VAR_PREV_SELECTED_T] = select->var_values[VAR_T];
select->var_values[VAR_SELECTED_N] += 1.0;
if (inlink->type == AVMEDIA_TYPE_AUDIO)
select->var_values[VAR_CONSUMED_SAMPLES_N] += frame->nb_samples;
}
select->var_values[VAR_PREV_PTS] = select->var_values[VAR_PTS];
select->var_values[VAR_PREV_T] = select->var_values[VAR_T];
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
SelectContext *select = ctx->priv;
select_frame(ctx, frame);
if (select->select)
return ff_filter_frame(ctx->outputs[select->select_out], frame);
av_frame_free(&frame);
return 0;
}
static int request_frame(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
SelectContext *select = ctx->priv;
AVFilterLink *inlink = outlink->src->inputs[0];
int out_no = FF_OUTLINK_IDX(outlink);
do {
int ret = ff_request_frame(inlink);
if (ret < 0)
return ret;
} while (select->select_out != out_no);
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
SelectContext *select = ctx->priv;
int i;
av_expr_free(select->expr);
select->expr = NULL;
for (i = 0; i < ctx->nb_outputs; i++)
av_freep(&ctx->output_pads[i].name);
#if CONFIG_AVCODEC
if (select->do_scene_detect) {
av_frame_free(&select->prev_picref);
if (select->avctx) {
avcodec_close(select->avctx);
av_freep(&select->avctx);
}
}
#endif
}
static int query_formats(AVFilterContext *ctx)
{
SelectContext *select = ctx->priv;
if (!select->do_scene_detect) {
return ff_default_query_formats(ctx);
} else {
static const enum AVPixelFormat pix_fmts[] = {
AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
AV_PIX_FMT_NONE
};
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
}
return 0;
}
#if CONFIG_ASELECT_FILTER
DEFINE_OPTIONS(aselect, AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM);
AVFILTER_DEFINE_CLASS(aselect);
static av_cold int aselect_init(AVFilterContext *ctx)
{
SelectContext *select = ctx->priv;
int ret;
if ((ret = init(ctx)) < 0)
return ret;
if (select->do_scene_detect) {
av_log(ctx, AV_LOG_ERROR, "Scene detection is ignored in aselect filter\n");
return AVERROR(EINVAL);
}
return 0;
}
static const AVFilterPad avfilter_af_aselect_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_input,
.filter_frame = filter_frame,
},
{ NULL }
};
AVFilter avfilter_af_aselect = {
.name = "aselect",
.description = NULL_IF_CONFIG_SMALL("Select audio frames to pass in output."),
.init = aselect_init,
.uninit = uninit,
.priv_size = sizeof(SelectContext),
.inputs = avfilter_af_aselect_inputs,
.priv_class = &aselect_class,
.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
};
#endif /* CONFIG_ASELECT_FILTER */
#if CONFIG_SELECT_FILTER
DEFINE_OPTIONS(select, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM);
AVFILTER_DEFINE_CLASS(select);
static av_cold int select_init(AVFilterContext *ctx)
{
SelectContext *select = ctx->priv;
int ret;
if ((ret = init(ctx)) < 0)
return ret;
if (select->do_scene_detect && !CONFIG_AVCODEC) {
av_log(ctx, AV_LOG_ERROR, "Scene detection is not available without libavcodec.\n");
return AVERROR(EINVAL);
}
return 0;
}
static const AVFilterPad avfilter_vf_select_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_input,
.filter_frame = filter_frame,
},
{ NULL }
};
AVFilter avfilter_vf_select = {
.name = "select",
.description = NULL_IF_CONFIG_SMALL("Select video frames to pass in output."),
.init = select_init,
.uninit = uninit,
.query_formats = query_formats,
.priv_size = sizeof(SelectContext),
.priv_class = &select_class,
.inputs = avfilter_vf_select_inputs,
.flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS,
};
#endif /* CONFIG_SELECT_FILTER */
+576
View File
@@ -0,0 +1,576 @@
/*
* Copyright (c) 2012 Stefano Sabatini
*
* 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
* send commands filter
*/
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/file.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "avfilter.h"
#include "internal.h"
#include "avfiltergraph.h"
#include "audio.h"
#include "video.h"
#define COMMAND_FLAG_ENTER 1
#define COMMAND_FLAG_LEAVE 2
static inline char *make_command_flags_str(AVBPrint *pbuf, int flags)
{
static const char * const flag_strings[] = { "enter", "leave" };
int i, is_first = 1;
av_bprint_init(pbuf, 0, AV_BPRINT_SIZE_AUTOMATIC);
for (i = 0; i < FF_ARRAY_ELEMS(flag_strings); i++) {
if (flags & 1<<i) {
if (!is_first)
av_bprint_chars(pbuf, '+', 1);
av_bprintf(pbuf, "%s", flag_strings[i]);
is_first = 0;
}
}
return pbuf->str;
}
typedef struct {
int flags;
char *target, *command, *arg;
int index;
} Command;
typedef struct {
int64_t start_ts; ///< start timestamp expressed as microseconds units
int64_t end_ts; ///< end timestamp expressed as microseconds units
int index; ///< unique index for these interval commands
Command *commands;
int nb_commands;
int enabled; ///< current time detected inside this interval
} Interval;
typedef struct {
const AVClass *class;
Interval *intervals;
int nb_intervals;
char *commands_filename;
char *commands_str;
} SendCmdContext;
#define OFFSET(x) offsetof(SendCmdContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM
static const AVOption options[] = {
{ "commands", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ "c", "set commands", OFFSET(commands_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ "filename", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ "f", "set commands file", OFFSET(commands_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS },
{ NULL }
};
#define SPACES " \f\t\n\r"
static void skip_comments(const char **buf)
{
while (**buf) {
/* skip leading spaces */
*buf += strspn(*buf, SPACES);
if (**buf != '#')
break;
(*buf)++;
/* skip comment until the end of line */
*buf += strcspn(*buf, "\n");
if (**buf)
(*buf)++;
}
}
#define COMMAND_DELIMS " \f\t\n\r,;"
static int parse_command(Command *cmd, int cmd_count, int interval_count,
const char **buf, void *log_ctx)
{
int ret;
memset(cmd, 0, sizeof(Command));
cmd->index = cmd_count;
/* format: [FLAGS] target command arg */
*buf += strspn(*buf, SPACES);
/* parse flags */
if (**buf == '[') {
(*buf)++; /* skip "[" */
while (**buf) {
int len = strcspn(*buf, "|+]");
if (!strncmp(*buf, "enter", strlen("enter"))) cmd->flags |= COMMAND_FLAG_ENTER;
else if (!strncmp(*buf, "leave", strlen("leave"))) cmd->flags |= COMMAND_FLAG_LEAVE;
else {
char flag_buf[64];
av_strlcpy(flag_buf, *buf, sizeof(flag_buf));
av_log(log_ctx, AV_LOG_ERROR,
"Unknown flag '%s' in interval #%d, command #%d\n",
flag_buf, interval_count, cmd_count);
return AVERROR(EINVAL);
}
*buf += len;
if (**buf == ']')
break;
if (!strspn(*buf, "+|")) {
av_log(log_ctx, AV_LOG_ERROR,
"Invalid flags char '%c' in interval #%d, command #%d\n",
**buf, interval_count, cmd_count);
return AVERROR(EINVAL);
}
if (**buf)
(*buf)++;
}
if (**buf != ']') {
av_log(log_ctx, AV_LOG_ERROR,
"Missing flag terminator or extraneous data found at the end of flags "
"in interval #%d, command #%d\n", interval_count, cmd_count);
return AVERROR(EINVAL);
}
(*buf)++; /* skip "]" */
} else {
cmd->flags = COMMAND_FLAG_ENTER;
}
*buf += strspn(*buf, SPACES);
cmd->target = av_get_token(buf, COMMAND_DELIMS);
if (!cmd->target || !cmd->target[0]) {
av_log(log_ctx, AV_LOG_ERROR,
"No target specified in interval #%d, command #%d\n",
interval_count, cmd_count);
ret = AVERROR(EINVAL);
goto fail;
}
*buf += strspn(*buf, SPACES);
cmd->command = av_get_token(buf, COMMAND_DELIMS);
if (!cmd->command || !cmd->command[0]) {
av_log(log_ctx, AV_LOG_ERROR,
"No command specified in interval #%d, command #%d\n",
interval_count, cmd_count);
ret = AVERROR(EINVAL);
goto fail;
}
*buf += strspn(*buf, SPACES);
cmd->arg = av_get_token(buf, COMMAND_DELIMS);
return 1;
fail:
av_freep(&cmd->target);
av_freep(&cmd->command);
av_freep(&cmd->arg);
return ret;
}
static int parse_commands(Command **cmds, int *nb_cmds, int interval_count,
const char **buf, void *log_ctx)
{
int cmd_count = 0;
int ret, n = 0;
AVBPrint pbuf;
*cmds = NULL;
*nb_cmds = 0;
while (**buf) {
Command cmd;
if ((ret = parse_command(&cmd, cmd_count, interval_count, buf, log_ctx)) < 0)
return ret;
cmd_count++;
/* (re)allocate commands array if required */
if (*nb_cmds == n) {
n = FFMAX(16, 2*n); /* first allocation = 16, or double the number */
*cmds = av_realloc_f(*cmds, n, 2*sizeof(Command));
if (!*cmds) {
av_log(log_ctx, AV_LOG_ERROR,
"Could not (re)allocate command array\n");
return AVERROR(ENOMEM);
}
}
(*cmds)[(*nb_cmds)++] = cmd;
*buf += strspn(*buf, SPACES);
if (**buf && **buf != ';' && **buf != ',') {
av_log(log_ctx, AV_LOG_ERROR,
"Missing separator or extraneous data found at the end of "
"interval #%d, in command #%d\n",
interval_count, cmd_count);
av_log(log_ctx, AV_LOG_ERROR,
"Command was parsed as: flags:[%s] target:%s command:%s arg:%s\n",
make_command_flags_str(&pbuf, cmd.flags), cmd.target, cmd.command, cmd.arg);
return AVERROR(EINVAL);
}
if (**buf == ';')
break;
if (**buf == ',')
(*buf)++;
}
return 0;
}
#define DELIMS " \f\t\n\r,;"
static int parse_interval(Interval *interval, int interval_count,
const char **buf, void *log_ctx)
{
char *intervalstr;
int ret;
*buf += strspn(*buf, SPACES);
if (!**buf)
return 0;
/* reset data */
memset(interval, 0, sizeof(Interval));
interval->index = interval_count;
/* format: INTERVAL COMMANDS */
/* parse interval */
intervalstr = av_get_token(buf, DELIMS);
if (intervalstr && intervalstr[0]) {
char *start, *end;
start = av_strtok(intervalstr, "-", &end);
if ((ret = av_parse_time(&interval->start_ts, start, 1)) < 0) {
av_log(log_ctx, AV_LOG_ERROR,
"Invalid start time specification '%s' in interval #%d\n",
start, interval_count);
goto end;
}
if (end) {
if ((ret = av_parse_time(&interval->end_ts, end, 1)) < 0) {
av_log(log_ctx, AV_LOG_ERROR,
"Invalid end time specification '%s' in interval #%d\n",
end, interval_count);
goto end;
}
} else {
interval->end_ts = INT64_MAX;
}
if (interval->end_ts < interval->start_ts) {
av_log(log_ctx, AV_LOG_ERROR,
"Invalid end time '%s' in interval #%d: "
"cannot be lesser than start time '%s'\n",
end, interval_count, start);
ret = AVERROR(EINVAL);
goto end;
}
} else {
av_log(log_ctx, AV_LOG_ERROR,
"No interval specified for interval #%d\n", interval_count);
ret = AVERROR(EINVAL);
goto end;
}
/* parse commands */
ret = parse_commands(&interval->commands, &interval->nb_commands,
interval_count, buf, log_ctx);
end:
av_free(intervalstr);
return ret;
}
static int parse_intervals(Interval **intervals, int *nb_intervals,
const char *buf, void *log_ctx)
{
int interval_count = 0;
int ret, n = 0;
*intervals = NULL;
*nb_intervals = 0;
while (1) {
Interval interval;
skip_comments(&buf);
if (!(*buf))
break;
if ((ret = parse_interval(&interval, interval_count, &buf, log_ctx)) < 0)
return ret;
buf += strspn(buf, SPACES);
if (*buf) {
if (*buf != ';') {
av_log(log_ctx, AV_LOG_ERROR,
"Missing terminator or extraneous data found at the end of interval #%d\n",
interval_count);
return AVERROR(EINVAL);
}
buf++; /* skip ';' */
}
interval_count++;
/* (re)allocate commands array if required */
if (*nb_intervals == n) {
n = FFMAX(16, 2*n); /* first allocation = 16, or double the number */
*intervals = av_realloc_f(*intervals, n, 2*sizeof(Interval));
if (!*intervals) {
av_log(log_ctx, AV_LOG_ERROR,
"Could not (re)allocate intervals array\n");
return AVERROR(ENOMEM);
}
}
(*intervals)[(*nb_intervals)++] = interval;
}
return 0;
}
static int cmp_intervals(const void *a, const void *b)
{
const Interval *i1 = a;
const Interval *i2 = b;
int64_t ts_diff = i1->start_ts - i2->start_ts;
int ret;
ret = ts_diff > 0 ? 1 : ts_diff < 0 ? -1 : 0;
return ret == 0 ? i1->index - i2->index : ret;
}
static av_cold int init(AVFilterContext *ctx)
{
SendCmdContext *sendcmd = ctx->priv;
int ret, i, j;
if (sendcmd->commands_filename && sendcmd->commands_str) {
av_log(ctx, AV_LOG_ERROR,
"Only one of the filename or commands options must be specified\n");
return AVERROR(EINVAL);
}
if (sendcmd->commands_filename) {
uint8_t *file_buf, *buf;
size_t file_bufsize;
ret = av_file_map(sendcmd->commands_filename,
&file_buf, &file_bufsize, 0, ctx);
if (ret < 0)
return ret;
/* create a 0-terminated string based on the read file */
buf = av_malloc(file_bufsize + 1);
if (!buf) {
av_file_unmap(file_buf, file_bufsize);
return AVERROR(ENOMEM);
}
memcpy(buf, file_buf, file_bufsize);
buf[file_bufsize] = 0;
av_file_unmap(file_buf, file_bufsize);
sendcmd->commands_str = buf;
}
if ((ret = parse_intervals(&sendcmd->intervals, &sendcmd->nb_intervals,
sendcmd->commands_str, ctx)) < 0)
return ret;
qsort(sendcmd->intervals, sendcmd->nb_intervals, sizeof(Interval), cmp_intervals);
av_log(ctx, AV_LOG_DEBUG, "Parsed commands:\n");
for (i = 0; i < sendcmd->nb_intervals; i++) {
AVBPrint pbuf;
Interval *interval = &sendcmd->intervals[i];
av_log(ctx, AV_LOG_VERBOSE, "start_time:%f end_time:%f index:%d\n",
(double)interval->start_ts/1000000, (double)interval->end_ts/1000000, interval->index);
for (j = 0; j < interval->nb_commands; j++) {
Command *cmd = &interval->commands[j];
av_log(ctx, AV_LOG_VERBOSE,
" [%s] target:%s command:%s arg:%s index:%d\n",
make_command_flags_str(&pbuf, cmd->flags), cmd->target, cmd->command, cmd->arg, cmd->index);
}
}
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
SendCmdContext *sendcmd = ctx->priv;
int i, j;
for (i = 0; i < sendcmd->nb_intervals; i++) {
Interval *interval = &sendcmd->intervals[i];
for (j = 0; j < interval->nb_commands; j++) {
Command *cmd = &interval->commands[j];
av_free(cmd->target);
av_free(cmd->command);
av_free(cmd->arg);
}
av_free(interval->commands);
}
av_freep(&sendcmd->intervals);
}
static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
{
AVFilterContext *ctx = inlink->dst;
SendCmdContext *sendcmd = ctx->priv;
int64_t ts;
int i, j, ret;
if (ref->pts == AV_NOPTS_VALUE)
goto end;
ts = av_rescale_q(ref->pts, inlink->time_base, AV_TIME_BASE_Q);
#define WITHIN_INTERVAL(ts, start_ts, end_ts) ((ts) >= (start_ts) && (ts) < (end_ts))
for (i = 0; i < sendcmd->nb_intervals; i++) {
Interval *interval = &sendcmd->intervals[i];
int flags = 0;
if (!interval->enabled && WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) {
flags += COMMAND_FLAG_ENTER;
interval->enabled = 1;
}
if (interval->enabled && !WITHIN_INTERVAL(ts, interval->start_ts, interval->end_ts)) {
flags += COMMAND_FLAG_LEAVE;
interval->enabled = 0;
}
if (flags) {
AVBPrint pbuf;
av_log(ctx, AV_LOG_VERBOSE,
"[%s] interval #%d start_ts:%f end_ts:%f ts:%f\n",
make_command_flags_str(&pbuf, flags), interval->index,
(double)interval->start_ts/1000000, (double)interval->end_ts/1000000,
(double)ts/1000000);
for (j = 0; flags && j < interval->nb_commands; j++) {
Command *cmd = &interval->commands[j];
char buf[1024];
if (cmd->flags & flags) {
av_log(ctx, AV_LOG_VERBOSE,
"Processing command #%d target:%s command:%s arg:%s\n",
cmd->index, cmd->target, cmd->command, cmd->arg);
ret = avfilter_graph_send_command(inlink->graph,
cmd->target, cmd->command, cmd->arg,
buf, sizeof(buf),
AVFILTER_CMD_FLAG_ONE);
av_log(ctx, AV_LOG_VERBOSE,
"Command reply for command #%d: ret:%s res:%s\n",
cmd->index, av_err2str(ret), buf);
}
}
}
}
end:
switch (inlink->type) {
case AVMEDIA_TYPE_VIDEO:
case AVMEDIA_TYPE_AUDIO:
return ff_filter_frame(inlink->dst->outputs[0], ref);
}
return AVERROR(ENOSYS);
}
#if CONFIG_SENDCMD_FILTER
#define sendcmd_options options
AVFILTER_DEFINE_CLASS(sendcmd);
static const AVFilterPad sendcmd_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad sendcmd_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
},
{ NULL }
};
AVFilter avfilter_vf_sendcmd = {
.name = "sendcmd",
.description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
.init = init,
.uninit = uninit,
.priv_size = sizeof(SendCmdContext),
.inputs = sendcmd_inputs,
.outputs = sendcmd_outputs,
.priv_class = &sendcmd_class,
};
#endif
#if CONFIG_ASENDCMD_FILTER
#define asendcmd_options options
AVFILTER_DEFINE_CLASS(asendcmd);
static const AVFilterPad asendcmd_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad asendcmd_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_asendcmd = {
.name = "asendcmd",
.description = NULL_IF_CONFIG_SMALL("Send commands to filters."),
.init = init,
.uninit = uninit,
.priv_size = sizeof(SendCmdContext),
.inputs = asendcmd_inputs,
.outputs = asendcmd_outputs,
.priv_class = &asendcmd_class,
};
#endif
+187
View File
@@ -0,0 +1,187 @@
/*
* Copyright (c) 2010 Stefano Sabatini
*
* 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
* Set timebase for the output link.
*/
#include <inttypes.h>
#include <stdio.h>
#include "libavutil/avstring.h"
#include "libavutil/eval.h"
#include "libavutil/internal.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
#include "libavutil/rational.h"
#include "avfilter.h"
#include "internal.h"
#include "audio.h"
#include "video.h"
static const char *const var_names[] = {
"AVTB", /* default timebase 1/AV_TIME_BASE */
"intb", /* input timebase */
"sr", /* sample rate */
NULL
};
enum var_name {
VAR_AVTB,
VAR_INTB,
VAR_SR,
VAR_VARS_NB
};
typedef struct {
const AVClass *class;
char *tb_expr;
double var_values[VAR_VARS_NB];
} SetTBContext;
#define OFFSET(x) offsetof(SetTBContext, x)
#define DEFINE_OPTIONS(filt_name, filt_type) \
static const AVOption filt_name##_options[] = { \
{ "expr", "set expression determining the output timebase", OFFSET(tb_expr), AV_OPT_TYPE_STRING, {.str="intb"}, \
.flags=AV_OPT_FLAG_##filt_type##_PARAM|AV_OPT_FLAG_FILTERING_PARAM }, \
{ "tb", "set expression determining the output timebase", OFFSET(tb_expr), AV_OPT_TYPE_STRING, {.str="intb"}, \
.flags=AV_OPT_FLAG_##filt_type##_PARAM|AV_OPT_FLAG_FILTERING_PARAM }, \
{ NULL } \
}
static int config_output_props(AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
SetTBContext *settb = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
AVRational time_base;
int ret;
double res;
settb->var_values[VAR_AVTB] = av_q2d(AV_TIME_BASE_Q);
settb->var_values[VAR_INTB] = av_q2d(inlink->time_base);
settb->var_values[VAR_SR] = inlink->sample_rate;
outlink->w = inlink->w;
outlink->h = inlink->h;
if ((ret = av_expr_parse_and_eval(&res, settb->tb_expr, var_names, settb->var_values,
NULL, NULL, NULL, NULL, NULL, 0, NULL)) < 0) {
av_log(ctx, AV_LOG_ERROR, "Invalid expression '%s' for timebase.\n", settb->tb_expr);
return ret;
}
time_base = av_d2q(res, INT_MAX);
if (time_base.num <= 0 || time_base.den <= 0) {
av_log(ctx, AV_LOG_ERROR,
"Invalid non-positive values for the timebase num:%d or den:%d.\n",
time_base.num, time_base.den);
return AVERROR(EINVAL);
}
outlink->time_base = time_base;
av_log(outlink->src, AV_LOG_VERBOSE, "tb:%d/%d -> tb:%d/%d\n",
inlink ->time_base.num, inlink ->time_base.den,
outlink->time_base.num, outlink->time_base.den);
return 0;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *ctx = inlink->dst;
AVFilterLink *outlink = ctx->outputs[0];
if (av_cmp_q(inlink->time_base, outlink->time_base)) {
int64_t orig_pts = frame->pts;
frame->pts = av_rescale_q(frame->pts, inlink->time_base, outlink->time_base);
av_log(ctx, AV_LOG_DEBUG, "tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n",
inlink ->time_base.num, inlink ->time_base.den, orig_pts,
outlink->time_base.num, outlink->time_base.den, frame->pts);
}
return ff_filter_frame(outlink, frame);
}
#if CONFIG_SETTB_FILTER
DEFINE_OPTIONS(settb, VIDEO);
AVFILTER_DEFINE_CLASS(settb);
static const AVFilterPad avfilter_vf_settb_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad avfilter_vf_settb_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_output_props,
},
{ NULL }
};
AVFilter avfilter_vf_settb = {
.name = "settb",
.description = NULL_IF_CONFIG_SMALL("Set timebase for the video output link."),
.priv_size = sizeof(SetTBContext),
.priv_class = &settb_class,
.inputs = avfilter_vf_settb_inputs,
.outputs = avfilter_vf_settb_outputs,
};
#endif
#if CONFIG_ASETTB_FILTER
DEFINE_OPTIONS(asettb, AUDIO);
AVFILTER_DEFINE_CLASS(asettb);
static const AVFilterPad avfilter_af_asettb_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad avfilter_af_asettb_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.config_props = config_output_props,
},
{ NULL }
};
AVFilter avfilter_af_asettb = {
.name = "asettb",
.description = NULL_IF_CONFIG_SMALL("Set timebase for the audio output link."),
.priv_size = sizeof(SetTBContext),
.inputs = avfilter_af_asettb_inputs,
.outputs = avfilter_af_asettb_outputs,
.priv_class = &asettb_class,
};
#endif
+275
View File
@@ -0,0 +1,275 @@
/*
* Copyright (c) 2013 Stefano Sabatini
*
* 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
* receive commands through libzeromq and broker them to filters
*/
#include <zmq.h>
#include "libavutil/avstring.h"
#include "libavutil/bprint.h"
#include "libavutil/opt.h"
#include "avfilter.h"
#include "internal.h"
#include "avfiltergraph.h"
#include "audio.h"
#include "video.h"
typedef struct {
const AVClass *class;
void *zmq;
void *responder;
char *bind_address;
int command_count;
} ZMQContext;
#define OFFSET(x) offsetof(ZMQContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM
static const AVOption options[] = {
{ "bind_address", "set bind address", OFFSET(bind_address), AV_OPT_TYPE_STRING, {.str = "tcp://*:5555"}, 0, 0, FLAGS },
{ "b", "set bind address", OFFSET(bind_address), AV_OPT_TYPE_STRING, {.str = "tcp://*:5555"}, 0, 0, FLAGS },
{ NULL }
};
static av_cold int init(AVFilterContext *ctx)
{
ZMQContext *zmq = ctx->priv;
zmq->zmq = zmq_ctx_new();
if (!zmq->zmq) {
av_log(ctx, AV_LOG_ERROR,
"Could not create ZMQ context: %s\n", zmq_strerror(errno));
return AVERROR_EXTERNAL;
}
zmq->responder = zmq_socket(zmq->zmq, ZMQ_REP);
if (!zmq->responder) {
av_log(ctx, AV_LOG_ERROR,
"Could not create ZMQ socket: %s\n", zmq_strerror(errno));
return AVERROR_EXTERNAL;
}
if (zmq_bind(zmq->responder, zmq->bind_address) == -1) {
av_log(ctx, AV_LOG_ERROR,
"Could not bind ZMQ socket to address '%s': %s\n",
zmq->bind_address, zmq_strerror(errno));
return AVERROR_EXTERNAL;
}
zmq->command_count = -1;
return 0;
}
static void av_cold uninit(AVFilterContext *ctx)
{
ZMQContext *zmq = ctx->priv;
zmq_close(zmq->responder);
zmq_ctx_destroy(zmq->zmq);
}
typedef struct {
char *target, *command, *arg;
} Command;
#define SPACES " \f\t\n\r"
static int parse_command(Command *cmd, const char *command_str, void *log_ctx)
{
const char **buf = &command_str;
cmd->target = av_get_token(buf, SPACES);
if (!cmd->target || !cmd->target[0]) {
av_log(log_ctx, AV_LOG_ERROR,
"No target specified in command '%s'\n", command_str);
return AVERROR(EINVAL);
}
cmd->command = av_get_token(buf, SPACES);
if (!cmd->command || !cmd->command[0]) {
av_log(log_ctx, AV_LOG_ERROR,
"No command specified in command '%s'\n", command_str);
return AVERROR(EINVAL);
}
cmd->arg = av_get_token(buf, SPACES);
return 0;
}
static int recv_msg(AVFilterContext *ctx, char **buf, int *buf_size)
{
ZMQContext *zmq = ctx->priv;
zmq_msg_t msg;
int ret = 0;
if (zmq_msg_init(&msg) == -1) {
av_log(ctx, AV_LOG_WARNING,
"Could not initialize receive message: %s\n", zmq_strerror(errno));
return AVERROR_EXTERNAL;
}
if (zmq_msg_recv(&msg, zmq->responder, ZMQ_DONTWAIT) == -1) {
if (errno != EAGAIN)
av_log(ctx, AV_LOG_WARNING,
"Could not receive message: %s\n", zmq_strerror(errno));
ret = AVERROR_EXTERNAL;
goto end;
}
*buf_size = zmq_msg_size(&msg) + 1;
*buf = av_malloc(*buf_size);
if (!*buf) {
ret = AVERROR(ENOMEM);
goto end;
}
memcpy(*buf, zmq_msg_data(&msg), *buf_size);
(*buf)[*buf_size-1] = 0;
end:
zmq_msg_close(&msg);
return ret;
}
static int filter_frame(AVFilterLink *inlink, AVFrame *ref)
{
AVFilterContext *ctx = inlink->dst;
ZMQContext *zmq = ctx->priv;
while (1) {
char cmd_buf[1024];
char *recv_buf, *send_buf;
int recv_buf_size;
Command cmd = {0};
int ret;
/* receive command */
if (recv_msg(ctx, &recv_buf, &recv_buf_size) < 0)
break;
zmq->command_count++;
/* parse command */
if (parse_command(&cmd, recv_buf, ctx) < 0) {
av_log(ctx, AV_LOG_ERROR, "Could not parse command #%d\n", zmq->command_count);
goto end;
}
/* process command */
av_log(ctx, AV_LOG_VERBOSE,
"Processing command #%d target:%s command:%s arg:%s\n",
zmq->command_count, cmd.target, cmd.command, cmd.arg);
ret = avfilter_graph_send_command(inlink->graph,
cmd.target, cmd.command, cmd.arg,
cmd_buf, sizeof(cmd_buf),
AVFILTER_CMD_FLAG_ONE);
send_buf = av_asprintf("%d %s%s%s",
-ret, av_err2str(ret), cmd_buf[0] ? "\n" : "", cmd_buf);
if (!send_buf) {
ret = AVERROR(ENOMEM);
goto end;
}
av_log(ctx, AV_LOG_VERBOSE,
"Sending command reply for command #%d:\n%s\n",
zmq->command_count, send_buf);
if (zmq_send(zmq->responder, send_buf, strlen(send_buf), 0) == -1)
av_log(ctx, AV_LOG_ERROR, "Failed to send reply for command #%d: %s\n",
zmq->command_count, zmq_strerror(ret));
end:
av_freep(&send_buf);
av_freep(&recv_buf);
recv_buf_size = 0;
av_freep(&cmd.target);
av_freep(&cmd.command);
av_freep(&cmd.arg);
}
return ff_filter_frame(ctx->outputs[0], ref);
}
#if CONFIG_ZMQ_FILTER
#define zmq_options options
AVFILTER_DEFINE_CLASS(zmq);
static const AVFilterPad zmq_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad zmq_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
},
{ NULL }
};
AVFilter avfilter_vf_zmq = {
.name = "zmq",
.description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."),
.init = init,
.uninit = uninit,
.priv_size = sizeof(ZMQContext),
.inputs = zmq_inputs,
.outputs = zmq_outputs,
.priv_class = &zmq_class,
};
#endif
#if CONFIG_AZMQ_FILTER
#define azmq_options options
AVFILTER_DEFINE_CLASS(azmq);
static const AVFilterPad azmq_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = filter_frame,
},
{ NULL }
};
static const AVFilterPad azmq_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
},
{ NULL }
};
AVFilter avfilter_af_azmq = {
.name = "azmq",
.description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."),
.init = init,
.uninit = uninit,
.priv_size = sizeof(ZMQContext),
.inputs = azmq_inputs,
.outputs = azmq_outputs,
.priv_class = &azmq_class,
};
#endif
+313
View File
@@ -0,0 +1,313 @@
/*
* Copyright (c) 2007 Bobby Bingham
*
* 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
* FIFO buffering filter
*/
#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/mathematics.h"
#include "libavutil/samplefmt.h"
#include "audio.h"
#include "avfilter.h"
#include "internal.h"
#include "video.h"
typedef struct Buf {
AVFrame *frame;
struct Buf *next;
} 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
*/
AVFrame *out;
int allocated_samples; ///< number of samples out was allocated for
} FifoContext;
static av_cold int init(AVFilterContext *ctx)
{
FifoContext *fifo = ctx->priv;
fifo->last = &fifo->root;
return 0;
}
static av_cold void uninit(AVFilterContext *ctx)
{
FifoContext *fifo = ctx->priv;
Buf *buf, *tmp;
for (buf = fifo->root.next; buf; buf = tmp) {
tmp = buf->next;
av_frame_free(&buf->frame);
av_free(buf);
}
av_frame_free(&fifo->out);
}
static int add_to_queue(AVFilterLink *inlink, AVFrame *frame)
{
FifoContext *fifo = inlink->dst->priv;
fifo->last->next = av_mallocz(sizeof(Buf));
if (!fifo->last->next) {
av_frame_free(&frame);
return AVERROR(ENOMEM);
}
fifo->last = fifo->last->next;
fifo->last->frame = frame;
return 0;
}
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;
}
/**
* Move data pointers and pts offset samples forward.
*/
static void buffer_offset(AVFilterLink *link, AVFrame *frame,
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(frame->nb_samples > offset);
for (i = 0; i < planes; i++)
frame->extended_data[i] += block_align * offset;
if (frame->data != frame->extended_data)
memcpy(frame->data, frame->extended_data,
FFMIN(planes, FF_ARRAY_ELEMS(frame->data)) * sizeof(*frame->data));
frame->linesize[0] -= block_align*offset;
frame->nb_samples -= offset;
if (frame->pts != AV_NOPTS_VALUE) {
frame->pts += av_rescale_q(offset, (AVRational){1, link->sample_rate},
link->time_base);
}
}
static int calc_ptr_alignment(AVFrame *frame)
{
int planes = av_sample_fmt_is_planar(frame->format) ?
av_get_channel_layout_nb_channels(frame->channel_layout) : 1;
int min_align = 128;
int p;
for (p = 0; p < planes; p++) {
int cur_align = 128;
while ((intptr_t)frame->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;
AVFrame *head = s->root.next ? s->root.next->frame : NULL;
AVFrame *out;
int ret;
/* if head is NULL then we're flushing the remaining samples in out */
if (!head && !s->out)
return AVERROR_EOF;
if (!s->out &&
head->nb_samples >= link->request_samples &&
calc_ptr_alignment(head) >= 32) {
if (head->nb_samples == link->request_samples) {
out = head;
queue_pop(s);
} else {
out = av_frame_clone(head);
if (!out)
return AVERROR(ENOMEM);
out->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->out) {
s->out = ff_get_audio_buffer(link, link->request_samples);
if (!s->out)
return AVERROR(ENOMEM);
s->out->nb_samples = 0;
s->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->out->nb_samples < s->allocated_samples) {
int len;
if (!s->root.next) {
ret = ff_request_frame(ctx->inputs[0]);
if (ret == AVERROR_EOF) {
av_samples_set_silence(s->out->extended_data,
s->out->nb_samples,
s->allocated_samples -
s->out->nb_samples,
nb_channels, link->format);
s->out->nb_samples = s->allocated_samples;
break;
} else if (ret < 0)
return ret;
av_assert0(s->root.next); // If ff_request_frame() succeeded then we should have a frame
}
head = s->root.next->frame;
len = FFMIN(s->allocated_samples - s->out->nb_samples,
head->nb_samples);
av_samples_copy(s->out->extended_data, head->extended_data,
s->out->nb_samples, 0, len, nb_channels,
link->format);
s->out->nb_samples += len;
if (len == head->nb_samples) {
av_frame_free(&head);
queue_pop(s);
} else {
buffer_offset(link, head, len);
}
}
out = s->out;
s->out = NULL;
}
return ff_filter_frame(link, out);
}
static int request_frame(AVFilterLink *outlink)
{
FifoContext *fifo = outlink->src->priv;
int ret = 0;
if (!fifo->root.next) {
if ((ret = ff_request_frame(outlink->src->inputs[0])) < 0) {
if (ret == AVERROR_EOF && outlink->request_samples)
return return_audio_frame(outlink->src);
return ret;
}
av_assert0(fifo->root.next);
}
if (outlink->request_samples) {
return return_audio_frame(outlink->src);
} else {
ret = ff_filter_frame(outlink, fifo->root.next->frame);
queue_pop(fifo);
}
return ret;
}
static const AVFilterPad avfilter_vf_fifo_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = add_to_queue,
},
{ NULL }
};
static const AVFilterPad avfilter_vf_fifo_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_vf_fifo = {
.name = "fifo",
.description = NULL_IF_CONFIG_SMALL("Buffer input images and send them when they are requested."),
.init = init,
.uninit = uninit,
.priv_size = sizeof(FifoContext),
.inputs = avfilter_vf_fifo_inputs,
.outputs = avfilter_vf_fifo_outputs,
};
static const AVFilterPad avfilter_af_afifo_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.filter_frame = add_to_queue,
},
{ NULL }
};
static const AVFilterPad avfilter_af_afifo_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_AUDIO,
.request_frame = request_frame,
},
{ NULL }
};
AVFilter avfilter_af_afifo = {
.name = "afifo",
.description = NULL_IF_CONFIG_SMALL("Buffer input frames and send them when they are requested."),
.init = init,
.uninit = uninit,
.priv_size = sizeof(FifoContext),
.inputs = avfilter_af_afifo_inputs,
.outputs = avfilter_af_afifo_outputs,
};
+136
View File
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2009 Stefano Sabatini
*
* 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 <stdio.h>
#include "libavformat/avformat.h"
#include "libavutil/pixdesc.h"
#include "libavutil/samplefmt.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/formats.h"
static void print_formats(AVFilterContext *filter_ctx)
{
int i, j;
#define PRINT_FMTS(inout, outin, INOUT) \
for (i = 0; i < filter_ctx->nb_##inout##puts; i++) { \
if (filter_ctx->inout##puts[i]->type == AVMEDIA_TYPE_VIDEO) { \
AVFilterFormats *fmts = \
filter_ctx->inout##puts[i]->outin##_formats; \
for (j = 0; j < fmts->nb_formats; j++) \
if(av_get_pix_fmt_name(fmts->formats[j])) \
printf(#INOUT "PUT[%d] %s: fmt:%s\n", \
i, filter_ctx->filter->inout##puts[i].name, \
av_get_pix_fmt_name(fmts->formats[j])); \
} else if (filter_ctx->inout##puts[i]->type == AVMEDIA_TYPE_AUDIO) { \
AVFilterFormats *fmts; \
AVFilterChannelLayouts *layouts; \
\
fmts = filter_ctx->inout##puts[i]->outin##_formats; \
for (j = 0; j < fmts->nb_formats; j++) \
printf(#INOUT "PUT[%d] %s: fmt:%s\n", \
i, filter_ctx->filter->inout##puts[i].name, \
av_get_sample_fmt_name(fmts->formats[j])); \
\
layouts = filter_ctx->inout##puts[i]->outin##_channel_layouts; \
for (j = 0; j < layouts->nb_channel_layouts; j++) { \
char buf[256]; \
av_get_channel_layout_string(buf, sizeof(buf), -1, \
layouts->channel_layouts[j]); \
printf(#INOUT "PUT[%d] %s: chlayout:%s\n", \
i, filter_ctx->filter->inout##puts[i].name, buf); \
} \
} \
} \
PRINT_FMTS(in, out, IN);
PRINT_FMTS(out, in, OUT);
}
int main(int argc, char **argv)
{
AVFilter *filter;
AVFilterContext *filter_ctx;
AVFilterGraph *graph_ctx;
const char *filter_name;
const char *filter_args = NULL;
int i;
av_log_set_level(AV_LOG_DEBUG);
if (argc < 2) {
fprintf(stderr, "Missing filter name as argument\n");
return 1;
}
filter_name = argv[1];
if (argc > 2)
filter_args = argv[2];
/* allocate graph */
graph_ctx = avfilter_graph_alloc();
if (!graph_ctx)
return 1;
avfilter_register_all();
/* get a corresponding filter and open it */
if (!(filter = avfilter_get_by_name(filter_name))) {
fprintf(stderr, "Unrecognized filter with name '%s'\n", filter_name);
return 1;
}
/* open filter and add it to the graph */
if (!(filter_ctx = avfilter_graph_alloc_filter(graph_ctx, filter, filter_name))) {
fprintf(stderr, "Impossible to open filter with name '%s'\n",
filter_name);
return 1;
}
if (avfilter_init_str(filter_ctx, filter_args) < 0) {
fprintf(stderr, "Impossible to init filter '%s' with arguments '%s'\n",
filter_name, filter_args);
return 1;
}
/* create a link for each of the input pads */
for (i = 0; i < filter_ctx->nb_inputs; i++) {
AVFilterLink *link = av_mallocz(sizeof(AVFilterLink));
link->type = filter_ctx->filter->inputs[i].type;
filter_ctx->inputs[i] = link;
}
for (i = 0; i < filter_ctx->nb_outputs; i++) {
AVFilterLink *link = av_mallocz(sizeof(AVFilterLink));
link->type = filter_ctx->filter->outputs[i].type;
filter_ctx->outputs[i] = link;
}
if (filter->query_formats)
filter->query_formats(filter_ctx);
else
ff_default_query_formats(filter_ctx);
print_formats(filter_ctx);
avfilter_free(filter_ctx);
avfilter_graph_free(&graph_ctx);
fflush(stdout);
return 0;
}
+664
View File
@@ -0,0 +1,664 @@
/*
* Filter layer - format negotiation
* Copyright (c) 2007 Bobby Bingham
*
* 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/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/eval.h"
#include "libavutil/pixdesc.h"
#include "libavutil/parseutils.h"
#include "avfilter.h"
#include "internal.h"
#include "formats.h"
#define KNOWN(l) (!FF_LAYOUT2COUNT(l)) /* for readability */
/**
* Add all refs from a to ret and destroy a.
*/
#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)
/**
* 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(NULL, 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 *ff_merge_formats(AVFilterFormats *a, AVFilterFormats *b,
enum AVMediaType type)
{
AVFilterFormats *ret = NULL;
int i, j;
int alpha1=0, alpha2=0;
int chroma1=0, chroma2=0;
if (a == b)
return a;
/* Do not lose chroma or alpha in merging.
It happens if both lists have formats with chroma (resp. alpha), but
the only formats in common do not have it (e.g. YUV+gray vs.
RGB+gray): in that case, the merging would select the gray format,
possibly causing a lossy conversion elsewhere in the graph.
To avoid that, pretend that there are no common formats to force the
insertion of a conversion filter. */
if (type == AVMEDIA_TYPE_VIDEO)
for (i = 0; i < a->nb_formats; i++)
for (j = 0; j < b->nb_formats; j++) {
const AVPixFmtDescriptor *adesc = av_pix_fmt_desc_get(a->formats[i]);
const AVPixFmtDescriptor *bdesc = av_pix_fmt_desc_get(b->formats[j]);
alpha2 |= adesc->flags & bdesc->flags & AV_PIX_FMT_FLAG_ALPHA;
chroma2|= adesc->nb_components > 1 && bdesc->nb_components > 1;
if (a->formats[i] == b->formats[j]) {
alpha1 |= adesc->flags & AV_PIX_FMT_FLAG_ALPHA;
chroma1|= adesc->nb_components > 1;
}
}
// If chroma or alpha can be lost through merging then do not merge
if (alpha2 > alpha1 || chroma2 > chroma1)
return NULL;
MERGE_FORMATS(ret, a, b, formats, nb_formats, 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;
if (a == b) return a;
if (a->nb_formats && b->nb_formats) {
MERGE_FORMATS(ret, a, b, formats, nb_formats, AVFilterFormats, fail);
} else if (a->nb_formats) {
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;
unsigned a_all = a->all_layouts + a->all_counts;
unsigned b_all = b->all_layouts + b->all_counts;
int ret_max, ret_nb = 0, i, j, round;
if (a == b) return a;
/* Put the most generic set in a, to avoid doing everything twice */
if (a_all < b_all) {
FFSWAP(AVFilterChannelLayouts *, a, b);
FFSWAP(unsigned, a_all, b_all);
}
if (a_all) {
if (a_all == 1 && !b_all) {
/* keep only known layouts in b; works also for b_all = 1 */
for (i = j = 0; i < b->nb_channel_layouts; i++)
if (KNOWN(b->channel_layouts[i]))
b->channel_layouts[j++] = b->channel_layouts[i];
/* Not optimal: the unknown layouts of b may become known after
another merge. */
if (!j)
return NULL;
b->nb_channel_layouts = j;
}
MERGE_REF(b, a, channel_layouts, AVFilterChannelLayouts, fail);
return b;
}
ret_max = a->nb_channel_layouts + b->nb_channel_layouts;
if (!(ret = av_mallocz(sizeof(*ret))) ||
!(ret->channel_layouts = av_malloc(sizeof(*ret->channel_layouts) *
ret_max)))
goto fail;
/* a[known] intersect b[known] */
for (i = 0; i < a->nb_channel_layouts; i++) {
if (!KNOWN(a->channel_layouts[i]))
continue;
for (j = 0; j < b->nb_channel_layouts; j++) {
if (a->channel_layouts[i] == b->channel_layouts[j]) {
ret->channel_layouts[ret_nb++] = a->channel_layouts[i];
a->channel_layouts[i] = b->channel_layouts[j] = 0;
}
}
}
/* 1st round: a[known] intersect b[generic]
2nd round: a[generic] intersect b[known] */
for (round = 0; round < 2; round++) {
for (i = 0; i < a->nb_channel_layouts; i++) {
uint64_t fmt = a->channel_layouts[i], bfmt;
if (!fmt || !KNOWN(fmt))
continue;
bfmt = FF_COUNT2LAYOUT(av_get_channel_layout_nb_channels(fmt));
for (j = 0; j < b->nb_channel_layouts; j++)
if (b->channel_layouts[j] == bfmt)
ret->channel_layouts[ret_nb++] = a->channel_layouts[i];
}
/* 1st round: swap to prepare 2nd round; 2nd round: put it back */
FFSWAP(AVFilterChannelLayouts *, a, b);
}
/* a[generic] intersect b[generic] */
for (i = 0; i < a->nb_channel_layouts; i++) {
if (KNOWN(a->channel_layouts[i]))
continue;
for (j = 0; j < b->nb_channel_layouts; j++)
if (a->channel_layouts[i] == b->channel_layouts[j])
ret->channel_layouts[ret_nb++] = a->channel_layouts[i];
}
ret->nb_channel_layouts = ret_nb;
if (!ret->nb_channel_layouts)
goto fail;
MERGE_REF(ret, a, channel_layouts, AVFilterChannelLayouts, fail);
MERGE_REF(ret, b, channel_layouts, AVFilterChannelLayouts, fail);
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)
{
const int *p;
for (p = fmts; *p != -1; p++) {
if (fmt == *p)
return 1;
}
return 0;
}
#define COPY_INT_LIST(list_copy, list, type) { \
int count = 0; \
if (list) \
for (count = 0; list[count] != -1; count++) \
; \
list_copy = av_calloc(count+1, sizeof(type)); \
if (list_copy) { \
memcpy(list_copy, list, sizeof(type) * count); \
list_copy[count] = -1; \
} \
}
#define MAKE_FORMAT_LIST(type, field, count_field) \
type *formats; \
int count = 0; \
if (fmts) \
for (count = 0; fmts[count] != -1; count++) \
; \
formats = av_mallocz(sizeof(*formats)); \
if (!formats) return NULL; \
formats->count_field = count; \
if (count) { \
formats->field = av_malloc(sizeof(*formats->field)*count); \
if (!formats->field) { \
av_free(formats); \
return NULL; \
} \
}
AVFilterFormats *ff_make_format_list(const int *fmts)
{
MAKE_FORMAT_LIST(AVFilterFormats, formats, nb_formats);
while (count--)
formats->formats[count] = fmts[count];
return formats;
}
AVFilterChannelLayouts *avfilter_make_format64_list(const int64_t *fmts)
{
MAKE_FORMAT_LIST(AVFilterChannelLayouts,
channel_layouts, nb_channel_layouts);
if (count)
memcpy(formats->channel_layouts, fmts,
sizeof(*formats->channel_layouts) * count);
return 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; \
} while (0)
int ff_add_format(AVFilterFormats **avff, int64_t fmt)
{
ADD_FORMAT(avff, fmt, int, formats, nb_formats);
return 0;
}
int ff_add_channel_layout(AVFilterChannelLayouts **l, uint64_t channel_layout)
{
av_assert1(!(*l && (*l)->all_layouts));
ADD_FORMAT(l, channel_layout, uint64_t, channel_layouts, nb_channel_layouts);
return 0;
}
AVFilterFormats *ff_all_formats(enum AVMediaType type)
{
AVFilterFormats *ret = NULL;
int fmt;
int num_formats = type == AVMEDIA_TYPE_VIDEO ? AV_PIX_FMT_NB :
type == AVMEDIA_TYPE_AUDIO ? AV_SAMPLE_FMT_NB : 0;
for (fmt = 0; fmt < num_formats; fmt++) {
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
if ((type != AVMEDIA_TYPE_VIDEO) ||
(type == AVMEDIA_TYPE_VIDEO && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)))
ff_add_format(&ret, fmt);
}
return ret;
}
const int64_t avfilter_all_channel_layouts[] = {
#include "all_channel_layouts.inc"
-1
};
// AVFilterFormats *avfilter_make_all_channel_layouts(void)
// {
// return avfilter_make_format64_list(avfilter_all_channel_layouts);
// }
AVFilterFormats *ff_planar_sample_fmts(void)
{
AVFilterFormats *ret = NULL;
int fmt;
for (fmt = 0; fmt < AV_SAMPLE_FMT_NB; fmt++)
if (av_sample_fmt_is_planar(fmt))
ff_add_format(&ret, fmt);
return ret;
}
AVFilterFormats *ff_all_samplerates(void)
{
AVFilterFormats *ret = av_mallocz(sizeof(*ret));
return ret;
}
AVFilterChannelLayouts *ff_all_channel_layouts(void)
{
AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret));
if (!ret)
return NULL;
ret->all_layouts = 1;
return ret;
}
AVFilterChannelLayouts *ff_all_channel_counts(void)
{
AVFilterChannelLayouts *ret = av_mallocz(sizeof(*ret));
if (!ret)
return NULL;
ret->all_layouts = ret->all_counts = 1;
return ret;
}
#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 ff_formats_ref(AVFilterFormats *f, AVFilterFormats **ref)
{
FORMATS_REF(f, ref);
}
#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)
void ff_formats_unref(AVFilterFormats **ref)
{
FORMATS_UNREF(ref, formats);
}
void ff_channel_layouts_unref(AVFilterChannelLayouts **ref)
{
FORMATS_UNREF(ref, channel_layouts);
}
#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 ff_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref)
{
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->nb_inputs; i++) { \
if (ctx->inputs[i] && !ctx->inputs[i]->out_fmts) { \
ref(fmts, &ctx->inputs[i]->out_fmts); \
count++; \
} \
} \
for (i = 0; i < ctx->nb_outputs; i++) { \
if (ctx->outputs[i] && !ctx->outputs[i]->in_fmts) { \
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,
ff_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 ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
{
SET_COMMON_FORMATS(ctx, formats, in_formats, out_formats,
ff_formats_ref, formats);
}
static int default_query_formats_common(AVFilterContext *ctx,
AVFilterChannelLayouts *(layouts)(void))
{
enum AVMediaType type = ctx->inputs && ctx->inputs [0] ? ctx->inputs [0]->type :
ctx->outputs && ctx->outputs[0] ? ctx->outputs[0]->type :
AVMEDIA_TYPE_VIDEO;
ff_set_common_formats(ctx, ff_all_formats(type));
if (type == AVMEDIA_TYPE_AUDIO) {
ff_set_common_channel_layouts(ctx, layouts());
ff_set_common_samplerates(ctx, ff_all_samplerates());
}
return 0;
}
int ff_default_query_formats(AVFilterContext *ctx)
{
return default_query_formats_common(ctx, ff_all_channel_layouts);
}
int ff_query_formats_all(AVFilterContext *ctx)
{
return default_query_formats_common(ctx, ff_all_channel_counts);
}
/* internal functions for parsing audio format arguments */
int ff_parse_pixel_format(enum AVPixelFormat *ret, const char *arg, void *log_ctx)
{
char *tail;
int pix_fmt = av_get_pix_fmt(arg);
if (pix_fmt == AV_PIX_FMT_NONE) {
pix_fmt = strtol(arg, &tail, 0);
if (*tail || (unsigned)pix_fmt >= AV_PIX_FMT_NB) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid pixel format '%s'\n", arg);
return AVERROR(EINVAL);
}
}
*ret = pix_fmt;
return 0;
}
int ff_parse_sample_format(int *ret, const char *arg, void *log_ctx)
{
char *tail;
int sfmt = av_get_sample_fmt(arg);
if (sfmt == AV_SAMPLE_FMT_NONE) {
sfmt = strtol(arg, &tail, 0);
if (*tail || (unsigned)sfmt >= AV_SAMPLE_FMT_NB) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid sample format '%s'\n", arg);
return AVERROR(EINVAL);
}
}
*ret = sfmt;
return 0;
}
int ff_parse_time_base(AVRational *ret, const char *arg, void *log_ctx)
{
AVRational r;
if(av_parse_ratio(&r, arg, INT_MAX, 0, log_ctx) < 0 ||r.num<=0 ||r.den<=0) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid time base '%s'\n", arg);
return AVERROR(EINVAL);
}
*ret = r;
return 0;
}
int ff_parse_sample_rate(int *ret, const char *arg, void *log_ctx)
{
char *tail;
double srate = av_strtod(arg, &tail);
if (*tail || srate < 1 || (int)srate != srate || srate > INT_MAX) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid sample rate '%s'\n", arg);
return AVERROR(EINVAL);
}
*ret = srate;
return 0;
}
int ff_parse_channel_layout(int64_t *ret, int *nret, const char *arg,
void *log_ctx)
{
char *tail;
int64_t chlayout, count;
if (nret) {
count = strtol(arg, &tail, 10);
if (*tail == 'c' && !tail[1] && count > 0 && count < 63) {
*nret = count;
*ret = 0;
return 0;
}
}
chlayout = av_get_channel_layout(arg);
if (chlayout == 0) {
chlayout = strtol(arg, &tail, 10);
if (*tail || chlayout == 0) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid channel layout '%s'\n", arg);
return AVERROR(EINVAL);
}
}
*ret = chlayout;
if (nret)
*nret = av_get_channel_layout_nb_channels(chlayout);
return 0;
}
#ifdef TEST
#undef printf
int main(void)
{
const int64_t *cl;
char buf[512];
for (cl = avfilter_all_channel_layouts; *cl != -1; cl++) {
av_get_channel_layout_string(buf, sizeof(buf), -1, *cl);
printf("%s\n", buf);
}
return 0;
}
#endif
+270
View File
@@ -0,0 +1,270 @@
/*
* 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"
/**
* A list of supported formats for one end of a filter link. This is used
* during the format negotiation process to try to pick the best format to
* use to minimize the number of necessary conversions. Each filter gives a
* list of the formats supported by each input and output pad. The list
* given for each pad need not be distinct - they may be references to the
* same list of formats, as is often the case when a filter supports multiple
* formats, but will always output the same format as it is given in input.
*
* In this way, a list of possible input formats and a list of possible
* output formats are associated with each link. When a set of formats is
* negotiated over a link, the input and output lists are merged to form a
* new list containing only the common elements of each list. In the case
* that there were no common elements, a format conversion is necessary.
* Otherwise, the lists are merged, and all other links which reference
* either of the format lists involved in the merge are also affected.
*
* For example, consider the filter chain:
* filter (a) --> (b) filter (b) --> (c) filter
*
* where the letters in parenthesis indicate a list of formats supported on
* the input or output of the link. Suppose the lists are as follows:
* (a) = {A, B}
* (b) = {A, B, C}
* (c) = {B, C}
*
* First, the first link's lists are merged, yielding:
* filter (a) --> (a) filter (a) --> (c) filter
*
* Notice that format list (b) now refers to the same list as filter list (a).
* Next, the lists for the second link are merged, yielding:
* filter (a) --> (a) filter (a) --> (a) filter
*
* where (a) = {B}.
*
* Unfortunately, when the format lists at the two ends of a link are merged,
* we must ensure that all links which reference either pre-merge format list
* get updated as well. Therefore, we have the format list structure store a
* pointer to each of the pointers to itself.
*/
struct AVFilterFormats {
unsigned nb_formats; ///< number of formats
int *formats; ///< list of media formats
unsigned refcount; ///< number of references to this list
struct AVFilterFormats ***refs; ///< references to this list
};
/**
* A list of supported channel layouts.
*
* The list works the same as AVFilterFormats, except for the following
* differences:
* - A list with all_layouts = 1 means all channel layouts with a known
* disposition; nb_channel_layouts must then be 0.
* - A list with all_counts = 1 means all channel counts, with a known or
* unknown disposition; nb_channel_layouts must then be 0 and all_layouts 1.
* - The list must not contain a layout with a known disposition and a
* channel count with unknown disposition with the same number of channels
* (e.g. AV_CH_LAYOUT_STEREO and FF_COUNT2LAYOUT(2).
*/
typedef struct AVFilterChannelLayouts {
uint64_t *channel_layouts; ///< list of channel layouts
int nb_channel_layouts; ///< number of channel layouts
char all_layouts; ///< accept any known channel layout
char all_counts; ///< accept any channel layout or count
unsigned refcount; ///< number of references to this list
struct AVFilterChannelLayouts ***refs; ///< references to this list
} AVFilterChannelLayouts;
/**
* Encode a channel count as a channel layout.
* FF_COUNT2LAYOUT(c) means any channel layout with c channels, with a known
* or unknown disposition.
* The result is only valid inside AVFilterChannelLayouts and immediately
* related functions.
*/
#define FF_COUNT2LAYOUT(c) (0x8000000000000000ULL | (c))
/**
* Decode a channel count encoded as a channel layout.
* Return 0 if the channel layout was a real one.
*/
#define FF_LAYOUT2COUNT(l) (((l) & 0x8000000000000000ULL) ? \
(int)((l) & 0x7FFFFFFF) : 0)
/**
* 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 (with known disposition)/sample rate.
*/
AVFilterChannelLayouts *ff_all_channel_layouts(void);
AVFilterFormats *ff_all_samplerates(void);
/**
* Construct an AVFilterChannelLayouts coding for any channel layout, with
* known or unknown disposition.
*/
AVFilterChannelLayouts *ff_all_channel_counts(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);
/**
* 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 ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats);
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);
int ff_default_query_formats(AVFilterContext *ctx);
/**
* Set the formats list to all existing formats.
* This function behaves like ff_default_query_formats(), except it also
* accepts channel layouts with unknown disposition. It should only be used
* with audio filters.
*/
int ff_query_formats_all(AVFilterContext *ctx);
/**
* Create a list of supported formats. This is intended for use in
* AVFilter->query_formats().
*
* @param fmts list of media formats, terminated by -1
* @return the format list, with no existing references
*/
AVFilterFormats *ff_make_format_list(const int *fmts);
/**
* Add fmt to the list of media formats contained in *avff.
* If *avff is NULL the function allocates the filter formats struct
* and puts its pointer in *avff.
*
* @return a non negative value in case of success, or a negative
* value corresponding to an AVERROR code in case of error
*/
int ff_add_format(AVFilterFormats **avff, int64_t fmt);
/**
* Return a list of all formats supported by FFmpeg for the given media type.
*/
AVFilterFormats *ff_all_formats(enum AVMediaType type);
/**
* Construct a formats list containing all planar sample formats.
*/
AVFilterFormats *ff_planar_sample_fmts(void);
/**
* Return a format list which contains the intersection of the formats 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 formats, neither is modified, and NULL
* is returned.
*/
AVFilterFormats *ff_merge_formats(AVFilterFormats *a, AVFilterFormats *b,
enum AVMediaType type);
/**
* Add *ref as a new reference to formats.
* That is the pointers will point like in the ascii art below:
* ________
* |formats |<--------.
* | ____ | ____|___________________
* | |refs| | | __|_
* | |* * | | | | | | AVFilterLink
* | |* *--------->|*ref|
* | |____| | | |____|
* |________| |________________________
*/
void ff_formats_ref(AVFilterFormats *formats, AVFilterFormats **ref);
/**
* If *ref is non-NULL, remove *ref as a reference to the format list
* it currently points to, deallocates that list if this was the last
* reference, and sets *ref to NULL.
*
* Before After
* ________ ________ NULL
* |formats |<--------. |formats | ^
* | ____ | ____|________________ | ____ | ____|________________
* | |refs| | | __|_ | |refs| | | __|_
* | |* * | | | | | | AVFilterLink | |* * | | | | | | AVFilterLink
* | |* *--------->|*ref| | |* | | | |*ref|
* | |____| | | |____| | |____| | | |____|
* |________| |_____________________ |________| |_____________________
*/
void ff_formats_unref(AVFilterFormats **ref);
/**
*
* Before After
* ________ ________
* |formats |<---------. |formats |<---------.
* | ____ | ___|___ | ____ | ___|___
* | |refs| | | | | | |refs| | | | | NULL
* | |* *--------->|*oldref| | |* *--------->|*newref| ^
* | |* * | | |_______| | |* * | | |_______| ___|___
* | |____| | | |____| | | | |
* |________| |________| |*oldref|
* |_______|
*/
void ff_formats_changeref(AVFilterFormats **oldref, AVFilterFormats **newref);
#endif /* AVFILTER_FORMATS_H */
+329
View File
@@ -0,0 +1,329 @@
/*
* Copyright (c) 2013 Nicolas George
*
* 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/avassert.h"
#include "avfilter.h"
#include "bufferqueue.h"
#include "framesync.h"
#include "internal.h"
#define OFFSET(member) offsetof(FFFrameSync, member)
static const char *framesync_name(void *ptr)
{
return "framesync";
}
static const AVClass framesync_class = {
.version = LIBAVUTIL_VERSION_INT,
.class_name = "framesync",
.item_name = framesync_name,
.category = AV_CLASS_CATEGORY_FILTER,
.option = NULL,
.parent_log_context_offset = OFFSET(parent),
};
enum {
STATE_BOF,
STATE_RUN,
STATE_EOF,
};
void ff_framesync_init(FFFrameSync *fs, void *parent, unsigned nb_in)
{
fs->class = &framesync_class;
fs->parent = parent;
fs->nb_in = nb_in;
}
static void framesync_sync_level_update(FFFrameSync *fs)
{
unsigned i, level = 0;
for (i = 0; i < fs->nb_in; i++)
if (fs->in[i].state != STATE_EOF)
level = FFMAX(level, fs->in[i].sync);
av_assert0(level <= fs->sync_level);
if (level < fs->sync_level)
av_log(fs, AV_LOG_VERBOSE, "Sync level %u\n", level);
if (level)
fs->sync_level = level;
else
fs->eof = 1;
}
int ff_framesync_configure(FFFrameSync *fs)
{
unsigned i;
int64_t gcd, lcm;
if (!fs->time_base.num) {
for (i = 0; i < fs->nb_in; i++) {
if (fs->in[i].sync) {
if (fs->time_base.num) {
gcd = av_gcd(fs->time_base.den, fs->in[i].time_base.den);
lcm = (fs->time_base.den / gcd) * fs->in[i].time_base.den;
if (lcm < AV_TIME_BASE / 2) {
fs->time_base.den = lcm;
fs->time_base.num = av_gcd(fs->time_base.num,
fs->in[i].time_base.num);
} else {
fs->time_base.num = 1;
fs->time_base.den = AV_TIME_BASE;
break;
}
} else {
fs->time_base = fs->in[i].time_base;
}
}
}
if (!fs->time_base.num) {
av_log(fs, AV_LOG_ERROR, "Impossible to set time base\n");
return AVERROR(EINVAL);
}
av_log(fs, AV_LOG_VERBOSE, "Selected %d/%d time base\n",
fs->time_base.num, fs->time_base.den);
}
for (i = 0; i < fs->nb_in; i++)
fs->in[i].pts = fs->in[i].pts_next = AV_NOPTS_VALUE;
fs->sync_level = UINT_MAX;
framesync_sync_level_update(fs);
return 0;
}
static void framesync_advance(FFFrameSync *fs)
{
int latest;
unsigned i;
int64_t pts;
if (fs->eof)
return;
while (!fs->frame_ready) {
latest = -1;
for (i = 0; i < fs->nb_in; i++) {
if (!fs->in[i].have_next) {
if (latest < 0 || fs->in[i].pts < fs->in[latest].pts)
latest = i;
}
}
if (latest >= 0) {
fs->in_request = latest;
break;
}
pts = fs->in[0].pts_next;
for (i = 1; i < fs->nb_in; i++)
if (fs->in[i].pts_next < pts)
pts = fs->in[i].pts_next;
if (pts == INT64_MAX) {
fs->eof = 1;
break;
}
for (i = 0; i < fs->nb_in; i++) {
if (fs->in[i].pts_next == pts ||
(fs->in[i].before == EXT_INFINITY &&
fs->in[i].state == STATE_BOF)) {
av_frame_free(&fs->in[i].frame);
fs->in[i].frame = fs->in[i].frame_next;
fs->in[i].pts = fs->in[i].pts_next;
fs->in[i].frame_next = NULL;
fs->in[i].pts_next = AV_NOPTS_VALUE;
fs->in[i].have_next = 0;
fs->in[i].state = fs->in[i].frame ? STATE_RUN : STATE_EOF;
if (fs->in[i].sync == fs->sync_level && fs->in[i].frame)
fs->frame_ready = 1;
if (fs->in[i].state == STATE_EOF &&
fs->in[i].after == EXT_STOP)
fs->eof = 1;
}
}
if (fs->eof)
fs->frame_ready = 0;
if (fs->frame_ready)
for (i = 0; i < fs->nb_in; i++)
if ((fs->in[i].state == STATE_BOF &&
fs->in[i].before == EXT_STOP))
fs->frame_ready = 0;
fs->pts = pts;
}
}
static int64_t framesync_pts_extrapolate(FFFrameSync *fs, unsigned in,
int64_t pts)
{
/* Possible enhancement: use the link's frame rate */
return pts + 1;
}
static void framesync_inject_frame(FFFrameSync *fs, unsigned in, AVFrame *frame)
{
int64_t pts;
av_assert0(!fs->in[in].have_next);
if (frame) {
pts = av_rescale_q(frame->pts, fs->in[in].time_base, fs->time_base);
frame->pts = pts;
} else {
pts = fs->in[in].state != STATE_RUN || fs->in[in].after == EXT_INFINITY
? INT64_MAX : framesync_pts_extrapolate(fs, in, fs->in[in].pts);
fs->in[in].sync = 0;
framesync_sync_level_update(fs);
}
fs->in[in].frame_next = frame;
fs->in[in].pts_next = pts;
fs->in[in].have_next = 1;
}
int ff_framesync_add_frame(FFFrameSync *fs, unsigned in, AVFrame *frame)
{
av_assert1(in < fs->nb_in);
if (!fs->in[in].have_next)
framesync_inject_frame(fs, in, frame);
else
ff_bufqueue_add(fs, &fs->in[in].queue, frame);
return 0;
}
void ff_framesync_next(FFFrameSync *fs)
{
unsigned i;
av_assert0(!fs->frame_ready);
for (i = 0; i < fs->nb_in; i++)
if (!fs->in[i].have_next && fs->in[i].queue.available)
framesync_inject_frame(fs, i, ff_bufqueue_get(&fs->in[i].queue));
fs->frame_ready = 0;
framesync_advance(fs);
}
void ff_framesync_drop(FFFrameSync *fs)
{
fs->frame_ready = 0;
}
int ff_framesync_get_frame(FFFrameSync *fs, unsigned in, AVFrame **rframe,
unsigned get)
{
AVFrame *frame;
unsigned need_copy = 0, i;
int64_t pts_next;
int ret;
if (!fs->in[in].frame) {
*rframe = NULL;
return 0;
}
frame = fs->in[in].frame;
if (get) {
/* Find out if we need to copy the frame: is there another sync
stream, and do we know if its current frame will outlast this one? */
pts_next = fs->in[in].have_next ? fs->in[in].pts_next : INT64_MAX;
for (i = 0; i < fs->nb_in && !need_copy; i++)
if (i != in && fs->in[i].sync &&
(!fs->in[i].have_next || fs->in[i].pts_next < pts_next))
need_copy = 1;
if (need_copy) {
if (!(frame = av_frame_clone(frame)))
return AVERROR(ENOMEM);
if ((ret = av_frame_make_writable(frame)) < 0) {
av_frame_free(&frame);
return ret;
}
} else {
fs->in[in].frame = NULL;
}
fs->frame_ready = 0;
}
*rframe = frame;
return 0;
}
void ff_framesync_uninit(FFFrameSync *fs)
{
unsigned i;
for (i = 0; i < fs->nb_in; i++) {
av_frame_free(&fs->in[i].frame);
av_frame_free(&fs->in[i].frame_next);
ff_bufqueue_discard_all(&fs->in[i].queue);
}
}
int ff_framesync_process_frame(FFFrameSync *fs, unsigned all)
{
int ret, count = 0;
av_assert0(fs->on_event);
while (1) {
ff_framesync_next(fs);
if (fs->eof || !fs->frame_ready)
break;
if ((ret = fs->on_event(fs)) < 0)
return ret;
ff_framesync_drop(fs);
count++;
if (!all)
break;
}
if (!count && fs->eof)
return AVERROR_EOF;
return count;
}
int ff_framesync_filter_frame(FFFrameSync *fs, AVFilterLink *inlink,
AVFrame *in)
{
int ret;
if ((ret = ff_framesync_process_frame(fs, 1)) < 0)
return ret;
if ((ret = ff_framesync_add_frame(fs, FF_INLINK_IDX(inlink), in)) < 0)
return ret;
if ((ret = ff_framesync_process_frame(fs, 0)) < 0)
return ret;
return 0;
}
int ff_framesync_request_frame(FFFrameSync *fs, AVFilterLink *outlink)
{
AVFilterContext *ctx = outlink->src;
int input, ret;
if ((ret = ff_framesync_process_frame(fs, 0)) < 0)
return ret;
if (ret > 0)
return 0;
if (fs->eof)
return AVERROR_EOF;
outlink->flags |= FF_LINK_FLAG_REQUEST_LOOP;
input = fs->in_request;
ret = ff_request_frame(ctx->inputs[input]);
if (ret == AVERROR_EOF) {
if ((ret = ff_framesync_add_frame(fs, input, NULL)) < 0)
return ret;
if ((ret = ff_framesync_process_frame(fs, 0)) < 0)
return ret;
ret = 0;
}
return ret;
}
+296
View File
@@ -0,0 +1,296 @@
/*
* Copyright (c) 2013 Nicolas George
*
* 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_FRAMESYNC_H
#define AVFILTER_FRAMESYNC_H
#include "bufferqueue.h"
/*
* TODO
* Callback-based API similar to dualinput.
* Export convenient options.
*/
/**
* This API is intended as a helper for filters that have several video
* input and need to combine them somehow. If the inputs have different or
* variable frame rate, getting the input frames to match requires a rather
* complex logic and a few user-tunable options.
*
* In this API, when a set of synchronized input frames is ready to be
* procesed is called a frame event. Frame event can be generated in
* response to input frames on any or all inputs and the handling of
* situations where some stream extend beyond the beginning or the end of
* others can be configured.
*
* The basic working of this API is the following:
*
* - When a frame is available on any input, add it using
* ff_framesync_add_frame().
*
* - When a frame event is ready to be processed (i.e. after adding a frame
* or when requested on input):
* - call ff_framesync_next();
* - if fs->frame_ready is true, process the frames;
* - call ff_framesync_drop().
*/
/**
* Stream extrapolation mode
*
* Describe how the frames of a stream are extrapolated before the first one
* and after EOF to keep sync with possibly longer other streams.
*/
enum FFFrameSyncExtMode {
/**
* Completely stop all streams with this one.
*/
EXT_STOP,
/**
* Ignore this stream and continue processing the other ones.
*/
EXT_NULL,
/**
* Extend the frame to infinity.
*/
EXT_INFINITY,
};
/**
* Input stream structure
*/
typedef struct FFFrameSyncIn {
/**
* Queue of incoming AVFrame, and NULL to mark EOF
*/
struct FFBufQueue queue;
/**
* Extrapolation mode for timestamps before the first frame
*/
enum FFFrameSyncExtMode before;
/**
* Extrapolation mode for timestamps after the last frame
*/
enum FFFrameSyncExtMode after;
/**
* Time base for the incoming frames
*/
AVRational time_base;
/**
* Current frame, may be NULL before the first one or after EOF
*/
AVFrame *frame;
/**
* Next frame, for internal use
*/
AVFrame *frame_next;
/**
* PTS of the current frame
*/
int64_t pts;
/**
* PTS of the next frame, for internal use
*/
int64_t pts_next;
/**
* Boolean flagging the next frame, for internal use
*/
uint8_t have_next;
/**
* State: before first, in stream or after EOF, for internal use
*/
uint8_t state;
/**
* Synchronization level: frames on input at the highest sync level will
* generate output frame events.
*
* For example, if inputs #0 and #1 have sync level 2 and input #2 has
* sync level 1, then a frame on either input #0 or #1 will generate a
* frame event, but not a frame on input #2 until both inputs #0 and #1
* have reached EOF.
*
* If sync is 0, no frame event will be generated.
*/
unsigned sync;
} FFFrameSyncIn;
/**
* Frame sync structure.
*/
typedef struct FFFrameSync {
const AVClass *class;
void *parent;
/**
* Number of input streams
*/
unsigned nb_in;
/**
* Time base for the output events
*/
AVRational time_base;
/**
* Timestamp of the current event
*/
int64_t pts;
/**
* Callback called when a frame event is ready
*/
int (*on_event)(struct FFFrameSync *fs);
/**
* Opaque pointer, not used by the API
*/
void *opaque;
/**
* Index of the input that requires a request
*/
unsigned in_request;
/**
* Synchronization level: only inputs with the same sync level are sync
* sources.
*/
unsigned sync_level;
/**
* Flag indicating that a frame event is ready
*/
uint8_t frame_ready;
/**
* Flag indicating that output has reached EOF.
*/
uint8_t eof;
/**
* Array of inputs; all inputs must be in consecutive memory
*/
FFFrameSyncIn in[1]; /* must be the last field */
} FFFrameSync;
/**
* Initialize a frame sync structure.
*
* The entire structure is expected to be already set to 0.
*
* @param fs frame sync structure to initialize
* @param parent parent object, used for logging
* @param nb_in number of inputs
*/
void ff_framesync_init(FFFrameSync *fs, void *parent, unsigned nb_in);
/**
* Configure a frame sync structure.
*
* Must be called after all options are set but before all use.
*
* @return >= 0 for success or a negative error code
*/
int ff_framesync_configure(FFFrameSync *fs);
/**
* Free all memory currently allocated.
*/
void ff_framesync_uninit(FFFrameSync *fs);
/**
* Add a frame to an input
*
* Typically called from the filter_frame() method.
*
* @param fs frame sync structure
* @param in index of the input
* @param frame input frame, or NULL for EOF
*/
int ff_framesync_add_frame(FFFrameSync *fs, unsigned in, AVFrame *frame);
/**
* Prepare the next frame event.
*
* The status of the operation can be found in fs->frame_ready and fs->eof.
*/
void ff_framesync_next(FFFrameSync *fs);
/**
* Drop the current frame event.
*/
void ff_framesync_drop(FFFrameSync *fs);
/**
* Get the current frame in an input.
*
* @param fs frame sync structure
* @param in index of the input
* @param rframe used to return the current frame (or NULL)
* @param get if not zero, the calling code needs to get ownership of
* the returned frame; the current frame will either be
* duplicated or removed from the framesync structure
*/
int ff_framesync_get_frame(FFFrameSync *fs, unsigned in, AVFrame **rframe,
unsigned get);
/**
* Process one or several frame using the on_event callback.
*
* @return number of frames processed or negative error code
*/
int ff_framesync_process_frame(FFFrameSync *fs, unsigned all);
/**
* Accept a frame on a filter input.
*
* This function can be the complete implementation of all filter_frame
* methods of a filter using framesync.
*/
int ff_framesync_filter_frame(FFFrameSync *fs, AVFilterLink *inlink,
AVFrame *in);
/**
* Request a frame on the filter output.
*
* This function can be the complete implementation of all filter_frame
* methods of a filter using framesync if it has only one output.
*/
int ff_framesync_request_frame(FFFrameSync *fs, AVFilterLink *outlink);
#endif /* AVFILTER_FRAMESYNC_H */
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2010 Nolan Lum <[email protected]>
* Copyright (c) 2009 Loren Merritt <[email protected]>
*
* 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_GRADFUN_H
#define AVFILTER_GRADFUN_H
#include "avfilter.h"
/// Holds instance-specific information for gradfun.
typedef struct GradFunContext {
const AVClass *class;
float strength;
int thresh; ///< threshold for gradient algorithm
int radius; ///< blur radius
int chroma_w; ///< width of the chroma planes
int chroma_h; ///< weight of the chroma planes
int chroma_r; ///< blur radius for the chroma planes
uint16_t *buf; ///< holds image data for blur algorithm passed into filter.
/// DSP functions.
void (*filter_line) (uint8_t *dst, const uint8_t *src, const uint16_t *dc, int width, int thresh, const uint16_t *dithers);
void (*blur_line) (uint16_t *dc, uint16_t *buf, const uint16_t *buf1, const uint8_t *src, int src_linesize, int width);
} GradFunContext;
void ff_gradfun_init_x86(GradFunContext *gf);
void ff_gradfun_filter_line_c(uint8_t *dst, const uint8_t *src, const uint16_t *dc, int width, int thresh, const uint16_t *dithers);
void ff_gradfun_blur_line_c(uint16_t *dc, uint16_t *buf, const uint16_t *buf1, const uint8_t *src, int src_linesize, int width);
#endif /* AVFILTER_GRADFUN_H */
+164
View File
@@ -0,0 +1,164 @@
/*
* Filter graphs to bad ASCII-art
* Copyright (c) 2012 Nicolas George
*
* 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 <string.h>
#include "libavutil/channel_layout.h"
#include "libavutil/bprint.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "avfiltergraph.h"
static int print_link_prop(AVBPrint *buf, AVFilterLink *link)
{
char *format;
char layout[64];
if (!buf)
buf = &(AVBPrint){ 0 }; /* dummy buffer */
switch (link->type) {
case AVMEDIA_TYPE_VIDEO:
format = av_x_if_null(av_get_pix_fmt_name(link->format), "?");
av_bprintf(buf, "[%dx%d %d:%d %s]", link->w, link->h,
link->sample_aspect_ratio.num,
link->sample_aspect_ratio.den,
format);
break;
case AVMEDIA_TYPE_AUDIO:
av_get_channel_layout_string(layout, sizeof(layout),
link->channels, link->channel_layout);
format = av_x_if_null(av_get_sample_fmt_name(link->format), "?");
av_bprintf(buf, "[%dHz %s:%s]",
(int)link->sample_rate, format, layout);
break;
default:
av_bprintf(buf, "?");
break;
}
return buf->len;
}
static void avfilter_graph_dump_to_buf(AVBPrint *buf, AVFilterGraph *graph)
{
unsigned i, j, x, e;
for (i = 0; i < graph->nb_filters; i++) {
AVFilterContext *filter = graph->filters[i];
unsigned max_src_name = 0, max_dst_name = 0;
unsigned max_in_name = 0, max_out_name = 0;
unsigned max_in_fmt = 0, max_out_fmt = 0;
unsigned width, height, in_indent;
unsigned lname = strlen(filter->name);
unsigned ltype = strlen(filter->filter->name);
for (j = 0; j < filter->nb_inputs; j++) {
AVFilterLink *l = filter->inputs[j];
unsigned ln = strlen(l->src->name) + 1 + strlen(l->srcpad->name);
max_src_name = FFMAX(max_src_name, ln);
max_in_name = FFMAX(max_in_name, strlen(l->dstpad->name));
max_in_fmt = FFMAX(max_in_fmt, print_link_prop(NULL, l));
}
for (j = 0; j < filter->nb_outputs; j++) {
AVFilterLink *l = filter->outputs[j];
unsigned ln = strlen(l->dst->name) + 1 + strlen(l->dstpad->name);
max_dst_name = FFMAX(max_dst_name, ln);
max_out_name = FFMAX(max_out_name, strlen(l->srcpad->name));
max_out_fmt = FFMAX(max_out_fmt, print_link_prop(NULL, l));
}
in_indent = max_src_name + max_in_name + max_in_fmt;
in_indent += in_indent ? 4 : 0;
width = FFMAX(lname + 2, ltype + 4);
height = FFMAX3(2, filter->nb_inputs, filter->nb_outputs);
av_bprint_chars(buf, ' ', in_indent);
av_bprintf(buf, "+");
av_bprint_chars(buf, '-', width);
av_bprintf(buf, "+\n");
for (j = 0; j < height; j++) {
unsigned in_no = j - (height - filter->nb_inputs ) / 2;
unsigned out_no = j - (height - filter->nb_outputs) / 2;
/* Input link */
if (in_no < filter->nb_inputs) {
AVFilterLink *l = filter->inputs[in_no];
e = buf->len + max_src_name + 2;
av_bprintf(buf, "%s:%s", l->src->name, l->srcpad->name);
av_bprint_chars(buf, '-', e - buf->len);
e = buf->len + max_in_fmt + 2 +
max_in_name - strlen(l->dstpad->name);
print_link_prop(buf, l);
av_bprint_chars(buf, '-', e - buf->len);
av_bprintf(buf, "%s", l->dstpad->name);
} else {
av_bprint_chars(buf, ' ', in_indent);
}
/* Filter */
av_bprintf(buf, "|");
if (j == (height - 2) / 2) {
x = (width - lname) / 2;
av_bprintf(buf, "%*s%-*s", x, "", width - x, filter->name);
} else if (j == (height - 2) / 2 + 1) {
x = (width - ltype - 2) / 2;
av_bprintf(buf, "%*s(%s)%*s", x, "", filter->filter->name,
width - ltype - 2 - x, "");
} else {
av_bprint_chars(buf, ' ', width);
}
av_bprintf(buf, "|");
/* Output link */
if (out_no < filter->nb_outputs) {
AVFilterLink *l = filter->outputs[out_no];
unsigned ln = strlen(l->dst->name) + 1 +
strlen(l->dstpad->name);
e = buf->len + max_out_name + 2;
av_bprintf(buf, "%s", l->srcpad->name);
av_bprint_chars(buf, '-', e - buf->len);
e = buf->len + max_out_fmt + 2 +
max_dst_name - ln;
print_link_prop(buf, l);
av_bprint_chars(buf, '-', e - buf->len);
av_bprintf(buf, "%s:%s", l->dst->name, l->dstpad->name);
}
av_bprintf(buf, "\n");
}
av_bprint_chars(buf, ' ', in_indent);
av_bprintf(buf, "+");
av_bprint_chars(buf, '-', width);
av_bprintf(buf, "+\n");
av_bprintf(buf, "\n");
}
}
char *avfilter_graph_dump(AVFilterGraph *graph, const char *options)
{
AVBPrint buf;
char *dump;
av_bprint_init(&buf, 0, 0);
avfilter_graph_dump_to_buf(&buf, graph);
av_bprint_init(&buf, buf.len + 1, buf.len + 1);
avfilter_graph_dump_to_buf(&buf, graph);
av_bprint_finalize(&buf, &dump);
return dump;
}
+605
View File
@@ -0,0 +1,605 @@
/*
* filter graph parser
* Copyright (c) 2008 Vitor Sessak
* Copyright (c) 2007 Bobby Bingham
*
* 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 <string.h>
#include <stdio.h>
#include "libavutil/avstring.h"
#include "libavutil/mem.h"
#include "avfilter.h"
#define WHITESPACES " \n\t"
/**
* Link two filters together.
*
* @see avfilter_link()
*/
static int link_filter(AVFilterContext *src, int srcpad,
AVFilterContext *dst, int dstpad,
void *log_ctx)
{
int ret;
if ((ret = avfilter_link(src, srcpad, dst, dstpad))) {
av_log(log_ctx, AV_LOG_ERROR,
"Cannot create the link %s:%d -> %s:%d\n",
src->filter->name, srcpad, dst->filter->name, dstpad);
return ret;
}
return 0;
}
/**
* Parse the name of a link, which has the format "[linkname]".
*
* @return a pointer (that need to be freed after use) to the name
* between parenthesis
*/
static char *parse_link_name(const char **buf, void *log_ctx)
{
const char *start = *buf;
char *name;
(*buf)++;
name = av_get_token(buf, "]");
if (!name[0]) {
av_log(log_ctx, AV_LOG_ERROR,
"Bad (empty?) label found in the following: \"%s\".\n", start);
goto fail;
}
if (*(*buf)++ != ']') {
av_log(log_ctx, AV_LOG_ERROR,
"Mismatched '[' found in the following: \"%s\".\n", start);
fail:
av_freep(&name);
}
return name;
}
/**
* Create an instance of a filter, initialize and insert it in the
* filtergraph in *ctx.
*
* @param filt_ctx put here a filter context in case of successful creation and configuration, NULL otherwise.
* @param ctx the filtergraph context
* @param index an index which is supposed to be unique for each filter instance added to the filtergraph
* @param filt_name the name of the filter to create
* @param args the arguments provided to the filter during its initialization
* @param log_ctx the log context to use
* @return >= 0 in case of success, a negative AVERROR code otherwise
*/
static int create_filter(AVFilterContext **filt_ctx, AVFilterGraph *ctx, int index,
const char *filt_name, const char *args, void *log_ctx)
{
AVFilter *filt;
char inst_name[30];
char *tmp_args = NULL;
int ret;
snprintf(inst_name, sizeof(inst_name), "Parsed_%s_%d", filt_name, index);
filt = avfilter_get_by_name(filt_name);
if (!filt) {
av_log(log_ctx, AV_LOG_ERROR,
"No such filter: '%s'\n", filt_name);
return AVERROR(EINVAL);
}
*filt_ctx = avfilter_graph_alloc_filter(ctx, filt, inst_name);
if (!*filt_ctx) {
av_log(log_ctx, AV_LOG_ERROR,
"Error creating filter '%s'\n", filt_name);
return AVERROR(ENOMEM);
}
if (!strcmp(filt_name, "scale") && args && !strstr(args, "flags") &&
ctx->scale_sws_opts) {
tmp_args = av_asprintf("%s:%s",
args, ctx->scale_sws_opts);
if (!tmp_args)
return AVERROR(ENOMEM);
args = tmp_args;
}
ret = avfilter_init_str(*filt_ctx, args);
if (ret < 0) {
av_log(log_ctx, AV_LOG_ERROR,
"Error initializing filter '%s'", filt_name);
if (args)
av_log(log_ctx, AV_LOG_ERROR, " with args '%s'", args);
av_log(log_ctx, AV_LOG_ERROR, "\n");
}
av_free(tmp_args);
return ret;
}
/**
* Parse a string of the form FILTER_NAME[=PARAMS], and create a
* corresponding filter instance which is added to graph with
* create_filter().
*
* @param filt_ctx Pointer that is set to the created and configured filter
* context on success, set to NULL on failure.
* @param filt_ctx put here a pointer to the created filter context on
* success, NULL otherwise
* @param buf pointer to the buffer to parse, *buf will be updated to
* point to the char next after the parsed string
* @param index an index which is assigned to the created filter
* instance, and which is supposed to be unique for each filter
* instance added to the filtergraph
* @return >= 0 in case of success, a negative AVERROR code otherwise
*/
static int parse_filter(AVFilterContext **filt_ctx, const char **buf, AVFilterGraph *graph,
int index, void *log_ctx)
{
char *opts = NULL;
char *name = av_get_token(buf, "=,;[\n");
int ret;
if (**buf == '=') {
(*buf)++;
opts = av_get_token(buf, "[],;\n");
}
ret = create_filter(filt_ctx, graph, index, name, opts, log_ctx);
av_free(name);
av_free(opts);
return ret;
}
AVFilterInOut *avfilter_inout_alloc(void)
{
return av_mallocz(sizeof(AVFilterInOut));
}
void avfilter_inout_free(AVFilterInOut **inout)
{
while (*inout) {
AVFilterInOut *next = (*inout)->next;
av_freep(&(*inout)->name);
av_freep(inout);
*inout = next;
}
}
static AVFilterInOut *extract_inout(const char *label, AVFilterInOut **links)
{
AVFilterInOut *ret;
while (*links && (!(*links)->name || strcmp((*links)->name, label)))
links = &((*links)->next);
ret = *links;
if (ret) {
*links = ret->next;
ret->next = NULL;
}
return ret;
}
static void insert_inout(AVFilterInOut **inouts, AVFilterInOut *element)
{
element->next = *inouts;
*inouts = element;
}
static void append_inout(AVFilterInOut **inouts, AVFilterInOut **element)
{
while (*inouts && (*inouts)->next)
inouts = &((*inouts)->next);
if (!*inouts)
*inouts = *element;
else
(*inouts)->next = *element;
*element = NULL;
}
static int link_filter_inouts(AVFilterContext *filt_ctx,
AVFilterInOut **curr_inputs,
AVFilterInOut **open_inputs, void *log_ctx)
{
int pad, ret;
for (pad = 0; pad < filt_ctx->nb_inputs; pad++) {
AVFilterInOut *p = *curr_inputs;
if (p) {
*curr_inputs = (*curr_inputs)->next;
p->next = NULL;
} else if (!(p = av_mallocz(sizeof(*p))))
return AVERROR(ENOMEM);
if (p->filter_ctx) {
ret = link_filter(p->filter_ctx, p->pad_idx, filt_ctx, pad, log_ctx);
av_free(p->name);
av_free(p);
if (ret < 0)
return ret;
} else {
p->filter_ctx = filt_ctx;
p->pad_idx = pad;
append_inout(open_inputs, &p);
}
}
if (*curr_inputs) {
av_log(log_ctx, AV_LOG_ERROR,
"Too many inputs specified for the \"%s\" filter.\n",
filt_ctx->filter->name);
return AVERROR(EINVAL);
}
pad = filt_ctx->nb_outputs;
while (pad--) {
AVFilterInOut *currlinkn = av_mallocz(sizeof(AVFilterInOut));
if (!currlinkn)
return AVERROR(ENOMEM);
currlinkn->filter_ctx = filt_ctx;
currlinkn->pad_idx = pad;
insert_inout(curr_inputs, currlinkn);
}
return 0;
}
static int parse_inputs(const char **buf, AVFilterInOut **curr_inputs,
AVFilterInOut **open_outputs, void *log_ctx)
{
AVFilterInOut *parsed_inputs = NULL;
int pad = 0;
while (**buf == '[') {
char *name = parse_link_name(buf, log_ctx);
AVFilterInOut *match;
if (!name)
return AVERROR(EINVAL);
/* First check if the label is not in the open_outputs list */
match = extract_inout(name, open_outputs);
if (match) {
av_free(name);
} else {
/* Not in the list, so add it as an input */
if (!(match = av_mallocz(sizeof(AVFilterInOut)))) {
av_free(name);
return AVERROR(ENOMEM);
}
match->name = name;
match->pad_idx = pad;
}
append_inout(&parsed_inputs, &match);
*buf += strspn(*buf, WHITESPACES);
pad++;
}
append_inout(&parsed_inputs, curr_inputs);
*curr_inputs = parsed_inputs;
return pad;
}
static int parse_outputs(const char **buf, AVFilterInOut **curr_inputs,
AVFilterInOut **open_inputs,
AVFilterInOut **open_outputs, void *log_ctx)
{
int ret, pad = 0;
while (**buf == '[') {
char *name = parse_link_name(buf, log_ctx);
AVFilterInOut *match;
AVFilterInOut *input = *curr_inputs;
if (!name)
return AVERROR(EINVAL);
if (!input) {
av_log(log_ctx, AV_LOG_ERROR,
"No output pad can be associated to link label '%s'.\n", name);
av_free(name);
return AVERROR(EINVAL);
}
*curr_inputs = (*curr_inputs)->next;
/* First check if the label is not in the open_inputs list */
match = extract_inout(name, open_inputs);
if (match) {
if ((ret = link_filter(input->filter_ctx, input->pad_idx,
match->filter_ctx, match->pad_idx, log_ctx)) < 0) {
av_free(name);
return ret;
}
av_free(match->name);
av_free(name);
av_free(match);
av_free(input);
} else {
/* Not in the list, so add the first input as a open_output */
input->name = name;
insert_inout(open_outputs, input);
}
*buf += strspn(*buf, WHITESPACES);
pad++;
}
return pad;
}
static int parse_sws_flags(const char **buf, AVFilterGraph *graph)
{
char *p = strchr(*buf, ';');
if (strncmp(*buf, "sws_flags=", 10))
return 0;
if (!p) {
av_log(graph, AV_LOG_ERROR, "sws_flags not terminated with ';'.\n");
return AVERROR(EINVAL);
}
*buf += 4; // keep the 'flags=' part
av_freep(&graph->scale_sws_opts);
if (!(graph->scale_sws_opts = av_mallocz(p - *buf + 1)))
return AVERROR(ENOMEM);
av_strlcpy(graph->scale_sws_opts, *buf, p - *buf + 1);
*buf = p + 1;
return 0;
}
int avfilter_graph_parse2(AVFilterGraph *graph, const char *filters,
AVFilterInOut **inputs,
AVFilterInOut **outputs)
{
int index = 0, ret = 0;
char chr = 0;
AVFilterInOut *curr_inputs = NULL, *open_inputs = NULL, *open_outputs = NULL;
filters += strspn(filters, WHITESPACES);
if ((ret = parse_sws_flags(&filters, graph)) < 0)
goto fail;
do {
AVFilterContext *filter;
filters += strspn(filters, WHITESPACES);
if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, graph)) < 0)
goto end;
if ((ret = parse_filter(&filter, &filters, graph, index, graph)) < 0)
goto end;
if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, graph)) < 0)
goto end;
if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
graph)) < 0)
goto end;
filters += strspn(filters, WHITESPACES);
chr = *filters++;
if (chr == ';' && curr_inputs)
append_inout(&open_outputs, &curr_inputs);
index++;
} while (chr == ',' || chr == ';');
if (chr) {
av_log(graph, AV_LOG_ERROR,
"Unable to parse graph description substring: \"%s\"\n",
filters - 1);
ret = AVERROR(EINVAL);
goto end;
}
append_inout(&open_outputs, &curr_inputs);
*inputs = open_inputs;
*outputs = open_outputs;
return 0;
fail:end:
while (graph->nb_filters)
avfilter_free(graph->filters[0]);
av_freep(&graph->filters);
avfilter_inout_free(&open_inputs);
avfilter_inout_free(&open_outputs);
avfilter_inout_free(&curr_inputs);
*inputs = NULL;
*outputs = NULL;
return ret;
}
#if HAVE_INCOMPATIBLE_LIBAV_ABI || !FF_API_OLD_GRAPH_PARSE
int avfilter_graph_parse(AVFilterGraph *graph, const char *filters,
AVFilterInOut *open_inputs,
AVFilterInOut *open_outputs, void *log_ctx)
{
int ret;
AVFilterInOut *cur, *match, *inputs = NULL, *outputs = NULL;
if ((ret = avfilter_graph_parse2(graph, filters, &inputs, &outputs)) < 0)
goto fail;
/* First input can be omitted if it is "[in]" */
if (inputs && !inputs->name)
inputs->name = av_strdup("in");
for (cur = inputs; cur; cur = cur->next) {
if (!cur->name) {
av_log(log_ctx, AV_LOG_ERROR,
"Not enough inputs specified for the \"%s\" filter.\n",
cur->filter_ctx->filter->name);
ret = AVERROR(EINVAL);
goto fail;
}
if (!(match = extract_inout(cur->name, &open_outputs)))
continue;
ret = avfilter_link(match->filter_ctx, match->pad_idx,
cur->filter_ctx, cur->pad_idx);
avfilter_inout_free(&match);
if (ret < 0)
goto fail;
}
/* Last output can be omitted if it is "[out]" */
if (outputs && !outputs->name)
outputs->name = av_strdup("out");
for (cur = outputs; cur; cur = cur->next) {
if (!cur->name) {
av_log(log_ctx, AV_LOG_ERROR,
"Invalid filterchain containing an unlabelled output pad: \"%s\"\n",
filters);
ret = AVERROR(EINVAL);
goto fail;
}
if (!(match = extract_inout(cur->name, &open_inputs)))
continue;
ret = avfilter_link(cur->filter_ctx, cur->pad_idx,
match->filter_ctx, match->pad_idx);
avfilter_inout_free(&match);
if (ret < 0)
goto fail;
}
fail:
if (ret < 0) {
while (graph->nb_filters)
avfilter_free(graph->filters[0]);
av_freep(&graph->filters);
}
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
avfilter_inout_free(&open_inputs);
avfilter_inout_free(&open_outputs);
return ret;
#else
int avfilter_graph_parse(AVFilterGraph *graph, const char *filters,
AVFilterInOut **inputs, AVFilterInOut **outputs,
void *log_ctx)
{
return avfilter_graph_parse_ptr(graph, filters, inputs, outputs, log_ctx);
#endif
}
int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters,
AVFilterInOut **open_inputs_ptr, AVFilterInOut **open_outputs_ptr,
void *log_ctx)
{
int index = 0, ret = 0;
char chr = 0;
AVFilterInOut *curr_inputs = NULL;
AVFilterInOut *open_inputs = open_inputs_ptr ? *open_inputs_ptr : NULL;
AVFilterInOut *open_outputs = open_outputs_ptr ? *open_outputs_ptr : NULL;
if ((ret = parse_sws_flags(&filters, graph)) < 0)
goto end;
do {
AVFilterContext *filter;
const char *filterchain = filters;
filters += strspn(filters, WHITESPACES);
if ((ret = parse_inputs(&filters, &curr_inputs, &open_outputs, log_ctx)) < 0)
goto end;
if ((ret = parse_filter(&filter, &filters, graph, index, log_ctx)) < 0)
goto end;
if (filter->nb_inputs == 1 && !curr_inputs && !index) {
/* First input pad, assume it is "[in]" if not specified */
const char *tmp = "[in]";
if ((ret = parse_inputs(&tmp, &curr_inputs, &open_outputs, log_ctx)) < 0)
goto end;
}
if ((ret = link_filter_inouts(filter, &curr_inputs, &open_inputs, log_ctx)) < 0)
goto end;
if ((ret = parse_outputs(&filters, &curr_inputs, &open_inputs, &open_outputs,
log_ctx)) < 0)
goto end;
filters += strspn(filters, WHITESPACES);
chr = *filters++;
if (chr == ';' && curr_inputs) {
av_log(log_ctx, AV_LOG_ERROR,
"Invalid filterchain containing an unlabelled output pad: \"%s\"\n",
filterchain);
ret = AVERROR(EINVAL);
goto end;
}
index++;
} while (chr == ',' || chr == ';');
if (chr) {
av_log(log_ctx, AV_LOG_ERROR,
"Unable to parse graph description substring: \"%s\"\n",
filters - 1);
ret = AVERROR(EINVAL);
goto end;
}
if (curr_inputs) {
/* Last output pad, assume it is "[out]" if not specified */
const char *tmp = "[out]";
if ((ret = parse_outputs(&tmp, &curr_inputs, &open_inputs, &open_outputs,
log_ctx)) < 0)
goto end;
}
end:
/* clear open_in/outputs only if not passed as parameters */
if (open_inputs_ptr) *open_inputs_ptr = open_inputs;
else avfilter_inout_free(&open_inputs);
if (open_outputs_ptr) *open_outputs_ptr = open_outputs;
else avfilter_inout_free(&open_outputs);
avfilter_inout_free(&curr_inputs);
if (ret < 0) {
while (graph->nb_filters)
avfilter_free(graph->filters[0]);
av_freep(&graph->filters);
}
return ret;
}
+369
View File
@@ -0,0 +1,369 @@
/*
* 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_INTERNAL_H
#define AVFILTER_INTERNAL_H
/**
* @file
* internal API functions
*/
#include "libavutil/internal.h"
#include "avfilter.h"
#include "avfiltergraph.h"
#include "formats.h"
#include "thread.h"
#include "version.h"
#include "video.h"
#define POOL_SIZE 32
typedef struct AVFilterPool {
AVFilterBufferRef *pic[POOL_SIZE];
int count;
int refcount;
int draining;
} AVFilterPool;
typedef struct AVFilterCommand {
double time; ///< time expressed in seconds
char *command; ///< command
char *arg; ///< optional argument for the command
int flags;
struct AVFilterCommand *next;
} AVFilterCommand;
/**
* Update the position of a link in the age heap.
*/
void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link);
#if !FF_API_AVFILTERPAD_PUBLIC
/**
* A filter pad used for either input or output.
*/
struct AVFilterPad {
/**
* Pad name. The name is unique among inputs and among outputs, but an
* input may have the same name as an output. This may be NULL if this
* pad has no need to ever be referenced by name.
*/
const char *name;
/**
* AVFilterPad type.
*/
enum AVMediaType type;
/**
* Callback function to get a video buffer. If NULL, the filter system will
* use ff_default_get_video_buffer().
*
* Input video pads only.
*/
AVFrame *(*get_video_buffer)(AVFilterLink *link, int w, int h);
/**
* Callback function to get an audio buffer. If NULL, the filter system will
* use ff_default_get_audio_buffer().
*
* Input audio pads only.
*/
AVFrame *(*get_audio_buffer)(AVFilterLink *link, int nb_samples);
/**
* Filtering callback. This is where a filter receives a frame with
* audio/video data and should do its processing.
*
* Input pads only.
*
* @return >= 0 on success, a negative AVERROR on error. This function
* must ensure that samplesref is properly unreferenced on error if it
* hasn't been passed on to another filter.
*/
int (*filter_frame)(AVFilterLink *link, AVFrame *frame);
/**
* Frame poll callback. This returns the number of immediately available
* samples. It should return a positive value if the next request_frame()
* is guaranteed to return one frame (with no delay).
*
* Defaults to just calling the source poll_frame() method.
*
* Output pads only.
*/
int (*poll_frame)(AVFilterLink *link);
/**
* Frame request callback. A call to this should result in at least one
* frame being output over the given link. This should return zero on
* success, and another value on error.
*
* Output pads only.
*/
int (*request_frame)(AVFilterLink *link);
/**
* Link configuration callback.
*
* For output pads, this should set the link properties such as
* width/height. This should NOT set the format property - that is
* negotiated between filters by the filter system using the
* query_formats() callback before this function is called.
*
* For input pads, this should check the properties of the link, and update
* the filter's internal state as necessary.
*
* For both input and output filters, this should return zero on success,
* 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
struct AVFilterGraphInternal {
void *thread;
avfilter_execute_func *thread_execute;
};
struct AVFilterInternal {
avfilter_execute_func *execute;
};
#if FF_API_AVFILTERBUFFER
/** default handler for freeing audio/video buffer when there are no references left */
void ff_avfilter_default_free_buffer(AVFilterBuffer *buf);
#endif
/** Tell is a format is contained in the provided list terminated by -1. */
int ff_fmt_is_in(int fmt, const int *fmts);
/* Functions to parse audio format arguments */
/**
* Parse a pixel format.
*
* @param ret pixel format pointer to where the value should be written
* @param arg string to parse
* @param log_ctx log context
* @return >= 0 in case of success, a negative AVERROR code on error
*/
int ff_parse_pixel_format(enum AVPixelFormat *ret, const char *arg, void *log_ctx);
/**
* Parse a sample rate.
*
* @param ret unsigned integer pointer to where the value should be written
* @param arg string to parse
* @param log_ctx log context
* @return >= 0 in case of success, a negative AVERROR code on error
*/
int ff_parse_sample_rate(int *ret, const char *arg, void *log_ctx);
/**
* Parse a time base.
*
* @param ret unsigned AVRational pointer to where the value should be written
* @param arg string to parse
* @param log_ctx log context
* @return >= 0 in case of success, a negative AVERROR code on error
*/
int ff_parse_time_base(AVRational *ret, const char *arg, void *log_ctx);
/**
* Parse a sample format name or a corresponding integer representation.
*
* @param ret integer pointer to where the value should be written
* @param arg string to parse
* @param log_ctx log context
* @return >= 0 in case of success, a negative AVERROR code on error
*/
int ff_parse_sample_format(int *ret, const char *arg, void *log_ctx);
/**
* Parse a channel layout or a corresponding integer representation.
*
* @param ret 64bit integer pointer to where the value should be written.
* @param nret integer pointer to the number of channels;
* if not NULL, then unknown channel layouts are accepted
* @param arg string to parse
* @param log_ctx log context
* @return >= 0 in case of success, a negative AVERROR code on error
*/
int ff_parse_channel_layout(int64_t *ret, int *nret, const char *arg,
void *log_ctx);
void ff_update_link_current_pts(AVFilterLink *link, int64_t pts);
void ff_command_queue_pop(AVFilterContext *filter);
/* misc trace functions */
/* #define FF_AVFILTER_TRACE */
#ifdef FF_AVFILTER_TRACE
# define ff_tlog(pctx, ...) av_log(pctx, AV_LOG_DEBUG, __VA_ARGS__)
#else
# define ff_tlog(pctx, ...) do { if (0) av_log(pctx, AV_LOG_DEBUG, __VA_ARGS__); } while (0)
#endif
#define FF_TPRINTF_START(ctx, func) ff_tlog(NULL, "%-16s: ", #func)
char *ff_get_ref_perms_string(char *buf, size_t buf_size, int perms);
void ff_tlog_ref(void *ctx, AVFrame *ref, int end);
void ff_tlog_link(void *ctx, AVFilterLink *link, int end);
/**
* Insert a new pad.
*
* @param idx Insertion point. Pad is inserted at the end if this point
* is beyond the end of the list of pads.
* @param count Pointer to the number of pads in the list
* @param padidx_off Offset within an AVFilterLink structure to the element
* to increment when inserting a new pad causes link
* numbering to change
* @param pads Pointer to the pointer to the beginning of the list of pads
* @param links Pointer to the pointer to the beginning of the list of links
* @param newpad The new pad to add. A copy is made when adding.
* @return >= 0 in case of success, a negative AVERROR code on error
*/
int ff_insert_pad(unsigned idx, unsigned *count, size_t padidx_off,
AVFilterPad **pads, AVFilterLink ***links,
AVFilterPad *newpad);
/** Insert a new input pad for the filter. */
static inline int ff_insert_inpad(AVFilterContext *f, unsigned index,
AVFilterPad *p)
{
int ret = ff_insert_pad(index, &f->nb_inputs, offsetof(AVFilterLink, dstpad),
&f->input_pads, &f->inputs, p);
#if FF_API_FOO_COUNT
FF_DISABLE_DEPRECATION_WARNINGS
f->input_count = f->nb_inputs;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
return ret;
}
/** Insert a new output pad for the filter. */
static inline int ff_insert_outpad(AVFilterContext *f, unsigned index,
AVFilterPad *p)
{
int ret = ff_insert_pad(index, &f->nb_outputs, offsetof(AVFilterLink, srcpad),
&f->output_pads, &f->outputs, p);
#if FF_API_FOO_COUNT
FF_DISABLE_DEPRECATION_WARNINGS
f->output_count = f->nb_outputs;
FF_ENABLE_DEPRECATION_WARNINGS
#endif
return ret;
}
/**
* Poll a frame from the filter chain.
*
* @param link the input link
* @return the number of immediately available frames, a negative
* number in case of error
*/
int ff_poll_frame(AVFilterLink *link);
/**
* Request an input frame from the filter at the other end of the link.
*
* @param link the input link
* @return zero on success
*/
int ff_request_frame(AVFilterLink *link);
#define AVFILTER_DEFINE_CLASS(fname) \
static const AVClass fname##_class = { \
.class_name = #fname, \
.item_name = av_default_item_name, \
.option = fname##_options, \
.version = LIBAVUTIL_VERSION_INT, \
.category = AV_CLASS_CATEGORY_FILTER, \
}
AVFilterBufferRef *ff_copy_buffer_ref(AVFilterLink *outlink,
AVFilterBufferRef *ref);
/**
* Find the index of a link.
*
* I.e. find i such that link == ctx->(in|out)puts[i]
*/
#define FF_INLINK_IDX(link) ((int)((link)->dstpad - (link)->dst->input_pads))
#define FF_OUTLINK_IDX(link) ((int)((link)->srcpad - (link)->src->output_pads))
int ff_buffersink_read_compat(AVFilterContext *ctx, AVFilterBufferRef **buf);
int ff_buffersink_read_samples_compat(AVFilterContext *ctx, AVFilterBufferRef **pbuf,
int nb_samples);
/**
* Send a frame of data to the next filter.
*
* @param link the output link over which the data is being sent
* @param frame a reference to the buffer of data being sent. The
* receiving filter will free this reference when it no longer
* needs it or pass it on to the next filter.
*
* @return >= 0 on success, a negative AVERROR on error. The receiving filter
* is responsible for unreferencing frame in case of error.
*/
int ff_filter_frame(AVFilterLink *link, AVFrame *frame);
/**
* Flags for AVFilterLink.flags.
*/
enum {
/**
* Frame requests may need to loop in order to be fulfilled.
* A filter must set this flags on an output link if it may return 0 in
* request_frame() without filtering a frame.
*/
FF_LINK_FLAG_REQUEST_LOOP = 1,
};
/**
* Allocate a new filter context and return it.
*
* @param filter what filter to create an instance of
* @param inst_name name to give to the new filter context
*
* @return newly created filter context or NULL on failure
*/
AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name);
/**
* Remove a filter from a graph;
*/
void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter);
#endif /* AVFILTER_INTERNAL_H */
+98
View File
@@ -0,0 +1,98 @@
/*
* Copyright 2012 Stefano Sabatini <stefasab gmail com>
*
* 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 "lavfutils.h"
int ff_load_image(uint8_t *data[4], int linesize[4],
int *w, int *h, enum AVPixelFormat *pix_fmt,
const char *filename, void *log_ctx)
{
AVInputFormat *iformat = NULL;
AVFormatContext *format_ctx = NULL;
AVCodec *codec;
AVCodecContext *codec_ctx;
AVFrame *frame;
int frame_decoded, ret = 0;
AVPacket pkt;
av_init_packet(&pkt);
av_register_all();
iformat = av_find_input_format("image2");
if ((ret = avformat_open_input(&format_ctx, filename, iformat, NULL)) < 0) {
av_log(log_ctx, AV_LOG_ERROR,
"Failed to open input file '%s'\n", filename);
return ret;
}
codec_ctx = format_ctx->streams[0]->codec;
codec = avcodec_find_decoder(codec_ctx->codec_id);
if (!codec) {
av_log(log_ctx, AV_LOG_ERROR, "Failed to find codec\n");
ret = AVERROR(EINVAL);
goto end;
}
if ((ret = avcodec_open2(codec_ctx, codec, NULL)) < 0) {
av_log(log_ctx, AV_LOG_ERROR, "Failed to open codec\n");
goto end;
}
if (!(frame = avcodec_alloc_frame()) ) {
av_log(log_ctx, AV_LOG_ERROR, "Failed to alloc frame\n");
ret = AVERROR(ENOMEM);
goto end;
}
ret = av_read_frame(format_ctx, &pkt);
if (ret < 0) {
av_log(log_ctx, AV_LOG_ERROR, "Failed to read frame from file\n");
goto end;
}
ret = avcodec_decode_video2(codec_ctx, frame, &frame_decoded, &pkt);
if (ret < 0 || !frame_decoded) {
av_log(log_ctx, AV_LOG_ERROR, "Failed to decode image from file\n");
goto end;
}
ret = 0;
*w = frame->width;
*h = frame->height;
*pix_fmt = frame->format;
if ((ret = av_image_alloc(data, linesize, *w, *h, *pix_fmt, 16)) < 0)
goto end;
ret = 0;
av_image_copy(data, linesize, (const uint8_t **)frame->data, frame->linesize, *pix_fmt, *w, *h);
end:
av_free_packet(&pkt);
avcodec_close(codec_ctx);
avformat_close_input(&format_ctx);
av_freep(&frame);
if (ret < 0)
av_log(log_ctx, AV_LOG_ERROR, "Error loading image file '%s'\n", filename);
return ret;
}
+43
View File
@@ -0,0 +1,43 @@
/*
* 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
* Miscellaneous utilities which make use of the libavformat library
*/
#ifndef AVFILTER_LAVFUTILS_H
#define AVFILTER_LAVFUTILS_H
#include "libavformat/avformat.h"
/**
* Load image from filename and put the resulting image in data.
*
* @param w pointer to the width of the loaded image
* @param h pointer to the height of the loaded image
* @param pix_fmt pointer to the pixel format of the loaded image
* @param filename the name of the image file to load
* @param log_ctx log context
* @return >= 0 in case of success, a negative error code otherwise.
*/
int ff_load_image(uint8_t *data[4], int linesize[4],
int *w, int *h, enum AVPixelFormat *pix_fmt,
const char *filename, void *log_ctx);
#endif /* AVFILTER_LAVFUTILS_H */
+14
View File
@@ -0,0 +1,14 @@
prefix=/usr/local
exec_prefix=${prefix}
libdir=${prefix}/lib
includedir=${prefix}/include
Name: libavfilter
Description: FFmpeg audio/video filtering library
Version: 3.90.100
Requires: libswresample = 0.17.104, libswscale = 2.5.101, libavformat = 55.19.104, libavcodec = 55.39.101, libavutil = 52.48.101
Requires.private:
Conflicts:
Libs: -L${libdir} -lavfilter -lavicap32 -lpsapi -lole32 -lstrmiids -luuid -lws2_32 -liconv -lm -lz -lpsapi -ladvapi32 -lshell32
Libs.private:
Cflags: -I${includedir}
+5
View File
@@ -0,0 +1,5 @@
LIBAVFILTER_$MAJOR {
global: avfilter_*; av_*;
ff_default_query_formats;
local: *;
};
@@ -0,0 +1,27 @@
/*
* Generic libav* helpers
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_AV_HELPERS_H
#define MPLAYER_AV_HELPERS_H
void ff_init_avcodec(void);
void ff_init_avformat(void);
#endif /* MPLAYER_AV_HELPERS_H */
@@ -0,0 +1,60 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_CPUDETECT_H
#define MPLAYER_CPUDETECT_H
#define CPUTYPE_I386 3
#define CPUTYPE_I486 4
#define CPUTYPE_I586 5
#define CPUTYPE_I686 6
#include "libavutil/x86_cpu.h"
typedef struct cpucaps_s {
int cpuType;
int cpuModel;
int cpuStepping;
int hasMMX;
int hasMMX2;
int has3DNow;
int has3DNowExt;
int hasSSE;
int hasSSE2;
int hasSSE3;
int hasSSSE3;
int hasSSE4;
int hasSSE42;
int hasSSE4a;
int hasAVX;
int isX86;
unsigned cl_size; /* size of cache line */
int hasAltiVec;
int hasTSC;
} CpuCaps;
extern CpuCaps ff_gCpuCaps;
void ff_do_cpuid(unsigned int ax, unsigned int *p);
void ff_GetCpuCaps(CpuCaps *caps);
/* returned value is malloc()'ed so free() it after use */
char *ff_GetCpuFriendlyName(unsigned int regs[], unsigned int regs2[]);
#endif /* MPLAYER_CPUDETECT_H */
@@ -0,0 +1,233 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "img_format.h"
#include "stdio.h"
#include "libavutil/bswap.h"
const char *ff_vo_format_name(int format)
{
static char unknown_format[20];
switch(format)
{
case IMGFMT_RGB1: return "RGB 1-bit";
case IMGFMT_RGB4: return "RGB 4-bit";
case IMGFMT_RG4B: return "RGB 4-bit per byte";
case IMGFMT_RGB8: return "RGB 8-bit";
case IMGFMT_RGB12: return "RGB 12-bit";
case IMGFMT_RGB15: return "RGB 15-bit";
case IMGFMT_RGB16: return "RGB 16-bit";
case IMGFMT_RGB24: return "RGB 24-bit";
// case IMGFMT_RGB32: return "RGB 32-bit";
case IMGFMT_RGB48LE: return "RGB 48-bit LE";
case IMGFMT_RGB48BE: return "RGB 48-bit BE";
case IMGFMT_RGB64LE: return "RGB 64-bit LE";
case IMGFMT_RGB64BE: return "RGB 64-bit BE";
case IMGFMT_BGR1: return "BGR 1-bit";
case IMGFMT_BGR4: return "BGR 4-bit";
case IMGFMT_BG4B: return "BGR 4-bit per byte";
case IMGFMT_BGR8: return "BGR 8-bit";
case IMGFMT_BGR12: return "BGR 12-bit";
case IMGFMT_BGR15: return "BGR 15-bit";
case IMGFMT_BGR16: return "BGR 16-bit";
case IMGFMT_BGR24: return "BGR 24-bit";
// case IMGFMT_BGR32: return "BGR 32-bit";
case IMGFMT_ABGR: return "ABGR";
case IMGFMT_BGRA: return "BGRA";
case IMGFMT_ARGB: return "ARGB";
case IMGFMT_RGBA: return "RGBA";
case IMGFMT_GBR24P: return "Planar GBR 24-bit";
case IMGFMT_GBR12P: return "Planar GBR 36-bit";
case IMGFMT_GBR14P: return "Planar GBR 42-bit";
case IMGFMT_YVU9: return "Planar YVU9";
case IMGFMT_IF09: return "Planar IF09";
case IMGFMT_YV12: return "Planar YV12";
case IMGFMT_I420: return "Planar I420";
case IMGFMT_IYUV: return "Planar IYUV";
case IMGFMT_CLPL: return "Planar CLPL";
case IMGFMT_Y800: return "Planar Y800";
case IMGFMT_Y8: return "Planar Y8";
case IMGFMT_Y8A: return "Planar Y8 with alpha";
case IMGFMT_Y16_LE: return "Planar Y16 little-endian";
case IMGFMT_Y16_BE: return "Planar Y16 big-endian";
case IMGFMT_420P16_LE: return "Planar 420P 16-bit little-endian";
case IMGFMT_420P16_BE: return "Planar 420P 16-bit big-endian";
case IMGFMT_420P14_LE: return "Planar 420P 14-bit little-endian";
case IMGFMT_420P14_BE: return "Planar 420P 14-bit big-endian";
case IMGFMT_420P12_LE: return "Planar 420P 12-bit little-endian";
case IMGFMT_420P12_BE: return "Planar 420P 12-bit big-endian";
case IMGFMT_420P10_LE: return "Planar 420P 10-bit little-endian";
case IMGFMT_420P10_BE: return "Planar 420P 10-bit big-endian";
case IMGFMT_420P9_LE: return "Planar 420P 9-bit little-endian";
case IMGFMT_420P9_BE: return "Planar 420P 9-bit big-endian";
case IMGFMT_422P16_LE: return "Planar 422P 16-bit little-endian";
case IMGFMT_422P16_BE: return "Planar 422P 16-bit big-endian";
case IMGFMT_422P14_LE: return "Planar 422P 14-bit little-endian";
case IMGFMT_422P14_BE: return "Planar 422P 14-bit big-endian";
case IMGFMT_422P12_LE: return "Planar 422P 12-bit little-endian";
case IMGFMT_422P12_BE: return "Planar 422P 12-bit big-endian";
case IMGFMT_422P10_LE: return "Planar 422P 10-bit little-endian";
case IMGFMT_422P10_BE: return "Planar 422P 10-bit big-endian";
case IMGFMT_422P9_LE: return "Planar 422P 9-bit little-endian";
case IMGFMT_422P9_BE: return "Planar 422P 9-bit big-endian";
case IMGFMT_444P16_LE: return "Planar 444P 16-bit little-endian";
case IMGFMT_444P16_BE: return "Planar 444P 16-bit big-endian";
case IMGFMT_444P14_LE: return "Planar 444P 14-bit little-endian";
case IMGFMT_444P14_BE: return "Planar 444P 14-bit big-endian";
case IMGFMT_444P12_LE: return "Planar 444P 12-bit little-endian";
case IMGFMT_444P12_BE: return "Planar 444P 12-bit big-endian";
case IMGFMT_444P10_LE: return "Planar 444P 10-bit little-endian";
case IMGFMT_444P10_BE: return "Planar 444P 10-bit big-endian";
case IMGFMT_444P9_LE: return "Planar 444P 9-bit little-endian";
case IMGFMT_444P9_BE: return "Planar 444P 9-bit big-endian";
case IMGFMT_420A: return "Planar 420P with alpha";
case IMGFMT_444P: return "Planar 444P";
case IMGFMT_444A: return "Planar 444P with alpha";
case IMGFMT_422P: return "Planar 422P";
case IMGFMT_422A: return "Planar 422P with alpha";
case IMGFMT_411P: return "Planar 411P";
case IMGFMT_NV12: return "Planar NV12";
case IMGFMT_NV21: return "Planar NV21";
case IMGFMT_HM12: return "Planar NV12 Macroblock";
case IMGFMT_IUYV: return "Packed IUYV";
case IMGFMT_IY41: return "Packed IY41";
case IMGFMT_IYU1: return "Packed IYU1";
case IMGFMT_IYU2: return "Packed IYU2";
case IMGFMT_UYVY: return "Packed UYVY";
case IMGFMT_UYNV: return "Packed UYNV";
case IMGFMT_cyuv: return "Packed CYUV";
case IMGFMT_Y422: return "Packed Y422";
case IMGFMT_YUY2: return "Packed YUY2";
case IMGFMT_YUNV: return "Packed YUNV";
case IMGFMT_YVYU: return "Packed YVYU";
case IMGFMT_Y41P: return "Packed Y41P";
case IMGFMT_Y211: return "Packed Y211";
case IMGFMT_Y41T: return "Packed Y41T";
case IMGFMT_Y42T: return "Packed Y42T";
case IMGFMT_V422: return "Packed V422";
case IMGFMT_V655: return "Packed V655";
case IMGFMT_CLJR: return "Packed CLJR";
case IMGFMT_YUVP: return "Packed YUVP";
case IMGFMT_UYVP: return "Packed UYVP";
case IMGFMT_MPEGPES: return "Mpeg PES";
case IMGFMT_ZRMJPEGNI: return "Zoran MJPEG non-interlaced";
case IMGFMT_ZRMJPEGIT: return "Zoran MJPEG top field first";
case IMGFMT_ZRMJPEGIB: return "Zoran MJPEG bottom field first";
case IMGFMT_XVMC_MOCO_MPEG2: return "MPEG1/2 Motion Compensation";
case IMGFMT_XVMC_IDCT_MPEG2: return "MPEG1/2 Motion Compensation and IDCT";
case IMGFMT_VDPAU_MPEG1: return "MPEG1 VDPAU acceleration";
case IMGFMT_VDPAU_MPEG2: return "MPEG2 VDPAU acceleration";
case IMGFMT_VDPAU_H264: return "H.264 VDPAU acceleration";
case IMGFMT_VDPAU_MPEG4: return "MPEG-4 Part 2 VDPAU acceleration";
case IMGFMT_VDPAU_WMV3: return "WMV3 VDPAU acceleration";
case IMGFMT_VDPAU_VC1: return "VC1 VDPAU acceleration";
}
snprintf(unknown_format,20,"Unknown 0x%04x",format);
return unknown_format;
}
int ff_mp_get_chroma_shift(int format, int *x_shift, int *y_shift, int *component_bits)
{
int xs = 0, ys = 0;
int bpp;
int err = 0;
int bits = 8;
if ((format & 0xff0000f0) == 0x34000050)
format = av_bswap32(format);
if ((format & 0xf00000ff) == 0x50000034) {
switch (format >> 24) {
case 0x50:
break;
case 0x51:
bits = 16;
break;
case 0x52:
bits = 10;
break;
case 0x53:
bits = 9;
break;
default:
err = 1;
break;
}
switch (format & 0x00ffffff) {
case 0x00343434: // 444
xs = 0;
ys = 0;
break;
case 0x00323234: // 422
xs = 1;
ys = 0;
break;
case 0x00303234: // 420
xs = 1;
ys = 1;
break;
case 0x00313134: // 411
xs = 2;
ys = 0;
break;
case 0x00303434: // 440
xs = 0;
ys = 1;
break;
default:
err = 1;
break;
}
} else switch (format) {
case IMGFMT_444A:
xs = 0;
ys = 0;
break;
case IMGFMT_422A:
xs = 1;
ys = 0;
break;
case IMGFMT_420A:
case IMGFMT_I420:
case IMGFMT_IYUV:
case IMGFMT_YV12:
xs = 1;
ys = 1;
break;
case IMGFMT_IF09:
case IMGFMT_YVU9:
xs = 2;
ys = 2;
break;
case IMGFMT_Y8:
case IMGFMT_Y800:
xs = 31;
ys = 31;
break;
default:
err = 1;
break;
}
if (x_shift) *x_shift = xs;
if (y_shift) *y_shift = ys;
if (component_bits) *component_bits = bits;
bpp = 8 + ((16 >> xs) >> ys);
if (format == IMGFMT_420A || format == IMGFMT_422A || format == IMGFMT_444A)
bpp += 8;
bpp *= (bits + 7) >> 3;
return err ? 0 : bpp;
}
@@ -0,0 +1,300 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_IMG_FORMAT_H
#define MPLAYER_IMG_FORMAT_H
#include "config.h"
/* RGB/BGR Formats */
#define IMGFMT_RGB_MASK 0xFFFFFF00
#define IMGFMT_RGB (('R'<<24)|('G'<<16)|('B'<<8))
#define IMGFMT_RGB1 (IMGFMT_RGB|1)
#define IMGFMT_RGB4 (IMGFMT_RGB|4)
#define IMGFMT_RGB4_CHAR (IMGFMT_RGB|4|128) // RGB4 with 1 pixel per byte
#define IMGFMT_RGB8 (IMGFMT_RGB|8)
#define IMGFMT_RGB12 (IMGFMT_RGB|12)
#define IMGFMT_RGB15 (IMGFMT_RGB|15)
#define IMGFMT_RGB16 (IMGFMT_RGB|16)
#define IMGFMT_RGB24 (IMGFMT_RGB|24)
#define IMGFMT_RGB32 (IMGFMT_RGB|32)
#define IMGFMT_RGB48LE (IMGFMT_RGB|48)
#define IMGFMT_RGB48BE (IMGFMT_RGB|48|128)
#define IMGFMT_RGB64LE (IMGFMT_RGB|64)
#define IMGFMT_RGB64BE (IMGFMT_RGB|64|128)
#define IMGFMT_BGR_MASK 0xFFFFFF00
#define IMGFMT_BGR (('B'<<24)|('G'<<16)|('R'<<8))
#define IMGFMT_BGR1 (IMGFMT_BGR|1)
#define IMGFMT_BGR4 (IMGFMT_BGR|4)
#define IMGFMT_BGR4_CHAR (IMGFMT_BGR|4|128) // BGR4 with 1 pixel per byte
#define IMGFMT_BGR8 (IMGFMT_BGR|8)
#define IMGFMT_BGR12 (IMGFMT_BGR|12)
#define IMGFMT_BGR15 (IMGFMT_BGR|15)
#define IMGFMT_BGR16 (IMGFMT_BGR|16)
#define IMGFMT_BGR24 (IMGFMT_BGR|24)
#define IMGFMT_BGR32 (IMGFMT_BGR|32)
#define IMGFMT_GBR24P (('G'<<24)|('B'<<16)|('R'<<8)|24)
#define IMGFMT_GBR12PLE (('G'<<24)|('B'<<16)|('R'<<8)|36)
#define IMGFMT_GBR12PBE (('G'<<24)|('B'<<16)|('R'<<8)|36|128)
#define IMGFMT_GBR14PLE (('G'<<24)|('B'<<16)|('R'<<8)|42)
#define IMGFMT_GBR14PBE (('G'<<24)|('B'<<16)|('R'<<8)|42|128)
#if HAVE_BIGENDIAN
#define IMGFMT_ABGR IMGFMT_RGB32
#define IMGFMT_BGRA (IMGFMT_RGB32|128)
#define IMGFMT_ARGB IMGFMT_BGR32
#define IMGFMT_RGBA (IMGFMT_BGR32|128)
#define IMGFMT_RGB64NE IMGFMT_RGB64BE
#define IMGFMT_RGB48NE IMGFMT_RGB48BE
#define IMGFMT_RGB12BE IMGFMT_RGB12
#define IMGFMT_RGB12LE (IMGFMT_RGB12|128)
#define IMGFMT_RGB15BE IMGFMT_RGB15
#define IMGFMT_RGB15LE (IMGFMT_RGB15|128)
#define IMGFMT_RGB16BE IMGFMT_RGB16
#define IMGFMT_RGB16LE (IMGFMT_RGB16|128)
#define IMGFMT_BGR12BE IMGFMT_BGR12
#define IMGFMT_BGR12LE (IMGFMT_BGR12|128)
#define IMGFMT_BGR15BE IMGFMT_BGR15
#define IMGFMT_BGR15LE (IMGFMT_BGR15|128)
#define IMGFMT_BGR16BE IMGFMT_BGR16
#define IMGFMT_BGR16LE (IMGFMT_BGR16|128)
#define IMGFMT_GBR12P IMGFMT_GBR12PBE
#define IMGFMT_GBR14P IMGFMT_GBR14PBE
#else
#define IMGFMT_ABGR (IMGFMT_BGR32|128)
#define IMGFMT_BGRA IMGFMT_BGR32
#define IMGFMT_ARGB (IMGFMT_RGB32|128)
#define IMGFMT_RGBA IMGFMT_RGB32
#define IMGFMT_RGB64NE IMGFMT_RGB64LE
#define IMGFMT_RGB48NE IMGFMT_RGB48LE
#define IMGFMT_RGB12BE (IMGFMT_RGB12|128)
#define IMGFMT_RGB12LE IMGFMT_RGB12
#define IMGFMT_RGB15BE (IMGFMT_RGB15|128)
#define IMGFMT_RGB15LE IMGFMT_RGB15
#define IMGFMT_RGB16BE (IMGFMT_RGB16|128)
#define IMGFMT_RGB16LE IMGFMT_RGB16
#define IMGFMT_BGR12BE (IMGFMT_BGR12|128)
#define IMGFMT_BGR12LE IMGFMT_BGR12
#define IMGFMT_BGR15BE (IMGFMT_BGR15|128)
#define IMGFMT_BGR15LE IMGFMT_BGR15
#define IMGFMT_BGR16BE (IMGFMT_BGR16|128)
#define IMGFMT_BGR16LE IMGFMT_BGR16
#define IMGFMT_GBR12P IMGFMT_GBR12PLE
#define IMGFMT_GBR14P IMGFMT_GBR14PLE
#endif
/* old names for compatibility */
#define IMGFMT_RG4B IMGFMT_RGB4_CHAR
#define IMGFMT_BG4B IMGFMT_BGR4_CHAR
#define IMGFMT_IS_RGB(fmt) (((fmt)&IMGFMT_RGB_MASK)==IMGFMT_RGB)
#define IMGFMT_IS_BGR(fmt) (((fmt)&IMGFMT_BGR_MASK)==IMGFMT_BGR)
#define IMGFMT_RGB_DEPTH(fmt) ((fmt)&0x7F)
#define IMGFMT_BGR_DEPTH(fmt) ((fmt)&0x7F)
/* Planar YUV Formats */
#define IMGFMT_YVU9 0x39555659
#define IMGFMT_IF09 0x39304649
#define IMGFMT_YV12 0x32315659
#define IMGFMT_I420 0x30323449
#define IMGFMT_IYUV 0x56555949
#define IMGFMT_CLPL 0x4C504C43
#define IMGFMT_Y800 0x30303859
#define IMGFMT_Y8 0x20203859
#define IMGFMT_NV12 0x3231564E
#define IMGFMT_NV21 0x3132564E
#define IMGFMT_Y16_LE 0x20363159
/* unofficial Planar Formats, FIXME if official 4CC exists */
#define IMGFMT_444P 0x50343434
#define IMGFMT_422P 0x50323234
#define IMGFMT_411P 0x50313134
#define IMGFMT_440P 0x50303434
#define IMGFMT_HM12 0x32314D48
#define IMGFMT_Y16_BE 0x59313620
// Gray with alpha
#define IMGFMT_Y8A 0x59320008
// 4:2:0 planar with alpha
#define IMGFMT_420A 0x41303234
// 4:2:2 planar with alpha
#define IMGFMT_422A 0x41323234
// 4:4:4 planar with alpha
#define IMGFMT_444A 0x41343434
#define IMGFMT_444P16_LE 0x51343434
#define IMGFMT_444P16_BE 0x34343451
#define IMGFMT_444P14_LE 0x54343434
#define IMGFMT_444P14_BE 0x34343454
#define IMGFMT_444P12_LE 0x55343434
#define IMGFMT_444P12_BE 0x34343455
#define IMGFMT_444P10_LE 0x52343434
#define IMGFMT_444P10_BE 0x34343452
#define IMGFMT_444P9_LE 0x53343434
#define IMGFMT_444P9_BE 0x34343453
#define IMGFMT_422P16_LE 0x51323234
#define IMGFMT_422P16_BE 0x34323251
#define IMGFMT_422P14_LE 0x54323234
#define IMGFMT_422P14_BE 0x34323254
#define IMGFMT_422P12_LE 0x55323234
#define IMGFMT_422P12_BE 0x34323255
#define IMGFMT_422P10_LE 0x52323234
#define IMGFMT_422P10_BE 0x34323252
#define IMGFMT_422P9_LE 0x53323234
#define IMGFMT_422P9_BE 0x34323253
#define IMGFMT_420P16_LE 0x51303234
#define IMGFMT_420P16_BE 0x34323051
#define IMGFMT_420P14_LE 0x54303234
#define IMGFMT_420P14_BE 0x34323054
#define IMGFMT_420P12_LE 0x55303234
#define IMGFMT_420P12_BE 0x34323055
#define IMGFMT_420P10_LE 0x52303234
#define IMGFMT_420P10_BE 0x34323052
#define IMGFMT_420P9_LE 0x53303234
#define IMGFMT_420P9_BE 0x34323053
#if HAVE_BIGENDIAN
#define IMGFMT_444P16 IMGFMT_444P16_BE
#define IMGFMT_444P14 IMGFMT_444P14_BE
#define IMGFMT_444P12 IMGFMT_444P12_BE
#define IMGFMT_444P10 IMGFMT_444P10_BE
#define IMGFMT_444P9 IMGFMT_444P9_BE
#define IMGFMT_422P16 IMGFMT_422P16_BE
#define IMGFMT_422P14 IMGFMT_422P14_BE
#define IMGFMT_422P12 IMGFMT_422P12_BE
#define IMGFMT_422P10 IMGFMT_422P10_BE
#define IMGFMT_422P9 IMGFMT_422P9_BE
#define IMGFMT_420P16 IMGFMT_420P16_BE
#define IMGFMT_420P14 IMGFMT_420P14_BE
#define IMGFMT_420P12 IMGFMT_420P12_BE
#define IMGFMT_420P10 IMGFMT_420P10_BE
#define IMGFMT_420P9 IMGFMT_420P9_BE
#define IMGFMT_Y16 IMGFMT_Y16_BE
#define IMGFMT_IS_YUVP16_NE(fmt) IMGFMT_IS_YUVP16_BE(fmt)
#else
#define IMGFMT_444P16 IMGFMT_444P16_LE
#define IMGFMT_444P14 IMGFMT_444P14_LE
#define IMGFMT_444P12 IMGFMT_444P12_LE
#define IMGFMT_444P10 IMGFMT_444P10_LE
#define IMGFMT_444P9 IMGFMT_444P9_LE
#define IMGFMT_422P16 IMGFMT_422P16_LE
#define IMGFMT_422P14 IMGFMT_422P14_LE
#define IMGFMT_422P12 IMGFMT_422P12_LE
#define IMGFMT_422P10 IMGFMT_422P10_LE
#define IMGFMT_422P9 IMGFMT_422P9_LE
#define IMGFMT_420P16 IMGFMT_420P16_LE
#define IMGFMT_420P14 IMGFMT_420P14_LE
#define IMGFMT_420P12 IMGFMT_420P12_LE
#define IMGFMT_420P10 IMGFMT_420P10_LE
#define IMGFMT_420P9 IMGFMT_420P9_LE
#define IMGFMT_Y16 IMGFMT_Y16_LE
#define IMGFMT_IS_YUVP16_NE(fmt) IMGFMT_IS_YUVP16_LE(fmt)
#endif
#define IMGFMT_IS_YUVP16_LE(fmt) (((fmt - 0x51000034) & 0xfc0000ff) == 0)
#define IMGFMT_IS_YUVP16_BE(fmt) (((fmt - 0x34000051) & 0xff0000fc) == 0)
#define IMGFMT_IS_YUVP16(fmt) (IMGFMT_IS_YUVP16_LE(fmt) || IMGFMT_IS_YUVP16_BE(fmt))
/**
* \brief Find the corresponding full 16 bit format, i.e. IMGFMT_420P10_LE -> IMGFMT_420P16_LE
* \return normalized format ID or 0 if none exists.
*/
static inline int normalize_yuvp16(int fmt) {
if (IMGFMT_IS_YUVP16_LE(fmt))
return (fmt & 0x00ffffff) | 0x51000000;
if (IMGFMT_IS_YUVP16_BE(fmt))
return (fmt & 0xffffff00) | 0x00000051;
return 0;
}
/* Packed YUV Formats */
#define IMGFMT_IUYV 0x56595549 // Interlaced UYVY
#define IMGFMT_IY41 0x31435949 // Interlaced Y41P
#define IMGFMT_IYU1 0x31555949
#define IMGFMT_IYU2 0x32555949
#define IMGFMT_UYVY 0x59565955
#define IMGFMT_UYNV 0x564E5955 // Exactly same as UYVY
#define IMGFMT_cyuv 0x76757963 // upside-down UYVY
#define IMGFMT_Y422 0x32323459 // Exactly same as UYVY
#define IMGFMT_YUY2 0x32595559
#define IMGFMT_YUNV 0x564E5559 // Exactly same as YUY2
#define IMGFMT_YVYU 0x55595659
#define IMGFMT_Y41P 0x50313459
#define IMGFMT_Y211 0x31313259
#define IMGFMT_Y41T 0x54313459 // Y41P, Y lsb = transparency
#define IMGFMT_Y42T 0x54323459 // UYVY, Y lsb = transparency
#define IMGFMT_V422 0x32323456 // upside-down UYVY?
#define IMGFMT_V655 0x35353656
#define IMGFMT_CLJR 0x524A4C43
#define IMGFMT_YUVP 0x50565559 // 10-bit YUYV
#define IMGFMT_UYVP 0x50565955 // 10-bit UYVY
/* Compressed Formats */
#define IMGFMT_MPEGPES (('M'<<24)|('P'<<16)|('E'<<8)|('S'))
#define IMGFMT_MJPEG (('M')|('J'<<8)|('P'<<16)|('G'<<24))
/* Formats that are understood by zoran chips, we include
* non-interlaced, interlaced top-first, interlaced bottom-first */
#define IMGFMT_ZRMJPEGNI (('Z'<<24)|('R'<<16)|('N'<<8)|('I'))
#define IMGFMT_ZRMJPEGIT (('Z'<<24)|('R'<<16)|('I'<<8)|('T'))
#define IMGFMT_ZRMJPEGIB (('Z'<<24)|('R'<<16)|('I'<<8)|('B'))
// I think that this code could not be used by any other codec/format
#define IMGFMT_XVMC 0x1DC70000
#define IMGFMT_XVMC_MASK 0xFFFF0000
#define IMGFMT_IS_XVMC(fmt) (((fmt)&IMGFMT_XVMC_MASK)==IMGFMT_XVMC)
//these are chroma420
#define IMGFMT_XVMC_MOCO_MPEG2 (IMGFMT_XVMC|0x02)
#define IMGFMT_XVMC_IDCT_MPEG2 (IMGFMT_XVMC|0x82)
// VDPAU specific format.
#define IMGFMT_VDPAU 0x1DC80000
#define IMGFMT_VDPAU_MASK 0xFFFF0000
#define IMGFMT_IS_VDPAU(fmt) (((fmt)&IMGFMT_VDPAU_MASK)==IMGFMT_VDPAU)
#define IMGFMT_VDPAU_MPEG1 (IMGFMT_VDPAU|0x01)
#define IMGFMT_VDPAU_MPEG2 (IMGFMT_VDPAU|0x02)
#define IMGFMT_VDPAU_H264 (IMGFMT_VDPAU|0x03)
#define IMGFMT_VDPAU_WMV3 (IMGFMT_VDPAU|0x04)
#define IMGFMT_VDPAU_VC1 (IMGFMT_VDPAU|0x05)
#define IMGFMT_VDPAU_MPEG4 (IMGFMT_VDPAU|0x06)
#define IMGFMT_IS_HWACCEL(fmt) (IMGFMT_IS_VDPAU(fmt) || IMGFMT_IS_XVMC(fmt))
typedef struct {
void* data;
int size;
int id; // stream id. usually 0x1E0
int timestamp; // pts, 90000 Hz counter based
} vo_mpegpes_t;
const char *ff_vo_format_name(int format);
/**
* Calculates the scale shifts for the chroma planes for planar YUV
*
* \param component_bits bits per component
* \return bits-per-pixel for format if successful (i.e. format is 3 or 4-planes planar YUV), 0 otherwise
*/
int ff_mp_get_chroma_shift(int format, int *x_shift, int *y_shift, int *component_bits);
#endif /* MPLAYER_IMG_FORMAT_H */
@@ -0,0 +1,99 @@
/*
* This file is part of MPlayer.
*
* MPlayer 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.
*
* MPlayer 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 MPlayer; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef MPLAYER_FASTMEMCPY_H
#define MPLAYER_FASTMEMCPY_H
#include <inttypes.h>
#include <string.h>
#include <stddef.h>
void * fast_memcpy(void * to, const void * from, size_t len);
void * mem2agpcpy(void * to, const void * from, size_t len);
#if ! defined(CONFIG_FASTMEMCPY) || ! (HAVE_MMX || HAVE_MMX2 || HAVE_AMD3DNOW /* || HAVE_SSE || HAVE_SSE2 */)
#define mem2agpcpy(a,b,c) memcpy(a,b,c)
#define fast_memcpy(a,b,c) memcpy(a,b,c)
#endif
static inline void * mem2agpcpy_pic(void * dst, const void * src, int bytesPerLine, int height, int dstStride, int srcStride)
{
int i;
void *retval=dst;
if(dstStride == srcStride)
{
if (srcStride < 0) {
src = (const uint8_t*)src + (height-1)*srcStride;
dst = (uint8_t*)dst + (height-1)*dstStride;
srcStride = -srcStride;
}
mem2agpcpy(dst, src, srcStride*height);
}
else
{
for(i=0; i<height; i++)
{
mem2agpcpy(dst, src, bytesPerLine);
src = (const uint8_t*)src + srcStride;
dst = (uint8_t*)dst + dstStride;
}
}
return retval;
}
#define memcpy_pic(d, s, b, h, ds, ss) memcpy_pic2(d, s, b, h, ds, ss, 0)
#define my_memcpy_pic(d, s, b, h, ds, ss) memcpy_pic2(d, s, b, h, ds, ss, 1)
/**
* \param limit2width always skip data between end of line and start of next
* instead of copying the full block when strides are the same
*/
static inline void * memcpy_pic2(void * dst, const void * src,
int bytesPerLine, int height,
int dstStride, int srcStride, int limit2width)
{
int i;
void *retval=dst;
if(!limit2width && dstStride == srcStride)
{
if (srcStride < 0) {
src = (const uint8_t*)src + (height-1)*srcStride;
dst = (uint8_t*)dst + (height-1)*dstStride;
srcStride = -srcStride;
}
fast_memcpy(dst, src, srcStride*height);
}
else
{
for(i=0; i<height; i++)
{
fast_memcpy(dst, src, bytesPerLine);
src = (const uint8_t*)src + srcStride;
dst = (uint8_t*)dst + dstStride;
}
}
return retval;
}
#endif /* MPLAYER_FASTMEMCPY_H */
@@ -0,0 +1,281 @@
/*
* Copyright (C) Aaron Holtzman - Aug 1999
* Strongly modified, most parts rewritten: A'rpi/ESP-team - 2000-2001
* (C) MPlayer developers
*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_VIDEO_OUT_H
#define MPLAYER_VIDEO_OUT_H
#include <inttypes.h>
#include <stdarg.h>
//#include "sub/font_load.h"
#include "../img_format.h"
//#include "vidix/vidix.h"
#define VO_EVENT_EXPOSE 1
#define VO_EVENT_RESIZE 2
#define VO_EVENT_KEYPRESS 4
#define VO_EVENT_REINIT 8
#define VO_EVENT_MOVE 16
/* Obsolete: VOCTRL_QUERY_VAA 1 */
/* does the device support the required format */
#define VOCTRL_QUERY_FORMAT 2
/* signal a device reset seek */
#define VOCTRL_RESET 3
/* true if vo driver can use GUI created windows */
#define VOCTRL_GUISUPPORT 4
#define VOCTRL_GUI_NOWINDOW 19
/* used to switch to fullscreen */
#define VOCTRL_FULLSCREEN 5
/* signal a device pause */
#define VOCTRL_PAUSE 7
/* start/resume playback */
#define VOCTRL_RESUME 8
/* libmpcodecs direct rendering: */
#define VOCTRL_GET_IMAGE 9
#define VOCTRL_DRAW_IMAGE 13
#define VOCTRL_SET_SPU_PALETTE 14
/* decoding ahead: */
#define VOCTRL_GET_NUM_FRAMES 10
#define VOCTRL_GET_FRAME_NUM 11
#define VOCTRL_SET_FRAME_NUM 12
#define VOCTRL_GET_PANSCAN 15
#define VOCTRL_SET_PANSCAN 16
/* equalizer controls */
#define VOCTRL_SET_EQUALIZER 17
#define VOCTRL_GET_EQUALIZER 18
//#define VOCTRL_GUI_NOWINDOW 19
/* Frame duplication */
#define VOCTRL_DUPLICATE_FRAME 20
// ... 21
#define VOCTRL_START_SLICE 21
#define VOCTRL_ONTOP 25
#define VOCTRL_ROOTWIN 26
#define VOCTRL_BORDER 27
#define VOCTRL_DRAW_EOSD 28
#define VOCTRL_GET_EOSD_RES 29
#define VOCTRL_SET_DEINTERLACE 30
#define VOCTRL_GET_DEINTERLACE 31
#define VOCTRL_UPDATE_SCREENINFO 32
// Vo can be used by xover
#define VOCTRL_XOVERLAY_SUPPORT 22
#define VOCTRL_XOVERLAY_SET_COLORKEY 24
typedef struct {
uint32_t x11; // The raw x11 color
uint16_t r,g,b;
} mp_colorkey_t;
#define VOCTRL_XOVERLAY_SET_WIN 23
typedef struct {
int x,y;
int w,h;
} mp_win_t;
#define VO_TRUE 1
#define VO_FALSE 0
#define VO_ERROR -1
#define VO_NOTAVAIL -2
#define VO_NOTIMPL -3
#define VOFLAG_FULLSCREEN 0x01
#define VOFLAG_MODESWITCHING 0x02
#define VOFLAG_SWSCALE 0x04
#define VOFLAG_FLIPPING 0x08
#define VOFLAG_HIDDEN 0x10 //< Use to create a hidden window
#define VOFLAG_STEREO 0x20 //< Use to create a stereo-capable window
#define VOFLAG_XOVERLAY_SUB_VO 0x10000
typedef struct vo_info_s
{
/* driver name ("Matrox Millennium G200/G400" */
const char *name;
/* short name (for config strings) ("mga") */
const char *short_name;
/* author ("Aaron Holtzman <[email protected]>") */
const char *author;
/* any additional comments */
const char *comment;
} vo_info_t;
typedef struct vo_functions_s
{
const vo_info_t *info;
/*
* Preinitializes driver (real INITIALIZATION)
* arg - currently it's vo_subdevice
* returns: zero on successful initialization, non-zero on error.
*/
int (*preinit)(const char *arg);
/*
* Initialize (means CONFIGURE) the display driver.
* params:
* width,height: image source size
* d_width,d_height: size of the requested window size, just a hint
* fullscreen: flag, 0=windowd 1=fullscreen, just a hint
* title: window title, if available
* format: fourcc of pixel format
* returns : zero on successful initialization, non-zero on error.
*/
int (*config)(uint32_t width, uint32_t height, uint32_t d_width,
uint32_t d_height, uint32_t fullscreen, char *title,
uint32_t format);
/*
* Control interface
*/
int (*control)(uint32_t request, void *data, ...);
/*
* Display a new RGB/BGR frame of the video to the screen.
* params:
* src[0] - pointer to the image
*/
int (*draw_frame)(uint8_t *src[]);
/*
* Draw a planar YUV slice to the buffer:
* params:
* src[3] = source image planes (Y,U,V)
* stride[3] = source image planes line widths (in bytes)
* w,h = width*height of area to be copied (in Y pixels)
* x,y = position at the destination image (in Y pixels)
*/
int (*draw_slice)(uint8_t *src[], int stride[], int w,int h, int x,int y);
/*
* Draws OSD to the screen buffer
*/
void (*draw_osd)(void);
/*
* Blit/Flip buffer to the screen. Must be called after each frame!
*/
void (*flip_page)(void);
/*
* This func is called after every frames to handle keyboard and
* other events. It's called in PAUSE mode too!
*/
void (*check_events)(void);
/*
* Closes driver. Should restore the original state of the system.
*/
void (*uninit)(void);
} vo_functions_t;
const vo_functions_t* init_best_video_out(char** vo_list);
int config_video_out(const vo_functions_t *vo, uint32_t width, uint32_t height,
uint32_t d_width, uint32_t d_height, uint32_t flags,
char *title, uint32_t format);
void list_video_out(void);
// NULL terminated array of all drivers
extern const vo_functions_t* const video_out_drivers[];
extern int vo_flags;
extern int vo_config_count;
extern int xinerama_screen;
extern int xinerama_x;
extern int xinerama_y;
// correct resolution/bpp on screen: (should be autodetected by vo_init())
extern int vo_depthonscreen;
extern int vo_screenwidth;
extern int vo_screenheight;
// requested resolution/bpp: (-x -y -bpp options)
extern int vo_dx;
extern int vo_dy;
extern int vo_dwidth;
extern int vo_dheight;
extern int vo_dbpp;
extern int vo_grabpointer;
extern int vo_doublebuffering;
extern int vo_directrendering;
extern int vo_vsync;
extern int vo_fsmode;
extern float vo_panscan;
extern int vo_adapter_num;
extern int vo_refresh_rate;
extern int vo_keepaspect;
extern int vo_rootwin;
extern int vo_ontop;
extern int vo_border;
extern int vo_gamma_gamma;
extern int vo_gamma_brightness;
extern int vo_gamma_saturation;
extern int vo_gamma_contrast;
extern int vo_gamma_hue;
extern int vo_gamma_red_intensity;
extern int vo_gamma_green_intensity;
extern int vo_gamma_blue_intensity;
extern int vo_nomouse_input;
extern int enable_mouse_movements;
extern int vo_pts;
extern float vo_fps;
extern char *vo_subdevice;
extern int vo_colorkey;
extern char *vo_winname;
extern char *vo_wintitle;
extern int64_t WinID;
typedef struct {
float min;
float max;
} range_t;
float range_max(range_t *r);
int in_range(range_t *r, float f);
range_t *str2range(char *s);
extern char *monitor_hfreq_str;
extern char *monitor_vfreq_str;
extern char *monitor_dotclock_str;
struct mp_keymap {
int from;
int to;
};
int lookup_keymap_table(const struct mp_keymap *map, int key);
struct vo_rect {
int left, right, top, bottom, width, height;
};
void calc_src_dst_rects(int src_width, int src_height, struct vo_rect *src, struct vo_rect *dst,
struct vo_rect *borders, const struct vo_rect *crop);
void vo_mouse_movement(int posx, int posy);
#endif /* MPLAYER_VIDEO_OUT_H */
@@ -0,0 +1,253 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if HAVE_MALLOC_H
#include <malloc.h>
#endif
#include "img_format.h"
#include "mp_image.h"
#include "libvo/fastmemcpy.h"
//#include "libavutil/mem.h"
#include "libavutil/imgutils.h"
void ff_mp_image_alloc_planes(mp_image_t *mpi) {
uint32_t temp[256];
if (avpriv_set_systematic_pal2(temp, ff_mp2ff_pix_fmt(mpi->imgfmt)) >= 0)
mpi->flags |= MP_IMGFLAG_RGB_PALETTE;
// IF09 - allocate space for 4. plane delta info - unused
if (mpi->imgfmt == IMGFMT_IF09) {
mpi->planes[0]=av_malloc(mpi->bpp*mpi->width*(mpi->height+2)/8+
mpi->chroma_width*mpi->chroma_height);
} else
mpi->planes[0]=av_malloc(mpi->bpp*mpi->width*(mpi->height+2)/8);
if (mpi->flags&MP_IMGFLAG_PLANAR) {
int bpp = IMGFMT_IS_YUVP16(mpi->imgfmt)? 2 : 1;
// YV12/I420/YVU9/IF09. feel free to add other planar formats here...
mpi->stride[0]=mpi->stride[3]=bpp*mpi->width;
if(mpi->num_planes > 2){
mpi->stride[1]=mpi->stride[2]=bpp*mpi->chroma_width;
if(mpi->flags&MP_IMGFLAG_SWAPPED){
// I420/IYUV (Y,U,V)
mpi->planes[1]=mpi->planes[0]+mpi->stride[0]*mpi->height;
mpi->planes[2]=mpi->planes[1]+mpi->stride[1]*mpi->chroma_height;
if (mpi->num_planes > 3)
mpi->planes[3]=mpi->planes[2]+mpi->stride[2]*mpi->chroma_height;
} else {
// YV12,YVU9,IF09 (Y,V,U)
mpi->planes[2]=mpi->planes[0]+mpi->stride[0]*mpi->height;
mpi->planes[1]=mpi->planes[2]+mpi->stride[1]*mpi->chroma_height;
if (mpi->num_planes > 3)
mpi->planes[3]=mpi->planes[1]+mpi->stride[1]*mpi->chroma_height;
}
} else {
// NV12/NV21
mpi->stride[1]=mpi->chroma_width;
mpi->planes[1]=mpi->planes[0]+mpi->stride[0]*mpi->height;
}
} else {
mpi->stride[0]=mpi->width*mpi->bpp/8;
if (mpi->flags & MP_IMGFLAG_RGB_PALETTE) {
mpi->planes[1] = av_malloc(1024);
memcpy(mpi->planes[1], temp, 1024);
}
}
mpi->flags|=MP_IMGFLAG_ALLOCATED;
}
mp_image_t* ff_alloc_mpi(int w, int h, unsigned long int fmt) {
mp_image_t* mpi = ff_new_mp_image(w,h);
ff_mp_image_setfmt(mpi,fmt);
ff_mp_image_alloc_planes(mpi);
return mpi;
}
void ff_copy_mpi(mp_image_t *dmpi, mp_image_t *mpi) {
if(mpi->flags&MP_IMGFLAG_PLANAR){
memcpy_pic(dmpi->planes[0],mpi->planes[0], mpi->w, mpi->h,
dmpi->stride[0],mpi->stride[0]);
memcpy_pic(dmpi->planes[1],mpi->planes[1], mpi->chroma_width, mpi->chroma_height,
dmpi->stride[1],mpi->stride[1]);
memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height,
dmpi->stride[2],mpi->stride[2]);
} else {
memcpy_pic(dmpi->planes[0],mpi->planes[0],
mpi->w*(dmpi->bpp/8), mpi->h,
dmpi->stride[0],mpi->stride[0]);
}
}
void ff_mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt){
mpi->flags&=~(MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV|MP_IMGFLAG_SWAPPED);
mpi->imgfmt=out_fmt;
// compressed formats
if(out_fmt == IMGFMT_MPEGPES ||
out_fmt == IMGFMT_ZRMJPEGNI || out_fmt == IMGFMT_ZRMJPEGIT || out_fmt == IMGFMT_ZRMJPEGIB ||
IMGFMT_IS_HWACCEL(out_fmt)){
mpi->bpp=0;
return;
}
mpi->num_planes=1;
if (IMGFMT_IS_RGB(out_fmt)) {
if (IMGFMT_RGB_DEPTH(out_fmt) < 8 && !(out_fmt&128))
mpi->bpp = IMGFMT_RGB_DEPTH(out_fmt);
else
mpi->bpp=(IMGFMT_RGB_DEPTH(out_fmt)+7)&(~7);
return;
}
if (IMGFMT_IS_BGR(out_fmt)) {
if (IMGFMT_BGR_DEPTH(out_fmt) < 8 && !(out_fmt&128))
mpi->bpp = IMGFMT_BGR_DEPTH(out_fmt);
else
mpi->bpp=(IMGFMT_BGR_DEPTH(out_fmt)+7)&(~7);
mpi->flags|=MP_IMGFLAG_SWAPPED;
return;
}
mpi->num_planes=3;
if (out_fmt == IMGFMT_GBR24P) {
mpi->bpp=24;
mpi->flags|=MP_IMGFLAG_PLANAR;
return;
} else if (out_fmt == IMGFMT_GBR12P) {
mpi->bpp=36;
mpi->flags|=MP_IMGFLAG_PLANAR;
return;
} else if (out_fmt == IMGFMT_GBR14P) {
mpi->bpp=42;
mpi->flags|=MP_IMGFLAG_PLANAR;
return;
}
mpi->flags|=MP_IMGFLAG_YUV;
if (ff_mp_get_chroma_shift(out_fmt, NULL, NULL, NULL)) {
mpi->flags|=MP_IMGFLAG_PLANAR;
mpi->bpp = ff_mp_get_chroma_shift(out_fmt, &mpi->chroma_x_shift, &mpi->chroma_y_shift, NULL);
mpi->chroma_width = mpi->width >> mpi->chroma_x_shift;
mpi->chroma_height = mpi->height >> mpi->chroma_y_shift;
}
switch(out_fmt){
case IMGFMT_I420:
case IMGFMT_IYUV:
mpi->flags|=MP_IMGFLAG_SWAPPED;
case IMGFMT_YV12:
return;
case IMGFMT_420A:
case IMGFMT_422A:
case IMGFMT_444A:
case IMGFMT_IF09:
mpi->num_planes=4;
case IMGFMT_YVU9:
case IMGFMT_444P:
case IMGFMT_422P:
case IMGFMT_411P:
case IMGFMT_440P:
case IMGFMT_444P16_LE:
case IMGFMT_444P16_BE:
case IMGFMT_444P14_LE:
case IMGFMT_444P14_BE:
case IMGFMT_444P12_LE:
case IMGFMT_444P12_BE:
case IMGFMT_444P10_LE:
case IMGFMT_444P10_BE:
case IMGFMT_444P9_LE:
case IMGFMT_444P9_BE:
case IMGFMT_422P16_LE:
case IMGFMT_422P16_BE:
case IMGFMT_422P14_LE:
case IMGFMT_422P14_BE:
case IMGFMT_422P12_LE:
case IMGFMT_422P12_BE:
case IMGFMT_422P10_LE:
case IMGFMT_422P10_BE:
case IMGFMT_422P9_LE:
case IMGFMT_422P9_BE:
case IMGFMT_420P16_LE:
case IMGFMT_420P16_BE:
case IMGFMT_420P14_LE:
case IMGFMT_420P14_BE:
case IMGFMT_420P12_LE:
case IMGFMT_420P12_BE:
case IMGFMT_420P10_LE:
case IMGFMT_420P10_BE:
case IMGFMT_420P9_LE:
case IMGFMT_420P9_BE:
return;
case IMGFMT_Y16_LE:
case IMGFMT_Y16_BE:
mpi->bpp=16;
case IMGFMT_Y800:
case IMGFMT_Y8:
/* they're planar ones, but for easier handling use them as packed */
mpi->flags&=~MP_IMGFLAG_PLANAR;
mpi->num_planes=1;
return;
case IMGFMT_Y8A:
mpi->num_planes=2;
return;
case IMGFMT_UYVY:
mpi->flags|=MP_IMGFLAG_SWAPPED;
case IMGFMT_YUY2:
mpi->chroma_x_shift = 1;
mpi->bpp=16;
mpi->num_planes=1;
return;
case IMGFMT_NV12:
mpi->flags|=MP_IMGFLAG_SWAPPED;
case IMGFMT_NV21:
mpi->flags|=MP_IMGFLAG_PLANAR;
mpi->bpp=12;
mpi->num_planes=2;
mpi->chroma_width=(mpi->width>>0);
mpi->chroma_height=(mpi->height>>1);
mpi->chroma_x_shift=0;
mpi->chroma_y_shift=1;
return;
}
ff_mp_msg(MSGT_DECVIDEO,MSGL_WARN,"mp_image: unknown out_fmt: 0x%X\n",out_fmt);
mpi->bpp=0;
}
mp_image_t* ff_new_mp_image(int w,int h){
mp_image_t* mpi = malloc(sizeof(mp_image_t));
if(!mpi) return NULL; // error!
memset(mpi,0,sizeof(mp_image_t));
mpi->width=mpi->w=w;
mpi->height=mpi->h=h;
return mpi;
}
void ff_free_mp_image(mp_image_t* mpi){
if(!mpi) return;
if(mpi->flags&MP_IMGFLAG_ALLOCATED){
/* becouse we allocate the whole image in once */
av_free(mpi->planes[0]);
if (mpi->flags & MP_IMGFLAG_RGB_PALETTE)
av_free(mpi->planes[1]);
}
free(mpi);
}
@@ -0,0 +1,159 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_MP_IMAGE_H
#define MPLAYER_MP_IMAGE_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#undef printf //FIXME
#undef fprintf //FIXME
#include "mp_msg.h"
#include "libavutil/avutil.h"
#include "libavutil/avassert.h"
#undef realloc
#undef malloc
#undef free
#undef rand
#undef srand
#undef printf
#undef strncpy
#define ASMALIGN(ZEROBITS) ".p2align " #ZEROBITS "\n\t"
#define CODEC_FLAG2_MEMC_ONLY 0x00001000 ///< Only do ME/MC (I frames -> ref, P frame -> ME+MC).
enum AVPixelFormat ff_mp2ff_pix_fmt(int mp);
//--------- codec's requirements (filled by the codec/vf) ---------
//--- buffer content restrictions:
// set if buffer content shouldn't be modified:
#define MP_IMGFLAG_PRESERVE 0x01
// set if buffer content will be READ.
// This can be e.g. for next frame's MC: (I/P mpeg frames) -
// then in combination with MP_IMGFLAG_PRESERVE - or it
// can be because a video filter or codec will read a significant
// amount of data while processing that frame (e.g. blending something
// onto the frame, MV based intra prediction).
// A frame marked like this should not be placed in to uncachable
// video RAM for example.
#define MP_IMGFLAG_READABLE 0x02
//--- buffer width/stride/plane restrictions: (used for direct rendering)
// stride _have_to_ be aligned to MB boundary: [for DR restrictions]
#define MP_IMGFLAG_ACCEPT_ALIGNED_STRIDE 0x4
// stride should be aligned to MB boundary: [for buffer allocation]
#define MP_IMGFLAG_PREFER_ALIGNED_STRIDE 0x8
// codec accept any stride (>=width):
#define MP_IMGFLAG_ACCEPT_STRIDE 0x10
// codec accept any width (width*bpp=stride -> stride%bpp==0) (>=width):
#define MP_IMGFLAG_ACCEPT_WIDTH 0x20
//--- for planar formats only:
// uses only stride[0], and stride[1]=stride[2]=stride[0]>>mpi->chroma_x_shift
#define MP_IMGFLAG_COMMON_STRIDE 0x40
// uses only planes[0], and calculates planes[1,2] from width,height,imgfmt
#define MP_IMGFLAG_COMMON_PLANE 0x80
#define MP_IMGFLAGMASK_RESTRICTIONS 0xFF
//--------- color info (filled by ff_mp_image_setfmt() ) -----------
// set if number of planes > 1
#define MP_IMGFLAG_PLANAR 0x100
// set if it's YUV colorspace
#define MP_IMGFLAG_YUV 0x200
// set if it's swapped (BGR or YVU) plane/byteorder
#define MP_IMGFLAG_SWAPPED 0x400
// set if you want memory for palette allocated and managed by ff_vf_get_image etc.
#define MP_IMGFLAG_RGB_PALETTE 0x800
#define MP_IMGFLAGMASK_COLORS 0xF00
// codec uses drawing/rendering callbacks (draw_slice()-like thing, DR method 2)
// [the codec will set this flag if it supports callbacks, and the vo _may_
// clear it in get_image() if draw_slice() not implemented]
#define MP_IMGFLAG_DRAW_CALLBACK 0x1000
// set if it's in video buffer/memory: [set by vo/vf's get_image() !!!]
#define MP_IMGFLAG_DIRECT 0x2000
// set if buffer is allocated (used in destination images):
#define MP_IMGFLAG_ALLOCATED 0x4000
// buffer type was printed (do NOT set this flag - it's for INTERNAL USE!!!)
#define MP_IMGFLAG_TYPE_DISPLAYED 0x8000
// codec doesn't support any form of direct rendering - it has own buffer
// allocation. so we just export its buffer pointers:
#define MP_IMGTYPE_EXPORT 0
// codec requires a static WO buffer, but it does only partial updates later:
#define MP_IMGTYPE_STATIC 1
// codec just needs some WO memory, where it writes/copies the whole frame to:
#define MP_IMGTYPE_TEMP 2
// I+P type, requires 2+ independent static R/W buffers
#define MP_IMGTYPE_IP 3
// I+P+B type, requires 2+ independent static R/W and 1+ temp WO buffers
#define MP_IMGTYPE_IPB 4
// Upper 16 bits give desired buffer number, -1 means get next available
#define MP_IMGTYPE_NUMBERED 5
// Doesn't need any buffer, incomplete image (probably a first field only)
// we need this type to be able to differentiate between half frames and
// all other cases
#define MP_IMGTYPE_INCOMPLETE 6
#define MP_MAX_PLANES 4
#define MP_IMGFIELD_ORDERED 0x01
#define MP_IMGFIELD_TOP_FIRST 0x02
#define MP_IMGFIELD_REPEAT_FIRST 0x04
#define MP_IMGFIELD_TOP 0x08
#define MP_IMGFIELD_BOTTOM 0x10
#define MP_IMGFIELD_INTERLACED 0x20
typedef struct mp_image {
unsigned int flags;
unsigned char type;
int number;
unsigned char bpp; // bits/pixel. NOT depth! for RGB it will be n*8
unsigned int imgfmt;
int width,height; // stored dimensions
int x,y,w,h; // visible dimensions
unsigned char* planes[MP_MAX_PLANES];
int stride[MP_MAX_PLANES];
char * qscale;
int qstride;
int pict_type; // 0->unknown, 1->I, 2->P, 3->B
int fields;
int qscale_type; // 0->mpeg1/4/h263, 1->mpeg2
int num_planes;
/* these are only used by planar formats Y,U(Cb),V(Cr) */
int chroma_width;
int chroma_height;
int chroma_x_shift; // horizontal
int chroma_y_shift; // vertical
int usage_count;
/* for private use by filter or vo driver (to store buffer id or dmpi) */
void* priv;
} mp_image_t;
void ff_mp_image_setfmt(mp_image_t* mpi,unsigned int out_fmt);
mp_image_t* ff_new_mp_image(int w,int h);
void ff_free_mp_image(mp_image_t* mpi);
mp_image_t* ff_alloc_mpi(int w, int h, unsigned long int fmt);
void ff_mp_image_alloc_planes(mp_image_t *mpi);
void ff_copy_mpi(mp_image_t *dmpi, mp_image_t *mpi);
#endif /* MPLAYER_MP_IMAGE_H */
@@ -0,0 +1,166 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_MP_MSG_H
#define MPLAYER_MP_MSG_H
#include <stdarg.h>
// defined in mplayer.c and mencoder.c
extern int verbose;
// verbosity elevel:
/* Only messages level MSGL_FATAL-MSGL_STATUS should be translated,
* messages level MSGL_V and above should not be translated. */
#define MSGL_FATAL 0 // will exit/abort
#define MSGL_ERR 1 // continues
#define MSGL_WARN 2 // only warning
#define MSGL_HINT 3 // short help message
#define MSGL_INFO 4 // -quiet
#define MSGL_STATUS 5 // v=0
#define MSGL_V 6 // v=1
#define MSGL_DBG2 7 // v=2
#define MSGL_DBG3 8 // v=3
#define MSGL_DBG4 9 // v=4
#define MSGL_DBG5 10 // v=5
#define MSGL_FIXME 1 // for conversions from printf where the appropriate MSGL is not known; set equal to ERR for obtrusiveness
#define MSGT_FIXME 0 // for conversions from printf where the appropriate MSGT is not known; set equal to GLOBAL for obtrusiveness
// code/module:
#define MSGT_GLOBAL 0 // common player stuff errors
#define MSGT_CPLAYER 1 // console player (mplayer.c)
#define MSGT_GPLAYER 2 // gui player
#define MSGT_VO 3 // libvo
#define MSGT_AO 4 // libao
#define MSGT_DEMUXER 5 // demuxer.c (general stuff)
#define MSGT_DS 6 // demux stream (add/read packet etc)
#define MSGT_DEMUX 7 // fileformat-specific stuff (demux_*.c)
#define MSGT_HEADER 8 // fileformat-specific header (*header.c)
#define MSGT_AVSYNC 9 // mplayer.c timer stuff
#define MSGT_AUTOQ 10 // mplayer.c auto-quality stuff
#define MSGT_CFGPARSER 11 // cfgparser.c
#define MSGT_DECAUDIO 12 // av decoder
#define MSGT_DECVIDEO 13
#define MSGT_SEEK 14 // seeking code
#define MSGT_WIN32 15 // win32 dll stuff
#define MSGT_OPEN 16 // open.c (stream opening)
#define MSGT_DVD 17 // open.c (DVD init/read/seek)
#define MSGT_PARSEES 18 // parse_es.c (mpeg stream parser)
#define MSGT_LIRC 19 // lirc_mp.c and input lirc driver
#define MSGT_STREAM 20 // stream.c
#define MSGT_CACHE 21 // cache2.c
#define MSGT_MENCODER 22
#define MSGT_XACODEC 23 // XAnim codecs
#define MSGT_TV 24 // TV input subsystem
#define MSGT_OSDEP 25 // OS-dependent parts
#define MSGT_SPUDEC 26 // spudec.c
#define MSGT_PLAYTREE 27 // Playtree handeling (playtree.c, playtreeparser.c)
#define MSGT_INPUT 28
#define MSGT_VFILTER 29
#define MSGT_OSD 30
#define MSGT_NETWORK 31
#define MSGT_CPUDETECT 32
#define MSGT_CODECCFG 33
#define MSGT_SWS 34
#define MSGT_VOBSUB 35
#define MSGT_SUBREADER 36
#define MSGT_AFILTER 37 // Audio filter messages
#define MSGT_NETST 38 // Netstream
#define MSGT_MUXER 39 // muxer layer
#define MSGT_OSD_MENU 40
#define MSGT_IDENTIFY 41 // -identify output
#define MSGT_RADIO 42
#define MSGT_ASS 43 // libass messages
#define MSGT_LOADER 44 // dll loader messages
#define MSGT_STATUSLINE 45 // playback/encoding status line
#define MSGT_TELETEXT 46 // Teletext decoder
#define MSGT_MAX 64
extern char *ff_mp_msg_charset;
extern int ff_mp_msg_color;
extern int ff_mp_msg_module;
extern int ff_mp_msg_levels[MSGT_MAX];
extern int ff_mp_msg_level_all;
void ff_mp_msg_init(void);
int ff_mp_msg_test(int mod, int lev);
#include "config.h"
void ff_mp_msg_va(int mod, int lev, const char *format, va_list va);
#ifdef __GNUC__
void ff_mp_msg(int mod, int lev, const char *format, ... ) __attribute__ ((format (printf, 3, 4)));
# ifdef MP_DEBUG
# define mp_dbg(mod,lev, args... ) ff_mp_msg(mod, lev, ## args )
# else
// only useful for developers, disable but check syntax
# define mp_dbg(mod,lev, args... ) do { if (0) ff_mp_msg(mod, lev, ## args ); } while (0)
# endif
#else // not GNU C
void ff_mp_msg(int mod, int lev, const char *format, ... );
# ifdef MP_DEBUG
# define mp_dbg(mod,lev, ... ) ff_mp_msg(mod, lev, __VA_ARGS__)
# else
// only useful for developers, disable but check syntax
# define mp_dbg(mod,lev, ... ) do { if (0) ff_mp_msg(mod, lev, __VA_ARGS__); } while (0)
# endif
#endif /* __GNUC__ */
const char* ff_filename_recode(const char* filename);
#endif /* MPLAYER_MP_MSG_H */
@@ -0,0 +1,43 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_MPC_INFO_H
#define MPLAYER_MPC_INFO_H
typedef struct mp_codec_info_s
{
/* codec long name ("Autodesk FLI/FLC Animation decoder" */
const char *name;
/* short name (same as driver name in codecs.conf) ("dshow") */
const char *short_name;
/* interface author/maintainer */
const char *maintainer;
/* codec author ("Aaron Holtzman <[email protected]>") */
const char *author;
/* any additional comments */
const char *comment;
} mp_codec_info_t;
#define CONTROL_OK 1
#define CONTROL_TRUE 1
#define CONTROL_FALSE 0
#define CONTROL_UNKNOWN -1
#define CONTROL_ERROR -2
#define CONTROL_NA -3
#endif /* MPLAYER_MPC_INFO_H */
+169
View File
@@ -0,0 +1,169 @@
/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPLAYER_VF_H
#define MPLAYER_VF_H
//#include "m_option.h"
#include "mp_image.h"
//extern m_obj_settings_t* vf_settings;
//extern const m_obj_list_t vf_obj_list;
struct vf_instance;
struct vf_priv_s;
typedef struct vf_info_s {
const char *info;
const char *name;
const char *author;
const char *comment;
int (*vf_open)(struct vf_instance *vf,char* args);
// Ptr to a struct dscribing the options
const void* opts;
} vf_info_t;
#define NUM_NUMBERED_MPI 50
typedef struct vf_image_context_s {
mp_image_t* static_images[2];
mp_image_t* temp_images[1];
mp_image_t* export_images[1];
mp_image_t* numbered_images[NUM_NUMBERED_MPI];
int static_idx;
} vf_image_context_t;
typedef struct vf_format_context_t {
int have_configured;
int orig_width, orig_height, orig_fmt;
} vf_format_context_t;
typedef struct vf_instance {
const vf_info_t* info;
// funcs:
int (*config)(struct vf_instance *vf,
int width, int height, int d_width, int d_height,
unsigned int flags, unsigned int outfmt);
int (*control)(struct vf_instance *vf,
int request, void* data);
int (*query_format)(struct vf_instance *vf,
unsigned int fmt);
void (*get_image)(struct vf_instance *vf,
mp_image_t *mpi);
int (*put_image)(struct vf_instance *vf,
mp_image_t *mpi, double pts);
void (*start_slice)(struct vf_instance *vf,
mp_image_t *mpi);
void (*draw_slice)(struct vf_instance *vf,
unsigned char** src, int* stride, int w,int h, int x, int y);
void (*uninit)(struct vf_instance *vf);
int (*continue_buffered_image)(struct vf_instance *vf);
// caps:
unsigned int default_caps; // used by default query_format()
unsigned int default_reqs; // used by default config()
// data:
int w, h;
vf_image_context_t imgctx;
vf_format_context_t fmt;
struct vf_instance *next;
mp_image_t *dmpi;
struct vf_priv_s* priv;
} vf_instance_t;
// control codes:
#include "mpc_info.h"
typedef struct vf_seteq_s
{
const char *item;
int value;
} vf_equalizer_t;
#define VFCTRL_QUERY_MAX_PP_LEVEL 4 /* test for postprocessing support (max level) */
#define VFCTRL_SET_PP_LEVEL 5 /* set postprocessing level */
#define VFCTRL_SET_EQUALIZER 6 /* set color options (brightness,contrast etc) */
#define VFCTRL_GET_EQUALIZER 8 /* gset color options (brightness,contrast etc) */
#define VFCTRL_DRAW_OSD 7
#define VFCTRL_CHANGE_RECTANGLE 9 /* Change the rectangle boundaries */
#define VFCTRL_FLIP_PAGE 10 /* Tell the vo to flip pages */
#define VFCTRL_DUPLICATE_FRAME 11 /* For encoding - encode zero-change frame */
#define VFCTRL_SKIP_NEXT_FRAME 12 /* For encoding - drop the next frame that passes thru */
#define VFCTRL_FLUSH_FRAMES 13 /* For encoding - flush delayed frames */
#define VFCTRL_SCREENSHOT 14 /* Make a screenshot */
#define VFCTRL_INIT_EOSD 15 /* Select EOSD renderer */
#define VFCTRL_DRAW_EOSD 16 /* Render EOSD */
#define VFCTRL_GET_PTS 17 /* Return last pts value that reached vf_vo*/
#define VFCTRL_SET_DEINTERLACE 18 /* Set deinterlacing status */
#define VFCTRL_GET_DEINTERLACE 19 /* Get deinterlacing status */
#include "vfcap.h"
//FIXME this should be in a common header, but i dunno which
#define MP_NOPTS_VALUE (-1LL<<63) //both int64_t and double should be able to represent this exactly
// functions:
void ff_vf_mpi_clear(mp_image_t* mpi,int x0,int y0,int w,int h);
mp_image_t* ff_vf_get_image(vf_instance_t* vf, unsigned int outfmt, int mp_imgtype, int mp_imgflag, int w, int h);
vf_instance_t* vf_open_plugin(const vf_info_t* const* filter_list, vf_instance_t* next, const char *name, char **args);
vf_instance_t* vf_open_filter(vf_instance_t* next, const char *name, char **args);
vf_instance_t* ff_vf_add_before_vo(vf_instance_t **vf, char *name, char **args);
vf_instance_t* vf_open_encoder(vf_instance_t* next, const char *name, char *args);
unsigned int ff_vf_match_csp(vf_instance_t** vfp,const unsigned int* list,unsigned int preferred);
void ff_vf_clone_mpi_attributes(mp_image_t* dst, mp_image_t* src);
void ff_vf_queue_frame(vf_instance_t *vf, int (*)(vf_instance_t *));
int ff_vf_output_queued_frame(vf_instance_t *vf);
// default wrappers:
int ff_vf_next_config(struct vf_instance *vf,
int width, int height, int d_width, int d_height,
unsigned int flags, unsigned int outfmt);
int ff_vf_next_control(struct vf_instance *vf, int request, void* data);
void ff_vf_extra_flip(struct vf_instance *vf);
int ff_vf_next_query_format(struct vf_instance *vf, unsigned int fmt);
int ff_vf_next_put_image(struct vf_instance *vf,mp_image_t *mpi, double pts);
void ff_vf_next_draw_slice (struct vf_instance *vf, unsigned char** src, int* stride, int w,int h, int x, int y);
vf_instance_t* ff_append_filters(vf_instance_t* last);
void ff_vf_uninit_filter(vf_instance_t* vf);
void ff_vf_uninit_filter_chain(vf_instance_t* vf);
int ff_vf_config_wrapper(struct vf_instance *vf,
int width, int height, int d_width, int d_height,
unsigned int flags, unsigned int outfmt);
static inline int norm_qscale(int qscale, int type)
{
switch (type) {
case 0: // MPEG-1
return qscale;
case 1: // MPEG-2
return qscale >> 1;
case 2: // H264
return qscale >> 2;
case 3: // VP56
return (63 - qscale + 2) >> 2;
}
return qscale;
}
#endif /* MPLAYER_VF_H */

Some files were not shown because too many files have changed in this diff Show More