2 #include "lib/configfile.h"
3 #include "lib/oldmenuapi.h"
5 #include "mpegplayer.h"
6 #include "mpeg_settings.h"
8 struct mpeg_settings settings
;
10 #define SETTINGS_VERSION 2
11 #define SETTINGS_MIN_VERSION 1
12 #define SETTINGS_FILENAME "mpegplayer.cfg"
14 #define THUMB_DELAY (75*HZ/100)
16 /* button definitions */
17 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
18 (CONFIG_KEYPAD == IRIVER_H300_PAD)
19 #define MPEG_SELECT BUTTON_ON
20 #define MPEG_RIGHT BUTTON_RIGHT
21 #define MPEG_LEFT BUTTON_LEFT
22 #define MPEG_UP BUTTON_UP
23 #define MPEG_DOWN BUTTON_DOWN
24 #define MPEG_EXIT BUTTON_OFF
26 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
27 #define MPEG_SELECT BUTTON_PLAY
28 #define MPEG_RIGHT BUTTON_RIGHT
29 #define MPEG_LEFT BUTTON_LEFT
30 #define MPEG_UP BUTTON_UP
31 #define MPEG_DOWN BUTTON_DOWN
32 #define MPEG_EXIT BUTTON_POWER
34 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
35 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
36 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
37 #define MPEG_SELECT BUTTON_SELECT
38 #define MPEG_RIGHT BUTTON_RIGHT
39 #define MPEG_LEFT BUTTON_LEFT
40 #define MPEG_UP BUTTON_SCROLL_FWD
41 #define MPEG_DOWN BUTTON_SCROLL_BACK
42 #define MPEG_EXIT BUTTON_MENU
44 #elif CONFIG_KEYPAD == GIGABEAT_PAD
45 #define MPEG_SELECT BUTTON_SELECT
46 #define MPEG_LEFT BUTTON_LEFT
47 #define MPEG_RIGHT BUTTON_RIGHT
48 #define MPEG_UP BUTTON_UP
49 #define MPEG_DOWN BUTTON_DOWN
50 #define MPEG_SCROLL_DOWN BUTTON_VOL_DOWN
51 #define MPEG_SCROLL_UP BUTTON_VOL_UP
52 #define MPEG_EXIT BUTTON_POWER
54 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
55 #define MPEG_SELECT BUTTON_PLAY
56 #define MPEG_LEFT BUTTON_LEFT
57 #define MPEG_RIGHT BUTTON_RIGHT
58 #define MPEG_UP BUTTON_SCROLL_UP
59 #define MPEG_DOWN BUTTON_SCROLL_DOWN
60 #define MPEG_EXIT BUTTON_POWER
62 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
63 #define MPEG_SELECT BUTTON_SELECT
64 #define MPEG_SCROLL_UP BUTTON_SCROLL_UP
65 #define MPEG_SCROLL_DOWN BUTTON_SCROLL_DOWN
66 #define MPEG_LEFT BUTTON_LEFT
67 #define MPEG_RIGHT BUTTON_RIGHT
68 #define MPEG_UP BUTTON_UP
69 #define MPEG_DOWN BUTTON_DOWN
70 #define MPEG_EXIT BUTTON_POWER
72 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
73 #define MPEG_SELECT BUTTON_SELECT
74 #define MPEG_SCROLL_UP BUTTON_VOL_UP
75 #define MPEG_SCROLL_DOWN BUTTON_VOL_DOWN
76 #define MPEG_LEFT BUTTON_LEFT
77 #define MPEG_RIGHT BUTTON_RIGHT
78 #define MPEG_UP BUTTON_UP
79 #define MPEG_DOWN BUTTON_DOWN
80 #define MPEG_EXIT BUTTON_POWER
82 #elif CONFIG_KEYPAD == MROBE500_PAD
83 #define MPEG_SELECT BUTTON_RC_HEART
84 #define MPEG_SCROLL_UP BUTTON_RC_VOL_UP
85 #define MPEG_SCROLL_DOWN BUTTON_RC_VOL_DOWN
86 #define MPEG_LEFT BUTTON_LEFT
87 #define MPEG_RIGHT BUTTON_RIGHT
88 #define MPEG_UP BUTTON_RC_PLAY
89 #define MPEG_DOWN BUTTON_RC_DOWN
90 #define MPEG_EXIT BUTTON_POWER
93 #error MPEGPLAYER: Unsupported keypad
96 static struct configdata config
[] =
98 {TYPE_INT
, 0, 2, &settings
.showfps
, "Show FPS", NULL
, NULL
},
99 {TYPE_INT
, 0, 2, &settings
.limitfps
, "Limit FPS", NULL
, NULL
},
100 {TYPE_INT
, 0, 2, &settings
.skipframes
, "Skip frames", NULL
, NULL
},
101 {TYPE_INT
, 0, INT_MAX
, &settings
.resume_count
, "Resume count",
103 {TYPE_INT
, 0, 2, &settings
.enable_start_menu
, "Enable start menu",
105 #if defined(TOSHIBA_GIGABEAT_F) || defined(SANSA_E200) || defined(SANSA_C200)
106 {TYPE_INT
, 0, INT_MAX
, &settings
.displayoptions
, "Display options",
111 static const struct opt_items noyes
[2] = {
116 static const struct opt_items enabledisable
[2] = {
121 static void display_options(void)
125 int options_quit
= 0;
127 static const struct menu_item items
[] = {
128 #if MPEG_OPTION_DITHERING_ENABLED
129 [MPEG_OPTION_DITHERING
] =
130 { "Dithering", NULL
},
132 [MPEG_OPTION_DISPLAY_FPS
] =
133 { "Display FPS", NULL
},
134 [MPEG_OPTION_LIMIT_FPS
] =
135 { "Limit FPS", NULL
},
136 [MPEG_OPTION_SKIP_FRAMES
] =
137 { "Skip frames", NULL
},
140 menu_id
= menu_init(rb
, items
, ARRAYLEN(items
),
141 NULL
, NULL
, NULL
, NULL
);
143 rb
->button_clear_queue();
145 while(options_quit
== 0)
147 result
= menu_show(menu_id
);
151 #if MPEG_OPTION_DITHERING_ENABLED
152 case MPEG_OPTION_DITHERING
:
153 result
= (settings
.displayoptions
& LCD_YUV_DITHER
) ? 1 : 0;
154 rb
->set_option("Dithering", &result
, INT
, noyes
, 2, NULL
);
155 settings
.displayoptions
= (settings
.displayoptions
& ~LCD_YUV_DITHER
)
156 | ((result
!= 0) ? LCD_YUV_DITHER
: 0);
157 rb
->lcd_yuv_set_options(settings
.displayoptions
);
159 #endif /* MPEG_OPTION_DITHERING_ENABLED */
160 case MPEG_OPTION_DISPLAY_FPS
:
161 rb
->set_option("Display FPS",&settings
.showfps
,INT
,
164 case MPEG_OPTION_LIMIT_FPS
:
165 rb
->set_option("Limit FPS",&settings
.limitfps
,INT
,
168 case MPEG_OPTION_SKIP_FRAMES
:
169 rb
->set_option("Skip frames",&settings
.skipframes
,INT
,
181 static void show_loading(struct vo_rect
*rc
)
184 #ifndef HAVE_LCD_COLOR
185 stream_gray_show(false);
187 oldmode
= rb
->lcd_get_drawmode();
188 rb
->lcd_set_drawmode(DRMODE_BG
| DRMODE_INVERSEVID
);
189 rb
->lcd_fillrect(rc
->l
-1, rc
->t
-1, rc
->r
- rc
->l
+ 2, rc
->b
- rc
->t
+ 2);
190 rb
->lcd_set_drawmode(oldmode
);
191 rb
->splash(0, "Loading...");
194 void draw_slider(uint32_t range
, uint32_t pos
, struct vo_rect
*rc
)
196 #define SLIDER_WIDTH (LCD_WIDTH-SLIDER_LMARGIN-SLIDER_RMARGIN)
197 #define SLIDER_X SLIDER_LMARGIN
198 #define SLIDER_Y (LCD_HEIGHT-SLIDER_HEIGHT-SLIDER_BMARGIN)
199 #define SLIDER_HEIGHT 8
200 #define SLIDER_TEXTMARGIN 1
201 #define SLIDER_LMARGIN 1
202 #define SLIDER_RMARGIN 1
203 #define SLIDER_TMARGIN 1
204 #define SLIDER_BMARGIN 1
205 #define SCREEN_MARGIN 1
209 int text_w
, text_h
, text_y
;
211 /* Put positition on left */
212 ts_to_hms(pos
, &hms
);
213 hms_format(str
, sizeof(str
), &hms
);
214 rb
->lcd_getstringsize(str
, NULL
, &text_h
);
215 text_y
= SLIDER_Y
- SLIDER_TEXTMARGIN
- text_h
;
219 int oldmode
= rb
->lcd_get_drawmode();
220 rb
->lcd_set_drawmode(DRMODE_BG
| DRMODE_INVERSEVID
);
221 rb
->lcd_fillrect(SLIDER_X
, text_y
, SLIDER_WIDTH
,
222 LCD_HEIGHT
- SLIDER_BMARGIN
- text_y
224 rb
->lcd_set_drawmode(oldmode
);
226 rb
->lcd_putsxy(SLIDER_X
, text_y
, str
);
228 /* Put duration on right */
229 ts_to_hms(range
, &hms
);
230 hms_format(str
, sizeof(str
), &hms
);
231 rb
->lcd_getstringsize(str
, &text_w
, NULL
);
233 rb
->lcd_putsxy(SLIDER_X
+ SLIDER_WIDTH
- text_w
, text_y
, str
);
236 rb
->lcd_drawrect(SLIDER_X
, SLIDER_Y
, SLIDER_WIDTH
, SLIDER_HEIGHT
);
237 rb
->lcd_fillrect(SLIDER_X
, SLIDER_Y
,
238 muldiv_uint32(pos
, SLIDER_WIDTH
, range
),
242 rb
->lcd_update_rect(SLIDER_X
, text_y
- SLIDER_TMARGIN
, SLIDER_WIDTH
,
243 LCD_HEIGHT
- SLIDER_BMARGIN
- text_y
+ SLIDER_TEXTMARGIN
);
247 /* Just return slider rectangle */
249 rc
->t
= text_y
- SLIDER_TMARGIN
;
250 rc
->r
= rc
->l
+ SLIDER_WIDTH
;
251 rc
->b
= rc
->t
+ LCD_HEIGHT
- SLIDER_BMARGIN
- text_y
;
255 bool display_thumb_image(const struct vo_rect
*rc
)
257 if (!stream_display_thumb(rc
))
259 rb
->splash(0, "Frame not available");
263 #ifdef HAVE_LCD_COLOR
264 /* Draw a raised border around the frame */
265 int oldcolor
= rb
->lcd_get_foreground();
266 rb
->lcd_set_foreground(LCD_LIGHTGRAY
);
268 rb
->lcd_hline(rc
->l
-1, rc
->r
-1, rc
->t
-1);
269 rb
->lcd_vline(rc
->l
-1, rc
->t
, rc
->b
-1);
271 rb
->lcd_set_foreground(LCD_DARKGRAY
);
273 rb
->lcd_hline(rc
->l
-1, rc
->r
, rc
->b
);
274 rb
->lcd_vline(rc
->r
, rc
->t
-1, rc
->b
);
276 rb
->lcd_set_foreground(oldcolor
);
278 rb
->lcd_update_rect(rc
->l
-1, rc
->t
-1, rc
->r
- rc
->l
+ 2, 1);
279 rb
->lcd_update_rect(rc
->l
-1, rc
->t
, 1, rc
->b
- rc
->t
);
280 rb
->lcd_update_rect(rc
->l
-1, rc
->b
, rc
->r
- rc
->l
+ 2, 1);
281 rb
->lcd_update_rect(rc
->r
, rc
->t
, 1, rc
->b
- rc
->t
);
283 /* Just show the thumbnail */
284 stream_gray_show(true);
290 /* Add an amount to the specified time - with saturation */
291 uint32_t increment_time(uint32_t val
, int32_t amount
, uint32_t range
)
295 uint32_t off
= -amount
;
296 if (range
> off
&& val
>= off
)
303 uint32_t off
= amount
;
304 if (range
> off
&& val
<= range
- off
)
313 int get_start_time(uint32_t duration
)
316 int tmo
= TIMEOUT_NOBLOCK
;
317 uint32_t resume_time
= settings
.resume_time
;
318 struct vo_rect rc_vid
, rc_bound
;
319 uint32_t aspect_vid
, aspect_bound
;
321 enum state_enum slider_state
= state0
;
323 rb
->lcd_clear_display();
326 draw_slider(0, 100, &rc_bound
);
327 rc_bound
.b
= rc_bound
.t
- SLIDER_TMARGIN
;
328 #ifdef HAVE_LCD_COLOR
329 rc_bound
.t
= SCREEN_MARGIN
;
333 rc_bound
.r
= LCD_WIDTH
;
336 DEBUGF("rc_bound: %d, %d, %d, %d\n", rc_bound
.l
, rc_bound
.t
,
337 rc_bound
.r
, rc_bound
.b
);
339 rc_vid
.l
= rc_vid
.t
= 0;
340 if (!stream_vo_get_size((struct vo_ext
*)&rc_vid
.r
))
342 /* Can't get size - fill whole thing */
343 rc_vid
.r
= rc_bound
.r
- rc_bound
.l
;
344 rc_vid
.b
= rc_bound
.b
- rc_bound
.t
;
347 #if !defined (HAVE_LCD_COLOR)
348 #if LCD_PIXELFORMAT == VERTICAL_PACKING
349 rc_bound
.b
&= ~7; /* Align bottom edge */
353 /* Get aspect ratio of bounding rectangle and video in u16.16 */
354 aspect_bound
= ((rc_bound
.r
- rc_bound
.l
) << 16) /
355 (rc_bound
.b
- rc_bound
.t
);
357 DEBUGF("aspect_bound: %ld.%02ld\n", aspect_bound
>> 16,
358 100*(aspect_bound
& 0xffff) >> 16);
360 aspect_vid
= (rc_vid
.r
<< 16) / rc_vid
.b
;
362 DEBUGF("aspect_vid: %ld.%02ld\n", aspect_vid
>> 16,
363 100*(aspect_vid
& 0xffff) >> 16);
365 if (aspect_vid
>= aspect_bound
)
367 /* Video proportionally wider than or same as bounding rectangle */
368 if (rc_vid
.r
> rc_bound
.r
- rc_bound
.l
)
370 rc_vid
.r
= rc_bound
.r
- rc_bound
.l
;
371 rc_vid
.b
= (rc_vid
.r
<< 16) / aspect_vid
;
373 /* else already fits */
377 /* Video proportionally narrower than bounding rectangle */
378 if (rc_vid
.b
> rc_bound
.b
- rc_bound
.t
)
380 rc_vid
.b
= rc_bound
.b
- rc_bound
.t
;
381 rc_vid
.r
= (aspect_vid
* rc_vid
.b
) >> 16;
383 /* else already fits */
386 /* Even width and height >= 2 */
387 rc_vid
.r
= (rc_vid
.r
< 2) ? 2 : (rc_vid
.r
& ~1);
388 rc_vid
.b
= (rc_vid
.b
< 2) ? 2 : (rc_vid
.b
& ~1);
390 /* Center display in bounding rectangle */
391 rc_vid
.l
= ((rc_bound
.l
+ rc_bound
.r
) - rc_vid
.r
) / 2;
392 rc_vid
.r
+= rc_vid
.l
;
394 rc_vid
.t
= ((rc_bound
.t
+ rc_bound
.b
) - rc_vid
.b
) / 2;
395 rc_vid
.b
+= rc_vid
.t
;
397 DEBUGF("rc_vid: %d, %d, %d, %d\n", rc_vid
.l
, rc_vid
.t
,
400 #ifndef HAVE_LCD_COLOR
401 /* Set gray overlay to the bounding rectangle */
402 stream_set_gray_rect(&rc_bound
);
405 while(slider_state
< state9
)
407 button
= tmo
== TIMEOUT_BLOCK
?
408 rb
->button_get(true) : rb
->button_get_w_tmo(tmo
);
415 /* Coarse (1 minute) control */
417 case MPEG_DOWN
| BUTTON_REPEAT
:
418 resume_time
= increment_time(resume_time
, -60*TS_SECOND
, duration
);
419 slider_state
= state0
;
423 case MPEG_UP
| BUTTON_REPEAT
:
424 resume_time
= increment_time(resume_time
, 60*TS_SECOND
, duration
);
425 slider_state
= state0
;
428 /* Fine (1 second) control */
430 case MPEG_LEFT
| BUTTON_REPEAT
:
431 #ifdef MPEG_SCROLL_UP
433 case MPEG_SCROLL_UP
| BUTTON_REPEAT
:
435 resume_time
= increment_time(resume_time
, -TS_SECOND
, duration
);
436 slider_state
= state0
;
440 case MPEG_RIGHT
| BUTTON_REPEAT
:
441 #ifdef MPEG_SCROLL_DOWN
442 case MPEG_SCROLL_DOWN
:
443 case MPEG_SCROLL_DOWN
| BUTTON_REPEAT
:
445 resume_time
= increment_time(resume_time
, TS_SECOND
, duration
);
446 slider_state
= state0
;
450 settings
.resume_time
= resume_time
;
452 slider_state
= state9
;
455 case SYS_USB_CONNECTED
:
456 slider_state
= state9
;
457 #ifndef HAVE_LCD_COLOR
458 stream_gray_show(false);
462 rb
->default_event_handler(button
);
471 stream_seek(resume_time
, SEEK_SET
);
472 show_loading(&rc_bound
);
473 draw_slider(duration
, resume_time
, NULL
);
474 slider_state
= state1
;
478 display_thumb_image(&rc_vid
);
479 slider_state
= state2
;
489 #ifndef HAVE_LCD_COLOR
490 /* Restore gray overlay dimensions */
491 stream_gray_show(false);
492 rc_bound
.b
= LCD_HEIGHT
;
493 stream_set_gray_rect(&rc_bound
);
501 enum mpeg_start_id
mpeg_start_menu(uint32_t duration
)
507 /* add the resume time to the menu display */
512 ts_to_hms(settings
.resume_time
, &hms
);
513 hms_format(hms_str
, sizeof(hms_str
), &hms
);
515 if (settings
.enable_start_menu
== 0)
517 rb
->snprintf(resume_str
, sizeof(resume_str
), "Yes: %s", hms_str
);
519 struct opt_items resume_no_yes
[2] =
524 if (settings
.resume_time
== 0)
525 return MPEG_START_RESTART
;
527 rb
->set_option("Resume", &result
, INT
,
528 resume_no_yes
, 2, NULL
);
532 settings
.resume_time
= 0;
533 return MPEG_START_RESTART
;
536 return MPEG_START_RESUME
;
539 rb
->snprintf(resume_str
, sizeof(resume_str
), "Resume at: %s", hms_str
);
541 struct menu_item items
[] =
543 [MPEG_START_RESTART
] =
544 { "Play from beginning", NULL
},
545 [MPEG_START_RESUME
] =
546 { resume_str
, NULL
},
548 { "Set start time", NULL
},
550 { "Quit mpegplayer", NULL
},
554 menu_id
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
),
555 NULL
, NULL
, NULL
, NULL
);
557 rb
->button_clear_queue();
559 while(menu_quit
== 0)
561 result
= menu_show(menu_id
);
565 case MPEG_START_RESTART
:
566 settings
.resume_time
= 0;
569 case MPEG_START_RESUME
:
572 case MPEG_START_SEEK
:
574 if (!stream_can_seek())
576 rb
->splash(HZ
, "Unavailable");
580 bool vis
= stream_show_vo(false);
581 if (get_start_time(duration
) == MPEG_SELECT
)
586 case MPEG_START_QUIT
:
590 result
= MPEG_START_QUIT
;
598 rb
->lcd_clear_display();
604 void clear_resume_count(void)
606 configfile_save(SETTINGS_FILENAME
, config
, ARRAYLEN(config
),
609 settings
.resume_count
= 0;
611 /* add this place holder so the count is above resume entries */
612 configfile_update_entry(SETTINGS_FILENAME
, "Resume count", 0);
615 enum mpeg_menu_id
mpeg_menu(void)
621 /* add the clear resume option to the menu display */
623 rb
->snprintf(clear_str
, sizeof(clear_str
),
624 "Clear all resumes: %u", settings
.resume_count
);
626 struct menu_item items
[] = {
627 [MPEG_MENU_DISPLAY_SETTINGS
] =
628 { "Display Options", NULL
},
629 [MPEG_MENU_ENABLE_START_MENU
] =
630 { "Start menu", NULL
},
631 [MPEG_MENU_CLEAR_RESUMES
] =
634 { "Quit mpegplayer", NULL
},
637 menu_id
= menu_init(rb
, items
, ARRAYLEN(items
),
638 NULL
, NULL
, NULL
, NULL
);
640 rb
->button_clear_queue();
642 while (menu_quit
== 0)
644 result
=menu_show(menu_id
);
648 case MPEG_MENU_DISPLAY_SETTINGS
:
651 case MPEG_MENU_ENABLE_START_MENU
:
652 rb
->set_option("Start menu",
653 &settings
.enable_start_menu
,
654 INT
, enabledisable
, 2, NULL
);
656 case MPEG_MENU_CLEAR_RESUMES
:
657 clear_resume_count();
658 rb
->snprintf(clear_str
, sizeof(clear_str
),
659 "Clear all resumes: %u", 0);
664 if (result
== MENU_ATTACHED_USB
)
665 result
= MPEG_MENU_QUIT
;
672 rb
->lcd_clear_display();
678 void init_settings(const char* filename
)
680 /* Set the default settings */
681 settings
.showfps
= 0; /* Do not show FPS */
682 settings
.limitfps
= 1; /* Limit FPS */
683 settings
.skipframes
= 1; /* Skip frames */
684 settings
.enable_start_menu
= 1; /* Enable start menu */
685 settings
.resume_count
= -1;
686 #if MPEG_OPTION_DITHERING_ENABLED
687 settings
.displayoptions
= 0; /* No visual effects */
692 if (configfile_load(SETTINGS_FILENAME
, config
,
693 sizeof(config
)/sizeof(*config
),
694 SETTINGS_MIN_VERSION
) < 0)
696 /* Generate a new config file with default values */
697 configfile_save(SETTINGS_FILENAME
, config
,
698 sizeof(config
)/sizeof(*config
),
702 #if MPEG_OPTION_DITHERING_ENABLED
703 if ((settings
.displayoptions
=
704 configfile_get_value(SETTINGS_FILENAME
, "Display options")) < 0)
706 configfile_update_entry(SETTINGS_FILENAME
, "Display options",
707 (settings
.displayoptions
=0));
709 rb
->lcd_yuv_set_options(settings
.displayoptions
);
712 if (settings
.resume_count
< 0)
714 settings
.resume_count
= 0;
715 configfile_update_entry(SETTINGS_FILENAME
, "Resume count", 0);
718 rb
->snprintf(settings
.resume_filename
, MAX_PATH
, "%s", filename
);
720 /* get the resume time for the current mpeg if it exist */
721 if ((settings
.resume_time
= configfile_get_value
722 (SETTINGS_FILENAME
, filename
)) < 0)
724 settings
.resume_time
= 0;
728 void save_settings(void)
730 configfile_update_entry(SETTINGS_FILENAME
, "Show FPS",
732 configfile_update_entry(SETTINGS_FILENAME
, "Limit FPS",
734 configfile_update_entry(SETTINGS_FILENAME
, "Skip frames",
735 settings
.skipframes
);
736 configfile_update_entry(SETTINGS_FILENAME
, "Enable start menu",
737 settings
.enable_start_menu
);
739 /* If this was a new resume entry then update the total resume count */
740 if (configfile_update_entry(SETTINGS_FILENAME
, settings
.resume_filename
,
741 settings
.resume_time
) == 0)
743 configfile_update_entry(SETTINGS_FILENAME
, "Resume count",
744 ++settings
.resume_count
);
747 #if MPEG_OPTION_DITHERING_ENABLED
748 configfile_update_entry(SETTINGS_FILENAME
, "Display options",
749 settings
.displayoptions
);