Browse code

Xiph CELT/Opus decoder using libcelt.

Signed-off-by: Nicolas George <nicolas.george@normalesup.org>

Nicolas George authored on 2011/04/17 00:45:30
Showing 4 changed files
... ...
@@ -161,6 +161,7 @@ Configuration options:
161 161
 External library support:
162 162
   --enable-avisynth        enable reading of AVISynth script files [no]
163 163
   --enable-bzlib           enable bzlib [autodetect]
164
+  --enable-libcelt         enable CELT/Opus decoding via libcelt [no]
164 165
   --enable-frei0r          enable frei0r video filtering
165 166
   --enable-libopencore-amrnb enable AMR-NB de/encoding via libopencore-amrnb [no]
166 167
   --enable-libopencore-amrwb enable AMR-WB decoding via libopencore-amrwb [no]
... ...
@@ -929,6 +930,7 @@ CONFIG_LIST="
929 929
     h264pred
930 930
     hardcoded_tables
931 931
     huffman
932
+    libcelt
932 933
     libdc1394
933 934
     libdirac
934 935
     libfaac
... ...
@@ -1393,6 +1395,7 @@ vdpau_deps="vdpau_vdpau_h vdpau_vdpau_x11_h"
1393 1393
 h264_parser_select="golomb h264dsp h264pred"
1394 1394
 
1395 1395
 # external libraries
1396
+libcelt_decoder_deps="libcelt"
1396 1397
 libdirac_decoder_deps="libdirac !libschroedinger"
1397 1398
 libdirac_encoder_deps="libdirac"
1398 1399
 libfaac_encoder_deps="libfaac"
... ...
@@ -2888,6 +2891,7 @@ check_mathfunc truncf
2888 2888
 
2889 2889
 # these are off by default, so fail if requested and not available
2890 2890
 enabled avisynth   && require2 vfw32 "windows.h vfw.h" AVIFileInit -lavifil32
2891
+enabled libcelt    && require libcelt celt/celt.h celt_decode -lcelt0
2891 2892
 enabled frei0r     && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; }
2892 2893
 enabled libdirac   && require_pkg_config dirac                          \
2893 2894
     "libdirac_decoder/dirac_parser.h libdirac_encoder/dirac_encoder.h"  \
... ...
@@ -3167,6 +3171,7 @@ echo "threading support         ${thread_type-no}"
3167 3167
 echo "SDL support               ${sdl-no}"
3168 3168
 echo "Sun medialib support      ${mlib-no}"
3169 3169
 echo "AVISynth enabled          ${avisynth-no}"
3170
+echo "libcelt enabled           ${libcelt-no}"
3170 3171
 echo "frei0r enabled            ${frei0r-no}"
3171 3172
 echo "libdc1394 support         ${libdc1394-no}"
3172 3173
 echo "libdirac enabled          ${libdirac-no}"
... ...
@@ -553,6 +553,7 @@ OBJS-$(CONFIG_WEBM_MUXER)              += xiph.o mpeg4audio.o \
553 553
                                           mpegaudiodata.o
554 554
 
555 555
 # external codec libraries
556
+OBJS-$(CONFIG_LIBCELT_DECODER)            += libcelt_dec.o
556 557
 OBJS-$(CONFIG_LIBDIRAC_DECODER)           += libdiracdec.o
557 558
 OBJS-$(CONFIG_LIBDIRAC_ENCODER)           += libdiracenc.o libdirac_libschro.o
558 559
 OBJS-$(CONFIG_LIBFAAC_ENCODER)            += libfaac.o
... ...
@@ -365,6 +365,7 @@ void avcodec_register_all(void)
365 365
     REGISTER_ENCDEC  (XSUB, xsub);
366 366
 
367 367
     /* external libraries */
368
+    REGISTER_DECODER (LIBCELT, libcelt);
368 369
     REGISTER_ENCDEC  (LIBDIRAC, libdirac);
369 370
     REGISTER_ENCODER (LIBFAAC, libfaac);
370 371
     REGISTER_ENCDEC  (LIBGSM, libgsm);
371 372
new file mode 100644
... ...
@@ -0,0 +1,134 @@
0
+/*
1
+ * Xiph CELT / Opus decoder using libcelt
2
+ * Copyright (c) 2011 Nicolas George
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+#include <celt/celt.h>
22
+#include <celt/celt_header.h>
23
+#include "avcodec.h"
24
+#include "libavutil/intreadwrite.h"
25
+
26
+struct libcelt_context {
27
+    CELTMode *mode;
28
+    CELTDecoder *dec;
29
+    int frame_bytes;
30
+    int discard;
31
+};
32
+
33
+static int ff_celt_error_to_averror(int err)
34
+{
35
+    switch(err) {
36
+        case CELT_BAD_ARG:          return AVERROR(EINVAL);
37
+#ifdef CELT_BUFFER_TOO_SMALL
38
+        case CELT_BUFFER_TOO_SMALL: return AVERROR(ENOBUFS);
39
+#endif
40
+        case CELT_INTERNAL_ERROR:   return AVERROR(EFAULT);
41
+        case CELT_CORRUPTED_DATA:   return AVERROR_INVALIDDATA;
42
+        case CELT_UNIMPLEMENTED:    return AVERROR(ENOTSUP);
43
+        case CELT_INVALID_STATE:    return AVERROR(ENOTRECOVERABLE);
44
+        case CELT_ALLOC_FAIL:       return AVERROR(ENOMEM);
45
+        default:                    return AVERROR_UNKNOWN;
46
+    }
47
+}
48
+
49
+static int ff_celt_bitstream_version_hack(CELTMode *mode)
50
+{
51
+    CELTHeader header = { .version_id = 0 };
52
+    celt_header_init(&header, mode, 960, 2);
53
+    return header.version_id;
54
+}
55
+
56
+static av_cold int libcelt_dec_init(AVCodecContext *c)
57
+{
58
+    struct libcelt_context *celt = c->priv_data;
59
+    int err;
60
+
61
+    if (!c->channels || !c->frame_size ||
62
+        c->frame_size > INT_MAX / sizeof(int16_t) / c->channels)
63
+        return AVERROR(EINVAL);
64
+    celt->frame_bytes = c->frame_size * c->channels * sizeof(int16_t);
65
+    celt->mode = celt_mode_create(c->sample_rate, c->frame_size, &err);
66
+    if (!celt->mode)
67
+        return ff_celt_error_to_averror(err);
68
+    celt->dec = celt_decoder_create_custom(celt->mode, c->channels, &err);
69
+    if (!celt->dec) {
70
+        celt_mode_destroy(celt->mode);
71
+        return ff_celt_error_to_averror(err);
72
+    }
73
+    if (c->extradata_size >= 4) {
74
+        celt->discard = AV_RL32(c->extradata);
75
+        if (celt->discard < 0 || celt->discard >= c->frame_size) {
76
+            av_log(c, AV_LOG_WARNING,
77
+                   "Invalid overlap (%d), ignored.\n", celt->discard);
78
+            celt->discard = 0;
79
+        }
80
+        celt->discard *= c->channels * sizeof(int16_t);
81
+    }
82
+    if(c->extradata_size >= 8) {
83
+        unsigned version = AV_RL32(c->extradata + 4);
84
+        unsigned lib_version = ff_celt_bitstream_version_hack(celt->mode);
85
+        if (version != lib_version)
86
+            av_log(c, AV_LOG_WARNING,
87
+                   "CELT bitstream version 0x%x may be "
88
+                   "improperly decoded by libcelt for version 0x%x.\n",
89
+                   version, lib_version);
90
+    }
91
+    return 0;
92
+}
93
+
94
+static av_cold int libcelt_dec_close(AVCodecContext *c)
95
+{
96
+    struct libcelt_context *celt = c->priv_data;
97
+
98
+    celt_decoder_destroy(celt->dec);
99
+    celt_mode_destroy(celt->mode);
100
+    return 0;
101
+}
102
+
103
+static int libcelt_dec_decode(AVCodecContext *c, void *pcm, int *pcm_size,
104
+                              AVPacket *pkt)
105
+{
106
+    struct libcelt_context *celt = c->priv_data;
107
+    int err;
108
+
109
+    if (*pcm_size < celt->frame_bytes)
110
+        return AVERROR(ENOBUFS);
111
+    err = celt_decode(celt->dec, pkt->data, pkt->size, pcm, c->frame_size);
112
+    if (err < 0)
113
+        return ff_celt_error_to_averror(err);
114
+    *pcm_size = celt->frame_bytes;
115
+    if (celt->discard) {
116
+        *pcm_size = celt->frame_bytes - celt->discard;
117
+        memmove(pcm, (char *)pcm + celt->discard, *pcm_size);
118
+        celt->discard = 0;
119
+    }
120
+    return pkt->size;
121
+}
122
+
123
+AVCodec ff_libcelt_decoder = {
124
+    .name           = "libcelt",
125
+    .type           = AVMEDIA_TYPE_AUDIO,
126
+    .id             = CODEC_ID_CELT,
127
+    .priv_data_size = sizeof(struct libcelt_context),
128
+    .init           = libcelt_dec_init,
129
+    .close          = libcelt_dec_close,
130
+    .decode         = libcelt_dec_decode,
131
+    .capabilities   = 0,
132
+    .long_name = NULL_IF_CONFIG_SMALL("Xiph CELT/Opus decoder using libcelt"),
133
+};