diff -Naur svn/apps/plugin.c rockbox_svn_2007-08-31/apps/plugin.c --- svn/apps/plugin.c 2007-08-14 00:09:51.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugin.c 2007-08-31 20:27:19.000000000 -0400 @@ -198,6 +198,7 @@ gui_synclist_set_title, /* button */ + button_available, button_get, button_get_w_tmo, button_status, diff -Naur svn/apps/plugin.h rockbox_svn_2007-08-31/apps/plugin.h --- svn/apps/plugin.h 2007-08-21 15:27:34.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugin.h 2007-08-31 20:27:19.000000000 -0400 @@ -285,6 +285,7 @@ void (*gui_synclist_set_title)(struct gui_synclist *lists, char* title, int icon); /* button */ + int (*button_available)(void); long (*button_get)(bool block); long (*button_get_w_tmo)(int ticks); int (*button_status)(void); diff -Naur svn/apps/plugins/lib/configfile.c rockbox_svn_2007-08-31/apps/plugins/lib/configfile.c --- svn/apps/plugins/lib/configfile.c 2007-08-09 12:27:07.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/lib/configfile.c 2007-08-31 20:27:19.000000000 -0400 @@ -55,12 +55,14 @@ if(fd < 0) return fd*10 - 1; - cfg_rb->fdprintf(fd, "file version: %d\n", version); + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "file version: %10d\n", version); for(i = 0;i < num_items;i++) { switch(cfg[i].type) { case TYPE_INT: - cfg_rb->fdprintf(fd, "%s: %d\n", + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "%s: %10d\n", cfg[i].name, *cfg[i].val); break; @@ -141,3 +143,72 @@ cfg_rb->close(fd); return 0; } + +int configfile_get_value(const char* filename, const char* name) +{ + int fd; + char *pname; + char *pval; + char buf[MAX_PATH]; + + get_cfg_filename(buf, MAX_PATH, filename); + fd = cfg_rb->open(buf, O_RDONLY); + if(fd < 0) + return -1; + + while(cfg_rb->read_line(fd, buf, MAX_PATH) > 0) + { + cfg_rb->settings_parseline(buf, &pname, &pval); + if(!cfg_rb->strcmp(name, pname)) + { + cfg_rb->close(fd); + return cfg_rb->atoi(pval); + } + } + + cfg_rb->close(fd); + return -1; +} + +int configfile_update_entry(const char* filename, const char* name, int val) +{ + int fd; + char *pname; + char *pval; + char path[MAX_PATH]; + char buf[256]; + int found = 0; + int line_len = 0; + int pos = 0; + + /* open the current config file */ + get_cfg_filename(path, MAX_PATH, filename); + fd = cfg_rb->open(path, O_RDWR); + if(fd < 0) + return -1; + + /* read in the current stored settings */ + while((line_len = cfg_rb->read_line(fd, buf, 256)) > 0) + { + cfg_rb->settings_parseline(buf, &pname, &pval); + + if(!cfg_rb->strcmp(name, pname)) + { + found = 1; + cfg_rb->lseek(fd, pos, SEEK_SET); + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "%s: %10d\n", pname, val); + break; + } + pos += line_len; + } + + /* if (name/val) is a new entry just append to file */ + if (found == 0) + /* pre-allocate 10 bytes for INT */ + cfg_rb->fdprintf(fd, "%s: %10d\n", name, val); + + cfg_rb->close(fd); + + return found; +} diff -Naur svn/apps/plugins/lib/configfile.h rockbox_svn_2007-08-31/apps/plugins/lib/configfile.h --- svn/apps/plugins/lib/configfile.h 2007-07-28 21:19:37.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/lib/configfile.h 2007-08-31 20:27:19.000000000 -0400 @@ -38,9 +38,41 @@ }; void configfile_init(struct plugin_api* newrb); + +/* configfile_save - Given configdata entries this function will + create a config file with these entries, destroying any + previous config file of the same name */ int configfile_save(const char *filename, struct configdata *cfg, int num_items, int version); + int configfile_load(const char *filename, struct configdata *cfg, int num_items, int min_version); +/* configfile_get_value - Given a key name, this function will + return the integer value for that key. + + Input: + filename = config file filename + name = (name/value) pair name entry + Return: + value if (name/value) pair is found + -1 if entry is not found +*/ +int configfile_get_value(const char* filename, const char* name); + +/* configure_update_entry - Given a key name and integer value + this function will update the entry if found, or add it if + not found. + + Input: + filename = config file filename + name = (name/value) pair name entry + val = new value for (name/value) pair + Return: + 1 if the (name/value) pair was found and updated with the new value + 0 if the (name/value) pair was added as a new entry + -1 if error +*/ +int configfile_update_entry(const char* filename, const char* name, int val); + #endif diff -Naur svn/apps/plugins/mpegplayer/alloc.c rockbox_svn_2007-08-31/apps/plugins/mpegplayer/alloc.c --- svn/apps/plugins/mpegplayer/alloc.c 2007-07-28 21:19:36.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/alloc.c 2007-08-31 20:27:19.000000000 -0400 @@ -47,7 +47,6 @@ (void)reason; - DEBUGF("mpeg2_malloc(%d,%d)\n",size,reason); if (mem_ptr + (long)size > bufsize) { DEBUGF("OUT OF MEMORY\n"); return NULL; @@ -56,11 +55,14 @@ x=&mallocbuf[mem_ptr]; mem_ptr+=(size+3)&~3; /* Keep memory 32-bit aligned */ + rb->memset(x,0,size); + + DEBUGF("mpeg2_malloc(%u,%d,%d)\n",(unsigned int)x,size,reason); return(x); } void mpeg2_free(void* ptr) { - (void)ptr; + mem_ptr = (void *)ptr - (void *)mallocbuf; } /* gcc may want to use memcpy before rb is initialised, so here's a trivial diff -Naur svn/apps/plugins/mpegplayer/header.c rockbox_svn_2007-08-31/apps/plugins/mpegplayer/header.c --- svn/apps/plugins/mpegplayer/header.c 2007-07-28 21:19:36.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/header.c 2007-08-31 20:27:19.000000000 -0400 @@ -58,7 +58,7 @@ 83 }; -uint8_t mpeg2_scan_norm[64] IDATA_ATTR = { +uint8_t default_mpeg2_scan_norm[64] IDATA_ATTR = { /* Zig-Zag scan pattern */ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, @@ -66,7 +66,7 @@ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; -uint8_t mpeg2_scan_alt[64] IDATA_ATTR = { +uint8_t default_mpeg2_scan_alt[64] IDATA_ATTR = { /* Alternate scan pattern */ 0, 8, 16, 24, 1, 9, 2, 10, 17, 25, 32, 40, 48, 56, 57, 49, 41, 33, 26, 18, 3, 11, 4, 12, 19, 27, 34, 42, 50, 58, 35, 43, @@ -74,6 +74,9 @@ 53, 61, 22, 30, 7, 15, 23, 31, 38, 46, 54, 62, 39, 47, 55, 63 }; +uint8_t mpeg2_scan_norm[64] IDATA_ATTR; +uint8_t mpeg2_scan_alt[64] IDATA_ATTR; + void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec) { if (mpeg2dec->sequence.width != (unsigned)-1) { diff -Naur svn/apps/plugins/mpegplayer/idct.c rockbox_svn_2007-08-31/apps/plugins/mpegplayer/idct.c --- svn/apps/plugins/mpegplayer/idct.c 2007-07-28 21:19:36.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/idct.c 2007-08-31 20:27:19.000000000 -0400 @@ -260,6 +260,8 @@ void mpeg2_idct_init (void) { + extern uint8_t default_mpeg2_scan_norm[64]; + extern uint8_t default_mpeg2_scan_alt[64]; extern uint8_t mpeg2_scan_norm[64]; extern uint8_t mpeg2_scan_alt[64]; int i, j; @@ -274,10 +276,10 @@ for (i = 0; i < 64; i++) { - j = mpeg2_scan_norm[i]; + j = default_mpeg2_scan_norm[i]; mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); - j = mpeg2_scan_alt[i]; + j = default_mpeg2_scan_alt[i]; mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); } } diff -Naur svn/apps/plugins/mpegplayer/mpegplayer.c rockbox_svn_2007-08-31/apps/plugins/mpegplayer/mpegplayer.c --- svn/apps/plugins/mpegplayer/mpegplayer.c 2007-08-21 15:27:34.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/mpegplayer.c 2007-08-31 21:04:26.000000000 -0400 @@ -110,6 +110,7 @@ #include "mpeg_settings.h" #include "video_out.h" #include "../../codecs/libmad/mad.h" +#include "splash.h" PLUGIN_HEADER PLUGIN_IRAM_DECLARE @@ -185,9 +186,7 @@ uint8_t* curr_packet_end; /* Current stream packet end */ 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 prev_packet_length; /* Lenth of previous packet */ size_t buffer_remaining; /* How much data is left in the buffer */ uint32_t curr_pts; /* Current presentation timestamp */ uint32_t curr_time; /* Current time in samples */ @@ -318,10 +317,20 @@ /* NOTE: Putting the following variables in IRAM cause audio corruption on the ipod (reason unknown) */ -static uint8_t *disk_buf IBSS_ATTR; -static uint8_t *disk_buf_end IBSS_ATTR; -static uint8_t *disk_buf_tail IBSS_ATTR; -static size_t buffer_size IBSS_ATTR; +static uint8_t *disk_buf_start IBSS_ATTR; /* Start pointer */ +static uint8_t *disk_buf_end IBSS_ATTR; /* End of buffer pointer less + MPEG_GUARDBUF_SIZE. The + guard space is used to wrap + data at the buffer start to + pass continuous data + packets */ +static uint8_t *disk_buf_tail IBSS_ATTR; /* Location of last data + 1 + filled into the buffer */ +static size_t disk_buf_size IBSS_ATTR; /* The total buffer length + including the guard + space */ +static size_t file_remaining IBSS_ATTR; + #if NUM_CORES > 1 /* Some stream variables are shared between cores */ struct mutex stream_lock IBSS_ATTR; @@ -341,11 +350,29 @@ { } #endif -/* Events */ -static struct event_queue msg_queue IBSS_ATTR; +static int audio_sync_start IBSS_ATTR; /* If 0, the audio thread + yields waiting on the video + thread to synchronize with + the stream */ +static uint32_t audio_sync_time IBSS_ATTR; /* The time that the video + thread has reached after + synchronizing. The + audio thread now needs + to advance to this + time */ +static int video_sync_start IBSS_ATTR; /* While 0, the video thread + yields until the audio + thread has reached the + audio_sync_time */ +static int video_thumb_print IBSS_ATTR; /* If 1, the video thread is + only decoding one frame for + use in the menu. If 0, + normal operation */ +static int play_time IBSS_ATTR; /* The movie time as represented by + the maximum audio PTS tag in the + stream converted to half minutes */ +char *filename; /* hack for resume time storage */ -#define MSG_BUFFER_NEARLY_EMPTY 1 -#define MSG_EXIT_REQUESTED 2 /* Various buffers */ /* TODO: Can we reduce the PCM buffer size? */ @@ -356,7 +383,7 @@ #define LIBMPEG2BUFFER_SIZE (2*1024*1024) /* 65536+6 is required since each PES has a 6 byte header with a 16 bit packet length field */ -#define MPEG_GUARDBUF_SIZE (64*1024+1024) /* Keep a bit extra - excessive for now */ +#define MPEG_GUARDBUF_SIZE (65*1024) /* Keep a bit extra - excessive for now */ #define MPEG_LOW_WATERMARK (1024*1024) static void pcm_playback_play_pause(bool play); @@ -477,8 +504,47 @@ ((p)[b3] << 6) | \ ((p)[b4] >> 2 ))) -/* This function demuxes the streams and gives the next stream data pointer */ -static void get_next_data( Stream* str ) +/* This function synchronizes the mpeg stream. The function returns + true on error */ +bool sync_data_stream(uint8_t **p) +{ + for (;;) + { + while ( !CMP_4_CONST(*p, PACK_START_CODE) && (*p) < disk_buf_tail ) + (*p)++; + if ( (*p) >= disk_buf_tail ) + break; + uint8_t *p_save = (*p); + if ( ((*p)[4] & 0xc0) == 0x40 ) /* mpeg-2 */ + (*p) += 14 + ((*p)[13] & 7); + else if ( ((*p)[4] & 0xf0) == 0x20 ) /* mpeg-1 */ + (*p) += 12; + else + (*p) += 5; + if ( (*p) >= disk_buf_tail ) + break; + if ( CMP_3_CONST(*p, PACKET_START_CODE_PREFIX) ) + { + (*p) = p_save; + break; + } + else + (*p) = p_save+1; + } + + if ( (*p) >= disk_buf_tail ) + return true; + else + return false; +} + +/* This function demuxes the streams and gives the next stream data + pointer. Type 0 is normal operation. Type 1 and 2 have been added + for rapid seeks into the data stream. Type 1 and 2 ignore the + video_sync_start state (a signal to yield for refilling the + buffer). Type 1 will append more data to the buffer tail (minumal + bufer size reads that are increased only as needed). */ +static int get_next_data( Stream* str, uint8_t type ) { uint8_t *p; uint8_t *header; @@ -487,29 +553,48 @@ static int mpeg1_skip_table[16] = { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - if (str->curr_packet_end == NULL) - { - /* What does this do? */ - while ((p = disk_buf) == NULL) - { - rb->lcd_putsxy(0,LCD_HEIGHT-10,"FREEZE!"); - rb->lcd_update(); - rb->sleep(HZ); - } - } - else - { - p = str->curr_packet_end; - } + if ( (p=str->curr_packet_end) == NULL) + p = disk_buf_start; while (1) { int length, bytes; + /* Yield for buffer filling */ + if ( (type == 0) && (str->buffer_remaining < 120*1024) && (file_remaining > 0) ) + while ( (str->buffer_remaining < 512*1024) && (file_remaining > 0) ) + rb->yield(); + + /* The packet start position (plus an arbitrary header length) + has exceeded the amount of data in the buffer */ + if ( type == 1 && (p+50) >= disk_buf_tail ) + { + DEBUGF("disk buffer overflow\n"); + return 1; + } + + /* are we at the end of file? */ + { + size_t tmp_length; + if (p < str->prev_packet) + tmp_length = (disk_buf_end - str->prev_packet) + + (p - disk_buf_start); + else + tmp_length = (p - str->prev_packet); + if (0 == str->buffer_remaining-tmp_length-str->prev_packet_length) + { + str->curr_packet_end = str->curr_packet = NULL; + break; + } + } + + /* wrap the disk buffer */ if (p >= disk_buf_end) - { - p = disk_buf + (p - disk_buf_end); - } + p = disk_buf_start + (p - disk_buf_end); + + /* wrap packet header if needed */ + if ( (p+50) >= disk_buf_end ) + rb->memcpy(disk_buf_end, disk_buf_start, 50); /* Pack header, skip it */ if (CMP_4_CONST(p, PACK_START_CODE)) @@ -527,7 +612,6 @@ rb->splash( 30, "Weird Pack header!" ); p += 5; } - /*rb->splash( 30, "Pack header" );*/ } /* System header, parse and skip it - four bytes */ @@ -541,29 +625,29 @@ p += header_length; - if (p >= disk_buf_end) - { - p = disk_buf + (p - disk_buf_end); - } - /*rb->splash( 30, "System header" );*/ + if ( p >= disk_buf_end ) + p = disk_buf_start + (p - disk_buf_end); } - + /* Packet header, parse it */ if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) { /* Problem */ - //rb->splash( HZ*3, "missing packet start code prefix : %X%X at %X", *p, *(p+2), p-disk_buf ); + rb->splash( HZ*3, "missing packet start code prefix : %X%X at %lX", + *p, *(p+2), (long unsigned int)(p-disk_buf_start) ); + + DEBUGF("end diff: %X,%X,%X,%X,%X,%X\n",(int)str->curr_packet_end, + (int)audio_str.curr_packet_end,(int)video_str.curr_packet_end, + (int)disk_buf_start,(int)disk_buf_end,(int)disk_buf_tail); + str->curr_packet_end = str->curr_packet = NULL; break; - //++p; - //break; } /* We retrieve basic infos */ stream = p[3]; length = (p[4] << 8) | p[5]; - /*rb->splash( 100, "Stream : %X", stream );*/ if (stream != str->id) { /* End of stream ? */ @@ -624,11 +708,13 @@ break; } } - - if ((header[length - 1] & 0xc0) == 0x40) - { + + if ( (header[length - 1] & 0xc0) == 0x40 ) length += 2; - } + + + + len_skip = length; length += mpeg1_skip_table[header[length - 1] >> 4]; @@ -663,23 +749,17 @@ if (bytes > 0) { str->curr_packet_end = p + bytes; - //DEBUGF("prev = %d, curr = %d\n",str->prev_packet,str->curr_packet); if (str->curr_packet != NULL) { lock_stream(); + str->buffer_remaining -= str->prev_packet_length; 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; - } + str->prev_packet_length = (disk_buf_end - str->prev_packet) + + (str->curr_packet - disk_buf_start); else - { - str->buffer_remaining -= (str->curr_packet - str->prev_packet); - } + str->prev_packet_length = (str->curr_packet - str->prev_packet); unlock_stream(); @@ -689,14 +769,12 @@ str->curr_packet = p; 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); - } + rb->memcpy(disk_buf_end, disk_buf_start, str->curr_packet_end - disk_buf_end ); } break; } /* end while */ + return 0; } /* Our clock rate in ticks/second - this won't be a constant for long */ @@ -954,6 +1032,8 @@ int vol, minvol, maxvol; int button; + if (video_sync_start==1) { + if (str_have_msg(&audio_str)) { struct event ev; @@ -1025,6 +1105,7 @@ rb->lcd_setfont(FONT_SYSFIXED); if (result) { + settings.resume_time = (int)(get_stream_time()/44100/30); str_send_msg(&video_str, STREAM_QUIT, 0); audio_str.status = STREAM_STOPPED; } else { @@ -1035,6 +1116,7 @@ break; case MPEG_STOP: + settings.resume_time = (int)(get_stream_time()/44100/30); str_send_msg(&video_str, STREAM_QUIT, 0); audio_str.status = STREAM_STOPPED; break; @@ -1071,7 +1153,7 @@ audio_str.status = STREAM_STOPPED; } } - + } quit: return audio_str.status; } @@ -1097,7 +1179,23 @@ pcm_playback_play(0); /* Get first packet */ - get_next_data(&audio_str); + get_next_data(&audio_str, 0 ); + + /* skip audio packets here */ + while (audio_sync_start==0) + { + audio_str.status = STREAM_PLAYING; + rb->yield(); + } + + if (audio_sync_time>10000) + { + while (TS_TO_TICKS(audio_str.curr_pts) < audio_sync_time - 10000) + { + get_next_data(&audio_str, 0 ); + rb->priority_yield(); + } + } if (audio_str.curr_packet == NULL) goto done; @@ -1176,7 +1274,7 @@ mpabuf = mpa_buffer; /* Get data from next audio packet */ - get_next_data(&audio_str); + get_next_data(&audio_str, 0 ); } while (audio_str.curr_packet != NULL && mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD); @@ -1209,8 +1307,6 @@ if (mad_stat != 0) { - DEBUGF("Audio stream error - %d\n", stream.error); - if (stream.error == MAD_FLAG_INCOMPLETE || stream.error == MAD_ERROR_BUFLEN) { @@ -1270,6 +1366,12 @@ rb->priority_yield(); } + if (video_sync_start == 0 && pts->pts+(uint32_t)synth.pcm.lengthyield(); + } + /* TODO: This part will be replaced with dsp calls soon */ if (MAD_NCHANNELS(&frame.header) == 2) { @@ -1316,6 +1418,7 @@ audio_str.status = STREAM_PLAYING; pcmbuf_threshold = PCMBUF_PLAY_ALL; pcm_playback_seek_time(pcmbuf_tail->time); + video_sync_start = 1; } /* Make this data available to DMA */ @@ -1403,29 +1506,32 @@ /* Clear the display - this is mainly just to indicate that the video thread has started successfully. */ - rb->lcd_clear_display(); - rb->lcd_update(); + if (!video_thumb_print) + { + rb->lcd_clear_display(); + rb->lcd_update(); + } /* Request the first packet data */ - get_next_data( &video_str ); + get_next_data( &video_str, 0 ); if (video_str.curr_packet == NULL) - goto done; + goto video_thread_quit; mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); total_offset += video_str.curr_packet_end - video_str.curr_packet; info = mpeg2_info (mpeg2dec); - /* Wait if the audio thread is buffering - i.e. before - the first frames are decoded */ - while (audio_str.status == STREAM_BUFFERING) - rb->priority_yield(); - while (1) { /* quickly check mailbox first */ - if (str_have_msg(&video_str)) + if (video_thumb_print) + { + if (video_str.status == STREAM_STOPPED) + break; + } + else if (str_have_msg(&video_str)) { while (1) { @@ -1460,7 +1566,8 @@ { case STATE_BUFFER: /* Request next packet data */ - get_next_data( &video_str ); + get_next_data( &video_str, 0 ); + mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); total_offset += video_str.curr_packet_end - video_str.curr_packet; info = mpeg2_info (mpeg2dec); @@ -1468,7 +1575,7 @@ if (video_str.curr_packet == NULL) { /* No more data. */ - goto done; + goto video_thread_quit; } continue; @@ -1538,8 +1645,12 @@ break; /* No limiting => no dropping - draw this frame */ - if (!settings.limitfps) + if (!settings.limitfps && (video_thumb_print == 0)) + { + audio_sync_start = 1; + video_sync_start = 1; goto picture_draw; + } /* Get presentation times in audio samples - quite accurate enough - add previous frame duration if not stamped */ @@ -1548,7 +1659,19 @@ period = TIME_TO_TICKS(info->sequence->frame_period); + if ( (video_thumb_print == 1 || video_sync_start == 0) && + ((int)(info->current_picture->flags & PIC_MASK_CODING_TYPE) + != PIC_FLAG_CODING_TYPE_I)) + break; + eta_video = curr_time; + + audio_sync_time = eta_video; + audio_sync_start = 1; + + while (video_sync_start == 0) + rb->yield(); + eta_audio = get_stream_time(); /* How early/late are we? > 0 = late, < 0 early */ @@ -1674,32 +1797,39 @@ picture_wait: /* Wait until audio catches up */ - while (eta_video > eta_audio) - { - rb->priority_yield(); - - /* Make sure not to get stuck waiting here forever */ - if (str_have_msg(&video_str)) + if (video_thumb_print) + video_str.status = STREAM_STOPPED; + else + while (eta_video > eta_audio) { - str_look_msg(&video_str, &ev); - - /* If not to play, process up top */ - if (ev.id != STREAM_PLAY) - goto rendering_finished; + rb->priority_yield(); + + /* Make sure not to get stuck waiting here forever */ + if (str_have_msg(&video_str)) + { + str_look_msg(&video_str, &ev); + + /* If not to play, process up top */ + if (ev.id != STREAM_PLAY) + goto rendering_finished; + + /* Told to play but already playing */ + str_get_msg(&video_str, &ev); + str_reply_msg(&video_str, 1); + } - /* Told to play but already playing */ - str_get_msg(&video_str, &ev); - str_reply_msg(&video_str, 1); + eta_audio = get_stream_time(); } - - eta_audio = get_stream_time(); - } - + picture_draw: /* Record last frame time */ last_render = *rb->current_tick; - vo_draw_frame(info->display_fbuf->buf); + if (video_thumb_print) + vo_draw_frame_thumb(info->display_fbuf->buf); + else + vo_draw_frame(info->display_fbuf->buf); + num_drawn++; picture_skip: @@ -1734,43 +1864,285 @@ rb->yield(); } -done: +video_thread_quit: + /* if video ends before time sync'd, + besure the audio thread is closed */ + if (video_sync_start == 0) + { + audio_str.status = STREAM_STOPPED; + audio_sync_start = 1; + } + flush_icache(); - video_str.status = STREAM_DONE; + mpeg2_close (mpeg2dec); - while (1) + /* Commit suicide */ + video_str.status = STREAM_TERMINATED; + rb->remove_thread(NULL); +} + +void initialize_stream( Stream *str, uint8_t *buffer_start, size_t disk_buf_len, int id ) +{ + str->curr_packet_end = str->curr_packet = NULL; + str->prev_packet_length = 0; + str->prev_packet = str->curr_packet_end = buffer_start; + str->buffer_remaining = disk_buf_len; + str->id = id; +} + +void display_thumb(int in_file) +{ + size_t disk_buf_len; + + video_thumb_print = 1; + audio_sync_start = 1; + video_sync_start = 1; + + disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); + disk_buf_tail = disk_buf_start + disk_buf_len; + file_remaining = 0; + initialize_stream(&video_str,disk_buf_start,disk_buf_len,0xe0); + + video_str.status = STREAM_PLAYING; + + if ((video_str.thread = rb->create_thread(video_thread, + (uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo" + IF_PRIO(,PRIORITY_PLAYBACK) + IF_COP(, COP, true))) == NULL) + { + rb->splash(HZ, "Cannot create video thread!"); + } + else { - str_get_msg(&video_str, &ev); + while (video_str.status != STREAM_TERMINATED) + rb->yield(); + } - if (ev.id == STREAM_QUIT) - break; + if ( video_str.curr_packet_end == video_str.curr_packet) + rb->splash(0, "frame not available"); +} + +int find_length( int in_file ) +{ + uint8_t *p; + size_t read_length = 60*1024; + size_t disk_buf_len; + + play_time = 0; - str_reply_msg(&video_str, 0); + /* temporary read buffer size cannot exceed buffer size */ + if ( read_length > disk_buf_size ) + read_length = disk_buf_size; + + /* read tail of file */ + rb->lseek( in_file, -1*read_length, SEEK_END ); + disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; } + + /* find last PTS in audio stream; will movie always have audio? if + the play time can not be determined, set play_time to 0 */ + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; + { + Stream tmp; + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + + do + { + get_next_data(&tmp, 2); + if (tmp.tagged == 1) + /* 10 sec less to insure the video frame exist */ + play_time = (int)((tmp.curr_pts/45000-10)/30); + } + while (tmp.curr_packet_end != NULL); + } + return 0; +} + +ssize_t seek_PTS( int in_file, int start_time, int accept_button ) +{ + static ssize_t last_seek_pos = 0; + static int last_start_time = 0; + ssize_t seek_pos; + size_t disk_buf_len; + uint8_t *p; + size_t read_length = 60*1024; -video_thread_quit: - flush_icache(); + /* temporary read buffer size cannot exceed buffer size */ + if ( read_length > disk_buf_size ) + read_length = disk_buf_size; - /* Commit suicide */ - video_str.status = STREAM_TERMINATED; - rb->remove_thread(NULL); + if ( start_time == last_start_time ) + { + seek_pos = last_seek_pos; + rb->lseek(in_file,seek_pos,SEEK_SET); + } + else if ( start_time != 0 ) + { + seek_pos = rb->filesize(in_file)*start_time/play_time; + int seek_pos_sec_inc = rb->filesize(in_file)/play_time/30; + + if (seek_pos<0) + seek_pos=0; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; + } + + /* find PTS >= start_time */ + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; + { + Stream tmp; + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + int cont_seek_loop = 1; + int coarse_seek = 1; + do + { + if ( accept_button ) + { + rb->yield(); + if (rb->button_available()) + return -101; + } + + while ( get_next_data(&tmp, 1) == 1 ) + { + if ( tmp.curr_packet_end == disk_buf_start ) + seek_pos += disk_buf_tail - disk_buf_start; + else + seek_pos += tmp.curr_packet_end - disk_buf_start; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + initialize_stream(&tmp,p,disk_buf_len,0xc0); + } + + /* are we after start_time in the stream? */ + if ( coarse_seek && (int)(tmp.curr_pts/45000) >= start_time*30 ) + { + int time_to_backup = (int)(tmp.curr_pts/45000) - start_time*30; + if (time_to_backup == 0) + time_to_backup++; + seek_pos -= seek_pos_sec_inc * time_to_backup; + seek_pos_sec_inc -= seek_pos_sec_inc/20; /* for stability */ + if (seek_pos<0) + seek_pos=0; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; + } + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + continue; + } + + /* are we well before start_time in the stream? */ + if ( coarse_seek && start_time*30 - (int)(tmp.curr_pts/45000) > 2 ) + { + int time_to_advance = start_time*30 - (int)(tmp.curr_pts/45000) - 2; + if (time_to_advance <= 0) + time_to_advance = 1; + seek_pos += seek_pos_sec_inc * time_to_advance; + if (seek_pos<0) + seek_pos=0; + if ((size_t)seek_pos > rb->filesize(in_file) - read_length) + seek_pos = rb->filesize(in_file) - read_length; + rb->lseek( in_file, seek_pos, SEEK_SET ); + disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); + disk_buf_tail = disk_buf_start + disk_buf_len; + + /* sync reader to this segment of the stream */ + p=disk_buf_start; + if (sync_data_stream(&p)) + { + DEBUGF("Could not sync stream\n"); + return PLUGIN_ERROR; + } + initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); + continue; + } + + coarse_seek = 0; + + /* are we at start_time in the stream? */ + if ( (int)(tmp.curr_pts/45000) >= start_time*30 ) + cont_seek_loop = 0; + + } + while ( cont_seek_loop ); + + + DEBUGF("start diff: %u %u\n",(unsigned int)(tmp.curr_pts/45000),start_time*30); + seek_pos+=tmp.curr_packet_end-disk_buf_start; + + last_seek_pos = seek_pos; + last_start_time = start_time; + + rb->lseek(in_file,seek_pos,SEEK_SET); + } + } + else + { + seek_pos = 0; + rb->lseek(in_file,0,SEEK_SET); + last_seek_pos = seek_pos; + last_start_time = start_time; + } + return seek_pos; } enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { int status = PLUGIN_ERROR; /* assume failure */ + int start_time=-1; void* audiobuf; ssize_t audiosize; int in_file; - uint8_t* buffer; - size_t file_remaining; size_t disk_buf_len; + ssize_t seek_pos; #ifndef HAVE_LCD_COLOR long graysize; int grayscales; #endif + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; + if (parameter == NULL) { api->splash(HZ*2, "No File"); @@ -1781,7 +2153,9 @@ PLUGIN_IRAM_INIT(api) rb = api; + rb->splash(0, "loading ..."); + /* sets audiosize and returns buffer pointer */ audiobuf = rb->plugin_get_audio_buffer(&audiosize); #if INPUT_SRC_CAPS != 0 @@ -1792,47 +2166,36 @@ rb->pcm_set_frequency(SAMPR_44); - /* Set disk pointers to NULL */ - disk_buf_end = disk_buf = NULL; - - /* Stream construction */ - /* We take the first stream of each (audio and video) */ - /* TODO : Search for these in the file first */ - audio_str.curr_packet_end = audio_str.curr_packet = audio_str.next_packet = NULL; - video_str = audio_str; - video_str.id = 0xe0; - audio_str.id = 0xc0; +#ifndef HAVE_LCD_COLOR + /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */ + grayscales = gray_init(rb, audiobuf, audiosize, false, LCD_WIDTH, LCD_HEIGHT, + 32, 2<<8, &graysize) + 1; + audiobuf += graysize; + audiosize -= graysize; + if (grayscales < 33 || audiosize <= 0) + { + rb->splash(HZ, "gray buf error"); + return PLUGIN_ERROR; + } +#endif /* Initialise our malloc buffer */ mpeg2_alloc_init(audiobuf,audiosize); + /* Set disk pointers to NULL */ + disk_buf_end = disk_buf_start = NULL; + /* 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+PCMBUFFER_GUARD_SIZE+ + disk_buf_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+ MPABUF_SIZE+LIBMPEG2BUFFER_SIZE); - DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size); - buffer = mpeg2_malloc(buffer_size,-1); + DEBUGF("audiosize=%ld, disk_buf_size=%ld\n",audiosize,disk_buf_size); + disk_buf_start = mpeg2_malloc(disk_buf_size,-1); - if (buffer == NULL) + if (disk_buf_start == NULL) return PLUGIN_ERROR; -#ifndef HAVE_LCD_COLOR - /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */ - grayscales = gray_init(rb, buffer, buffer_size, false, LCD_WIDTH, LCD_HEIGHT, - 32, 2<<8, &graysize) + 1; - buffer += graysize; - buffer_size -= graysize; - if (grayscales < 33 || buffer_size <= 0) - { - rb->splash(HZ, "gray buf error"); - return PLUGIN_ERROR; - } -#endif - - buffer_size &= ~(0x7ff); /* Round buffer down to nearest 2KB */ - DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size); - if (!init_mpabuf()) return PLUGIN_ERROR; @@ -1845,9 +2208,10 @@ in_file = rb->open((char*)parameter,O_RDONLY); if (in_file < 0){ - //fprintf(stderr,"Could not open %s\n",argv[1]); + DEBUGF("Could not open %s\n",(char*)parameter); return PLUGIN_ERROR; } + filename = (char*)parameter; #ifdef HAVE_LCD_COLOR rb->lcd_set_backdrop(NULL); @@ -1867,36 +2231,51 @@ /* From this point on we've altered settings, colors, cpu_boost, etc. and cannot just return PLUGIN_ERROR - instead drop though to cleanup code */ - - init_settings(); - - /* Msg queue init - no need for queue_remove since it's not a registered - queue */ - rb->queue_init( &msg_queue, false ); + init_settings((char*)parameter); /* Initialise libmad */ rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap)); init_mad(mad_frame_overlap); - file_remaining = rb->filesize(in_file); - disk_buf_end = buffer + buffer_size-MPEG_GUARDBUF_SIZE; + disk_buf_end = disk_buf_start + disk_buf_size-MPEG_GUARDBUF_SIZE; + + /* initalize play_time with the length (in half minutes) of the movie + zero if the time could not be determined */ + find_length( in_file ); + + /* start menu */ + start_time = mpeg_start_menu(play_time, in_file); + if ( start_time == -1 ) + return 0; + else if ( start_time < 0 ) + start_time = 0; + else if ( start_time > play_time ) + start_time = play_time; + + rb->splash(0, "loading ..."); + + /* seek start time */ + seek_pos = seek_PTS( in_file, start_time, 0 ); + + rb->lseek(in_file,seek_pos,SEEK_SET); + video_thumb_print = 0; + audio_sync_start = 0; + audio_sync_time = 0; + video_sync_start = 0; /* Read some stream data */ - disk_buf_len = rb->read (in_file, buffer, MPEG_LOW_WATERMARK); + disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); + + disk_buf_tail = disk_buf_start + disk_buf_len; + file_remaining = rb->filesize(in_file); + file_remaining -= disk_buf_len + seek_pos; - DEBUGF("Initial Buffering - %d bytes\n",(int)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; + initialize_stream( &video_str, disk_buf_start, disk_buf_len, 0xe0 ); + initialize_stream( &audio_str, disk_buf_start, disk_buf_len, 0xc0 ); rb->spinlock_init(&audio_str.msg_lock); rb->spinlock_init(&video_str.msg_lock); + audio_str.status = STREAM_BUFFERING; video_str.status = STREAM_PLAYING; @@ -1923,7 +2302,6 @@ } else { - //DEBUGF("START: video = %d, audio = %d\n",audio_str.buffer_remaining,video_str.buffer_remaining); rb->lcd_setfont(FONT_SYSFIXED); /* Wait until both threads have finished their work */ @@ -1932,16 +2310,28 @@ size_t audio_remaining = audio_str.buffer_remaining; size_t video_remaining = video_str.buffer_remaining; - if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) { + if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) + { - size_t bytes_to_read = buffer_size - MPEG_GUARDBUF_SIZE - + size_t bytes_to_read = disk_buf_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) && - ((audio_str.status >= 0) || (video_str.status >= 0))) { - size_t n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); + ((audio_str.status != STREAM_DONE) || (video_str.status != STREAM_DONE))) + { + + size_t n; + if ( video_sync_start != 0 ) + n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); + else + { + n = rb->read(in_file, disk_buf_tail,bytes_to_read); + if (n==0) + rb->splash(30,"buffer fill error"); + } + bytes_to_read -= n; file_remaining -= n; @@ -1957,7 +2347,7 @@ } if (disk_buf_tail == disk_buf_end) - disk_buf_tail = buffer; + disk_buf_tail = disk_buf_start; } rb->sleep(HZ/10); @@ -1976,6 +2366,8 @@ rb->sleep(HZ/10); + vo_cleanup(); + #ifndef HAVE_LCD_COLOR gray_release(); #endif @@ -1983,8 +2375,6 @@ rb->lcd_clear_display(); rb->lcd_update(); - mpeg2_close (mpeg2dec); - rb->close (in_file); #ifdef HAVE_ADJUSTABLE_CPU_FREQ diff -Naur svn/apps/plugins/mpegplayer/mpeg_settings.c rockbox_svn_2007-08-31/apps/plugins/mpegplayer/mpeg_settings.c --- svn/apps/plugins/mpegplayer/mpeg_settings.c 2007-07-28 21:19:36.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/mpeg_settings.c 2007-08-31 21:03:34.000000000 -0400 @@ -7,39 +7,352 @@ extern struct plugin_api* rb; struct mpeg_settings settings; -static struct mpeg_settings old_settings; + +ssize_t seek_PTS(int in_file, int startTime, int accept_button); +void display_thumb(int in_file); #define SETTINGS_VERSION 1 #define SETTINGS_MIN_VERSION 1 #define SETTINGS_FILENAME "mpegplayer.cfg" -static char* showfps_options[] = {"No", "Yes"}; -static char* limitfps_options[] = {"No", "Yes"}; -static char* skipframes_options[] = {"No", "Yes"}; +const unsigned char rockbox91x32[] = { + 0x00, 0x02, 0x7f, 0x02, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf8, 0xf8, 0xf0, + 0xe0, 0x80, 0x00, 0x00, 0x80, 0xe0, 0xf0, 0xf0, 0xf8, 0xfa, 0xfa, 0xf4, 0xf4, + 0xe8, 0x90, 0x60, 0x80, 0x60, 0x90, 0xe8, 0xf4, 0xf4, 0xfa, 0xfa, 0xfa, 0xfa, + 0xf4, 0xe8, 0x04, 0xf4, 0xf4, 0xf4, 0x04, 0xfe, 0x04, 0xc0, 0xf0, 0xf4, 0xf4, + 0x34, 0x1e, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x70, 0x88, 0xf0, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02, 0xc2, 0x3c, 0xc3, 0xff, + 0xff, 0xff, 0x00, 0xfe, 0xff, 0xff, 0x07, 0x01, 0x00, 0xfe, 0x02, 0xfd, 0x07, + 0xff, 0xff, 0xfe, 0x01, 0xfe, 0xff, 0xff, 0x0f, 0x01, 0x00, 0x80, 0x00, 0x01, + 0x03, 0x0f, 0x00, 0xff, 0xff, 0xff, 0xc0, 0xf9, 0xfe, 0xff, 0xff, 0x07, 0x04, + 0x04, 0x04, 0xfc, 0x80, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x40, 0x40, + 0x40, 0x40, 0x80, 0x00, 0x80, 0x40, 0x80, 0x40, 0x40, 0x40, 0x40, 0xc0, 0x00, + + 0x00, 0x7c, 0xc3, 0x9e, 0x67, 0x37, 0x8f, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, + 0xe3, 0x80, 0x00, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x01, 0x03, 0xc1, 0x3f, 0xc1, + 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff, 0xc1, 0x01, 0x01, 0xff, 0x01, 0x00, + 0x00, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xcf, 0x3f, 0xff, 0xf8, 0xff, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x10, 0x08, 0x18, 0xe8, 0x10, 0xe0, 0x00, 0x01, 0x02, 0x04, + 0x02, 0x01, 0xe0, 0x10, 0x08, 0x08, 0x18, 0xe8, 0x10, 0xe0, 0x00, 0x01, 0x00, + 0x00, 0xe0, 0x40, 0x01, 0x02, 0x01, 0x40, 0xe0, 0x18, 0xe4, 0x1b, 0x04, 0x03, + + 0x00, 0x00, 0x00, 0x00, 0x77, 0x6e, 0x61, 0x7f, 0x40, 0xff, 0x00, 0x07, 0x1f, + 0x3f, 0x7f, 0x7c, 0x71, 0xcf, 0x5f, 0x3f, 0x7e, 0x7c, 0x7d, 0x7d, 0x7e, 0x3f, + 0x1f, 0x0f, 0x01, 0x00, 0x41, 0x47, 0x5f, 0x7f, 0x7e, 0x7c, 0x7f, 0x7c, 0x7e, + 0x3f, 0x1f, 0x00, 0x7f, 0x7f, 0x7f, 0x01, 0x4e, 0x71, 0xcf, 0x7f, 0x40, 0x40, + 0x40, 0x40, 0x60, 0x21, 0x42, 0x43, 0x42, 0x41, 0x40, 0x20, 0x10, 0x08, 0x04, + 0x08, 0x10, 0x20, 0x41, 0x42, 0x42, 0x43, 0x42, 0x41, 0x40, 0x60, 0x50, 0x40, + 0x40, 0x40, 0x20, 0x50, 0x28, 0x10, 0x20, 0x40, 0x43, 0x44, 0x5b, 0x64, 0x18, +}; + +#define LOGO rockbox91x32 +#define LOGO_WIDTH 91 +#define LOGO_HEIGHT 32 + +enum sliderState_t {state0, state1, state2, state3, state4, state5} sliderState; +volatile long thumbDelayTimer; + +/* button definitions */ +#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) +#define MPEG_SELECT BUTTON_PLAY +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_UP +#define MPEG_SCROLL_UP BUTTON_SCROLL_DOWN +#define MPEG_EXIT BUTTON_POWER + +#elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD) +#define MPEG_SELECT BUTTON_SELECT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_BACK +#define MPEG_SCROLL_UP BUTTON_SCROLL_FWD +#define MPEG_EXIT BUTTON_MENU + +#elif CONFIG_KEYPAD == GIGABEAT_PAD +#define MPEG_SELECT BUTTON_SELECT +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_SCROLL_DOWN BUTTON_VOL_DOWN +#define MPEG_SCROLL_UP BUTTON_VOL_UP +#define MPEG_EXIT BUTTON_POWER + +#elif CONFIG_KEYPAD == IRIVER_H10_PAD +#define MPEG_SELECT BUTTON_PLAY +#define MPEG_SCROLL_UP BUTTON_SCROLL_UP +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_DOWN +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_EXIT BUTTON_POWER + +#elif CONFIG_KEYPAD == SANSA_E200_PAD +#define MPEG_SELECT BUTTON_SELECT +#define MPEG_SCROLL_UP BUTTON_SCROLL_UP +#define MPEG_SCROLL_DOWN BUTTON_SCROLL_DOWN +#define MPEG_LEFT BUTTON_LEFT +#define MPEG_RIGHT BUTTON_RIGHT +#define MPEG_EXIT BUTTON_POWER + +#else +#error MPEGPLAYER: Unsupported keypad +#endif static struct configdata config[] = { - {TYPE_ENUM, 0, 2, &settings.showfps, "Show FPS", showfps_options, NULL}, - {TYPE_ENUM, 0, 2, &settings.limitfps, "Limit FPS", limitfps_options, NULL}, - {TYPE_ENUM, 0, 2, &settings.skipframes, "Skip frames", skipframes_options, NULL}, + {TYPE_INT, 0, 2, &settings.showfps, "Show FPS", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.limitfps, "Limit FPS", NULL, NULL}, + {TYPE_INT, 0, 2, &settings.skipframes, "Skip frames", NULL, NULL}, }; +void draw_logo(int x, int y, int w, int h) +{ + rb->lcd_mono_bitmap(LOGO, x+((w-LOGO_WIDTH)/2), y+((h-LOGO_HEIGHT)/2), + LOGO_WIDTH, LOGO_HEIGHT); + rb->lcd_drawrect(x, y, w, h); + rb->lcd_update_rect(x, y, w, h); +} + +void draw_slider(int slider_ypos, int max_val, int current_val) +{ + float slider_margin = LCD_WIDTH*0.12; + float slider_width = LCD_WIDTH-(2*slider_margin); + float fill_rate = slider_width/max_val; + char resume_str[32]; + + /* max_val and current_val are in half minutes + determine value .0 or .5 to display */ + int max_hol = (int)((float)max_val/2.0f); + int max_rem = ((max_val%2)==0) ? 0 : 5; + int current_hol = (int)((float)current_val/2.0f); + int current_rem = ((current_val%2)==0) ? 0 : 5; + + rb->snprintf(resume_str, sizeof(resume_str), "0.0"); + rb->lcd_putsxy(slider_margin-5, slider_ypos, resume_str); + + rb->snprintf(resume_str, sizeof(resume_str), "%d.%d", max_hol, max_rem); + rb->lcd_putsxy(LCD_WIDTH-slider_margin-26, slider_ypos, resume_str); + + rb->lcd_drawrect(slider_margin, slider_ypos+17, slider_width, 8); + rb->lcd_fillrect(slider_margin, slider_ypos+17, + fill_rate*current_val, 8); + + rb->snprintf(resume_str, sizeof(resume_str), "%d.%d", current_hol, current_rem); + rb->lcd_putsxy(slider_margin+(fill_rate*current_val-16), + slider_ypos+29, resume_str); + + rb->lcd_update_rect(0, slider_ypos, LCD_WIDTH, LCD_HEIGHT-slider_ypos); +} + +int get_start_time(int play_time, int in_file) +{ + int quit = 0; + int button = 0; + int resume_time = settings.resume_time; + + int preview_height = 90; + int preview_width = 114; + int preview_ypos = (LCD_HEIGHT/2)-(preview_height/2); + int preview_xpos = (LCD_WIDTH/2)-(preview_width/2); + + int seek_rtn; + + sliderState = state0; + thumbDelayTimer = *(rb->current_tick); + draw_logo(preview_xpos, preview_ypos, preview_width, preview_height); + + while(quit == 0) + { + button = rb->button_get(false); + switch (button) + { + case MPEG_SCROLL_UP: + case MPEG_SCROLL_UP | BUTTON_REPEAT: + case MPEG_LEFT: + case MPEG_LEFT | BUTTON_REPEAT: + if (--resume_time < 0) + resume_time = 0; + sliderState = state0; + thumbDelayTimer = *(rb->current_tick); + break; + case MPEG_SCROLL_DOWN: + case MPEG_SCROLL_DOWN | BUTTON_REPEAT: + case MPEG_RIGHT: + case MPEG_RIGHT | BUTTON_REPEAT: + if (++resume_time > play_time) + resume_time = play_time; + sliderState = state0; + thumbDelayTimer = *(rb->current_tick); + break; + case MPEG_SELECT: + quit = 1; + break; + case MPEG_EXIT: + resume_time = -1; + quit = 1; + break; + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + { + resume_time = -1; + quit = 1; + } + break; + } + + rb->yield(); + + if (sliderState == state0) + { + rb->lcd_clear_display(); + rb->splash(0, "loading ..."); + sliderState = state1; + } + + else if (sliderState == state1) + { + if (*(rb->current_tick) - thumbDelayTimer > 75) + sliderState = state2; + if (resume_time == 0) + { + seek_rtn = 0; + draw_logo(preview_xpos, preview_ypos, preview_width, preview_height); + sliderState = state5; + } + draw_slider(preview_ypos+preview_height+20, play_time, resume_time); + } + + else if (sliderState == 2) + { + if ( (seek_rtn = seek_PTS(in_file, resume_time, 1)) >= 0) + sliderState = state3; + else if ( seek_rtn == -101) + { + sliderState = 0; + thumbDelayTimer = *(rb->current_tick); + } + else + { + draw_logo(preview_xpos, preview_ypos, preview_width, preview_height); + sliderState = state4; + } + } + + else if (sliderState == 3) + { + display_thumb(in_file); + sliderState = 4; + } + + else if (sliderState == 4) + { + draw_slider(preview_ypos+preview_height+20, play_time, resume_time); + sliderState = 5; + } + } + + return resume_time; +} + +int mpeg_start_menu(int play_time, int in_file) +{ + int m; + int result = 0; + int menu_quit = 0; + + /* add the resume time to the menu display */ + char resume_str[32]; + int time_hol = (int)((float)settings.resume_time/2.0f); + int time_rem = ((settings.resume_time%2)==0) ? 0 : 5; + rb->snprintf(resume_str, sizeof(resume_str), + "Resume time (min): %d.%d", time_hol, time_rem); + + struct menu_item items[] = { + { "Play from beginning", NULL }, + { resume_str, NULL }, + { "Set start time (min)", NULL }, + { "Quit mpegplayer", NULL }, + }; + + m = menu_init(rb, items, sizeof(items) / sizeof(*items), + NULL, NULL, NULL, NULL); + + rb->button_clear_queue(); + + while(menu_quit == 0) + { + result = menu_show(m); + + switch (result) + { + case 0: + menu_quit = 1; + result = 0; + break; + case 1: + menu_quit = 1; + result = settings.resume_time; + break; + case 2: + if ((result = get_start_time(play_time, in_file)) >= 0) + menu_quit = 1; + break; + case 3: + menu_quit = 1; + result = -1; + break; + default: + if (result == MENU_ATTACHED_USB) + { + menu_quit = 1; + result = -1; + } + break; + } + } + menu_exit(m); + + settings.resume_time = result; + return (int)result; +} + +void clear_resume_count() +{ + configfile_save(SETTINGS_FILENAME, config, + sizeof(config)/sizeof(*config), + SETTINGS_VERSION); + + settings.resume_count = 0; + + /* add this place holder so the count is above resume entries */ + configfile_update_entry(SETTINGS_FILENAME, "Resume count", 0); +} + bool mpeg_menu(void) { int m; int result; int menu_quit=0; + /* add the clear resume option to the menu display */ + char clear_str[32]; + rb->snprintf(clear_str, sizeof(clear_str), + "Clear all resumes: %u", settings.resume_count); + static const struct opt_items noyes[2] = { { "No", -1 }, - { "Yes", -1 }, + { "Yes", -1 }, }; - static const struct menu_item items[] = { + struct menu_item items[] = { { "Display FPS", NULL }, { "Limit FPS", NULL }, { "Skip frames", NULL }, - { "Quit mpegplayer", NULL }, + { clear_str, NULL }, + { "Quit mpegplayer", NULL } }; m = menu_init(rb, items, sizeof(items) / sizeof(*items), @@ -64,6 +377,11 @@ rb->set_option("Skip frames",&settings.skipframes,INT, noyes, 2, NULL); break; + case 3: /* Clear resumes */ + clear_resume_count(); + rb->snprintf(clear_str, sizeof(clear_str), + "Clear all resumes: %u", 0); + break; default: menu_quit=1; if (result == MENU_ATTACHED_USB) @@ -77,45 +395,65 @@ rb->lcd_clear_display(); rb->lcd_update(); - return (result==3); + return (result==4); } -void init_settings(void) +void init_settings(const char* filename) { /* Set the default settings */ settings.showfps = 0; /* Do not show FPS */ settings.limitfps = 1; /* Limit FPS */ settings.skipframes = 1; /* Skip frames */ + settings.resume_count = -1; configfile_init(rb); - if (configfile_load(SETTINGS_FILENAME, config, - sizeof(config)/sizeof(*config), - SETTINGS_MIN_VERSION - ) < 0) + /* If the config file don't contain resume count + or the load fails, then rebuild the config file. + This eliminates the worry for older config files + having unused data. */ + if (((settings.resume_count = configfile_get_value + (SETTINGS_FILENAME, "Resume count")) < 0) || + (configfile_load(SETTINGS_FILENAME, config, + sizeof(config)/sizeof(*config), + SETTINGS_MIN_VERSION) < 0)) { - /* If the loading failed, save a new config file (as the disk is - already spinning) */ + /* Generate a new config file with default values */ configfile_save(SETTINGS_FILENAME, config, sizeof(config)/sizeof(*config), SETTINGS_VERSION); } - /* Keep a copy of the saved version of the settings - so we can check if - the settings have changed when we quit */ - old_settings = settings; + if (settings.resume_count < 0) + { + settings.resume_count = 0; + + /* add this place holder so the count is above resume entries */ + configfile_update_entry(SETTINGS_FILENAME, "Resume count", 0); + } + + rb->strcpy(settings.resume_filename, filename); + + /* get the resume time for the current mpeg if it exist */ + if ((settings.resume_time = configfile_get_value + (SETTINGS_FILENAME, filename)) < 0) + { + settings.resume_time = 0; + } } void save_settings(void) { - /* Save the user settings if they have changed */ - if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) { - configfile_save(SETTINGS_FILENAME, config, - sizeof(config)/sizeof(*config), - SETTINGS_VERSION); - - /* Store the settings in old_settings - to check for future changes */ - old_settings = settings; - } + configfile_update_entry(SETTINGS_FILENAME, "Show FPS", settings.showfps); + configfile_update_entry(SETTINGS_FILENAME, "Limit FPS", settings.limitfps); + configfile_update_entry(SETTINGS_FILENAME, "Skip frames", settings.skipframes); + + /* If this was a new resume entry then update the total resume count */ + if (configfile_update_entry(SETTINGS_FILENAME, settings.resume_filename, + settings.resume_time) == 0) + { + configfile_update_entry(SETTINGS_FILENAME, "Resume count", + ++settings.resume_count); + } } diff -Naur svn/apps/plugins/mpegplayer/mpeg_settings.h rockbox_svn_2007-08-31/apps/plugins/mpegplayer/mpeg_settings.h --- svn/apps/plugins/mpegplayer/mpeg_settings.h 2007-07-28 21:19:36.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/mpeg_settings.h 2007-08-31 20:27:19.000000000 -0400 @@ -1,14 +1,20 @@ #include "plugin.h" -struct mpeg_settings { - int showfps; - int limitfps; - int skipframes; +struct mpeg_settings +{ + int showfps; /* flag to display fps */ + int limitfps; /* flag to limit fps */ + int skipframes; /* flag to skip frames */ + int resume_count; /* total # of resumes in config file */ + int resume_time; /* resume time for current mpeg (in half minutes) */ + char resume_filename[128]; /* filename of current mpeg */ }; extern struct mpeg_settings settings; +int get_start_time(int play_time, int in_file); +int mpeg_start_menu(int play_time, int in_file); bool mpeg_menu(void); -void init_settings(void); +void init_settings(const char* filename); void save_settings(void); diff -Naur svn/apps/plugins/mpegplayer/video_out.h rockbox_svn_2007-08-31/apps/plugins/mpegplayer/video_out.h --- svn/apps/plugins/mpegplayer/video_out.h 2007-07-28 21:19:36.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/video_out.h 2007-08-31 20:27:19.000000000 -0400 @@ -22,4 +22,6 @@ */ void vo_draw_frame (uint8_t * const * buf); +void vo_draw_frame_thumb (uint8_t * const * buf); void vo_setup (const mpeg2_sequence_t * sequence); +void vo_cleanup (); diff -Naur svn/apps/plugins/mpegplayer/video_out_rockbox.c rockbox_svn_2007-08-31/apps/plugins/mpegplayer/video_out_rockbox.c --- svn/apps/plugins/mpegplayer/video_out_rockbox.c 2007-07-28 21:19:36.000000000 -0400 +++ rockbox_svn_2007-08-31/apps/plugins/mpegplayer/video_out_rockbox.c 2007-08-31 20:27:19.000000000 -0400 @@ -264,10 +264,111 @@ #define SCREEN_HEIGHT LCD_WIDTH #endif +uint8_t* tmpbufa = 0; +uint8_t* tmpbufb = 0; +uint8_t* tmpbufc = 0; +uint8_t* tmpbuf[3]; + +void vo_draw_frame_thumb (uint8_t * const * buf) +{ + +/* DEBUGF("screen (w,h) (%u,%i)\n",SCREEN_WIDTH,SCREEN_HEIGHT); */ +/* DEBUGF("image (w,h) (%u,%i)\n",image_width,image_height); */ + + int r,c; + + /* image_width is 224 (224x176)*/ + +#if LCD_WIDTH >= LCD_HEIGHT + for (r=0;r= LCD_HEIGHT + yuv_bitmap_part(tmpbuf,0,0,image_width/2, + (LCD_WIDTH-1-image_width/2)/2, + (LCD_HEIGHT-1-image_height/2)/2, + output_width/2,output_height/2); + + rb->lcd_update_rect(output_x,output_y,output_width,output_height); +#else + yuv_bitmap_part(tmpbuf,0,0,image_height/2, + (LCD_HEIGHT-1-image_height/2)/2, + (LCD_WIDTH-1-image_width/2)/2, + output_height/2,output_width/2); + + rb->lcd_update_rect(output_y,output_x,output_height,output_width); +#endif +#else +#if LCD_WIDTH >= LCD_HEIGHT + rb->lcd_yuv_blit(tmpbuf,0,0,image_width/2, + (LCD_WIDTH-1-image_width/2)/2, + (LCD_HEIGHT-1-image_height/2)/2, + output_width/2,output_height/2); +#else + rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2, + (LCD_HEIGHT-1-image_height/2)/2, + (LCD_WIDTH-1-image_width/2)/2, + output_height/2,output_width/2); +#endif +#endif +#else +#if LCD_WIDTH >= LCD_HEIGHT + gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_width/2, + (LCD_WIDTH-1-image_width/2)/2, + (LCD_HEIGHT-1-image_height/2)/2, + output_width/2,output_height/2); +#else + gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_height/2, + (LCD_HEIGHT-1-image_height/2)/2, + (LCD_WIDTH-1-image_width/2)/2, + output_height/2,output_width/2); +#endif +#endif +} + void vo_setup(const mpeg2_sequence_t * sequence) { image_width=sequence->width; image_height=sequence->height; + + tmpbufa = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/2* + image_height/2, -2); + tmpbufb = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/4* + image_height/4, -2); + tmpbufc = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/4* + image_height/4, -2); + tmpbuf[0] = tmpbufa; + tmpbuf[1] = tmpbufb; + tmpbuf[2] = tmpbufc; + image_chroma_x=image_width/sequence->chroma_width; image_chroma_y=image_height/sequence->chroma_height; @@ -287,3 +388,13 @@ output_y = (SCREEN_HEIGHT-sequence->display_height)/2; } } + +void vo_cleanup() +{ + if (tmpbufc) + mpeg2_free(tmpbufc); + if (tmpbufb) + mpeg2_free(tmpbufb); + if (tmpbufa) + mpeg2_free(tmpbufa); +} diff -Naur svn/firmware/drivers/button.c rockbox_svn_2007-08-31/firmware/drivers/button.c --- svn/firmware/drivers/button.c 2007-08-14 00:09:51.000000000 -0400 +++ rockbox_svn_2007-08-31/firmware/drivers/button.c 2007-08-31 20:27:19.000000000 -0400 @@ -288,6 +288,11 @@ } #endif /* HAVE_ADJUSTABLE_CPU_FREQ */ +int button_available( void ) +{ + return queue_count(&button_queue); +} + long button_get(bool block) { struct event ev; diff -Naur svn/firmware/export/button.h rockbox_svn_2007-08-31/firmware/export/button.h --- svn/firmware/export/button.h 2007-07-28 21:19:59.000000000 -0400 +++ rockbox_svn_2007-08-31/firmware/export/button.h 2007-08-31 20:27:19.000000000 -0400 @@ -27,6 +27,7 @@ extern struct event_queue button_queue; void button_init (void); +int button_available(void); long button_get (bool block); long button_get_w_tmo(int ticks); intptr_t button_get_data(void); diff -Naur svn/uisimulator/sdl/button.c rockbox_svn_2007-08-31/uisimulator/sdl/button.c --- svn/uisimulator/sdl/button.c 2007-08-14 00:09:52.000000000 -0400 +++ rockbox_svn_2007-08-31/uisimulator/sdl/button.c 2007-08-31 20:27:19.000000000 -0400 @@ -696,6 +696,11 @@ /* Again copied from real button.c... */ +int button_available( void ) +{ + return queue_count(&button_queue); +} + long button_get(bool block) { struct event ev;