diff options
Diffstat (limited to 'apps/plugins/mpegplayer/mpegplayer.c')
-rw-r--r-- | apps/plugins/mpegplayer/mpegplayer.c | 770 |
1 files changed, 596 insertions, 174 deletions
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index 54fdf05355..128eb268a6 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c | |||
@@ -110,6 +110,7 @@ FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz | |||
110 | #include "mpeg_settings.h" | 110 | #include "mpeg_settings.h" |
111 | #include "video_out.h" | 111 | #include "video_out.h" |
112 | #include "../../codecs/libmad/mad.h" | 112 | #include "../../codecs/libmad/mad.h" |
113 | #include "splash.h" | ||
113 | 114 | ||
114 | PLUGIN_HEADER | 115 | PLUGIN_HEADER |
115 | PLUGIN_IRAM_DECLARE | 116 | PLUGIN_IRAM_DECLARE |
@@ -198,11 +199,8 @@ typedef struct | |||
198 | uint8_t* curr_packet_end; /* Current stream packet end */ | 199 | uint8_t* curr_packet_end; /* Current stream packet end */ |
199 | 200 | ||
200 | uint8_t* prev_packet; /* Previous stream packet beginning */ | 201 | uint8_t* prev_packet; /* Previous stream packet beginning */ |
201 | uint8_t* next_packet; /* Next stream packet beginning */ | 202 | size_t prev_packet_length; /* Lenth of previous packet */ |
202 | 203 | size_t buffer_remaining; /* How much data is left in the buffer */ | |
203 | size_t guard_bytes; /* Number of bytes in guardbuf used */ | ||
204 | uint64_t buffer_tail; /* Accumulation of bytes added */ | ||
205 | uint64_t buffer_head; /* Accumulation of bytes removed */ | ||
206 | uint32_t curr_pts; /* Current presentation timestamp */ | 204 | uint32_t curr_pts; /* Current presentation timestamp */ |
207 | uint32_t curr_time; /* Current time in samples */ | 205 | uint32_t curr_time; /* Current time in samples */ |
208 | uint32_t tagged; /* curr_pts is valid */ | 206 | uint32_t tagged; /* curr_pts is valid */ |
@@ -301,8 +299,7 @@ static intptr_t str_send_msg(Stream *str, int id, intptr_t data) | |||
301 | return str->dispatch_fn(str, msg); | 299 | return str->dispatch_fn(str, msg); |
302 | #endif | 300 | #endif |
303 | 301 | ||
304 | /* Only one thread at a time, please - only one core may safely send | 302 | /* Only one thread at a time, please */ |
305 | right now */ | ||
306 | rb->spinlock_lock(&str->msg_lock); | 303 | rb->spinlock_lock(&str->msg_lock); |
307 | 304 | ||
308 | str->ev.id = id; | 305 | str->ev.id = id; |
@@ -333,13 +330,62 @@ static intptr_t str_send_msg(Stream *str, int id, intptr_t data) | |||
333 | /* NOTE: Putting the following variables in IRAM cause audio corruption | 330 | /* NOTE: Putting the following variables in IRAM cause audio corruption |
334 | on the ipod (reason unknown) | 331 | on the ipod (reason unknown) |
335 | */ | 332 | */ |
336 | static uint8_t *disk_buf IBSS_ATTR; | 333 | static uint8_t *disk_buf_start IBSS_ATTR; /* Start pointer */ |
337 | static uint8_t *disk_buf_end IBSS_ATTR; | 334 | static uint8_t *disk_buf_end IBSS_ATTR; /* End of buffer pointer less |
338 | static uint8_t *disk_buf_tail IBSS_ATTR; | 335 | MPEG_GUARDBUF_SIZE. The |
339 | static size_t buffer_size IBSS_ATTR; | 336 | guard space is used to wrap |
337 | data at the buffer start to | ||
338 | pass continuous data | ||
339 | packets */ | ||
340 | static uint8_t *disk_buf_tail IBSS_ATTR; /* Location of last data + 1 | ||
341 | filled into the buffer */ | ||
342 | static size_t disk_buf_size IBSS_ATTR; /* The total buffer length | ||
343 | including the guard | ||
344 | space */ | ||
345 | static size_t file_remaining IBSS_ATTR; | ||
346 | |||
347 | #if NUM_CORES > 1 | ||
348 | /* Some stream variables are shared between cores */ | ||
349 | struct mutex stream_lock IBSS_ATTR; | ||
350 | static inline void init_stream_lock(void) | ||
351 | { rb->spinlock_init(&stream_lock); } | ||
352 | static inline void lock_stream(void) | ||
353 | { rb->spinlock_lock(&stream_lock); } | ||
354 | static inline void unlock_stream(void) | ||
355 | { rb->spinlock_unlock(&stream_lock); } | ||
356 | #else | ||
357 | /* No RMW issue here */ | ||
358 | static inline void init_stream_lock(void) | ||
359 | { } | ||
360 | static inline void lock_stream(void) | ||
361 | { } | ||
362 | static inline void unlock_stream(void) | ||
363 | { } | ||
364 | #endif | ||
365 | |||
366 | static int audio_sync_start IBSS_ATTR; /* If 0, the audio thread | ||
367 | yields waiting on the video | ||
368 | thread to synchronize with | ||
369 | the stream */ | ||
370 | static uint32_t audio_sync_time IBSS_ATTR; /* The time that the video | ||
371 | thread has reached after | ||
372 | synchronizing. The | ||
373 | audio thread now needs | ||
374 | to advance to this | ||
375 | time */ | ||
376 | static int video_sync_start IBSS_ATTR; /* While 0, the video thread | ||
377 | yields until the audio | ||
378 | thread has reached the | ||
379 | audio_sync_time */ | ||
380 | static int video_thumb_print IBSS_ATTR; /* If 1, the video thread is | ||
381 | only decoding one frame for | ||
382 | use in the menu. If 0, | ||
383 | normal operation */ | ||
384 | static int play_time IBSS_ATTR; /* The movie time as represented by | ||
385 | the maximum audio PTS tag in the | ||
386 | stream converted to half minutes */ | ||
387 | char *filename; /* hack for resume time storage */ | ||
340 | 388 | ||
341 | #define MSG_BUFFER_NEARLY_EMPTY 1 | ||
342 | #define MSG_EXIT_REQUESTED 2 | ||
343 | 389 | ||
344 | /* Various buffers */ | 390 | /* Various buffers */ |
345 | /* TODO: Can we reduce the PCM buffer size? */ | 391 | /* TODO: Can we reduce the PCM buffer size? */ |
@@ -350,7 +396,7 @@ static size_t buffer_size IBSS_ATTR; | |||
350 | #define LIBMPEG2BUFFER_SIZE (2*1024*1024) | 396 | #define LIBMPEG2BUFFER_SIZE (2*1024*1024) |
351 | 397 | ||
352 | /* 65536+6 is required since each PES has a 6 byte header with a 16 bit packet length field */ | 398 | /* 65536+6 is required since each PES has a 6 byte header with a 16 bit packet length field */ |
353 | #define MPEG_GUARDBUF_SIZE (64*1024+1024) /* Keep a bit extra - excessive for now */ | 399 | #define MPEG_GUARDBUF_SIZE (65*1024) /* Keep a bit extra - excessive for now */ |
354 | #define MPEG_LOW_WATERMARK (1024*1024) | 400 | #define MPEG_LOW_WATERMARK (1024*1024) |
355 | 401 | ||
356 | static void pcm_playback_play_pause(bool play); | 402 | static void pcm_playback_play_pause(bool play); |
@@ -471,8 +517,47 @@ static void init_mad(void* mad_frame_overlap) | |||
471 | ((p)[b3] << 6) | \ | 517 | ((p)[b3] << 6) | \ |
472 | ((p)[b4] >> 2 ))) | 518 | ((p)[b4] >> 2 ))) |
473 | 519 | ||
474 | /* This function demuxes the streams and gives the next stream data pointer */ | 520 | /* This function synchronizes the mpeg stream. The function returns |
475 | static void get_next_data( Stream* str ) | 521 | true on error */ |
522 | bool sync_data_stream(uint8_t **p) | ||
523 | { | ||
524 | for (;;) | ||
525 | { | ||
526 | while ( !CMP_4_CONST(*p, PACK_START_CODE) && (*p) < disk_buf_tail ) | ||
527 | (*p)++; | ||
528 | if ( (*p) >= disk_buf_tail ) | ||
529 | break; | ||
530 | uint8_t *p_save = (*p); | ||
531 | if ( ((*p)[4] & 0xc0) == 0x40 ) /* mpeg-2 */ | ||
532 | (*p) += 14 + ((*p)[13] & 7); | ||
533 | else if ( ((*p)[4] & 0xf0) == 0x20 ) /* mpeg-1 */ | ||
534 | (*p) += 12; | ||
535 | else | ||
536 | (*p) += 5; | ||
537 | if ( (*p) >= disk_buf_tail ) | ||
538 | break; | ||
539 | if ( CMP_3_CONST(*p, PACKET_START_CODE_PREFIX) ) | ||
540 | { | ||
541 | (*p) = p_save; | ||
542 | break; | ||
543 | } | ||
544 | else | ||
545 | (*p) = p_save+1; | ||
546 | } | ||
547 | |||
548 | if ( (*p) >= disk_buf_tail ) | ||
549 | return true; | ||
550 | else | ||
551 | return false; | ||
552 | } | ||
553 | |||
554 | /* This function demuxes the streams and gives the next stream data | ||
555 | pointer. Type 0 is normal operation. Type 1 and 2 have been added | ||
556 | for rapid seeks into the data stream. Type 1 and 2 ignore the | ||
557 | video_sync_start state (a signal to yield for refilling the | ||
558 | buffer). Type 1 will append more data to the buffer tail (minumal | ||
559 | bufer size reads that are increased only as needed). */ | ||
560 | static int get_next_data( Stream* str, uint8_t type ) | ||
476 | { | 561 | { |
477 | uint8_t *p; | 562 | uint8_t *p; |
478 | uint8_t *header; | 563 | uint8_t *header; |
@@ -481,30 +566,49 @@ static void get_next_data( Stream* str ) | |||
481 | static int mpeg1_skip_table[16] = | 566 | static int mpeg1_skip_table[16] = |
482 | { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | 567 | { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
483 | 568 | ||
484 | if (str->curr_packet_end == NULL) | 569 | if ( (p=str->curr_packet_end) == NULL) |
485 | { | 570 | p = disk_buf_start; |
486 | /* What does this do? */ | ||
487 | while ((p = disk_buf) == NULL) | ||
488 | { | ||
489 | rb->lcd_putsxy(0,LCD_HEIGHT-10,"FREEZE!"); | ||
490 | rb->lcd_update(); | ||
491 | rb->sleep(HZ); | ||
492 | } | ||
493 | } | ||
494 | else | ||
495 | { | ||
496 | p = str->curr_packet_end; | ||
497 | } | ||
498 | 571 | ||
499 | while (1) | 572 | while (1) |
500 | { | 573 | { |
501 | int length, bytes; | 574 | int length, bytes; |
502 | 575 | ||
503 | if (p >= disk_buf_end) | 576 | /* Yield for buffer filling */ |
577 | if ( (type == 0) && (str->buffer_remaining < 120*1024) && (file_remaining > 0) ) | ||
578 | while ( (str->buffer_remaining < 512*1024) && (file_remaining > 0) ) | ||
579 | rb->yield(); | ||
580 | |||
581 | /* The packet start position (plus an arbitrary header length) | ||
582 | has exceeded the amount of data in the buffer */ | ||
583 | if ( type == 1 && (p+50) >= disk_buf_tail ) | ||
504 | { | 584 | { |
505 | p = disk_buf + (p - disk_buf_end); | 585 | DEBUGF("disk buffer overflow\n"); |
586 | return 1; | ||
506 | } | 587 | } |
507 | 588 | ||
589 | /* are we at the end of file? */ | ||
590 | { | ||
591 | size_t tmp_length; | ||
592 | if (p < str->prev_packet) | ||
593 | tmp_length = (disk_buf_end - str->prev_packet) + | ||
594 | (p - disk_buf_start); | ||
595 | else | ||
596 | tmp_length = (p - str->prev_packet); | ||
597 | if (0 == str->buffer_remaining-tmp_length-str->prev_packet_length) | ||
598 | { | ||
599 | str->curr_packet_end = str->curr_packet = NULL; | ||
600 | break; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | /* wrap the disk buffer */ | ||
605 | if (p >= disk_buf_end) | ||
606 | p = disk_buf_start + (p - disk_buf_end); | ||
607 | |||
608 | /* wrap packet header if needed */ | ||
609 | if ( (p+50) >= disk_buf_end ) | ||
610 | rb->memcpy(disk_buf_end, disk_buf_start, 50); | ||
611 | |||
508 | /* Pack header, skip it */ | 612 | /* Pack header, skip it */ |
509 | if (CMP_4_CONST(p, PACK_START_CODE)) | 613 | if (CMP_4_CONST(p, PACK_START_CODE)) |
510 | { | 614 | { |
@@ -521,7 +625,6 @@ static void get_next_data( Stream* str ) | |||
521 | rb->splash( 30, "Weird Pack header!" ); | 625 | rb->splash( 30, "Weird Pack header!" ); |
522 | p += 5; | 626 | p += 5; |
523 | } | 627 | } |
524 | /*rb->splash( 30, "Pack header" );*/ | ||
525 | } | 628 | } |
526 | 629 | ||
527 | /* System header, parse and skip it - four bytes */ | 630 | /* System header, parse and skip it - four bytes */ |
@@ -535,29 +638,29 @@ static void get_next_data( Stream* str ) | |||
535 | 638 | ||
536 | p += header_length; | 639 | p += header_length; |
537 | 640 | ||
538 | if (p >= disk_buf_end) | 641 | if ( p >= disk_buf_end ) |
539 | { | 642 | p = disk_buf_start + (p - disk_buf_end); |
540 | p = disk_buf + (p - disk_buf_end); | ||
541 | } | ||
542 | /*rb->splash( 30, "System header" );*/ | ||
543 | } | 643 | } |
544 | 644 | ||
545 | /* Packet header, parse it */ | 645 | /* Packet header, parse it */ |
546 | if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) | 646 | if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) |
547 | { | 647 | { |
548 | /* Problem */ | 648 | /* Problem */ |
549 | //rb->splash( HZ*3, "missing packet start code prefix : %X%X at %X", *p, *(p+2), p-disk_buf ); | 649 | rb->splash( HZ*3, "missing packet start code prefix : %X%X at %lX", |
650 | *p, *(p+2), (long unsigned int)(p-disk_buf_start) ); | ||
651 | |||
652 | DEBUGF("end diff: %X,%X,%X,%X,%X,%X\n",(int)str->curr_packet_end, | ||
653 | (int)audio_str.curr_packet_end,(int)video_str.curr_packet_end, | ||
654 | (int)disk_buf_start,(int)disk_buf_end,(int)disk_buf_tail); | ||
655 | |||
550 | str->curr_packet_end = str->curr_packet = NULL; | 656 | str->curr_packet_end = str->curr_packet = NULL; |
551 | break; | 657 | break; |
552 | //++p; | ||
553 | //break; | ||
554 | } | 658 | } |
555 | 659 | ||
556 | /* We retrieve basic infos */ | 660 | /* We retrieve basic infos */ |
557 | stream = p[3]; | 661 | stream = p[3]; |
558 | length = (p[4] << 8) | p[5]; | 662 | length = (p[4] << 8) | p[5]; |
559 | 663 | ||
560 | /*rb->splash( 100, "Stream : %X", stream );*/ | ||
561 | if (stream != str->id) | 664 | if (stream != str->id) |
562 | { | 665 | { |
563 | /* End of stream ? */ | 666 | /* End of stream ? */ |
@@ -618,11 +721,9 @@ static void get_next_data( Stream* str ) | |||
618 | break; | 721 | break; |
619 | } | 722 | } |
620 | } | 723 | } |
621 | 724 | ||
622 | if ((header[length - 1] & 0xc0) == 0x40) | 725 | if ( (header[length - 1] & 0xc0) == 0x40 ) |
623 | { | ||
624 | length += 2; | 726 | length += 2; |
625 | } | ||
626 | 727 | ||
627 | len_skip = length; | 728 | len_skip = length; |
628 | length += mpeg1_skip_table[header[length - 1] >> 4]; | 729 | length += mpeg1_skip_table[header[length - 1] >> 4]; |
@@ -657,20 +758,19 @@ static void get_next_data( Stream* str ) | |||
657 | if (bytes > 0) | 758 | if (bytes > 0) |
658 | { | 759 | { |
659 | str->curr_packet_end = p + bytes; | 760 | str->curr_packet_end = p + bytes; |
660 | //DEBUGF("prev = %d, curr = %d\n",str->prev_packet,str->curr_packet); | ||
661 | 761 | ||
662 | if (str->curr_packet != NULL) | 762 | if (str->curr_packet != NULL) |
663 | { | 763 | { |
764 | lock_stream(); | ||
765 | |||
766 | str->buffer_remaining -= str->prev_packet_length; | ||
664 | if (str->curr_packet < str->prev_packet) | 767 | if (str->curr_packet < str->prev_packet) |
665 | { | 768 | str->prev_packet_length = (disk_buf_end - str->prev_packet) + |
666 | str->buffer_head += (disk_buf_end - str->prev_packet) + | 769 | (str->curr_packet - disk_buf_start); |
667 | (str->curr_packet - disk_buf); | ||
668 | str->guard_bytes = 0; | ||
669 | } | ||
670 | else | 770 | else |
671 | { | 771 | str->prev_packet_length = (str->curr_packet - str->prev_packet); |
672 | str->buffer_head += (str->curr_packet - str->prev_packet); | 772 | |
673 | } | 773 | unlock_stream(); |
674 | 774 | ||
675 | str->prev_packet = str->curr_packet; | 775 | str->prev_packet = str->curr_packet; |
676 | } | 776 | } |
@@ -678,14 +778,13 @@ static void get_next_data( Stream* str ) | |||
678 | str->curr_packet = p; | 778 | str->curr_packet = p; |
679 | 779 | ||
680 | if (str->curr_packet_end > disk_buf_end) | 780 | if (str->curr_packet_end > disk_buf_end) |
681 | { | 781 | rb->memcpy(disk_buf_end, disk_buf_start, |
682 | str->guard_bytes = str->curr_packet_end - disk_buf_end; | 782 | str->curr_packet_end - disk_buf_end ); |
683 | rb->memcpy(disk_buf_end, disk_buf, str->guard_bytes); | ||
684 | } | ||
685 | } | 783 | } |
686 | 784 | ||
687 | break; | 785 | break; |
688 | } /* end while */ | 786 | } /* end while */ |
787 | return 0; | ||
689 | } | 788 | } |
690 | 789 | ||
691 | /* Our clock rate in ticks/second - this won't be a constant for long */ | 790 | /* Our clock rate in ticks/second - this won't be a constant for long */ |
@@ -943,6 +1042,8 @@ static int button_loop(void) | |||
943 | int vol, minvol, maxvol; | 1042 | int vol, minvol, maxvol; |
944 | int button; | 1043 | int button; |
945 | 1044 | ||
1045 | if (video_sync_start==1) { | ||
1046 | |||
946 | if (str_have_msg(&audio_str)) | 1047 | if (str_have_msg(&audio_str)) |
947 | { | 1048 | { |
948 | struct event ev; | 1049 | struct event ev; |
@@ -1014,6 +1115,7 @@ static int button_loop(void) | |||
1014 | rb->lcd_setfont(FONT_SYSFIXED); | 1115 | rb->lcd_setfont(FONT_SYSFIXED); |
1015 | 1116 | ||
1016 | if (result) { | 1117 | if (result) { |
1118 | settings.resume_time = (int)(get_stream_time()/44100/30); | ||
1017 | str_send_msg(&video_str, STREAM_QUIT, 0); | 1119 | str_send_msg(&video_str, STREAM_QUIT, 0); |
1018 | audio_str.status = STREAM_STOPPED; | 1120 | audio_str.status = STREAM_STOPPED; |
1019 | } else { | 1121 | } else { |
@@ -1024,6 +1126,7 @@ static int button_loop(void) | |||
1024 | break; | 1126 | break; |
1025 | 1127 | ||
1026 | case MPEG_STOP: | 1128 | case MPEG_STOP: |
1129 | settings.resume_time = (int)(get_stream_time()/44100/30); | ||
1027 | str_send_msg(&video_str, STREAM_QUIT, 0); | 1130 | str_send_msg(&video_str, STREAM_QUIT, 0); |
1028 | audio_str.status = STREAM_STOPPED; | 1131 | audio_str.status = STREAM_STOPPED; |
1029 | break; | 1132 | break; |
@@ -1060,7 +1163,7 @@ static int button_loop(void) | |||
1060 | audio_str.status = STREAM_STOPPED; | 1163 | audio_str.status = STREAM_STOPPED; |
1061 | } | 1164 | } |
1062 | } | 1165 | } |
1063 | 1166 | } | |
1064 | quit: | 1167 | quit: |
1065 | return audio_str.status; | 1168 | return audio_str.status; |
1066 | } | 1169 | } |
@@ -1086,7 +1189,23 @@ static void audio_thread(void) | |||
1086 | pcm_playback_play(0); | 1189 | pcm_playback_play(0); |
1087 | 1190 | ||
1088 | /* Get first packet */ | 1191 | /* Get first packet */ |
1089 | get_next_data(&audio_str); | 1192 | get_next_data(&audio_str, 0 ); |
1193 | |||
1194 | /* skip audio packets here */ | ||
1195 | while (audio_sync_start==0) | ||
1196 | { | ||
1197 | audio_str.status = STREAM_PLAYING; | ||
1198 | rb->yield(); | ||
1199 | } | ||
1200 | |||
1201 | if (audio_sync_time>10000) | ||
1202 | { | ||
1203 | while (TS_TO_TICKS(audio_str.curr_pts) < audio_sync_time - 10000) | ||
1204 | { | ||
1205 | get_next_data(&audio_str, 0 ); | ||
1206 | rb->priority_yield(); | ||
1207 | } | ||
1208 | } | ||
1090 | 1209 | ||
1091 | if (audio_str.curr_packet == NULL) | 1210 | if (audio_str.curr_packet == NULL) |
1092 | goto done; | 1211 | goto done; |
@@ -1165,7 +1284,7 @@ static void audio_thread(void) | |||
1165 | mpabuf = mpa_buffer; | 1284 | mpabuf = mpa_buffer; |
1166 | 1285 | ||
1167 | /* Get data from next audio packet */ | 1286 | /* Get data from next audio packet */ |
1168 | get_next_data(&audio_str); | 1287 | get_next_data(&audio_str, 0 ); |
1169 | } | 1288 | } |
1170 | while (audio_str.curr_packet != NULL && | 1289 | while (audio_str.curr_packet != NULL && |
1171 | mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD); | 1290 | mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD); |
@@ -1198,8 +1317,6 @@ static void audio_thread(void) | |||
1198 | 1317 | ||
1199 | if (mad_stat != 0) | 1318 | if (mad_stat != 0) |
1200 | { | 1319 | { |
1201 | DEBUGF("Audio stream error - %d\n", stream.error); | ||
1202 | |||
1203 | if (stream.error == MAD_FLAG_INCOMPLETE | 1320 | if (stream.error == MAD_FLAG_INCOMPLETE |
1204 | || stream.error == MAD_ERROR_BUFLEN) | 1321 | || stream.error == MAD_ERROR_BUFLEN) |
1205 | { | 1322 | { |
@@ -1259,6 +1376,13 @@ static void audio_thread(void) | |||
1259 | rb->priority_yield(); | 1376 | rb->priority_yield(); |
1260 | } | 1377 | } |
1261 | 1378 | ||
1379 | if (video_sync_start == 0 && | ||
1380 | pts->pts+(uint32_t)synth.pcm.length<audio_sync_time) { | ||
1381 | synth.pcm.length = 0; | ||
1382 | size = 0; | ||
1383 | rb->yield(); | ||
1384 | } | ||
1385 | |||
1262 | /* TODO: This part will be replaced with dsp calls soon */ | 1386 | /* TODO: This part will be replaced with dsp calls soon */ |
1263 | if (MAD_NCHANNELS(&frame.header) == 2) | 1387 | if (MAD_NCHANNELS(&frame.header) == 2) |
1264 | { | 1388 | { |
@@ -1305,6 +1429,7 @@ static void audio_thread(void) | |||
1305 | audio_str.status = STREAM_PLAYING; | 1429 | audio_str.status = STREAM_PLAYING; |
1306 | pcmbuf_threshold = PCMBUF_PLAY_ALL; | 1430 | pcmbuf_threshold = PCMBUF_PLAY_ALL; |
1307 | pcm_playback_seek_time(pcmbuf_tail->time); | 1431 | pcm_playback_seek_time(pcmbuf_tail->time); |
1432 | video_sync_start = 1; | ||
1308 | } | 1433 | } |
1309 | 1434 | ||
1310 | /* Make this data available to DMA */ | 1435 | /* Make this data available to DMA */ |
@@ -1391,29 +1516,32 @@ static void video_thread(void) | |||
1391 | 1516 | ||
1392 | /* Clear the display - this is mainly just to indicate that the | 1517 | /* Clear the display - this is mainly just to indicate that the |
1393 | video thread has started successfully. */ | 1518 | video thread has started successfully. */ |
1394 | rb->lcd_clear_display(); | 1519 | if (!video_thumb_print) |
1395 | rb->lcd_update(); | 1520 | { |
1521 | rb->lcd_clear_display(); | ||
1522 | rb->lcd_update(); | ||
1523 | } | ||
1396 | 1524 | ||
1397 | /* Request the first packet data */ | 1525 | /* Request the first packet data */ |
1398 | get_next_data( &video_str ); | 1526 | get_next_data( &video_str, 0 ); |
1399 | 1527 | ||
1400 | if (video_str.curr_packet == NULL) | 1528 | if (video_str.curr_packet == NULL) |
1401 | goto done; | 1529 | goto video_thread_quit; |
1402 | 1530 | ||
1403 | mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); | 1531 | mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); |
1404 | total_offset += video_str.curr_packet_end - video_str.curr_packet; | 1532 | total_offset += video_str.curr_packet_end - video_str.curr_packet; |
1405 | 1533 | ||
1406 | info = mpeg2_info (mpeg2dec); | 1534 | info = mpeg2_info (mpeg2dec); |
1407 | 1535 | ||
1408 | /* Wait if the audio thread is buffering - i.e. before | ||
1409 | the first frames are decoded */ | ||
1410 | while (audio_str.status == STREAM_BUFFERING) | ||
1411 | rb->priority_yield(); | ||
1412 | |||
1413 | while (1) | 1536 | while (1) |
1414 | { | 1537 | { |
1415 | /* quickly check mailbox first */ | 1538 | /* quickly check mailbox first */ |
1416 | if (str_have_msg(&video_str)) | 1539 | if (video_thumb_print) |
1540 | { | ||
1541 | if (video_str.status == STREAM_STOPPED) | ||
1542 | break; | ||
1543 | } | ||
1544 | else if (str_have_msg(&video_str)) | ||
1417 | { | 1545 | { |
1418 | while (1) | 1546 | while (1) |
1419 | { | 1547 | { |
@@ -1450,7 +1578,8 @@ static void video_thread(void) | |||
1450 | { | 1578 | { |
1451 | case STATE_BUFFER: | 1579 | case STATE_BUFFER: |
1452 | /* Request next packet data */ | 1580 | /* Request next packet data */ |
1453 | get_next_data( &video_str ); | 1581 | get_next_data( &video_str, 0 ); |
1582 | |||
1454 | mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); | 1583 | mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); |
1455 | total_offset += video_str.curr_packet_end - video_str.curr_packet; | 1584 | total_offset += video_str.curr_packet_end - video_str.curr_packet; |
1456 | info = mpeg2_info (mpeg2dec); | 1585 | info = mpeg2_info (mpeg2dec); |
@@ -1458,7 +1587,7 @@ static void video_thread(void) | |||
1458 | if (video_str.curr_packet == NULL) | 1587 | if (video_str.curr_packet == NULL) |
1459 | { | 1588 | { |
1460 | /* No more data. */ | 1589 | /* No more data. */ |
1461 | goto done; | 1590 | goto video_thread_quit; |
1462 | } | 1591 | } |
1463 | continue; | 1592 | continue; |
1464 | 1593 | ||
@@ -1528,8 +1657,12 @@ static void video_thread(void) | |||
1528 | break; | 1657 | break; |
1529 | 1658 | ||
1530 | /* No limiting => no dropping - draw this frame */ | 1659 | /* No limiting => no dropping - draw this frame */ |
1531 | if (!settings.limitfps) | 1660 | if (!settings.limitfps && (video_thumb_print == 0)) |
1661 | { | ||
1662 | audio_sync_start = 1; | ||
1663 | video_sync_start = 1; | ||
1532 | goto picture_draw; | 1664 | goto picture_draw; |
1665 | } | ||
1533 | 1666 | ||
1534 | /* Get presentation times in audio samples - quite accurate | 1667 | /* Get presentation times in audio samples - quite accurate |
1535 | enough - add previous frame duration if not stamped */ | 1668 | enough - add previous frame duration if not stamped */ |
@@ -1538,7 +1671,19 @@ static void video_thread(void) | |||
1538 | 1671 | ||
1539 | period = TIME_TO_TICKS(info->sequence->frame_period); | 1672 | period = TIME_TO_TICKS(info->sequence->frame_period); |
1540 | 1673 | ||
1674 | if ( (video_thumb_print == 1 || video_sync_start == 0) && | ||
1675 | ((int)(info->current_picture->flags & PIC_MASK_CODING_TYPE) | ||
1676 | == PIC_FLAG_CODING_TYPE_B)) | ||
1677 | break; | ||
1678 | |||
1541 | eta_video = curr_time; | 1679 | eta_video = curr_time; |
1680 | |||
1681 | audio_sync_time = eta_video; | ||
1682 | audio_sync_start = 1; | ||
1683 | |||
1684 | while (video_sync_start == 0) | ||
1685 | rb->yield(); | ||
1686 | |||
1542 | eta_audio = get_stream_time(); | 1687 | eta_audio = get_stream_time(); |
1543 | 1688 | ||
1544 | /* How early/late are we? > 0 = late, < 0 early */ | 1689 | /* How early/late are we? > 0 = late, < 0 early */ |
@@ -1664,32 +1809,39 @@ static void video_thread(void) | |||
1664 | 1809 | ||
1665 | picture_wait: | 1810 | picture_wait: |
1666 | /* Wait until audio catches up */ | 1811 | /* Wait until audio catches up */ |
1667 | while (eta_video > eta_audio) | 1812 | if (video_thumb_print) |
1668 | { | 1813 | video_str.status = STREAM_STOPPED; |
1669 | rb->priority_yield(); | 1814 | else |
1670 | 1815 | while (eta_video > eta_audio) | |
1671 | /* Make sure not to get stuck waiting here forever */ | ||
1672 | if (str_have_msg(&video_str)) | ||
1673 | { | 1816 | { |
1674 | str_look_msg(&video_str, &ev); | 1817 | rb->priority_yield(); |
1675 | 1818 | ||
1676 | /* If not to play, process up top */ | 1819 | /* Make sure not to get stuck waiting here forever */ |
1677 | if (ev.id != STREAM_PLAY) | 1820 | if (str_have_msg(&video_str)) |
1678 | goto rendering_finished; | 1821 | { |
1822 | str_look_msg(&video_str, &ev); | ||
1823 | |||
1824 | /* If not to play, process up top */ | ||
1825 | if (ev.id != STREAM_PLAY) | ||
1826 | goto rendering_finished; | ||
1827 | |||
1828 | /* Told to play but already playing */ | ||
1829 | str_get_msg(&video_str, &ev); | ||
1830 | str_reply_msg(&video_str, 1); | ||
1831 | } | ||
1679 | 1832 | ||
1680 | /* Told to play but already playing */ | 1833 | eta_audio = get_stream_time(); |
1681 | str_get_msg(&video_str, &ev); | ||
1682 | str_reply_msg(&video_str, 1); | ||
1683 | } | 1834 | } |
1684 | 1835 | ||
1685 | eta_audio = get_stream_time(); | ||
1686 | } | ||
1687 | |||
1688 | picture_draw: | 1836 | picture_draw: |
1689 | /* Record last frame time */ | 1837 | /* Record last frame time */ |
1690 | last_render = *rb->current_tick; | 1838 | last_render = *rb->current_tick; |
1691 | 1839 | ||
1692 | vo_draw_frame(info->display_fbuf->buf); | 1840 | if (video_thumb_print) |
1841 | vo_draw_frame_thumb(info->display_fbuf->buf); | ||
1842 | else | ||
1843 | vo_draw_frame(info->display_fbuf->buf); | ||
1844 | |||
1693 | num_drawn++; | 1845 | num_drawn++; |
1694 | 1846 | ||
1695 | picture_skip: | 1847 | picture_skip: |
@@ -1724,53 +1876,298 @@ static void video_thread(void) | |||
1724 | rb->yield(); | 1876 | rb->yield(); |
1725 | } | 1877 | } |
1726 | 1878 | ||
1727 | done: | 1879 | video_thread_quit: |
1728 | #if NUM_CORES > 1 | 1880 | /* if video ends before time sync'd, |
1729 | flush_icache(); | 1881 | besure the audio thread is closed */ |
1730 | #endif | 1882 | if (video_sync_start == 0) |
1883 | { | ||
1884 | audio_str.status = STREAM_STOPPED; | ||
1885 | audio_sync_start = 1; | ||
1886 | } | ||
1731 | 1887 | ||
1732 | video_str.status = STREAM_DONE; | 1888 | #if NUM_CORES > 1 |
1889 | flush_icache(); | ||
1890 | #endif | ||
1891 | |||
1892 | mpeg2_close (mpeg2dec); | ||
1893 | |||
1894 | /* Commit suicide */ | ||
1895 | video_str.status = STREAM_TERMINATED; | ||
1896 | } | ||
1733 | 1897 | ||
1734 | while (1) | 1898 | void initialize_stream( Stream *str, uint8_t *buffer_start, size_t disk_buf_len, int id ) |
1735 | { | 1899 | { |
1736 | str_get_msg(&video_str, &ev); | 1900 | str->curr_packet_end = str->curr_packet = NULL; |
1901 | str->prev_packet_length = 0; | ||
1902 | str->prev_packet = str->curr_packet_end = buffer_start; | ||
1903 | str->buffer_remaining = disk_buf_len; | ||
1904 | str->id = id; | ||
1905 | } | ||
1906 | |||
1907 | void display_thumb(int in_file) | ||
1908 | { | ||
1909 | size_t disk_buf_len; | ||
1737 | 1910 | ||
1738 | if (ev.id == STREAM_QUIT) | 1911 | video_thumb_print = 1; |
1739 | break; | 1912 | audio_sync_start = 1; |
1913 | video_sync_start = 1; | ||
1914 | |||
1915 | disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); | ||
1916 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
1917 | file_remaining = 0; | ||
1918 | initialize_stream(&video_str,disk_buf_start,disk_buf_len,0xe0); | ||
1919 | |||
1920 | video_str.status = STREAM_PLAYING; | ||
1740 | 1921 | ||
1741 | str_reply_msg(&video_str, 0); | 1922 | if ((video_str.thread = rb->create_thread(video_thread, |
1923 | (uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" | ||
1924 | IF_PRIO(,PRIORITY_PLAYBACK) | ||
1925 | IF_COP(, COP, true))) == NULL) | ||
1926 | { | ||
1927 | rb->splash(HZ, "Cannot create video thread!"); | ||
1928 | } | ||
1929 | else | ||
1930 | { | ||
1931 | while (video_str.status != STREAM_TERMINATED) | ||
1932 | rb->yield(); | ||
1742 | } | 1933 | } |
1743 | 1934 | ||
1744 | video_thread_quit: | 1935 | if ( video_str.curr_packet_end == video_str.curr_packet) |
1745 | /* Commit suicide */ | 1936 | rb->splash(0, "frame not available"); |
1746 | video_str.status = STREAM_TERMINATED; | ||
1747 | } | 1937 | } |
1748 | 1938 | ||
1939 | int find_length( int in_file ) | ||
1940 | { | ||
1941 | uint8_t *p; | ||
1942 | size_t read_length = 60*1024; | ||
1943 | size_t disk_buf_len; | ||
1944 | |||
1945 | play_time = 0; | ||
1946 | |||
1947 | /* temporary read buffer size cannot exceed buffer size */ | ||
1948 | if ( read_length > disk_buf_size ) | ||
1949 | read_length = disk_buf_size; | ||
1950 | |||
1951 | /* read tail of file */ | ||
1952 | rb->lseek( in_file, -1*read_length, SEEK_END ); | ||
1953 | disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); | ||
1954 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
1955 | |||
1956 | /* sync reader to this segment of the stream */ | ||
1957 | p=disk_buf_start; | ||
1958 | if (sync_data_stream(&p)) | ||
1959 | { | ||
1960 | DEBUGF("Could not sync stream\n"); | ||
1961 | return PLUGIN_ERROR; | ||
1962 | } | ||
1963 | |||
1964 | /* find last PTS in audio stream; will movie always have audio? if | ||
1965 | the play time can not be determined, set play_time to 0 */ | ||
1966 | audio_sync_start = 0; | ||
1967 | audio_sync_time = 0; | ||
1968 | video_sync_start = 0; | ||
1969 | { | ||
1970 | Stream tmp; | ||
1971 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
1972 | |||
1973 | do | ||
1974 | { | ||
1975 | get_next_data(&tmp, 2); | ||
1976 | if (tmp.tagged == 1) | ||
1977 | /* 10 sec less to insure the video frame exist */ | ||
1978 | play_time = (int)((tmp.curr_pts/45000-10)/30); | ||
1979 | } | ||
1980 | while (tmp.curr_packet_end != NULL); | ||
1981 | } | ||
1982 | return 0; | ||
1983 | } | ||
1984 | |||
1985 | ssize_t seek_PTS( int in_file, int start_time, int accept_button ) | ||
1986 | { | ||
1987 | static ssize_t last_seek_pos = 0; | ||
1988 | static int last_start_time = 0; | ||
1989 | ssize_t seek_pos; | ||
1990 | size_t disk_buf_len; | ||
1991 | uint8_t *p; | ||
1992 | size_t read_length = 60*1024; | ||
1993 | |||
1994 | /* temporary read buffer size cannot exceed buffer size */ | ||
1995 | if ( read_length > disk_buf_size ) | ||
1996 | read_length = disk_buf_size; | ||
1997 | |||
1998 | if ( start_time == last_start_time ) | ||
1999 | { | ||
2000 | seek_pos = last_seek_pos; | ||
2001 | rb->lseek(in_file,seek_pos,SEEK_SET); | ||
2002 | } | ||
2003 | else if ( start_time != 0 ) | ||
2004 | { | ||
2005 | seek_pos = rb->filesize(in_file)*start_time/play_time; | ||
2006 | int seek_pos_sec_inc = rb->filesize(in_file)/play_time/30; | ||
2007 | |||
2008 | if (seek_pos<0) | ||
2009 | seek_pos=0; | ||
2010 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2011 | seek_pos = rb->filesize(in_file) - read_length; | ||
2012 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2013 | disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); | ||
2014 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2015 | |||
2016 | /* sync reader to this segment of the stream */ | ||
2017 | p=disk_buf_start; | ||
2018 | if (sync_data_stream(&p)) | ||
2019 | { | ||
2020 | DEBUGF("Could not sync stream\n"); | ||
2021 | return PLUGIN_ERROR; | ||
2022 | } | ||
2023 | |||
2024 | /* find PTS >= start_time */ | ||
2025 | audio_sync_start = 0; | ||
2026 | audio_sync_time = 0; | ||
2027 | video_sync_start = 0; | ||
2028 | { | ||
2029 | Stream tmp; | ||
2030 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
2031 | int cont_seek_loop = 1; | ||
2032 | int coarse_seek = 1; | ||
2033 | do | ||
2034 | { | ||
2035 | if ( accept_button ) | ||
2036 | { | ||
2037 | rb->yield(); | ||
2038 | if (rb->button_available()) | ||
2039 | return -101; | ||
2040 | } | ||
2041 | |||
2042 | while ( get_next_data(&tmp, 1) == 1 ) | ||
2043 | { | ||
2044 | if ( tmp.curr_packet_end == disk_buf_start ) | ||
2045 | seek_pos += disk_buf_tail - disk_buf_start; | ||
2046 | else | ||
2047 | seek_pos += tmp.curr_packet_end - disk_buf_start; | ||
2048 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2049 | seek_pos = rb->filesize(in_file) - read_length; | ||
2050 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2051 | disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); | ||
2052 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2053 | |||
2054 | /* sync reader to this segment of the stream */ | ||
2055 | p=disk_buf_start; | ||
2056 | initialize_stream(&tmp,p,disk_buf_len,0xc0); | ||
2057 | } | ||
2058 | |||
2059 | /* are we after start_time in the stream? */ | ||
2060 | if ( coarse_seek && (int)(tmp.curr_pts/45000) >= start_time*30 ) | ||
2061 | { | ||
2062 | int time_to_backup = (int)(tmp.curr_pts/45000) - start_time*30; | ||
2063 | if (time_to_backup == 0) | ||
2064 | time_to_backup++; | ||
2065 | seek_pos -= seek_pos_sec_inc * time_to_backup; | ||
2066 | seek_pos_sec_inc -= seek_pos_sec_inc/20; /* for stability */ | ||
2067 | if (seek_pos<0) | ||
2068 | seek_pos=0; | ||
2069 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2070 | seek_pos = rb->filesize(in_file) - read_length; | ||
2071 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2072 | disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); | ||
2073 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2074 | |||
2075 | /* sync reader to this segment of the stream */ | ||
2076 | p=disk_buf_start; | ||
2077 | if (sync_data_stream(&p)) | ||
2078 | { | ||
2079 | DEBUGF("Could not sync stream\n"); | ||
2080 | return PLUGIN_ERROR; | ||
2081 | } | ||
2082 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
2083 | continue; | ||
2084 | } | ||
2085 | |||
2086 | /* are we well before start_time in the stream? */ | ||
2087 | if ( coarse_seek && start_time*30 - (int)(tmp.curr_pts/45000) > 2 ) | ||
2088 | { | ||
2089 | int time_to_advance = start_time*30 - (int)(tmp.curr_pts/45000) - 2; | ||
2090 | if (time_to_advance <= 0) | ||
2091 | time_to_advance = 1; | ||
2092 | seek_pos += seek_pos_sec_inc * time_to_advance; | ||
2093 | if (seek_pos<0) | ||
2094 | seek_pos=0; | ||
2095 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2096 | seek_pos = rb->filesize(in_file) - read_length; | ||
2097 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2098 | disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); | ||
2099 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2100 | |||
2101 | /* sync reader to this segment of the stream */ | ||
2102 | p=disk_buf_start; | ||
2103 | if (sync_data_stream(&p)) | ||
2104 | { | ||
2105 | DEBUGF("Could not sync stream\n"); | ||
2106 | return PLUGIN_ERROR; | ||
2107 | } | ||
2108 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
2109 | continue; | ||
2110 | } | ||
2111 | |||
2112 | coarse_seek = 0; | ||
2113 | |||
2114 | /* are we at start_time in the stream? */ | ||
2115 | if ( (int)(tmp.curr_pts/45000) >= start_time*30 ) | ||
2116 | cont_seek_loop = 0; | ||
2117 | |||
2118 | } | ||
2119 | while ( cont_seek_loop ); | ||
2120 | |||
2121 | |||
2122 | DEBUGF("start diff: %u %u\n",(unsigned int)(tmp.curr_pts/45000),start_time*30); | ||
2123 | seek_pos+=tmp.curr_packet_end-disk_buf_start; | ||
2124 | |||
2125 | last_seek_pos = seek_pos; | ||
2126 | last_start_time = start_time; | ||
2127 | |||
2128 | rb->lseek(in_file,seek_pos,SEEK_SET); | ||
2129 | } | ||
2130 | } | ||
2131 | else | ||
2132 | { | ||
2133 | seek_pos = 0; | ||
2134 | rb->lseek(in_file,0,SEEK_SET); | ||
2135 | last_seek_pos = seek_pos; | ||
2136 | last_start_time = start_time; | ||
2137 | } | ||
2138 | return seek_pos; | ||
2139 | } | ||
2140 | |||
1749 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | 2141 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) |
1750 | { | 2142 | { |
1751 | int status = PLUGIN_ERROR; /* assume failure */ | 2143 | int status = PLUGIN_ERROR; /* assume failure */ |
2144 | int start_time=-1; | ||
1752 | void* audiobuf; | 2145 | void* audiobuf; |
1753 | ssize_t audiosize; | 2146 | ssize_t audiosize; |
1754 | int in_file; | 2147 | int in_file; |
1755 | uint8_t* buffer; | ||
1756 | size_t file_remaining; | ||
1757 | size_t disk_buf_len; | 2148 | size_t disk_buf_len; |
2149 | ssize_t seek_pos; | ||
1758 | #ifndef HAVE_LCD_COLOR | 2150 | #ifndef HAVE_LCD_COLOR |
1759 | long graysize; | 2151 | long graysize; |
1760 | int grayscales; | 2152 | int grayscales; |
1761 | #endif | 2153 | #endif |
1762 | 2154 | ||
2155 | audio_sync_start = 0; | ||
2156 | audio_sync_time = 0; | ||
2157 | video_sync_start = 0; | ||
2158 | |||
1763 | if (parameter == NULL) | 2159 | if (parameter == NULL) |
1764 | { | 2160 | { |
1765 | api->splash(HZ*2, "No File"); | 2161 | api->splash(HZ*2, "No File"); |
1766 | return PLUGIN_ERROR; | ||
1767 | } | 2162 | } |
1768 | 2163 | ||
1769 | /* Initialize IRAM - stops audio and voice as well */ | 2164 | /* Initialize IRAM - stops audio and voice as well */ |
1770 | PLUGIN_IRAM_INIT(api) | 2165 | PLUGIN_IRAM_INIT(api) |
1771 | 2166 | ||
1772 | rb = api; | 2167 | rb = api; |
2168 | rb->splash(0, "loading ..."); | ||
1773 | 2169 | ||
2170 | /* sets audiosize and returns buffer pointer */ | ||
1774 | audiobuf = rb->plugin_get_audio_buffer(&audiosize); | 2171 | audiobuf = rb->plugin_get_audio_buffer(&audiosize); |
1775 | 2172 | ||
1776 | #if INPUT_SRC_CAPS != 0 | 2173 | #if INPUT_SRC_CAPS != 0 |
@@ -1781,49 +2178,38 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | |||
1781 | 2178 | ||
1782 | rb->pcm_set_frequency(SAMPR_44); | 2179 | rb->pcm_set_frequency(SAMPR_44); |
1783 | 2180 | ||
1784 | /* Set disk pointers to NULL */ | 2181 | #ifndef HAVE_LCD_COLOR |
1785 | disk_buf_end = disk_buf = NULL; | 2182 | /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */ |
1786 | 2183 | grayscales = gray_init(rb, audiobuf, audiosize, false, LCD_WIDTH, LCD_HEIGHT, | |
1787 | /* Stream construction */ | 2184 | 32, 2<<8, &graysize) + 1; |
1788 | /* We take the first stream of each (audio and video) */ | 2185 | audiobuf += graysize; |
1789 | /* TODO : Search for these in the file first */ | 2186 | audiosize -= graysize; |
1790 | audio_str.curr_packet_end = audio_str.curr_packet = audio_str.next_packet = NULL; | 2187 | if (grayscales < 33 || audiosize <= 0) |
1791 | video_str = audio_str; | 2188 | { |
1792 | video_str.id = 0xe0; | 2189 | rb->splash(HZ, "gray buf error"); |
1793 | audio_str.id = 0xc0; | 2190 | return PLUGIN_ERROR; |
2191 | } | ||
2192 | #endif | ||
1794 | 2193 | ||
1795 | /* Initialise our malloc buffer */ | 2194 | /* Initialise our malloc buffer */ |
1796 | audiosize = mpeg_alloc_init(audiobuf, audiosize, LIBMPEG2BUFFER_SIZE); | 2195 | audiosize = mpeg_alloc_init(audiobuf,audiosize, LIBMPEG2BUFFER_SIZE); |
1797 | if (audiosize == 0) | 2196 | if (audiosize == 0) |
1798 | return PLUGIN_ERROR; | 2197 | return PLUGIN_ERROR; |
1799 | 2198 | ||
2199 | /* Set disk pointers to NULL */ | ||
2200 | disk_buf_end = disk_buf_start = NULL; | ||
2201 | |||
1800 | /* Grab most of the buffer for the compressed video - leave some for | 2202 | /* Grab most of the buffer for the compressed video - leave some for |
1801 | PCM audio data and some for libmpeg2 malloc use. */ | 2203 | PCM audio data and some for libmpeg2 malloc use. */ |
1802 | buffer_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+ | 2204 | disk_buf_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+ |
1803 | MPABUF_SIZE); | 2205 | MPABUF_SIZE); |
1804 | 2206 | ||
1805 | DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size); | 2207 | DEBUGF("audiosize=%ld, disk_buf_size=%ld\n",audiosize,disk_buf_size); |
1806 | buffer = mpeg_malloc(buffer_size,-1); | 2208 | disk_buf_start = mpeg_malloc(disk_buf_size,-1); |
1807 | 2209 | ||
1808 | if (buffer == NULL) | 2210 | if (disk_buf_start == NULL) |
1809 | return PLUGIN_ERROR; | 2211 | return PLUGIN_ERROR; |
1810 | 2212 | ||
1811 | #ifndef HAVE_LCD_COLOR | ||
1812 | /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */ | ||
1813 | grayscales = gray_init(rb, buffer, buffer_size, false, LCD_WIDTH, LCD_HEIGHT, | ||
1814 | 32, 2<<8, &graysize) + 1; | ||
1815 | buffer += graysize; | ||
1816 | buffer_size -= graysize; | ||
1817 | if (grayscales < 33 || buffer_size <= 0) | ||
1818 | { | ||
1819 | rb->splash(HZ, "gray buf error"); | ||
1820 | return PLUGIN_ERROR; | ||
1821 | } | ||
1822 | #endif | ||
1823 | |||
1824 | buffer_size &= ~(0x7ff); /* Round buffer down to nearest 2KB */ | ||
1825 | DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size); | ||
1826 | |||
1827 | if (!init_mpabuf()) | 2213 | if (!init_mpabuf()) |
1828 | return PLUGIN_ERROR; | 2214 | return PLUGIN_ERROR; |
1829 | 2215 | ||
@@ -1836,9 +2222,10 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | |||
1836 | in_file = rb->open((char*)parameter,O_RDONLY); | 2222 | in_file = rb->open((char*)parameter,O_RDONLY); |
1837 | 2223 | ||
1838 | if (in_file < 0){ | 2224 | if (in_file < 0){ |
1839 | //fprintf(stderr,"Could not open %s\n",argv[1]); | 2225 | DEBUGF("Could not open %s\n",(char*)parameter); |
1840 | return PLUGIN_ERROR; | 2226 | return PLUGIN_ERROR; |
1841 | } | 2227 | } |
2228 | filename = (char*)parameter; | ||
1842 | 2229 | ||
1843 | #ifdef HAVE_LCD_COLOR | 2230 | #ifdef HAVE_LCD_COLOR |
1844 | rb->lcd_set_backdrop(NULL); | 2231 | rb->lcd_set_backdrop(NULL); |
@@ -1860,34 +2247,51 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | |||
1860 | cannot just return PLUGIN_ERROR - instead drop though to cleanup code | 2247 | cannot just return PLUGIN_ERROR - instead drop though to cleanup code |
1861 | */ | 2248 | */ |
1862 | 2249 | ||
1863 | init_settings(); | 2250 | init_settings((char*)parameter); |
1864 | 2251 | ||
1865 | /* Initialise libmad */ | 2252 | /* Initialise libmad */ |
1866 | rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap)); | 2253 | rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap)); |
1867 | init_mad(mad_frame_overlap); | 2254 | init_mad(mad_frame_overlap); |
1868 | 2255 | ||
1869 | file_remaining = rb->filesize(in_file); | 2256 | disk_buf_end = disk_buf_start + disk_buf_size-MPEG_GUARDBUF_SIZE; |
1870 | disk_buf_end = buffer + buffer_size-MPEG_GUARDBUF_SIZE; | 2257 | |
2258 | /* initalize play_time with the length (in half minutes) of the movie | ||
2259 | zero if the time could not be determined */ | ||
2260 | find_length( in_file ); | ||
2261 | |||
2262 | /* start menu */ | ||
2263 | start_time = mpeg_start_menu(play_time, in_file); | ||
2264 | if ( start_time == -1 ) | ||
2265 | return 0; | ||
2266 | else if ( start_time < 0 ) | ||
2267 | start_time = 0; | ||
2268 | else if ( start_time > play_time ) | ||
2269 | start_time = play_time; | ||
2270 | |||
2271 | rb->splash(0, "loading ..."); | ||
2272 | |||
2273 | /* seek start time */ | ||
2274 | seek_pos = seek_PTS( in_file, start_time, 0 ); | ||
2275 | |||
2276 | rb->lseek(in_file,seek_pos,SEEK_SET); | ||
2277 | video_thumb_print = 0; | ||
2278 | audio_sync_start = 0; | ||
2279 | audio_sync_time = 0; | ||
2280 | video_sync_start = 0; | ||
1871 | 2281 | ||
1872 | /* Read some stream data */ | 2282 | /* Read some stream data */ |
1873 | disk_buf_len = rb->read (in_file, buffer, MPEG_LOW_WATERMARK); | 2283 | disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); |
1874 | 2284 | ||
1875 | DEBUGF("Initial Buffering - %d bytes\n",(int)disk_buf_len); | 2285 | disk_buf_tail = disk_buf_start + disk_buf_len; |
1876 | disk_buf = buffer; | 2286 | file_remaining = rb->filesize(in_file); |
1877 | disk_buf_tail = buffer+disk_buf_len; | 2287 | file_remaining -= disk_buf_len + seek_pos; |
1878 | file_remaining -= disk_buf_len; | 2288 | |
1879 | 2289 | initialize_stream( &video_str, disk_buf_start, disk_buf_len, 0xe0 ); | |
1880 | audio_str.guard_bytes = 0; | 2290 | initialize_stream( &audio_str, disk_buf_start, disk_buf_len, 0xc0 ); |
1881 | audio_str.prev_packet = disk_buf; | ||
1882 | audio_str.buffer_head = 0; | ||
1883 | audio_str.buffer_tail = disk_buf_len; | ||
1884 | video_str.guard_bytes = 0; | ||
1885 | video_str.prev_packet = disk_buf; | ||
1886 | video_str.buffer_head = 0; | ||
1887 | video_str.buffer_tail = disk_buf_len; | ||
1888 | 2291 | ||
1889 | rb->spinlock_init(&audio_str.msg_lock); | 2292 | rb->spinlock_init(&audio_str.msg_lock); |
1890 | rb->spinlock_init(&video_str.msg_lock); | 2293 | rb->spinlock_init(&video_str.msg_lock); |
2294 | |||
1891 | audio_str.status = STREAM_BUFFERING; | 2295 | audio_str.status = STREAM_BUFFERING; |
1892 | video_str.status = STREAM_PLAYING; | 2296 | video_str.status = STREAM_PLAYING; |
1893 | 2297 | ||
@@ -1895,6 +2299,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | |||
1895 | gray_show(true); | 2299 | gray_show(true); |
1896 | #endif | 2300 | #endif |
1897 | 2301 | ||
2302 | init_stream_lock(); | ||
2303 | |||
1898 | #if NUM_CORES > 1 | 2304 | #if NUM_CORES > 1 |
1899 | flush_icache(); | 2305 | flush_icache(); |
1900 | #endif | 2306 | #endif |
@@ -1914,38 +2320,52 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | |||
1914 | } | 2320 | } |
1915 | else | 2321 | else |
1916 | { | 2322 | { |
1917 | //DEBUGF("START: video = %d, audio = %d\n",audio_str.buffer_remaining,video_str.buffer_remaining); | ||
1918 | rb->lcd_setfont(FONT_SYSFIXED); | 2323 | rb->lcd_setfont(FONT_SYSFIXED); |
1919 | 2324 | ||
1920 | /* Wait until both threads have finished their work */ | 2325 | /* Wait until both threads have finished their work */ |
1921 | while ((audio_str.status >= 0) || (video_str.status >= 0)) | 2326 | while ((audio_str.status >= 0) || (video_str.status >= 0)) |
1922 | { | 2327 | { |
1923 | size_t audio_remaining = audio_str.buffer_tail - audio_str.buffer_head; | 2328 | size_t audio_remaining = audio_str.buffer_remaining; |
1924 | size_t video_remaining = video_str.buffer_tail - video_str.buffer_head; | 2329 | size_t video_remaining = video_str.buffer_remaining; |
1925 | 2330 | ||
1926 | if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) { | 2331 | if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) |
2332 | { | ||
1927 | 2333 | ||
1928 | size_t bytes_to_read = buffer_size - MPEG_GUARDBUF_SIZE - | 2334 | size_t bytes_to_read = disk_buf_size - MPEG_GUARDBUF_SIZE - |
1929 | MAX(audio_remaining,video_remaining); | 2335 | MAX(audio_remaining,video_remaining); |
1930 | 2336 | ||
1931 | bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail)); | 2337 | bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail)); |
1932 | 2338 | ||
1933 | while (( bytes_to_read > 0) && (file_remaining > 0) && | 2339 | while (( bytes_to_read > 0) && (file_remaining > 0) && |
1934 | ((audio_str.status >= 0) || (video_str.status >= 0))) { | 2340 | ((audio_str.status != STREAM_DONE) || (video_str.status != STREAM_DONE))) |
1935 | size_t n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); | 2341 | { |
2342 | |||
2343 | size_t n; | ||
2344 | if ( video_sync_start != 0 ) | ||
2345 | n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); | ||
2346 | else | ||
2347 | { | ||
2348 | n = rb->read(in_file, disk_buf_tail,bytes_to_read); | ||
2349 | if (n==0) | ||
2350 | rb->splash(30,"buffer fill error"); | ||
2351 | } | ||
2352 | |||
1936 | 2353 | ||
1937 | bytes_to_read -= n; | 2354 | bytes_to_read -= n; |
1938 | file_remaining -= n; | 2355 | file_remaining -= n; |
1939 | 2356 | ||
1940 | audio_str.buffer_tail += n; | 2357 | lock_stream(); |
1941 | video_str.buffer_tail += n; | 2358 | audio_str.buffer_remaining += n; |
2359 | video_str.buffer_remaining += n; | ||
2360 | unlock_stream(); | ||
2361 | |||
1942 | disk_buf_tail += n; | 2362 | disk_buf_tail += n; |
1943 | 2363 | ||
1944 | rb->yield(); | 2364 | rb->yield(); |
1945 | } | 2365 | } |
1946 | 2366 | ||
1947 | if (disk_buf_tail == disk_buf_end) | 2367 | if (disk_buf_tail == disk_buf_end) |
1948 | disk_buf_tail = buffer; | 2368 | disk_buf_tail = disk_buf_start; |
1949 | } | 2369 | } |
1950 | 2370 | ||
1951 | rb->sleep(HZ/10); | 2371 | rb->sleep(HZ/10); |
@@ -1968,6 +2388,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | |||
1968 | invalidate_icache(); | 2388 | invalidate_icache(); |
1969 | #endif | 2389 | #endif |
1970 | 2390 | ||
2391 | vo_cleanup(); | ||
2392 | |||
1971 | #ifndef HAVE_LCD_COLOR | 2393 | #ifndef HAVE_LCD_COLOR |
1972 | gray_release(); | 2394 | gray_release(); |
1973 | #endif | 2395 | #endif |