libopusenc: Add channel mapping family argument

The default value of -1 indicates that ffmpeg should determine the channel
mapping automatically, which was the behavior before this commit.

Unless the -mapping_family argument is provided, behavior is unchanged.

Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
pull/198/head
Michael Graczyk 9 years ago committed by Michael Niedermayer
parent a1e3c7cf0f
commit 37941878f1
  1. 11
      doc/encoders.texi
  2. 165
      libavcodec/libopusenc.c

@ -1184,6 +1184,17 @@ following: 4000, 6000, 8000, 12000, or 20000, corresponding to
narrowband, mediumband, wideband, super wideband, and fullband narrowband, mediumband, wideband, super wideband, and fullband
respectively. The default is 0 (cutoff disabled). respectively. The default is 0 (cutoff disabled).
@item mapping_family (@emph{mapping_family})
Set channel mapping family to be used by the encoder. The default value of -1
uses mapping family 0 for mono and stereo inputs, and mapping family 1
otherwise. The default also disables the surround masking and LFE bandwidth
optimzations in libopus, and requires that the input contains 8 channels or
fewer.
Other values include 0 for mono and stereo, 1 for surround sound with masking
and LFE bandwidth optimizations, and 255 for independent streams with an
unspecified channel layout.
@end table @end table
@section libvorbis @section libvorbis

@ -38,6 +38,7 @@ typedef struct LibopusEncOpts {
float frame_duration; float frame_duration;
int packet_size; int packet_size;
int max_bandwidth; int max_bandwidth;
int mapping_family;
} LibopusEncOpts; } LibopusEncOpts;
typedef struct LibopusEncContext { typedef struct LibopusEncContext {
@ -47,6 +48,7 @@ typedef struct LibopusEncContext {
uint8_t *samples; uint8_t *samples;
LibopusEncOpts opts; LibopusEncOpts opts;
AudioFrameQueue afq; AudioFrameQueue afq;
const uint8_t *encoder_channel_map;
} LibopusEncContext; } LibopusEncContext;
static const uint8_t opus_coupled_streams[8] = { static const uint8_t opus_coupled_streams[8] = {
@ -93,13 +95,11 @@ static void libopus_write_header(AVCodecContext *avctx, int stream_count,
bytestream_put_le16(&p, 0); /* Gain of 0dB is recommended. */ bytestream_put_le16(&p, 0); /* Gain of 0dB is recommended. */
/* Channel mapping */ /* Channel mapping */
if (channels > 2) { bytestream_put_byte(&p, mapping_family);
bytestream_put_byte(&p, mapping_family); if (mapping_family != 0) {
bytestream_put_byte(&p, stream_count); bytestream_put_byte(&p, stream_count);
bytestream_put_byte(&p, coupled_stream_count); bytestream_put_byte(&p, coupled_stream_count);
bytestream_put_buffer(&p, channel_mapping, channels); bytestream_put_buffer(&p, channel_mapping, channels);
} else {
bytestream_put_byte(&p, 0);
} }
} }
@ -157,13 +157,92 @@ static int libopus_configure_encoder(AVCodecContext *avctx, OpusMSEncoder *enc,
return OPUS_OK; return OPUS_OK;
} }
static int libopus_check_max_channels(AVCodecContext *avctx,
int max_channels) {
if (avctx->channels > max_channels) {
av_log(avctx, AV_LOG_ERROR, "Opus mapping family undefined for %d channels.\n",
avctx->channels);
return AVERROR(EINVAL);
}
return 0;
}
static int libopus_check_vorbis_layout(AVCodecContext *avctx, int mapping_family) {
av_assert2(avctx->channels < FF_ARRAY_ELEMS(ff_vorbis_channel_layouts));
if (!avctx->channel_layout) {
av_log(avctx, AV_LOG_WARNING,
"No channel layout specified. Opus encoder will use Vorbis "
"channel layout for %d channels.\n", avctx->channels);
} else if (avctx->channel_layout != ff_vorbis_channel_layouts[avctx->channels - 1]) {
char name[32];
av_get_channel_layout_string(name, sizeof(name), avctx->channels,
avctx->channel_layout);
av_log(avctx, AV_LOG_ERROR,
"Invalid channel layout %s for specified mapping family %d.\n",
name, mapping_family);
return AVERROR(EINVAL);
}
return 0;
}
static int libopus_validate_layout_and_get_channel_map(
AVCodecContext *avctx,
int mapping_family,
const uint8_t ** channel_map_result)
{
const uint8_t * channel_map = NULL;
int ret;
switch (mapping_family) {
case -1:
ret = libopus_check_max_channels(avctx, 8);
if (ret == 0) {
ret = libopus_check_vorbis_layout(avctx, mapping_family);
/* Channels do not need to be reordered. */
}
break;
case 0:
ret = libopus_check_max_channels(avctx, 2);
if (ret == 0) {
ret = libopus_check_vorbis_layout(avctx, mapping_family);
}
break;
case 1:
/* Opus expects channels to be in Vorbis order. */
ret = libopus_check_max_channels(avctx, 8);
if (ret == 0) {
ret = libopus_check_vorbis_layout(avctx, mapping_family);
channel_map = ff_vorbis_channel_layout_offsets[avctx->channels - 1];
}
break;
case 255:
ret = libopus_check_max_channels(avctx, 254);
break;
default:
av_log(avctx, AV_LOG_WARNING,
"Unknown channel mapping family %d. Output channel layout may be invalid.\n",
mapping_family);
ret = 0;
}
*channel_map_result = channel_map;
return ret;
}
static av_cold int libopus_encode_init(AVCodecContext *avctx) static av_cold int libopus_encode_init(AVCodecContext *avctx)
{ {
LibopusEncContext *opus = avctx->priv_data; LibopusEncContext *opus = avctx->priv_data;
OpusMSEncoder *enc; OpusMSEncoder *enc;
uint8_t libopus_channel_mapping[255]; uint8_t libopus_channel_mapping[255];
int ret = OPUS_OK; int ret = OPUS_OK;
int av_ret;
int coupled_stream_count, header_size, frame_size; int coupled_stream_count, header_size, frame_size;
int mapping_family;
frame_size = opus->opts.frame_duration * 48000 / 1000; frame_size = opus->opts.frame_duration * 48000 / 1000;
switch (frame_size) { switch (frame_size) {
@ -226,26 +305,40 @@ static av_cold int libopus_encode_init(AVCodecContext *avctx)
} }
} }
/* FIXME: Opus can handle up to 255 channels. However, the mapping for /* Channels may need to be reordered to match opus mapping. */
* anything greater than 8 is undefined. */ av_ret = libopus_validate_layout_and_get_channel_map(avctx, opus->opts.mapping_family,
if (avctx->channels > 8) { &opus->encoder_channel_map);
av_log(avctx, AV_LOG_ERROR, if (av_ret) {
"Channel layout undefined for %d channels.\n", avctx->channels); return av_ret;
return AVERROR_PATCHWELCOME;
} }
coupled_stream_count = opus_coupled_streams[avctx->channels - 1]; if (opus->opts.mapping_family == -1) {
opus->stream_count = avctx->channels - coupled_stream_count; /* By default, use mapping family 1 for the header but use the older
* libopus multistream API to avoid surround masking. */
memcpy(libopus_channel_mapping,
opus_vorbis_channel_map[avctx->channels - 1], /* Set the mapping family so that the value is correct in the header */
avctx->channels * sizeof(*libopus_channel_mapping)); mapping_family = avctx->channels > 2 ? 1 : 0;
coupled_stream_count = opus_coupled_streams[avctx->channels - 1];
enc = opus_multistream_encoder_create( opus->stream_count = avctx->channels - coupled_stream_count;
avctx->sample_rate, avctx->channels, opus->stream_count, memcpy(libopus_channel_mapping,
coupled_stream_count, opus_vorbis_channel_map[avctx->channels - 1],
libavcodec_libopus_channel_map[avctx->channels - 1], avctx->channels * sizeof(*libopus_channel_mapping));
opus->opts.application, &ret);
enc = opus_multistream_encoder_create(
avctx->sample_rate, avctx->channels, opus->stream_count,
coupled_stream_count,
libavcodec_libopus_channel_map[avctx->channels - 1],
opus->opts.application, &ret);
} else {
/* Use the newer multistream API. The encoder will set the channel
* mapping and coupled stream counts to its internal defaults and will
* use surround masking analysis to save bits. */
mapping_family = opus->opts.mapping_family;
enc = opus_multistream_surround_encoder_create(
avctx->sample_rate, avctx->channels, mapping_family,
&opus->stream_count, &coupled_stream_count, libopus_channel_mapping,
opus->opts.application, &ret);
}
if (ret != OPUS_OK) { if (ret != OPUS_OK) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
@ -275,7 +368,8 @@ static av_cold int libopus_encode_init(AVCodecContext *avctx)
goto fail; goto fail;
} }
header_size = 19 + (avctx->channels > 2 ? 2 + avctx->channels : 0); /* Header includes channel mapping table if and only if mapping family is 0 */
header_size = 19 + (mapping_family == 0 ? 0 : 2 + avctx->channels);
avctx->extradata = av_malloc(header_size + AV_INPUT_BUFFER_PADDING_SIZE); avctx->extradata = av_malloc(header_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!avctx->extradata) { if (!avctx->extradata) {
av_log(avctx, AV_LOG_ERROR, "Failed to allocate extradata.\n"); av_log(avctx, AV_LOG_ERROR, "Failed to allocate extradata.\n");
@ -299,7 +393,7 @@ static av_cold int libopus_encode_init(AVCodecContext *avctx)
opus_strerror(ret)); opus_strerror(ret));
libopus_write_header(avctx, opus->stream_count, coupled_stream_count, libopus_write_header(avctx, opus->stream_count, coupled_stream_count,
avctx->channels <= 8 ? 1 : 255, libopus_channel_mapping); mapping_family, libopus_channel_mapping);
ff_af_queue_init(avctx, &opus->afq); ff_af_queue_init(avctx, &opus->afq);
@ -313,6 +407,20 @@ fail:
return ret; return ret;
} }
static void libopus_copy_samples_with_channel_map(
uint8_t *dst, const uint8_t *src, const uint8_t *channel_map,
int nb_channels, int nb_samples, int bytes_per_sample) {
int sample, channel;
for (sample = 0; sample < nb_samples; ++sample) {
for (channel = 0; channel < nb_channels; ++channel) {
const size_t src_pos = bytes_per_sample * (nb_channels * sample + channel);
const size_t dst_pos = bytes_per_sample * (nb_channels * sample + channel_map[channel]);
memcpy(&dst[dst_pos], &src[src_pos], bytes_per_sample);
}
}
}
static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt, static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt,
const AVFrame *frame, int *got_packet_ptr) const AVFrame *frame, int *got_packet_ptr)
{ {
@ -327,7 +435,12 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt,
ret = ff_af_queue_add(&opus->afq, frame); ret = ff_af_queue_add(&opus->afq, frame);
if (ret < 0) if (ret < 0)
return ret; return ret;
if (frame->nb_samples < opus->opts.packet_size) { if (opus->encoder_channel_map != NULL) {
audio = opus->samples;
libopus_copy_samples_with_channel_map(
audio, frame->data[0], opus->encoder_channel_map,
avctx->channels, frame->nb_samples, bytes_per_sample);
} else if (frame->nb_samples < opus->opts.packet_size) {
audio = opus->samples; audio = opus->samples;
memcpy(audio, frame->data[0], frame->nb_samples * sample_size); memcpy(audio, frame->data[0], frame->nb_samples * sample_size);
} else } else
@ -416,6 +529,7 @@ static const AVOption libopus_options[] = {
{ "off", "Use constant bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "vbr" }, { "off", "Use constant bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "vbr" },
{ "on", "Use variable bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "vbr" }, { "on", "Use variable bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "vbr" },
{ "constrained", "Use constrained VBR", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "vbr" }, { "constrained", "Use constrained VBR", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "vbr" },
{ "mapping_family", "Channel Mapping Family", OFFSET(mapping_family), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, FLAGS, "mapping_family" },
{ NULL }, { NULL },
}; };
@ -449,7 +563,6 @@ AVCodec ff_libopus_encoder = {
.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT,
AV_SAMPLE_FMT_NONE }, AV_SAMPLE_FMT_NONE },
.channel_layouts = ff_vorbis_channel_layouts,
.supported_samplerates = libopus_sample_rates, .supported_samplerates = libopus_sample_rates,
.priv_class = &libopus_class, .priv_class = &libopus_class,
.defaults = libopus_defaults, .defaults = libopus_defaults,

Loading…
Cancel
Save