/* * Shorten decoder * Copyright (c) 2005 Jeff Muizelaar * * 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 */ /** * @file * Shorten decoder * @author Jeff Muizelaar * */ #define DEBUG #include #include "avcodec.h" #include "get_bits.h" #include "golomb.h" #define MAX_CHANNELS 8 #define MAX_BLOCKSIZE 65535 #define OUT_BUFFER_SIZE 16384 #define ULONGSIZE 2 #define WAVE_FORMAT_PCM 0x0001 #define DEFAULT_BLOCK_SIZE 256 #define TYPESIZE 4 #define CHANSIZE 0 #define LPCQSIZE 2 #define ENERGYSIZE 3 #define BITSHIFTSIZE 2 #define TYPE_S16HL 3 #define TYPE_S16LH 5 #define NWRAP 3 #define NSKIPSIZE 1 #define LPCQUANT 5 #define V2LPCQOFFSET (1 << LPCQUANT) #define FNSIZE 2 #define FN_DIFF0 0 #define FN_DIFF1 1 #define FN_DIFF2 2 #define FN_DIFF3 3 #define FN_QUIT 4 #define FN_BLOCKSIZE 5 #define FN_BITSHIFT 6 #define FN_QLPC 7 #define FN_ZERO 8 #define FN_VERBATIM 9 #define VERBATIM_CKSIZE_SIZE 5 #define VERBATIM_BYTE_SIZE 8 #define CANONICAL_HEADER_SIZE 44 typedef struct ShortenContext { AVCodecContext *avctx; GetBitContext gb; int min_framesize, max_framesize; int channels; int32_t *decoded[MAX_CHANNELS]; int32_t *offset[MAX_CHANNELS]; int *coeffs; uint8_t *bitstream; int bitstream_size; int bitstream_index; unsigned int allocated_bitstream_size; int header_size; uint8_t header[OUT_BUFFER_SIZE]; int version; int cur_chan; int bitshift; int nmean; int internal_ftype; int nwrap; int blocksize; int bitindex; int32_t lpcqoffset; } ShortenContext; static av_cold int shorten_decode_init(AVCodecContext * avctx) { ShortenContext *s = avctx->priv_data; s->avctx = avctx; avctx->sample_fmt = AV_SAMPLE_FMT_S16; return 0; } static int allocate_buffers(ShortenContext *s) { int i, chan; int *coeffs; for (chan=0; chanchannels; chan++) { if(FFMAX(1, s->nmean) >= UINT_MAX/sizeof(int32_t)){ av_log(s->avctx, AV_LOG_ERROR, "nmean too large\n"); return -1; } if(s->blocksize + s->nwrap >= UINT_MAX/sizeof(int32_t) || s->blocksize + s->nwrap <= (unsigned)s->nwrap){ av_log(s->avctx, AV_LOG_ERROR, "s->blocksize + s->nwrap too large\n"); return -1; } s->offset[chan] = av_realloc(s->offset[chan], sizeof(int32_t)*FFMAX(1, s->nmean)); s->decoded[chan] = av_realloc(s->decoded[chan], sizeof(int32_t)*(s->blocksize + s->nwrap)); for (i=0; inwrap; i++) s->decoded[chan][i] = 0; s->decoded[chan] += s->nwrap; } coeffs = av_realloc(s->coeffs, s->nwrap * sizeof(*s->coeffs)); if (!coeffs) return AVERROR(ENOMEM); s->coeffs = coeffs; return 0; } static inline unsigned int get_uint(ShortenContext *s, int k) { if (s->version != 0) k = get_ur_golomb_shorten(&s->gb, ULONGSIZE); return get_ur_golomb_shorten(&s->gb, k); } static void fix_bitshift(ShortenContext *s, int32_t *buffer) { int i; if (s->bitshift != 0) for (i = 0; i < s->blocksize; i++) buffer[s->nwrap + i] <<= s->bitshift; } static void init_offset(ShortenContext *s) { int32_t mean = 0; int chan, i; int nblock = FFMAX(1, s->nmean); /* initialise offset */ switch (s->internal_ftype) { case TYPE_S16HL: case TYPE_S16LH: mean = 0; break; default: av_log(s->avctx, AV_LOG_ERROR, "unknown audio type"); abort(); } for (chan = 0; chan < s->channels; chan++) for (i = 0; i < nblock; i++) s->offset[chan][i] = mean; } static inline int get_le32(GetBitContext *gb) { return av_bswap32(get_bits_long(gb, 32)); } static inline short get_le16(GetBitContext *gb) { return av_bswap16(get_bits_long(gb, 16)); } static int decode_wave_header(AVCodecContext *avctx, uint8_t *header, int header_size) { GetBitContext hb; int len; short wave_format; init_get_bits(&hb, header, header_size*8); if (get_le32(&hb) != MKTAG('R','I','F','F')) { av_log(avctx, AV_LOG_ERROR, "missing RIFF tag\n"); return -1; } skip_bits_long(&hb, 32); /* chunk_size */ if (get_le32(&hb) != MKTAG('W','A','V','E')) { av_log(avctx, AV_LOG_ERROR, "missing WAVE tag\n"); return -1; } while (get_le32(&hb) != MKTAG('f','m','t',' ')) { len = get_le32(&hb); skip_bits(&hb, 8*len); } len = get_le32(&hb); if (len < 16) { av_log(avctx, AV_LOG_ERROR, "fmt chunk was too short\n"); return -1; } wave_format = get_le16(&hb); switch (wave_format) { case WAVE_FORMAT_PCM: break; default: av_log(avctx, AV_LOG_ERROR, "unsupported wave format\n"); return -1; } avctx->channels = get_le16(&hb); avctx->sample_rate = get_le32(&hb); avctx->bit_rate = get_le32(&hb) * 8; avctx->block_align = get_le16(&hb); avctx->bits_per_coded_sample = get_le16(&hb); if (avctx->bits_per_coded_sample != 16) { av_log(avctx, AV_LOG_ERROR, "unsupported number of bits per sample\n"); return -1; } len -= 16; if (len > 0) av_log(avctx, AV_LOG_INFO, "%d header bytes unparsed\n", len); return 0; } static int16_t * interleave_buffer(int16_t *samples, int nchan, int blocksize, int32_t **buffer) { int i, chan; for (i=0; icoeffs; for (i=0; igb, LPCQUANT); for (i=0; i < s->blocksize; i++) { sum = s->lpcqoffset; for (j=0; jdecoded[channel][i-j-1]; s->decoded[channel][i] = get_sr_golomb_shorten(&s->gb, residual_size) + (sum >> LPCQUANT); } } static int shorten_decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt) { const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; ShortenContext *s = avctx->priv_data; int i, input_buf_size = 0; int16_t *samples = data; if(s->max_framesize == 0){ s->max_framesize= 1024; // should hopefully be enough for the first header s->bitstream= av_fast_realloc(s->bitstream, &s->allocated_bitstream_size, s->max_framesize); } if(1 && s->max_framesize){//FIXME truncated buf_size= FFMIN(buf_size, s->max_framesize - s->bitstream_size); input_buf_size= buf_size; if(s->bitstream_index + s->bitstream_size + buf_size > s->allocated_bitstream_size){ // printf("memmove\n"); memmove(s->bitstream, &s->bitstream[s->bitstream_index], s->bitstream_size); s->bitstream_index=0; } memcpy(&s->bitstream[s->bitstream_index + s->bitstream_size], buf, buf_size); buf= &s->bitstream[s->bitstream_index]; buf_size += s->bitstream_size; s->bitstream_size= buf_size; if(buf_size < s->max_framesize){ //av_dlog(avctx, "wanna more data ... %d\n", buf_size); *data_size = 0; return input_buf_size; } } init_get_bits(&s->gb, buf, buf_size*8); skip_bits(&s->gb, s->bitindex); if (!s->blocksize) { int maxnlpc = 0; /* shorten signature */ if (get_bits_long(&s->gb, 32) != AV_RB32("ajkg")) { av_log(s->avctx, AV_LOG_ERROR, "missing shorten magic 'ajkg'\n"); return -1; } s->lpcqoffset = 0; s->blocksize = DEFAULT_BLOCK_SIZE; s->channels = 1; s->nmean = -1; s->version = get_bits(&s->gb, 8); s->internal_ftype = get_uint(s, TYPESIZE); s->channels = get_uint(s, CHANSIZE); if (s->channels > MAX_CHANNELS) { av_log(s->avctx, AV_LOG_ERROR, "too many channels: %d\n", s->channels); return -1; } /* get blocksize if version > 0 */ if (s->version > 0) { int skip_bytes; s->blocksize = get_uint(s, av_log2(DEFAULT_BLOCK_SIZE)); maxnlpc = get_uint(s, LPCQSIZE); s->nmean = get_uint(s, 0); skip_bytes = get_uint(s, NSKIPSIZE); for (i=0; igb, 8); } } s->nwrap = FFMAX(NWRAP, maxnlpc); if (allocate_buffers(s)) return -1; init_offset(s); if (s->version > 1) s->lpcqoffset = V2LPCQOFFSET; if (get_ur_golomb_shorten(&s->gb, FNSIZE) != FN_VERBATIM) { av_log(s->avctx, AV_LOG_ERROR, "missing verbatim section at beginning of stream\n"); return -1; } s->header_size = get_ur_golomb_shorten(&s->gb, VERBATIM_CKSIZE_SIZE); if (s->header_size >= OUT_BUFFER_SIZE || s->header_size < CANONICAL_HEADER_SIZE) { av_log(s->avctx, AV_LOG_ERROR, "header is wrong size: %d\n", s->header_size); return -1; } for (i=0; iheader_size; i++) s->header[i] = (char)get_ur_golomb_shorten(&s->gb, VERBATIM_BYTE_SIZE); if (decode_wave_header(avctx, s->header, s->header_size) < 0) return -1; s->cur_chan = 0; s->bitshift = 0; } else { int cmd; int len; cmd = get_ur_golomb_shorten(&s->gb, FNSIZE); switch (cmd) { case FN_ZERO: case FN_DIFF0: case FN_DIFF1: case FN_DIFF2: case FN_DIFF3: case FN_QLPC: { int residual_size = 0; int channel = s->cur_chan; int32_t coffset; if (cmd != FN_ZERO) { residual_size = get_ur_golomb_shorten(&s->gb, ENERGYSIZE); /* this is a hack as version 0 differed in defintion of get_sr_golomb_shorten */ if (s->version == 0) residual_size--; } if (s->nmean == 0) coffset = s->offset[channel][0]; else { int32_t sum = (s->version < 2) ? 0 : s->nmean / 2; for (i=0; inmean; i++) sum += s->offset[channel][i]; coffset = sum / s->nmean; if (s->version >= 2) coffset >>= FFMIN(1, s->bitshift); } switch (cmd) { case FN_ZERO: for (i=0; iblocksize; i++) s->decoded[channel][i] = 0; break; case FN_DIFF0: for (i=0; iblocksize; i++) s->decoded[channel][i] = get_sr_golomb_shorten(&s->gb, residual_size) + coffset; break; case FN_DIFF1: for (i=0; iblocksize; i++) s->decoded[channel][i] = get_sr_golomb_shorten(&s->gb, residual_size) + s->decoded[channel][i - 1]; break; case FN_DIFF2: for (i=0; iblocksize; i++) s->decoded[channel][i] = get_sr_golomb_shorten(&s->gb, residual_size) + 2*s->decoded[channel][i-1] - s->decoded[channel][i-2]; break; case FN_DIFF3: for (i=0; iblocksize; i++) s->decoded[channel][i] = get_sr_golomb_shorten(&s->gb, residual_size) + 3*s->decoded[channel][i-1] - 3*s->decoded[channel][i-2] + s->decoded[channel][i-3]; break; case FN_QLPC: { int pred_order = get_ur_golomb_shorten(&s->gb, LPCQSIZE); if (pred_order > s->nwrap) { av_log(avctx, AV_LOG_ERROR, "invalid pred_order %d\n", pred_order); return -1; } for (i=0; idecoded[channel][i - pred_order] -= coffset; decode_subframe_lpc(s, channel, residual_size, pred_order); if (coffset != 0) for (i=0; i < s->blocksize; i++) s->decoded[channel][i] += coffset; } } if (s->nmean > 0) { int32_t sum = (s->version < 2) ? 0 : s->blocksize / 2; for (i=0; iblocksize; i++) sum += s->decoded[channel][i]; for (i=1; inmean; i++) s->offset[channel][i-1] = s->offset[channel][i]; if (s->version < 2) s->offset[channel][s->nmean - 1] = sum / s->blocksize; else s->offset[channel][s->nmean - 1] = (sum / s->blocksize) << s->bitshift; } for (i=-s->nwrap; i<0; i++) s->decoded[channel][i] = s->decoded[channel][i + s->blocksize]; fix_bitshift(s, s->decoded[channel]); s->cur_chan++; if (s->cur_chan == s->channels) { samples = interleave_buffer(samples, s->channels, s->blocksize, s->decoded); s->cur_chan = 0; goto frame_done; } break; } break; case FN_VERBATIM: len = get_ur_golomb_shorten(&s->gb, VERBATIM_CKSIZE_SIZE); while (len--) { get_ur_golomb_shorten(&s->gb, VERBATIM_BYTE_SIZE); } break; case FN_BITSHIFT: s->bitshift = get_ur_golomb_shorten(&s->gb, BITSHIFTSIZE); break; case FN_BLOCKSIZE: s->blocksize = get_uint(s, av_log2(s->blocksize)); break; case FN_QUIT: *data_size = 0; return buf_size; break; default: av_log(avctx, AV_LOG_ERROR, "unknown shorten function %d\n", cmd); return -1; break; } } frame_done: *data_size = (int8_t *)samples - (int8_t *)data; // s->last_blocksize = s->blocksize; s->bitindex = get_bits_count(&s->gb) - 8*((get_bits_count(&s->gb))/8); i= (get_bits_count(&s->gb))/8; if (i > buf_size) { av_log(s->avctx, AV_LOG_ERROR, "overread: %d\n", i - buf_size); s->bitstream_size=0; s->bitstream_index=0; return -1; } if (s->bitstream_size) { s->bitstream_index += i; s->bitstream_size -= i; return input_buf_size; } else return i; } static av_cold int shorten_decode_close(AVCodecContext *avctx) { ShortenContext *s = avctx->priv_data; int i; for (i = 0; i < s->channels; i++) { s->decoded[i] -= s->nwrap; av_freep(&s->decoded[i]); av_freep(&s->offset[i]); } av_freep(&s->bitstream); av_freep(&s->coeffs); return 0; } static void shorten_flush(AVCodecContext *avctx){ ShortenContext *s = avctx->priv_data; s->bitstream_size= s->bitstream_index= 0; } AVCodec ff_shorten_decoder = { "shorten", AVMEDIA_TYPE_AUDIO, CODEC_ID_SHORTEN, sizeof(ShortenContext), shorten_decode_init, NULL, shorten_decode_close, shorten_decode_frame, .flush= shorten_flush, .long_name= NULL_IF_CONFIG_SMALL("Shorten"), };