Commit | Line | Data |
---|---|---|
cb21e0a7 | 1 | /* |
4477dedc | 2 | * 8SVX audio decoder |
cb21e0a7 JM |
3 | * Copyright (C) 2008 Jaikrishnan Menon |
4 | * | |
2912e87a | 5 | * This file is part of Libav. |
cb21e0a7 | 6 | * |
2912e87a | 7 | * Libav is free software; you can redistribute it and/or |
cb21e0a7 JM |
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 | * | |
2912e87a | 12 | * Libav is distributed in the hope that it will be useful, |
cb21e0a7 JM |
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 | |
2912e87a | 18 | * License along with Libav; if not, write to the Free Software |
cb21e0a7 JM |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ | |
21 | ||
22 | /** | |
ba87f080 | 23 | * @file |
cb21e0a7 JM |
24 | * 8svx audio decoder |
25 | * @author Jaikrishnan Menon | |
ec679eb4 | 26 | * |
cb21e0a7 JM |
27 | * supports: fibonacci delta encoding |
28 | * : exponential encoding | |
29 | */ | |
30 | ||
31 | #include "avcodec.h" | |
32 | ||
1b41f260 | 33 | /** decoder context */ |
cb21e0a7 | 34 | typedef struct EightSvxContext { |
0eea2129 | 35 | AVFrame frame; |
1993c684 | 36 | uint8_t fib_acc[2]; |
e3718784 | 37 | const int8_t *table; |
1993c684 JR |
38 | |
39 | /* buffer used to store the whole first packet. | |
40 | data is only sent as one large packet */ | |
41 | uint8_t *data[2]; | |
42 | int data_size; | |
43 | int data_idx; | |
cb21e0a7 JM |
44 | } EightSvxContext; |
45 | ||
e3718784 JR |
46 | static const int8_t fibonacci[16] = { -34, -21, -13, -8, -5, -3, -2, -1, |
47 | 0, 1, 2, 3, 5, 8, 13, 21 }; | |
48 | static const int8_t exponential[16] = { -128, -64, -32, -16, -8, -4, -2, -1, | |
49 | 0, 1, 2, 4, 8, 16, 32, 64 }; | |
cb21e0a7 | 50 | |
1993c684 JR |
51 | #define MAX_FRAME_SIZE 32768 |
52 | ||
ac68607b JR |
53 | /** |
54 | * Delta decode the compressed values in src, and put the resulting | |
55 | * decoded samples in dst. | |
56 | * | |
57 | * @param[in,out] state starting value. it is saved for use in the next call. | |
58 | */ | |
e3718784 | 59 | static void delta_decode(uint8_t *dst, const uint8_t *src, int src_size, |
1993c684 | 60 | uint8_t *state, const int8_t *table, int channels) |
ac68607b | 61 | { |
e3718784 | 62 | uint8_t val = *state; |
ac68607b JR |
63 | |
64 | while (src_size--) { | |
65 | uint8_t d = *src++; | |
e3718784 | 66 | val = av_clip_uint8(val + table[d & 0xF]); |
1993c684 JR |
67 | *dst = val; |
68 | dst += channels; | |
e3718784 | 69 | val = av_clip_uint8(val + table[d >> 4]); |
1993c684 JR |
70 | *dst = val; |
71 | dst += channels; | |
ac68607b JR |
72 | } |
73 | ||
74 | *state = val; | |
75 | } | |
76 | ||
f1f6d361 JR |
77 | static void raw_decode(uint8_t *dst, const int8_t *src, int src_size, |
78 | int channels) | |
79 | { | |
80 | while (src_size--) { | |
81 | *dst = *src++ + 128; | |
82 | dst += channels; | |
83 | } | |
84 | } | |
85 | ||
1b41f260 | 86 | /** decode a frame */ |
0eea2129 JR |
87 | static int eightsvx_decode_frame(AVCodecContext *avctx, void *data, |
88 | int *got_frame_ptr, AVPacket *avpkt) | |
cb21e0a7 JM |
89 | { |
90 | EightSvxContext *esc = avctx->priv_data; | |
1993c684 | 91 | int buf_size; |
0eea2129 JR |
92 | uint8_t *out_data; |
93 | int ret; | |
f1f6d361 | 94 | int is_compr = (avctx->codec_id != CODEC_ID_PCM_S8_PLANAR); |
1993c684 JR |
95 | |
96 | /* for the first packet, copy data to buffer */ | |
97 | if (avpkt->data) { | |
f1f6d361 JR |
98 | int hdr_size = is_compr ? 2 : 0; |
99 | int chan_size = (avpkt->size - hdr_size * avctx->channels) / avctx->channels; | |
cb21e0a7 | 100 | |
f1f6d361 | 101 | if (avpkt->size < hdr_size * avctx->channels) { |
0ac3b8fc JR |
102 | av_log(avctx, AV_LOG_ERROR, "packet size is too small\n"); |
103 | return AVERROR(EINVAL); | |
104 | } | |
1993c684 JR |
105 | if (esc->data[0]) { |
106 | av_log(avctx, AV_LOG_ERROR, "unexpected data after first packet\n"); | |
107 | return AVERROR(EINVAL); | |
108 | } | |
109 | ||
f1f6d361 | 110 | if (is_compr) { |
1993c684 JR |
111 | esc->fib_acc[0] = avpkt->data[1] + 128; |
112 | if (avctx->channels == 2) | |
113 | esc->fib_acc[1] = avpkt->data[2+chan_size+1] + 128; | |
f1f6d361 | 114 | } |
1993c684 JR |
115 | |
116 | esc->data_idx = 0; | |
117 | esc->data_size = chan_size; | |
118 | if (!(esc->data[0] = av_malloc(chan_size))) | |
119 | return AVERROR(ENOMEM); | |
120 | if (avctx->channels == 2) { | |
121 | if (!(esc->data[1] = av_malloc(chan_size))) { | |
122 | av_freep(&esc->data[0]); | |
123 | return AVERROR(ENOMEM); | |
124 | } | |
125 | } | |
f1f6d361 | 126 | memcpy(esc->data[0], &avpkt->data[hdr_size], chan_size); |
1993c684 | 127 | if (avctx->channels == 2) |
f1f6d361 | 128 | memcpy(esc->data[1], &avpkt->data[2*hdr_size+chan_size], chan_size); |
1993c684 JR |
129 | } |
130 | if (!esc->data[0]) { | |
131 | av_log(avctx, AV_LOG_ERROR, "unexpected empty packet\n"); | |
132 | return AVERROR(EINVAL); | |
cb21e0a7 JM |
133 | } |
134 | ||
1993c684 JR |
135 | /* decode next piece of data from the buffer */ |
136 | buf_size = FFMIN(MAX_FRAME_SIZE, esc->data_size - esc->data_idx); | |
137 | if (buf_size <= 0) { | |
0eea2129 | 138 | *got_frame_ptr = 0; |
1993c684 JR |
139 | return avpkt->size; |
140 | } | |
0eea2129 JR |
141 | |
142 | /* get output buffer */ | |
143 | esc->frame.nb_samples = buf_size * (is_compr + 1); | |
144 | if ((ret = avctx->get_buffer(avctx, &esc->frame)) < 0) { | |
145 | av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); | |
146 | return ret; | |
fda459ce | 147 | } |
0eea2129 JR |
148 | out_data = esc->frame.data[0]; |
149 | ||
f1f6d361 | 150 | if (is_compr) { |
1993c684 JR |
151 | delta_decode(out_data, &esc->data[0][esc->data_idx], buf_size, |
152 | &esc->fib_acc[0], esc->table, avctx->channels); | |
153 | if (avctx->channels == 2) { | |
154 | delta_decode(&out_data[1], &esc->data[1][esc->data_idx], buf_size, | |
155 | &esc->fib_acc[1], esc->table, avctx->channels); | |
156 | } | |
f1f6d361 JR |
157 | } else { |
158 | int ch; | |
159 | for (ch = 0; ch < avctx->channels; ch++) { | |
160 | raw_decode((int8_t *)&out_data[ch], &esc->data[ch][esc->data_idx], | |
161 | buf_size, avctx->channels); | |
162 | } | |
163 | } | |
1993c684 | 164 | esc->data_idx += buf_size; |
0eea2129 JR |
165 | |
166 | *got_frame_ptr = 1; | |
167 | *(AVFrame *)data = esc->frame; | |
cb21e0a7 | 168 | |
1993c684 | 169 | return avpkt->size; |
cb21e0a7 JM |
170 | } |
171 | ||
1b41f260 | 172 | /** initialize 8svx decoder */ |
cb21e0a7 JM |
173 | static av_cold int eightsvx_decode_init(AVCodecContext *avctx) |
174 | { | |
175 | EightSvxContext *esc = avctx->priv_data; | |
176 | ||
1993c684 JR |
177 | if (avctx->channels < 1 || avctx->channels > 2) { |
178 | av_log(avctx, AV_LOG_ERROR, "8SVX does not support more than 2 channels\n"); | |
179 | return AVERROR(EINVAL); | |
180 | } | |
181 | ||
cb21e0a7 JM |
182 | switch(avctx->codec->id) { |
183 | case CODEC_ID_8SVX_FIB: | |
184 | esc->table = fibonacci; | |
185 | break; | |
186 | case CODEC_ID_8SVX_EXP: | |
187 | esc->table = exponential; | |
188 | break; | |
f1f6d361 JR |
189 | case CODEC_ID_PCM_S8_PLANAR: |
190 | break; | |
cb21e0a7 JM |
191 | default: |
192 | return -1; | |
193 | } | |
e3718784 | 194 | avctx->sample_fmt = AV_SAMPLE_FMT_U8; |
0eea2129 JR |
195 | |
196 | avcodec_get_frame_defaults(&esc->frame); | |
197 | avctx->coded_frame = &esc->frame; | |
198 | ||
cb21e0a7 JM |
199 | return 0; |
200 | } | |
201 | ||
1993c684 JR |
202 | static av_cold int eightsvx_decode_close(AVCodecContext *avctx) |
203 | { | |
204 | EightSvxContext *esc = avctx->priv_data; | |
205 | ||
206 | av_freep(&esc->data[0]); | |
207 | av_freep(&esc->data[1]); | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
d36beb3f | 212 | AVCodec ff_eightsvx_fib_decoder = { |
99e3913d | 213 | .name = "8svx_fib", |
72415b2a | 214 | .type = AVMEDIA_TYPE_AUDIO, |
cb21e0a7 JM |
215 | .id = CODEC_ID_8SVX_FIB, |
216 | .priv_data_size = sizeof (EightSvxContext), | |
217 | .init = eightsvx_decode_init, | |
1993c684 | 218 | .close = eightsvx_decode_close, |
cb21e0a7 | 219 | .decode = eightsvx_decode_frame, |
0eea2129 | 220 | .capabilities = CODEC_CAP_DELAY | CODEC_CAP_DR1, |
fe4bf374 | 221 | .long_name = NULL_IF_CONFIG_SMALL("8SVX fibonacci"), |
cb21e0a7 JM |
222 | }; |
223 | ||
d36beb3f | 224 | AVCodec ff_eightsvx_exp_decoder = { |
99e3913d | 225 | .name = "8svx_exp", |
72415b2a | 226 | .type = AVMEDIA_TYPE_AUDIO, |
cb21e0a7 JM |
227 | .id = CODEC_ID_8SVX_EXP, |
228 | .priv_data_size = sizeof (EightSvxContext), | |
229 | .init = eightsvx_decode_init, | |
1993c684 | 230 | .close = eightsvx_decode_close, |
cb21e0a7 | 231 | .decode = eightsvx_decode_frame, |
0eea2129 | 232 | .capabilities = CODEC_CAP_DELAY | CODEC_CAP_DR1, |
fe4bf374 | 233 | .long_name = NULL_IF_CONFIG_SMALL("8SVX exponential"), |
cb21e0a7 | 234 | }; |
f1f6d361 JR |
235 | |
236 | AVCodec ff_pcm_s8_planar_decoder = { | |
237 | .name = "pcm_s8_planar", | |
238 | .type = AVMEDIA_TYPE_AUDIO, | |
239 | .id = CODEC_ID_PCM_S8_PLANAR, | |
240 | .priv_data_size = sizeof(EightSvxContext), | |
241 | .init = eightsvx_decode_init, | |
242 | .close = eightsvx_decode_close, | |
243 | .decode = eightsvx_decode_frame, | |
0eea2129 | 244 | .capabilities = CODEC_CAP_DELAY | CODEC_CAP_DR1, |
f1f6d361 JR |
245 | .long_name = NULL_IF_CONFIG_SMALL("PCM signed 8-bit planar"), |
246 | }; |