libavcodec/libcelt_dec.c
89451dd6
 /*
cf88cf17
  * Xiph CELT decoder using libcelt
89451dd6
  * Copyright (c) 2011 Nicolas George
  *
  * This file is part of FFmpeg.
  *
  * FFmpeg 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.
  *
  * FFmpeg 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 FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 #include <celt/celt.h>
 #include <celt/celt_header.h>
 #include "avcodec.h"
4c554c96
 #include "internal.h"
89451dd6
 #include "libavutil/intreadwrite.h"
 
 struct libcelt_context {
     CELTMode *mode;
     CELTDecoder *dec;
     int discard;
 };
 
 static int ff_celt_error_to_averror(int err)
 {
02ac6a65
     switch (err) {
89451dd6
         case CELT_BAD_ARG:          return AVERROR(EINVAL);
 #ifdef CELT_BUFFER_TOO_SMALL
         case CELT_BUFFER_TOO_SMALL: return AVERROR(ENOBUFS);
 #endif
         case CELT_INTERNAL_ERROR:   return AVERROR(EFAULT);
         case CELT_CORRUPTED_DATA:   return AVERROR_INVALIDDATA;
b0aaf304
         case CELT_UNIMPLEMENTED:    return AVERROR(ENOSYS);
e499187f
 #ifdef ENOTRECOVERABLE
89451dd6
         case CELT_INVALID_STATE:    return AVERROR(ENOTRECOVERABLE);
e499187f
 #endif
89451dd6
         case CELT_ALLOC_FAIL:       return AVERROR(ENOMEM);
4b5c2175
         default:                    return AVERROR(EINVAL);
89451dd6
     }
 }
 
 static int ff_celt_bitstream_version_hack(CELTMode *mode)
 {
     CELTHeader header = { .version_id = 0 };
     celt_header_init(&header, mode, 960, 2);
     return header.version_id;
 }
 
 static av_cold int libcelt_dec_init(AVCodecContext *c)
 {
     struct libcelt_context *celt = c->priv_data;
     int err;
 
     if (!c->channels || !c->frame_size ||
         c->frame_size > INT_MAX / sizeof(int16_t) / c->channels)
         return AVERROR(EINVAL);
     celt->mode = celt_mode_create(c->sample_rate, c->frame_size, &err);
     if (!celt->mode)
         return ff_celt_error_to_averror(err);
     celt->dec = celt_decoder_create_custom(celt->mode, c->channels, &err);
     if (!celt->dec) {
         celt_mode_destroy(celt->mode);
         return ff_celt_error_to_averror(err);
     }
     if (c->extradata_size >= 4) {
         celt->discard = AV_RL32(c->extradata);
         if (celt->discard < 0 || celt->discard >= c->frame_size) {
             av_log(c, AV_LOG_WARNING,
                    "Invalid overlap (%d), ignored.\n", celt->discard);
             celt->discard = 0;
         }
     }
02ac6a65
     if (c->extradata_size >= 8) {
89451dd6
         unsigned version = AV_RL32(c->extradata + 4);
         unsigned lib_version = ff_celt_bitstream_version_hack(celt->mode);
         if (version != lib_version)
             av_log(c, AV_LOG_WARNING,
                    "CELT bitstream version 0x%x may be "
                    "improperly decoded by libcelt for version 0x%x.\n",
                    version, lib_version);
     }
f44c8165
     c->sample_fmt = AV_SAMPLE_FMT_S16;
89451dd6
     return 0;
 }
 
 static av_cold int libcelt_dec_close(AVCodecContext *c)
 {
     struct libcelt_context *celt = c->priv_data;
 
     celt_decoder_destroy(celt->dec);
     celt_mode_destroy(celt->mode);
     return 0;
 }
 
a17ececc
 static int libcelt_dec_decode(AVCodecContext *c, void *data,
90bfd511
                               int *got_frame_ptr, AVPacket *pkt)
89451dd6
 {
     struct libcelt_context *celt = c->priv_data;
a17ececc
     AVFrame *frame = data;
89451dd6
     int err;
90bfd511
     int16_t *pcm;
89451dd6
 
a17ececc
     frame->nb_samples = c->frame_size;
1ec94b0f
     if ((err = ff_get_buffer(c, frame, 0)) < 0)
90bfd511
         return err;
a17ececc
     pcm = (int16_t *)frame->data[0];
89451dd6
     err = celt_decode(celt->dec, pkt->data, pkt->size, pcm, c->frame_size);
     if (err < 0)
         return ff_celt_error_to_averror(err);
     if (celt->discard) {
a17ececc
         frame->nb_samples -= celt->discard;
90bfd511
         memmove(pcm, pcm + celt->discard * c->channels,
a17ececc
                 frame->nb_samples * c->channels * sizeof(int16_t));
89451dd6
         celt->discard = 0;
     }
90bfd511
     *got_frame_ptr = 1;
89451dd6
     return pkt->size;
 }
 
 AVCodec ff_libcelt_decoder = {
     .name           = "libcelt",
b46f1910
     .long_name      = NULL_IF_CONFIG_SMALL("Xiph CELT decoder using libcelt"),
89451dd6
     .type           = AVMEDIA_TYPE_AUDIO,
7a72695c
     .id             = AV_CODEC_ID_CELT,
89451dd6
     .priv_data_size = sizeof(struct libcelt_context),
     .init           = libcelt_dec_init,
     .close          = libcelt_dec_close,
     .decode         = libcelt_dec_decode,
90bfd511
     .capabilities   = CODEC_CAP_DR1,
89451dd6
 };