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 = {
518 talk_disable_menus, 518 talk_disable_menus,
519 talk_enable_menus, 519 talk_enable_menus,
520 520
521 button_available,
522
521}; 523};
522 524
523int plugin_load(const char* plugin, void* parameter) 525int 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 @@
112#define PLUGIN_MAGIC 0x526F634B /* RocK */ 112#define PLUGIN_MAGIC 0x526F634B /* RocK */
113 113
114/* increase this every time the api struct changes */ 114/* increase this every time the api struct changes */
115#define PLUGIN_API_VERSION 79 115#define PLUGIN_API_VERSION 80
116 116
117/* update this to latest version if a change to the api struct breaks 117/* update this to latest version if a change to the api struct breaks
118 backwards compatibility (and please take the opportunity to sort in any 118 backwards compatibility (and please take the opportunity to sort in any
@@ -635,6 +635,8 @@ struct plugin_api {
635 635
636 void (*talk_disable_menus)(void); 636 void (*talk_disable_menus)(void);
637 void (*talk_enable_menus)(void); 637 void (*talk_enable_menus)(void);
638
639 int (*button_available)(void);
638}; 640};
639 641
640/* plugin header */ 642/* 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,
55 if(fd < 0) 55 if(fd < 0)
56 return fd*10 - 1; 56 return fd*10 - 1;
57 57
58 cfg_rb->fdprintf(fd, "file version: %d\n", version); 58 /* pre-allocate 10 bytes for INT */
59 cfg_rb->fdprintf(fd, "file version: %10d\n", version);
59 60
60 for(i = 0;i < num_items;i++) { 61 for(i = 0;i < num_items;i++) {
61 switch(cfg[i].type) { 62 switch(cfg[i].type) {
62 case TYPE_INT: 63 case TYPE_INT:
63 cfg_rb->fdprintf(fd, "%s: %d\n", 64 /* pre-allocate 10 bytes for INT */
65 cfg_rb->fdprintf(fd, "%s: %10d\n",
64 cfg[i].name, 66 cfg[i].name,
65 *cfg[i].val); 67 *cfg[i].val);
66 break; 68 break;
@@ -141,3 +143,72 @@ int configfile_load(const char *filename, struct configdata *cfg,
141 cfg_rb->close(fd); 143 cfg_rb->close(fd);
142 return 0; 144 return 0;
143} 145}
146
147int configfile_get_value(const char* filename, const char* name)
148{
149 int fd;
150 char *pname;
151 char *pval;
152 char buf[MAX_PATH];
153
154 get_cfg_filename(buf, MAX_PATH, filename);
155 fd = cfg_rb->open(buf, O_RDONLY);
156 if(fd < 0)
157 return -1;
158
159 while(cfg_rb->read_line(fd, buf, MAX_PATH) > 0)
160 {
161 cfg_rb->settings_parseline(buf, &pname, &pval);
162 if(!cfg_rb->strcmp(name, pname))
163 {
164 cfg_rb->close(fd);
165 return cfg_rb->atoi(pval);
166 }
167 }
168
169 cfg_rb->close(fd);
170 return -1;
171}
172
173int configfile_update_entry(const char* filename, const char* name, int val)
174{
175 int fd;
176 char *pname;
177 char *pval;
178 char path[MAX_PATH];
179 char buf[256];
180 int found = 0;
181 int line_len = 0;
182 int pos = 0;
183
184 /* open the current config file */
185 get_cfg_filename(path, MAX_PATH, filename);
186 fd = cfg_rb->open(path, O_RDWR);
187 if(fd < 0)
188 return -1;
189
190 /* read in the current stored settings */
191 while((line_len = cfg_rb->read_line(fd, buf, 256)) > 0)
192 {
193 cfg_rb->settings_parseline(buf, &pname, &pval);
194
195 if(!cfg_rb->strcmp(name, pname))
196 {
197 found = 1;
198 cfg_rb->lseek(fd, pos, SEEK_SET);
199 /* pre-allocate 10 bytes for INT */
200 cfg_rb->fdprintf(fd, "%s: %10d\n", pname, val);
201 break;
202 }
203 pos += line_len;
204 }
205
206 /* if (name/val) is a new entry just append to file */
207 if (found == 0)
208 /* pre-allocate 10 bytes for INT */
209 cfg_rb->fdprintf(fd, "%s: %10d\n", name, val);
210
211 cfg_rb->close(fd);
212
213 return found;
214}
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
38}; 38};
39 39
40void configfile_init(struct plugin_api* newrb); 40void configfile_init(struct plugin_api* newrb);
41
42/* configfile_save - Given configdata entries this function will
43 create a config file with these entries, destroying any
44 previous config file of the same name */
41int configfile_save(const char *filename, struct configdata *cfg, 45int configfile_save(const char *filename, struct configdata *cfg,
42 int num_items, int version); 46 int num_items, int version);
47
43int configfile_load(const char *filename, struct configdata *cfg, 48int configfile_load(const char *filename, struct configdata *cfg,
44 int num_items, int min_version); 49 int num_items, int min_version);
45 50
51/* configfile_get_value - Given a key name, this function will
52 return the integer value for that key.
53
54 Input:
55 filename = config file filename
56 name = (name/value) pair name entry
57 Return:
58 value if (name/value) pair is found
59 -1 if entry is not found
60*/
61int configfile_get_value(const char* filename, const char* name);
62
63/* configure_update_entry - Given a key name and integer value
64 this function will update the entry if found, or add it if
65 not found.
66
67 Input:
68 filename = config file filename
69 name = (name/value) pair name entry
70 val = new value for (name/value) pair
71 Return:
72 1 if the (name/value) pair was found and updated with the new value
73 0 if the (name/value) pair was added as a new entry
74 -1 if error
75*/
76int configfile_update_entry(const char* filename, const char* name, int val);
77
46#endif 78#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,
54 x = &mallocbuf[*mem_ptr]; 54 x = &mallocbuf[*mem_ptr];
55 *mem_ptr += (size + 3) & ~3; /* Keep memory 32-bit aligned */ 55 *mem_ptr += (size + 3) & ~3; /* Keep memory 32-bit aligned */
56 56
57 rb->memset(x,0,size);
58
57 return x; 59 return x;
58 (void)reason; 60 (void)reason;
59} 61}
@@ -116,7 +118,7 @@ void * mpeg2_malloc(unsigned size, mpeg2_alloc_t reason)
116 118
117void mpeg2_free(void *ptr) 119void mpeg2_free(void *ptr)
118{ 120{
119 (void)ptr; 121 mpeg2_mem_ptr = (void *)ptr - (void *)mpeg2_mallocbuf;
120} 122}
121 123
122/* The following are expected by libmad */ 124/* The following are expected by libmad */
@@ -141,7 +143,7 @@ void * codec_calloc(size_t nmemb, size_t size)
141 143
142void codec_free(void* ptr) 144void codec_free(void* ptr)
143{ 145{
144 (void)ptr; 146 mem_ptr = (void *)ptr - (void *)mallocbuf;
145} 147}
146 148
147void *memmove(void *dest, const void *src, size_t n) 149void *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 = {
58 83 58 83
59}; 59};
60 60
61uint8_t mpeg2_scan_norm[64] IDATA_ATTR = { 61uint8_t default_mpeg2_scan_norm[64] IDATA_ATTR = {
62 /* Zig-Zag scan pattern */ 62 /* Zig-Zag scan pattern */
63 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 63 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5,
64 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 64 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 = {
66 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 66 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
67}; 67};
68 68
69uint8_t mpeg2_scan_alt[64] IDATA_ATTR = { 69uint8_t default_mpeg2_scan_alt[64] IDATA_ATTR = {
70 /* Alternate scan pattern */ 70 /* Alternate scan pattern */
71 0, 8, 16, 24, 1, 9, 2, 10, 17, 25, 32, 40, 48, 56, 57, 49, 71 0, 8, 16, 24, 1, 9, 2, 10, 17, 25, 32, 40, 48, 56, 57, 49,
72 41, 33, 26, 18, 3, 11, 4, 12, 19, 27, 34, 42, 50, 58, 35, 43, 72 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 = {
74 53, 61, 22, 30, 7, 15, 23, 31, 38, 46, 54, 62, 39, 47, 55, 63 74 53, 61, 22, 30, 7, 15, 23, 31, 38, 46, 54, 62, 39, 47, 55, 63
75}; 75};
76 76
77uint8_t mpeg2_scan_norm[64] IDATA_ATTR;
78uint8_t mpeg2_scan_alt[64] IDATA_ATTR;
79
77void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec) 80void mpeg2_header_state_init (mpeg2dec_t * mpeg2dec)
78{ 81{
79 if (mpeg2dec->sequence.width != (unsigned)-1) { 82 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,
260 260
261void mpeg2_idct_init (void) 261void mpeg2_idct_init (void)
262{ 262{
263 extern uint8_t default_mpeg2_scan_norm[64];
264 extern uint8_t default_mpeg2_scan_alt[64];
263 extern uint8_t mpeg2_scan_norm[64]; 265 extern uint8_t mpeg2_scan_norm[64];
264 extern uint8_t mpeg2_scan_alt[64]; 266 extern uint8_t mpeg2_scan_alt[64];
265 int i, j; 267 int i, j;
@@ -274,10 +276,10 @@ void mpeg2_idct_init (void)
274 276
275 for (i = 0; i < 64; i++) 277 for (i = 0; i < 64; i++)
276 { 278 {
277 j = mpeg2_scan_norm[i]; 279 j = default_mpeg2_scan_norm[i];
278 mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); 280 mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
279 281
280 j = mpeg2_scan_alt[i]; 282 j = default_mpeg2_scan_alt[i];
281 mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); 283 mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
282 } 284 }
283} 285}
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,
509 509
510void mpeg2_idct_init (void) 510void mpeg2_idct_init (void)
511{ 511{
512 extern uint8_t default_mpeg2_scan_norm[64];
513 extern uint8_t default_mpeg2_scan_alt[64];
512 extern uint8_t mpeg2_scan_norm[64]; 514 extern uint8_t mpeg2_scan_norm[64];
513 extern uint8_t mpeg2_scan_alt[64]; 515 extern uint8_t mpeg2_scan_alt[64];
514 int i, j; 516 int i, j;
@@ -518,10 +520,10 @@ void mpeg2_idct_init (void)
518 520
519 for (i = 0; i < 64; i++) 521 for (i = 0; i < 64; i++)
520 { 522 {
521 j = mpeg2_scan_norm[i]; 523 j = default_mpeg2_scan_norm[i];
522 mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); 524 mpeg2_scan_norm[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
523 525
524 j = mpeg2_scan_alt[i]; 526 j = default_mpeg2_scan_alt[i];
525 mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2); 527 mpeg2_scan_alt[i] = ((j & 0x36) >> 1) | ((j & 0x09) << 2);
526 } 528 }
527} 529}
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 @@
7extern struct plugin_api* rb; 7extern struct plugin_api* rb;
8 8
9struct mpeg_settings settings; 9struct mpeg_settings settings;
10static struct mpeg_settings old_settings; 10
11ssize_t seek_PTS(int in_file, int startTime, int accept_button);
12void display_thumb(int in_file);
13
14#ifndef HAVE_LCD_COLOR
15void gray_show(bool enable);
16#endif
11 17
12#define SETTINGS_VERSION 2 18#define SETTINGS_VERSION 2
13#define SETTINGS_MIN_VERSION 1 19#define SETTINGS_MIN_VERSION 1
14#define SETTINGS_FILENAME "mpegplayer.cfg" 20#define SETTINGS_FILENAME "mpegplayer.cfg"
15 21
22enum slider_state_t {state0, state1, state2,
23 state3, state4, state5} slider_state;
24
25volatile long thumbDelayTimer;
26
27/* button definitions */
28#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
29 (CONFIG_KEYPAD == IRIVER_H300_PAD)
30#define MPEG_SELECT BUTTON_ON
31#define MPEG_RIGHT BUTTON_RIGHT
32#define MPEG_LEFT BUTTON_LEFT
33#define MPEG_SCROLL_DOWN BUTTON_UP
34#define MPEG_SCROLL_UP BUTTON_DOWN
35#define MPEG_EXIT BUTTON_OFF
36
37#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
38#define MPEG_SELECT BUTTON_PLAY
39#define MPEG_RIGHT BUTTON_RIGHT
40#define MPEG_LEFT BUTTON_LEFT
41#define MPEG_SCROLL_DOWN BUTTON_UP
42#define MPEG_SCROLL_UP BUTTON_DOWN
43#define MPEG_EXIT BUTTON_POWER
44
45#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
46 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
47 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
48#define MPEG_SELECT BUTTON_SELECT
49#define MPEG_RIGHT BUTTON_RIGHT
50#define MPEG_LEFT BUTTON_LEFT
51#define MPEG_SCROLL_DOWN BUTTON_SCROLL_FWD
52#define MPEG_SCROLL_UP BUTTON_SCROLL_BACK
53#define MPEG_EXIT BUTTON_MENU
54
55#elif CONFIG_KEYPAD == GIGABEAT_PAD
56#define MPEG_SELECT BUTTON_SELECT
57#define MPEG_LEFT BUTTON_LEFT
58#define MPEG_RIGHT BUTTON_RIGHT
59#define MPEG_UP BUTTON_UP
60#define MPEG_DOWN BUTTON_DOWN
61#define MPEG_SCROLL_DOWN BUTTON_VOL_DOWN
62#define MPEG_SCROLL_UP BUTTON_VOL_UP
63#define MPEG_EXIT BUTTON_POWER
64
65#elif CONFIG_KEYPAD == IRIVER_H10_PAD
66#define MPEG_SELECT BUTTON_PLAY
67#define MPEG_SCROLL_UP BUTTON_SCROLL_UP
68#define MPEG_SCROLL_DOWN BUTTON_SCROLL_DOWN
69#define MPEG_LEFT BUTTON_LEFT
70#define MPEG_RIGHT BUTTON_RIGHT
71#define MPEG_EXIT BUTTON_POWER
72
73#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
74#define MPEG_SELECT BUTTON_SELECT
75#define MPEG_SCROLL_UP BUTTON_SCROLL_UP
76#define MPEG_SCROLL_DOWN BUTTON_SCROLL_DOWN
77#define MPEG_LEFT BUTTON_LEFT
78#define MPEG_RIGHT BUTTON_RIGHT
79#define MPEG_UP BUTTON_UP
80#define MPEG_DOWN BUTTON_DOWN
81#define MPEG_EXIT BUTTON_POWER
82
83#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
84#define MPEG_SELECT BUTTON_SELECT
85#define MPEG_SCROLL_UP BUTTON_VOL_UP
86#define MPEG_SCROLL_DOWN BUTTON_VOL_DOWN
87#define MPEG_LEFT BUTTON_LEFT
88#define MPEG_RIGHT BUTTON_RIGHT
89#define MPEG_UP BUTTON_UP
90#define MPEG_DOWN BUTTON_DOWN
91#define MPEG_EXIT BUTTON_POWER
92
93#else
94#error MPEGPLAYER: Unsupported keypad
95#endif
96
16static struct configdata config[] = 97static struct configdata config[] =
17{ 98{
18 {TYPE_ENUM, 0, 2, &settings.showfps, "Show FPS", 99 {TYPE_INT, 0, 2, &settings.showfps, "Show FPS", NULL, NULL},
19 (char *[]){ "No", "Yes" }, NULL}, 100 {TYPE_INT, 0, 2, &settings.limitfps, "Limit FPS", NULL, NULL},
20 {TYPE_ENUM, 0, 2, &settings.limitfps, "Limit FPS", 101 {TYPE_INT, 0, 2, &settings.skipframes, "Skip frames", NULL, NULL},
21 (char *[]){ "No", "Yes" }, NULL}, 102
22 {TYPE_ENUM, 0, 2, &settings.skipframes, "Skip frames",
23 (char *[]){ "No", "Yes" }, NULL},
24#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) 103#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
25 {TYPE_INT, 0, INT_MAX, &settings.displayoptions, "Display options", 104 {TYPE_INT, 0, INT_MAX, &settings.displayoptions, "Display options",
26 NULL, NULL}, 105 NULL, NULL},
@@ -36,6 +115,7 @@ enum mpeg_menu_ids
36 MPEG_OPTION_DISPLAY_FPS, 115 MPEG_OPTION_DISPLAY_FPS,
37 MPEG_OPTION_LIMIT_FPS, 116 MPEG_OPTION_LIMIT_FPS,
38 MPEG_OPTION_SKIP_FRAMES, 117 MPEG_OPTION_SKIP_FRAMES,
118 MPEG_OPTION_CLEAR_RESUMES,
39 MPEG_OPTION_QUIT, 119 MPEG_OPTION_QUIT,
40}; 120};
41 121
@@ -68,13 +148,250 @@ static void display_options(void)
68} 148}
69#endif /* #ifdef TOSHIBA_GIGABEAT_F */ 149#endif /* #ifdef TOSHIBA_GIGABEAT_F */
70 150
151void draw_slider(int slider_ypos, int max_val, int current_val)
152{
153 int slider_margin = LCD_WIDTH*12/100; /* 12% */
154 int slider_width = LCD_WIDTH-(slider_margin*2);
155 char resume_str[32];
156
157 /* max_val and current_val are in half minutes
158 determine value .0 or .5 to display */
159 int max_hol = max_val/2;
160 int max_rem = (max_val-(max_hol*2))*5;
161 int current_hol = current_val/2;
162 int current_rem = (current_val-(current_hol*2))*5;
163
164 rb->snprintf(resume_str, sizeof(resume_str), "0.0");
165 rb->lcd_putsxy(slider_margin, slider_ypos, resume_str);
166
167 rb->snprintf(resume_str, sizeof(resume_str), "%u.%u", max_hol, max_rem);
168 rb->lcd_putsxy(LCD_WIDTH-slider_margin-25, slider_ypos, resume_str);
169
170 rb->lcd_drawrect(slider_margin, slider_ypos+17, slider_width, 8);
171 rb->lcd_fillrect(slider_margin, slider_ypos+17,
172 current_val*slider_width/max_val, 8);
173
174 rb->snprintf(resume_str, sizeof(resume_str), "%u.%u", current_hol,
175 current_rem);
176 rb->lcd_putsxy(slider_margin+(current_val*slider_width/max_val)-16,
177 slider_ypos+29, resume_str);
178
179 rb->lcd_update_rect(0, slider_ypos, LCD_WIDTH, LCD_HEIGHT-slider_ypos);
180}
181
182int get_start_time(int play_time, int in_file)
183{
184 int quit = 0;
185 int button = 0;
186 int resume_time = settings.resume_time;
187 int slider_ypos = LCD_HEIGHT-45;
188 int seek_rtn;
189
190 slider_state = state0;
191 thumbDelayTimer = *(rb->current_tick);
192 rb->lcd_clear_display();
193 rb->lcd_update();
194
195 while(quit == 0)
196 {
197 button = rb->button_get(false);
198 switch (button)
199 {
200#if (CONFIG_KEYPAD == GIGABEAT_PAD) || \
201 (CONFIG_KEYPAD == SANSA_E200_PAD) || \
202 (CONFIG_KEYPAD == SANSA_C200_PAD)
203 case MPEG_DOWN:
204 case MPEG_DOWN | BUTTON_REPEAT:
205 if ((resume_time -= 20) < 0)
206 resume_time = 0;
207 slider_state = state0;
208 thumbDelayTimer = *(rb->current_tick);
209 break;
210 case MPEG_UP:
211 case MPEG_UP | BUTTON_REPEAT:
212 if ((resume_time += 20) > play_time)
213 resume_time = play_time;
214 slider_state = state0;
215 thumbDelayTimer = *(rb->current_tick);
216 break;
217#endif
218 case MPEG_LEFT:
219 case MPEG_LEFT | BUTTON_REPEAT:
220 case MPEG_SCROLL_UP:
221 case MPEG_SCROLL_UP | BUTTON_REPEAT:
222 if (--resume_time < 0)
223 resume_time = 0;
224 slider_state = state0;
225 thumbDelayTimer = *(rb->current_tick);
226 break;
227 case MPEG_RIGHT:
228 case MPEG_RIGHT | BUTTON_REPEAT:
229 case MPEG_SCROLL_DOWN:
230 case MPEG_SCROLL_DOWN | BUTTON_REPEAT:
231 if (++resume_time > play_time)
232 resume_time = play_time;
233 slider_state = state0;
234 thumbDelayTimer = *(rb->current_tick);
235 break;
236 case MPEG_SELECT:
237 quit = 1;
238 break;
239 case MPEG_EXIT:
240 resume_time = -1;
241 quit = 1;
242 break;
243 default:
244 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
245 {
246 resume_time = -1;
247 quit = 1;
248 }
249 break;
250 }
251
252 rb->yield();
253
254 switch(slider_state)
255 {
256 case state0:
257 rb->lcd_clear_display();
258 rb->lcd_update();
259#ifdef HAVE_LCD_COLOR
260 if (resume_time > 0)
261 rb->splash(0, "loading ...");
262#endif
263 slider_state = state1;
264 break;
265 case state1:
266 if (*(rb->current_tick) - thumbDelayTimer > 75)
267 slider_state = state2;
268 if (resume_time == 0)
269 {
270 seek_rtn = 0;
271 slider_state = state5;
272 }
273 draw_slider(slider_ypos, play_time, resume_time);
274 break;
275 case state2:
276 if ( (seek_rtn = seek_PTS(in_file, resume_time, 1)) >= 0)
277 slider_state = state3;
278 else if (seek_rtn == -101)
279 {
280 slider_state = state0;
281 thumbDelayTimer = *(rb->current_tick);
282 }
283 else
284 slider_state = state4;
285 break;
286 case state3:
287 display_thumb(in_file);
288 draw_slider(slider_ypos, play_time, resume_time);
289 slider_state = state4;
290 break;
291 case state4:
292 draw_slider(slider_ypos, play_time, resume_time);
293 slider_state = state5;
294 break;
295 case state5:
296 break;
297 }
298 }
299
300 return resume_time;
301}
302
303int mpeg_start_menu(int play_time, int in_file)
304{
305 int m;
306 int result = 0;
307 int menu_quit = 0;
308
309 /* add the resume time to the menu display */
310 char resume_str[32];
311 int time_hol = (int)(settings.resume_time/2);
312 int time_rem = ((settings.resume_time%2)==0) ? 0 : 5;
313 rb->snprintf(resume_str, sizeof(resume_str),
314 "Resume time (min): %d.%d", time_hol, time_rem);
315
316 struct menu_item items[] = {
317 { "Play from beginning", NULL },
318 { resume_str, NULL },
319 { "Set start time (min)", NULL },
320 { "Quit mpegplayer", NULL },
321 };
322
323 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
324 NULL, NULL, NULL, NULL);
325
326 rb->button_clear_queue();
327
328 while(menu_quit == 0)
329 {
330 result = menu_show(m);
331
332 switch (result)
333 {
334 case 0:
335 menu_quit = 1;
336 result = 0;
337 break;
338 case 1:
339 menu_quit = 1;
340 result = settings.resume_time;
341 break;
342 case 2:
343#ifndef HAVE_LCD_COLOR
344 gray_show(true);
345#endif
346 if ((result = get_start_time(play_time, in_file)) >= 0)
347 menu_quit = 1;
348#ifndef HAVE_LCD_COLOR
349 gray_show(false);
350#endif
351 break;
352 case 3:
353 menu_quit = 1;
354 result = -1;
355 break;
356 default:
357 if (result == MENU_ATTACHED_USB)
358 {
359 menu_quit = 1;
360 result = -1;
361 }
362 break;
363 }
364 }
365 menu_exit(m);
366
367 settings.resume_time = result;
368 return (int)result;
369}
370
371void clear_resume_count(void)
372{
373 configfile_save(SETTINGS_FILENAME, config,
374 sizeof(config)/sizeof(*config),
375 SETTINGS_VERSION);
376
377 settings.resume_count = 0;
378
379 /* add this place holder so the count is above resume entries */
380 configfile_update_entry(SETTINGS_FILENAME, "Resume count", 0);
381}
382
71bool mpeg_menu(void) 383bool mpeg_menu(void)
72{ 384{
73 int m; 385 int m;
74 int result; 386 int result;
75 int menu_quit=0; 387 int menu_quit=0;
76 388
77 static const struct menu_item items[] = { 389 /* add the clear resume option to the menu display */
390 char clear_str[32];
391 rb->snprintf(clear_str, sizeof(clear_str),
392 "Clear all resumes: %u", settings.resume_count);
393
394 struct menu_item items[] = {
78#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) 395#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
79 [MPEG_OPTION_DISPLAY_SETTINGS] = 396 [MPEG_OPTION_DISPLAY_SETTINGS] =
80 { "Display Options", NULL }, 397 { "Display Options", NULL },
@@ -85,6 +402,8 @@ bool mpeg_menu(void)
85 { "Limit FPS", NULL }, 402 { "Limit FPS", NULL },
86 [MPEG_OPTION_SKIP_FRAMES] = 403 [MPEG_OPTION_SKIP_FRAMES] =
87 { "Skip frames", NULL }, 404 { "Skip frames", NULL },
405 [MPEG_OPTION_CLEAR_RESUMES] =
406 { clear_str, NULL },
88 [MPEG_OPTION_QUIT] = 407 [MPEG_OPTION_QUIT] =
89 { "Quit mpegplayer", NULL }, 408 { "Quit mpegplayer", NULL },
90 }; 409 };
@@ -115,6 +434,11 @@ bool mpeg_menu(void)
115 rb->set_option("Skip frames",&settings.skipframes,INT, 434 rb->set_option("Skip frames",&settings.skipframes,INT,
116 noyes, 2, NULL); 435 noyes, 2, NULL);
117 break; 436 break;
437 case MPEG_OPTION_CLEAR_RESUMES:
438 clear_resume_count();
439 rb->snprintf(clear_str, sizeof(clear_str),
440 "Clear all resumes: %u", 0);
441 break;
118 case MPEG_OPTION_QUIT: 442 case MPEG_OPTION_QUIT:
119 default: 443 default:
120 menu_quit=1; 444 menu_quit=1;
@@ -132,48 +456,82 @@ bool mpeg_menu(void)
132 return (result==MPEG_OPTION_QUIT); 456 return (result==MPEG_OPTION_QUIT);
133} 457}
134 458
135 459void init_settings(const char* filename)
136void init_settings(void)
137{ 460{
138 /* Set the default settings */ 461 /* Set the default settings */
139 settings.showfps = 0; /* Do not show FPS */ 462 settings.showfps = 0; /* Do not show FPS */
140 settings.limitfps = 1; /* Limit FPS */ 463 settings.limitfps = 1; /* Limit FPS */
141 settings.skipframes = 1; /* Skip frames */ 464 settings.skipframes = 1; /* Skip frames */
465 settings.resume_count = -1;
142#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) 466#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
143 settings.displayoptions = 0; /* No visual effects */ 467 settings.displayoptions = 0; /* No visual effects */
144#endif 468#endif
145 469
146 configfile_init(rb); 470 configfile_init(rb);
147 471
148 if (configfile_load(SETTINGS_FILENAME, config, 472 /* If the config file don't contain resume count
149 sizeof(config)/sizeof(*config), 473 or the load fails, then rebuild the config file.
150 SETTINGS_MIN_VERSION 474 This eliminates the worry for older config files
151 ) < 0) 475 having unused data. */
476 if (((settings.resume_count = configfile_get_value
477 (SETTINGS_FILENAME, "Resume count")) < 0) ||
478 (configfile_load(SETTINGS_FILENAME, config,
479 sizeof(config)/sizeof(*config),
480 SETTINGS_MIN_VERSION) < 0))
152 { 481 {
153 /* If the loading failed, save a new config file (as the disk is 482 /* Generate a new config file with default values */
154 already spinning) */
155 configfile_save(SETTINGS_FILENAME, config, 483 configfile_save(SETTINGS_FILENAME, config,
156 sizeof(config)/sizeof(*config), 484 sizeof(config)/sizeof(*config),
157 SETTINGS_VERSION); 485 SETTINGS_VERSION);
158 } 486 }
159 487
160 /* Keep a copy of the saved version of the settings - so we can check if
161 the settings have changed when we quit */
162 old_settings = settings;
163#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) 488#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
489 if ((settings.displayoptions =
490 configfile_get_value(SETTINGS_FILENAME, "Display options")) < 0)
491 {
492 configfile_update_entry(SETTINGS_FILENAME, "Display options",
493 (settings.displayoptions=0));
494 }
164 rb->lcd_yuv_set_options(settings.displayoptions); 495 rb->lcd_yuv_set_options(settings.displayoptions);
165#endif 496#endif
497
498 if (settings.resume_count < 0)
499 {
500 settings.resume_count = 0;
501
502 /* add this place holder so the count is above resume entries */
503 configfile_update_entry(SETTINGS_FILENAME, "Resume count", 0);
504 }
505
506 rb->strcpy(settings.resume_filename, filename);
507
508 /* get the resume time for the current mpeg if it exist */
509 if ((settings.resume_time = configfile_get_value
510 (SETTINGS_FILENAME, filename)) < 0)
511 {
512 settings.resume_time = 0;
513 }
166} 514}
167 515
168void save_settings(void) 516void save_settings(void)
169{ 517{
170 /* Save the user settings if they have changed */ 518 configfile_update_entry(SETTINGS_FILENAME, "Show FPS",
171 if (rb->memcmp(&settings,&old_settings,sizeof(settings))!=0) { 519 settings.showfps);
172 configfile_save(SETTINGS_FILENAME, config, 520 configfile_update_entry(SETTINGS_FILENAME, "Limit FPS",
173 sizeof(config)/sizeof(*config), 521 settings.limitfps);
174 SETTINGS_VERSION); 522 configfile_update_entry(SETTINGS_FILENAME, "Skip frames",
523 settings.skipframes);
175 524
176 /* Store the settings in old_settings - to check for future changes */ 525 /* If this was a new resume entry then update the total resume count */
177 old_settings = settings; 526 if (configfile_update_entry(SETTINGS_FILENAME, settings.resume_filename,
527 settings.resume_time) == 0)
528 {
529 configfile_update_entry(SETTINGS_FILENAME, "Resume count",
530 ++settings.resume_count);
178 } 531 }
532
533#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
534 configfile_update_entry(SETTINGS_FILENAME, "Display options",
535 settings.displayoptions);
536#endif
179} 537}
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 @@
2#include "plugin.h" 2#include "plugin.h"
3 3
4struct mpeg_settings { 4struct mpeg_settings {
5 int showfps; 5 int showfps; /* flag to display fps */
6 int limitfps; 6 int limitfps; /* flag to limit fps */
7 int skipframes; 7 int skipframes; /* flag to skip frames */
8 int resume_count; /* total # of resumes in config file */
9 int resume_time; /* resume time for current mpeg (in half minutes) */
10 char resume_filename[128]; /* filename of current mpeg */
11
8#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) 12#if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200)
9 unsigned displayoptions; 13 int displayoptions;
10#endif 14#endif
11}; 15};
12 16
13extern struct mpeg_settings settings; 17extern struct mpeg_settings settings;
14 18
19int get_start_time(int play_time, int in_file);
20int mpeg_start_menu(int play_time, int in_file);
15bool mpeg_menu(void); 21bool mpeg_menu(void);
16void init_settings(void); 22void init_settings(const char* filename);
17void save_settings(void); 23void save_settings(void);
24void 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
110#include "mpeg_settings.h" 110#include "mpeg_settings.h"
111#include "video_out.h" 111#include "video_out.h"
112#include "../../codecs/libmad/mad.h" 112#include "../../codecs/libmad/mad.h"
113#include "splash.h"
113 114
114PLUGIN_HEADER 115PLUGIN_HEADER
115PLUGIN_IRAM_DECLARE 116PLUGIN_IRAM_DECLARE
@@ -198,11 +199,8 @@ typedef struct
198 uint8_t* curr_packet_end; /* Current stream packet end */ 199 uint8_t* curr_packet_end; /* Current stream packet end */
199 200
200 uint8_t* prev_packet; /* Previous stream packet beginning */ 201 uint8_t* prev_packet; /* Previous stream packet beginning */
201 uint8_t* next_packet; /* Next stream packet beginning */ 202 size_t prev_packet_length; /* Lenth of previous packet */
202 203 size_t buffer_remaining; /* How much data is left in the buffer */
203 size_t guard_bytes; /* Number of bytes in guardbuf used */
204 uint64_t buffer_tail; /* Accumulation of bytes added */
205 uint64_t buffer_head; /* Accumulation of bytes removed */
206 uint32_t curr_pts; /* Current presentation timestamp */ 204 uint32_t curr_pts; /* Current presentation timestamp */
207 uint32_t curr_time; /* Current time in samples */ 205 uint32_t curr_time; /* Current time in samples */
208 uint32_t tagged; /* curr_pts is valid */ 206 uint32_t tagged; /* curr_pts is valid */
@@ -301,8 +299,7 @@ static intptr_t str_send_msg(Stream *str, int id, intptr_t data)
301 return str->dispatch_fn(str, msg); 299 return str->dispatch_fn(str, msg);
302#endif 300#endif
303 301
304 /* Only one thread at a time, please - only one core may safely send 302 /* Only one thread at a time, please */
305 right now */
306 rb->spinlock_lock(&str->msg_lock); 303 rb->spinlock_lock(&str->msg_lock);
307 304
308 str->ev.id = id; 305 str->ev.id = id;
@@ -333,13 +330,62 @@ static intptr_t str_send_msg(Stream *str, int id, intptr_t data)
333/* NOTE: Putting the following variables in IRAM cause audio corruption 330/* NOTE: Putting the following variables in IRAM cause audio corruption
334 on the ipod (reason unknown) 331 on the ipod (reason unknown)
335*/ 332*/
336static uint8_t *disk_buf IBSS_ATTR; 333static uint8_t *disk_buf_start IBSS_ATTR; /* Start pointer */
337static uint8_t *disk_buf_end IBSS_ATTR; 334static uint8_t *disk_buf_end IBSS_ATTR; /* End of buffer pointer less
338static uint8_t *disk_buf_tail IBSS_ATTR; 335 MPEG_GUARDBUF_SIZE. The
339static size_t buffer_size IBSS_ATTR; 336 guard space is used to wrap
337 data at the buffer start to
338 pass continuous data
339 packets */
340static uint8_t *disk_buf_tail IBSS_ATTR; /* Location of last data + 1
341 filled into the buffer */
342static size_t disk_buf_size IBSS_ATTR; /* The total buffer length
343 including the guard
344 space */
345static size_t file_remaining IBSS_ATTR;
346
347#if NUM_CORES > 1
348/* Some stream variables are shared between cores */
349struct mutex stream_lock IBSS_ATTR;
350static inline void init_stream_lock(void)
351 { rb->spinlock_init(&stream_lock); }
352static inline void lock_stream(void)
353 { rb->spinlock_lock(&stream_lock); }
354static inline void unlock_stream(void)
355 { rb->spinlock_unlock(&stream_lock); }
356#else
357/* No RMW issue here */
358static inline void init_stream_lock(void)
359 { }
360static inline void lock_stream(void)
361 { }
362static inline void unlock_stream(void)
363 { }
364#endif
365
366static int audio_sync_start IBSS_ATTR; /* If 0, the audio thread
367 yields waiting on the video
368 thread to synchronize with
369 the stream */
370static uint32_t audio_sync_time IBSS_ATTR; /* The time that the video
371 thread has reached after
372 synchronizing. The
373 audio thread now needs
374 to advance to this
375 time */
376static int video_sync_start IBSS_ATTR; /* While 0, the video thread
377 yields until the audio
378 thread has reached the
379 audio_sync_time */
380static int video_thumb_print IBSS_ATTR; /* If 1, the video thread is
381 only decoding one frame for
382 use in the menu. If 0,
383 normal operation */
384static int play_time IBSS_ATTR; /* The movie time as represented by
385 the maximum audio PTS tag in the
386 stream converted to half minutes */
387char *filename; /* hack for resume time storage */
340 388
341#define MSG_BUFFER_NEARLY_EMPTY 1
342#define MSG_EXIT_REQUESTED 2
343 389
344/* Various buffers */ 390/* Various buffers */
345/* TODO: Can we reduce the PCM buffer size? */ 391/* TODO: Can we reduce the PCM buffer size? */
@@ -350,7 +396,7 @@ static size_t buffer_size IBSS_ATTR;
350#define LIBMPEG2BUFFER_SIZE (2*1024*1024) 396#define LIBMPEG2BUFFER_SIZE (2*1024*1024)
351 397
352/* 65536+6 is required since each PES has a 6 byte header with a 16 bit packet length field */ 398/* 65536+6 is required since each PES has a 6 byte header with a 16 bit packet length field */
353#define MPEG_GUARDBUF_SIZE (64*1024+1024) /* Keep a bit extra - excessive for now */ 399#define MPEG_GUARDBUF_SIZE (65*1024) /* Keep a bit extra - excessive for now */
354#define MPEG_LOW_WATERMARK (1024*1024) 400#define MPEG_LOW_WATERMARK (1024*1024)
355 401
356static void pcm_playback_play_pause(bool play); 402static void pcm_playback_play_pause(bool play);
@@ -471,8 +517,47 @@ static void init_mad(void* mad_frame_overlap)
471 ((p)[b3] << 6) | \ 517 ((p)[b3] << 6) | \
472 ((p)[b4] >> 2 ))) 518 ((p)[b4] >> 2 )))
473 519
474/* This function demuxes the streams and gives the next stream data pointer */ 520/* This function synchronizes the mpeg stream. The function returns
475static void get_next_data( Stream* str ) 521 true on error */
522bool sync_data_stream(uint8_t **p)
523{
524 for (;;)
525 {
526 while ( !CMP_4_CONST(*p, PACK_START_CODE) && (*p) < disk_buf_tail )
527 (*p)++;
528 if ( (*p) >= disk_buf_tail )
529 break;
530 uint8_t *p_save = (*p);
531 if ( ((*p)[4] & 0xc0) == 0x40 ) /* mpeg-2 */
532 (*p) += 14 + ((*p)[13] & 7);
533 else if ( ((*p)[4] & 0xf0) == 0x20 ) /* mpeg-1 */
534 (*p) += 12;
535 else
536 (*p) += 5;
537 if ( (*p) >= disk_buf_tail )
538 break;
539 if ( CMP_3_CONST(*p, PACKET_START_CODE_PREFIX) )
540 {
541 (*p) = p_save;
542 break;
543 }
544 else
545 (*p) = p_save+1;
546 }
547
548 if ( (*p) >= disk_buf_tail )
549 return true;
550 else
551 return false;
552}
553
554/* This function demuxes the streams and gives the next stream data
555 pointer. Type 0 is normal operation. Type 1 and 2 have been added
556 for rapid seeks into the data stream. Type 1 and 2 ignore the
557 video_sync_start state (a signal to yield for refilling the
558 buffer). Type 1 will append more data to the buffer tail (minumal
559 bufer size reads that are increased only as needed). */
560static int get_next_data( Stream* str, uint8_t type )
476{ 561{
477 uint8_t *p; 562 uint8_t *p;
478 uint8_t *header; 563 uint8_t *header;
@@ -481,30 +566,49 @@ static void get_next_data( Stream* str )
481 static int mpeg1_skip_table[16] = 566 static int mpeg1_skip_table[16] =
482 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 567 { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
483 568
484 if (str->curr_packet_end == NULL) 569 if ( (p=str->curr_packet_end) == NULL)
485 { 570 p = disk_buf_start;
486 /* What does this do? */
487 while ((p = disk_buf) == NULL)
488 {
489 rb->lcd_putsxy(0,LCD_HEIGHT-10,"FREEZE!");
490 rb->lcd_update();
491 rb->sleep(HZ);
492 }
493 }
494 else
495 {
496 p = str->curr_packet_end;
497 }
498 571
499 while (1) 572 while (1)
500 { 573 {
501 int length, bytes; 574 int length, bytes;
502 575
503 if (p >= disk_buf_end) 576 /* Yield for buffer filling */
577 if ( (type == 0) && (str->buffer_remaining < 120*1024) && (file_remaining > 0) )
578 while ( (str->buffer_remaining < 512*1024) && (file_remaining > 0) )
579 rb->yield();
580
581 /* The packet start position (plus an arbitrary header length)
582 has exceeded the amount of data in the buffer */
583 if ( type == 1 && (p+50) >= disk_buf_tail )
504 { 584 {
505 p = disk_buf + (p - disk_buf_end); 585 DEBUGF("disk buffer overflow\n");
586 return 1;
506 } 587 }
507 588
589 /* are we at the end of file? */
590 {
591 size_t tmp_length;
592 if (p < str->prev_packet)
593 tmp_length = (disk_buf_end - str->prev_packet) +
594 (p - disk_buf_start);
595 else
596 tmp_length = (p - str->prev_packet);
597 if (0 == str->buffer_remaining-tmp_length-str->prev_packet_length)
598 {
599 str->curr_packet_end = str->curr_packet = NULL;
600 break;
601 }
602 }
603
604 /* wrap the disk buffer */
605 if (p >= disk_buf_end)
606 p = disk_buf_start + (p - disk_buf_end);
607
608 /* wrap packet header if needed */
609 if ( (p+50) >= disk_buf_end )
610 rb->memcpy(disk_buf_end, disk_buf_start, 50);
611
508 /* Pack header, skip it */ 612 /* Pack header, skip it */
509 if (CMP_4_CONST(p, PACK_START_CODE)) 613 if (CMP_4_CONST(p, PACK_START_CODE))
510 { 614 {
@@ -521,7 +625,6 @@ static void get_next_data( Stream* str )
521 rb->splash( 30, "Weird Pack header!" ); 625 rb->splash( 30, "Weird Pack header!" );
522 p += 5; 626 p += 5;
523 } 627 }
524 /*rb->splash( 30, "Pack header" );*/
525 } 628 }
526 629
527 /* System header, parse and skip it - four bytes */ 630 /* System header, parse and skip it - four bytes */
@@ -535,29 +638,29 @@ static void get_next_data( Stream* str )
535 638
536 p += header_length; 639 p += header_length;
537 640
538 if (p >= disk_buf_end) 641 if ( p >= disk_buf_end )
539 { 642 p = disk_buf_start + (p - disk_buf_end);
540 p = disk_buf + (p - disk_buf_end);
541 }
542 /*rb->splash( 30, "System header" );*/
543 } 643 }
544 644
545 /* Packet header, parse it */ 645 /* Packet header, parse it */
546 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) 646 if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX))
547 { 647 {
548 /* Problem */ 648 /* Problem */
549 //rb->splash( HZ*3, "missing packet start code prefix : %X%X at %X", *p, *(p+2), p-disk_buf ); 649 rb->splash( HZ*3, "missing packet start code prefix : %X%X at %lX",
650 *p, *(p+2), (long unsigned int)(p-disk_buf_start) );
651
652 DEBUGF("end diff: %X,%X,%X,%X,%X,%X\n",(int)str->curr_packet_end,
653 (int)audio_str.curr_packet_end,(int)video_str.curr_packet_end,
654 (int)disk_buf_start,(int)disk_buf_end,(int)disk_buf_tail);
655
550 str->curr_packet_end = str->curr_packet = NULL; 656 str->curr_packet_end = str->curr_packet = NULL;
551 break; 657 break;
552 //++p;
553 //break;
554 } 658 }
555 659
556 /* We retrieve basic infos */ 660 /* We retrieve basic infos */
557 stream = p[3]; 661 stream = p[3];
558 length = (p[4] << 8) | p[5]; 662 length = (p[4] << 8) | p[5];
559 663
560 /*rb->splash( 100, "Stream : %X", stream );*/
561 if (stream != str->id) 664 if (stream != str->id)
562 { 665 {
563 /* End of stream ? */ 666 /* End of stream ? */
@@ -618,11 +721,9 @@ static void get_next_data( Stream* str )
618 break; 721 break;
619 } 722 }
620 } 723 }
621 724
622 if ((header[length - 1] & 0xc0) == 0x40) 725 if ( (header[length - 1] & 0xc0) == 0x40 )
623 {
624 length += 2; 726 length += 2;
625 }
626 727
627 len_skip = length; 728 len_skip = length;
628 length += mpeg1_skip_table[header[length - 1] >> 4]; 729 length += mpeg1_skip_table[header[length - 1] >> 4];
@@ -657,20 +758,19 @@ static void get_next_data( Stream* str )
657 if (bytes > 0) 758 if (bytes > 0)
658 { 759 {
659 str->curr_packet_end = p + bytes; 760 str->curr_packet_end = p + bytes;
660 //DEBUGF("prev = %d, curr = %d\n",str->prev_packet,str->curr_packet);
661 761
662 if (str->curr_packet != NULL) 762 if (str->curr_packet != NULL)
663 { 763 {
764 lock_stream();
765
766 str->buffer_remaining -= str->prev_packet_length;
664 if (str->curr_packet < str->prev_packet) 767 if (str->curr_packet < str->prev_packet)
665 { 768 str->prev_packet_length = (disk_buf_end - str->prev_packet) +
666 str->buffer_head += (disk_buf_end - str->prev_packet) + 769 (str->curr_packet - disk_buf_start);
667 (str->curr_packet - disk_buf);
668 str->guard_bytes = 0;
669 }
670 else 770 else
671 { 771 str->prev_packet_length = (str->curr_packet - str->prev_packet);
672 str->buffer_head += (str->curr_packet - str->prev_packet); 772
673 } 773 unlock_stream();
674 774
675 str->prev_packet = str->curr_packet; 775 str->prev_packet = str->curr_packet;
676 } 776 }
@@ -678,14 +778,13 @@ static void get_next_data( Stream* str )
678 str->curr_packet = p; 778 str->curr_packet = p;
679 779
680 if (str->curr_packet_end > disk_buf_end) 780 if (str->curr_packet_end > disk_buf_end)
681 { 781 rb->memcpy(disk_buf_end, disk_buf_start,
682 str->guard_bytes = str->curr_packet_end - disk_buf_end; 782 str->curr_packet_end - disk_buf_end );
683 rb->memcpy(disk_buf_end, disk_buf, str->guard_bytes);
684 }
685 } 783 }
686 784
687 break; 785 break;
688 } /* end while */ 786 } /* end while */
787 return 0;
689} 788}
690 789
691/* Our clock rate in ticks/second - this won't be a constant for long */ 790/* Our clock rate in ticks/second - this won't be a constant for long */
@@ -943,6 +1042,8 @@ static int button_loop(void)
943 int vol, minvol, maxvol; 1042 int vol, minvol, maxvol;
944 int button; 1043 int button;
945 1044
1045 if (video_sync_start==1) {
1046
946 if (str_have_msg(&audio_str)) 1047 if (str_have_msg(&audio_str))
947 { 1048 {
948 struct event ev; 1049 struct event ev;
@@ -1014,6 +1115,7 @@ static int button_loop(void)
1014 rb->lcd_setfont(FONT_SYSFIXED); 1115 rb->lcd_setfont(FONT_SYSFIXED);
1015 1116
1016 if (result) { 1117 if (result) {
1118 settings.resume_time = (int)(get_stream_time()/44100/30);
1017 str_send_msg(&video_str, STREAM_QUIT, 0); 1119 str_send_msg(&video_str, STREAM_QUIT, 0);
1018 audio_str.status = STREAM_STOPPED; 1120 audio_str.status = STREAM_STOPPED;
1019 } else { 1121 } else {
@@ -1024,6 +1126,7 @@ static int button_loop(void)
1024 break; 1126 break;
1025 1127
1026 case MPEG_STOP: 1128 case MPEG_STOP:
1129 settings.resume_time = (int)(get_stream_time()/44100/30);
1027 str_send_msg(&video_str, STREAM_QUIT, 0); 1130 str_send_msg(&video_str, STREAM_QUIT, 0);
1028 audio_str.status = STREAM_STOPPED; 1131 audio_str.status = STREAM_STOPPED;
1029 break; 1132 break;
@@ -1060,7 +1163,7 @@ static int button_loop(void)
1060 audio_str.status = STREAM_STOPPED; 1163 audio_str.status = STREAM_STOPPED;
1061 } 1164 }
1062 } 1165 }
1063 1166 }
1064quit: 1167quit:
1065 return audio_str.status; 1168 return audio_str.status;
1066} 1169}
@@ -1086,7 +1189,23 @@ static void audio_thread(void)
1086 pcm_playback_play(0); 1189 pcm_playback_play(0);
1087 1190
1088 /* Get first packet */ 1191 /* Get first packet */
1089 get_next_data(&audio_str); 1192 get_next_data(&audio_str, 0 );
1193
1194 /* skip audio packets here */
1195 while (audio_sync_start==0)
1196 {
1197 audio_str.status = STREAM_PLAYING;
1198 rb->yield();
1199 }
1200
1201 if (audio_sync_time>10000)
1202 {
1203 while (TS_TO_TICKS(audio_str.curr_pts) < audio_sync_time - 10000)
1204 {
1205 get_next_data(&audio_str, 0 );
1206 rb->priority_yield();
1207 }
1208 }
1090 1209
1091 if (audio_str.curr_packet == NULL) 1210 if (audio_str.curr_packet == NULL)
1092 goto done; 1211 goto done;
@@ -1165,7 +1284,7 @@ static void audio_thread(void)
1165 mpabuf = mpa_buffer; 1284 mpabuf = mpa_buffer;
1166 1285
1167 /* Get data from next audio packet */ 1286 /* Get data from next audio packet */
1168 get_next_data(&audio_str); 1287 get_next_data(&audio_str, 0 );
1169 } 1288 }
1170 while (audio_str.curr_packet != NULL && 1289 while (audio_str.curr_packet != NULL &&
1171 mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD); 1290 mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD);
@@ -1198,8 +1317,6 @@ static void audio_thread(void)
1198 1317
1199 if (mad_stat != 0) 1318 if (mad_stat != 0)
1200 { 1319 {
1201 DEBUGF("Audio stream error - %d\n", stream.error);
1202
1203 if (stream.error == MAD_FLAG_INCOMPLETE 1320 if (stream.error == MAD_FLAG_INCOMPLETE
1204 || stream.error == MAD_ERROR_BUFLEN) 1321 || stream.error == MAD_ERROR_BUFLEN)
1205 { 1322 {
@@ -1259,6 +1376,13 @@ static void audio_thread(void)
1259 rb->priority_yield(); 1376 rb->priority_yield();
1260 } 1377 }
1261 1378
1379 if (video_sync_start == 0 &&
1380 pts->pts+(uint32_t)synth.pcm.length<audio_sync_time) {
1381 synth.pcm.length = 0;
1382 size = 0;
1383 rb->yield();
1384 }
1385
1262 /* TODO: This part will be replaced with dsp calls soon */ 1386 /* TODO: This part will be replaced with dsp calls soon */
1263 if (MAD_NCHANNELS(&frame.header) == 2) 1387 if (MAD_NCHANNELS(&frame.header) == 2)
1264 { 1388 {
@@ -1305,6 +1429,7 @@ static void audio_thread(void)
1305 audio_str.status = STREAM_PLAYING; 1429 audio_str.status = STREAM_PLAYING;
1306 pcmbuf_threshold = PCMBUF_PLAY_ALL; 1430 pcmbuf_threshold = PCMBUF_PLAY_ALL;
1307 pcm_playback_seek_time(pcmbuf_tail->time); 1431 pcm_playback_seek_time(pcmbuf_tail->time);
1432 video_sync_start = 1;
1308 } 1433 }
1309 1434
1310 /* Make this data available to DMA */ 1435 /* Make this data available to DMA */
@@ -1391,29 +1516,32 @@ static void video_thread(void)
1391 1516
1392 /* Clear the display - this is mainly just to indicate that the 1517 /* Clear the display - this is mainly just to indicate that the
1393 video thread has started successfully. */ 1518 video thread has started successfully. */
1394 rb->lcd_clear_display(); 1519 if (!video_thumb_print)
1395 rb->lcd_update(); 1520 {
1521 rb->lcd_clear_display();
1522 rb->lcd_update();
1523 }
1396 1524
1397 /* Request the first packet data */ 1525 /* Request the first packet data */
1398 get_next_data( &video_str ); 1526 get_next_data( &video_str, 0 );
1399 1527
1400 if (video_str.curr_packet == NULL) 1528 if (video_str.curr_packet == NULL)
1401 goto done; 1529 goto video_thread_quit;
1402 1530
1403 mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); 1531 mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end);
1404 total_offset += video_str.curr_packet_end - video_str.curr_packet; 1532 total_offset += video_str.curr_packet_end - video_str.curr_packet;
1405 1533
1406 info = mpeg2_info (mpeg2dec); 1534 info = mpeg2_info (mpeg2dec);
1407 1535
1408 /* Wait if the audio thread is buffering - i.e. before
1409 the first frames are decoded */
1410 while (audio_str.status == STREAM_BUFFERING)
1411 rb->priority_yield();
1412
1413 while (1) 1536 while (1)
1414 { 1537 {
1415 /* quickly check mailbox first */ 1538 /* quickly check mailbox first */
1416 if (str_have_msg(&video_str)) 1539 if (video_thumb_print)
1540 {
1541 if (video_str.status == STREAM_STOPPED)
1542 break;
1543 }
1544 else if (str_have_msg(&video_str))
1417 { 1545 {
1418 while (1) 1546 while (1)
1419 { 1547 {
@@ -1450,7 +1578,8 @@ static void video_thread(void)
1450 { 1578 {
1451 case STATE_BUFFER: 1579 case STATE_BUFFER:
1452 /* Request next packet data */ 1580 /* Request next packet data */
1453 get_next_data( &video_str ); 1581 get_next_data( &video_str, 0 );
1582
1454 mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); 1583 mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end);
1455 total_offset += video_str.curr_packet_end - video_str.curr_packet; 1584 total_offset += video_str.curr_packet_end - video_str.curr_packet;
1456 info = mpeg2_info (mpeg2dec); 1585 info = mpeg2_info (mpeg2dec);
@@ -1458,7 +1587,7 @@ static void video_thread(void)
1458 if (video_str.curr_packet == NULL) 1587 if (video_str.curr_packet == NULL)
1459 { 1588 {
1460 /* No more data. */ 1589 /* No more data. */
1461 goto done; 1590 goto video_thread_quit;
1462 } 1591 }
1463 continue; 1592 continue;
1464 1593
@@ -1528,8 +1657,12 @@ static void video_thread(void)
1528 break; 1657 break;
1529 1658
1530 /* No limiting => no dropping - draw this frame */ 1659 /* No limiting => no dropping - draw this frame */
1531 if (!settings.limitfps) 1660 if (!settings.limitfps && (video_thumb_print == 0))
1661 {
1662 audio_sync_start = 1;
1663 video_sync_start = 1;
1532 goto picture_draw; 1664 goto picture_draw;
1665 }
1533 1666
1534 /* Get presentation times in audio samples - quite accurate 1667 /* Get presentation times in audio samples - quite accurate
1535 enough - add previous frame duration if not stamped */ 1668 enough - add previous frame duration if not stamped */
@@ -1538,7 +1671,19 @@ static void video_thread(void)
1538 1671
1539 period = TIME_TO_TICKS(info->sequence->frame_period); 1672 period = TIME_TO_TICKS(info->sequence->frame_period);
1540 1673
1674 if ( (video_thumb_print == 1 || video_sync_start == 0) &&
1675 ((int)(info->current_picture->flags & PIC_MASK_CODING_TYPE)
1676 == PIC_FLAG_CODING_TYPE_B))
1677 break;
1678
1541 eta_video = curr_time; 1679 eta_video = curr_time;
1680
1681 audio_sync_time = eta_video;
1682 audio_sync_start = 1;
1683
1684 while (video_sync_start == 0)
1685 rb->yield();
1686
1542 eta_audio = get_stream_time(); 1687 eta_audio = get_stream_time();
1543 1688
1544 /* How early/late are we? > 0 = late, < 0 early */ 1689 /* How early/late are we? > 0 = late, < 0 early */
@@ -1664,32 +1809,39 @@ static void video_thread(void)
1664 1809
1665 picture_wait: 1810 picture_wait:
1666 /* Wait until audio catches up */ 1811 /* Wait until audio catches up */
1667 while (eta_video > eta_audio) 1812 if (video_thumb_print)
1668 { 1813 video_str.status = STREAM_STOPPED;
1669 rb->priority_yield(); 1814 else
1670 1815 while (eta_video > eta_audio)
1671 /* Make sure not to get stuck waiting here forever */
1672 if (str_have_msg(&video_str))
1673 { 1816 {
1674 str_look_msg(&video_str, &ev); 1817 rb->priority_yield();
1675 1818
1676 /* If not to play, process up top */ 1819 /* Make sure not to get stuck waiting here forever */
1677 if (ev.id != STREAM_PLAY) 1820 if (str_have_msg(&video_str))
1678 goto rendering_finished; 1821 {
1822 str_look_msg(&video_str, &ev);
1823
1824 /* If not to play, process up top */
1825 if (ev.id != STREAM_PLAY)
1826 goto rendering_finished;
1827
1828 /* Told to play but already playing */
1829 str_get_msg(&video_str, &ev);
1830 str_reply_msg(&video_str, 1);
1831 }
1679 1832
1680 /* Told to play but already playing */ 1833 eta_audio = get_stream_time();
1681 str_get_msg(&video_str, &ev);
1682 str_reply_msg(&video_str, 1);
1683 } 1834 }
1684 1835
1685 eta_audio = get_stream_time();
1686 }
1687
1688 picture_draw: 1836 picture_draw:
1689 /* Record last frame time */ 1837 /* Record last frame time */
1690 last_render = *rb->current_tick; 1838 last_render = *rb->current_tick;
1691 1839
1692 vo_draw_frame(info->display_fbuf->buf); 1840 if (video_thumb_print)
1841 vo_draw_frame_thumb(info->display_fbuf->buf);
1842 else
1843 vo_draw_frame(info->display_fbuf->buf);
1844
1693 num_drawn++; 1845 num_drawn++;
1694 1846
1695 picture_skip: 1847 picture_skip:
@@ -1724,53 +1876,298 @@ static void video_thread(void)
1724 rb->yield(); 1876 rb->yield();
1725 } 1877 }
1726 1878
1727done: 1879 video_thread_quit:
1728#if NUM_CORES > 1 1880 /* if video ends before time sync'd,
1729 flush_icache(); 1881 besure the audio thread is closed */
1730#endif 1882 if (video_sync_start == 0)
1883 {
1884 audio_str.status = STREAM_STOPPED;
1885 audio_sync_start = 1;
1886 }
1731 1887
1732 video_str.status = STREAM_DONE; 1888 #if NUM_CORES > 1
1889 flush_icache();
1890 #endif
1891
1892 mpeg2_close (mpeg2dec);
1893
1894 /* Commit suicide */
1895 video_str.status = STREAM_TERMINATED;
1896}
1733 1897
1734 while (1) 1898void initialize_stream( Stream *str, uint8_t *buffer_start, size_t disk_buf_len, int id )
1735 { 1899{
1736 str_get_msg(&video_str, &ev); 1900 str->curr_packet_end = str->curr_packet = NULL;
1901 str->prev_packet_length = 0;
1902 str->prev_packet = str->curr_packet_end = buffer_start;
1903 str->buffer_remaining = disk_buf_len;
1904 str->id = id;
1905}
1906
1907void display_thumb(int in_file)
1908{
1909 size_t disk_buf_len;
1737 1910
1738 if (ev.id == STREAM_QUIT) 1911 video_thumb_print = 1;
1739 break; 1912 audio_sync_start = 1;
1913 video_sync_start = 1;
1914
1915 disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE);
1916 disk_buf_tail = disk_buf_start + disk_buf_len;
1917 file_remaining = 0;
1918 initialize_stream(&video_str,disk_buf_start,disk_buf_len,0xe0);
1919
1920 video_str.status = STREAM_PLAYING;
1740 1921
1741 str_reply_msg(&video_str, 0); 1922 if ((video_str.thread = rb->create_thread(video_thread,
1923 (uint8_t*)video_stack,VIDEO_STACKSIZE,"mpgvideo"
1924 IF_PRIO(,PRIORITY_PLAYBACK)
1925 IF_COP(, COP, true))) == NULL)
1926 {
1927 rb->splash(HZ, "Cannot create video thread!");
1928 }
1929 else
1930 {
1931 while (video_str.status != STREAM_TERMINATED)
1932 rb->yield();
1742 } 1933 }
1743 1934
1744video_thread_quit: 1935 if ( video_str.curr_packet_end == video_str.curr_packet)
1745 /* Commit suicide */ 1936 rb->splash(0, "frame not available");
1746 video_str.status = STREAM_TERMINATED;
1747} 1937}
1748 1938
1939int find_length( int in_file )
1940{
1941 uint8_t *p;
1942 size_t read_length = 60*1024;
1943 size_t disk_buf_len;
1944
1945 play_time = 0;
1946
1947 /* temporary read buffer size cannot exceed buffer size */
1948 if ( read_length > disk_buf_size )
1949 read_length = disk_buf_size;
1950
1951 /* read tail of file */
1952 rb->lseek( in_file, -1*read_length, SEEK_END );
1953 disk_buf_len = rb->read( in_file, disk_buf_start, read_length );
1954 disk_buf_tail = disk_buf_start + disk_buf_len;
1955
1956 /* sync reader to this segment of the stream */
1957 p=disk_buf_start;
1958 if (sync_data_stream(&p))
1959 {
1960 DEBUGF("Could not sync stream\n");
1961 return PLUGIN_ERROR;
1962 }
1963
1964 /* find last PTS in audio stream; will movie always have audio? if
1965 the play time can not be determined, set play_time to 0 */
1966 audio_sync_start = 0;
1967 audio_sync_time = 0;
1968 video_sync_start = 0;
1969 {
1970 Stream tmp;
1971 initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0);
1972
1973 do
1974 {
1975 get_next_data(&tmp, 2);
1976 if (tmp.tagged == 1)
1977 /* 10 sec less to insure the video frame exist */
1978 play_time = (int)((tmp.curr_pts/45000-10)/30);
1979 }
1980 while (tmp.curr_packet_end != NULL);
1981 }
1982 return 0;
1983}
1984
1985ssize_t seek_PTS( int in_file, int start_time, int accept_button )
1986{
1987 static ssize_t last_seek_pos = 0;
1988 static int last_start_time = 0;
1989 ssize_t seek_pos;
1990 size_t disk_buf_len;
1991 uint8_t *p;
1992 size_t read_length = 60*1024;
1993
1994 /* temporary read buffer size cannot exceed buffer size */
1995 if ( read_length > disk_buf_size )
1996 read_length = disk_buf_size;
1997
1998 if ( start_time == last_start_time )
1999 {
2000 seek_pos = last_seek_pos;
2001 rb->lseek(in_file,seek_pos,SEEK_SET);
2002 }
2003 else if ( start_time != 0 )
2004 {
2005 seek_pos = rb->filesize(in_file)*start_time/play_time;
2006 int seek_pos_sec_inc = rb->filesize(in_file)/play_time/30;
2007
2008 if (seek_pos<0)
2009 seek_pos=0;
2010 if ((size_t)seek_pos > rb->filesize(in_file) - read_length)
2011 seek_pos = rb->filesize(in_file) - read_length;
2012 rb->lseek( in_file, seek_pos, SEEK_SET );
2013 disk_buf_len = rb->read( in_file, disk_buf_start, read_length );
2014 disk_buf_tail = disk_buf_start + disk_buf_len;
2015
2016 /* sync reader to this segment of the stream */
2017 p=disk_buf_start;
2018 if (sync_data_stream(&p))
2019 {
2020 DEBUGF("Could not sync stream\n");
2021 return PLUGIN_ERROR;
2022 }
2023
2024 /* find PTS >= start_time */
2025 audio_sync_start = 0;
2026 audio_sync_time = 0;
2027 video_sync_start = 0;
2028 {
2029 Stream tmp;
2030 initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0);
2031 int cont_seek_loop = 1;
2032 int coarse_seek = 1;
2033 do
2034 {
2035 if ( accept_button )
2036 {
2037 rb->yield();
2038 if (rb->button_available())
2039 return -101;
2040 }
2041
2042 while ( get_next_data(&tmp, 1) == 1 )
2043 {
2044 if ( tmp.curr_packet_end == disk_buf_start )
2045 seek_pos += disk_buf_tail - disk_buf_start;
2046 else
2047 seek_pos += tmp.curr_packet_end - disk_buf_start;
2048 if ((size_t)seek_pos > rb->filesize(in_file) - read_length)
2049 seek_pos = rb->filesize(in_file) - read_length;
2050 rb->lseek( in_file, seek_pos, SEEK_SET );
2051 disk_buf_len = rb->read ( in_file, disk_buf_start, read_length );
2052 disk_buf_tail = disk_buf_start + disk_buf_len;
2053
2054 /* sync reader to this segment of the stream */
2055 p=disk_buf_start;
2056 initialize_stream(&tmp,p,disk_buf_len,0xc0);
2057 }
2058
2059 /* are we after start_time in the stream? */
2060 if ( coarse_seek && (int)(tmp.curr_pts/45000) >= start_time*30 )
2061 {
2062 int time_to_backup = (int)(tmp.curr_pts/45000) - start_time*30;
2063 if (time_to_backup == 0)
2064 time_to_backup++;
2065 seek_pos -= seek_pos_sec_inc * time_to_backup;
2066 seek_pos_sec_inc -= seek_pos_sec_inc/20; /* for stability */
2067 if (seek_pos<0)
2068 seek_pos=0;
2069 if ((size_t)seek_pos > rb->filesize(in_file) - read_length)
2070 seek_pos = rb->filesize(in_file) - read_length;
2071 rb->lseek( in_file, seek_pos, SEEK_SET );
2072 disk_buf_len = rb->read( in_file, disk_buf_start, read_length );
2073 disk_buf_tail = disk_buf_start + disk_buf_len;
2074
2075 /* sync reader to this segment of the stream */
2076 p=disk_buf_start;
2077 if (sync_data_stream(&p))
2078 {
2079 DEBUGF("Could not sync stream\n");
2080 return PLUGIN_ERROR;
2081 }
2082 initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0);
2083 continue;
2084 }
2085
2086 /* are we well before start_time in the stream? */
2087 if ( coarse_seek && start_time*30 - (int)(tmp.curr_pts/45000) > 2 )
2088 {
2089 int time_to_advance = start_time*30 - (int)(tmp.curr_pts/45000) - 2;
2090 if (time_to_advance <= 0)
2091 time_to_advance = 1;
2092 seek_pos += seek_pos_sec_inc * time_to_advance;
2093 if (seek_pos<0)
2094 seek_pos=0;
2095 if ((size_t)seek_pos > rb->filesize(in_file) - read_length)
2096 seek_pos = rb->filesize(in_file) - read_length;
2097 rb->lseek( in_file, seek_pos, SEEK_SET );
2098 disk_buf_len = rb->read ( in_file, disk_buf_start, read_length );
2099 disk_buf_tail = disk_buf_start + disk_buf_len;
2100
2101 /* sync reader to this segment of the stream */
2102 p=disk_buf_start;
2103 if (sync_data_stream(&p))
2104 {
2105 DEBUGF("Could not sync stream\n");
2106 return PLUGIN_ERROR;
2107 }
2108 initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0);
2109 continue;
2110 }
2111
2112 coarse_seek = 0;
2113
2114 /* are we at start_time in the stream? */
2115 if ( (int)(tmp.curr_pts/45000) >= start_time*30 )
2116 cont_seek_loop = 0;
2117
2118 }
2119 while ( cont_seek_loop );
2120
2121
2122 DEBUGF("start diff: %u %u\n",(unsigned int)(tmp.curr_pts/45000),start_time*30);
2123 seek_pos+=tmp.curr_packet_end-disk_buf_start;
2124
2125 last_seek_pos = seek_pos;
2126 last_start_time = start_time;
2127
2128 rb->lseek(in_file,seek_pos,SEEK_SET);
2129 }
2130 }
2131 else
2132 {
2133 seek_pos = 0;
2134 rb->lseek(in_file,0,SEEK_SET);
2135 last_seek_pos = seek_pos;
2136 last_start_time = start_time;
2137 }
2138 return seek_pos;
2139}
2140
1749enum plugin_status plugin_start(struct plugin_api* api, void* parameter) 2141enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1750{ 2142{
1751 int status = PLUGIN_ERROR; /* assume failure */ 2143 int status = PLUGIN_ERROR; /* assume failure */
2144 int start_time=-1;
1752 void* audiobuf; 2145 void* audiobuf;
1753 ssize_t audiosize; 2146 ssize_t audiosize;
1754 int in_file; 2147 int in_file;
1755 uint8_t* buffer;
1756 size_t file_remaining;
1757 size_t disk_buf_len; 2148 size_t disk_buf_len;
2149 ssize_t seek_pos;
1758#ifndef HAVE_LCD_COLOR 2150#ifndef HAVE_LCD_COLOR
1759 long graysize; 2151 long graysize;
1760 int grayscales; 2152 int grayscales;
1761#endif 2153#endif
1762 2154
2155 audio_sync_start = 0;
2156 audio_sync_time = 0;
2157 video_sync_start = 0;
2158
1763 if (parameter == NULL) 2159 if (parameter == NULL)
1764 { 2160 {
1765 api->splash(HZ*2, "No File"); 2161 api->splash(HZ*2, "No File");
1766 return PLUGIN_ERROR;
1767 } 2162 }
1768 2163
1769 /* Initialize IRAM - stops audio and voice as well */ 2164 /* Initialize IRAM - stops audio and voice as well */
1770 PLUGIN_IRAM_INIT(api) 2165 PLUGIN_IRAM_INIT(api)
1771 2166
1772 rb = api; 2167 rb = api;
2168 rb->splash(0, "loading ...");
1773 2169
2170 /* sets audiosize and returns buffer pointer */
1774 audiobuf = rb->plugin_get_audio_buffer(&audiosize); 2171 audiobuf = rb->plugin_get_audio_buffer(&audiosize);
1775 2172
1776#if INPUT_SRC_CAPS != 0 2173#if INPUT_SRC_CAPS != 0
@@ -1781,49 +2178,38 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1781 2178
1782 rb->pcm_set_frequency(SAMPR_44); 2179 rb->pcm_set_frequency(SAMPR_44);
1783 2180
1784 /* Set disk pointers to NULL */ 2181#ifndef HAVE_LCD_COLOR
1785 disk_buf_end = disk_buf = NULL; 2182 /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */
1786 2183 grayscales = gray_init(rb, audiobuf, audiosize, false, LCD_WIDTH, LCD_HEIGHT,
1787 /* Stream construction */ 2184 32, 2<<8, &graysize) + 1;
1788 /* We take the first stream of each (audio and video) */ 2185 audiobuf += graysize;
1789 /* TODO : Search for these in the file first */ 2186 audiosize -= graysize;
1790 audio_str.curr_packet_end = audio_str.curr_packet = audio_str.next_packet = NULL; 2187 if (grayscales < 33 || audiosize <= 0)
1791 video_str = audio_str; 2188 {
1792 video_str.id = 0xe0; 2189 rb->splash(HZ, "gray buf error");
1793 audio_str.id = 0xc0; 2190 return PLUGIN_ERROR;
2191 }
2192#endif
1794 2193
1795 /* Initialise our malloc buffer */ 2194 /* Initialise our malloc buffer */
1796 audiosize = mpeg_alloc_init(audiobuf, audiosize, LIBMPEG2BUFFER_SIZE); 2195 audiosize = mpeg_alloc_init(audiobuf,audiosize, LIBMPEG2BUFFER_SIZE);
1797 if (audiosize == 0) 2196 if (audiosize == 0)
1798 return PLUGIN_ERROR; 2197 return PLUGIN_ERROR;
1799 2198
2199 /* Set disk pointers to NULL */
2200 disk_buf_end = disk_buf_start = NULL;
2201
1800 /* Grab most of the buffer for the compressed video - leave some for 2202 /* Grab most of the buffer for the compressed video - leave some for
1801 PCM audio data and some for libmpeg2 malloc use. */ 2203 PCM audio data and some for libmpeg2 malloc use. */
1802 buffer_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+ 2204 disk_buf_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+
1803 MPABUF_SIZE); 2205 MPABUF_SIZE);
1804 2206
1805 DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size); 2207 DEBUGF("audiosize=%ld, disk_buf_size=%ld\n",audiosize,disk_buf_size);
1806 buffer = mpeg_malloc(buffer_size,-1); 2208 disk_buf_start = mpeg_malloc(disk_buf_size,-1);
1807 2209
1808 if (buffer == NULL) 2210 if (disk_buf_start == NULL)
1809 return PLUGIN_ERROR; 2211 return PLUGIN_ERROR;
1810 2212
1811#ifndef HAVE_LCD_COLOR
1812 /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */
1813 grayscales = gray_init(rb, buffer, buffer_size, false, LCD_WIDTH, LCD_HEIGHT,
1814 32, 2<<8, &graysize) + 1;
1815 buffer += graysize;
1816 buffer_size -= graysize;
1817 if (grayscales < 33 || buffer_size <= 0)
1818 {
1819 rb->splash(HZ, "gray buf error");
1820 return PLUGIN_ERROR;
1821 }
1822#endif
1823
1824 buffer_size &= ~(0x7ff); /* Round buffer down to nearest 2KB */
1825 DEBUGF("audiosize=%ld, buffer_size=%ld\n",audiosize,buffer_size);
1826
1827 if (!init_mpabuf()) 2213 if (!init_mpabuf())
1828 return PLUGIN_ERROR; 2214 return PLUGIN_ERROR;
1829 2215
@@ -1836,9 +2222,10 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1836 in_file = rb->open((char*)parameter,O_RDONLY); 2222 in_file = rb->open((char*)parameter,O_RDONLY);
1837 2223
1838 if (in_file < 0){ 2224 if (in_file < 0){
1839 //fprintf(stderr,"Could not open %s\n",argv[1]); 2225 DEBUGF("Could not open %s\n",(char*)parameter);
1840 return PLUGIN_ERROR; 2226 return PLUGIN_ERROR;
1841 } 2227 }
2228 filename = (char*)parameter;
1842 2229
1843#ifdef HAVE_LCD_COLOR 2230#ifdef HAVE_LCD_COLOR
1844 rb->lcd_set_backdrop(NULL); 2231 rb->lcd_set_backdrop(NULL);
@@ -1860,34 +2247,51 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1860 cannot just return PLUGIN_ERROR - instead drop though to cleanup code 2247 cannot just return PLUGIN_ERROR - instead drop though to cleanup code
1861 */ 2248 */
1862 2249
1863 init_settings(); 2250 init_settings((char*)parameter);
1864 2251
1865 /* Initialise libmad */ 2252 /* Initialise libmad */
1866 rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap)); 2253 rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap));
1867 init_mad(mad_frame_overlap); 2254 init_mad(mad_frame_overlap);
1868 2255
1869 file_remaining = rb->filesize(in_file); 2256 disk_buf_end = disk_buf_start + disk_buf_size-MPEG_GUARDBUF_SIZE;
1870 disk_buf_end = buffer + buffer_size-MPEG_GUARDBUF_SIZE; 2257
2258 /* initalize play_time with the length (in half minutes) of the movie
2259 zero if the time could not be determined */
2260 find_length( in_file );
2261
2262 /* start menu */
2263 start_time = mpeg_start_menu(play_time, in_file);
2264 if ( start_time == -1 )
2265 return 0;
2266 else if ( start_time < 0 )
2267 start_time = 0;
2268 else if ( start_time > play_time )
2269 start_time = play_time;
2270
2271 rb->splash(0, "loading ...");
2272
2273 /* seek start time */
2274 seek_pos = seek_PTS( in_file, start_time, 0 );
2275
2276 rb->lseek(in_file,seek_pos,SEEK_SET);
2277 video_thumb_print = 0;
2278 audio_sync_start = 0;
2279 audio_sync_time = 0;
2280 video_sync_start = 0;
1871 2281
1872 /* Read some stream data */ 2282 /* Read some stream data */
1873 disk_buf_len = rb->read (in_file, buffer, MPEG_LOW_WATERMARK); 2283 disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE);
1874 2284
1875 DEBUGF("Initial Buffering - %d bytes\n",(int)disk_buf_len); 2285 disk_buf_tail = disk_buf_start + disk_buf_len;
1876 disk_buf = buffer; 2286 file_remaining = rb->filesize(in_file);
1877 disk_buf_tail = buffer+disk_buf_len; 2287 file_remaining -= disk_buf_len + seek_pos;
1878 file_remaining -= disk_buf_len; 2288
1879 2289 initialize_stream( &video_str, disk_buf_start, disk_buf_len, 0xe0 );
1880 audio_str.guard_bytes = 0; 2290 initialize_stream( &audio_str, disk_buf_start, disk_buf_len, 0xc0 );
1881 audio_str.prev_packet = disk_buf;
1882 audio_str.buffer_head = 0;
1883 audio_str.buffer_tail = disk_buf_len;
1884 video_str.guard_bytes = 0;
1885 video_str.prev_packet = disk_buf;
1886 video_str.buffer_head = 0;
1887 video_str.buffer_tail = disk_buf_len;
1888 2291
1889 rb->spinlock_init(&audio_str.msg_lock); 2292 rb->spinlock_init(&audio_str.msg_lock);
1890 rb->spinlock_init(&video_str.msg_lock); 2293 rb->spinlock_init(&video_str.msg_lock);
2294
1891 audio_str.status = STREAM_BUFFERING; 2295 audio_str.status = STREAM_BUFFERING;
1892 video_str.status = STREAM_PLAYING; 2296 video_str.status = STREAM_PLAYING;
1893 2297
@@ -1895,6 +2299,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1895 gray_show(true); 2299 gray_show(true);
1896#endif 2300#endif
1897 2301
2302 init_stream_lock();
2303
1898#if NUM_CORES > 1 2304#if NUM_CORES > 1
1899 flush_icache(); 2305 flush_icache();
1900#endif 2306#endif
@@ -1914,38 +2320,52 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1914 } 2320 }
1915 else 2321 else
1916 { 2322 {
1917 //DEBUGF("START: video = %d, audio = %d\n",audio_str.buffer_remaining,video_str.buffer_remaining);
1918 rb->lcd_setfont(FONT_SYSFIXED); 2323 rb->lcd_setfont(FONT_SYSFIXED);
1919 2324
1920 /* Wait until both threads have finished their work */ 2325 /* Wait until both threads have finished their work */
1921 while ((audio_str.status >= 0) || (video_str.status >= 0)) 2326 while ((audio_str.status >= 0) || (video_str.status >= 0))
1922 { 2327 {
1923 size_t audio_remaining = audio_str.buffer_tail - audio_str.buffer_head; 2328 size_t audio_remaining = audio_str.buffer_remaining;
1924 size_t video_remaining = video_str.buffer_tail - video_str.buffer_head; 2329 size_t video_remaining = video_str.buffer_remaining;
1925 2330
1926 if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) { 2331 if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK)
2332 {
1927 2333
1928 size_t bytes_to_read = buffer_size - MPEG_GUARDBUF_SIZE - 2334 size_t bytes_to_read = disk_buf_size - MPEG_GUARDBUF_SIZE -
1929 MAX(audio_remaining,video_remaining); 2335 MAX(audio_remaining,video_remaining);
1930 2336
1931 bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail)); 2337 bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail));
1932 2338
1933 while (( bytes_to_read > 0) && (file_remaining > 0) && 2339 while (( bytes_to_read > 0) && (file_remaining > 0) &&
1934 ((audio_str.status >= 0) || (video_str.status >= 0))) { 2340 ((audio_str.status != STREAM_DONE) || (video_str.status != STREAM_DONE)))
1935 size_t n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); 2341 {
2342
2343 size_t n;
2344 if ( video_sync_start != 0 )
2345 n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read));
2346 else
2347 {
2348 n = rb->read(in_file, disk_buf_tail,bytes_to_read);
2349 if (n==0)
2350 rb->splash(30,"buffer fill error");
2351 }
2352
1936 2353
1937 bytes_to_read -= n; 2354 bytes_to_read -= n;
1938 file_remaining -= n; 2355 file_remaining -= n;
1939 2356
1940 audio_str.buffer_tail += n; 2357 lock_stream();
1941 video_str.buffer_tail += n; 2358 audio_str.buffer_remaining += n;
2359 video_str.buffer_remaining += n;
2360 unlock_stream();
2361
1942 disk_buf_tail += n; 2362 disk_buf_tail += n;
1943 2363
1944 rb->yield(); 2364 rb->yield();
1945 } 2365 }
1946 2366
1947 if (disk_buf_tail == disk_buf_end) 2367 if (disk_buf_tail == disk_buf_end)
1948 disk_buf_tail = buffer; 2368 disk_buf_tail = disk_buf_start;
1949 } 2369 }
1950 2370
1951 rb->sleep(HZ/10); 2371 rb->sleep(HZ/10);
@@ -1968,6 +2388,8 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1968 invalidate_icache(); 2388 invalidate_icache();
1969#endif 2389#endif
1970 2390
2391 vo_cleanup();
2392
1971#ifndef HAVE_LCD_COLOR 2393#ifndef HAVE_LCD_COLOR
1972 gray_release(); 2394 gray_release();
1973#endif 2395#endif
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 @@
22 */ 22 */
23 23
24void vo_draw_frame (uint8_t * const * buf); 24void vo_draw_frame (uint8_t * const * buf);
25void vo_draw_frame_thumb (uint8_t * const * buf);
25void vo_setup (const mpeg2_sequence_t * sequence); 26void vo_setup (const mpeg2_sequence_t * sequence);
27void 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;
43void vo_draw_frame (uint8_t * const * buf) 43void vo_draw_frame (uint8_t * const * buf)
44{ 44{
45#ifdef HAVE_LCD_COLOR 45#ifdef HAVE_LCD_COLOR
46 rb->lcd_yuv_blit(buf, 46 rb->lcd_yuv_blit(buf, 0,0,image_width,
47 0,0,image_width,
48 output_x,output_y,output_width,output_height); 47 output_x,output_y,output_width,output_height);
49#else 48#else
50 gray_ub_gray_bitmap_part(buf[0],0,0,image_width, 49 gray_ub_gray_bitmap_part(buf[0],0,0,image_width,
@@ -60,10 +59,105 @@ void vo_draw_frame (uint8_t * const * buf)
60#define SCREEN_HEIGHT LCD_WIDTH 59#define SCREEN_HEIGHT LCD_WIDTH
61#endif 60#endif
62 61
62uint8_t* tmpbufa = 0;
63uint8_t* tmpbufb = 0;
64uint8_t* tmpbufc = 0;
65uint8_t* tmpbuf[3];
66
67void vo_draw_frame_thumb (uint8_t * const * buf)
68{
69 int r,c;
70
71#if LCD_WIDTH >= LCD_HEIGHT
72 for (r=0;r<image_width/2;r++)
73 for (c=0;c<image_height/2;c++)
74 *(tmpbuf[0]+c*image_width/2+r) =
75 *(buf[0]+2*c*image_width+2*r);
76
77 for (r=0;r<image_width/4;r++)
78 for (c=0;c<image_height/4;c++)
79 {
80 *(tmpbuf[1]+c*image_width/4+r) =
81 *(buf[1]+2*c*image_width/2+2*r);
82 *(tmpbuf[2]+c*image_width/4+r) =
83 *(buf[2]+2*c*image_width/2+2*r);
84 }
85#else
86 for (r=0;r<image_width/2;r++)
87 for (c=0;c<image_height/2;c++)
88 *(tmpbuf[0]+(image_width/2-1-r)*image_height/2+c) =
89 *(buf[0]+2*c*image_width+2*r);
90
91 for (r=0;r<image_width/4;r++)
92 for (c=0;c<image_height/4;c++)
93 {
94 *(tmpbuf[1]+(image_width/4-1-r)*image_height/4+c) =
95 *(buf[1]+2*c*image_width/2+2*r);
96 *(tmpbuf[2]+(image_width/4-1-r)*image_height/4+c) =
97 *(buf[2]+2*c*image_width/2+2*r);
98 }
99#endif
100
101rb->lcd_clear_display();
102rb->lcd_update();
103
104#ifdef HAVE_LCD_COLOR
105#ifdef SIMULATOR
106#if LCD_WIDTH >= LCD_HEIGHT
107 rb->lcd_yuv_blit(tmpbuf,0,0,image_width/2,
108 (LCD_WIDTH-1-image_width/2)/2,
109 LCD_HEIGHT-50-(image_height/2),
110 output_width/2,output_height/2);
111
112#else
113 rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2,
114 LCD_HEIGHT-50-(image_height/2),
115 (LCD_WIDTH-1-image_width/2)/2,
116 output_height/2,output_width/2);
117#endif
118#else
119#if LCD_WIDTH >= LCD_HEIGHT
120 rb->lcd_yuv_blit(tmpbuf,0,0,image_width/2,
121 (LCD_WIDTH-1-image_width/2)/2,
122 LCD_HEIGHT-50-(image_height/2),
123 output_width/2,output_height/2);
124#else
125 rb->lcd_yuv_blit(tmpbuf,0,0,image_height/2,
126 LCD_HEIGHT-50-(image_height/2),
127 (LCD_WIDTH-1-image_width/2)/2,
128 output_height/2,output_width/2);
129#endif
130#endif
131#else
132#if LCD_WIDTH >= LCD_HEIGHT
133 gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_width/2,
134 (LCD_WIDTH-1-image_width/2)/2,
135 LCD_HEIGHT-50-(image_height/2),
136 output_width/2,output_height/2);
137#else
138 gray_ub_gray_bitmap_part(tmpbuf[0],0,0,image_height/2,
139 LCD_HEIGHT-50-(image_height/2),
140 (LCD_WIDTH-1-image_width/2)/2,
141 output_height/2,output_width/2);
142#endif
143#endif
144}
145
63void vo_setup(const mpeg2_sequence_t * sequence) 146void vo_setup(const mpeg2_sequence_t * sequence)
64{ 147{
65 image_width=sequence->width; 148 image_width=sequence->width;
66 image_height=sequence->height; 149 image_height=sequence->height;
150
151 tmpbufa = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/2*
152 image_height/2, -2);
153 tmpbufb = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/4*
154 image_height/4, -2);
155 tmpbufc = (uint8_t*)mpeg2_malloc(sizeof(uint8_t)*image_width/4*
156 image_height/4, -2);
157 tmpbuf[0] = tmpbufa;
158 tmpbuf[1] = tmpbufb;
159 tmpbuf[2] = tmpbufc;
160
67 image_chroma_x=image_width/sequence->chroma_width; 161 image_chroma_x=image_width/sequence->chroma_width;
68 image_chroma_y=image_height/sequence->chroma_height; 162 image_chroma_y=image_height/sequence->chroma_height;
69 163
@@ -83,3 +177,13 @@ void vo_setup(const mpeg2_sequence_t * sequence)
83 output_y = (SCREEN_HEIGHT-sequence->display_height)/2; 177 output_y = (SCREEN_HEIGHT-sequence->display_height)/2;
84 } 178 }
85} 179}
180
181void vo_cleanup(void)
182{
183 if (tmpbufc)
184 mpeg2_free(tmpbufc);
185 if (tmpbufb)
186 mpeg2_free(tmpbufb);
187 if (tmpbufa)
188 mpeg2_free(tmpbufa);
189}
diff --git a/docs/CREDITS b/docs/CREDITS
index cb12c9688b..2abfcb8b46 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -339,6 +339,8 @@ David Bishop
339Hein-Pieter van Braam 339Hein-Pieter van Braam
340Przemysław Hołubowski 340Przemysław Hołubowski
341Stepan Moskovchenko 341Stepan Moskovchenko
342John S. Gwynne
343Brian J. Morey
342 344
343The libmad team 345The libmad team
344The wavpack team 346The 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)
293} 293}
294#endif /* HAVE_ADJUSTABLE_CPU_FREQ */ 294#endif /* HAVE_ADJUSTABLE_CPU_FREQ */
295 295
296int button_available( void )
297{
298 return queue_count(&button_queue);
299}
300
296long button_get(bool block) 301long button_get(bool block)
297{ 302{
298 struct event ev; 303 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 @@
27extern struct event_queue button_queue; 27extern struct event_queue button_queue;
28 28
29void button_init (void); 29void button_init (void);
30int button_available(void);
30long button_get (bool block); 31long button_get (bool block);
31long button_get_w_tmo(int ticks); 32long button_get_w_tmo(int ticks);
32intptr_t button_get_data(void); 33intptr_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)
736 736
737/* Again copied from real button.c... */ 737/* Again copied from real button.c... */
738 738
739int button_available( void )
740{
741 return queue_count(&button_queue);
742}
743
739long button_get(bool block) 744long button_get(bool block)
740{ 745{
741 struct event ev; 746 struct event ev;