On 09/24/2012 11:00 PM, Nathan Caldwell wrote:
> From: Nicolas George <[email protected]>
> 
> ---
>  Changelog               |    1 +
>  configure               |    5 ++
>  doc/general.texi        |    2 +
>  libavcodec/Makefile     |    1 +
>  libavcodec/allcodecs.c  |    1 +
>  libavcodec/libopusdec.c |  201 
> +++++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 211 insertions(+)
>  create mode 100644 libavcodec/libopusdec.c
> 
> diff --git a/Changelog b/Changelog
> index 669aa5a..4cc2f6a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -49,6 +49,7 @@ version <next>:
>  - RTP depacketization of JPEG
>  - Smooth Streaming live segmenter muxer
>  - RTP packetization of JPEG
> +- Opus decoder using libopus
>  
>  
>  version 0.8:
> diff --git a/configure b/configure
> index 4461bde..5080dcf 100755
> --- a/configure
> +++ b/configure
> @@ -181,6 +181,7 @@ External library support:
>    --enable-libopencore-amrwb enable AMR-WB decoding via libopencore-amrwb 
> [no]
>    --enable-libopencv       enable video filtering via libopencv [no]
>    --enable-libopenjpeg     enable JPEG 2000 de/encoding via OpenJPEG [no]
> +  --enable-libopus         enable Opus decoding via libopus [no]
>    --enable-libpulse        enable Pulseaudio input via libpulse [no]
>    --enable-librtmp         enable RTMP[E] support via librtmp [no]
>    --enable-libschroedinger enable Dirac de/encoding via libschroedinger [no]
> @@ -1009,6 +1010,7 @@ CONFIG_LIST="
>      libopencore_amrwb
>      libopencv
>      libopenjpeg
> +    libopus
>      libpulse
>      librtmp
>      libschroedinger
> @@ -1580,6 +1582,7 @@ libopencore_amrnb_encoder_deps="libopencore_amrnb"
>  libopencore_amrwb_decoder_deps="libopencore_amrwb"
>  libopenjpeg_decoder_deps="libopenjpeg"
>  libopenjpeg_encoder_deps="libopenjpeg"
> +libopus_decoder_deps="libopus"
>  libschroedinger_decoder_deps="libschroedinger"
>  libschroedinger_encoder_deps="libschroedinger"
>  libspeex_decoder_deps="libspeex"
> @@ -3291,6 +3294,7 @@ enabled libopencore_amrnb  && require libopencore_amrnb 
> opencore-amrnb/interf_de
>  enabled libopencore_amrwb  && require libopencore_amrwb 
> opencore-amrwb/dec_if.h D_IF_init -lopencore-amrwb
>  enabled libopencv  && require_pkg_config opencv opencv/cv.h 
> cvCreateImageHeader
>  enabled libopenjpeg && require libopenjpeg openjpeg.h opj_version -lopenjpeg
> +enabled libopus    && require_pkg_config opus opus_multistream.h 
> opus_multistream_decoder_create
>  enabled libpulse && require_pkg_config libpulse-simple pulse/simple.h 
> pa_simple_new
>  enabled librtmp    && require_pkg_config librtmp librtmp/rtmp.h RTMP_Socket
>  enabled libschroedinger && require_pkg_config schroedinger-1.0 
> schroedinger/schro.h schro_init
> @@ -3589,6 +3593,7 @@ echo "libopencore-amrnb support ${libopencore_amrnb-no}"
>  echo "libopencore-amrwb support ${libopencore_amrwb-no}"
>  echo "libopencv support         ${libopencv-no}"
>  echo "libopenjpeg enabled       ${libopenjpeg-no}"
> +echo "libopus enabled           ${libopus-no}"
>  echo "libpulse enabled          ${libpulse-no}"
>  echo "librtmp enabled           ${librtmp-no}"
>  echo "libschroedinger enabled   ${libschroedinger-no}"
> diff --git a/doc/general.texi b/doc/general.texi
> index 8415ca3..3aeb92b 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -756,6 +756,8 @@ following image formats are supported:
>  @item Musepack SV7           @tab     @tab  X
>  @item Musepack SV8           @tab     @tab  X
>  @item Nellymoser Asao        @tab  X  @tab  X
> +@item Opus                   @tab     @tab  E
> +    @tab supported through external library libopus
>  @item PCM A-law              @tab  X  @tab  X
>  @item PCM mu-law             @tab  X  @tab  X
>  @item PCM 16-bit little-endian planar  @tab     @tab  X
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 45c6d66..d4537fd 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -581,6 +581,7 @@ OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER)  += 
> libopencore-amr.o
>  OBJS-$(CONFIG_LIBOPENCORE_AMRWB_DECODER)  += libopencore-amr.o
>  OBJS-$(CONFIG_LIBOPENJPEG_DECODER)        += libopenjpegdec.o
>  OBJS-$(CONFIG_LIBOPENJPEG_ENCODER)        += libopenjpegenc.o
> +OBJS-$(CONFIG_LIBOPUS_DECODER)            += libopusdec.o vorbis_data.o
>  OBJS-$(CONFIG_LIBSCHROEDINGER_DECODER)    += libschroedingerdec.o \
>                                               libschroedinger.o
>  OBJS-$(CONFIG_LIBSCHROEDINGER_ENCODER)    += libschroedingerenc.o \
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index c6df818..7881831 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -393,6 +393,7 @@ void avcodec_register_all(void)
>      REGISTER_ENCDEC  (LIBOPENCORE_AMRNB, libopencore_amrnb);
>      REGISTER_DECODER (LIBOPENCORE_AMRWB, libopencore_amrwb);
>      REGISTER_ENCDEC  (LIBOPENJPEG, libopenjpeg);
> +    REGISTER_DECODER (LIBOPUS, libopus);
>      REGISTER_ENCDEC  (LIBSCHROEDINGER, libschroedinger);
>      REGISTER_ENCDEC  (LIBSPEEX, libspeex);
>      REGISTER_ENCODER (LIBTHEORA, libtheora);
> diff --git a/libavcodec/libopusdec.c b/libavcodec/libopusdec.c
> new file mode 100644
> index 0000000..44ad63b
> --- /dev/null
> +++ b/libavcodec/libopusdec.c
> @@ -0,0 +1,201 @@
> +/*
> + * Opus decoder using libopus
> + * Copyright (c) 2012 Nicolas George
> + *
> + * 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 "vorbis.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/common.h"
> +
> +struct libopus_context {
> +    OpusMSDecoder *dec;
> +    AVFrame frame;
> +    int pre_skip;

Remove pre_skip. It is unused.

> +};
> +
> +static int opus_error_to_averror(int err)
> +{
> +    switch (err) {
> +    case OPUS_BAD_ARG:
> +        return AVERROR(EINVAL);
> +    case OPUS_BUFFER_TOO_SMALL:
> +        return AVERROR_UNKNOWN;
> +    case OPUS_INTERNAL_ERROR:
> +        return AVERROR(EFAULT);
> +    case OPUS_INVALID_PACKET:
> +        return AVERROR_INVALIDDATA;
> +    case OPUS_UNIMPLEMENTED:
> +        return AVERROR(ENOSYS);
> +    case OPUS_INVALID_STATE:
> +        return AVERROR_UNKNOWN;
> +    case OPUS_ALLOC_FAIL:
> +        return AVERROR(ENOMEM);
> +    default:
> +        return AVERROR(EINVAL);
> +    }
> +}
> +
> +static inline void reorder(uint8_t *data, unsigned channels, unsigned bps,
> +                           unsigned samples, const uint8_t *map)
> +{
> +    uint8_t tmp[8 * 4];
> +    unsigned i;
> +
> +    av_assert1(channels * bps <= sizeof(tmp));
> +    for (; samples > 0; samples--) {
> +        for (i = 0; i < channels; i++)
> +            memcpy(tmp + bps * i, data + bps * map[i], bps);
> +        memcpy(data, tmp, bps * channels);
> +        data += bps * channels;
> +    }
> +}
> +
> +#define OPUS_HEAD_SIZE 19
> +
> +static av_cold int libopus_decode_init(AVCodecContext *avc)
> +{
> +    struct libopus_context *opus = avc->priv_data;
> +    int ret, channel_map = 0, gain_db = 0, nb_streams, nb_coupled;
> +    uint8_t mapping_stereo[] = { 0, 1 }, *mapping;
> +
> +    avc->sample_rate    = 48000;
> +    avc->sample_fmt     = avc->request_sample_fmt == AV_SAMPLE_FMT_FLT ?
> +                          AV_SAMPLE_FMT_FLT : AV_SAMPLE_FMT_S16;
> +    avc->channel_layout = avc->channels > 8 ? 0 :
> +                          ff_vorbis_channel_layouts[avc->channels - 1];
> +
> +    if (avc->extradata_size >= OPUS_HEAD_SIZE) {
> +        opus->pre_skip = AV_RL16(avc->extradata + 10);
> +        gain_db        = AV_RL16(avc->extradata + 16);
> +        channel_map    = AV_RL8 (avc->extradata + 18);
> +        gain_db -= (gain_db & 0x8000) << 1; /* signed */
> +    }

Would it work to just use sign_extend() for the gain_db calculation?

> +    if (avc->extradata_size >= OPUS_HEAD_SIZE + 2 + avc->channels) {
> +        nb_streams = avc->extradata[OPUS_HEAD_SIZE + 0];
> +        nb_coupled = avc->extradata[OPUS_HEAD_SIZE + 1];
> +        if (nb_streams + nb_coupled != avc->channels)
> +            av_log(avc, AV_LOG_WARNING, "Inconsistent channel mapping.\n");
> +        mapping = avc->extradata + OPUS_HEAD_SIZE + 2;
> +    } else {
> +        if (avc->channels > 2 || channel_map) {
> +            av_log(avc, AV_LOG_ERROR,
> +                   "No channel mapping for %d channels.\n", avc->channels);
> +            return AVERROR(EINVAL);
> +        }
> +        nb_streams = 1;
> +        nb_coupled = avc->channels > 1;
> +        mapping    = mapping_stereo;
> +    }
> +
> +    opus->dec = opus_multistream_decoder_create(avc->sample_rate, 
> avc->channels,
> +                                                nb_streams, nb_coupled,
> +                                                mapping, &ret);
> +    if (!opus->dec) {
> +        av_log(avc, AV_LOG_ERROR, "Unable to create decoder: %s\n",
> +               opus_strerror(ret));
> +        return opus_error_to_averror(ret);
> +    }
> +
> +    ret = opus_multistream_decoder_ctl(opus->dec, OPUS_SET_GAIN(gain_db));
> +    if (ret != OPUS_OK)
> +        av_log(avc, AV_LOG_WARNING, "Failed to set gain: %s\n",
> +               opus_strerror(ret));
> +
> +    avcodec_get_frame_defaults(&opus->frame);
> +    avc->coded_frame = &opus->frame;
> +    return 0;
> +}
> +
> +static av_cold int libopus_decode_close(AVCodecContext *avc)
> +{
> +    struct libopus_context *opus = avc->priv_data;
> +
> +    opus_multistream_decoder_destroy(opus->dec);
> +    return 0;
> +}
> +
> +#define MAX_FRAME_SIZE (960 * 6)
> +
> +static int libopus_decode(AVCodecContext *avc, void *frame,
> +                              int *got_frame_ptr, AVPacket *pkt)
> +{
> +    struct libopus_context *opus = avc->priv_data;
> +    int ret, nb_samples;
> +
> +    opus->frame.nb_samples = MAX_FRAME_SIZE;
> +    ret = avc->get_buffer(avc, &opus->frame);
> +    if (ret < 0) {
> +        av_log(avc, AV_LOG_ERROR, "get_buffer() failed\n");
> +        return ret;
> +    }
> +
> +    if (avc->sample_fmt == AV_SAMPLE_FMT_S16)
> +        nb_samples = opus_multistream_decode(opus->dec, pkt->data, pkt->size,
> +                                             (opus_int16 
> *)opus->frame.data[0],
> +                                             opus->frame.nb_samples, 0);
> +    else
> +        nb_samples = opus_multistream_decode_float(opus->dec, pkt->data, 
> pkt->size,
> +                                                   (float 
> *)opus->frame.data[0],
> +                                                   opus->frame.nb_samples, 
> 0);
> +
> +    if (nb_samples < 0) {
> +        av_log(avc, AV_LOG_ERROR, "Decoding error: %s\n",
> +               opus_strerror(nb_samples));
> +        return opus_error_to_averror(nb_samples);
> +    }
> +
> +    if (avc->channels > 3 && avc->channels <= 8) {
> +        const uint8_t *m = ff_vorbis_channel_layout_offsets[avc->channels - 
> 1];
> +        if (avc->sample_fmt == AV_SAMPLE_FMT_S16)
> +            reorder(opus->frame.data[0], avc->channels, 2, nb_samples, m);
> +        else
> +            reorder(opus->frame.data[0], avc->channels, 4, nb_samples, m);
> +    }
> +
> +    opus->frame.nb_samples = nb_samples;
> +    *(AVFrame *)frame = opus->frame;
> +    *got_frame_ptr = 1;
> +    return pkt->size;
> +}
> +
> +static void libopus_flush(AVCodecContext *avc)
> +{
> +    struct libopus_context *opus = avc->priv_data;
> +
> +    opus_multistream_decoder_ctl(opus->dec, OPUS_RESET_STATE);
> +    /* TODO: Discard pre_skip samples to account for codec delay */
> +}

I would remove that TODO. It's not something that should be done in the
decoder.

> +
> +AVCodec ff_libopus_decoder = {
> +    .name           = "libopus",
> +    .type           = AVMEDIA_TYPE_AUDIO,
> +    .id             = AV_CODEC_ID_OPUS,
> +    .priv_data_size = sizeof(struct libopus_context),
> +    .init           = libopus_decode_init,
> +    .close          = libopus_decode_close,
> +    .decode         = libopus_decode,
> +    .flush          = libopus_flush,
> +    .capabilities   = CODEC_CAP_DR1,
> +    .long_name      = NULL_IF_CONFIG_SMALL("libopus Opus"),
> +};

Need to set .sample_fmts.

The rest looks ok, taking into consideration your follow-up patches.

-Justin
_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to