yading@10: /* yading@10: * Westwood SNDx codecs yading@10: * Copyright (c) 2005 Konstantin Shishkov 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: yading@10: #include "libavutil/channel_layout.h" yading@10: #include "libavutil/common.h" yading@10: #include "libavutil/intreadwrite.h" yading@10: #include "avcodec.h" yading@10: #include "internal.h" yading@10: yading@10: /** yading@10: * @file yading@10: * Westwood SNDx codecs yading@10: * yading@10: * Reference documents about VQA format and its audio codecs yading@10: * can be found here: yading@10: * http://www.multimedia.cx yading@10: */ yading@10: yading@10: static const int8_t ws_adpcm_4bit[] = { yading@10: -9, -8, -6, -5, -4, -3, -2, -1, yading@10: 0, 1, 2, 3, 4, 5, 6, 8 yading@10: }; yading@10: yading@10: static av_cold int ws_snd_decode_init(AVCodecContext *avctx) yading@10: { yading@10: avctx->channels = 1; yading@10: avctx->channel_layout = AV_CH_LAYOUT_MONO; yading@10: avctx->sample_fmt = AV_SAMPLE_FMT_U8; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int ws_snd_decode_frame(AVCodecContext *avctx, void *data, yading@10: int *got_frame_ptr, AVPacket *avpkt) yading@10: { yading@10: AVFrame *frame = data; yading@10: const uint8_t *buf = avpkt->data; yading@10: int buf_size = avpkt->size; yading@10: yading@10: int in_size, out_size, ret; yading@10: int sample = 128; yading@10: uint8_t *samples; yading@10: uint8_t *samples_end; yading@10: yading@10: if (!buf_size) yading@10: return 0; yading@10: yading@10: if (buf_size < 4) { yading@10: av_log(avctx, AV_LOG_ERROR, "packet is too small\n"); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: yading@10: out_size = AV_RL16(&buf[0]); yading@10: in_size = AV_RL16(&buf[2]); yading@10: buf += 4; yading@10: yading@10: if (in_size > buf_size) { yading@10: av_log(avctx, AV_LOG_ERROR, "Frame data is larger than input buffer\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: /* get output buffer */ yading@10: frame->nb_samples = out_size; yading@10: if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) yading@10: return ret; yading@10: samples = frame->data[0]; yading@10: samples_end = samples + out_size; yading@10: yading@10: if (in_size == out_size) { yading@10: memcpy(samples, buf, out_size); yading@10: *got_frame_ptr = 1; yading@10: return buf_size; yading@10: } yading@10: yading@10: while (samples < samples_end && buf - avpkt->data < buf_size) { yading@10: int code, smp, size; yading@10: uint8_t count; yading@10: code = *buf >> 6; yading@10: count = *buf & 0x3F; yading@10: buf++; yading@10: yading@10: /* make sure we don't write past the output buffer */ yading@10: switch (code) { yading@10: case 0: smp = 4 * (count + 1); break; yading@10: case 1: smp = 2 * (count + 1); break; yading@10: case 2: smp = (count & 0x20) ? 1 : count + 1; break; yading@10: default: smp = count + 1; break; yading@10: } yading@10: if (samples_end - samples < smp) yading@10: break; yading@10: yading@10: /* make sure we don't read past the input buffer */ yading@10: size = ((code == 2 && (count & 0x20)) || code == 3) ? 0 : count + 1; yading@10: if ((buf - avpkt->data) + size > buf_size) yading@10: break; yading@10: yading@10: switch (code) { yading@10: case 0: /* ADPCM 2-bit */ yading@10: for (count++; count > 0; count--) { yading@10: code = *buf++; yading@10: sample += ( code & 0x3) - 2; yading@10: sample = av_clip_uint8(sample); yading@10: *samples++ = sample; yading@10: sample += ((code >> 2) & 0x3) - 2; yading@10: sample = av_clip_uint8(sample); yading@10: *samples++ = sample; yading@10: sample += ((code >> 4) & 0x3) - 2; yading@10: sample = av_clip_uint8(sample); yading@10: *samples++ = sample; yading@10: sample += (code >> 6) - 2; yading@10: sample = av_clip_uint8(sample); yading@10: *samples++ = sample; yading@10: } yading@10: break; yading@10: case 1: /* ADPCM 4-bit */ yading@10: for (count++; count > 0; count--) { yading@10: code = *buf++; yading@10: sample += ws_adpcm_4bit[code & 0xF]; yading@10: sample = av_clip_uint8(sample); yading@10: *samples++ = sample; yading@10: sample += ws_adpcm_4bit[code >> 4]; yading@10: sample = av_clip_uint8(sample); yading@10: *samples++ = sample; yading@10: } yading@10: break; yading@10: case 2: /* no compression */ yading@10: if (count & 0x20) { /* big delta */ yading@10: int8_t t; yading@10: t = count; yading@10: t <<= 3; yading@10: sample += t >> 3; yading@10: sample = av_clip_uint8(sample); yading@10: *samples++ = sample; yading@10: } else { /* copy */ yading@10: memcpy(samples, buf, smp); yading@10: samples += smp; yading@10: buf += smp; yading@10: sample = buf[-1]; yading@10: } yading@10: break; yading@10: default: /* run */ yading@10: memset(samples, sample, smp); yading@10: samples += smp; yading@10: } yading@10: } yading@10: yading@10: frame->nb_samples = samples - frame->data[0]; yading@10: *got_frame_ptr = 1; yading@10: yading@10: return buf_size; yading@10: } yading@10: yading@10: AVCodec ff_ws_snd1_decoder = { yading@10: .name = "ws_snd1", yading@10: .type = AVMEDIA_TYPE_AUDIO, yading@10: .id = AV_CODEC_ID_WESTWOOD_SND1, yading@10: .init = ws_snd_decode_init, yading@10: .decode = ws_snd_decode_frame, yading@10: .capabilities = CODEC_CAP_DR1, yading@10: .long_name = NULL_IF_CONFIG_SMALL("Westwood Audio (SND1)"), yading@10: };