diff options
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 | } |