* cigaes/master: lavfi/af_pan: support unknown layouts on input. lavfi/af_pan: support unknown layouts on output. lswr: fix assert failure on unknown layouts. lavfi: parsing helper for unknown channel layouts. lavfi/avfiltergraph: do not reduce incompatible lists. lavfi/avfiltergraph: suggest a solution when format selection fails. lavd/lavfi: support unknown channel layouts. lavf/wavenc: check for a single stream. lavd/alsa: add stream validation lavd/alsa: fix timestamp calculation Merged-by: Michael Niedermayer <michaelni@gmx.at>tags/n2.2-rc1
@@ -47,12 +47,17 @@ | |||||
static av_cold int audio_write_header(AVFormatContext *s1) | static av_cold int audio_write_header(AVFormatContext *s1) | ||||
{ | { | ||||
AlsaData *s = s1->priv_data; | AlsaData *s = s1->priv_data; | ||||
AVStream *st; | |||||
AVStream *st = NULL; | |||||
unsigned int sample_rate; | unsigned int sample_rate; | ||||
enum AVCodecID codec_id; | enum AVCodecID codec_id; | ||||
int res; | int res; | ||||
if (s1->nb_streams != 1 || s1->streams[0]->codec->codec_type != AVMEDIA_TYPE_AUDIO) { | |||||
av_log(s1, AV_LOG_ERROR, "Only a single audio stream is supported.\n"); | |||||
return AVERROR(EINVAL); | |||||
} | |||||
st = s1->streams[0]; | st = s1->streams[0]; | ||||
sample_rate = st->codec->sample_rate; | sample_rate = st->codec->sample_rate; | ||||
codec_id = st->codec->codec_id; | codec_id = st->codec->codec_id; | ||||
res = ff_alsa_open(s1, SND_PCM_STREAM_PLAYBACK, &sample_rate, | res = ff_alsa_open(s1, SND_PCM_STREAM_PLAYBACK, &sample_rate, | ||||
@@ -80,6 +85,10 @@ static int audio_write_packet(AVFormatContext *s1, AVPacket *pkt) | |||||
uint8_t *buf = pkt->data; | uint8_t *buf = pkt->data; | ||||
size /= s->frame_size; | size /= s->frame_size; | ||||
if (pkt->dts != AV_NOPTS_VALUE) | |||||
s->timestamp = pkt->dts; | |||||
s->timestamp += pkt->duration ? pkt->duration : size; | |||||
if (s->reorder_func) { | if (s->reorder_func) { | ||||
if (size > s->reorder_buf_size) | if (size > s->reorder_buf_size) | ||||
if (ff_alsa_extend_reorder_buf(s, size)) | if (ff_alsa_extend_reorder_buf(s, size)) | ||||
@@ -112,7 +121,7 @@ audio_get_output_timestamp(AVFormatContext *s1, int stream, | |||||
snd_pcm_sframes_t delay = 0; | snd_pcm_sframes_t delay = 0; | ||||
*wall = av_gettime(); | *wall = av_gettime(); | ||||
snd_pcm_delay(s->h, &delay); | snd_pcm_delay(s->h, &delay); | ||||
*dts = s1->streams[0]->cur_dts - delay; | |||||
*dts = s->timestamp - delay; | |||||
} | } | ||||
AVOutputFormat ff_alsa_muxer = { | AVOutputFormat ff_alsa_muxer = { | ||||
@@ -57,6 +57,7 @@ typedef struct AlsaData { | |||||
void (*reorder_func)(const void *, void *, int); | void (*reorder_func)(const void *, void *, int); | ||||
void *reorder_buf; | void *reorder_buf; | ||||
int reorder_buf_size; ///< in frames | int reorder_buf_size; ///< in frames | ||||
int64_t timestamp; ///< current timestamp, without latency applied. | |||||
} AlsaData; | } AlsaData; | ||||
/** | /** | ||||
@@ -248,6 +248,10 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx) | |||||
ret = av_opt_set_int_list(sink, "sample_fmts", sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN); | ret = av_opt_set_int_list(sink, "sample_fmts", sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN); | ||||
if (ret < 0) | if (ret < 0) | ||||
goto end; | goto end; | ||||
ret = av_opt_set_int(sink, "all_channel_counts", 1, | |||||
AV_OPT_SEARCH_CHILDREN); | |||||
if (ret < 0) | |||||
goto end; | |||||
} | } | ||||
lavfi->sinks[i] = sink; | lavfi->sinks[i] = sink; | ||||
@@ -66,7 +66,7 @@ static av_cold int init(AVFilterContext *ctx) | |||||
(ret = ff_parse_sample_format(&aconvert->out_sample_fmt, aconvert->format_str, ctx)) < 0) | (ret = ff_parse_sample_format(&aconvert->out_sample_fmt, aconvert->format_str, ctx)) < 0) | ||||
return ret; | return ret; | ||||
if (aconvert->channel_layout_str && strcmp(aconvert->channel_layout_str, "auto")) | if (aconvert->channel_layout_str && strcmp(aconvert->channel_layout_str, "auto")) | ||||
return ff_parse_channel_layout(&aconvert->out_chlayout, aconvert->channel_layout_str, ctx); | |||||
return ff_parse_channel_layout(&aconvert->out_chlayout, NULL, aconvert->channel_layout_str, ctx); | |||||
return ret; | return ret; | ||||
} | } | ||||
@@ -46,7 +46,6 @@ typedef struct PanContext { | |||||
double gain[MAX_CHANNELS][MAX_CHANNELS]; | double gain[MAX_CHANNELS][MAX_CHANNELS]; | ||||
int64_t need_renorm; | int64_t need_renorm; | ||||
int need_renumber; | int need_renumber; | ||||
int nb_input_channels; | |||||
int nb_output_channels; | int nb_output_channels; | ||||
int pure_gains; | int pure_gains; | ||||
@@ -116,10 +115,10 @@ static av_cold int init(AVFilterContext *ctx) | |||||
if (!args) | if (!args) | ||||
return AVERROR(ENOMEM); | return AVERROR(ENOMEM); | ||||
arg = av_strtok(args, "|", &tokenizer); | arg = av_strtok(args, "|", &tokenizer); | ||||
ret = ff_parse_channel_layout(&pan->out_channel_layout, arg, ctx); | |||||
ret = ff_parse_channel_layout(&pan->out_channel_layout, | |||||
&pan->nb_output_channels, arg, ctx); | |||||
if (ret < 0) | if (ret < 0) | ||||
goto fail; | goto fail; | ||||
pan->nb_output_channels = av_get_channel_layout_nb_channels(pan->out_channel_layout); | |||||
/* parse channel specifications */ | /* parse channel specifications */ | ||||
while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) { | while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) { | ||||
@@ -239,12 +238,14 @@ static int query_formats(AVFilterContext *ctx) | |||||
ff_set_common_samplerates(ctx, formats); | ff_set_common_samplerates(ctx, formats); | ||||
// inlink supports any channel layout | // inlink supports any channel layout | ||||
layouts = ff_all_channel_layouts(); | |||||
layouts = ff_all_channel_counts(); | |||||
ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts); | ff_channel_layouts_ref(layouts, &inlink->out_channel_layouts); | ||||
// outlink supports only requested output channel layout | // outlink supports only requested output channel layout | ||||
layouts = NULL; | layouts = NULL; | ||||
ff_add_channel_layout(&layouts, pan->out_channel_layout); | |||||
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); | ff_channel_layouts_ref(layouts, &outlink->in_channel_layouts); | ||||
return 0; | return 0; | ||||
} | } | ||||
@@ -257,7 +258,6 @@ static int config_props(AVFilterLink *link) | |||||
int i, j, k, r; | int i, j, k, r; | ||||
double t; | double t; | ||||
pan->nb_input_channels = av_get_channel_layout_nb_channels(link->channel_layout); | |||||
if (pan->need_renumber) { | if (pan->need_renumber) { | ||||
// input channels were given by their name: renumber them | // input channels were given by their name: renumber them | ||||
for (i = j = 0; i < MAX_CHANNELS; i++) { | for (i = j = 0; i < MAX_CHANNELS; i++) { | ||||
@@ -271,7 +271,7 @@ static int config_props(AVFilterLink *link) | |||||
// sanity check; can't be done in query_formats since the inlink | // sanity check; can't be done in query_formats since the inlink | ||||
// channel layout is unknown at that time | // channel layout is unknown at that time | ||||
if (pan->nb_input_channels > SWR_CH_MAX || | |||||
if (link->channels > SWR_CH_MAX || | |||||
pan->nb_output_channels > SWR_CH_MAX) { | pan->nb_output_channels > SWR_CH_MAX) { | ||||
av_log(ctx, AV_LOG_ERROR, | av_log(ctx, AV_LOG_ERROR, | ||||
"libswresample support a maximum of %d channels. " | "libswresample support a maximum of %d channels. " | ||||
@@ -286,6 +286,10 @@ static int config_props(AVFilterLink *link) | |||||
0, ctx); | 0, ctx); | ||||
if (!pan->swr) | if (!pan->swr) | ||||
return AVERROR(ENOMEM); | 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 | // gains are pure, init the channel mapping | ||||
if (pan->pure_gains) { | if (pan->pure_gains) { | ||||
@@ -293,7 +297,7 @@ static int config_props(AVFilterLink *link) | |||||
// get channel map from the pure gains | // get channel map from the pure gains | ||||
for (i = 0; i < pan->nb_output_channels; i++) { | for (i = 0; i < pan->nb_output_channels; i++) { | ||||
int ch_id = -1; | int ch_id = -1; | ||||
for (j = 0; j < pan->nb_input_channels; j++) { | |||||
for (j = 0; j < link->channels; j++) { | |||||
if (pan->gain[i][j]) { | if (pan->gain[i][j]) { | ||||
ch_id = j; | ch_id = j; | ||||
break; | break; | ||||
@@ -311,7 +315,7 @@ static int config_props(AVFilterLink *link) | |||||
if (!((pan->need_renorm >> i) & 1)) | if (!((pan->need_renorm >> i) & 1)) | ||||
continue; | continue; | ||||
t = 0; | t = 0; | ||||
for (j = 0; j < pan->nb_input_channels; j++) | |||||
for (j = 0; j < link->channels; j++) | |||||
t += pan->gain[i][j]; | t += pan->gain[i][j]; | ||||
if (t > -1E-5 && t < 1E-5) { | if (t > -1E-5 && t < 1E-5) { | ||||
// t is almost 0 but not exactly, this is probably a mistake | // t is almost 0 but not exactly, this is probably a mistake | ||||
@@ -320,7 +324,7 @@ static int config_props(AVFilterLink *link) | |||||
"Degenerate coefficients while renormalizing\n"); | "Degenerate coefficients while renormalizing\n"); | ||||
continue; | continue; | ||||
} | } | ||||
for (j = 0; j < pan->nb_input_channels; j++) | |||||
for (j = 0; j < link->channels; j++) | |||||
pan->gain[i][j] /= t; | pan->gain[i][j] /= t; | ||||
} | } | ||||
av_opt_set_int(pan->swr, "icl", link->channel_layout, 0); | av_opt_set_int(pan->swr, "icl", link->channel_layout, 0); | ||||
@@ -335,7 +339,7 @@ static int config_props(AVFilterLink *link) | |||||
// summary | // summary | ||||
for (i = 0; i < pan->nb_output_channels; i++) { | for (i = 0; i < pan->nb_output_channels; i++) { | ||||
cur = buf; | cur = buf; | ||||
for (j = 0; j < pan->nb_input_channels; j++) { | |||||
for (j = 0; j < link->channels; j++) { | |||||
r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d", | r = snprintf(cur, buf + sizeof(buf) - cur, "%s%.3g i%d", | ||||
j ? " + " : "", pan->gain[i][j], j); | j ? " + " : "", pan->gain[i][j], j); | ||||
cur += FFMIN(buf + sizeof(buf) - cur, r); | cur += FFMIN(buf + sizeof(buf) - cur, r); | ||||
@@ -109,7 +109,7 @@ static av_cold int init(AVFilterContext *ctx) | |||||
if (eval->chlayout_str) { | if (eval->chlayout_str) { | ||||
int n; | int n; | ||||
ret = ff_parse_channel_layout(&eval->chlayout, eval->chlayout_str, ctx); | |||||
ret = ff_parse_channel_layout(&eval->chlayout, NULL, eval->chlayout_str, ctx); | |||||
if (ret < 0) | if (ret < 0) | ||||
goto end; | goto end; | ||||
@@ -68,7 +68,7 @@ static av_cold int init(AVFilterContext *ctx) | |||||
null->sample_rate_str, ctx)) < 0) | null->sample_rate_str, ctx)) < 0) | ||||
return ret; | return ret; | ||||
if ((ret = ff_parse_channel_layout(&null->channel_layout, | |||||
if ((ret = ff_parse_channel_layout(&null->channel_layout, NULL, | |||||
null->channel_layout_str, ctx)) < 0) | null->channel_layout_str, ctx)) < 0) | ||||
return ret; | return ret; | ||||
@@ -654,6 +654,10 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref) | |||||
av_log(link->src, AV_LOG_ERROR, "Cannot select channel layout for" | av_log(link->src, AV_LOG_ERROR, "Cannot select channel layout for" | ||||
" the link between filters %s and %s.\n", link->src->name, | " the link between filters %s and %s.\n", link->src->name, | ||||
link->dst->name); | link->dst->name); | ||||
if (!link->in_channel_layouts->all_counts) | |||||
av_log(link->src, AV_LOG_ERROR, "Unknown channel layouts not " | |||||
"supported, try specifying a channel layout using " | |||||
"'aformat=channel_layouts=something'.\n"); | |||||
return AVERROR(EINVAL); | return AVERROR(EINVAL); | ||||
} | } | ||||
link->in_channel_layouts->nb_channel_layouts = 1; | link->in_channel_layouts->nb_channel_layouts = 1; | ||||
@@ -737,7 +741,8 @@ static int reduce_formats_on_filter(AVFilterContext *filter) | |||||
if (inlink->type != outlink->type || fmts->nb_channel_layouts == 1) | if (inlink->type != outlink->type || fmts->nb_channel_layouts == 1) | ||||
continue; | continue; | ||||
if (fmts->all_layouts) { | |||||
if (fmts->all_layouts && | |||||
(!FF_LAYOUT2COUNT(fmt) || fmts->all_counts)) { | |||||
/* Turn the infinite list into a singleton */ | /* Turn the infinite list into a singleton */ | ||||
fmts->all_layouts = fmts->all_counts = 0; | fmts->all_layouts = fmts->all_counts = 0; | ||||
ff_add_channel_layout(&outlink->in_channel_layouts, fmt); | ff_add_channel_layout(&outlink->in_channel_layouts, fmt); | ||||
@@ -615,10 +615,21 @@ int ff_parse_sample_rate(int *ret, const char *arg, void *log_ctx) | |||||
return 0; | return 0; | ||||
} | } | ||||
int ff_parse_channel_layout(int64_t *ret, const char *arg, void *log_ctx) | |||||
int ff_parse_channel_layout(int64_t *ret, int *nret, const char *arg, | |||||
void *log_ctx) | |||||
{ | { | ||||
char *tail; | char *tail; | ||||
int64_t chlayout = av_get_channel_layout(arg); | |||||
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) { | if (chlayout == 0) { | ||||
chlayout = strtol(arg, &tail, 10); | chlayout = strtol(arg, &tail, 10); | ||||
if (*tail || chlayout == 0) { | if (*tail || chlayout == 0) { | ||||
@@ -627,6 +638,8 @@ int ff_parse_channel_layout(int64_t *ret, const char *arg, void *log_ctx) | |||||
} | } | ||||
} | } | ||||
*ret = chlayout; | *ret = chlayout; | ||||
if (nret) | |||||
*nret = av_get_channel_layout_nb_channels(chlayout); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -207,11 +207,14 @@ int ff_parse_sample_format(int *ret, const char *arg, void *log_ctx); | |||||
* Parse a channel layout or a corresponding integer representation. | * Parse a channel layout or a corresponding integer representation. | ||||
* | * | ||||
* @param ret 64bit integer pointer to where the value should be written. | * @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 arg string to parse | ||||
* @param log_ctx log context | * @param log_ctx log context | ||||
* @return >= 0 in case of success, a negative AVERROR code on error | * @return >= 0 in case of success, a negative AVERROR code on error | ||||
*/ | */ | ||||
int ff_parse_channel_layout(int64_t *ret, const char *arg, void *log_ctx); | |||||
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_update_link_current_pts(AVFilterLink *link, int64_t pts); | ||||
@@ -116,6 +116,11 @@ static int wav_write_header(AVFormatContext *s) | |||||
AVIOContext *pb = s->pb; | AVIOContext *pb = s->pb; | ||||
int64_t fmt; | int64_t fmt; | ||||
if (s->nb_streams != 1) { | |||||
av_log(s, AV_LOG_ERROR, "WAVE files have exactly one stream\n"); | |||||
return AVERROR(EINVAL); | |||||
} | |||||
if (wav->rf64 == RF64_ALWAYS) { | if (wav->rf64 == RF64_ALWAYS) { | ||||
ffio_wfourcc(pb, "RF64"); | ffio_wfourcc(pb, "RF64"); | ||||
avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */ | avio_wl32(pb, -1); /* RF64 chunk size: use size in ds64 */ | ||||
@@ -433,8 +433,8 @@ int swri_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mus | |||||
off = len1 * out->bps; | off = len1 * out->bps; | ||||
} | } | ||||
av_assert0(out->ch_count == av_get_channel_layout_nb_channels(s->out_ch_layout)); | |||||
av_assert0(in ->ch_count == av_get_channel_layout_nb_channels(s-> in_ch_layout)); | |||||
av_assert0(!s->out_ch_layout || out->ch_count == av_get_channel_layout_nb_channels(s->out_ch_layout)); | |||||
av_assert0(!s-> in_ch_layout || in ->ch_count == av_get_channel_layout_nb_channels(s-> in_ch_layout)); | |||||
for(out_i=0; out_i<out->ch_count; out_i++){ | for(out_i=0; out_i<out->ch_count; out_i++){ | ||||
switch(s->matrix_ch[out_i][0]){ | switch(s->matrix_ch[out_i][0]){ | ||||