From 37941878f193a2316c514bd5ba55bfe9d2dfdfcf Mon Sep 17 00:00:00 2001 From: Michael Graczyk Date: Tue, 14 Jun 2016 18:33:15 -0700 Subject: [PATCH] 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 --- doc/encoders.texi | 11 +++ libavcodec/libopusenc.c | 161 ++++++++++++++++++++++++++++++++++------ 2 files changed, 148 insertions(+), 24 deletions(-) diff --git a/doc/encoders.texi b/doc/encoders.texi index f38cad3e23..d0caa77ee3 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -1184,6 +1184,17 @@ following: 4000, 6000, 8000, 12000, or 20000, corresponding to narrowband, mediumband, wideband, super wideband, and fullband 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 @section libvorbis diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c index c9b96ce3f3..c40fcde7ba 100644 --- a/libavcodec/libopusenc.c +++ b/libavcodec/libopusenc.c @@ -38,6 +38,7 @@ typedef struct LibopusEncOpts { float frame_duration; int packet_size; int max_bandwidth; + int mapping_family; } LibopusEncOpts; typedef struct LibopusEncContext { @@ -47,6 +48,7 @@ typedef struct LibopusEncContext { uint8_t *samples; LibopusEncOpts opts; AudioFrameQueue afq; + const uint8_t *encoder_channel_map; } LibopusEncContext; 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. */ /* 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, coupled_stream_count); 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; } +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) { LibopusEncContext *opus = avctx->priv_data; OpusMSEncoder *enc; uint8_t libopus_channel_mapping[255]; int ret = OPUS_OK; + int av_ret; int coupled_stream_count, header_size, frame_size; + int mapping_family; frame_size = opus->opts.frame_duration * 48000 / 1000; 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 - * anything greater than 8 is undefined. */ - if (avctx->channels > 8) { - av_log(avctx, AV_LOG_ERROR, - "Channel layout undefined for %d channels.\n", avctx->channels); - return AVERROR_PATCHWELCOME; + /* Channels may need to be reordered to match opus mapping. */ + av_ret = libopus_validate_layout_and_get_channel_map(avctx, opus->opts.mapping_family, + &opus->encoder_channel_map); + if (av_ret) { + return av_ret; } - coupled_stream_count = opus_coupled_streams[avctx->channels - 1]; - opus->stream_count = avctx->channels - coupled_stream_count; + if (opus->opts.mapping_family == -1) { + /* 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], - avctx->channels * sizeof(*libopus_channel_mapping)); + /* Set the mapping family so that the value is correct in the header */ + mapping_family = avctx->channels > 2 ? 1 : 0; + coupled_stream_count = opus_coupled_streams[avctx->channels - 1]; + opus->stream_count = avctx->channels - coupled_stream_count; + memcpy(libopus_channel_mapping, + opus_vorbis_channel_map[avctx->channels - 1], + avctx->channels * sizeof(*libopus_channel_mapping)); - 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); + 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) { av_log(avctx, AV_LOG_ERROR, @@ -275,7 +368,8 @@ static av_cold int libopus_encode_init(AVCodecContext *avctx) 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); if (!avctx->extradata) { 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)); 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); @@ -313,6 +407,20 @@ fail: 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, 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); if (ret < 0) 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; memcpy(audio, frame->data[0], frame->nb_samples * sample_size); } 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" }, { "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" }, + { "mapping_family", "Channel Mapping Family", OFFSET(mapping_family), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, FLAGS, "mapping_family" }, { NULL }, }; @@ -449,7 +563,6 @@ AVCodec ff_libopus_encoder = { .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE }, - .channel_layouts = ff_vorbis_channel_layouts, .supported_samplerates = libopus_sample_rates, .priv_class = &libopus_class, .defaults = libopus_defaults,