summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Giacomelli <giac2000@hotmail.com>2007-11-22 05:32:26 +0000
committerMichael Giacomelli <giac2000@hotmail.com>2007-11-22 05:32:26 +0000
commitf861320177805e88811b58d177ed6c959f910db4 (patch)
tree35e8b044e261053d28ba5ec6a72d3317e486e848
parent73c5ec540fce4812c426a5dd491d766996ed32fd (diff)
downloadrockbox-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
-rw-r--r--apps/codecs/wma.c206
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
24CODEC_HEADER 24CODEC_HEADER
25 25
26int 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 */
304enum codec_status codec_main(void) 443enum 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;
535new_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
417done: 586done:
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
423exit: 591exit:
424 return retval; 592 return retval;
425} 593}