summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugin.c2
-rw-r--r--apps/plugin.h4
-rw-r--r--apps/plugins/lib/configfile.c75
-rw-r--r--apps/plugins/lib/configfile.h32
-rw-r--r--apps/plugins/mpegplayer/alloc.c6
-rw-r--r--apps/plugins/mpegplayer/header.c7
-rw-r--r--apps/plugins/mpegplayer/idct.c6
-rw-r--r--apps/plugins/mpegplayer/idct_arm_c.c6
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.c410
-rw-r--r--apps/plugins/mpegplayer/mpeg_settings.h17
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c770
-rw-r--r--apps/plugins/mpegplayer/video_out.h2
-rw-r--r--apps/plugins/mpegplayer/video_out_rockbox.c108
-rw-r--r--docs/CREDITS2
-rw-r--r--firmware/drivers/button.c5
-rw-r--r--firmware/export/button.h1
-rw-r--r--uisimulator/sdl/button.c5
17 files changed, 1240 insertions, 218 deletions
diff --git a/apps/plugin.c b/apps/plugin.c
index d5f70be043..7981c36b92 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -518,6 +518,8 @@ static const struct plugin_api rockbox_api = {
talk_disable_menus,
talk_enable_menus,
+ button_available,
+
};
int plugin_load(const char* plugin, void* parameter)
diff --git a/apps/plugin.h b/apps/plugin.h
index f025704f31..7277d8031c 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -112,7 +112,7 @@
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 79
+#define PLUGIN_API_VERSION 80
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
@@ -635,6 +635,8 @@ struct plugin_api {
void (*talk_disable_menus)(void);
void (*talk_enable_menus)(void);
+
+ int (*button_available)(void);
};
/* plugin header */
diff --git a/apps/plugins/lib/configfile.c b/apps/plugins/lib/configfile.c
index 0fbba81580..476f776878 100644
--- a/apps/plugins/lib/configfile.c
+++ b/apps/plugins/lib/configfile.c
@@ -55,12 +55,14 @@ int configfile_save(const char *filename, struct configdata *cfg,
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 @@ int configfile_load(const char *filename, struct configdata *cfg,
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 --git a/apps/plugins/lib/configfile.h b/apps/plugins/lib/configfile.h
index fcce7de275..7aa69f3ecf 100644
--- a/apps/plugins/lib/configfile.h
+++ b/apps/plugins/lib/configfile.h
@@ -38,9 +38,41 @@ struct configdata
};
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 --git a/apps/plugins/mpegplayer/alloc.c b/apps/plugins/mpegplayer/alloc.c
index 0ba86a51f3..ae482de112 100644
--- a/apps/plugins/mpegplayer/alloc.c
+++ b/apps/plugins/mpegplayer/alloc.c
@@ -54,6 +54,8 @@ static void * mpeg_malloc_internal (unsigned char *mallocbuf,
x = &mallocbuf[*mem_ptr];
*mem_ptr += (size + 3) & ~3; /* Keep memory 32-bit aligned */
+ rb->memset(x,0,size);
+
return x;
(void)reason;
}
@@ -116,7 +118,7 @@ void * mpeg2_malloc(unsigned size, mpeg2_alloc_t reason)
void mpeg2_free(void *ptr)
{
- (void)ptr;
+ mpeg2_mem_ptr = (void *)ptr - (void *)mpeg2_mallocbuf;
}
/* The following are expected by libmad */
@@ -141,7 +143,7 @@ void * codec_calloc(size_t nmemb, size_t size)
void codec_free(void* ptr)
{
- (void)ptr;
+ mem_ptr = (void *)ptr - (void *)mallocbuf;
}
void *memmove(void *dest, const void *src, size_t n)
diff --git a/apps/plugins/mpegplayer/header.c b/apps/plugins/mpegplayer/header.c
index 7f94705f52..7486b0ebf0 100644
--- a/apps/plugins/mpegplayer/header.c
+++ b/apps/plugins/mpegplayer/header.c
@@ -58,7 +58,7 @@ static const uint8_t default_intra_quantizer_matrix[64] ICONST_ATTR = {
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 @@ uint8_t mpeg2_scan_norm[64] IDATA_ATTR = {
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 @@ uint8_t mpeg2_scan_alt[64] IDATA_ATTR = {
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 --git a/apps/plugins/mpegplayer/idct.c b/apps/plugins/mpegplayer/idct.c
index ee02e72a6f..bf705c6a2f 100644
--- a/apps/plugins/mpegplayer/idct.c
+++ b/apps/plugins/mpegplayer/idct.c
@@ -260,6 +260,8 @@ static void mpeg2_idct_add_c (const int last, int16_t * block,
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 @@ void mpeg2_idct_init (void)
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 --git a/apps/plugins/mpegplayer/idct_arm_c.c b/apps/plugins/mpegplayer/idct_arm_c.c
index be9971f5c3..9805f421a6 100644
--- a/apps/plugins/mpegplayer/idct_arm_c.c
+++ b/apps/plugins/mpegplayer/idct_arm_c.c
@@ -509,6 +509,8 @@ static void mpeg2_idct_add_c (int last, int16_t * block,
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;
@@ -518,10 +520,10 @@ void mpeg2_idct_init (void)
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 --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
index 28062f4567..197fa09236 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.c
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -7,20 +7,99 @@
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);
+
+#ifndef HAVE_LCD_COLOR
+void gray_show(bool enable);
+#endif
#define SETTINGS_VERSION 2
#define SETTINGS_MIN_VERSION 1
#define SETTINGS_FILENAME "mpegplayer.cfg"
+enum slider_state_t {state0, state1, state2,
+ state3, state4, state5} slider_state;
+
+volatile long thumbDelayTimer;
+
+/* button definitions */
+#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define MPEG_SELECT BUTTON_ON
+#define MPEG_RIGHT BUTTON_RIGHT
+#define MPEG_LEFT BUTTON_LEFT
+#define MPEG_SCROLL_DOWN BUTTON_UP
+#define MPEG_SCROLL_UP BUTTON_DOWN
+#define MPEG_EXIT BUTTON_OFF
+
+#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
+#define MPEG_SELECT BUTTON_PLAY
+#define MPEG_RIGHT BUTTON_RIGHT
+#define MPEG_LEFT BUTTON_LEFT
+#define MPEG_SCROLL_DOWN BUTTON_UP
+#define MPEG_SCROLL_UP BUTTON_DOWN
+#define MPEG_EXIT BUTTON_POWER
+
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_3G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_1G2G_PAD)
+#define MPEG_SELECT BUTTON_SELECT
+#define MPEG_RIGHT BUTTON_RIGHT
+#define MPEG_LEFT BUTTON_LEFT
+#define MPEG_SCROLL_DOWN BUTTON_SCROLL_FWD
+#define MPEG_SCROLL_UP BUTTON_SCROLL_BACK
+#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_UP BUTTON_UP
+#define MPEG_DOWN BUTTON_DOWN
+#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_UP BUTTON_UP
+#define MPEG_DOWN BUTTON_DOWN
+#define MPEG_EXIT BUTTON_POWER
+
+#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
+#define MPEG_SELECT BUTTON_SELECT
+#define MPEG_SCROLL_UP BUTTON_VOL_UP
+#define MPEG_SCROLL_DOWN BUTTON_VOL_DOWN
+#define MPEG_LEFT BUTTON_LEFT
+#define MPEG_RIGHT BUTTON_RIGHT
+#define MPEG_UP BUTTON_UP
+#define MPEG_DOWN BUTTON_DOWN
+#define MPEG_EXIT BUTTON_POWER
+
+#else
+#error MPEGPLAYER: Unsupported keypad
+#endif
+
static struct configdata config[] =
{
- {TYPE_ENUM, 0, 2, &settings.showfps, "Show FPS",
- (char *[]){ "No", "Yes" }, NULL},
- {TYPE_ENUM, 0, 2, &settings.limitfps, "Limit FPS",
- (char *[]){ "No", "Yes" }, NULL},
- {TYPE_ENUM, 0, 2, &settings.skipframes, "Skip frames",
- (char *[]){ "No", "Yes" }, 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},
+
#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
{TYPE_INT, 0, INT_MAX, &settings.displayoptions, "Display options",
NULL, NULL},
@@ -36,6 +115,7 @@ enum mpeg_menu_ids
MPEG_OPTION_DISPLAY_FPS,
MPEG_OPTION_LIMIT_FPS,
MPEG_OPTION_SKIP_FRAMES,
+ MPEG_OPTION_CLEAR_RESUMES,
MPEG_OPTION_QUIT,
};
@@ -68,13 +148,250 @@ static void display_options(void)
}
#endif /* #ifdef TOSHIBA_GIGABEAT_F */
+void draw_slider(int slider_ypos, int max_val, int current_val)
+{
+ int slider_margin = LCD_WIDTH*12/100; /* 12% */
+ int slider_width = LCD_WIDTH-(slider_margin*2);
+ char resume_str[32];
+
+ /* max_val and current_val are in half minutes
+ determine value .0 or .5 to display */
+ int max_hol = max_val/2;
+ int max_rem = (max_val-(max_hol*2))*5;
+ int current_hol = current_val/2;
+ int current_rem = (current_val-(current_hol*2))*5;
+
+ rb->snprintf(resume_str, sizeof(resume_str), "0.0");
+ rb->lcd_putsxy(slider_margin, slider_ypos, resume_str);
+
+ rb->snprintf(resume_str, sizeof(resume_str), "%u.%u", max_hol, max_rem);
+ rb->lcd_putsxy(LCD_WIDTH-slider_margin-25, slider_ypos, resume_str);
+
+ rb->lcd_drawrect(slider_margin, slider_ypos+17, slider_width, 8);
+ rb->lcd_fillrect(slider_margin, slider_ypos+17,
+ current_val*slider_width/max_val, 8);
+
+ rb->snprintf(resume_str, sizeof(resume_str), "%u.%u", current_hol,
+ current_rem);
+ rb->lcd_putsxy(slider_margin+(current_val*slider_width/max_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 slider_ypos = LCD_HEIGHT-45;
+ int seek_rtn;
+
+ slider_state = state0;
+ thumbDelayTimer = *(rb->current_tick);
+ rb->lcd_clear_display();
+ rb->lcd_update();
+
+ while(quit == 0)
+ {
+ button = rb->button_get(false);
+ switch (button)
+ {
+#if (CONFIG_KEYPAD == GIGABEAT_PAD) || \
+ (CONFIG_KEYPAD == SANSA_E200_PAD) || \
+ (CONFIG_KEYPAD == SANSA_C200_PAD)
+ case MPEG_DOWN:
+ case MPEG_DOWN | BUTTON_REPEAT:
+ if ((resume_time -= 20) < 0)
+ resume_time = 0;
+ slider_state = state0;
+ thumbDelayTimer = *(rb->current_tick);
+ break;
+ case MPEG_UP:
+ case MPEG_UP | BUTTON_REPEAT:
+ if ((resume_time += 20) > play_time)
+ resume_time = play_time;
+ slider_state = state0;
+ thumbDelayTimer = *(rb->current_tick);
+ break;
+#endif
+ case MPEG_LEFT:
+ case MPEG_LEFT | BUTTON_REPEAT:
+ case MPEG_SCROLL_UP:
+ case MPEG_SCROLL_UP | BUTTON_REPEAT:
+ if (--resume_time < 0)
+ resume_time = 0;
+ slider_state = state0;
+ thumbDelayTimer = *(rb->current_tick);
+ break;
+ case MPEG_RIGHT:
+ case MPEG_RIGHT | BUTTON_REPEAT:
+ case MPEG_SCROLL_DOWN:
+ case MPEG_SCROLL_DOWN | BUTTON_REPEAT:
+ if (++resume_time > play_time)
+ resume_time = play_time;
+ slider_state = 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();
+
+ switch(slider_state)
+ {
+ case state0:
+ rb->lcd_clear_display();
+ rb->lcd_update();
+#ifdef HAVE_LCD_COLOR
+ if (resume_time > 0)
+ rb->splash(0, "loading ...");
+#endif
+ slider_state = state1;
+ break;
+ case state1:
+ if (*(rb->current_tick) - thumbDelayTimer > 75)
+ slider_state = state2;
+ if (resume_time == 0)
+ {
+ seek_rtn = 0;
+ slider_state = state5;
+ }
+ draw_slider(slider_ypos, play_time, resume_time);
+ break;
+ case state2:
+ if ( (seek_rtn = seek_PTS(in_file, resume_time, 1)) >= 0)
+ slider_state = state3;
+ else if (seek_rtn == -101)
+ {
+ slider_state = state0;
+ thumbDelayTimer = *(rb->current_tick);
+ }
+ else
+ slider_state = state4;
+ break;
+ case state3:
+ display_thumb(in_file);
+ draw_slider(slider_ypos, play_time, resume_time);
+ slider_state = state4;
+ break;
+ case state4:
+ draw_slider(slider_ypos, play_time, resume_time);
+ slider_state = state5;
+ break;
+ case state5:
+ break;
+ }
+ }
+
+ 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)(settings.resume_time/2);
+ 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:
+#ifndef HAVE_LCD_COLOR
+ gray_show(true);
+#endif
+ if ((result = get_start_time(play_time, in_file)) >= 0)
+ menu_quit = 1;
+#ifndef HAVE_LCD_COLOR
+ gray_show(false);
+#endif
+ 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(void)
+{
+ 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;
- static const struct menu_item items[] = {
+ /* 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);
+
+ struct menu_item items[] = {
#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
[MPEG_OPTION_DISPLAY_SETTINGS] =
{ "Display Options", NULL },
@@ -85,6 +402,8 @@ bool mpeg_menu(void)
{ "Limit FPS", NULL },
[MPEG_OPTION_SKIP_FRAMES] =
{ "Skip frames", NULL },
+ [MPEG_OPTION_CLEAR_RESUMES] =
+ { clear_str, NULL },
[MPEG_OPTION_QUIT] =
{ "Quit mpegplayer", NULL },
};
@@ -115,6 +434,11 @@ bool mpeg_menu(void)
rb->set_option("Skip frames",&settings.skipframes,INT,
noyes, 2, NULL);
break;
+ case MPEG_OPTION_CLEAR_RESUMES:
+ clear_resume_count();
+ rb->snprintf(clear_str, sizeof(clear_str),
+ "Clear all resumes: %u", 0);
+ break;
case MPEG_OPTION_QUIT:
default:
menu_quit=1;
@@ -132,48 +456,82 @@ bool mpeg_menu(void)
return (result==MPEG_OPTION_QUIT);
}
-
-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;
#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
settings.displayoptions = 0; /* No visual effects */
#endif
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 defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
+ if ((settings.displayoptions =
+ configfile_get_value(SETTINGS_FILENAME, "Display options")) < 0)
+ {
+ configfile_update_entry(SETTINGS_FILENAME, "Display options",
+ (settings.displayoptions=0));
+ }
rb->lcd_yuv_set_options(settings.displayoptions);
#endif
+
+ 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);
+ 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);
- /* Store the settings in old_settings - to check for future changes */
- old_settings = settings;
+ /* 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);
}
+
+#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
+ configfile_update_entry(SETTINGS_FILENAME, "Display options",
+ settings.displayoptions);
+#endif
}
diff --git a/apps/plugins/mpegplayer/mpeg_settings.h b/apps/plugins/mpegplayer/mpeg_settings.h
index 7721c46f64..690667f632 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.h
+++ b/apps/plugins/mpegplayer/mpeg_settings.h
@@ -2,16 +2,23 @@
#include "plugin.h"
struct mpeg_settings {
- int showfps;
- int limitfps;
- int skipframes;
+ 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 */
+
#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
- unsigned displayoptions;
+ int displayoptions;
#endif
};
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);
+void clear_resume_count(void);
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
#include "mpeg_settings.h"
#include "video_out.h"
#include "../../codecs/libmad/mad.h"
+#include "splash.h"
PLUGIN_HEADER
PLUGIN_IRAM_DECLARE
@@ -198,11 +199,8 @@ typedef struct
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 */
- uint64_t buffer_tail; /* Accumulation of bytes added */
- uint64_t buffer_head; /* Accumulation of bytes removed */
+ 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 */
uint32_t tagged; /* curr_pts is valid */
@@ -301,8 +299,7 @@ static intptr_t str_send_msg(Stream *str, int id, intptr_t data)
return str->dispatch_fn(str, msg);
#endif
- /* Only one thread at a time, please - only one core may safely send
- right now */
+ /* Only one thread at a time, please */
rb->spinlock_lock(&str->msg_lock);
str->ev.id = id;
@@ -333,13 +330,62 @@ static intptr_t str_send_msg(Stream *str, int id, intptr_t data)
/* 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;
+static inline void init_stream_lock(void)
+ { rb->spinlock_init(&stream_lock); }
+static inline void lock_stream(void)
+ { rb->spinlock_lock(&stream_lock); }
+static inline void unlock_stream(void)
+ { rb->spinlock_unlock(&stream_lock); }
+#else
+/* No RMW issue here */
+static inline void init_stream_lock(void)
+ { }
+static inline void lock_stream(void)
+ { }
+static inline void unlock_stream(void)
+ { }
+#endif
+
+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? */
@@ -350,7 +396,7 @@ static size_t buffer_size IBSS_ATTR;
#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);
@@ -471,8 +517,47 @@ static void init_mad(void* mad_frame_overlap)
((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;
@@ -481,30 +566,49 @@ static void get_next_data( Stream* str )
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;
- if (p >= disk_buf_end)
+ /* 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 )
{
- p = disk_buf + (p - disk_buf_end);
+ 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_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))
{
@@ -521,7 +625,6 @@ static void get_next_data( Stream* str )
rb->splash( 30, "Weird Pack header!" );
p += 5;
}
- /*rb->splash( 30, "Pack header" );*/
}
/* System header, parse and skip it - four bytes */
@@ -535,29 +638,29 @@ static void get_next_data( Stream* str )
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 ? */
@@ -618,11 +721,9 @@ static void get_next_data( Stream* str )
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];
@@ -657,20 +758,19 @@ static void get_next_data( Stream* str )
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_head += (disk_buf_end - str->prev_packet) +
- (str->curr_packet - disk_buf);
- str->guard_bytes = 0;
- }
+ str->prev_packet_length = (disk_buf_end - str->prev_packet) +
+ (str->curr_packet - disk_buf_start);
else
- {
- str->buffer_head += (str->curr_packet - str->prev_packet);
- }
+ str->prev_packet_length = (str->curr_packet - str->prev_packet);
+
+ unlock_stream();
str->prev_packet = str->curr_packet;
}
@@ -678,14 +778,13 @@ static void get_next_data( Stream* str )
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 */
@@ -943,6 +1042,8 @@ static int button_loop(void)
int vol, minvol, maxvol;
int button;
+ if (video_sync_start==1) {
+
if (str_have_msg(&audio_str))
{
struct event ev;
@@ -1014,6 +1115,7 @@ static int button_loop(void)
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 {
@@ -1024,6 +1126,7 @@ static int button_loop(void)
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;
@@ -1060,7 +1163,7 @@ static int button_loop(void)
audio_str.status = STREAM_STOPPED;
}
}
-
+ }
quit:
return audio_str.status;
}
@@ -1086,7 +1189,23 @@ static void audio_thread(void)
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;
@@ -1165,7 +1284,7 @@ static void audio_thread(void)
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);
@@ -1198,8 +1317,6 @@ static void audio_thread(void)
if (mad_stat != 0)
{
- DEBUGF("Audio stream error - %d\n", stream.error);
-
if (stream.error == MAD_FLAG_INCOMPLETE
|| stream.error == MAD_ERROR_BUFLEN)
{
@@ -1259,6 +1376,13 @@ static void audio_thread(void)
rb->priority_yield();
}
+ if (video_sync_start == 0 &&
+ pts->pts+(uint32_t)synth.pcm.length<audio_sync_time) {
+ synth.pcm.length = 0;
+ size = 0;
+ rb->yield();
+ }
+
/* TODO: This part will be replaced with dsp calls soon */
if (MAD_NCHANNELS(&frame.header) == 2)
{
@@ -1305,6 +1429,7 @@ static void audio_thread(void)
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 */
@@ -1391,29 +1516,32 @@ static void video_thread(void)
/* 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)
{
@@ -1450,7 +1578,8 @@ static void video_thread(void)
{
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);
@@ -1458,7 +1587,7 @@ static void video_thread(void)
if (video_str.curr_packet == NULL)
{
/* No more data. */
- goto done;
+ goto video_thread_quit;
}
continue;
@@ -1528,8 +1657,12 @@ static void video_thread(void)
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 */
@@ -1538,7 +1671,19 @@ static void video_thread(void)
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_B))
+ 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 */
@@ -1664,32 +1809,39 @@ static void video_thread(void)
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:
@@ -1724,53 +1876,298 @@ static void video_thread(void)
rb->yield();
}
-done:
-#if NUM_CORES > 1
- flush_icache();
-#endif
+ 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;
+ }
- video_str.status = STREAM_DONE;
+ #if NUM_CORES > 1
+ flush_icache();
+ #endif
+
+ mpeg2_close (mpeg2dec);
+
+ /* Commit suicide */
+ video_str.status = STREAM_TERMINATED;
+}
- while (1)
- {
- str_get_msg(&video_str, &ev);
+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;
- if (ev.id == STREAM_QUIT)
- break;
+ 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;
- str_reply_msg(&video_str, 0);
+ 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
+ {
+ while (video_str.status != STREAM_TERMINATED)
+ rb->yield();
}
-video_thread_quit:
- /* Commit suicide */
- video_str.status = STREAM_TERMINATED;
+ 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;
+
+ /* 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;
+
+ /* temporary read buffer size cannot exceed buffer size */
+ if ( read_length > disk_buf_size )
+ read_length = disk_buf_size;
+
+ 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");
- return PLUGIN_ERROR;
}
/* Initialize IRAM - stops audio and voice as well */
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
@@ -1781,49 +2178,38 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
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 */
- audiosize = mpeg_alloc_init(audiobuf, audiosize, LIBMPEG2BUFFER_SIZE);
+ audiosize = mpeg_alloc_init(audiobuf,audiosize, LIBMPEG2BUFFER_SIZE);
if (audiosize == 0)
return PLUGIN_ERROR;
+ /* 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);
- DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size);
- buffer = mpeg_malloc(buffer_size,-1);
+ DEBUGF("audiosize=%ld, disk_buf_size=%ld\n",audiosize,disk_buf_size);
+ disk_buf_start = mpeg_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;
@@ -1836,9 +2222,10 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
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);
@@ -1860,34 +2247,51 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
cannot just return PLUGIN_ERROR - instead drop though to cleanup code
*/
- init_settings();
+ 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);
-
- 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;
-
- audio_str.guard_bytes = 0;
- audio_str.prev_packet = disk_buf;
- audio_str.buffer_head = 0;
- audio_str.buffer_tail = disk_buf_len;
- video_str.guard_bytes = 0;
- video_str.prev_packet = disk_buf;
- video_str.buffer_head = 0;
- video_str.buffer_tail = disk_buf_len;
+ 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;
+
+ 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;
@@ -1895,6 +2299,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
gray_show(true);
#endif
+ init_stream_lock();
+
#if NUM_CORES > 1
flush_icache();
#endif
@@ -1914,38 +2320,52 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
}
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 */
while ((audio_str.status >= 0) || (video_str.status >= 0))
{
- size_t audio_remaining = audio_str.buffer_tail - audio_str.buffer_head;
- size_t video_remaining = video_str.buffer_tail - video_str.buffer_head;
+ 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;
- audio_str.buffer_tail += n;
- video_str.buffer_tail += n;
+ lock_stream();
+ audio_str.buffer_remaining += n;
+ video_str.buffer_remaining += n;
+ unlock_stream();
+
disk_buf_tail += n;
rb->yield();
}
if (disk_buf_tail == disk_buf_end)
- disk_buf_tail = buffer;
+ disk_buf_tail = disk_buf_start;
}
rb->sleep(HZ/10);
@@ -1968,6 +2388,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
invalidate_icache();
#endif
+ vo_cleanup();
+
#ifndef HAVE_LCD_COLOR
gray_release();
#endif
diff --git a/apps/plugins/mpegplayer/video_out.h b/apps/plugins/mpegplayer/video_out.h
index 0d91eb7b1c..ec3f7c65d3 100644
--- a/apps/plugins/mpegplayer/video_out.h
+++ b/apps/plugins/mpegplayer/video_out.h
@@ -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 (void);
diff --git a/apps/plugins/mpegplayer/video_out_rockbox.c b/apps/plugins/mpegplayer/video_out_rockbox.c
index 2aac0b8039..490f04411f 100644
--- a/apps/plugins/mpegplayer/video_out_rockbox.c
+++ b/apps/plugins/mpegplayer/video_out_rockbox.c
@@ -43,8 +43,7 @@ static int output_height;
void vo_draw_frame (uint8_t * const * buf)
{
#ifdef HAVE_LCD_COLOR
- rb->lcd_yuv_blit(buf,
- 0,0,image_width,
+ rb->lcd_yuv_blit(buf, 0,0,image_width,
output_x,output_y,output_width,output_height);
#else
gray_ub_gray_bitmap_part(buf[0],0,0,image_width,
@@ -60,10 +59,105 @@ void vo_draw_frame (uint8_t * const * buf)
#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)
+{
+ int r,c;
+
+#if LCD_WIDTH >= LCD_HEIGHT
+ for (r=0;r<image_width/2;r++)
+ for (c=0;c<image_height/2;c++)
+ *(tmpbuf[0]+c*image_width/2+r) =
+ *(buf[0]+2*c*image_width+2*r);
+
+ for (r=0;r<image_width/4;r++)
+ for (c=0;c<image_height/4;c++)
+ {
+ *(tmpbuf[1]+c*image_width/4+r) =
+ *(buf[1]+2*c*image_width/2+2*r);
+ *(tmpbuf[2]+c*image_width/4+r) =
+ *(buf[2]+2*c*image_width/2+2*r);
+ }
+#else
+ for (r=0;r<image_width/2;r++)
+ for (c=0;c<image_height/2;c++)
+ *(tmpbuf[0]+(image_width/2-1-r)*image_height/2+c) =
+ *(buf[0]+2*c*image_width+2*r);
+
+ for (r=0;r<image_width/4;r++)
+ for (c=0;c<image_height/4;c++)
+ {
+ *(tmpbuf[1]+(image_width/4-1-r)*image_height/4+c) =
+ *(buf[1]+2*c*image_width/2+2*r);
+ *(tmpbuf[2]+(image_width/4-1-r)*image_height/4+c) =
+ *(buf[2]+2*c*image_width/2+2*r);
+ }
+#endif
+
+rb->lcd_clear_display();
+rb->lcd_update();
+
+#ifdef HAVE_LCD_COLOR
+#ifdef SIMULATOR
+#if LCD_WIDTH >= LCD_HEIGHT
+ rb->lcd_yuv_blit(tmpbuf,0,0,image_width/2,
+ (LCD_WIDTH-1-image_width/2)/2,
+ LCD_HEIGHT-50-(image_height/2),
+ output_width/2,output_height/2);
+
+#else
+ rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2,
+ LCD_HEIGHT-50-(image_height/2),
+ (LCD_WIDTH-1-image_width/2)/2,
+ output_height/2,output_width/2);
+#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-50-(image_height/2),
+ output_width/2,output_height/2);
+#else
+ rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2,
+ LCD_HEIGHT-50-(image_height/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-50-(image_height/2),
+ output_width/2,output_height/2);
+#else
+ gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_height/2,
+ LCD_HEIGHT-50-(image_height/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;
@@ -83,3 +177,13 @@ void vo_setup(const mpeg2_sequence_t * sequence)
output_y = (SCREEN_HEIGHT-sequence->display_height)/2;
}
}
+
+void vo_cleanup(void)
+{
+ if (tmpbufc)
+ mpeg2_free(tmpbufc);
+ if (tmpbufb)
+ mpeg2_free(tmpbufb);
+ if (tmpbufa)
+ mpeg2_free(tmpbufa);
+}
diff --git a/docs/CREDITS b/docs/CREDITS
index cb12c9688b..2abfcb8b46 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -339,6 +339,8 @@ David Bishop
Hein-Pieter van Braam
Przemysław Hołubowski
Stepan Moskovchenko
+John S. Gwynne
+Brian J. Morey
The libmad team
The wavpack team
diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c
index 3daa08b2c3..d79a9333ff 100644
--- a/firmware/drivers/button.c
+++ b/firmware/drivers/button.c
@@ -293,6 +293,11 @@ static void button_boost(bool state)
}
#endif /* HAVE_ADJUSTABLE_CPU_FREQ */
+int button_available( void )
+{
+ return queue_count(&button_queue);
+}
+
long button_get(bool block)
{
struct event ev;
diff --git a/firmware/export/button.h b/firmware/export/button.h
index 5322d814bf..881d7c424d 100644
--- a/firmware/export/button.h
+++ b/firmware/export/button.h
@@ -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 --git a/uisimulator/sdl/button.c b/uisimulator/sdl/button.c
index 4869dd06b1..2b44a7bace 100644
--- a/uisimulator/sdl/button.c
+++ b/uisimulator/sdl/button.c
@@ -736,6 +736,11 @@ void button_event(int key, bool pressed)
/* Again copied from real button.c... */
+int button_available( void )
+{
+ return queue_count(&button_queue);
+}
+
long button_get(bool block)
{
struct event ev;