diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libasf/asf.c | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/libasf/asf.c')
-rw-r--r-- | lib/rbcodec/codecs/libasf/asf.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libasf/asf.c b/lib/rbcodec/codecs/libasf/asf.c new file mode 100644 index 0000000000..4e3235a422 --- /dev/null +++ b/lib/rbcodec/codecs/libasf/asf.c | |||
@@ -0,0 +1,435 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Dave Chapman | ||
11 | * | ||
12 | * ASF parsing code based on libasf by Juho Vähä-Herttua | ||
13 | * http://code.google.com/p/libasf/ libasf itself was based on the ASF | ||
14 | * parser in VLC - http://www.videolan.org/ | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or | ||
17 | * modify it under the terms of the GNU General Public License | ||
18 | * as published by the Free Software Foundation; either version 2 | ||
19 | * of the License, or (at your option) any later version. | ||
20 | * | ||
21 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
22 | * KIND, either express or implied. | ||
23 | * | ||
24 | ****************************************************************************/ | ||
25 | #include <inttypes.h> | ||
26 | #include "codeclib.h" | ||
27 | #include "asf.h" | ||
28 | |||
29 | /* Read an unaligned 32-bit little endian long from buffer. */ | ||
30 | static unsigned long get_long_le(void* buf) | ||
31 | { | ||
32 | unsigned char* p = (unsigned char*) buf; | ||
33 | |||
34 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
35 | } | ||
36 | |||
37 | /* Read an unaligned 16-bit little endian short from buffer. */ | ||
38 | static unsigned short get_short_le(void* buf) | ||
39 | { | ||
40 | unsigned char* p = (unsigned char*) buf; | ||
41 | |||
42 | return p[0] | (p[1] << 8); | ||
43 | } | ||
44 | |||
45 | #define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits) | ||
46 | |||
47 | #define GETVALUE2b(bits, data) \ | ||
48 | (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \ | ||
49 | 0 : *(data) : get_short_le(data) : get_long_le(data)) | ||
50 | |||
51 | int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, | ||
52 | asf_waveformatex_t* wfx) | ||
53 | { | ||
54 | uint8_t tmp8, packet_flags, packet_property; | ||
55 | int stream_id; | ||
56 | int ec_length, opaque_data, ec_length_type; | ||
57 | int datalen; | ||
58 | uint8_t data[18]; | ||
59 | uint8_t* datap; | ||
60 | uint32_t length; | ||
61 | uint32_t padding_length; | ||
62 | /* rockbox: comment 'set but unused' variables | ||
63 | uint32_t send_time; | ||
64 | uint16_t duration; | ||
65 | uint32_t media_object_number; | ||
66 | uint32_t media_object_offset; | ||
67 | */ | ||
68 | uint16_t payload_count; | ||
69 | int payload_length_type; | ||
70 | uint32_t payload_hdrlen; | ||
71 | int payload_datalen; | ||
72 | int multiple; | ||
73 | uint32_t replicated_length; | ||
74 | uint32_t bytesread = 0; | ||
75 | uint8_t* buf; | ||
76 | size_t bufsize; | ||
77 | int i; | ||
78 | /*DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);*/ | ||
79 | |||
80 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
81 | return ASF_ERROR_EOF; | ||
82 | } | ||
83 | bytesread++; | ||
84 | |||
85 | /* TODO: We need a better way to detect endofstream */ | ||
86 | if (tmp8 != 0x82) { | ||
87 | DEBUGF("Read failed: packet did not sync\n"); | ||
88 | return -1; | ||
89 | } | ||
90 | |||
91 | |||
92 | if (tmp8 & 0x80) { | ||
93 | ec_length = tmp8 & 0x0f; | ||
94 | opaque_data = (tmp8 >> 4) & 0x01; | ||
95 | ec_length_type = (tmp8 >> 5) & 0x03; | ||
96 | |||
97 | if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) { | ||
98 | DEBUGF("incorrect error correction flags\n"); | ||
99 | return ASF_ERROR_INVALID_VALUE; | ||
100 | } | ||
101 | |||
102 | /* Skip ec_data */ | ||
103 | ci->advance_buffer(ec_length); | ||
104 | bytesread += ec_length; | ||
105 | } else { | ||
106 | ec_length = 0; | ||
107 | } | ||
108 | |||
109 | if (ci->read_filebuf(&packet_flags, 1) == 0) { return ASF_ERROR_EOF; } | ||
110 | if (ci->read_filebuf(&packet_property, 1) == 0) { return ASF_ERROR_EOF; } | ||
111 | bytesread += 2; | ||
112 | |||
113 | datalen = GETLEN2b((packet_flags >> 1) & 0x03) + | ||
114 | GETLEN2b((packet_flags >> 3) & 0x03) + | ||
115 | GETLEN2b((packet_flags >> 5) & 0x03) + 6; | ||
116 | |||
117 | #if 0 | ||
118 | if (datalen > sizeof(data)) { | ||
119 | DEBUGF("Unexpectedly long datalen in data - %d\n",datalen); | ||
120 | return ASF_ERROR_OUTOFMEM; | ||
121 | } | ||
122 | #endif | ||
123 | |||
124 | if (ci->read_filebuf(data, datalen) == 0) { | ||
125 | return ASF_ERROR_EOF; | ||
126 | } | ||
127 | |||
128 | bytesread += datalen; | ||
129 | |||
130 | datap = data; | ||
131 | length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); | ||
132 | datap += GETLEN2b((packet_flags >> 5) & 0x03); | ||
133 | /* sequence value is not used */ | ||
134 | GETVALUE2b((packet_flags >> 1) & 0x03, datap); | ||
135 | datap += GETLEN2b((packet_flags >> 1) & 0x03); | ||
136 | padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap); | ||
137 | datap += GETLEN2b((packet_flags >> 3) & 0x03); | ||
138 | /* send_time = get_long_le(datap); */ | ||
139 | datap += 4; | ||
140 | /* duration = get_short_le(datap); */ | ||
141 | datap += 2; | ||
142 | /*DEBUGF("and duration %d ms\n", duration);*/ | ||
143 | |||
144 | /* this is really idiotic, packet length can (and often will) be | ||
145 | * undefined and we just have to use the header packet size as the size | ||
146 | * value */ | ||
147 | if (!((packet_flags >> 5) & 0x03)) { | ||
148 | length = wfx->packet_size; | ||
149 | } | ||
150 | |||
151 | /* this is also really idiotic, if packet length is smaller than packet | ||
152 | * size, we need to manually add the additional bytes into padding length | ||
153 | */ | ||
154 | if (length < wfx->packet_size) { | ||
155 | padding_length += wfx->packet_size - length; | ||
156 | length = wfx->packet_size; | ||
157 | } | ||
158 | |||
159 | if (length > wfx->packet_size) { | ||
160 | DEBUGF("packet with too big length value\n"); | ||
161 | return ASF_ERROR_INVALID_LENGTH; | ||
162 | } | ||
163 | |||
164 | /* check if we have multiple payloads */ | ||
165 | if (packet_flags & 0x01) { | ||
166 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
167 | return ASF_ERROR_EOF; | ||
168 | } | ||
169 | payload_count = tmp8 & 0x3f; | ||
170 | payload_length_type = (tmp8 >> 6) & 0x03; | ||
171 | bytesread++; | ||
172 | } else { | ||
173 | payload_count = 1; | ||
174 | payload_length_type = 0x02; /* not used */ | ||
175 | } | ||
176 | |||
177 | if (length < bytesread) { | ||
178 | DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread); | ||
179 | /* FIXME: should this be checked earlier? */ | ||
180 | return ASF_ERROR_INVALID_LENGTH; | ||
181 | } | ||
182 | |||
183 | |||
184 | /* We now parse the individual payloads, and move all payloads | ||
185 | belonging to our audio stream to a contiguous block, starting at | ||
186 | the location of the first payload. | ||
187 | */ | ||
188 | |||
189 | *audiobuf = NULL; | ||
190 | *audiobufsize = 0; | ||
191 | *packetlength = length - bytesread; | ||
192 | |||
193 | buf = ci->request_buffer(&bufsize, length); | ||
194 | datap = buf; | ||
195 | |||
196 | #define ASF_MAX_REQUEST (1L<<15) /* 32KB */ | ||
197 | if (bufsize != length && length >= ASF_MAX_REQUEST) { | ||
198 | /* This should only happen with packets larger than 32KB (the | ||
199 | guard buffer size). All the streams I've seen have | ||
200 | relatively small packets less than about 8KB), but I don't | ||
201 | know what is expected. | ||
202 | */ | ||
203 | DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n", | ||
204 | (int)length,(int)bufsize,(int)ci->curpos); | ||
205 | return -1; | ||
206 | } | ||
207 | |||
208 | for (i=0; i<payload_count; i++) { | ||
209 | stream_id = datap[0]&0x7f; | ||
210 | datap++; | ||
211 | bytesread++; | ||
212 | |||
213 | payload_hdrlen = GETLEN2b(packet_property & 0x03) + | ||
214 | GETLEN2b((packet_property >> 2) & 0x03) + | ||
215 | GETLEN2b((packet_property >> 4) & 0x03); | ||
216 | |||
217 | //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen); | ||
218 | #if 0 | ||
219 | /* TODO */ | ||
220 | if (payload_hdrlen > size) { | ||
221 | return ASF_ERROR_INVALID_LENGTH; | ||
222 | } | ||
223 | #endif | ||
224 | if (payload_hdrlen > sizeof(data)) { | ||
225 | DEBUGF("Unexpectedly long datalen in data - %d\n",datalen); | ||
226 | return ASF_ERROR_OUTOFMEM; | ||
227 | } | ||
228 | |||
229 | bytesread += payload_hdrlen; | ||
230 | /* media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap); */ | ||
231 | datap += GETLEN2b((packet_property >> 4) & 0x03); | ||
232 | /* media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap); */ | ||
233 | datap += GETLEN2b((packet_property >> 2) & 0x03); | ||
234 | replicated_length = GETVALUE2b(packet_property & 0x03, datap); | ||
235 | datap += GETLEN2b(packet_property & 0x03); | ||
236 | |||
237 | /* TODO: Validate replicated_length */ | ||
238 | /* TODO: Is the content of this important for us? */ | ||
239 | datap += replicated_length; | ||
240 | bytesread += replicated_length; | ||
241 | |||
242 | multiple = packet_flags & 0x01; | ||
243 | |||
244 | |||
245 | if (multiple) { | ||
246 | int x; | ||
247 | |||
248 | x = GETLEN2b(payload_length_type); | ||
249 | |||
250 | if (x != 2) { | ||
251 | /* in multiple payloads datalen should be a word */ | ||
252 | return ASF_ERROR_INVALID_VALUE; | ||
253 | } | ||
254 | |||
255 | #if 0 | ||
256 | if (skip + tmp > datalen) { | ||
257 | /* not enough data */ | ||
258 | return ASF_ERROR_INVALID_LENGTH; | ||
259 | } | ||
260 | #endif | ||
261 | payload_datalen = GETVALUE2b(payload_length_type, datap); | ||
262 | datap += x; | ||
263 | bytesread += x; | ||
264 | } else { | ||
265 | payload_datalen = length - bytesread - padding_length; | ||
266 | } | ||
267 | |||
268 | if (replicated_length==1) | ||
269 | datap++; | ||
270 | |||
271 | if (stream_id == wfx->audiostream) | ||
272 | { | ||
273 | if (*audiobuf == NULL) { | ||
274 | /* The first payload can stay where it is */ | ||
275 | *audiobuf = datap; | ||
276 | *audiobufsize = payload_datalen; | ||
277 | } else { | ||
278 | /* The second and subsequent payloads in this packet | ||
279 | that belong to the audio stream need to be moved to be | ||
280 | contiguous with the first payload. | ||
281 | */ | ||
282 | memmove(*audiobuf + *audiobufsize, datap, payload_datalen); | ||
283 | *audiobufsize += payload_datalen; | ||
284 | } | ||
285 | } | ||
286 | datap += payload_datalen; | ||
287 | bytesread += payload_datalen; | ||
288 | } | ||
289 | |||
290 | if (*audiobuf != NULL) | ||
291 | return 1; | ||
292 | else | ||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | |||
297 | int asf_get_timestamp(int *duration) | ||
298 | { | ||
299 | uint8_t tmp8, packet_flags, packet_property; | ||
300 | int ec_length, opaque_data, ec_length_type; | ||
301 | int datalen; | ||
302 | uint8_t data[18]; | ||
303 | uint8_t* datap; | ||
304 | /* rockbox: comment 'set but unused' variables | ||
305 | uint32_t length; | ||
306 | uint32_t padding_length; | ||
307 | */ | ||
308 | uint32_t send_time; | ||
309 | static int packet_count = 0; | ||
310 | |||
311 | uint32_t bytesread = 0; | ||
312 | packet_count++; | ||
313 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
314 | DEBUGF("ASF ERROR (EOF?)\n"); | ||
315 | return ASF_ERROR_EOF; | ||
316 | } | ||
317 | bytesread++; | ||
318 | |||
319 | /* TODO: We need a better way to detect endofstream */ | ||
320 | if (tmp8 != 0x82) { | ||
321 | DEBUGF("Get timestamp: Detected end of stream\n"); | ||
322 | return ASF_ERROR_EOF; | ||
323 | } | ||
324 | |||
325 | |||
326 | if (tmp8 & 0x80) { | ||
327 | ec_length = tmp8 & 0x0f; | ||
328 | opaque_data = (tmp8 >> 4) & 0x01; | ||
329 | ec_length_type = (tmp8 >> 5) & 0x03; | ||
330 | |||
331 | if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) { | ||
332 | DEBUGF("incorrect error correction flags\n"); | ||
333 | return ASF_ERROR_INVALID_VALUE; | ||
334 | } | ||
335 | |||
336 | /* Skip ec_data */ | ||
337 | ci->advance_buffer(ec_length); | ||
338 | bytesread += ec_length; | ||
339 | } else { | ||
340 | ec_length = 0; | ||
341 | } | ||
342 | |||
343 | if (ci->read_filebuf(&packet_flags, 1) == 0) { | ||
344 | DEBUGF("Detected end of stream 2\n"); | ||
345 | return ASF_ERROR_EOF; | ||
346 | } | ||
347 | |||
348 | if (ci->read_filebuf(&packet_property, 1) == 0) { | ||
349 | DEBUGF("Detected end of stream3\n"); | ||
350 | return ASF_ERROR_EOF; | ||
351 | } | ||
352 | bytesread += 2; | ||
353 | |||
354 | datalen = GETLEN2b((packet_flags >> 1) & 0x03) + | ||
355 | GETLEN2b((packet_flags >> 3) & 0x03) + | ||
356 | GETLEN2b((packet_flags >> 5) & 0x03) + 6; | ||
357 | |||
358 | if (ci->read_filebuf(data, datalen) == 0) { | ||
359 | DEBUGF("Detected end of stream4\n"); | ||
360 | return ASF_ERROR_EOF; | ||
361 | } | ||
362 | |||
363 | bytesread += datalen; | ||
364 | |||
365 | datap = data; | ||
366 | /* length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); */ | ||
367 | datap += GETLEN2b((packet_flags >> 5) & 0x03); | ||
368 | |||
369 | /* sequence value is not used */ | ||
370 | GETVALUE2b((packet_flags >> 1) & 0x03, datap); | ||
371 | datap += GETLEN2b((packet_flags >> 1) & 0x03); | ||
372 | /* padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap); */ | ||
373 | datap += GETLEN2b((packet_flags >> 3) & 0x03); | ||
374 | send_time = get_long_le(datap); | ||
375 | datap += 4; | ||
376 | *duration = get_short_le(datap); | ||
377 | |||
378 | /*the asf_get_timestamp function advances us 12-13 bytes past the packet start, | ||
379 | need to undo this here so that we stay synced with the packet*/ | ||
380 | ci->seek_buffer(ci->curpos-bytesread); | ||
381 | |||
382 | return send_time; | ||
383 | } | ||
384 | |||
385 | /*entry point for seeks*/ | ||
386 | int asf_seek(int ms, asf_waveformatex_t* wfx) | ||
387 | { | ||
388 | int time, duration, delta, temp, count=0; | ||
389 | |||
390 | /*estimate packet number from bitrate*/ | ||
391 | int initial_packet = ci->curpos/wfx->packet_size; | ||
392 | int packet_num = (((int64_t)ms)*(wfx->bitrate>>3))/wfx->packet_size/1000; | ||
393 | int last_packet = ci->id3->filesize / wfx->packet_size; | ||
394 | |||
395 | if (packet_num > last_packet) { | ||
396 | packet_num = last_packet; | ||
397 | } | ||
398 | |||
399 | /*calculate byte address of the start of that packet*/ | ||
400 | int packet_offset = packet_num*wfx->packet_size; | ||
401 | |||
402 | /*seek to estimated packet*/ | ||
403 | ci->seek_buffer(ci->id3->first_frame_offset+packet_offset); | ||
404 | temp = ms; | ||
405 | while (1) | ||
406 | { | ||
407 | /*for very large files it can be difficult and unimportant to find the exact packet*/ | ||
408 | count++; | ||
409 | |||
410 | /*check the time stamp of our packet*/ | ||
411 | time = asf_get_timestamp(&duration); | ||
412 | /*DEBUGF("seeked to %d ms with duration %d\n", time, duration);*/ | ||
413 | |||
414 | if (time < 0) { | ||
415 | /*unknown error, try to recover*/ | ||
416 | DEBUGF("UKNOWN SEEK ERROR\n"); | ||
417 | ci->seek_buffer(ci->id3->first_frame_offset+initial_packet*wfx->packet_size); | ||
418 | /*seek failed so return time stamp of the initial packet*/ | ||
419 | return asf_get_timestamp(&duration); | ||
420 | } | ||
421 | |||
422 | if ((time+duration>=ms && time<=ms) || count > 10) { | ||
423 | /*DEBUGF("Found our packet! Now at %d packet\n", packet_num);*/ | ||
424 | return time; | ||
425 | } else { | ||
426 | /*seek again*/ | ||
427 | delta = ms-time; | ||
428 | /*estimate new packet number from bitrate and our current position*/ | ||
429 | temp += delta; | ||
430 | packet_num = ((temp/1000)*(wfx->bitrate>>3) - (wfx->packet_size>>1))/wfx->packet_size; //round down! | ||
431 | packet_offset = packet_num*wfx->packet_size; | ||
432 | ci->seek_buffer(ci->id3->first_frame_offset+packet_offset); | ||
433 | } | ||
434 | } | ||
435 | } | ||