On 09/18/2012 10:41 PM, Nathan Caldwell wrote:
> ---
> Changelog | 1 +
> doc/general.texi | 2 +-
> libavcodec/Makefile | 1 +
> libavcodec/allcodecs.c | 2 +-
> libavcodec/libopus.h | 43 ++++++
> libavcodec/libopusdec.c | 27 +---
> libavcodec/libopusenc.c | 357
> +++++++++++++++++++++++++++++++++++++++++++++++
> libavcodec/version.h | 2 +-
> 8 files changed, 408 insertions(+), 27 deletions(-)
> create mode 100644 libavcodec/libopus.h
> create mode 100644 libavcodec/libopusenc.c
>
[...]
> diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c
> new file mode 100644
> index 0000000..7691263
> --- /dev/null
> +++ b/libavcodec/libopusenc.c
> @@ -0,0 +1,357 @@
> +/*
> + * Opus encoder using libopus
> + * Copyright (c) 2012 Nathan Caldwell
> + *
> + * This file is part of libav.
> + *
> + * libav 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.
> + *
> + * libav 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 libav; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA
> + */
> +
> +#include <opus.h>
> +#include <opus_multistream.h>
> +
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "internal.h"
> +#include "libopus.h"
> +#include "vorbis.h"
> +#include "audio_frame_queue.h"
> +#include "libavutil/common.h"
> +#include "libavutil/opt.h"
put libavutil includes before libavcodec includes
> +
> +typedef struct {
> + int vbr;
> + int application;
> + int packet_loss;
> + int complexity;
> + int packet_size;
> +} LibOpusEncOpts;
> +
> +typedef struct {
> + AVClass *class;
> + OpusMSEncoder *enc;
> + const uint8_t *channel_layout;
> + int stream_count;
> + LibOpusEncOpts opts;
> + AudioFrameQueue afq;
> +} LibOpusEncContext;
> +
> +static const uint8_t opus_coupled_streams[8] = {
> + 0, 1, 1, 2, 2, 2, 2, 3
> +};
> +
> +/* Opus internal to Vorbis channel order mapping written in the header */
> +static const uint8_t opus_vorbis_channel_map[8][8] = {
> + { 0 },
> + { 0, 1 },
> + { 0, 2, 1 },
> + { 0, 1, 2, 3 },
> + { 0, 4, 1, 2, 3 },
> + { 0, 4, 1, 2, 3, 5 },
> + { 0, 4, 1, 2, 3, 5, 6 },
> + { 0, 6, 1, 2, 3, 4, 5, 7 },
> +};
> +
> +/* libav to libopus channel order mapping, passed to libopus */
> +static const uint8_t libav_libopus_channel_map[8][8] = {
> + { 0 },
> + { 0, 1 },
> + { 0, 1, 2 },
> + { 0, 1, 2, 3 },
> + { 0, 1, 3, 4, 2 },
> + { 0, 1, 4, 5, 2, 3 },
> + { 0, 1, 5, 6, 2, 4, 3 },
> + { 0, 1, 6, 7, 4, 5, 2, 3 },
> +};
> +
> +static void libopus_write_header(AVCodecContext *avctx, int stream_count,
> int coupled_stream_count, const uint8_t *channel_mapping)
> +{
> + uint8_t *p = avctx->extradata;
> + int channels = avctx->channels;
> +
> + bytestream_put_buffer(&p, "OpusHead", 8);
> + bytestream_put_byte(&p, 1); /* Version */
> + bytestream_put_byte(&p, channels);
> + bytestream_put_le16(&p, avctx->delay); /* Lookahead samples at 48kHz */
> + bytestream_put_le32(&p, avctx->sample_rate); /* Original sample rate */
> + bytestream_put_le16(&p, 0); /* Gain of 0dB is recommended. */
> +
> + /* Channel mapping */
> + if (channels > 2) {
> + int ch;
> +
> + bytestream_put_byte(&p, channels <= 8 ? 1 : 255);
> + bytestream_put_byte(&p, stream_count);
> + bytestream_put_byte(&p, coupled_stream_count);
> + for (ch = 0; ch < channels; ch++)
> + bytestream_put_byte(&p, channel_mapping[ch]);
bytestream_put_buffer(&p, channel_mapping, channels);
> + } else {
> + bytestream_put_byte(&p, 0);
> + }
> +}
> +
> +static int libopus_configure_encoder(AVCodecContext *avctx, OpusMSEncoder
> *enc, LibOpusEncOpts *opts)
> +{
> + int ret;
> +
> + ret = opus_multistream_encoder_ctl(enc,
> OPUS_SET_BITRATE(avctx->bit_rate));
> + if (ret != OPUS_OK) {
> + av_log(avctx, AV_LOG_ERROR, "Failed to set bitrate: %s\n",
> opus_strerror(ret));
> + return ret;
> + }
> +
> + ret = opus_multistream_encoder_ctl(enc,
> OPUS_SET_COMPLEXITY(opts->complexity));
> + if (ret != OPUS_OK)
> + av_log(avctx, AV_LOG_WARNING, "Unable to set complexity: %s\n",
> opus_strerror(ret));
> +
> + ret = opus_multistream_encoder_ctl(enc, OPUS_SET_VBR(!!opts->vbr));
> + if (ret != OPUS_OK)
> + av_log(avctx, AV_LOG_WARNING, "Unable to set VBR: %s\n",
> opus_strerror(ret));
> +
> + ret = opus_multistream_encoder_ctl(enc,
> OPUS_SET_VBR_CONSTRAINT(opts->vbr == 2));
> + if (ret != OPUS_OK)
> + av_log(avctx, AV_LOG_WARNING, "Unable to set constrained VBR: %s\n",
> opus_strerror(ret));
> +
> + ret = opus_multistream_encoder_ctl(enc,
> OPUS_SET_PACKET_LOSS_PERC(opts->packet_loss));
> + if (ret != OPUS_OK)
> + av_log(avctx, AV_LOG_WARNING, "Unable to set expect packet loss
> percentage: %s\n", opus_strerror(ret));
> +
> + if (avctx->cutoff) {
> + ret = opus_multistream_encoder_ctl(enc,
> OPUS_SET_MAX_BANDWIDTH(avctx->cutoff));
> + if (ret != OPUS_OK)
> + av_log(avctx, AV_LOG_WARNING, "Unable to set maximum bandwidth:
> %s\n", opus_strerror(ret));
> + }
> +
> + return OPUS_OK;
> +}
> +
> +static int av_cold libopus_encode_init(AVCodecContext *avctx)
> +{
> + LibOpusEncContext *opus = avctx->priv_data;
> + OpusMSEncoder *enc;
> + int ret = OPUS_OK;
> + int coupled_stream_count, header_size;
> +
> + /* 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_WARNING, "Unsupported number of channels
> (%d).\n",
> + avctx->channels);
> + return AVERROR(EINVAL);
> + }
This check is already covered in avcodec_open2() because you have set
channel_layouts in the AVCodec.
> +
> + /* libopus officially supports 8kHz, 12kHz, 16kHz, 24kHz and 48kHz sample
> + * rates. Except for 48kHz, everything is resampled up to 48kHz
> internally by libopus.
> + * Additionally, libopus dynamically adjusts it's bandwidth depending on
> the requested bitrate.
> + * So it is safe to use 48kHz all the time.
> + */
> + if (avctx->sample_rate != 48000) {
> + av_log(avctx, AV_LOG_ERROR, "Unsupported sample rate (%d).\n",
> avctx->sample_rate);
> + return AVERROR(EINVAL);
> + }
same here for AVCodec.supported_samplerates
> +
> + if (avctx->bit_rate < 500 || avctx->bit_rate > 256000 * avctx->channels)
> {
> + av_log(avctx, AV_LOG_ERROR, "The bit rate %d bps is unsupported.
> Please choose a value between 500 and %d.\n",
> + avctx->bit_rate, 256000 * avctx->channels);
> + return AVERROR(EINVAL);
> + }
would be useful to set the bit_rate default to 0, then if it's 0 use a
good default value based on the number of channels (check what defaults
opusenc uses).
> +
> + switch (avctx->frame_size) {
> + case 120:
> + case 240:
> + if (opus->opts.application !=
> OPUS_APPLICATION_RESTRICTED_LOWDELAY)
> + av_log(avctx, AV_LOG_WARNING, "LPC mode can not be used with
> a frame size smaller than 480. "
> + "Enabling restricted low-delay mode.\nUse a larger
> frame size if this is not what you want.\n");
> + /* Frame sizes less than 480 samples (10 ms) can only use MDCT
> mode, so
> + * switching to RESTRICTED_LOWDELAY avoids an unnecessary 120
> samples (2.5 ms) of lookahead.
> + */
> + opus->opts.application = OPUS_APPLICATION_RESTRICTED_LOWDELAY;
> + case 480:
> + case 960:
> + case 1920:
> + case 2880:
> + opus->opts.packet_size = avctx->frame_size;
> + break;
> + default:
> + av_log(avctx, AV_LOG_ERROR, "Invalid frame size: %d.\nFrame size
> must be exactly one of: 120, 240, 480, 960, 1920 or 2880.\n",
> + avctx->frame_size);
> + return AVERROR(EINVAL);
> + }
> +
> + if (avctx->compression_level < 0 || avctx->compression_level > 10) {
> + av_log(avctx, AV_LOG_WARNING, "Compression level must be in the
> range 0 to 10. Defaulting to 10.\n");
> + opus->opts.complexity = 10;
> + } else {
> + opus->opts.complexity = avctx->compression_level;
> + }
> +
> + if (avctx->cutoff) {
> + switch (avctx->cutoff) {
> + case 4000:
> + case 6000:
> + case 8000:
> + case 12000:
> + case 20000:
> + break;
> + default:
> + av_log(avctx, AV_LOG_WARNING, "Invalid frequency cutoff: %d.
> Using default maximum bandwidth.\n"
> + "Cutoff frequency must be exactly one of: 4000, 6000,
> 8000, 12000 or 20000.\n", avctx->cutoff);
> + avctx->cutoff = 0;
> + }
> + }
> +
> + coupled_stream_count = opus_coupled_streams[avctx->channels - 1];
> + opus->stream_count = avctx->channels - coupled_stream_count;
> + opus->channel_layout = libav_libopus_channel_map[avctx->channels - 1];
> +
> + enc = opus_multistream_encoder_create(avctx->sample_rate,
> avctx->channels,
> + opus->stream_count,
> coupled_stream_count,
> + opus->channel_layout,
> opus->opts.application, &ret);
> + if (ret != OPUS_OK) {
> + av_log(avctx, AV_LOG_ERROR, "Failed to create encoder: %s\n",
> opus_strerror(ret));
> + return ff_opus_error_to_averror(ret);
> + }
> +
> + ret = libopus_configure_encoder(avctx, enc, &opus->opts);
> + if (ret != OPUS_OK)
> + goto fail;
ret needs to be converted from opus error to averror here.
> +
> + header_size = 19 + (avctx->channels > 2 ? 2 + avctx->channels : 0);
> + avctx->extradata = av_malloc(header_size + FF_INPUT_BUFFER_PADDING_SIZE);
> + if (!avctx->extradata) {
> + av_log(avctx, AV_LOG_ERROR, "Failed to allocate extradata.\n");
> + ret = AVERROR(ENOMEM);
> + goto fail;
> + }
> + avctx->extradata_size = header_size;
> +
> + ret = opus_multistream_encoder_ctl(enc,
> OPUS_GET_LOOKAHEAD(&avctx->delay));
> + if (ret != OPUS_OK)
> + av_log(avctx, AV_LOG_WARNING, "Unable to get number of lookahead
> samples: %s\n", opus_strerror(ret));
> +
> + libopus_write_header(avctx, opus->stream_count, coupled_stream_count,
> opus_vorbis_channel_map[avctx->channels - 1]);
> +
> + ff_af_queue_init(avctx, &opus->afq);
> +
> + opus->enc = enc;
> +
> + return 0;
> +
> +fail:
> + opus_multistream_encoder_destroy(enc);
> + return ret;
> +}
> +
> +static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt,
> + const AVFrame *frame, int *got_packet_ptr)
> +{
> + LibOpusEncContext *opus = avctx->priv_data;
> + int ret;
> +
> + if (!frame)
> + return 0;
Why even use CODEC_CAP_DELAY if you're just returning 0?
Have you checked that the encoder will always write all input samples?
The API documentation for libopus isn't really clear about flushing the
encoder to account for the initial delay. You may have to write another
silent frame depending on how many real samples were given in the last
frame. You may or may not have to use CODEC_CAP_SMALL_LAST_FRAME to do
this properly.
> +
> + ff_af_queue_add(&opus->afq, frame);
> +
> + /* Maximum packet size taken from opusenc.c in opus-tools */
> + if (ret = ff_alloc_packet(avpkt, (1275 * 3 + 7) * opus->stream_count)) {
> + av_log(avctx, AV_LOG_ERROR, "Error getting output packet\n");
> + return ret;
> + }
> +
> + if (avctx->sample_fmt == AV_SAMPLE_FMT_FLT) {
> + ret = opus_multistream_encode_float(opus->enc, (float
> *)frame->data[0],
> + opus->opts.packet_size,
> avpkt->data, avpkt->size);
> + } else {
> + ret = opus_multistream_encode(opus->enc, (opus_int16
> *)frame->data[0],
> + opus->opts.packet_size, avpkt->data,
> avpkt->size);
> + }
> + /* FIXME: When DTX is enabled, opus_multistream_encode will return 1 if
> + * a packet does not need to be transmitted.
> + */
> + if (ret < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Error encoding frame: %s\n",
> opus_strerror(ret));
> + return ff_opus_error_to_averror(ret);
> + }
> +
> + avpkt->size = ret;
av_shrink_packet(avpkt, ret);
> +
> + ff_af_queue_remove(&opus->afq, opus->opts.packet_size, &avpkt->pts,
> &avpkt->duration);
> +
> + *got_packet_ptr = 1;
> +
> + return 0;
> +}
> +
> +static int av_cold libopus_encode_close(AVCodecContext *avctx)
> +{
> + LibOpusEncContext *opus = avctx->priv_data;
> +
> + opus_multistream_encoder_destroy(opus->enc);
> +
> + ff_af_queue_close(&opus->afq);
> +
> + av_freep(&avctx->extradata);
> +
> + return 0;
> +}
> +
> +#define OFFSET(x) offsetof(LibOpusEncContext, opts.x)
> +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +static const AVOption libopus_options[] = {
> + { "vbr", "Variable bit rate mode", OFFSET(vbr),
> AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 2, 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" },
> + { "constrained", "Use constrained VBR", 0, AV_OPT_TYPE_CONST, {
> .i64 = 2 }, 0, 0, FLAGS, "vbr" },
> + { "packet_loss", "Expected packet loss percentage",
> OFFSET(packet_loss), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, FLAGS },
> + { "application", "Intended application type",
> OFFSET(application), AV_OPT_TYPE_INT, { .i64 = OPUS_APPLICATION_AUDIO },
> OPUS_APPLICATION_VOIP, OPUS_APPLICATION_RESTRICTED_LOWDELAY, FLAGS,
> "application" },
> + { "voip", "Favor improved speech intelligibility", 0,
> AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_VOIP }, 0, 0,
> FLAGS, "application" },
> + { "audio", "Favor faithfulness to the input", 0,
> AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_AUDIO }, 0, 0,
> FLAGS, "application" },
> + { "lowdelay", "Favor minimum possible coding delay", 0,
> AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_RESTRICTED_LOWDELAY }, 0, 0,
> FLAGS, "application" },
> + { NULL },
> +};
I know there have been disagreements on formatting for AVOption lists,
but at least some alignment would be nice...
Thanks,
Justin
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel