Index: apps/plugins/mpegplayer/mpegplayer.c =================================================================== --- apps/plugins/mpegplayer/mpegplayer.c (revision 12900) +++ apps/plugins/mpegplayer/mpegplayer.c (working copy) @@ -172,8 +172,11 @@ uint8_t* curr_packet; /* Current stream packet beginning */ uint8_t* curr_packet_end; /* Current stream packet end */ - uint8_t* next_packet; /* Next stream packet beginning */ - + uint8_t* prev_packet; /* Previous stream packet beginning */ + uint8_t* next_packet; /* Next stream packet beginning */ + + size_t guard_bytes; /* Number of bytes in guardbuf used */ + size_t buffer_remaining; /* How much data is left in the buffer */ int id; } Stream; @@ -184,6 +187,8 @@ on the ipod (reason unknown) */ static uint8_t *disk_buf, *disk_buf_end; +static uint8_t *disk_buf_tail IBSS_ATTR; +static size_t buffer_size IBSS_ATTR; /* Events */ static struct event_queue msg_queue IBSS_ATTR; @@ -211,8 +216,17 @@ /* TODO: Can we reduce the PCM buffer size? */ #define PCMBUFFER_SIZE (512*1024) #define AUDIOBUFFER_SIZE (32*1024) +#ifdef SIMULATOR +/* Make this large, so the main buffer is small - for testing buffering */ #define LIBMPEG2BUFFER_SIZE (2*1024*1024) +#else +#define LIBMPEG2BUFFER_SIZE (2*1024*1024) +#endif +/* TODO: Is 32KB enough? */ +#define MPEG_GUARDBUF_SIZE (32*1024) +#define MPEG_LOW_WATERMARK (1024*1024) + static void button_loop(void) { bool result; @@ -344,7 +358,7 @@ uint8_t system_header_start_code [4] = { 0x00, 0x00, 0x01, 0xbb }; /* This function demux the streams and give the next stream data pointer */ -static void get_next_data( Stream* str ) +static void get_next_data( Stream* str) { uint8_t *p; uint8_t *header; @@ -358,6 +372,7 @@ /* What does this do? */ while( (p = disk_buf) == NULL ) { +DEBUGF("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); rb->lcd_putsxy(0,LCD_HEIGHT-10,"FREEZE!"); rb->lcd_update(); rb->sleep(100); @@ -365,11 +380,17 @@ } else { p = str->curr_packet_end; } - + for( ;; ) { int length, bytes; + if( p >= disk_buf_end ) + { + p = disk_buf + (p - disk_buf_end); +DEBUGF("**** Wrapping pointer for %02x\n",str->id); + } + /* Pack header, skip it */ if( rb->memcmp (p, pack_start_code, sizeof (pack_start_code)) == 0 ) { @@ -397,6 +418,11 @@ /*rb->splash( 30, "System header" );*/ } + if( p > disk_buf_end ) + { + DEBUGF("REBUFFERING NEEDED... 3\n"); + } + /* Packet header, parse it */ if( rb->memcmp (p, packet_start_code_prefix, sizeof (packet_start_code_prefix)) != 0 ) { @@ -428,6 +454,11 @@ continue; } + if( p > disk_buf_end ) + { + DEBUGF("REBUFFERING NEEDED... 4\n"); + } + /* Ok, it's our packet */ str->curr_packet_end = p + length+6; header = p; @@ -461,6 +492,7 @@ if (length > 23) { rb->splash( 30, "Too much stuffing" ); +DEBUGF("Too much stuffing" ); break; } } @@ -488,20 +520,37 @@ mpeg2_tag_picture (mpeg2dec, pts, dts); } } + p += length; bytes = 6 + (header[4] << 8) + header[5] - length; if (bytes > 0) { - /*str->curr_packet_end = p+bytes;*/ + str->curr_packet_end = p+bytes; + //DEBUGF("prev = %d, curr = %d\n",str->prev_packet,str->curr_packet); + + if (str->curr_packet != NULL) { + if (str->curr_packet < str->prev_packet) { + str->buffer_remaining -= (disk_buf_end - str->prev_packet) + (str->curr_packet - disk_buf); + str->buffer_remaining -= str->guard_bytes; + str->guard_bytes = 0; + } else { + str->buffer_remaining -= (str->curr_packet - str->prev_packet); + } + + str->prev_packet = str->curr_packet; + } + + DEBUGF("audio = %d, video = %d\n",audio_str.buffer_remaining,video_str.buffer_remaining); + str->curr_packet = p; - return; + + if( str->curr_packet_end > disk_buf_end ) + { + str->guard_bytes = str->curr_packet_end-disk_buf_end; + rb->memcpy(disk_buf_end,disk_buf,str->guard_bytes); + DEBUGF("memcpy to guard buffer - curr_packet=%d, curr_packet_end=%d, disk_buf_end=%d\n",str->curr_packet,str->curr_packet_end,disk_buf_end); + } + return ; } - - if( str->curr_packet_end > disk_buf_end ) - { - /* We should ask for buffering here */ - str->curr_packet_end = str->curr_packet = NULL; - return; - } break; } @@ -575,7 +624,7 @@ goto done; } - if (n < 1000) { /* TODO: What is the maximum size of an MPEG audio frame? */ + if (n < 1500) { /* TODO: What is the maximum size of an MPEG audio frame? */ get_next_data( &audio_str ); if (audio_str.curr_packet == NULL) { /* Wait for audio to finish */ @@ -585,6 +634,7 @@ len = audio_str.curr_packet_end - audio_str.curr_packet; if (n + len > mpa_buffer_size) { rb->splash( 30, "Audio buffer overflow" ); + DEBUGF("Audio buffer overflow" ); audiostatus=STREAM_DONE; /* Wait to be killed */ for (;;) { rb->sleep(HZ); } @@ -599,6 +649,7 @@ } if (mad_frame_decode(&frame, &stream)) { + DEBUGF("Audio stream error - %d\n",stream.error); if (stream.error == MAD_FLAG_INCOMPLETE || stream.error == MAD_ERROR_BUFLEN) { /* This makes the codec support partially corrupted files */ @@ -620,6 +671,7 @@ continue; } else { /* Some other unrecoverable error */ + DEBUGF("Unrecoverable error\n"); break; } break; @@ -636,6 +688,7 @@ n -= len; } else { /* What to do here? */ + DEBUGF("/* What to do here? */\n"); goto done; } #if 0 @@ -714,6 +767,7 @@ done: rb->pcm_play_stop(); audiostatus=STREAM_DONE; +DEBUGF("Audio thread ended." ); for (;;) { button_loop(); @@ -723,7 +777,7 @@ /* End of libmad stuff */ -static uint64_t eta IBSS_ATTR; +static int64_t eta IBSS_ATTR; /* TODO: Running in the main thread, libmad needs 8.25KB of stack. The codec thread uses a 9KB stack. So we can probable reduce this a @@ -745,9 +799,9 @@ int skipcount = 0; int frame = 0; int lasttick; - unsigned int eta2; - unsigned int s; - int fps; + int64_t eta2; + int64_t s; + int64_t fps; rb->sleep(HZ/5); mpeg2dec = mpeg2_init (); @@ -820,6 +874,7 @@ /* Convert eta (in 27MHz ticks) into audio samples */ eta2 =(eta * 44100) / 27000000; + s = samplesplayed - (rb->pcm_get_bytes_waiting() >> 2); if (settings.limitfps) { if (eta2 > s) { @@ -841,9 +896,11 @@ /* Calculate fps */ frame++; if (settings.showfps && (*rb->current_tick-lasttick>=HZ)) { - fps=(frame*441000)/s; + fps=frame; + fps*=441000; + fps/=s; rb->snprintf(str,sizeof(str),"%d.%d %d %d %d", - (fps/10),fps%10,skipped,s,eta2); + (int)(fps/10),(int)(fps%10),skipped,(int)s,(int)eta2); rb->lcd_putsxy(0,0,str); rb->lcd_update_rect(0,0,LCD_WIDTH,8); @@ -870,7 +927,11 @@ int audiosize; int in_file; uint8_t* buffer; - size_t buffer_size; + size_t audio_remaining, video_remaining; + size_t bytes_to_read; + size_t file_remaining; + size_t n; + size_t disk_buf_len; /* We define this here so it is on the main stack (in IRAM) */ mad_fixed_t mad_frame_overlap[2][32][18]; /* 4608 bytes */ @@ -898,7 +959,13 @@ /* Grab most of the buffer for the compressed video - leave some for PCM audio data and some for libmpeg2 malloc use. */ buffer_size = audiosize - (PCMBUFFER_SIZE+AUDIOBUFFER_SIZE+LIBMPEG2BUFFER_SIZE); + DEBUGF("audiosize=%d, buffer_size=%ld\n",audiosize,buffer_size); + buffer_size &= ~(0x7ff); /* Round buffer down to nearest 2KB */ +#ifdef SIMULATOR + buffer_size = 28096512; +#endif + DEBUGF("audiosize=%d, buffer_size=%ld\n",audiosize,buffer_size); buffer = mpeg2_malloc(buffer_size,-1); if (buffer == NULL) @@ -961,11 +1028,23 @@ eta = 0; - rb->splash(0, "Buffering..."); + file_remaining = rb->filesize(in_file); + disk_buf_end = buffer + buffer_size-MPEG_GUARDBUF_SIZE; - disk_buf_end = buffer + rb->read (in_file, buffer, buffer_size); + disk_buf_len = rb->read (in_file, buffer, MPEG_LOW_WATERMARK); + + DEBUGF("Initial Buffering - %d bytes\n",disk_buf_len); disk_buf = buffer; + disk_buf_tail = buffer+disk_buf_len; + file_remaining -= disk_buf_len; + video_str.guard_bytes = audio_str.guard_bytes = 0; + video_str.prev_packet = disk_buf; + audio_str.prev_packet = disk_buf; + video_str.buffer_remaining = disk_buf_len; + audio_str.buffer_remaining = disk_buf_len; + + //DEBUGF("START: video = %d, audio = %d\n",audio_str.buffer_remaining,video_str.buffer_remaining); rb->lcd_setfont(FONT_SYSFIXED); audiostatus = STREAM_BUFFERING; @@ -991,6 +1070,32 @@ /* Wait until both threads have finished their work */ while ((audiostatus != STREAM_DONE) || (videostatus != STREAM_DONE)) { + audio_remaining = audio_str.buffer_remaining; + video_remaining = video_str.buffer_remaining; + if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) { + + // TODO: Add mutex when updating the A/V buffer_remaining variables. + bytes_to_read = buffer_size - MPEG_GUARDBUF_SIZE - MAX(audio_remaining,video_remaining); + + bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail)); + + while (( bytes_to_read > 0) && (file_remaining > 0)) { + DEBUGF("Reading %d bytes\n",MIN(128*1024,bytes_to_read)); + n = rb->read(in_file, disk_buf_tail, MIN(128*1024,bytes_to_read)); + DEBUGF("Read %d bytes, bytes_to_read = %d, file_remaining=%d\n",n,bytes_to_read,file_remaining); + DEBUGF("buffer = %d, disk_buf_tail = %d\n",buffer, disk_buf_tail); + bytes_to_read -= n; + file_remaining -= n; + audio_str.buffer_remaining += n; + video_str.buffer_remaining += n; + disk_buf_tail += n; + rb->yield(); + } + + if (disk_buf_tail == disk_buf_end) + disk_buf_tail = buffer; + + } rb->sleep(HZ/10); }