| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2011-10-25 10:14:34 +02:00
										 |  |  |  * Xiph CELT decoder using libcelt | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |  * 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"
 | 
					
						
							|  |  |  | #include "libavutil/intreadwrite.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct libcelt_context { | 
					
						
							|  |  |  |     CELTMode *mode; | 
					
						
							|  |  |  |     CELTDecoder *dec; | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  |     AVFrame frame; | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |     int discard; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ff_celt_error_to_averror(int err) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-10-25 09:38:08 +02:00
										 |  |  |     switch (err) { | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |         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; | 
					
						
							|  |  |  |         case CELT_UNIMPLEMENTED:    return AVERROR(ENOTSUP); | 
					
						
							| 
									
										
										
										
											2011-04-23 10:09:11 +02:00
										 |  |  | #ifdef ENOTRECOVERABLE
 | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |         case CELT_INVALID_STATE:    return AVERROR(ENOTRECOVERABLE); | 
					
						
							| 
									
										
										
										
											2011-04-23 10:09:11 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |         case CELT_ALLOC_FAIL:       return AVERROR(ENOMEM); | 
					
						
							| 
									
										
										
										
											2011-04-23 10:06:39 +02:00
										 |  |  |         default:                    return AVERROR(EINVAL); | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-10-25 09:38:08 +02:00
										 |  |  |     if (c->extradata_size >= 8) { | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |         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); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-10-24 19:55:49 +02:00
										 |  |  |     c->sample_fmt = AV_SAMPLE_FMT_S16; | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  |     avcodec_get_frame_defaults(&celt->frame); | 
					
						
							|  |  |  |     c->coded_frame = &celt->frame; | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |     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; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  | static int libcelt_dec_decode(AVCodecContext *c, void *frame, | 
					
						
							|  |  |  |                               int *got_frame_ptr, AVPacket *pkt) | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  | { | 
					
						
							|  |  |  |     struct libcelt_context *celt = c->priv_data; | 
					
						
							|  |  |  |     int err; | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  |     int16_t *pcm; | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  |     celt->frame.nb_samples = c->frame_size; | 
					
						
							|  |  |  |     err = c->get_buffer(c, &celt->frame); | 
					
						
							|  |  |  |     if (err < 0) { | 
					
						
							|  |  |  |         av_log(c, AV_LOG_ERROR, "get_buffer() failed\n"); | 
					
						
							|  |  |  |         return err; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pcm = (int16_t *)celt->frame.data[0]; | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |     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) { | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  |         celt->frame.nb_samples -= celt->discard; | 
					
						
							|  |  |  |         memmove(pcm, pcm + celt->discard * c->channels, | 
					
						
							|  |  |  |                 celt->frame.nb_samples * c->channels * sizeof(int16_t)); | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |         celt->discard = 0; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  |     *got_frame_ptr = 1; | 
					
						
							|  |  |  |     *(AVFrame *)frame = celt->frame; | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  |     return pkt->size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AVCodec ff_libcelt_decoder = { | 
					
						
							|  |  |  |     .name           = "libcelt", | 
					
						
							|  |  |  |     .type           = AVMEDIA_TYPE_AUDIO, | 
					
						
							|  |  |  |     .id             = CODEC_ID_CELT, | 
					
						
							|  |  |  |     .priv_data_size = sizeof(struct libcelt_context), | 
					
						
							|  |  |  |     .init           = libcelt_dec_init, | 
					
						
							|  |  |  |     .close          = libcelt_dec_close, | 
					
						
							|  |  |  |     .decode         = libcelt_dec_decode, | 
					
						
							| 
									
										
										
										
											2011-12-03 11:52:48 +01:00
										 |  |  |     .capabilities   = CODEC_CAP_DR1, | 
					
						
							| 
									
										
										
										
											2011-10-25 10:14:34 +02:00
										 |  |  |     .long_name      = NULL_IF_CONFIG_SMALL("Xiph CELT decoder using libcelt"), | 
					
						
							| 
									
										
										
										
											2011-04-16 17:45:30 +02:00
										 |  |  | }; |