@@ -20,6 +20,7 @@
#include <stdint.h>
+#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/libm.h"
#include "libavutil/samplefmt.h"
@@ -34,10 +35,8 @@ struct AudioMix {
AVAudioResampleContext *avr;
enum AVSampleFormat fmt;
enum AVMixCoeffType coeff_type;
- uint64_t in_layout;
- uint64_t out_layout;
- int in_channels;
- int out_channels;
+ AVChannelLayout in_layout;
+ AVChannelLayout out_layout;
int ptr_align;
int samples_align;
@@ -331,8 +330,8 @@ static av_cold int mix_function_init(AudioMix *am)
if (!am->mix) {
av_log(am->avr, AV_LOG_ERROR, "audio_mix: NO FUNCTION FOUND: [fmt=%s] "
"[c=%s] [%d to %d]\n", av_get_sample_fmt_name(am->fmt),
- coeff_type_names[am->coeff_type], am->in_channels,
- am->out_channels);
+ coeff_type_names[am->coeff_type], am->in_layout.nb_channels,
+ am->out_layout.nb_channels);
return AVERROR_PATCHWELCOME;
}
return 0;
@@ -358,38 +357,42 @@ AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr)
am->fmt = avr->internal_sample_fmt;
am->coeff_type = avr->mix_coeff_type;
- am->in_layout = avr->in_channel_layout;
- am->out_layout = avr->out_channel_layout;
- am->in_channels = avr->in_channels;
- am->out_channels = avr->out_channels;
+
+ ret = av_channel_layout_copy(&am->in_layout, &avr->in_ch_layout);
+ if (ret < 0)
+ goto error;
+ ret = av_channel_layout_copy(&am->out_layout, &avr->out_ch_layout);
+ if (ret < 0)
+ goto error;
/* build matrix if the user did not already set one */
if (avr->mix_matrix) {
- ret = ff_audio_mix_set_matrix(am, avr->mix_matrix, avr->in_channels);
+ ret = ff_audio_mix_set_matrix(am, avr->mix_matrix, avr->in_ch_layout.nb_channels);
if (ret < 0)
goto error;
av_freep(&avr->mix_matrix);
} else {
- double *matrix_dbl = av_mallocz(avr->out_channels * avr->in_channels *
+ double *matrix_dbl = av_mallocz(avr->out_ch_layout.nb_channels *
+ avr->in_ch_layout.nb_channels *
sizeof(*matrix_dbl));
if (!matrix_dbl)
goto error;
- ret = avresample_build_matrix(avr->in_channel_layout,
- avr->out_channel_layout,
- avr->center_mix_level,
- avr->surround_mix_level,
- avr->lfe_mix_level,
- avr->normalize_mix_level,
- matrix_dbl,
- avr->in_channels,
- avr->matrix_encoding);
+ ret = avresample_build_matrix2(&avr->in_ch_layout,
+ &avr->out_ch_layout,
+ avr->center_mix_level,
+ avr->surround_mix_level,
+ avr->lfe_mix_level,
+ avr->normalize_mix_level,
+ matrix_dbl,
+ avr->in_ch_layout.nb_channels,
+ avr->matrix_encoding);
if (ret < 0) {
av_free(matrix_dbl);
goto error;
}
- ret = ff_audio_mix_set_matrix(am, matrix_dbl, avr->in_channels);
+ ret = ff_audio_mix_set_matrix(am, matrix_dbl, avr->in_ch_layout.nb_channels);
if (ret < 0) {
av_log(avr, AV_LOG_ERROR, "error setting mix matrix\n");
av_free(matrix_dbl);
@@ -402,7 +405,7 @@ AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr)
return am;
error:
- av_free(am);
+ ff_audio_mix_free(&am);
return NULL;
}
@@ -422,11 +425,16 @@ void ff_audio_mix_free(AudioMix **am_p)
memset(am->matrix_q15, 0, sizeof(am->matrix_q15));
memset(am->matrix_flt, 0, sizeof(am->matrix_flt));
+ av_channel_layout_uninit(&am->in_layout);
+ av_channel_layout_uninit(&am->out_layout);
+
av_freep(am_p);
}
int ff_audio_mix(AudioMix *am, AudioData *src)
{
+ int in_channels = am->in_layout.nb_channels;
+ int out_channels = am->out_layout.nb_channels;
int use_generic = 1;
int len = src->nb_samples;
int i, j;
@@ -442,16 +450,16 @@ int ff_audio_mix(AudioMix *am, AudioData *src)
}
}
av_log(am->avr, AV_LOG_TRACE, "audio_mix: %d samples - %d to %d channels (%s)\n",
- src->nb_samples, am->in_channels, am->out_channels,
+ src->nb_samples, in_channels, out_channels,
use_generic ? am->func_descr_generic : am->func_descr);
if (am->in_matrix_channels && am->out_matrix_channels) {
uint8_t **data;
uint8_t *data0[AVRESAMPLE_MAX_CHANNELS] = { NULL };
- if (am->out_matrix_channels < am->out_channels ||
- am->in_matrix_channels < am->in_channels) {
- for (i = 0, j = 0; i < FFMAX(am->in_channels, am->out_channels); i++) {
+ if (am->out_matrix_channels < out_channels ||
+ am->in_matrix_channels < in_channels) {
+ for (i = 0, j = 0; i < FFMAX(in_channels, out_channels); i++) {
if (am->input_skip[i] || am->output_skip[i] || am->output_zero[i])
continue;
data0[j++] = src->data[i];
@@ -469,23 +477,25 @@ int ff_audio_mix(AudioMix *am, AudioData *src)
am->in_matrix_channels);
}
- if (am->out_matrix_channels < am->out_channels) {
- for (i = 0; i < am->out_channels; i++)
+ if (am->out_matrix_channels < out_channels) {
+ for (i = 0; i < out_channels; i++)
if (am->output_zero[i])
av_samples_set_silence(&src->data[i], 0, len, 1, am->fmt);
}
- ff_audio_data_set_channels(src, am->out_channels);
+ ff_audio_data_set_channels(src, out_channels);
return 0;
}
int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride)
{
+ int in_channels = am->in_layout.nb_channels;
+ int out_channels = am->out_layout.nb_channels;
int i, o, i0, o0;
- if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS ||
- am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) {
+ if (in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS ||
+ out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) {
av_log(am->avr, AV_LOG_ERROR, "Invalid channel counts\n");
return AVERROR(EINVAL);
}
@@ -495,8 +505,8 @@ int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride)
av_log(am->avr, AV_LOG_ERROR, "matrix is not set\n"); \
return AVERROR(EINVAL); \
} \
- for (o = 0, o0 = 0; o < am->out_channels; o++) { \
- for (i = 0, i0 = 0; i < am->in_channels; i++) { \
+ for (o = 0, o0 = 0; o < out_channels; o++) { \
+ for (i = 0, i0 = 0; i < in_channels; i++) { \
if (am->input_skip[i] || am->output_zero[o]) \
matrix[o * stride + i] = 0.0; \
else \
@@ -529,6 +539,8 @@ int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride)
static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
{
+ int in_channels = am->in_layout.nb_channels;
+ int out_channels = am->out_layout.nb_channels;
int i, o;
memset(am->output_zero, 0, sizeof(am->output_zero));
@@ -536,11 +548,11 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
memset(am->output_skip, 0, sizeof(am->output_skip));
/* exclude output channels if they can be zeroed instead of mixed */
- for (o = 0; o < am->out_channels; o++) {
+ for (o = 0; o < out_channels; o++) {
int zero = 1;
/* check if the output is always silent */
- for (i = 0; i < am->in_channels; i++) {
+ for (i = 0; i < in_channels; i++) {
if (matrix[o * stride + i] != 0.0) {
zero = 0;
break;
@@ -548,8 +560,8 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
}
/* check if the corresponding input channel makes a contribution to
any output channel */
- if (o < am->in_channels) {
- for (i = 0; i < am->out_channels; i++) {
+ if (o < in_channels) {
+ for (i = 0; i < out_channels; i++) {
if (matrix[i * stride + o] != 0.0) {
zero = 0;
break;
@@ -559,7 +571,7 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
if (zero) {
am->output_zero[o] = 1;
am->out_matrix_channels--;
- if (o < am->in_channels)
+ if (o < in_channels)
am->in_matrix_channels--;
}
}
@@ -571,10 +583,10 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
/* skip input channels that contribute fully only to the corresponding
output channel */
- for (i = 0; i < FFMIN(am->in_channels, am->out_channels); i++) {
+ for (i = 0; i < FFMIN(in_channels, out_channels); i++) {
int skip = 1;
- for (o = 0; o < am->out_channels; o++) {
+ for (o = 0; o < out_channels; o++) {
int i0;
if ((o != i && matrix[o * stride + i] != 0.0) ||
(o == i && matrix[o * stride + i] != 1.0)) {
@@ -584,7 +596,7 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
/* if the input contributes fully to the output, also check that no
other inputs contribute to this output */
if (o == i) {
- for (i0 = 0; i0 < am->in_channels; i0++) {
+ for (i0 = 0; i0 < in_channels; i0++) {
if (i0 != i && matrix[o * stride + i0] != 0.0) {
skip = 0;
break;
@@ -598,10 +610,10 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
}
}
/* skip input channels that do not contribute to any output channel */
- for (; i < am->in_channels; i++) {
+ for (; i < in_channels; i++) {
int contrib = 0;
- for (o = 0; o < am->out_channels; o++) {
+ for (o = 0; o < out_channels; o++) {
if (matrix[o * stride + i] != 0.0) {
contrib = 1;
break;
@@ -619,11 +631,11 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
/* skip output channels that only get full contribution from the
corresponding input channel */
- for (o = 0; o < FFMIN(am->in_channels, am->out_channels); o++) {
+ for (o = 0; o < FFMIN(in_channels, out_channels); o++) {
int skip = 1;
int o0;
- for (i = 0; i < am->in_channels; i++) {
+ for (i = 0; i < in_channels; i++) {
if ((o != i && matrix[o * stride + i] != 0.0) ||
(o == i && matrix[o * stride + i] != 1.0)) {
skip = 0;
@@ -633,7 +645,7 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
/* check if the corresponding input channel makes a contribution to
any other output channel */
i = o;
- for (o0 = 0; o0 < am->out_channels; o0++) {
+ for (o0 = 0; o0 < out_channels; o0++) {
if (o0 != i && matrix[o0 * stride + i] != 0.0) {
skip = 0;
break;
@@ -653,11 +665,15 @@ static void reduce_matrix(AudioMix *am, const double *matrix, int stride)
int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride)
{
int i, o, i0, o0, ret;
- char in_layout_name[128];
- char out_layout_name[128];
-
- if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS ||
- am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) {
+ int in_channels = am->in_layout.nb_channels;
+ int out_channels = am->out_layout.nb_channels;
+ char *in_layout_name;
+ char *out_layout_name;
+
+ if (!av_channel_layout_check(&am->in_layout) ||
+ !av_channel_layout_check(&am->out_layout) ||
+ am->in_layout.nb_channels > AVRESAMPLE_MAX_CHANNELS ||
+ am->out_layout.nb_channels > AVRESAMPLE_MAX_CHANNELS) {
av_log(am->avr, AV_LOG_ERROR, "Invalid channel counts\n");
return AVERROR(EINVAL);
}
@@ -667,8 +683,8 @@ int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride)
am->matrix = NULL;
}
- am->in_matrix_channels = am->in_channels;
- am->out_matrix_channels = am->out_channels;
+ am->in_matrix_channels = am->in_layout.nb_channels;
+ am->out_matrix_channels = am->out_layout.nb_channels;
reduce_matrix(am, matrix, stride);
@@ -678,13 +694,13 @@ int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride)
sizeof(*am->matrix_## type[0])); \
if (!am->matrix_## type[0]) \
return AVERROR(ENOMEM); \
- for (o = 0, o0 = 0; o < am->out_channels; o++) { \
+ for (o = 0, o0 = 0; o < out_channels; o++) { \
if (am->output_zero[o] || am->output_skip[o]) \
continue; \
if (o0 > 0) \
am->matrix_## type[o0] = am->matrix_## type[o0 - 1] + \
am->in_matrix_channels; \
- for (i = 0, i0 = 0; i < am->in_channels; i++) { \
+ for (i = 0, i0 = 0; i < in_channels; i++) { \
double v; \
if (am->input_skip[i] || am->output_zero[i]) \
continue; \
@@ -717,26 +733,28 @@ int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride)
if (ret < 0)
return ret;
- av_get_channel_layout_string(in_layout_name, sizeof(in_layout_name),
- am->in_channels, am->in_layout);
- av_get_channel_layout_string(out_layout_name, sizeof(out_layout_name),
- am->out_channels, am->out_layout);
- av_log(am->avr, AV_LOG_DEBUG, "audio_mix: %s to %s\n",
- in_layout_name, out_layout_name);
+ in_layout_name = av_channel_layout_describe(&am->in_layout);
+ out_layout_name = av_channel_layout_describe(&am->out_layout);
+ if (in_layout_name && out_layout_name)
+ av_log(am->avr, AV_LOG_DEBUG, "audio_mix: %s to %s\n",
+ in_layout_name, out_layout_name);
av_log(am->avr, AV_LOG_DEBUG, "matrix size: %d x %d\n",
am->in_matrix_channels, am->out_matrix_channels);
- for (o = 0; o < am->out_channels; o++) {
- for (i = 0; i < am->in_channels; i++) {
+ for (o = 0; o < out_channels; o++) {
+ for (i = 0; i < in_channels; i++) {
if (am->output_zero[o])
av_log(am->avr, AV_LOG_DEBUG, " (ZERO)");
else if (am->input_skip[i] || am->output_zero[i] || am->output_skip[o])
av_log(am->avr, AV_LOG_DEBUG, " (SKIP)");
else
av_log(am->avr, AV_LOG_DEBUG, " %0.3f ",
- matrix[o * am->in_channels + i]);
+ matrix[o * in_channels + i]);
}
av_log(am->avr, AV_LOG_DEBUG, "\n");
}
+ av_freep(&in_layout_name);
+ av_freep(&out_layout_name);
+
return 0;
}
@@ -21,6 +21,7 @@
#include <stdint.h>
+#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/libm.h"
#include "libavutil/samplefmt.h"
@@ -29,33 +30,6 @@
#include "audio_data.h"
#include "audio_mix.h"
-/* channel positions */
-#define FRONT_LEFT 0
-#define FRONT_RIGHT 1
-#define FRONT_CENTER 2
-#define LOW_FREQUENCY 3
-#define BACK_LEFT 4
-#define BACK_RIGHT 5
-#define FRONT_LEFT_OF_CENTER 6
-#define FRONT_RIGHT_OF_CENTER 7
-#define BACK_CENTER 8
-#define SIDE_LEFT 9
-#define SIDE_RIGHT 10
-#define TOP_CENTER 11
-#define TOP_FRONT_LEFT 12
-#define TOP_FRONT_CENTER 13
-#define TOP_FRONT_RIGHT 14
-#define TOP_BACK_LEFT 15
-#define TOP_BACK_CENTER 16
-#define TOP_BACK_RIGHT 17
-#define STEREO_LEFT 29
-#define STEREO_RIGHT 30
-#define WIDE_LEFT 31
-#define WIDE_RIGHT 32
-#define SURROUND_DIRECT_LEFT 33
-#define SURROUND_DIRECT_RIGHT 34
-#define LOW_FREQUENCY_2 35
-
#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */
static av_always_inline int even(uint64_t layout)
@@ -63,232 +37,313 @@ static av_always_inline int even(uint64_t layout)
return (!layout || !!(layout & (layout - 1)));
}
-static int sane_layout(uint64_t layout)
+static int sane_layout(const AVChannelLayout *layout)
{
/* check that there is at least 1 front speaker */
- if (!(layout & AV_CH_LAYOUT_SURROUND))
+ if (!av_channel_layout_subset(layout, AV_CH_LAYOUT_SURROUND))
return 0;
/* check for left/right symmetry */
- if (!even(layout & (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT)) ||
- !even(layout & (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT)) ||
- !even(layout & (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT)) ||
- !even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER)) ||
- !even(layout & (AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT)) ||
- !even(layout & (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT)) ||
- !even(layout & (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT)) ||
- !even(layout & (AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT)) ||
- !even(layout & (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT)))
+ if (!even(av_channel_layout_subset(layout, (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT))) ||
+ !even(av_channel_layout_subset(layout, (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT))))
return 0;
return 1;
}
-int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
- double center_mix_level, double surround_mix_level,
- double lfe_mix_level, int normalize,
- double *matrix_out, int stride,
- enum AVMatrixEncoding matrix_encoding)
+#define IDX_OUT(ch) (av_channel_layout_channel_index(out_layout, ch))
+
+int avresample_build_matrix2(const AVChannelLayout *in_layout,
+ const AVChannelLayout *out_layout,
+ double center_mix_level, double surround_mix_level,
+ double lfe_mix_level, int normalize,
+ double *matrix, int stride,
+ enum AVMatrixEncoding matrix_encoding)
{
- int i, j, out_i, out_j;
- double matrix[64][64] = {{0}};
- int64_t unaccounted;
+ static const AVChannelLayout stereo = AV_CHANNEL_LAYOUT_STEREO;
+ int i, j;
double maxcoef = 0;
- int in_channels, out_channels;
-
- if ((out_layout & AV_CH_LAYOUT_STEREO_DOWNMIX) == AV_CH_LAYOUT_STEREO_DOWNMIX) {
- out_layout = AV_CH_LAYOUT_STEREO;
- }
-
- unaccounted = in_layout & ~out_layout;
-
- in_channels = av_get_channel_layout_nb_channels( in_layout);
- out_channels = av_get_channel_layout_nb_channels(out_layout);
-
- memset(matrix_out, 0, out_channels * stride * sizeof(*matrix_out));
+ int idx_in, idx_out, idx_r, idx_l;
+ int in_channels = in_layout->nb_channels;
+ int out_channels = out_layout->nb_channels;;
/* check if layouts are supported */
- if (!in_layout || in_channels > AVRESAMPLE_MAX_CHANNELS)
+ if (!av_channel_layout_check(in_layout) ||
+ in_channels > AVRESAMPLE_MAX_CHANNELS)
return AVERROR(EINVAL);
- if (!out_layout || out_channels > AVRESAMPLE_MAX_CHANNELS)
+ if (!av_channel_layout_check(out_layout) ||
+ out_channels > AVRESAMPLE_MAX_CHANNELS)
return AVERROR(EINVAL);
/* check if layouts are unbalanced or abnormal */
if (!sane_layout(in_layout) || !sane_layout(out_layout))
return AVERROR_PATCHWELCOME;
- /* route matching input/output channels */
- for (i = 0; i < 64; i++) {
- if (in_layout & out_layout & (1ULL << i))
- matrix[i][i] = 1.0;
- }
+ if (out_channels == 2 &&
+ IDX_OUT(AV_CHAN_STEREO_LEFT) >= 0 &&
+ IDX_OUT(AV_CHAN_STEREO_RIGHT) >= 0)
+ out_layout = &stereo;
+
+ memset(matrix, 0, out_channels * stride * sizeof(*matrix));
- /* mix front center to front left/right */
- if (unaccounted & AV_CH_FRONT_CENTER) {
- if ((out_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO) {
- if ((in_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO) {
- matrix[FRONT_LEFT ][FRONT_CENTER] += center_mix_level;
- matrix[FRONT_RIGHT][FRONT_CENTER] += center_mix_level;
+ for (idx_in = 0; idx_in < in_channels; idx_in++) {
+ enum AVChannel in_ch = av_channel_layout_get_channel(in_layout, idx_in);
+
+ idx_out = IDX_OUT(in_ch);
+
+ /* check if the input channel is also present in output */
+ if (idx_out >= 0) {
+ if (in_ch == AV_CHAN_FRONT_CENTER &&
+ av_channel_layout_subset(in_layout, AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO &&
+ !av_channel_layout_subset(out_layout, AV_CH_LAYOUT_STEREO)) {
+ /* mix left/right/center to center */
+ matrix[idx_out * stride + idx_in] = center_mix_level * M_SQRT2;
} else {
- matrix[FRONT_LEFT ][FRONT_CENTER] += M_SQRT1_2;
- matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2;
+ /* just copy it */
+ matrix[idx_out * stride + idx_in] = 1.0;
}
- } else
- return AVERROR_PATCHWELCOME;
- }
- /* mix front left/right to center */
- if (unaccounted & AV_CH_LAYOUT_STEREO) {
- if (out_layout & AV_CH_FRONT_CENTER) {
- matrix[FRONT_CENTER][FRONT_LEFT ] += M_SQRT1_2;
- matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2;
- /* mix left/right/center to center */
- if (in_layout & AV_CH_FRONT_CENTER)
- matrix[FRONT_CENTER][FRONT_CENTER] = center_mix_level * M_SQRT2;
- } else
- return AVERROR_PATCHWELCOME;
- }
- /* mix back center to back, side, or front */
- if (unaccounted & AV_CH_BACK_CENTER) {
- if (out_layout & AV_CH_BACK_LEFT) {
- matrix[BACK_LEFT ][BACK_CENTER] += M_SQRT1_2;
- matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2;
- } else if (out_layout & AV_CH_SIDE_LEFT) {
- matrix[SIDE_LEFT ][BACK_CENTER] += M_SQRT1_2;
- matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2;
- } else if (out_layout & AV_CH_FRONT_LEFT) {
- if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY ||
- matrix_encoding == AV_MATRIX_ENCODING_DPLII) {
- if (unaccounted & (AV_CH_BACK_LEFT | AV_CH_SIDE_LEFT)) {
- matrix[FRONT_LEFT ][BACK_CENTER] -= surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2;
+
+ continue;
+ }
+
+ /* the input channel is not present in the output layout */
+
+ /* mix front center to front left/right */
+ if (in_ch == AV_CHAN_FRONT_CENTER) {
+ int idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT);
+ int idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT);
+ if (idx_l >= 0 && idx_r >= 0) {
+ matrix[idx_l * stride + idx_in] += M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += M_SQRT1_2;
+ }
+ }
+
+ /* mix front left/right to center */
+ if (in_ch == AV_CHAN_FRONT_LEFT || in_ch == AV_CHAN_FRONT_RIGHT) {
+ idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER);
+ if (idx_out >= 0)
+ matrix[idx_out * stride + idx_in] += M_SQRT1_2;
+ }
+
+ /* mix back center to back, side, or front */
+ if (in_ch == AV_CHAN_BACK_CENTER) {
+ int idx_l = IDX_OUT(AV_CHAN_BACK_LEFT);
+ int idx_r = IDX_OUT(AV_CHAN_BACK_RIGHT);
+ if (idx_l >= 0 && idx_r >= 0) {
+ matrix[idx_l * stride + idx_in] += M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += M_SQRT1_2;
+ continue;
+ }
+
+ idx_l = IDX_OUT(AV_CHAN_SIDE_LEFT);
+ idx_r = IDX_OUT(AV_CHAN_SIDE_RIGHT);
+ if (idx_l >= 0 && idx_r >= 0) {
+ matrix[idx_l * stride + idx_in] += M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += M_SQRT1_2;
+ continue;
+ }
+
+ idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT);
+ idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT);
+ if (idx_l >= 0 && idx_r >= 0) {
+ if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY ||
+ matrix_encoding == AV_MATRIX_ENCODING_DPLII) {
+ if (!av_channel_layout_subset(out_layout, AV_CH_BACK_LEFT | AV_CH_SIDE_LEFT) &&
+ av_channel_layout_subset(in_layout, AV_CH_BACK_LEFT | AV_CH_SIDE_LEFT)) {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ } else {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level;
+ matrix[idx_r * stride + idx_in] += surround_mix_level;
+ }
} else {
- matrix[FRONT_LEFT ][BACK_CENTER] -= surround_mix_level;
- matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level;
+ matrix[idx_l * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2;
}
- } else {
- matrix[FRONT_LEFT ][BACK_CENTER] += surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2;
+ continue;
}
- } else if (out_layout & AV_CH_FRONT_CENTER) {
- matrix[FRONT_CENTER][BACK_CENTER] += surround_mix_level * M_SQRT1_2;
- } else
- return AVERROR_PATCHWELCOME;
- }
- /* mix back left/right to back center, side, or front */
- if (unaccounted & AV_CH_BACK_LEFT) {
- if (out_layout & AV_CH_BACK_CENTER) {
- matrix[BACK_CENTER][BACK_LEFT ] += M_SQRT1_2;
- matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2;
- } else if (out_layout & AV_CH_SIDE_LEFT) {
- /* if side channels do not exist in the input, just copy back
- channels to side channels, otherwise mix back into side */
- if (in_layout & AV_CH_SIDE_LEFT) {
- matrix[SIDE_LEFT ][BACK_LEFT ] += M_SQRT1_2;
- matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2;
- } else {
- matrix[SIDE_LEFT ][BACK_LEFT ] += 1.0;
- matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0;
+
+ idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ continue;
}
- } else if (out_layout & AV_CH_FRONT_LEFT) {
- if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) {
- matrix[FRONT_LEFT ][BACK_LEFT ] -= surround_mix_level * M_SQRT1_2;
- matrix[FRONT_LEFT ][BACK_RIGHT] -= surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][BACK_LEFT ] += surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level * M_SQRT1_2;
- } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) {
- matrix[FRONT_LEFT ][BACK_LEFT ] -= surround_mix_level * SQRT3_2;
- matrix[FRONT_LEFT ][BACK_RIGHT] -= surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][BACK_LEFT ] += surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level * SQRT3_2;
- } else {
- matrix[FRONT_LEFT ][BACK_LEFT ] += surround_mix_level;
- matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level;
+ }
+
+ /* mix back left/right to back center, side, or front */
+ if (in_ch == AV_CHAN_BACK_LEFT || in_ch == AV_CHAN_BACK_RIGHT) {
+ idx_out = IDX_OUT(AV_CHAN_BACK_CENTER);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += M_SQRT1_2;
+ continue;
}
- } else if (out_layout & AV_CH_FRONT_CENTER) {
- matrix[FRONT_CENTER][BACK_LEFT ] += surround_mix_level * M_SQRT1_2;
- matrix[FRONT_CENTER][BACK_RIGHT] += surround_mix_level * M_SQRT1_2;
- } else
- return AVERROR_PATCHWELCOME;
- }
- /* mix side left/right into back or front */
- if (unaccounted & AV_CH_SIDE_LEFT) {
- if (out_layout & AV_CH_BACK_LEFT) {
- /* if back channels do not exist in the input, just copy side
- channels to back channels, otherwise mix side into back */
- if (in_layout & AV_CH_BACK_LEFT) {
- matrix[BACK_LEFT ][SIDE_LEFT ] += M_SQRT1_2;
- matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2;
- } else {
- matrix[BACK_LEFT ][SIDE_LEFT ] += 1.0;
- matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0;
+
+ idx_out = IDX_OUT(in_ch == AV_CHAN_BACK_LEFT ? AV_CHAN_SIDE_LEFT :
+ AV_CHAN_SIDE_RIGHT);
+ if (idx_out >= 0) {
+ /* if side channels do not exist in the input, just copy back
+ channels to side channels, otherwise mix back into side */
+ if (av_channel_layout_subset(in_layout, AV_CH_SIDE_LEFT))
+ matrix[idx_out * stride + idx_in] += M_SQRT1_2;
+ else
+ matrix[idx_out * stride + idx_in] += 1.0;
+
+ continue;
}
- } else if (out_layout & AV_CH_BACK_CENTER) {
- matrix[BACK_CENTER][SIDE_LEFT ] += M_SQRT1_2;
- matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2;
- } else if (out_layout & AV_CH_FRONT_LEFT) {
- if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) {
- matrix[FRONT_LEFT ][SIDE_LEFT ] -= surround_mix_level * M_SQRT1_2;
- matrix[FRONT_LEFT ][SIDE_RIGHT] -= surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2;
- } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) {
- matrix[FRONT_LEFT ][SIDE_LEFT ] -= surround_mix_level * SQRT3_2;
- matrix[FRONT_LEFT ][SIDE_RIGHT] -= surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level * SQRT3_2;
- } else {
- matrix[FRONT_LEFT ][SIDE_LEFT ] += surround_mix_level;
- matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level;
+
+ idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT);
+ idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT);
+ if (idx_l >= 0 && idx_r >= 0) {
+ if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) {
+ if (in_ch == AV_CHAN_BACK_LEFT) {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level * SQRT3_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ } else {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * SQRT3_2;
+ }
+ } else {
+ idx_out = (in_ch == AV_CHAN_BACK_LEFT) ? idx_l : idx_r;
+ matrix[idx_out * stride + idx_in] += surround_mix_level;
+ }
+
+ continue;
}
- } else if (out_layout & AV_CH_FRONT_CENTER) {
- matrix[FRONT_CENTER][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2;
- matrix[FRONT_CENTER][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2;
- } else
- return AVERROR_PATCHWELCOME;
- }
- /* mix left-of-center/right-of-center into front left/right or center */
- if (unaccounted & AV_CH_FRONT_LEFT_OF_CENTER) {
- if (out_layout & AV_CH_FRONT_LEFT) {
- matrix[FRONT_LEFT ][FRONT_LEFT_OF_CENTER ] += 1.0;
- matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
- } else if (out_layout & AV_CH_FRONT_CENTER) {
- matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER ] += M_SQRT1_2;
- matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2;
- } else
- return AVERROR_PATCHWELCOME;
- }
- /* mix LFE into front left/right or center */
- if (unaccounted & AV_CH_LOW_FREQUENCY) {
- if (out_layout & AV_CH_FRONT_CENTER) {
- matrix[FRONT_CENTER][LOW_FREQUENCY] += lfe_mix_level;
- } else if (out_layout & AV_CH_FRONT_LEFT) {
- matrix[FRONT_LEFT ][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2;
- matrix[FRONT_RIGHT][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2;
- } else
- return AVERROR_PATCHWELCOME;
+
+ idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ continue;
+ }
+ }
+
+ /* mix side left/right into back or front */
+ if (in_ch == AV_CHAN_SIDE_LEFT || in_ch == AV_CHAN_SIDE_RIGHT) {
+ idx_out = IDX_OUT(in_ch == AV_CHAN_SIDE_LEFT ? AV_CHAN_BACK_LEFT :
+ AV_CHAN_BACK_RIGHT);
+ if (idx_out >= 0) {
+ /* if back channels do not exist in the input, just copy side
+ channels to back channels, otherwise mix side into back */
+ if (av_channel_layout_subset(in_layout, AV_CH_BACK_LEFT))
+ matrix[idx_out * stride + idx_in] += M_SQRT1_2;
+ else
+ matrix[idx_out * stride + idx_in] += 1.0;
+
+ continue;
+ }
+
+ idx_out = IDX_OUT(AV_CHAN_BACK_CENTER);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += M_SQRT1_2;
+ continue;
+ }
+
+
+ idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT);
+ idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT);
+ if (idx_l >= 0 && idx_r >= 0) {
+ if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) {
+ if (in_ch == AV_CHAN_SIDE_LEFT) {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level * SQRT3_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ } else {
+ matrix[idx_l * stride + idx_in] -= surround_mix_level * M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += surround_mix_level * SQRT3_2;
+ }
+ } else {
+ idx_out = (in_ch == AV_CHAN_SIDE_LEFT) ? idx_l : idx_r;
+ matrix[idx_out * stride + idx_in] += surround_mix_level;
+ }
+
+ continue;
+ }
+
+ idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += surround_mix_level * M_SQRT1_2;
+ continue;
+ }
+ }
+
+ /* mix left-of-center/right-of-center into front left/right or center */
+ if (in_ch == AV_CHAN_FRONT_LEFT_OF_CENTER ||
+ in_ch == AV_CHAN_FRONT_RIGHT_OF_CENTER) {
+ idx_out = IDX_OUT(in_ch == AV_CHAN_FRONT_LEFT_OF_CENTER ?
+ AV_CHAN_FRONT_LEFT : AV_CHAN_FRONT_RIGHT);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += 1.0;
+ continue;
+ }
+
+ idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += M_SQRT1_2;
+ continue;
+ }
+ }
+
+ /* mix LFE into front left/right or center */
+ if (in_ch == AV_CHAN_LOW_FREQUENCY) {
+ idx_out = IDX_OUT(AV_CHAN_FRONT_CENTER);
+ if (idx_out >= 0) {
+ matrix[idx_out * stride + idx_in] += lfe_mix_level;
+ continue;
+ }
+
+ idx_l = IDX_OUT(AV_CHAN_FRONT_LEFT);
+ idx_r = IDX_OUT(AV_CHAN_FRONT_RIGHT);
+ if (idx_l >= 0 && idx_r >= 0) {
+ matrix[idx_l * stride + idx_in] += lfe_mix_level * M_SQRT1_2;
+ matrix[idx_r * stride + idx_in] += lfe_mix_level * M_SQRT1_2;
+ continue;
+ }
+ }
}
- /* transfer internal matrix to output matrix and calculate maximum
- per-channel coefficient sum */
- for (out_i = i = 0; out_i < out_channels && i < 64; i++) {
+ /* calculate maximum per-channel coefficient sum */
+ for (idx_out = 0; idx_out < out_channels; idx_out++) {
double sum = 0;
- for (out_j = j = 0; out_j < in_channels && j < 64; j++) {
- matrix_out[out_i * stride + out_j] = matrix[i][j];
- sum += fabs(matrix[i][j]);
- if (in_layout & (1ULL << j))
- out_j++;
- }
+ for (idx_in = 0; idx_in < in_channels; idx_in++)
+ sum += fabs(matrix[idx_out * stride + idx_in]);
maxcoef = FFMAX(maxcoef, sum);
- if (out_layout & (1ULL << i))
- out_i++;
}
/* normalize */
if (normalize && maxcoef > 1.0) {
for (i = 0; i < out_channels; i++)
for (j = 0; j < in_channels; j++)
- matrix_out[i * stride + j] /= maxcoef;
+ matrix[i * stride + j] /= maxcoef;
}
return 0;
}
+
+#if FF_API_OLD_CHANNEL_LAYOUT
+int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
+ double center_mix_level, double surround_mix_level,
+ double lfe_mix_level, int normalize,
+ double *matrix_out, int stride,
+ enum AVMatrixEncoding matrix_encoding)
+{
+ AVChannelLayout in_chl, out_chl;
+
+ av_channel_layout_from_mask(&in_chl, in_layout);
+ av_channel_layout_from_mask(&out_chl, out_layout);
+ return avresample_build_matrix2(&in_chl, &out_chl, center_mix_level,
+ surround_mix_level, lfe_mix_level, normalize,
+ matrix_out, stride, matrix_encoding);
+}
+#endif
@@ -44,8 +44,10 @@
* matrix):
* @code
* AVAudioResampleContext *avr = avresample_alloc_context();
- * av_opt_set_int(avr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0);
- * av_opt_set_int(avr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
+ * AVChannelLayout in_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1;
+ * AVChannelLayout out_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
+ * av_opt_set_channel_layout(avr, "in_ch_layout", &in_ch_layout, 0);
+ * av_opt_set_channel_layout(avr, "out_ch_layout", &out_ch_layout, 0);
* av_opt_set_int(avr, "in_sample_rate", 48000, 0);
* av_opt_set_int(avr, "out_sample_rate", 44100, 0);
* av_opt_set_int(avr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
@@ -92,6 +94,7 @@
* avresample_free().
*/
+#include "libavutil/attributes.h"
#include "libavutil/avutil.h"
#include "libavutil/channel_layout.h"
#include "libavutil/dict.h"
@@ -167,7 +170,7 @@ AVAudioResampleContext *avresample_alloc_context(void);
/**
* Initialize AVAudioResampleContext.
* @note The context must be configured using the AVOption API.
- * @note The fields "in_channel_layout", "out_channel_layout",
+ * @note The fields "in_ch_layout", "out_ch_layout",
* "in_sample_rate", "out_sample_rate", "in_sample_fmt",
* "out_sample_fmt" must be set.
*
@@ -212,6 +215,7 @@ void avresample_close(AVAudioResampleContext *avr);
*/
void avresample_free(AVAudioResampleContext **avr);
+#if FF_API_OLD_CHANNEL_LAYOUT
/**
* Generate a channel mixing matrix.
*
@@ -233,11 +237,43 @@ void avresample_free(AVAudioResampleContext **avr);
* matrix array
* @param matrix_encoding matrixed stereo downmix mode (e.g. dplii)
* @return 0 on success, negative AVERROR code on failure
+ *
+ * @deprecated use avresample_build_matrix2()
*/
+attribute_deprecated
int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
double center_mix_level, double surround_mix_level,
double lfe_mix_level, int normalize, double *matrix,
int stride, enum AVMatrixEncoding matrix_encoding);
+#endif
+
+/**
+ * Generate a channel mixing matrix.
+ *
+ * This function is the one used internally by libavresample for building the
+ * default mixing matrix. It is made public just as a utility function for
+ * building custom matrices.
+ *
+ * @param in_layout input channel layout
+ * @param out_layout output channel layout
+ * @param center_mix_level mix level for the center channel
+ * @param surround_mix_level mix level for the surround channel(s)
+ * @param lfe_mix_level mix level for the low-frequency effects channel
+ * @param normalize if 1, coefficients will be normalized to prevent
+ * overflow. if 0, coefficients will not be
+ * normalized.
+ * @param[out] matrix mixing coefficients; matrix[i + stride * o] is
+ * the weight of input channel i in output channel o.
+ * @param stride distance between adjacent input channels in the
+ * matrix array
+ * @param matrix_encoding matrixed stereo downmix mode (e.g. dplii)
+ * @return 0 on success, negative AVERROR code on failure
+ */
+int avresample_build_matrix2(const AVChannelLayout *in_layout,
+ const AVChannelLayout *out_layout,
+ double center_mix_level, double surround_mix_level,
+ double lfe_mix_level, int normalize, double *matrix,
+ int stride, enum AVMatrixEncoding matrix_encoding);
/**
* Get the current channel mixing matrix.
@@ -22,6 +22,7 @@
#define AVRESAMPLE_INTERNAL_H
#include "libavutil/audio_fifo.h"
+#include "libavutil/channel_layout.h"
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/samplefmt.h"
@@ -53,10 +54,17 @@ typedef struct ChannelMapInfo {
struct AVAudioResampleContext {
const AVClass *av_class; /**< AVClass for logging and AVOptions */
+#if FF_API_OLD_CHANNEL_LAYOUT
uint64_t in_channel_layout; /**< input channel layout */
+#endif
+ AVChannelLayout in_ch_layout; /**< input channel layout */
+
enum AVSampleFormat in_sample_fmt; /**< input sample format */
int in_sample_rate; /**< input sample rate */
+#if FF_API_OLD_CHANNEL_LAYOUT
uint64_t out_channel_layout; /**< output channel layout */
+#endif
+ AVChannelLayout out_ch_layout; /**< output channel layout */
enum AVSampleFormat out_sample_fmt; /**< output sample format */
int out_sample_rate; /**< output sample rate */
enum AVSampleFormat internal_sample_fmt; /**< internal sample format */
@@ -74,8 +82,6 @@ struct AVAudioResampleContext {
int kaiser_beta; /**< beta value for Kaiser window (only applicable if filter_type == AV_FILTER_TYPE_KAISER) */
enum AVResampleDitherMethod dither_method; /**< dither method */
- int in_channels; /**< number of input channels */
- int out_channels; /**< number of output channels */
int resample_channels; /**< number of channels used for resampling */
int downmix_needed; /**< downmixing is needed */
int upmix_needed; /**< upmixing is needed */
@@ -36,10 +36,18 @@
#define PARAM AV_OPT_FLAG_AUDIO_PARAM
static const AVOption avresample_options[] = {
+#if FF_API_OLD_CHANNEL_LAYOUT
{ "in_channel_layout", "Input Channel Layout", OFFSET(in_channel_layout), AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, PARAM },
+#endif
+ { "in_ch_layout", "Input Channel Layout", OFFSET(in_ch_layout), AV_OPT_TYPE_CHANNEL_LAYOUT,
+ { .str = NULL }, .flags = PARAM },
{ "in_sample_fmt", "Input Sample Format", OFFSET(in_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM },
{ "in_sample_rate", "Input Sample Rate", OFFSET(in_sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1, INT_MAX, PARAM },
+#if FF_API_OLD_CHANNEL_LAYOUT
{ "out_channel_layout", "Output Channel Layout", OFFSET(out_channel_layout), AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, PARAM },
+#endif
+ { "out_ch_layout", "Output Channel Layout", OFFSET(out_ch_layout), AV_OPT_TYPE_CHANNEL_LAYOUT,
+ { .str = NULL }, .flags = PARAM },
{ "out_sample_fmt", "Output Sample Format", OFFSET(out_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM },
{ "out_sample_rate", "Output Sample Rate", OFFSET(out_sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1, INT_MAX, PARAM },
{ "internal_sample_fmt", "Internal Sample Format", OFFSET(internal_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_NONE }, AV_SAMPLE_FMT_NONE, AV_SAMPLE_FMT_NB-1, PARAM, "internal_sample_fmt" },
@@ -179,11 +179,11 @@ static const int rates[] = {
16000
};
-static const uint64_t layouts[] = {
- AV_CH_LAYOUT_STEREO,
- AV_CH_LAYOUT_MONO,
- AV_CH_LAYOUT_5POINT1,
- AV_CH_LAYOUT_7POINT1,
+static const AVChannelLayout layouts[] = {
+ AV_CHANNEL_LAYOUT_STEREO,
+ AV_CHANNEL_LAYOUT_MONO,
+ AV_CHANNEL_LAYOUT_5POINT1,
+ AV_CHANNEL_LAYOUT_7POINT1,
};
int main(int argc, char **argv)
@@ -199,12 +199,8 @@ int main(int argc, char **argv)
uint8_t *out_data[AVRESAMPLE_MAX_CHANNELS] = { 0 };
int in_linesize;
int out_linesize;
- uint64_t in_ch_layout;
- int in_channels;
enum AVSampleFormat in_fmt;
int in_rate;
- uint64_t out_ch_layout;
- int out_channels;
enum AVSampleFormat out_fmt;
int out_rate;
int num_formats, num_rates, num_layouts;
@@ -257,8 +253,8 @@ int main(int argc, char **argv)
for (i = 0; i < num_formats; i++) {
in_fmt = formats[i];
for (k = 0; k < num_layouts; k++) {
- in_ch_layout = layouts[k];
- in_channels = av_get_channel_layout_nb_channels(in_ch_layout);
+ AVChannelLayout in_ch_layout = layouts[k];
+ int in_channels = in_ch_layout.nb_channels;
for (m = 0; m < num_rates; m++) {
in_rate = rates[m];
@@ -274,8 +270,8 @@ int main(int argc, char **argv)
for (j = 0; j < num_formats; j++) {
out_fmt = formats[j];
for (l = 0; l < num_layouts; l++) {
- out_ch_layout = layouts[l];
- out_channels = av_get_channel_layout_nb_channels(out_ch_layout);
+ AVChannelLayout out_ch_layout = layouts[l];
+ int out_channels = out_ch_layout.nb_channels;
for (n = 0; n < num_rates; n++) {
out_rate = rates[n];
@@ -291,10 +287,10 @@ int main(int argc, char **argv)
goto end;
}
- av_opt_set_int(s, "in_channel_layout", in_ch_layout, 0);
+ av_opt_set_channel_layout(s, "in_ch_layout", &in_ch_layout, 0);
+ av_opt_set_channel_layout(s, "out_ch_layout", &out_ch_layout, 0);
av_opt_set_int(s, "in_sample_fmt", in_fmt, 0);
av_opt_set_int(s, "in_sample_rate", in_rate, 0);
- av_opt_set_int(s, "out_channel_layout", out_ch_layout, 0);
av_opt_set_int(s, "out_sample_fmt", out_fmt, 0);
av_opt_set_int(s, "out_sample_rate", out_rate, 0);
@@ -18,6 +18,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/dict.h"
#include "libavutil/error.h"
@@ -35,6 +36,7 @@
int avresample_open(AVAudioResampleContext *avr)
{
+ int in_ch, out_ch;
int ret;
if (avresample_is_open(avr)) {
@@ -42,24 +44,67 @@ int avresample_open(AVAudioResampleContext *avr)
return AVERROR(EINVAL);
}
+ if ( avr->in_ch_layout.order == AV_CHANNEL_ORDER_CUSTOM ||
+ avr->out_ch_layout.order == AV_CHANNEL_ORDER_CUSTOM) {
+ av_log(avr, AV_LOG_ERROR,
+ "Resampling a custom channel layout order is not supported.\n");
+ return AVERROR(ENOSYS);
+ }
+
+ if (avr->in_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) {
+ if (avr->in_ch_layout.nb_channels > 63) {
+ av_log(avr, AV_LOG_ERROR,
+ "Unspecified channel layout order is supported only for up "
+ "to 63 channels (got %d).\n", avr->in_ch_layout.nb_channels);
+ return AVERROR(ENOSYS);
+ }
+ av_channel_layout_default(&avr->in_ch_layout, avr->in_ch_layout.nb_channels);
+ }
+ if (avr->out_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) {
+ if (avr->out_ch_layout.nb_channels > 63) {
+ av_log(avr, AV_LOG_ERROR,
+ "Unspecified channel layout order is supported only for up "
+ "to 63 channels (got %d).\n", avr->out_ch_layout.nb_channels);
+ return AVERROR(ENOSYS);
+ }
+ av_channel_layout_default(&avr->out_ch_layout, avr->out_ch_layout.nb_channels);
+ }
+
/* set channel mixing parameters */
- avr->in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout);
- if (avr->in_channels <= 0 || avr->in_channels > AVRESAMPLE_MAX_CHANNELS) {
- av_log(avr, AV_LOG_ERROR, "Invalid input channel layout: %"PRIu64"\n",
- avr->in_channel_layout);
+#if FF_API_OLD_CHANNEL_LAYOUT
+ if (avr->in_channel_layout) {
+ av_log(avr, AV_LOG_WARNING, "Setting the input channel layout with the "
+ "'in_channel_layout' option is deprecated, use the "
+ "'in_ch_layout' option instead].\n");
+ av_channel_layout_uninit(&avr->in_ch_layout);
+ av_channel_layout_from_mask(&avr->in_ch_layout, avr->in_channel_layout);
+ }
+ if (avr->out_channel_layout) {
+ av_log(avr, AV_LOG_WARNING, "Setting the output channel layout with the "
+ "'out_channel_layout' option is deprecated, use the "
+ "'out_ch_layout' option instead].\n");
+ av_channel_layout_uninit(&avr->out_ch_layout);
+ av_channel_layout_from_mask(&avr->out_ch_layout, avr->out_channel_layout);
+ }
+#endif
+ in_ch = avr->in_ch_layout.nb_channels;
+ out_ch = avr->out_ch_layout.nb_channels;
+ if (!av_channel_layout_check(&avr->in_ch_layout) ||
+ in_ch > AVRESAMPLE_MAX_CHANNELS) {
+ av_log(avr, AV_LOG_ERROR, "Invalid input channel layout.\n");
return AVERROR(EINVAL);
}
- avr->out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout);
- if (avr->out_channels <= 0 || avr->out_channels > AVRESAMPLE_MAX_CHANNELS) {
- av_log(avr, AV_LOG_ERROR, "Invalid output channel layout: %"PRIu64"\n",
- avr->out_channel_layout);
+ if (!av_channel_layout_check(&avr->out_ch_layout) ||
+ out_ch > AVRESAMPLE_MAX_CHANNELS) {
+ av_log(avr, AV_LOG_ERROR, "Invalid output channel layout.\n");
return AVERROR(EINVAL);
}
- avr->resample_channels = FFMIN(avr->in_channels, avr->out_channels);
- avr->downmix_needed = avr->in_channels > avr->out_channels;
- avr->upmix_needed = avr->out_channels > avr->in_channels ||
+ avr->resample_channels = FFMIN(in_ch, out_ch);
+ avr->downmix_needed = in_ch > out_ch;
+ avr->upmix_needed = out_ch > in_ch ||
(!avr->downmix_needed && (avr->mix_matrix ||
- avr->in_channel_layout != avr->out_channel_layout));
+ av_channel_layout_compare(&avr->in_ch_layout,
+ &avr->out_ch_layout)));
avr->mixing_needed = avr->downmix_needed || avr->upmix_needed;
/* set resampling parameters */
@@ -105,7 +150,7 @@ int avresample_open(AVAudioResampleContext *avr)
/* we may need to add an extra conversion in order to remap channels if
the output format is not planar */
if (avr->use_channel_map && !avr->mixing_needed && !avr->resample_needed &&
- !ff_sample_fmt_is_planar(avr->out_sample_fmt, avr->out_channels)) {
+ !ff_sample_fmt_is_planar(avr->out_sample_fmt, avr->out_ch_layout.nb_channels)) {
avr->internal_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt);
}
@@ -114,7 +159,8 @@ int avresample_open(AVAudioResampleContext *avr)
avr->in_convert_needed = avr->in_sample_fmt != avr->internal_sample_fmt;
else
avr->in_convert_needed = avr->use_channel_map &&
- !ff_sample_fmt_is_planar(avr->out_sample_fmt, avr->out_channels);
+ !ff_sample_fmt_is_planar(avr->out_sample_fmt,
+ avr->out_ch_layout.nb_channels);
if (avr->resample_needed || avr->mixing_needed || avr->in_convert_needed)
avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt;
@@ -144,27 +190,27 @@ int avresample_open(AVAudioResampleContext *avr)
int ch;
av_log(avr, AV_LOG_TRACE, "output map: ");
if (avr->ch_map_info.do_remap)
- for (ch = 0; ch < avr->in_channels; ch++)
+ for (ch = 0; ch < in_ch; ch++)
av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.channel_map[ch]);
else
av_log(avr, AV_LOG_TRACE, "n/a");
av_log(avr, AV_LOG_TRACE, "\n");
av_log(avr, AV_LOG_TRACE, "copy map: ");
if (avr->ch_map_info.do_copy)
- for (ch = 0; ch < avr->in_channels; ch++)
+ for (ch = 0; ch < in_ch; ch++)
av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.channel_copy[ch]);
else
av_log(avr, AV_LOG_TRACE, "n/a");
av_log(avr, AV_LOG_TRACE, "\n");
av_log(avr, AV_LOG_TRACE, "zero map: ");
if (avr->ch_map_info.do_zero)
- for (ch = 0; ch < avr->in_channels; ch++)
+ for (ch = 0; ch < in_ch; ch++)
av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.channel_zero[ch]);
else
av_log(avr, AV_LOG_TRACE, "n/a");
av_log(avr, AV_LOG_TRACE, "\n");
av_log(avr, AV_LOG_TRACE, "input map: ");
- for (ch = 0; ch < avr->in_channels; ch++)
+ for (ch = 0; ch < in_ch; ch++)
av_log(avr, AV_LOG_TRACE, " % 2d", avr->ch_map_info.input_map[ch]);
av_log(avr, AV_LOG_TRACE, "\n");
}
@@ -174,7 +220,7 @@ int avresample_open(AVAudioResampleContext *avr)
/* allocate buffers */
if (avr->in_copy_needed || avr->in_convert_needed) {
- avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels),
+ avr->in_buffer = ff_audio_data_alloc(FFMAX(in_ch, out_ch),
0, avr->internal_sample_fmt,
"in_buffer");
if (!avr->in_buffer) {
@@ -183,7 +229,7 @@ int avresample_open(AVAudioResampleContext *avr)
}
}
if (avr->resample_needed) {
- avr->resample_out_buffer = ff_audio_data_alloc(avr->out_channels,
+ avr->resample_out_buffer = ff_audio_data_alloc(out_ch,
1024, avr->internal_sample_fmt,
"resample_out_buffer");
if (!avr->resample_out_buffer) {
@@ -192,15 +238,14 @@ int avresample_open(AVAudioResampleContext *avr)
}
}
if (avr->out_convert_needed) {
- avr->out_buffer = ff_audio_data_alloc(avr->out_channels, 0,
+ avr->out_buffer = ff_audio_data_alloc(out_ch, 0,
avr->out_sample_fmt, "out_buffer");
if (!avr->out_buffer) {
ret = AVERROR(EINVAL);
goto error;
}
}
- avr->out_fifo = av_audio_fifo_alloc(avr->out_sample_fmt, avr->out_channels,
- 1024);
+ avr->out_fifo = av_audio_fifo_alloc(avr->out_sample_fmt, out_ch, 1024);
if (!avr->out_fifo) {
ret = AVERROR(ENOMEM);
goto error;
@@ -209,7 +254,7 @@ int avresample_open(AVAudioResampleContext *avr)
/* setup contexts */
if (avr->in_convert_needed) {
avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt,
- avr->in_sample_fmt, avr->in_channels,
+ avr->in_sample_fmt, in_ch,
avr->in_sample_rate,
avr->remap_point == REMAP_IN_CONVERT);
if (!avr->ac_in) {
@@ -224,7 +269,7 @@ int avresample_open(AVAudioResampleContext *avr)
else
src_fmt = avr->in_sample_fmt;
avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt,
- avr->out_channels,
+ out_ch,
avr->out_sample_rate,
avr->remap_point == REMAP_OUT_CONVERT);
if (!avr->ac_out) {
@@ -361,7 +406,7 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr,
direct_output = output && av_audio_fifo_size(avr->out_fifo) == 0;
if (output) {
ret = ff_audio_data_init(&output_buffer, output, out_plane_size,
- avr->out_channels, out_samples,
+ avr->out_ch_layout.nb_channels, out_samples,
avr->out_sample_fmt, 0, "output");
if (ret < 0)
return ret;
@@ -371,7 +416,7 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr,
if (input) {
/* initialize input_buffer with input data */
ret = ff_audio_data_init(&input_buffer, input, in_plane_size,
- avr->in_channels, in_samples,
+ avr->in_ch_layout.nb_channels, in_samples,
avr->in_sample_fmt, 1, "input");
if (ret < 0)
return ret;
@@ -420,7 +465,7 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr,
if (ret < 0)
return ret;
}
- ff_audio_data_set_channels(avr->in_buffer, avr->in_channels);
+ ff_audio_data_set_channels(avr->in_buffer, avr->in_ch_layout.nb_channels);
if (avr->downmix_needed) {
av_log(avr, AV_LOG_TRACE, "[downmix] in_buffer\n");
ret = ff_audio_mix(avr->am, avr->in_buffer);
@@ -504,18 +549,24 @@ int attribute_align_arg avresample_convert(AVAudioResampleContext *avr,
int avresample_config(AVAudioResampleContext *avr, AVFrame *out, AVFrame *in)
{
+ int ret;
+
if (avresample_is_open(avr)) {
avresample_close(avr);
}
if (in) {
- avr->in_channel_layout = in->channel_layout;
+ ret = av_channel_layout_copy(&avr->in_ch_layout, &in->ch_layout);
+ if (ret < 0)
+ return ret;
avr->in_sample_rate = in->sample_rate;
avr->in_sample_fmt = in->format;
}
if (out) {
- avr->out_channel_layout = out->channel_layout;
+ ret = av_channel_layout_copy(&avr->out_ch_layout, &out->ch_layout);
+ if (ret < 0)
+ return ret;
avr->out_sample_rate = out->sample_rate;
avr->out_sample_fmt = out->format;
}
@@ -529,7 +580,7 @@ static int config_changed(AVAudioResampleContext *avr,
int ret = 0;
if (in) {
- if (avr->in_channel_layout != in->channel_layout ||
+ if (av_channel_layout_compare(&avr->in_ch_layout, &in->ch_layout) ||
avr->in_sample_rate != in->sample_rate ||
avr->in_sample_fmt != in->format) {
ret |= AVERROR_INPUT_CHANGED;
@@ -537,7 +588,7 @@ static int config_changed(AVAudioResampleContext *avr,
}
if (out) {
- if (avr->out_channel_layout != out->channel_layout ||
+ if (av_channel_layout_compare(&avr->out_ch_layout, &out->ch_layout) ||
avr->out_sample_rate != out->sample_rate ||
avr->out_sample_fmt != out->format) {
ret |= AVERROR_OUTPUT_CHANGED;
@@ -595,7 +646,7 @@ static inline int available_samples(AVFrame *out)
if (av_sample_fmt_is_planar(out->format)) {
return samples;
} else {
- int channels = av_get_channel_layout_nb_channels(out->channel_layout);
+ int channels = out->ch_layout.nb_channels;
return samples / channels;
}
}
@@ -642,8 +693,8 @@ int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix,
if (avr->am)
return ff_audio_mix_get_matrix(avr->am, matrix, stride);
- in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout);
- out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout);
+ in_channels = avr->in_ch_layout.nb_channels;
+ out_channels = avr->out_ch_layout.nb_channels;
if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS ||
out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) {
@@ -671,8 +722,8 @@ int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix,
if (avr->am)
return ff_audio_mix_set_matrix(avr->am, matrix, stride);
- in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout);
- out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout);
+ in_channels = avr->in_ch_layout.nb_channels;
+ out_channels = avr->out_ch_layout.nb_channels;
if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS ||
out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) {
@@ -700,7 +751,7 @@ int avresample_set_channel_mapping(AVAudioResampleContext *avr,
ChannelMapInfo *info = &avr->ch_map_info;
int in_channels, ch, i;
- in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout);
+ in_channels = avr->in_ch_layout.nb_channels;
if (in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS) {
av_log(avr, AV_LOG_ERROR, "Invalid input channel layout\n");
return AVERROR(EINVAL);