diff options
author | Michael Giacomelli <giac2000@hotmail.com> | 2007-11-22 05:32:26 +0000 |
---|---|---|
committer | Michael Giacomelli <giac2000@hotmail.com> | 2007-11-22 05:32:26 +0000 |
commit | f861320177805e88811b58d177ed6c959f910db4 (patch) | |
tree | 35e8b044e261053d28ba5ec6a72d3317e486e848 /apps/codecs/wma.c | |
parent | 73c5ec540fce4812c426a5dd491d766996ed32fd (diff) | |
download | rockbox-f861320177805e88811b58d177ed6c959f910db4.tar.gz rockbox-f861320177805e88811b58d177ed6c959f910db4.zip |
Add seeking support for wma. Works quite well on my Sansa, and theres some effort to catch and recover from seeking errors, but theres likely to be problems in some files. Also, add Thom's idea to skip past some errors in wma streams rather then just giving up.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15753 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/wma.c')
-rw-r--r-- | apps/codecs/wma.c | 206 |
1 files changed, 187 insertions, 19 deletions
diff --git a/apps/codecs/wma.c b/apps/codecs/wma.c index 958cf1525b..629194c5e2 100644 --- a/apps/codecs/wma.c +++ b/apps/codecs/wma.c | |||
@@ -23,6 +23,8 @@ | |||
23 | 23 | ||
24 | CODEC_HEADER | 24 | CODEC_HEADER |
25 | 25 | ||
26 | int packet_count=0; | ||
27 | |||
26 | /* The output buffer containing the decoded samples (channels 0 and 1) | 28 | /* The output buffer containing the decoded samples (channels 0 and 1) |
27 | BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2. | 29 | BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2. |
28 | */ | 30 | */ |
@@ -91,6 +93,7 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen | |||
91 | uint8_t* buf; | 93 | uint8_t* buf; |
92 | size_t bufsize; | 94 | size_t bufsize; |
93 | int i; | 95 | int i; |
96 | DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos); | ||
94 | 97 | ||
95 | if (ci->read_filebuf(&tmp8, 1) == 0) { | 98 | if (ci->read_filebuf(&tmp8, 1) == 0) { |
96 | return ASF_ERROR_EOF; | 99 | return ASF_ERROR_EOF; |
@@ -99,7 +102,10 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen | |||
99 | 102 | ||
100 | //DEBUGF("tmp8=0x%02x\n",tmp8); | 103 | //DEBUGF("tmp8=0x%02x\n",tmp8); |
101 | /* TODO: We need a better way to detect endofstream */ | 104 | /* TODO: We need a better way to detect endofstream */ |
102 | if (tmp8 != 0x82) { return -1; } | 105 | if (tmp8 != 0x82) { |
106 | DEBUGF("Read failed: packet did not sync\n"); | ||
107 | return -1; | ||
108 | } | ||
103 | 109 | ||
104 | 110 | ||
105 | if (tmp8 & 0x80) { | 111 | if (tmp8 & 0x80) { |
@@ -152,6 +158,7 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen | |||
152 | datap += 4; | 158 | datap += 4; |
153 | duration = get_short_le(datap); | 159 | duration = get_short_le(datap); |
154 | datap += 2; | 160 | datap += 2; |
161 | DEBUGF("and duration %d ms\n", duration); | ||
155 | 162 | ||
156 | /* this is really idiotic, packet length can (and often will) be | 163 | /* this is really idiotic, packet length can (and often will) be |
157 | * undefined and we just have to use the header packet size as the size | 164 | * undefined and we just have to use the header packet size as the size |
@@ -252,6 +259,7 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen | |||
252 | 259 | ||
253 | multiple = packet_flags & 0x01; | 260 | multiple = packet_flags & 0x01; |
254 | 261 | ||
262 | |||
255 | if (multiple) { | 263 | if (multiple) { |
256 | int x; | 264 | int x; |
257 | 265 | ||
@@ -300,10 +308,140 @@ static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlen | |||
300 | return 0; | 308 | return 0; |
301 | } | 309 | } |
302 | 310 | ||
311 | |||
312 | static int get_timestamp(int *duration){ | ||
313 | |||
314 | uint8_t tmp8, packet_flags, packet_property; | ||
315 | //int stream_id; | ||
316 | int ec_length, opaque_data, ec_length_type; | ||
317 | int datalen; | ||
318 | uint8_t data[18]; | ||
319 | uint8_t* datap; | ||
320 | uint32_t length; | ||
321 | uint32_t padding_length; | ||
322 | uint32_t send_time; | ||
323 | |||
324 | //uint16_t payload_count; | ||
325 | uint32_t bytesread = 0; | ||
326 | packet_count++; | ||
327 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
328 | DEBUGF("ASF ERROR (EOF?)\n"); | ||
329 | return ASF_ERROR_EOF; | ||
330 | } | ||
331 | bytesread++; | ||
332 | |||
333 | /* TODO: We need a better way to detect endofstream */ | ||
334 | if (tmp8 != 0x82) { | ||
335 | DEBUGF("Get timestamp: Detected end of stream\n"); | ||
336 | |||
337 | return ASF_ERROR_EOF; } | ||
338 | |||
339 | |||
340 | if (tmp8 & 0x80) { | ||
341 | ec_length = tmp8 & 0x0f; | ||
342 | opaque_data = (tmp8 >> 4) & 0x01; | ||
343 | ec_length_type = (tmp8 >> 5) & 0x03; | ||
344 | |||
345 | if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) { | ||
346 | DEBUGF("incorrect error correction flags\n"); | ||
347 | return ASF_ERROR_INVALID_VALUE; | ||
348 | } | ||
349 | |||
350 | /* Skip ec_data */ | ||
351 | ci->advance_buffer(ec_length); | ||
352 | bytesread += ec_length; | ||
353 | } else { | ||
354 | ec_length = 0; | ||
355 | } | ||
356 | |||
357 | if (ci->read_filebuf(&packet_flags, 1) == 0) { DEBUGF("Detected end of stream 2\n"); return ASF_ERROR_EOF; } | ||
358 | if (ci->read_filebuf(&packet_property, 1) == 0) {DEBUGF("Detected end of stream3\n"); return ASF_ERROR_EOF; } | ||
359 | bytesread += 2; | ||
360 | |||
361 | datalen = GETLEN2b((packet_flags >> 1) & 0x03) + | ||
362 | GETLEN2b((packet_flags >> 3) & 0x03) + | ||
363 | GETLEN2b((packet_flags >> 5) & 0x03) + 6; | ||
364 | |||
365 | if (ci->read_filebuf(data, datalen) == 0) { | ||
366 | DEBUGF("Detected end of stream4\n"); | ||
367 | return ASF_ERROR_EOF; | ||
368 | } | ||
369 | |||
370 | bytesread += datalen; | ||
371 | |||
372 | datap = data; | ||
373 | length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); | ||
374 | datap += GETLEN2b((packet_flags >> 5) & 0x03); | ||
375 | /* sequence value is not used */ | ||
376 | GETVALUE2b((packet_flags >> 1) & 0x03, datap); | ||
377 | datap += GETLEN2b((packet_flags >> 1) & 0x03); | ||
378 | padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap); | ||
379 | datap += GETLEN2b((packet_flags >> 3) & 0x03); | ||
380 | send_time = get_long_le(datap); | ||
381 | datap += 4; | ||
382 | *duration = get_short_le(datap); | ||
383 | |||
384 | return send_time; | ||
385 | } | ||
386 | |||
387 | /*entry point for seeks*/ | ||
388 | static int seek(int ms, asf_waveformatex_t* wfx){ | ||
389 | int time, duration, delta, temp; | ||
390 | |||
391 | /*estimate packet number from bitrate*/ | ||
392 | int initial_packet = ci->curpos/wfx->packet_size; | ||
393 | int packet_num = (ms*(wfx->bitrate>>3))/wfx->packet_size/1000; | ||
394 | int last_packet = ci->id3->filesize / wfx->packet_size; | ||
395 | |||
396 | if(packet_num > last_packet){ | ||
397 | packet_num = last_packet; | ||
398 | } | ||
399 | |||
400 | /*calculate byte address of the start of that packet*/ | ||
401 | int packet_offset = packet_num*wfx->packet_size; | ||
402 | |||
403 | /*seek to estimated packet*/ | ||
404 | ci->seek_buffer(ci->id3->first_frame_offset+packet_offset); | ||
405 | temp = ms; | ||
406 | while(1){ | ||
407 | |||
408 | /*check the time stamp of our packet*/ | ||
409 | time = get_timestamp(&duration); | ||
410 | DEBUGF("seeked to %d ms with duration %d\n", time, duration); | ||
411 | |||
412 | if(time < 0){ | ||
413 | /*unknown error, try to recover*/ | ||
414 | DEBUGF("UKNOWN SEEK ERROR\n"); | ||
415 | ci->seek_buffer(ci->id3->first_frame_offset+initial_packet*wfx->packet_size); | ||
416 | return ms; | ||
417 | } | ||
418 | |||
419 | if(time+duration>=ms && time<=ms){ | ||
420 | /*the get_timestamp function advances us 12 bytes past the packet start*/ | ||
421 | ci->seek_buffer(ci->curpos-12); | ||
422 | DEBUGF("Found our packet! Now at %d packet\n", packet_num); | ||
423 | return time; | ||
424 | }else { | ||
425 | /*seek again*/ | ||
426 | delta = ms-time; | ||
427 | DEBUGF("delta is %d ms\n", delta); | ||
428 | |||
429 | /*estimate new packet number from bitrate and our current position*/ | ||
430 | temp += delta; | ||
431 | packet_num = (temp*(wfx->bitrate>>3)/1000 - (wfx->packet_size>>1))/wfx->packet_size; //round down! | ||
432 | DEBUGF("updated Packet_num is %d\n", packet_num); | ||
433 | packet_offset = packet_num*wfx->packet_size; | ||
434 | |||
435 | ci->seek_buffer(ci->id3->first_frame_offset+packet_offset); | ||
436 | } | ||
437 | } | ||
438 | } | ||
439 | |||
440 | |||
441 | |||
303 | /* this is the codec entry point */ | 442 | /* this is the codec entry point */ |
304 | enum codec_status codec_main(void) | 443 | enum codec_status codec_main(void) |
305 | { | 444 | { |
306 | uint32_t samplesdone; | ||
307 | uint32_t elapsedtime; | 445 | uint32_t elapsedtime; |
308 | int retval; | 446 | int retval; |
309 | asf_waveformatex_t wfx; | 447 | asf_waveformatex_t wfx; |
@@ -314,6 +452,7 @@ enum codec_status codec_main(void) | |||
314 | uint8_t* audiobuf; | 452 | uint8_t* audiobuf; |
315 | int audiobufsize; | 453 | int audiobufsize; |
316 | int packetlength; | 454 | int packetlength; |
455 | int errcount = 0; | ||
317 | 456 | ||
318 | /* Generic codec initialisation */ | 457 | /* Generic codec initialisation */ |
319 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512); | 458 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512); |
@@ -359,7 +498,7 @@ next_track: | |||
359 | /* The main decoding loop */ | 498 | /* The main decoding loop */ |
360 | 499 | ||
361 | currentframe = 0; | 500 | currentframe = 0; |
362 | samplesdone = 0; | 501 | elapsedtime = 0; |
363 | 502 | ||
364 | DEBUGF("**************** IN WMA.C ******************\n"); | 503 | DEBUGF("**************** IN WMA.C ******************\n"); |
365 | wma_decode_init(&wmadec,&wfx); | 504 | wma_decode_init(&wmadec,&wfx); |
@@ -373,18 +512,44 @@ next_track: | |||
373 | } | 512 | } |
374 | 513 | ||
375 | /* Deal with any pending seek requests */ | 514 | /* Deal with any pending seek requests */ |
376 | if (ci->seek_time) | 515 | if (ci->seek_time){ |
377 | { | 516 | |
378 | /* Ignore all seeks for now, unless for the start of the track */ | ||
379 | if (ci->seek_time == 1) { | 517 | if (ci->seek_time == 1) { |
380 | ci->seek_complete(); | 518 | ci->seek_complete(); |
381 | goto next_track; /* Pretend you never saw this... */ | 519 | goto next_track; /* Pretend you never saw this... */ |
382 | } | 520 | } |
383 | ci->seek_complete(); | ||
384 | } | ||
385 | 521 | ||
522 | elapsedtime = seek(ci->seek_time, &wfx); | ||
523 | if(elapsedtime < 1){ | ||
524 | ci->seek_complete(); | ||
525 | goto next_track; | ||
526 | } | ||
527 | ci->set_elapsed(elapsedtime); | ||
528 | |||
529 | /*flush the wma decoder state*/ | ||
530 | wmadec.last_superframe_len = 0; | ||
531 | wmadec.last_bitoffset = 0; | ||
532 | ci->seek_complete(); | ||
533 | } | ||
534 | errcount = 0; | ||
535 | new_packet: | ||
386 | res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); | 536 | res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); |
387 | if (res > 0) { | 537 | if(res < 0){ |
538 | |||
539 | /* We'll try to recover from a parse error a certain number of | ||
540 | * times. If we succeed, the error counter will be reset. | ||
541 | */ | ||
542 | |||
543 | errcount++; | ||
544 | DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount); | ||
545 | if (errcount > 5) { | ||
546 | goto done; | ||
547 | } else { | ||
548 | ci->advance_buffer(packetlength); | ||
549 | goto new_packet; | ||
550 | } | ||
551 | |||
552 | }else if (res > 0) { | ||
388 | wma_decode_superframe_init(&wmadec, | 553 | wma_decode_superframe_init(&wmadec, |
389 | audiobuf, audiobufsize); | 554 | audiobuf, audiobufsize); |
390 | 555 | ||
@@ -393,20 +558,24 @@ next_track: | |||
393 | wmares = wma_decode_superframe_frame(&wmadec, | 558 | wmares = wma_decode_superframe_frame(&wmadec, |
394 | decoded, | 559 | decoded, |
395 | audiobuf, audiobufsize); | 560 | audiobuf, audiobufsize); |
396 | |||
397 | ci->yield (); | 561 | ci->yield (); |
398 | 562 | ||
399 | if (wmares < 0) | 563 | if (wmares < 0){ |
400 | { | 564 | /* Do the above, but for errors in decode.*/ |
401 | LOGF("WMA decode error %d\n",wmares); | 565 | errcount++; |
402 | goto done; | 566 | DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount); |
567 | if (errcount > 5) { | ||
568 | goto done; | ||
569 | } else { | ||
570 | ci->advance_buffer(packetlength); | ||
571 | goto new_packet; | ||
572 | } | ||
403 | } else if (wmares > 0) { | 573 | } else if (wmares > 0) { |
404 | ci->pcmbuf_insert(decoded, NULL, wmares); | 574 | ci->pcmbuf_insert(decoded, NULL, wmares); |
405 | samplesdone += wmares; | 575 | elapsedtime += (wmares*10)/(wfx.rate/100); |
406 | elapsedtime = (samplesdone*10)/(wfx.rate/100); | ||
407 | ci->set_elapsed(elapsedtime); | 576 | ci->set_elapsed(elapsedtime); |
408 | } | 577 | } |
409 | ci->yield (); | 578 | ci->yield(); |
410 | } | 579 | } |
411 | } | 580 | } |
412 | 581 | ||
@@ -415,11 +584,10 @@ next_track: | |||
415 | retval = CODEC_OK; | 584 | retval = CODEC_OK; |
416 | 585 | ||
417 | done: | 586 | done: |
418 | LOGF("WMA: Decoded %ld samples\n",samplesdone); | 587 | LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000); |
419 | 588 | ||
420 | if (ci->request_next_track()) | 589 | if (ci->request_next_track()) |
421 | goto next_track; | 590 | goto next_track; |
422 | |||
423 | exit: | 591 | exit: |
424 | return retval; | 592 | return retval; |
425 | } | 593 | } |