Opus decoder using libopus
authorNicolas George <nicolas.george@normalesup.org>
Fri, 28 Sep 2012 01:39:23 +0000 (19:39 -0600)
committerDiego Biurrun <diego@biurrun.de>
Fri, 28 Sep 2012 09:03:20 +0000 (11:03 +0200)
Signed-off-by: Diego Biurrun <diego@biurrun.de>
Changelog
configure
doc/general.texi
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/libopusdec.c [new file with mode: 0644]
libavcodec/version.h

index 669aa5a..4cc2f6a 100644 (file)
--- 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:
index 4461bde..5080dcf 100755 (executable)
--- 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}"
index 8415ca3..3aeb92b 100644 (file)
@@ -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
index 45c6d66..d4537fd 100644 (file)
@@ -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 \
index c6df818..7881831 100644 (file)
@@ -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 (file)
index 0000000..9d24e6e
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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 "libavutil/common.h"
+#include "libavutil/avassert.h"
+#include "libavutil/intreadwrite.h"
+#include "avcodec.h"
+#include "vorbis.h"
+#include "mathops.h"
+
+struct libopus_context {
+    OpusMSDecoder *dec;
+    AVFrame frame;
+};
+
+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) {
+        gain_db     = sign_extend(AV_RL16(avc->extradata + 16), 16);
+        channel_map = AV_RL8 (avc->extradata + 18);
+    }
+    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));
+
+    avc->delay = 3840;  /* Decoder delay (in samples) at 48kHz */
+    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);
+}
+
+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"),
+    .sample_fmts    = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLT,
+                                                     AV_SAMPLE_FMT_S16,
+                                                     AV_SAMPLE_FMT_NONE },
+};
index ee70842..8844502 100644 (file)
@@ -27,7 +27,7 @@
  */
 
 #define LIBAVCODEC_VERSION_MAJOR 54
-#define LIBAVCODEC_VERSION_MINOR 29
+#define LIBAVCODEC_VERSION_MINOR 30
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \