Originally committed as revision 6336 to svn://svn.ffmpeg.org/ffmpeg/trunk
Kostya Shishkov authored on 2006/09/26 12:41:51... | ... |
@@ -167,6 +167,7 @@ Codecs: |
167 | 167 |
vp5 Aurelien Jacobs |
168 | 168 |
vp6 Aurelien Jacobs |
169 | 169 |
vqavideo.c Mike Melanson |
170 |
+ wavpack.c Kostya Shishkov |
|
170 | 171 |
wmv2.c Michael Niedermayer |
171 | 172 |
wnv1.c Kostya Shishkov |
172 | 173 |
x264.c Mans Rullgard |
... | ... |
@@ -219,6 +220,7 @@ Muxers/Demuxers: |
219 | 219 |
wav.c Michael Niedermayer |
220 | 220 |
wc3movie.c Mike Melanson |
221 | 221 |
westwood.c Mike Melanson |
222 |
+ wv.c Kostya Shishkov |
|
222 | 223 |
|
223 | 224 |
|
224 | 225 |
Operating systems / CPU architectures |
... | ... |
@@ -893,6 +893,7 @@ following image formats are supported: |
893 | 893 |
@item DSP Group TrueSpeech @tab @tab X |
894 | 894 |
@item True Audio (TTA) @tab @tab X |
895 | 895 |
@item Smacker Audio @tab @tab X |
896 |
+@item WavPack Audio @tab @tab X |
|
896 | 897 |
@end multitable |
897 | 898 |
|
898 | 899 |
@code{X} means that encoding (resp. decoding) is supported. |
... | ... |
@@ -126,6 +126,7 @@ OBJS-$(CONFIG_VP3_DECODER) += vp3.o |
126 | 126 |
OBJS-$(CONFIG_VP5_DECODER) += vp5.o vp56.o vp56data.o |
127 | 127 |
OBJS-$(CONFIG_VP6_DECODER) += vp6.o vp56.o vp56data.o |
128 | 128 |
OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o |
129 |
+OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o |
|
129 | 130 |
OBJS-$(CONFIG_WMAV1_DECODER) += wmadec.o |
130 | 131 |
OBJS-$(CONFIG_WMAV2_DECODER) += wmadec.o |
131 | 132 |
OBJS-$(CONFIG_WMV3_DECODER) += vc1.o |
... | ... |
@@ -540,6 +540,9 @@ void avcodec_register_all(void) |
540 | 540 |
#ifdef CONFIG_VMNC_DECODER |
541 | 541 |
register_avcodec(&vmnc_decoder); |
542 | 542 |
#endif //CONFIG_VMNC_DECODER |
543 |
+#ifdef CONFIG_WAVPACK_DECODER |
|
544 |
+ register_avcodec(&wavpack_decoder); |
|
545 |
+#endif //CONFIG_WAVPACK_DECODER |
|
543 | 546 |
#endif /* CONFIG_DECODERS */ |
544 | 547 |
|
545 | 548 |
#if defined(CONFIG_AMR_NB) || defined(CONFIG_AMR_NB_FIXED) |
... | ... |
@@ -224,6 +224,7 @@ enum CodecID { |
224 | 224 |
CODEC_ID_TTA, |
225 | 225 |
CODEC_ID_SMACKAUDIO, |
226 | 226 |
CODEC_ID_QCELP, |
227 |
+ CODEC_ID_WAVPACK, |
|
227 | 228 |
|
228 | 229 |
/* subtitle codecs */ |
229 | 230 |
CODEC_ID_DVD_SUBTITLE= 0x17000, |
... | ... |
@@ -2285,6 +2286,7 @@ extern AVCodec kmvc_decoder; |
2285 | 2285 |
extern AVCodec flashsv_decoder; |
2286 | 2286 |
extern AVCodec cavs_decoder; |
2287 | 2287 |
extern AVCodec vmnc_decoder; |
2288 |
+extern AVCodec wavpack_decoder; |
|
2288 | 2289 |
|
2289 | 2290 |
/* pcm codecs */ |
2290 | 2291 |
#define PCM_CODEC(id, name) \ |
2291 | 2292 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,548 @@ |
0 |
+/* |
|
1 |
+ * WavPack lossless audio decoder |
|
2 |
+ * Copyright (c) 2006 Konstantin Shishkov |
|
3 |
+ * |
|
4 |
+ * This library is free software; you can redistribute it and/or |
|
5 |
+ * modify it under the terms of the GNU Lesser General Public |
|
6 |
+ * License as published by the Free Software Foundation; either |
|
7 |
+ * version 2 of the License, or (at your option) any later version. |
|
8 |
+ * |
|
9 |
+ * This library is distributed in the hope that it will be useful, |
|
10 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 |
+ * Lesser General Public License for more details. |
|
13 |
+ * |
|
14 |
+ * You should have received a copy of the GNU Lesser General Public |
|
15 |
+ * License along with this library; if not, write to the Free Software |
|
16 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
17 |
+ */ |
|
18 |
+#define ALT_BITSTREAM_READER_LE |
|
19 |
+#include "avcodec.h" |
|
20 |
+#include "bitstream.h" |
|
21 |
+ |
|
22 |
+/** |
|
23 |
+ * @file wavpack.c |
|
24 |
+ * WavPack lossless audio decoder |
|
25 |
+ */ |
|
26 |
+ |
|
27 |
+#define WV_JOINT 0x0010 |
|
28 |
+ |
|
29 |
+enum WP_ID_Flags{ |
|
30 |
+ WP_IDF_MASK = 0x1F, |
|
31 |
+ WP_IDF_IGNORE = 0x20, |
|
32 |
+ WP_IDF_ODD = 0x40, |
|
33 |
+ WP_IDF_LONG = 0x80 |
|
34 |
+}; |
|
35 |
+ |
|
36 |
+enum WP_ID{ |
|
37 |
+ WP_ID_DUMMY = 0, |
|
38 |
+ WP_ID_ENCINFO, |
|
39 |
+ WP_ID_DECTERMS, |
|
40 |
+ WP_ID_DECWEIGHTS, |
|
41 |
+ WP_ID_DECSAMPLES, |
|
42 |
+ WP_ID_ENTROPY, |
|
43 |
+ WP_ID_HYBRID, |
|
44 |
+ WP_ID_SHAPING, |
|
45 |
+ WP_ID_FLOATINFO, |
|
46 |
+ WP_ID_INT32INFO, |
|
47 |
+ WP_ID_DATA, |
|
48 |
+ WP_ID_CORR, |
|
49 |
+ WP_ID_FLT, |
|
50 |
+ WP_ID_CHANINFO |
|
51 |
+}; |
|
52 |
+ |
|
53 |
+#define MAX_TERMS 16 |
|
54 |
+ |
|
55 |
+typedef struct Decorr { |
|
56 |
+ int delta; |
|
57 |
+ int value; |
|
58 |
+ int weightA; |
|
59 |
+ int weightB; |
|
60 |
+ int samplesA[8]; |
|
61 |
+ int samplesB[8]; |
|
62 |
+} Decorr; |
|
63 |
+ |
|
64 |
+typedef struct WavpackContext { |
|
65 |
+ AVCodecContext *avctx; |
|
66 |
+ int stereo; |
|
67 |
+ int joint; |
|
68 |
+ uint32_t CRC; |
|
69 |
+ GetBitContext gb; |
|
70 |
+ int data_size; // in bits |
|
71 |
+ int samples; |
|
72 |
+ int median[6]; |
|
73 |
+ int terms; |
|
74 |
+ Decorr decorr[MAX_TERMS]; |
|
75 |
+ int zero, one, zeroes; |
|
76 |
+} WavpackContext; |
|
77 |
+ |
|
78 |
+// exponent table copied from WavPack source |
|
79 |
+static const uint8_t wp_exp2_table [256] = { |
|
80 |
+ 0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b, |
|
81 |
+ 0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16, |
|
82 |
+ 0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23, |
|
83 |
+ 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, |
|
84 |
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d, |
|
85 |
+ 0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b, |
|
86 |
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, |
|
87 |
+ 0x5b, 0x5c, 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, |
|
88 |
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, |
|
89 |
+ 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, |
|
90 |
+ 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, |
|
91 |
+ 0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, |
|
92 |
+ 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, |
|
93 |
+ 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4, |
|
94 |
+ 0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9, |
|
95 |
+ 0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff |
|
96 |
+}; |
|
97 |
+ |
|
98 |
+static always_inline int wp_exp2(int16_t val) |
|
99 |
+{ |
|
100 |
+ int res, neg = 0; |
|
101 |
+ |
|
102 |
+ if(val < 0){ |
|
103 |
+ val = -val; |
|
104 |
+ neg = 1; |
|
105 |
+ } |
|
106 |
+ |
|
107 |
+ res = wp_exp2_table[val & 0xFF] | 0x100; |
|
108 |
+ val >>= 8; |
|
109 |
+ res = (val > 9) ? (res << (val - 9)) : (res >> (9 - val)); |
|
110 |
+ return neg ? -res : res; |
|
111 |
+} |
|
112 |
+ |
|
113 |
+static inline int get_unary(GetBitContext *gb){ |
|
114 |
+ int r=0; |
|
115 |
+ while(get_bits1(gb) && r<33)r++; |
|
116 |
+ return r; |
|
117 |
+} |
|
118 |
+ |
|
119 |
+// macros for manipulating median values |
|
120 |
+#define GET_MED(n) ((median[n] >> 4) + 1) |
|
121 |
+#define DEC_MED(n) median[n] -= ((median[n] + (128>>n) - 2) / (128>>n)) * 2 |
|
122 |
+#define INC_MED(n) median[n] += ((median[n] + (128>>n)) / (128>>n)) * 5 |
|
123 |
+ |
|
124 |
+// macros for applying weight |
|
125 |
+#define UPDATE_WEIGHT_CLIP(weight, delta, samples, in) \ |
|
126 |
+ if(samples && in){ \ |
|
127 |
+ if((samples ^ in) < 0){ \ |
|
128 |
+ weight -= delta; \ |
|
129 |
+ if(weight < -1024) weight = -1024; \ |
|
130 |
+ }else{ \ |
|
131 |
+ weight += delta; \ |
|
132 |
+ if(weight > 1024) weight = 1024; \ |
|
133 |
+ } \ |
|
134 |
+ } |
|
135 |
+ |
|
136 |
+ |
|
137 |
+static always_inline int get_tail(GetBitContext *gb, int k) |
|
138 |
+{ |
|
139 |
+ int p, e, res; |
|
140 |
+ |
|
141 |
+ if(k<1 || k>65535)return 0; |
|
142 |
+ p = av_log2_16bit(k); |
|
143 |
+ e = (1 << (p + 1)) - k - 1; |
|
144 |
+ res = get_bits(gb, p); |
|
145 |
+ if(res >= e){ |
|
146 |
+ res = (res<<1) - e + get_bits1(gb); |
|
147 |
+ } |
|
148 |
+ return res; |
|
149 |
+} |
|
150 |
+ |
|
151 |
+static int wv_get_value(WavpackContext *ctx, GetBitContext *gb, int *median, int *last) |
|
152 |
+{ |
|
153 |
+ int t, t2; |
|
154 |
+ int sign, base, add, ret; |
|
155 |
+ |
|
156 |
+ *last = 0; |
|
157 |
+ |
|
158 |
+ if((ctx->median[0] < 2U) && (ctx->median[3] < 2U) && !ctx->zero && !ctx->one){ |
|
159 |
+ if(ctx->zeroes){ |
|
160 |
+ ctx->zeroes--; |
|
161 |
+ if(ctx->zeroes) |
|
162 |
+ return 0; |
|
163 |
+ }else{ |
|
164 |
+ t = get_unary(gb); |
|
165 |
+ if(t >= 2) t = get_bits(gb, t - 1) | (1 << (t-1)); |
|
166 |
+ ctx->zeroes = t; |
|
167 |
+ if(ctx->zeroes){ |
|
168 |
+ memset(ctx->median, 0, sizeof(ctx->median)); |
|
169 |
+ return 0; |
|
170 |
+ } |
|
171 |
+ } |
|
172 |
+ } |
|
173 |
+ |
|
174 |
+ if(get_bits_count(gb) >= ctx->data_size){ |
|
175 |
+ *last = 1; |
|
176 |
+ return 0; |
|
177 |
+ } |
|
178 |
+ |
|
179 |
+ if(ctx->zero){ |
|
180 |
+ t = 0; |
|
181 |
+ ctx->zero = 0; |
|
182 |
+ }else{ |
|
183 |
+ t = get_unary(gb); |
|
184 |
+ if(get_bits_count(gb) >= ctx->data_size){ |
|
185 |
+ *last = 1; |
|
186 |
+ return 0; |
|
187 |
+ } |
|
188 |
+ if(t == 16) { |
|
189 |
+ t2 = get_unary(gb); |
|
190 |
+ if(t2 < 2) t += t2; |
|
191 |
+ else t += get_bits(gb, t2 - 1) | (1 << (t2 - 1)); |
|
192 |
+ } |
|
193 |
+ |
|
194 |
+ if(ctx->one){ |
|
195 |
+ ctx->one = t&1; |
|
196 |
+ t = (t>>1) + 1; |
|
197 |
+ }else{ |
|
198 |
+ ctx->one = t&1; |
|
199 |
+ t >>= 1; |
|
200 |
+ } |
|
201 |
+ ctx->zero = !ctx->one; |
|
202 |
+ } |
|
203 |
+ |
|
204 |
+ if(!t){ |
|
205 |
+ base = 0; |
|
206 |
+ add = GET_MED(0) - 1; |
|
207 |
+ DEC_MED(0); |
|
208 |
+ }else if(t == 1){ |
|
209 |
+ base = GET_MED(0); |
|
210 |
+ add = GET_MED(1) - 1; |
|
211 |
+ INC_MED(0); |
|
212 |
+ DEC_MED(1); |
|
213 |
+ }else if(t == 2){ |
|
214 |
+ base = GET_MED(0) + GET_MED(1); |
|
215 |
+ add = GET_MED(2) - 1; |
|
216 |
+ INC_MED(0); |
|
217 |
+ INC_MED(1); |
|
218 |
+ DEC_MED(2); |
|
219 |
+ }else{ |
|
220 |
+ base = GET_MED(0) + GET_MED(1) + GET_MED(2) * (t - 2); |
|
221 |
+ add = GET_MED(2) - 1; |
|
222 |
+ INC_MED(0); |
|
223 |
+ INC_MED(1); |
|
224 |
+ INC_MED(2); |
|
225 |
+ } |
|
226 |
+ ret = base + get_tail(gb, add); |
|
227 |
+ sign = get_bits1(gb); |
|
228 |
+ return sign ? ~ret : ret; |
|
229 |
+} |
|
230 |
+ |
|
231 |
+static int wv_unpack_stereo(WavpackContext *s, GetBitContext *gb, int16_t *dst) |
|
232 |
+{ |
|
233 |
+ int i, j, count = 0; |
|
234 |
+ int last, t; |
|
235 |
+ int A, B, L, L2, R, R2; |
|
236 |
+ int pos = 0; |
|
237 |
+ uint32_t crc = 0xFFFFFFFF; |
|
238 |
+ |
|
239 |
+ s->one = s->zero = s->zeroes = 0; |
|
240 |
+ do{ |
|
241 |
+ L = wv_get_value(s, gb, s->median, &last); |
|
242 |
+ if(last) break; |
|
243 |
+ R = wv_get_value(s, gb, s->median + 3, &last); |
|
244 |
+ if(last) break; |
|
245 |
+ for(i = 0; i < s->terms; i++){ |
|
246 |
+ t = s->decorr[i].value; |
|
247 |
+ j = 0; |
|
248 |
+ if(t > 0){ |
|
249 |
+ if(t > 8){ |
|
250 |
+ if(t & 1){ |
|
251 |
+ A = 2 * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]; |
|
252 |
+ B = 2 * s->decorr[i].samplesB[0] - s->decorr[i].samplesB[1]; |
|
253 |
+ }else{ |
|
254 |
+ A = (3 * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]) >> 1; |
|
255 |
+ B = (3 * s->decorr[i].samplesB[0] - s->decorr[i].samplesB[1]) >> 1; |
|
256 |
+ } |
|
257 |
+ s->decorr[i].samplesA[1] = s->decorr[i].samplesA[0]; |
|
258 |
+ s->decorr[i].samplesB[1] = s->decorr[i].samplesB[0]; |
|
259 |
+ j = 0; |
|
260 |
+ }else{ |
|
261 |
+ A = s->decorr[i].samplesA[pos]; |
|
262 |
+ B = s->decorr[i].samplesB[pos]; |
|
263 |
+ j = (pos + t) & 7; |
|
264 |
+ } |
|
265 |
+ L2 = L + ((s->decorr[i].weightA * A + 512) >> 10); |
|
266 |
+ R2 = R + ((s->decorr[i].weightB * B + 512) >> 10); |
|
267 |
+ if(A && L) s->decorr[i].weightA -= ((((L ^ A) >> 30) & 2) - 1) * s->decorr[i].delta; |
|
268 |
+ if(B && R) s->decorr[i].weightB -= ((((R ^ B) >> 30) & 2) - 1) * s->decorr[i].delta; |
|
269 |
+ s->decorr[i].samplesA[j] = L = L2; |
|
270 |
+ s->decorr[i].samplesB[j] = R = R2; |
|
271 |
+ }else if(t == -1){ |
|
272 |
+ L2 = L + ((s->decorr[i].weightA * s->decorr[i].samplesA[0] + 512) >> 10); |
|
273 |
+ UPDATE_WEIGHT_CLIP(s->decorr[i].weightA, s->decorr[i].delta, s->decorr[i].samplesA[0], L); |
|
274 |
+ L = L2; |
|
275 |
+ R2 = R + ((s->decorr[i].weightB * L2 + 512) >> 10); |
|
276 |
+ UPDATE_WEIGHT_CLIP(s->decorr[i].weightB, s->decorr[i].delta, L2, R); |
|
277 |
+ R = R2; |
|
278 |
+ s->decorr[i].samplesA[0] = R; |
|
279 |
+ }else{ |
|
280 |
+ R2 = R + ((s->decorr[i].weightB * s->decorr[i].samplesB[0] + 512) >> 10); |
|
281 |
+ UPDATE_WEIGHT_CLIP(s->decorr[i].weightB, s->decorr[i].delta, s->decorr[i].samplesB[0], R); |
|
282 |
+ R = R2; |
|
283 |
+ |
|
284 |
+ if(t == -3){ |
|
285 |
+ R2 = s->decorr[i].samplesA[0]; |
|
286 |
+ s->decorr[i].samplesA[0] = R; |
|
287 |
+ } |
|
288 |
+ |
|
289 |
+ L2 = L + ((s->decorr[i].weightA * R2 + 512) >> 10); |
|
290 |
+ UPDATE_WEIGHT_CLIP(s->decorr[i].weightA, s->decorr[i].delta, R2, L); |
|
291 |
+ L = L2; |
|
292 |
+ s->decorr[i].samplesB[0] = L; |
|
293 |
+ } |
|
294 |
+ } |
|
295 |
+ pos = (pos + 1) & 7; |
|
296 |
+ if(s->joint) |
|
297 |
+ L += (R -= (L >> 1)); |
|
298 |
+ crc = (crc * 3 + L) * 3 + R; |
|
299 |
+ *dst++ = L; |
|
300 |
+ *dst++ = R; |
|
301 |
+ |
|
302 |
+ count++; |
|
303 |
+ }while(!last && count < s->samples); |
|
304 |
+ |
|
305 |
+ if(crc != s->CRC){ |
|
306 |
+ av_log(s->avctx, AV_LOG_ERROR, "CRC error\n"); |
|
307 |
+ return -1; |
|
308 |
+ } |
|
309 |
+ return count * 2; |
|
310 |
+} |
|
311 |
+ |
|
312 |
+static int wv_unpack_mono(WavpackContext *s, GetBitContext *gb, int16_t *dst) |
|
313 |
+{ |
|
314 |
+ int i, j, count = 0; |
|
315 |
+ int last, t; |
|
316 |
+ int A, S, T; |
|
317 |
+ int pos = 0; |
|
318 |
+ uint32_t crc = 0xFFFFFFFF; |
|
319 |
+ |
|
320 |
+ s->one = s->zero = s->zeroes = 0; |
|
321 |
+ do{ |
|
322 |
+ T = wv_get_value(s, gb, s->median, &last); |
|
323 |
+ S = 0; |
|
324 |
+ if(last) break; |
|
325 |
+ for(i = 0; i < s->terms; i++){ |
|
326 |
+ t = s->decorr[i].value; |
|
327 |
+ if(t > 8){ |
|
328 |
+ if(t & 1) |
|
329 |
+ A = 2 * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]; |
|
330 |
+ else |
|
331 |
+ A = (3 * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]) >> 1; |
|
332 |
+ s->decorr[i].samplesA[1] = s->decorr[i].samplesA[0]; |
|
333 |
+ j = 0; |
|
334 |
+ }else{ |
|
335 |
+ A = s->decorr[i].samplesA[pos]; |
|
336 |
+ j = (pos + t) & 7; |
|
337 |
+ } |
|
338 |
+ S = T + ((s->decorr[i].weightA * A + 512) >> 10); |
|
339 |
+ if(A && T) s->decorr[i].weightA -= ((((T ^ A) >> 30) & 2) - 1) * s->decorr[i].delta; |
|
340 |
+ s->decorr[i].samplesA[j] = T = S; |
|
341 |
+ } |
|
342 |
+ pos = (pos + 1) & 7; |
|
343 |
+ crc = crc * 3 + S; |
|
344 |
+ *dst++ = S; |
|
345 |
+ count++; |
|
346 |
+ }while(!last && count < s->samples); |
|
347 |
+ |
|
348 |
+ if(crc != s->CRC){ |
|
349 |
+ av_log(s->avctx, AV_LOG_ERROR, "CRC error\n"); |
|
350 |
+ return -1; |
|
351 |
+ } |
|
352 |
+ return count; |
|
353 |
+} |
|
354 |
+ |
|
355 |
+static int wavpack_decode_init(AVCodecContext *avctx) |
|
356 |
+{ |
|
357 |
+ WavpackContext *s = avctx->priv_data; |
|
358 |
+ |
|
359 |
+ s->avctx = avctx; |
|
360 |
+ s->stereo = (avctx->channels == 2); |
|
361 |
+ |
|
362 |
+ return 0; |
|
363 |
+} |
|
364 |
+ |
|
365 |
+static int wavpack_decode_close(AVCodecContext *avctx) |
|
366 |
+{ |
|
367 |
+// WavpackContext *s = avctx->priv_data; |
|
368 |
+ |
|
369 |
+ return 0; |
|
370 |
+} |
|
371 |
+ |
|
372 |
+static int wavpack_decode_frame(AVCodecContext *avctx, |
|
373 |
+ void *data, int *data_size, |
|
374 |
+ uint8_t *buf, int buf_size) |
|
375 |
+{ |
|
376 |
+ WavpackContext *s = avctx->priv_data; |
|
377 |
+ int16_t *samples = data; |
|
378 |
+ int samplecount; |
|
379 |
+ int got_terms = 0, got_weights = 0, got_samples = 0, got_entropy = 0, got_bs = 0; |
|
380 |
+ uint8_t* buf_end = buf + buf_size; |
|
381 |
+ int i, j, id, size, ssize, weights, t; |
|
382 |
+ |
|
383 |
+ if (buf_size == 0) return 0; |
|
384 |
+ |
|
385 |
+ memset(s->decorr, 0, MAX_TERMS * sizeof(Decorr)); |
|
386 |
+ |
|
387 |
+ s->samples = LE_32(buf); buf += 4; |
|
388 |
+ s->joint = LE_32(buf) & WV_JOINT; buf += 4; |
|
389 |
+ s->CRC = LE_32(buf); buf += 4; |
|
390 |
+ // parse metadata blocks |
|
391 |
+ while(buf < buf_end){ |
|
392 |
+ id = *buf++; |
|
393 |
+ size = *buf++; |
|
394 |
+ if(id & WP_IDF_LONG) { |
|
395 |
+ size |= (*buf++) << 8; |
|
396 |
+ size |= (*buf++) << 16; |
|
397 |
+ } |
|
398 |
+ size <<= 1; // size is specified in words |
|
399 |
+ ssize = size; |
|
400 |
+ if(id & WP_IDF_ODD) size--; |
|
401 |
+ if(size < 0){ |
|
402 |
+ av_log(avctx, AV_LOG_ERROR, "Got incorrect block %02X with size %i\n", id, size); |
|
403 |
+ break; |
|
404 |
+ } |
|
405 |
+ if(buf + ssize > buf_end){ |
|
406 |
+ av_log(avctx, AV_LOG_ERROR, "Block size %i is out of bounds\n", size); |
|
407 |
+ break; |
|
408 |
+ } |
|
409 |
+ if(id & WP_IDF_IGNORE){ |
|
410 |
+ buf += ssize; |
|
411 |
+ continue; |
|
412 |
+ } |
|
413 |
+ switch(id & WP_IDF_MASK){ |
|
414 |
+ case WP_ID_DECTERMS: |
|
415 |
+ s->terms = size; |
|
416 |
+ if(s->terms > MAX_TERMS){ |
|
417 |
+ av_log(avctx, AV_LOG_ERROR, "Too many decorrelation terms\n"); |
|
418 |
+ buf += ssize; |
|
419 |
+ continue; |
|
420 |
+ } |
|
421 |
+ for(i = 0; i < s->terms; i++) { |
|
422 |
+ s->decorr[s->terms - i - 1].value = (*buf & 0x1F) - 5; |
|
423 |
+ s->decorr[s->terms - i - 1].delta = *buf >> 5; |
|
424 |
+ buf++; |
|
425 |
+ } |
|
426 |
+ got_terms = 1; |
|
427 |
+ break; |
|
428 |
+ case WP_ID_DECWEIGHTS: |
|
429 |
+ if(!got_terms){ |
|
430 |
+ av_log(avctx, AV_LOG_ERROR, "No decorrelation terms met\n"); |
|
431 |
+ continue; |
|
432 |
+ } |
|
433 |
+ weights = size >> s->stereo; |
|
434 |
+ if(weights > MAX_TERMS || weights > s->terms){ |
|
435 |
+ av_log(avctx, AV_LOG_ERROR, "Too many decorrelation weights\n"); |
|
436 |
+ buf += ssize; |
|
437 |
+ continue; |
|
438 |
+ } |
|
439 |
+ for(i = 0; i < weights; i++) { |
|
440 |
+ t = (int8_t)(*buf++); |
|
441 |
+ s->decorr[s->terms - i - 1].weightA = t << 3; |
|
442 |
+ if(s->decorr[s->terms - i - 1].weightA > 0) |
|
443 |
+ s->decorr[s->terms - i - 1].weightA += (s->decorr[s->terms - i - 1].weightA + 64) >> 7; |
|
444 |
+ if(s->stereo){ |
|
445 |
+ t = (int8_t)(*buf++); |
|
446 |
+ s->decorr[s->terms - i - 1].weightB = t << 3; |
|
447 |
+ if(s->decorr[s->terms - i - 1].weightB > 0) |
|
448 |
+ s->decorr[s->terms - i - 1].weightB += (s->decorr[s->terms - i - 1].weightB + 64) >> 7; |
|
449 |
+ } |
|
450 |
+ } |
|
451 |
+ got_weights = 1; |
|
452 |
+ break; |
|
453 |
+ case WP_ID_DECSAMPLES: |
|
454 |
+ if(!got_terms){ |
|
455 |
+ av_log(avctx, AV_LOG_ERROR, "No decorrelation terms met\n"); |
|
456 |
+ continue; |
|
457 |
+ } |
|
458 |
+ t = 0; |
|
459 |
+ for(i = s->terms - 1; (i >= 0) && (t < size); i--) { |
|
460 |
+ if(s->decorr[i].value > 8){ |
|
461 |
+ s->decorr[i].samplesA[0] = wp_exp2(LE_16(buf)); buf += 2; |
|
462 |
+ s->decorr[i].samplesA[1] = wp_exp2(LE_16(buf)); buf += 2; |
|
463 |
+ if(s->stereo){ |
|
464 |
+ s->decorr[i].samplesB[0] = wp_exp2(LE_16(buf)); buf += 2; |
|
465 |
+ s->decorr[i].samplesB[1] = wp_exp2(LE_16(buf)); buf += 2; |
|
466 |
+ t += 4; |
|
467 |
+ } |
|
468 |
+ t += 4; |
|
469 |
+ }else if(s->decorr[i].value < 0){ |
|
470 |
+ s->decorr[i].samplesA[0] = wp_exp2(LE_16(buf)); buf += 2; |
|
471 |
+ s->decorr[i].samplesB[0] = wp_exp2(LE_16(buf)); buf += 2; |
|
472 |
+ t += 4; |
|
473 |
+ }else{ |
|
474 |
+ for(j = 0; j < s->decorr[i].value; j++){ |
|
475 |
+ s->decorr[i].samplesA[j] = wp_exp2(LE_16(buf)); buf += 2; |
|
476 |
+ if(s->stereo){ |
|
477 |
+ s->decorr[i].samplesB[j] = wp_exp2(LE_16(buf)); buf += 2; |
|
478 |
+ } |
|
479 |
+ } |
|
480 |
+ t += s->decorr[i].value * 2 * avctx->channels; |
|
481 |
+ } |
|
482 |
+ } |
|
483 |
+ got_samples = 1; |
|
484 |
+ break; |
|
485 |
+ case WP_ID_ENTROPY: |
|
486 |
+ if(size != 6 * avctx->channels){ |
|
487 |
+ av_log(avctx, AV_LOG_ERROR, "Entropy vars size should be %i, got %i", 6 * avctx->channels, size); |
|
488 |
+ buf += ssize; |
|
489 |
+ continue; |
|
490 |
+ } |
|
491 |
+ for(i = 0; i < 3 * avctx->channels; i++){ |
|
492 |
+ s->median[i] = wp_exp2(LE_16(buf)); |
|
493 |
+ buf += 2; |
|
494 |
+ } |
|
495 |
+ got_entropy = 1; |
|
496 |
+ break; |
|
497 |
+ case WP_ID_DATA: |
|
498 |
+ init_get_bits(&s->gb, buf, size * 8); |
|
499 |
+ s->data_size = size * 8; |
|
500 |
+ buf += size; |
|
501 |
+ got_bs = 1; |
|
502 |
+ break; |
|
503 |
+ default: |
|
504 |
+ buf += size; |
|
505 |
+ } |
|
506 |
+ if(id & WP_IDF_ODD) buf++; |
|
507 |
+ } |
|
508 |
+ if(!got_terms){ |
|
509 |
+ av_log(avctx, AV_LOG_ERROR, "No block with decorrelation terms\n"); |
|
510 |
+ return -1; |
|
511 |
+ } |
|
512 |
+ if(!got_weights){ |
|
513 |
+ av_log(avctx, AV_LOG_ERROR, "No block with decorrelation weights\n"); |
|
514 |
+ return -1; |
|
515 |
+ } |
|
516 |
+ if(!got_samples){ |
|
517 |
+ av_log(avctx, AV_LOG_ERROR, "No block with decorrelation samples\n"); |
|
518 |
+ return -1; |
|
519 |
+ } |
|
520 |
+ if(!got_entropy){ |
|
521 |
+ av_log(avctx, AV_LOG_ERROR, "No block with entropy info\n"); |
|
522 |
+ return -1; |
|
523 |
+ } |
|
524 |
+ if(!got_bs){ |
|
525 |
+ av_log(avctx, AV_LOG_ERROR, "Packed samples not found\n"); |
|
526 |
+ return -1; |
|
527 |
+ } |
|
528 |
+ |
|
529 |
+ if(s->stereo) |
|
530 |
+ samplecount = wv_unpack_stereo(s, &s->gb, samples); |
|
531 |
+ else |
|
532 |
+ samplecount = wv_unpack_mono(s, &s->gb, samples); |
|
533 |
+ *data_size = samplecount * 2; |
|
534 |
+ |
|
535 |
+ return buf_size; |
|
536 |
+} |
|
537 |
+ |
|
538 |
+AVCodec wavpack_decoder = { |
|
539 |
+ "wavpack", |
|
540 |
+ CODEC_TYPE_AUDIO, |
|
541 |
+ CODEC_ID_WAVPACK, |
|
542 |
+ sizeof(WavpackContext), |
|
543 |
+ wavpack_decode_init, |
|
544 |
+ NULL, |
|
545 |
+ wavpack_decode_close, |
|
546 |
+ wavpack_decode_frame, |
|
547 |
+}; |
... | ... |
@@ -127,6 +127,7 @@ OBJS-$(CONFIG_WAV_MUXER) += wav.o riff.o |
127 | 127 |
OBJS-$(CONFIG_WC3_DEMUXER) += wc3movie.o |
128 | 128 |
OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood.o |
129 | 129 |
OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood.o |
130 |
+OBJS-$(CONFIG_WV_DEMUXER) += wv.o |
|
130 | 131 |
OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpeg.o |
131 | 132 |
OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpeg.o |
132 | 133 |
|
... | ... |
@@ -461,6 +461,9 @@ void av_register_all(void) |
461 | 461 |
#ifdef CONFIG_WSVQA_DEMUXER |
462 | 462 |
av_register_input_format(&wsvqa_demuxer); |
463 | 463 |
#endif |
464 |
+#ifdef CONFIG_WV_DEMUXER |
|
465 |
+ av_register_input_format(&wv_demuxer); |
|
466 |
+#endif |
|
464 | 467 |
#ifdef CONFIG_YUV4MPEGPIPE_MUXER |
465 | 468 |
av_register_output_format(&yuv4mpegpipe_muxer); |
466 | 469 |
#endif |
... | ... |
@@ -155,6 +155,7 @@ extern AVOutputFormat wav_muxer; |
155 | 155 |
extern AVInputFormat wc3_demuxer; |
156 | 156 |
extern AVInputFormat wsaud_demuxer; |
157 | 157 |
extern AVInputFormat wsvqa_demuxer; |
158 |
+extern AVInputFormat wv_demuxer; |
|
158 | 159 |
extern AVOutputFormat yuv4mpegpipe_muxer; |
159 | 160 |
extern AVInputFormat yuv4mpegpipe_demuxer; |
160 | 161 |
|
161 | 162 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,207 @@ |
0 |
+/* |
|
1 |
+ * WavPack demuxer |
|
2 |
+ * Copyright (c) 2006 Konstantin Shishkov. |
|
3 |
+ * |
|
4 |
+ * This library is free software; you can redistribute it and/or |
|
5 |
+ * modify it under the terms of the GNU Lesser General Public |
|
6 |
+ * License as published by the Free Software Foundation; either |
|
7 |
+ * version 2 of the License, or (at your option) any later version. |
|
8 |
+ * |
|
9 |
+ * This library is distributed in the hope that it will be useful, |
|
10 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 |
+ * Lesser General Public License for more details. |
|
13 |
+ * |
|
14 |
+ * You should have received a copy of the GNU Lesser General Public |
|
15 |
+ * License along with this library; if not, write to the Free Software |
|
16 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
17 |
+ */ |
|
18 |
+ |
|
19 |
+#include "avformat.h" |
|
20 |
+#include "allformats.h" |
|
21 |
+#include "bswap.h" |
|
22 |
+ |
|
23 |
+// specs say that maximum block size is 1Mb |
|
24 |
+#define WV_BLOCK_LIMIT 1047576 |
|
25 |
+ |
|
26 |
+#define WV_EXTRA_SIZE 12 |
|
27 |
+ |
|
28 |
+enum WV_FLAGS{ |
|
29 |
+ WV_MONO = 0x0004, |
|
30 |
+ WV_HYBRID = 0x0008, |
|
31 |
+ WV_JOINT = 0x0010, |
|
32 |
+ WV_CROSSD = 0x0020, |
|
33 |
+ WV_HSHAPE = 0x0040, |
|
34 |
+ WV_FLOAT = 0x0080, |
|
35 |
+ WV_INT32 = 0x0100, |
|
36 |
+ WV_HBR = 0x0200, |
|
37 |
+ WV_HBAL = 0x0400, |
|
38 |
+ WV_MCINIT = 0x0800, |
|
39 |
+ WV_MCEND = 0x1000, |
|
40 |
+}; |
|
41 |
+ |
|
42 |
+static const int wv_rates[16] = { |
|
43 |
+ 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, |
|
44 |
+ 32000, 44100, 48000, 64000, 88200, 96000, 192000, -1 |
|
45 |
+}; |
|
46 |
+ |
|
47 |
+typedef struct{ |
|
48 |
+ uint32_t blksize, flags; |
|
49 |
+ int rate, chan, bpp; |
|
50 |
+ int block_parsed; |
|
51 |
+ uint8_t extra[WV_EXTRA_SIZE]; |
|
52 |
+}WVContext; |
|
53 |
+ |
|
54 |
+static int wv_probe(AVProbeData *p) |
|
55 |
+{ |
|
56 |
+ /* check file header */ |
|
57 |
+ if (p->buf_size <= 32) |
|
58 |
+ return 0; |
|
59 |
+ if (p->buf[0] == 'w' && p->buf[1] == 'v' && |
|
60 |
+ p->buf[2] == 'p' && p->buf[3] == 'k') |
|
61 |
+ return AVPROBE_SCORE_MAX; |
|
62 |
+ else |
|
63 |
+ return 0; |
|
64 |
+} |
|
65 |
+ |
|
66 |
+static int wv_read_block_header(AVFormatContext *ctx, ByteIOContext *pb) |
|
67 |
+{ |
|
68 |
+ WVContext *wc = ctx->priv_data; |
|
69 |
+ uint32_t tag, ver; |
|
70 |
+ int size; |
|
71 |
+ int rate, bpp, chan; |
|
72 |
+ |
|
73 |
+ tag = get_le32(pb); |
|
74 |
+ if (tag != MKTAG('w', 'v', 'p', 'k')) |
|
75 |
+ return -1; |
|
76 |
+ size = get_le32(pb); |
|
77 |
+ if(size < 24 || size > WV_BLOCK_LIMIT){ |
|
78 |
+ av_log(ctx, AV_LOG_ERROR, "Incorrect block size %i\n", size); |
|
79 |
+ return -1; |
|
80 |
+ } |
|
81 |
+ wc->blksize = size; |
|
82 |
+ ver = get_le16(pb); |
|
83 |
+ if(ver < 0x402 || ver > 0x40F){ |
|
84 |
+ av_log(ctx, AV_LOG_ERROR, "Unsupported version %03X\n", ver); |
|
85 |
+ return -1; |
|
86 |
+ } |
|
87 |
+ get_byte(pb); // track no |
|
88 |
+ get_byte(pb); // track sub index |
|
89 |
+ get_le32(pb); // total samples in file |
|
90 |
+ get_le32(pb); // offset in samples of current block |
|
91 |
+ get_buffer(pb, wc->extra, WV_EXTRA_SIZE); |
|
92 |
+ wc->flags = LE_32(wc->extra + 4); |
|
93 |
+ //parse flags |
|
94 |
+ if(wc->flags & WV_FLOAT){ |
|
95 |
+ av_log(ctx, AV_LOG_ERROR, "Floating point data is not supported\n"); |
|
96 |
+ return -1; |
|
97 |
+ } |
|
98 |
+ if(wc->flags & WV_HYBRID){ |
|
99 |
+ av_log(ctx, AV_LOG_ERROR, "Hybrid coding mode is not supported\n"); |
|
100 |
+ return -1; |
|
101 |
+ } |
|
102 |
+ if(wc->flags & WV_INT32){ |
|
103 |
+ av_log(ctx, AV_LOG_ERROR, "Integer point data is not supported\n"); |
|
104 |
+ return -1; |
|
105 |
+ } |
|
106 |
+ |
|
107 |
+ bpp = ((wc->flags & 3) + 1) << 3; |
|
108 |
+ chan = 1 + !(wc->flags & WV_MONO); |
|
109 |
+ rate = wv_rates[(wc->flags >> 23) & 0xF]; |
|
110 |
+ if(rate == -1){ |
|
111 |
+ av_log(ctx, AV_LOG_ERROR, "Unknown sampling rate\n"); |
|
112 |
+ return -1; |
|
113 |
+ } |
|
114 |
+ if(!wc->bpp) wc->bpp = bpp; |
|
115 |
+ if(!wc->chan) wc->chan = chan; |
|
116 |
+ if(!wc->rate) wc->rate = rate; |
|
117 |
+ |
|
118 |
+ if(bpp != wc->bpp){ |
|
119 |
+ av_log(ctx, AV_LOG_ERROR, "Bits per sample differ, this block: %i, header block: %i\n", bpp, wc->bpp); |
|
120 |
+ return -1; |
|
121 |
+ } |
|
122 |
+ if(chan != wc->chan){ |
|
123 |
+ av_log(ctx, AV_LOG_ERROR, "Channels differ, this block: %i, header block: %i\n", chan, wc->chan); |
|
124 |
+ return -1; |
|
125 |
+ } |
|
126 |
+ if(rate != wc->rate){ |
|
127 |
+ av_log(ctx, AV_LOG_ERROR, "Sampling rate differ, this block: %i, header block: %i\n", rate, wc->rate); |
|
128 |
+ return -1; |
|
129 |
+ } |
|
130 |
+ wc->blksize = size - 24; |
|
131 |
+ return 0; |
|
132 |
+} |
|
133 |
+ |
|
134 |
+static int wv_read_header(AVFormatContext *s, |
|
135 |
+ AVFormatParameters *ap) |
|
136 |
+{ |
|
137 |
+ ByteIOContext *pb = &s->pb; |
|
138 |
+ WVContext *wc = s->priv_data; |
|
139 |
+ AVStream *st; |
|
140 |
+ |
|
141 |
+ if(wv_read_block_header(s, pb) < 0) |
|
142 |
+ return -1; |
|
143 |
+ |
|
144 |
+ wc->block_parsed = 0; |
|
145 |
+ /* now we are ready: build format streams */ |
|
146 |
+ st = av_new_stream(s, 0); |
|
147 |
+ if (!st) |
|
148 |
+ return -1; |
|
149 |
+ st->codec->codec_type = CODEC_TYPE_AUDIO; |
|
150 |
+ st->codec->codec_id = CODEC_ID_WAVPACK; |
|
151 |
+ st->codec->channels = wc->chan; |
|
152 |
+ st->codec->sample_rate = wc->rate; |
|
153 |
+ st->codec->bits_per_sample = wc->bpp; |
|
154 |
+ av_set_pts_info(st, 64, 1, wc->rate); |
|
155 |
+ return 0; |
|
156 |
+} |
|
157 |
+ |
|
158 |
+static int wv_read_packet(AVFormatContext *s, |
|
159 |
+ AVPacket *pkt) |
|
160 |
+{ |
|
161 |
+ WVContext *wc = s->priv_data; |
|
162 |
+ int ret, samples; |
|
163 |
+ |
|
164 |
+ if (url_feof(&s->pb)) |
|
165 |
+ return -EIO; |
|
166 |
+ if(wc->block_parsed){ |
|
167 |
+ if(wv_read_block_header(s, &s->pb) < 0) |
|
168 |
+ return -1; |
|
169 |
+ } |
|
170 |
+ |
|
171 |
+ samples = LE_32(wc->extra); |
|
172 |
+ /* should not happen but who knows */ |
|
173 |
+ if(samples * 2 * wc->chan > AVCODEC_MAX_AUDIO_FRAME_SIZE){ |
|
174 |
+ av_log(s, AV_LOG_ERROR, "Packet size is too big to be handled in lavc!\n"); |
|
175 |
+ return -EIO; |
|
176 |
+ } |
|
177 |
+ if(av_new_packet(pkt, wc->blksize + WV_EXTRA_SIZE) < 0) |
|
178 |
+ return AVERROR_NOMEM; |
|
179 |
+ memcpy(pkt->data, wc->extra, WV_EXTRA_SIZE); |
|
180 |
+ ret = get_buffer(&s->pb, pkt->data + WV_EXTRA_SIZE, wc->blksize); |
|
181 |
+ if(ret != wc->blksize){ |
|
182 |
+ av_free_packet(pkt); |
|
183 |
+ return AVERROR_IO; |
|
184 |
+ } |
|
185 |
+ pkt->stream_index = 0; |
|
186 |
+ wc->block_parsed = 1; |
|
187 |
+ pkt->size = ret + WV_EXTRA_SIZE; |
|
188 |
+ |
|
189 |
+ return 0; |
|
190 |
+} |
|
191 |
+ |
|
192 |
+static int wv_read_close(AVFormatContext *s) |
|
193 |
+{ |
|
194 |
+ return 0; |
|
195 |
+} |
|
196 |
+ |
|
197 |
+AVInputFormat wv_demuxer = { |
|
198 |
+ "wv", |
|
199 |
+ "WavPack", |
|
200 |
+ sizeof(WVContext), |
|
201 |
+ wv_probe, |
|
202 |
+ wv_read_header, |
|
203 |
+ wv_read_packet, |
|
204 |
+ wv_read_close, |
|
205 |
+ pcm_read_seek, |
|
206 |
+}; |