yading@10: /* yading@10: * Xiph CELT decoder using libcelt yading@10: * Copyright (c) 2011 Nicolas George yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include yading@10: #include yading@10: #include "avcodec.h" yading@10: #include "internal.h" yading@10: #include "libavutil/intreadwrite.h" yading@10: yading@10: struct libcelt_context { yading@10: CELTMode *mode; yading@10: CELTDecoder *dec; yading@10: int discard; yading@10: }; yading@10: yading@10: static int ff_celt_error_to_averror(int err) yading@10: { yading@10: switch (err) { yading@10: case CELT_BAD_ARG: return AVERROR(EINVAL); yading@10: #ifdef CELT_BUFFER_TOO_SMALL yading@10: case CELT_BUFFER_TOO_SMALL: return AVERROR(ENOBUFS); yading@10: #endif yading@10: case CELT_INTERNAL_ERROR: return AVERROR(EFAULT); yading@10: case CELT_CORRUPTED_DATA: return AVERROR_INVALIDDATA; yading@10: case CELT_UNIMPLEMENTED: return AVERROR(ENOSYS); yading@10: #ifdef ENOTRECOVERABLE yading@10: case CELT_INVALID_STATE: return AVERROR(ENOTRECOVERABLE); yading@10: #endif yading@10: case CELT_ALLOC_FAIL: return AVERROR(ENOMEM); yading@10: default: return AVERROR(EINVAL); yading@10: } yading@10: } yading@10: yading@10: static int ff_celt_bitstream_version_hack(CELTMode *mode) yading@10: { yading@10: CELTHeader header = { .version_id = 0 }; yading@10: celt_header_init(&header, mode, 960, 2); yading@10: return header.version_id; yading@10: } yading@10: yading@10: static av_cold int libcelt_dec_init(AVCodecContext *c) yading@10: { yading@10: struct libcelt_context *celt = c->priv_data; yading@10: int err; yading@10: yading@10: if (!c->channels || !c->frame_size || yading@10: c->frame_size > INT_MAX / sizeof(int16_t) / c->channels) yading@10: return AVERROR(EINVAL); yading@10: celt->mode = celt_mode_create(c->sample_rate, c->frame_size, &err); yading@10: if (!celt->mode) yading@10: return ff_celt_error_to_averror(err); yading@10: celt->dec = celt_decoder_create_custom(celt->mode, c->channels, &err); yading@10: if (!celt->dec) { yading@10: celt_mode_destroy(celt->mode); yading@10: return ff_celt_error_to_averror(err); yading@10: } yading@10: if (c->extradata_size >= 4) { yading@10: celt->discard = AV_RL32(c->extradata); yading@10: if (celt->discard < 0 || celt->discard >= c->frame_size) { yading@10: av_log(c, AV_LOG_WARNING, yading@10: "Invalid overlap (%d), ignored.\n", celt->discard); yading@10: celt->discard = 0; yading@10: } yading@10: } yading@10: if (c->extradata_size >= 8) { yading@10: unsigned version = AV_RL32(c->extradata + 4); yading@10: unsigned lib_version = ff_celt_bitstream_version_hack(celt->mode); yading@10: if (version != lib_version) yading@10: av_log(c, AV_LOG_WARNING, yading@10: "CELT bitstream version 0x%x may be " yading@10: "improperly decoded by libcelt for version 0x%x.\n", yading@10: version, lib_version); yading@10: } yading@10: c->sample_fmt = AV_SAMPLE_FMT_S16; yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold int libcelt_dec_close(AVCodecContext *c) yading@10: { yading@10: struct libcelt_context *celt = c->priv_data; yading@10: yading@10: celt_decoder_destroy(celt->dec); yading@10: celt_mode_destroy(celt->mode); yading@10: return 0; yading@10: } yading@10: yading@10: static int libcelt_dec_decode(AVCodecContext *c, void *data, yading@10: int *got_frame_ptr, AVPacket *pkt) yading@10: { yading@10: struct libcelt_context *celt = c->priv_data; yading@10: AVFrame *frame = data; yading@10: int err; yading@10: int16_t *pcm; yading@10: yading@10: frame->nb_samples = c->frame_size; yading@10: if ((err = ff_get_buffer(c, frame, 0)) < 0) yading@10: return err; yading@10: pcm = (int16_t *)frame->data[0]; yading@10: err = celt_decode(celt->dec, pkt->data, pkt->size, pcm, c->frame_size); yading@10: if (err < 0) yading@10: return ff_celt_error_to_averror(err); yading@10: if (celt->discard) { yading@10: frame->nb_samples -= celt->discard; yading@10: memmove(pcm, pcm + celt->discard * c->channels, yading@10: frame->nb_samples * c->channels * sizeof(int16_t)); yading@10: celt->discard = 0; yading@10: } yading@10: *got_frame_ptr = 1; yading@10: return pkt->size; yading@10: } yading@10: yading@10: AVCodec ff_libcelt_decoder = { yading@10: .name = "libcelt", yading@10: .type = AVMEDIA_TYPE_AUDIO, yading@10: .id = AV_CODEC_ID_CELT, yading@10: .priv_data_size = sizeof(struct libcelt_context), yading@10: .init = libcelt_dec_init, yading@10: .close = libcelt_dec_close, yading@10: .decode = libcelt_dec_decode, yading@10: .capabilities = CODEC_CAP_DR1, yading@10: .long_name = NULL_IF_CONFIG_SMALL("Xiph CELT decoder using libcelt"), yading@10: };