summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libasf/asf.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libasf/asf.c')
-rw-r--r--lib/rbcodec/codecs/libasf/asf.c435
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. */
30static 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. */
38static 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
51int 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
297int 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*/
386int 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}