1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Xiph CELT decoder using libcelt
3cabdff1aSopenharmony_ci * Copyright (c) 2011 Nicolas George
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include <celt/celt.h>
23cabdff1aSopenharmony_ci#include <celt/celt_header.h>
24cabdff1aSopenharmony_ci#include "avcodec.h"
25cabdff1aSopenharmony_ci#include "codec_internal.h"
26cabdff1aSopenharmony_ci#include "internal.h"
27cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h"
28cabdff1aSopenharmony_ci
29cabdff1aSopenharmony_cistruct libcelt_context {
30cabdff1aSopenharmony_ci    CELTMode *mode;
31cabdff1aSopenharmony_ci    CELTDecoder *dec;
32cabdff1aSopenharmony_ci    int discard;
33cabdff1aSopenharmony_ci};
34cabdff1aSopenharmony_ci
35cabdff1aSopenharmony_cistatic int ff_celt_error_to_averror(int err)
36cabdff1aSopenharmony_ci{
37cabdff1aSopenharmony_ci    switch (err) {
38cabdff1aSopenharmony_ci        case CELT_BAD_ARG:          return AVERROR(EINVAL);
39cabdff1aSopenharmony_ci#ifdef CELT_BUFFER_TOO_SMALL
40cabdff1aSopenharmony_ci        case CELT_BUFFER_TOO_SMALL: return AVERROR(ENOBUFS);
41cabdff1aSopenharmony_ci#endif
42cabdff1aSopenharmony_ci        case CELT_INTERNAL_ERROR:   return AVERROR(EFAULT);
43cabdff1aSopenharmony_ci        case CELT_CORRUPTED_DATA:   return AVERROR_INVALIDDATA;
44cabdff1aSopenharmony_ci        case CELT_UNIMPLEMENTED:    return AVERROR(ENOSYS);
45cabdff1aSopenharmony_ci#ifdef ENOTRECOVERABLE
46cabdff1aSopenharmony_ci        case CELT_INVALID_STATE:    return AVERROR(ENOTRECOVERABLE);
47cabdff1aSopenharmony_ci#endif
48cabdff1aSopenharmony_ci        case CELT_ALLOC_FAIL:       return AVERROR(ENOMEM);
49cabdff1aSopenharmony_ci        default:                    return AVERROR(EINVAL);
50cabdff1aSopenharmony_ci    }
51cabdff1aSopenharmony_ci}
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_cistatic int ff_celt_bitstream_version_hack(CELTMode *mode)
54cabdff1aSopenharmony_ci{
55cabdff1aSopenharmony_ci    CELTHeader header = { .version_id = 0 };
56cabdff1aSopenharmony_ci    celt_header_init(&header, mode, 960, 2);
57cabdff1aSopenharmony_ci    return header.version_id;
58cabdff1aSopenharmony_ci}
59cabdff1aSopenharmony_ci
60cabdff1aSopenharmony_cistatic av_cold int libcelt_dec_init(AVCodecContext *c)
61cabdff1aSopenharmony_ci{
62cabdff1aSopenharmony_ci    struct libcelt_context *celt = c->priv_data;
63cabdff1aSopenharmony_ci    int err;
64cabdff1aSopenharmony_ci
65cabdff1aSopenharmony_ci    if (!c->ch_layout.nb_channels || !c->frame_size ||
66cabdff1aSopenharmony_ci        c->frame_size > INT_MAX / sizeof(int16_t) / c->ch_layout.nb_channels)
67cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
68cabdff1aSopenharmony_ci    celt->mode = celt_mode_create(c->sample_rate, c->frame_size, &err);
69cabdff1aSopenharmony_ci    if (!celt->mode)
70cabdff1aSopenharmony_ci        return ff_celt_error_to_averror(err);
71cabdff1aSopenharmony_ci    celt->dec = celt_decoder_create_custom(celt->mode, c->ch_layout.nb_channels, &err);
72cabdff1aSopenharmony_ci    if (!celt->dec) {
73cabdff1aSopenharmony_ci        celt_mode_destroy(celt->mode);
74cabdff1aSopenharmony_ci        return ff_celt_error_to_averror(err);
75cabdff1aSopenharmony_ci    }
76cabdff1aSopenharmony_ci    if (c->extradata_size >= 4) {
77cabdff1aSopenharmony_ci        celt->discard = AV_RL32(c->extradata);
78cabdff1aSopenharmony_ci        if (celt->discard < 0 || celt->discard >= c->frame_size) {
79cabdff1aSopenharmony_ci            av_log(c, AV_LOG_WARNING,
80cabdff1aSopenharmony_ci                   "Invalid overlap (%d), ignored.\n", celt->discard);
81cabdff1aSopenharmony_ci            celt->discard = 0;
82cabdff1aSopenharmony_ci        }
83cabdff1aSopenharmony_ci    }
84cabdff1aSopenharmony_ci    if (c->extradata_size >= 8) {
85cabdff1aSopenharmony_ci        unsigned version = AV_RL32(c->extradata + 4);
86cabdff1aSopenharmony_ci        unsigned lib_version = ff_celt_bitstream_version_hack(celt->mode);
87cabdff1aSopenharmony_ci        if (version != lib_version)
88cabdff1aSopenharmony_ci            av_log(c, AV_LOG_WARNING,
89cabdff1aSopenharmony_ci                   "CELT bitstream version 0x%x may be "
90cabdff1aSopenharmony_ci                   "improperly decoded by libcelt for version 0x%x.\n",
91cabdff1aSopenharmony_ci                   version, lib_version);
92cabdff1aSopenharmony_ci    }
93cabdff1aSopenharmony_ci    c->sample_fmt = AV_SAMPLE_FMT_S16;
94cabdff1aSopenharmony_ci    return 0;
95cabdff1aSopenharmony_ci}
96cabdff1aSopenharmony_ci
97cabdff1aSopenharmony_cistatic av_cold int libcelt_dec_close(AVCodecContext *c)
98cabdff1aSopenharmony_ci{
99cabdff1aSopenharmony_ci    struct libcelt_context *celt = c->priv_data;
100cabdff1aSopenharmony_ci
101cabdff1aSopenharmony_ci    celt_decoder_destroy(celt->dec);
102cabdff1aSopenharmony_ci    celt_mode_destroy(celt->mode);
103cabdff1aSopenharmony_ci    return 0;
104cabdff1aSopenharmony_ci}
105cabdff1aSopenharmony_ci
106cabdff1aSopenharmony_cistatic int libcelt_dec_decode(AVCodecContext *c, AVFrame *frame,
107cabdff1aSopenharmony_ci                              int *got_frame_ptr, AVPacket *pkt)
108cabdff1aSopenharmony_ci{
109cabdff1aSopenharmony_ci    struct libcelt_context *celt = c->priv_data;
110cabdff1aSopenharmony_ci    int err;
111cabdff1aSopenharmony_ci    int16_t *pcm;
112cabdff1aSopenharmony_ci
113cabdff1aSopenharmony_ci    frame->nb_samples = c->frame_size;
114cabdff1aSopenharmony_ci    if ((err = ff_get_buffer(c, frame, 0)) < 0)
115cabdff1aSopenharmony_ci        return err;
116cabdff1aSopenharmony_ci    pcm = (int16_t *)frame->data[0];
117cabdff1aSopenharmony_ci    err = celt_decode(celt->dec, pkt->data, pkt->size, pcm, c->frame_size);
118cabdff1aSopenharmony_ci    if (err < 0)
119cabdff1aSopenharmony_ci        return ff_celt_error_to_averror(err);
120cabdff1aSopenharmony_ci    if (celt->discard) {
121cabdff1aSopenharmony_ci        frame->nb_samples -= celt->discard;
122cabdff1aSopenharmony_ci        memmove(pcm, pcm + celt->discard * c->ch_layout.nb_channels,
123cabdff1aSopenharmony_ci                frame->nb_samples * c->ch_layout.nb_channels * sizeof(int16_t));
124cabdff1aSopenharmony_ci        celt->discard = 0;
125cabdff1aSopenharmony_ci    }
126cabdff1aSopenharmony_ci    *got_frame_ptr = 1;
127cabdff1aSopenharmony_ci    return pkt->size;
128cabdff1aSopenharmony_ci}
129cabdff1aSopenharmony_ci
130cabdff1aSopenharmony_ciconst FFCodec ff_libcelt_decoder = {
131cabdff1aSopenharmony_ci    .p.name         = "libcelt",
132cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("Xiph CELT decoder using libcelt"),
133cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_AUDIO,
134cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_CELT,
135cabdff1aSopenharmony_ci    .p.capabilities = AV_CODEC_CAP_DR1,
136cabdff1aSopenharmony_ci    .p.wrapper_name = "libcelt",
137cabdff1aSopenharmony_ci    .priv_data_size = sizeof(struct libcelt_context),
138cabdff1aSopenharmony_ci    .init           = libcelt_dec_init,
139cabdff1aSopenharmony_ci    .close          = libcelt_dec_close,
140cabdff1aSopenharmony_ci    FF_CODEC_DECODE_CB(libcelt_dec_decode),
141cabdff1aSopenharmony_ci};
142