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 { |
1993c684 | 35 | uint8_t fib_acc[2]; |
e3718784 | 36 | const int8_t *table; |
1993c684 JR |
37 | |
38 | /* buffer used to store the whole first packet. | |
39 | data is only sent as one large packet */ | |
40 | uint8_t *data[2]; | |
41 | int data_size; | |
42 | int data_idx; | |
cb21e0a7 JM |
43 | } EightSvxContext; |
44 | ||
e3718784 JR |
45 | static const int8_t fibonacci[16] = { -34, -21, -13, -8, -5, -3, -2, -1, |
46 | 0, 1, 2, 3, 5, 8, 13, 21 }; | |
47 | static const int8_t exponential[16] = { -128, -64, -32, -16, -8, -4, -2, -1, | |
48 | 0, 1, 2, 4, 8, 16, 32, 64 }; | |
cb21e0a7 | 49 | |
1993c684 JR |
50 | #define MAX_FRAME_SIZE 32768 |
51 | ||
ac68607b JR |
52 | /** |
53 | * Delta decode the compressed values in src, and put the resulting | |
54 | * decoded samples in dst. | |
55 | * | |
56 | * @param[in,out] state starting value. it is saved for use in the next call. | |
57 | */ | |
e3718784 | 58 | static void delta_decode(uint8_t *dst, const uint8_t *src, int src_size, |
1993c684 | 59 | uint8_t *state, const int8_t *table, int channels) |
ac68607b | 60 | { |
e3718784 | 61 | uint8_t val = *state; |
ac68607b JR |
62 | |
63 | while (src_size--) { | |
64 | uint8_t d = *src++; | |
e3718784 | 65 | val = av_clip_uint8(val + table[d & 0xF]); |
1993c684 JR |
66 | *dst = val; |
67 | dst += channels; | |
e3718784 | 68 | val = av_clip_uint8(val + table[d >> 4]); |
1993c684 JR |
69 | *dst = val; |
70 | dst += channels; | |
ac68607b JR |
71 | } |
72 | ||
73 | *state = val; | |
74 | } | |
75 | ||
1b41f260 | 76 | /** decode a frame */ |
cb21e0a7 | 77 | static int eightsvx_decode_frame(AVCodecContext *avctx, void *data, int *data_size, |
7a00bbad | 78 | AVPacket *avpkt) |
cb21e0a7 JM |
79 | { |
80 | EightSvxContext *esc = avctx->priv_data; | |
1993c684 | 81 | int buf_size; |
e3718784 | 82 | uint8_t *out_data = data; |
1993c684 JR |
83 | int out_data_size; |
84 | ||
85 | /* for the first packet, copy data to buffer */ | |
86 | if (avpkt->data) { | |
87 | int chan_size = (avpkt->size / avctx->channels) - 2; | |
cb21e0a7 | 88 | |
1993c684 | 89 | if (avpkt->size < 2) { |
0ac3b8fc JR |
90 | av_log(avctx, AV_LOG_ERROR, "packet size is too small\n"); |
91 | return AVERROR(EINVAL); | |
92 | } | |
1993c684 JR |
93 | if (esc->data[0]) { |
94 | av_log(avctx, AV_LOG_ERROR, "unexpected data after first packet\n"); | |
95 | return AVERROR(EINVAL); | |
96 | } | |
97 | ||
98 | esc->fib_acc[0] = avpkt->data[1] + 128; | |
99 | if (avctx->channels == 2) | |
100 | esc->fib_acc[1] = avpkt->data[2+chan_size+1] + 128; | |
101 | ||
102 | esc->data_idx = 0; | |
103 | esc->data_size = chan_size; | |
104 | if (!(esc->data[0] = av_malloc(chan_size))) | |
105 | return AVERROR(ENOMEM); | |
106 | if (avctx->channels == 2) { | |
107 | if (!(esc->data[1] = av_malloc(chan_size))) { | |
108 | av_freep(&esc->data[0]); | |
109 | return AVERROR(ENOMEM); | |
110 | } | |
111 | } | |
112 | memcpy(esc->data[0], &avpkt->data[2], chan_size); | |
113 | if (avctx->channels == 2) | |
114 | memcpy(esc->data[1], &avpkt->data[2+chan_size+2], chan_size); | |
115 | } | |
116 | if (!esc->data[0]) { | |
117 | av_log(avctx, AV_LOG_ERROR, "unexpected empty packet\n"); | |
118 | return AVERROR(EINVAL); | |
cb21e0a7 JM |
119 | } |
120 | ||
1993c684 JR |
121 | /* decode next piece of data from the buffer */ |
122 | buf_size = FFMIN(MAX_FRAME_SIZE, esc->data_size - esc->data_idx); | |
123 | if (buf_size <= 0) { | |
124 | *data_size = 0; | |
125 | return avpkt->size; | |
126 | } | |
127 | out_data_size = buf_size * 2 * avctx->channels; | |
128 | if (*data_size < out_data_size) { | |
fda459ce JR |
129 | av_log(avctx, AV_LOG_ERROR, "Provided buffer with size %d is too small.\n", |
130 | *data_size); | |
e3718784 | 131 | return AVERROR(EINVAL); |
fda459ce | 132 | } |
1993c684 JR |
133 | delta_decode(out_data, &esc->data[0][esc->data_idx], buf_size, |
134 | &esc->fib_acc[0], esc->table, avctx->channels); | |
135 | if (avctx->channels == 2) { | |
136 | delta_decode(&out_data[1], &esc->data[1][esc->data_idx], buf_size, | |
137 | &esc->fib_acc[1], esc->table, avctx->channels); | |
138 | } | |
139 | esc->data_idx += buf_size; | |
140 | *data_size = out_data_size; | |
cb21e0a7 | 141 | |
1993c684 | 142 | return avpkt->size; |
cb21e0a7 JM |
143 | } |
144 | ||
1b41f260 | 145 | /** initialize 8svx decoder */ |
cb21e0a7 JM |
146 | static av_cold int eightsvx_decode_init(AVCodecContext *avctx) |
147 | { | |
148 | EightSvxContext *esc = avctx->priv_data; | |
149 | ||
1993c684 JR |
150 | if (avctx->channels < 1 || avctx->channels > 2) { |
151 | av_log(avctx, AV_LOG_ERROR, "8SVX does not support more than 2 channels\n"); | |
152 | return AVERROR(EINVAL); | |
153 | } | |
154 | ||
cb21e0a7 JM |
155 | switch(avctx->codec->id) { |
156 | case CODEC_ID_8SVX_FIB: | |
157 | esc->table = fibonacci; | |
158 | break; | |
159 | case CODEC_ID_8SVX_EXP: | |
160 | esc->table = exponential; | |
161 | break; | |
162 | default: | |
163 | return -1; | |
164 | } | |
e3718784 | 165 | avctx->sample_fmt = AV_SAMPLE_FMT_U8; |
cb21e0a7 JM |
166 | return 0; |
167 | } | |
168 | ||
1993c684 JR |
169 | static av_cold int eightsvx_decode_close(AVCodecContext *avctx) |
170 | { | |
171 | EightSvxContext *esc = avctx->priv_data; | |
172 | ||
173 | av_freep(&esc->data[0]); | |
174 | av_freep(&esc->data[1]); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
d36beb3f | 179 | AVCodec ff_eightsvx_fib_decoder = { |
99e3913d | 180 | .name = "8svx_fib", |
72415b2a | 181 | .type = AVMEDIA_TYPE_AUDIO, |
cb21e0a7 JM |
182 | .id = CODEC_ID_8SVX_FIB, |
183 | .priv_data_size = sizeof (EightSvxContext), | |
184 | .init = eightsvx_decode_init, | |
1993c684 | 185 | .close = eightsvx_decode_close, |
cb21e0a7 | 186 | .decode = eightsvx_decode_frame, |
1993c684 | 187 | .capabilities = CODEC_CAP_DELAY, |
fe4bf374 | 188 | .long_name = NULL_IF_CONFIG_SMALL("8SVX fibonacci"), |
cb21e0a7 JM |
189 | }; |
190 | ||
d36beb3f | 191 | AVCodec ff_eightsvx_exp_decoder = { |
99e3913d | 192 | .name = "8svx_exp", |
72415b2a | 193 | .type = AVMEDIA_TYPE_AUDIO, |
cb21e0a7 JM |
194 | .id = CODEC_ID_8SVX_EXP, |
195 | .priv_data_size = sizeof (EightSvxContext), | |
196 | .init = eightsvx_decode_init, | |
1993c684 | 197 | .close = eightsvx_decode_close, |
cb21e0a7 | 198 | .decode = eightsvx_decode_frame, |
1993c684 | 199 | .capabilities = CODEC_CAP_DELAY, |
fe4bf374 | 200 | .long_name = NULL_IF_CONFIG_SMALL("8SVX exponential"), |
cb21e0a7 | 201 | }; |