1/* 2 * Westwood SNDx codecs 3 * Copyright (c) 2005 Konstantin Shishkov 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include <stdint.h> 23 24#include "libavutil/channel_layout.h" 25#include "libavutil/common.h" 26#include "libavutil/intreadwrite.h" 27#include "avcodec.h" 28#include "codec_internal.h" 29#include "internal.h" 30 31/** 32 * @file 33 * Westwood SNDx codecs 34 * 35 * Reference documents about VQA format and its audio codecs 36 * can be found here: 37 * http://www.multimedia.cx 38 */ 39 40static const int8_t ws_adpcm_4bit[] = { 41 -9, -8, -6, -5, -4, -3, -2, -1, 42 0, 1, 2, 3, 4, 5, 6, 8 43}; 44 45static av_cold int ws_snd_decode_init(AVCodecContext *avctx) 46{ 47 av_channel_layout_uninit(&avctx->ch_layout); 48 avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; 49 avctx->sample_fmt = AV_SAMPLE_FMT_U8; 50 51 return 0; 52} 53 54static int ws_snd_decode_frame(AVCodecContext *avctx, AVFrame *frame, 55 int *got_frame_ptr, AVPacket *avpkt) 56{ 57 const uint8_t *buf = avpkt->data; 58 int buf_size = avpkt->size; 59 60 int in_size, out_size, ret; 61 int sample = 128; 62 uint8_t *samples; 63 uint8_t *samples_end; 64 65 if (!buf_size) 66 return 0; 67 68 if (buf_size < 4) { 69 av_log(avctx, AV_LOG_ERROR, "packet is too small\n"); 70 return AVERROR(EINVAL); 71 } 72 73 out_size = AV_RL16(&buf[0]); 74 in_size = AV_RL16(&buf[2]); 75 buf += 4; 76 77 if (in_size > buf_size) { 78 av_log(avctx, AV_LOG_ERROR, "Frame data is larger than input buffer\n"); 79 return AVERROR_INVALIDDATA; 80 } 81 82 /* get output buffer */ 83 frame->nb_samples = out_size; 84 if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) 85 return ret; 86 samples = frame->data[0]; 87 samples_end = samples + out_size; 88 89 if (in_size == out_size) { 90 memcpy(samples, buf, out_size); 91 *got_frame_ptr = 1; 92 return buf_size; 93 } 94 95 while (samples < samples_end && buf - avpkt->data < buf_size) { 96 int code, smp, size; 97 uint8_t count; 98 code = *buf >> 6; 99 count = *buf & 0x3F; 100 buf++; 101 102 /* make sure we don't write past the output buffer */ 103 switch (code) { 104 case 0: smp = 4 * (count + 1); break; 105 case 1: smp = 2 * (count + 1); break; 106 case 2: smp = (count & 0x20) ? 1 : count + 1; break; 107 default: smp = count + 1; break; 108 } 109 if (samples_end - samples < smp) 110 break; 111 112 /* make sure we don't read past the input buffer */ 113 size = ((code == 2 && (count & 0x20)) || code == 3) ? 0 : count + 1; 114 if ((buf - avpkt->data) + size > buf_size) 115 break; 116 117 switch (code) { 118 case 0: /* ADPCM 2-bit */ 119 for (count++; count > 0; count--) { 120 code = *buf++; 121 sample += ( code & 0x3) - 2; 122 sample = av_clip_uint8(sample); 123 *samples++ = sample; 124 sample += ((code >> 2) & 0x3) - 2; 125 sample = av_clip_uint8(sample); 126 *samples++ = sample; 127 sample += ((code >> 4) & 0x3) - 2; 128 sample = av_clip_uint8(sample); 129 *samples++ = sample; 130 sample += (code >> 6) - 2; 131 sample = av_clip_uint8(sample); 132 *samples++ = sample; 133 } 134 break; 135 case 1: /* ADPCM 4-bit */ 136 for (count++; count > 0; count--) { 137 code = *buf++; 138 sample += ws_adpcm_4bit[code & 0xF]; 139 sample = av_clip_uint8(sample); 140 *samples++ = sample; 141 sample += ws_adpcm_4bit[code >> 4]; 142 sample = av_clip_uint8(sample); 143 *samples++ = sample; 144 } 145 break; 146 case 2: /* no compression */ 147 if (count & 0x20) { /* big delta */ 148 int8_t t; 149 t = count; 150 t <<= 3; 151 sample += t >> 3; 152 sample = av_clip_uint8(sample); 153 *samples++ = sample; 154 } else { /* copy */ 155 memcpy(samples, buf, smp); 156 samples += smp; 157 buf += smp; 158 sample = buf[-1]; 159 } 160 break; 161 default: /* run */ 162 memset(samples, sample, smp); 163 samples += smp; 164 } 165 } 166 167 frame->nb_samples = samples - frame->data[0]; 168 *got_frame_ptr = 1; 169 170 return buf_size; 171} 172 173const FFCodec ff_ws_snd1_decoder = { 174 .p.name = "ws_snd1", 175 .p.long_name = NULL_IF_CONFIG_SMALL("Westwood Audio (SND1)"), 176 .p.type = AVMEDIA_TYPE_AUDIO, 177 .p.id = AV_CODEC_ID_WESTWOOD_SND1, 178 .init = ws_snd_decode_init, 179 FF_CODEC_DECODE_CB(ws_snd_decode_frame), 180 .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, 181 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 182}; 183