FS#9281 Rename of splash functions.
[kugel-rb.git] / apps / plugins / mpegplayer / mpegplayer.c
blobc58a2f6d0f088ed6afd0abe3686992eb87e4e65c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * mpegplayer main entrypoint and UI implementation
12 * Copyright (c) 2007 Michael Sevakis
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
24 /****************************************************************************
25 * NOTES:
27 * mpegplayer is structured as follows:
29 * +-->Video Thread-->Video Output-->LCD
30 * |
31 * UI-->Stream Manager-->+-->Audio Thread-->PCM buffer--Audio Device
32 * | | | | (ref. clock)
33 * | | +-->Buffer Thread |
34 * Stream Data | | (clock intf./
35 * Requests | File Cache drift adj.)
36 * | Disk I/O
37 * Stream services
38 * (timing, etc.)
40 * Thread list:
41 * 1) The main thread - Handles user input, settings, basic playback control
42 * and USB connect.
44 * 2) Stream Manager thread - Handles playback state, events from streams
45 * such as when a stream is finished, stream commands, PCM state. The
46 * layer in which this thread run also handles arbitration of data
47 * requests between the streams and the disk buffer. The actual specific
48 * transport layer code may get moved out to support multiple container
49 * formats.
51 * 3) Buffer thread - Buffers data in the background, generates notifications
52 * to streams when their data has been buffered, and watches streams'
53 * progress to keep data available during playback. Handles synchronous
54 * random access requests when the file cache is missed.
56 * 4) Video thread (running on the COP for PortalPlayer targets) - Decodes
57 * the video stream and renders video frames to the LCD. Handles
58 * miscellaneous video tasks like frame and thumbnail printing.
60 * 5) Audio thread (running on the main CPU to maintain consistency with the
61 * audio FIQ hander on PP) - Decodes audio frames and places them into
62 * the PCM buffer for rendering by the audio device.
64 * Streams are neither aware of one another nor care about one another. All
65 * streams shall have their own thread (unless it is _really_ efficient to
66 * have a single thread handle a couple minor streams). All coordination of
67 * the streams is done through the stream manager. The clocking is controlled
68 * by and exposed by the stream manager to other streams and implemented at
69 * the PCM level.
71 * Notes about MPEG files:
73 * MPEG System Clock is 27MHz - i.e. 27000000 ticks/second.
75 * FPS is represented in terms of a frame period - this is always an
76 * integer number of 27MHz ticks.
78 * e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of
79 * 900900 27MHz ticks.
81 * In libmpeg2, info->sequence->frame_period contains the frame_period.
83 * Working with Rockbox's 100Hz tick, the common frame rates would need
84 * to be as follows (1):
86 * FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz
87 * --------|-----------------------------------------------------------
88 * 10* | 2700000 | 10 | 4410 | 4800
89 * 12* | 2250000 | 8.3333 | 3675 | 4000
90 * 15* | 1800000 | 6.6667 | 2940 | 3200
91 * 23.9760 | 1126125 | 4.170833333 | 1839.3375 | 2002
92 * 24 | 1125000 | 4.166667 | 1837.5 | 2000
93 * 25 | 1080000 | 4 | 1764 | 1920
94 * 29.9700 | 900900 | 3.336667 | 1471,47 | 1601.6
95 * 30 | 900000 | 3.333333 | 1470 | 1600
97 * *Unofficial framerates
99 * (1) But we don't really care since the audio clock is used anyway and has
100 * very fine resolution ;-)
101 *****************************************************************************/
102 #include "plugin.h"
103 #include "mpegplayer.h"
104 #include "helper.h"
105 #include "mpeg_settings.h"
106 #include "mpeg2.h"
107 #include "video_out.h"
108 #include "stream_thread.h"
109 #include "stream_mgr.h"
111 PLUGIN_HEADER
112 PLUGIN_IRAM_DECLARE
114 /* button definitions */
115 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
116 #define MPEG_MENU BUTTON_MODE
117 #define MPEG_STOP BUTTON_OFF
118 #define MPEG_PAUSE BUTTON_ON
119 #define MPEG_VOLDOWN BUTTON_DOWN
120 #define MPEG_VOLUP BUTTON_UP
121 #define MPEG_RW BUTTON_LEFT
122 #define MPEG_FF BUTTON_RIGHT
124 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
125 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
126 #define MPEG_MENU BUTTON_MENU
127 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
128 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
129 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
130 #define MPEG_VOLUP BUTTON_SCROLL_FWD
131 #define MPEG_RW BUTTON_LEFT
132 #define MPEG_FF BUTTON_RIGHT
134 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
135 #define MPEG_MENU (BUTTON_REC | BUTTON_REL)
136 #define MPEG_STOP BUTTON_POWER
137 #define MPEG_PAUSE BUTTON_PLAY
138 #define MPEG_VOLDOWN BUTTON_DOWN
139 #define MPEG_VOLUP BUTTON_UP
140 #define MPEG_RW BUTTON_LEFT
141 #define MPEG_FF BUTTON_RIGHT
143 #elif CONFIG_KEYPAD == GIGABEAT_PAD
144 #define MPEG_MENU BUTTON_MENU
145 #define MPEG_STOP BUTTON_POWER
146 #define MPEG_PAUSE BUTTON_SELECT
147 #define MPEG_PAUSE2 BUTTON_A
148 #define MPEG_VOLDOWN BUTTON_LEFT
149 #define MPEG_VOLUP BUTTON_RIGHT
150 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
151 #define MPEG_VOLUP2 BUTTON_VOL_UP
152 #define MPEG_RW BUTTON_UP
153 #define MPEG_FF BUTTON_DOWN
154 #define MPEG_RC_MENU BUTTON_RC_DSP
155 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
156 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
157 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
158 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
159 #define MPEG_RC_RW BUTTON_RC_REW
160 #define MPEG_RC_FF BUTTON_RC_FF
162 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
163 #define MPEG_MENU BUTTON_MENU
164 #define MPEG_STOP BUTTON_POWER
165 #define MPEG_PAUSE BUTTON_SELECT
166 #define MPEG_PAUSE2 BUTTON_PLAY
167 #define MPEG_VOLDOWN BUTTON_LEFT
168 #define MPEG_VOLUP BUTTON_RIGHT
169 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
170 #define MPEG_VOLUP2 BUTTON_VOL_UP
171 #define MPEG_RW BUTTON_UP
172 #define MPEG_FF BUTTON_DOWN
174 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
175 #define MPEG_MENU BUTTON_LEFT
176 #define MPEG_STOP BUTTON_POWER
177 #define MPEG_PAUSE BUTTON_PLAY
178 #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
179 #define MPEG_VOLUP BUTTON_SCROLL_UP
180 #define MPEG_RW BUTTON_REW
181 #define MPEG_FF BUTTON_FF
183 #elif CONFIG_KEYPAD == SANSA_E200_PAD
184 #define MPEG_MENU BUTTON_SELECT
185 #define MPEG_STOP BUTTON_POWER
186 #define MPEG_PAUSE BUTTON_UP
187 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
188 #define MPEG_VOLUP BUTTON_SCROLL_FWD
189 #define MPEG_RW BUTTON_LEFT
190 #define MPEG_FF BUTTON_RIGHT
192 #elif CONFIG_KEYPAD == SANSA_C200_PAD
193 #define MPEG_MENU BUTTON_SELECT
194 #define MPEG_STOP BUTTON_POWER
195 #define MPEG_PAUSE BUTTON_UP
196 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
197 #define MPEG_VOLUP BUTTON_VOL_UP
198 #define MPEG_RW BUTTON_LEFT
199 #define MPEG_FF BUTTON_RIGHT
201 #elif CONFIG_KEYPAD == MROBE500_PAD
202 #define MPEG_MENU BUTTON_RC_HEART
203 #define MPEG_STOP BUTTON_POWER
204 #define MPEG_PAUSE BUTTON_TOUCHPAD
205 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
206 #define MPEG_VOLUP BUTTON_RC_VOL_UP
207 #define MPEG_RW BUTTON_RC_REW
208 #define MPEG_FF BUTTON_RC_FF
210 #elif CONFIG_KEYPAD == MROBE100_PAD
211 #define MPEG_MENU BUTTON_MENU
212 #define MPEG_STOP BUTTON_POWER
213 #define MPEG_PAUSE BUTTON_PLAY
214 #define MPEG_VOLDOWN BUTTON_DOWN
215 #define MPEG_VOLUP BUTTON_UP
216 #define MPEG_RW BUTTON_LEFT
217 #define MPEG_FF BUTTON_RIGHT
219 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
220 #define MPEG_MENU BUTTON_RC_MENU
221 #define MPEG_STOP BUTTON_RC_REC
222 #define MPEG_PAUSE BUTTON_RC_PLAY
223 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
224 #define MPEG_VOLUP BUTTON_RC_VOL_UP
225 #define MPEG_RW BUTTON_RC_REW
226 #define MPEG_FF BUTTON_RC_FF
228 #elif CONFIG_KEYPAD == COWOND2_PAD
229 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
230 //#define MPEG_STOP BUTTON_POWER
231 #define MPEG_VOLDOWN BUTTON_MINUS
232 #define MPEG_VOLUP BUTTON_PLUS
234 #else
235 #error No keymap defined!
236 #endif
238 #ifdef HAVE_TOUCHPAD
239 #ifndef MPEG_MENU
240 #define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
241 #endif
242 #ifndef MPEG_STOP
243 #define MPEG_STOP BUTTON_TOPLEFT
244 #endif
245 #ifndef MPEG_PAUSE
246 #define MPEG_PAUSE BUTTON_CENTER
247 #endif
248 #ifndef MPEG_VOLDOWN
249 #define MPEG_VOLDOWN BUTTON_TOPMIDDLE
250 #endif
251 #ifndef MPEG_VOLUP
252 #define MPEG_VOLUP BUTTON_BOTTOMMIDDLE
253 #endif
254 #ifndef MPEG_RW
255 #define MPEG_RW BUTTON_MIDLEFT
256 #endif
257 #ifndef MPEG_FF
258 #define MPEG_FF BUTTON_MIDRIGHT
259 #endif
260 #endif
262 const struct plugin_api* rb;
264 CACHE_FUNCTION_WRAPPERS(rb);
265 ALIGN_BUFFER_WRAPPER(rb);
267 /* One thing we can do here for targets with remotes is having a display
268 * always on the remote instead of always forcing a popup on the main display */
270 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
271 /* 3% of 30min file == 54s step size */
272 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
273 #define WVS_MIN_UPDATE_INTERVAL (HZ/2)
275 /* WVS status - same order as icon array */
276 enum wvs_status_enum
278 WVS_STATUS_STOPPED = 0,
279 WVS_STATUS_PAUSED,
280 WVS_STATUS_PLAYING,
281 WVS_STATUS_FF,
282 WVS_STATUS_RW,
283 WVS_STATUS_COUNT,
284 WVS_STATUS_MASK = 0x7
287 enum wvs_bits
289 WVS_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
290 /* Refresh the... */
291 WVS_REFRESH_VOLUME = 0x0001, /* ...volume display */
292 WVS_REFRESH_TIME = 0x0002, /* ...time display+progress */
293 WVS_REFRESH_STATUS = 0x0004, /* ...playback status icon */
294 WVS_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
295 WVS_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
296 WVS_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
297 WVS_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
298 WVS_SHOW = 0x4000, /* OR bitflag - show the WVS */
299 WVS_HP_PAUSE = 0x2000,
300 WVS_HIDE = 0x0000, /* hide the WVS (aid readability) */
301 WVS_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
304 /* Status icons selected according to font height */
305 extern const unsigned char mpegplayer_status_icons_8x8x1[];
306 extern const unsigned char mpegplayer_status_icons_12x12x1[];
307 extern const unsigned char mpegplayer_status_icons_16x16x1[];
309 /* Main border areas that contain WVS elements */
310 #define WVS_BDR_L 2
311 #define WVS_BDR_T 2
312 #define WVS_BDR_R 2
313 #define WVS_BDR_B 2
315 struct wvs
317 long hide_tick;
318 long show_for;
319 long print_tick;
320 long print_delay;
321 long resume_tick;
322 long resume_delay;
323 long next_auto_refresh;
324 int x;
325 int y;
326 int width;
327 int height;
328 unsigned fgcolor;
329 unsigned bgcolor;
330 unsigned prog_fillcolor;
331 struct vo_rect update_rect;
332 struct vo_rect prog_rect;
333 struct vo_rect time_rect;
334 struct vo_rect dur_rect;
335 struct vo_rect vol_rect;
336 const unsigned char *icons;
337 struct vo_rect stat_rect;
338 int status;
339 uint32_t curr_time;
340 unsigned auto_refresh;
341 unsigned flags;
344 static struct wvs wvs;
346 static void wvs_show(unsigned show);
348 #ifdef LCD_LANDSCAPE
349 #define _X (x + wvs.x)
350 #define _Y (y + wvs.y)
351 #define _W width
352 #define _H height
353 #else
354 #define _X (LCD_WIDTH - (y + wvs.y) - height)
355 #define _Y (x + wvs.x)
356 #define _W height
357 #define _H width
358 #endif
360 #ifdef HAVE_LCD_COLOR
361 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
362 static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
364 int r1 = RGB_UNPACK_RED(c1);
365 int g1 = RGB_UNPACK_GREEN(c1);
366 int b1 = RGB_UNPACK_BLUE(c1);
368 int r2 = RGB_UNPACK_RED(c2);
369 int g2 = RGB_UNPACK_GREEN(c2);
370 int b2 = RGB_UNPACK_BLUE(c2);
372 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
373 amount*(g2 - g1) / 255 + g1,
374 amount*(b2 - b1) / 255 + b1);
376 #endif
378 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
379 * most are just wrappers of lcd_* functions with transforms applied.
380 * The origin is the upper-left corner of the WVS area */
381 static void draw_update_rect(int x, int y, int width, int height)
383 lcd_(update_rect)(_X, _Y, _W, _H);
386 static void draw_clear_area(int x, int y, int width, int height)
388 #ifdef HAVE_LCD_COLOR
389 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
390 #else
391 int oldmode = grey_get_drawmode();
392 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
393 grey_fillrect(_X, _Y, _W, _H);
394 grey_set_drawmode(oldmode);
395 #endif
398 static void draw_clear_area_rect(const struct vo_rect *rc)
400 int x = rc->l;
401 int y = rc->t;
402 int width = rc->r - rc->l;
403 int height = rc->b - rc->t;
404 #ifdef HAVE_LCD_COLOR
405 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
406 #else
407 int oldmode = grey_get_drawmode();
408 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
409 grey_fillrect(_X, _Y, _W, _H);
410 grey_set_drawmode(oldmode);
411 #endif
414 static void draw_fillrect(int x, int y, int width, int height)
416 lcd_(fillrect)(_X, _Y, _W, _H);
419 static void draw_hline(int x1, int x2, int y)
421 #ifdef LCD_LANDSCAPE
422 lcd_(hline)(x1 + wvs.x, x2 + wvs.x, y + wvs.y);
423 #else
424 y = LCD_WIDTH - (y + wvs.y) - 1;
425 lcd_(vline)(y, x1 + wvs.x, x2 + wvs.x);
426 #endif
429 static void draw_vline(int x, int y1, int y2)
431 #ifdef LCD_LANDSCAPE
432 lcd_(vline)(x + wvs.x, y1 + wvs.y, y2 + wvs.y);
433 #else
434 y1 = LCD_WIDTH - (y1 + wvs.y) - 1;
435 y2 = LCD_WIDTH - (y2 + wvs.y) - 1;
436 lcd_(hline)(y1, y2, x + wvs.x);
437 #endif
440 static void draw_scrollbar_draw(int x, int y, int width, int height,
441 uint32_t min, uint32_t max, uint32_t val)
443 unsigned oldfg = lcd_(get_foreground)();
445 draw_hline(x + 1, x + width - 2, y);
446 draw_hline(x + 1, x + width - 2, y + height - 1);
447 draw_vline(x, y + 1, y + height - 2);
448 draw_vline(x + width - 1, y + 1, y + height - 2);
450 val = muldiv_uint32(width - 2, val, max - min);
451 val = MIN(val, (uint32_t)(width - 2));
453 draw_fillrect(x + 1, y + 1, val, height - 2);
455 lcd_(set_foreground)(wvs.prog_fillcolor);
457 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
459 lcd_(set_foreground)(oldfg);
462 static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
463 int max, int val)
465 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
466 min, max, val);
469 #ifdef LCD_PORTRAIT
470 /* Portrait displays need rotated text rendering */
472 /* Limited function that only renders in DRMODE_FG and uses absolute screen
473 * coordinates */
474 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
475 int src_x, int src_y,
476 int stride, int x, int y,
477 int width, int height)
479 const unsigned char *src_end;
480 fb_data *dst, *dst_end;
481 unsigned fg_pattern, bg_pattern;
483 if (x + width > SCREEN_WIDTH)
484 width = SCREEN_WIDTH - x; /* Clip right */
485 if (x < 0)
486 width += x, x = 0; /* Clip left */
487 if (width <= 0)
488 return; /* nothing left to do */
490 if (y + height > SCREEN_HEIGHT)
491 height = SCREEN_HEIGHT - y; /* Clip bottom */
492 if (y < 0)
493 height += y, y = 0; /* Clip top */
494 if (height <= 0)
495 return; /* nothing left to do */
497 fg_pattern = rb->lcd_get_foreground();
498 bg_pattern = rb->lcd_get_background();
500 src += stride * (src_y >> 3) + src_x; /* move starting point */
501 src_y &= 7;
502 src_end = src + width;
504 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
507 const unsigned char *src_col = src++;
508 unsigned data = *src_col >> src_y;
509 int numbits = 8 - src_y;
511 fb_data *dst_col = dst;
512 dst_end = dst_col - height;
513 dst += LCD_WIDTH;
517 dst_col--;
519 if (data & 1)
520 *dst_col = fg_pattern;
521 #if 0
522 else
523 *dst_col = bg_pattern;
524 #endif
525 data >>= 1;
526 if (--numbits == 0) {
527 src_col += stride;
528 data = *src_col;
529 numbits = 8;
532 while (dst_col > dst_end);
534 while (src < src_end);
537 static void draw_putsxy_oriented(int x, int y, const char *str)
539 unsigned short ch;
540 unsigned short *ucs;
541 int ofs = MIN(x, 0);
542 struct font* pf = rb->font_get(FONT_UI);
544 ucs = rb->bidi_l2v(str, 1);
546 x += wvs.x;
547 y += wvs.y;
549 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
551 int width;
552 const unsigned char *bits;
554 /* get proportional width and glyph bits */
555 width = rb->font_get_width(pf, ch);
557 if (ofs > width) {
558 ofs -= width;
559 continue;
562 bits = rb->font_get_bits(pf, ch);
564 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
565 width - ofs, pf->height);
567 x += width - ofs;
568 ofs = 0;
571 #else
572 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
573 int src_x, int src_y,
574 int stride, int x, int y,
575 int width, int height)
577 int mode = lcd_(get_drawmode)();
578 lcd_(set_drawmode)(DRMODE_FG);
579 lcd_(mono_bitmap_part)(src, src_x, src_y, stride, x, y, width, height);
580 lcd_(set_drawmode)(mode);
583 static void draw_putsxy_oriented(int x, int y, const char *str)
585 int mode = lcd_(get_drawmode)();
586 lcd_(set_drawmode)(DRMODE_FG);
587 lcd_(putsxy)(x + wvs.x, y + wvs.y, str);
588 lcd_(set_drawmode)(mode);
590 #endif /* LCD_PORTRAIT */
592 #ifdef HAVE_LCD_ENABLE
593 /* So we can refresh the overlay */
594 static void wvs_lcd_enable_hook(void)
596 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
598 #endif
600 static void wvs_backlight_on_video_mode(bool video_on)
602 if (video_on) {
603 /* Turn off backlight timeout */
604 /* backlight control in lib/helper.c */
605 backlight_force_on(rb);
606 #ifdef HAVE_LCD_ENABLE
607 rb->lcd_set_enable_hook(NULL);
608 #endif
609 } else {
610 #ifdef HAVE_LCD_ENABLE
611 rb->lcd_set_enable_hook(wvs_lcd_enable_hook);
612 #endif
613 /* Revert to user's backlight settings */
614 backlight_use_settings(rb);
618 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
619 static void wvs_backlight_brightness_video_mode(bool video_on)
621 if (settings.backlight_brightness < 0)
622 return;
624 mpeg_backlight_update_brightness(
625 video_on ? settings.backlight_brightness : -1);
627 #else
628 #define wvs_backlight_brightness_video_mode(video_on)
629 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
631 static void wvs_text_init(void)
633 struct hms hms;
634 char buf[32];
635 int phys;
636 int spc_width;
638 lcd_(setfont)(FONT_UI);
640 wvs.x = 0;
641 wvs.width = SCREEN_WIDTH;
643 vo_rect_clear(&wvs.time_rect);
644 vo_rect_clear(&wvs.stat_rect);
645 vo_rect_clear(&wvs.prog_rect);
646 vo_rect_clear(&wvs.vol_rect);
648 ts_to_hms(stream_get_duration(), &hms);
649 hms_format(buf, sizeof (buf), &hms);
650 lcd_(getstringsize)(buf, &wvs.time_rect.r, &wvs.time_rect.b);
652 /* Choose well-sized bitmap images relative to font height */
653 if (wvs.time_rect.b < 12) {
654 wvs.icons = mpegplayer_status_icons_8x8x1;
655 wvs.stat_rect.r = wvs.stat_rect.b = 8;
656 } else if (wvs.time_rect.b < 16) {
657 wvs.icons = mpegplayer_status_icons_12x12x1;
658 wvs.stat_rect.r = wvs.stat_rect.b = 12;
659 } else {
660 wvs.icons = mpegplayer_status_icons_16x16x1;
661 wvs.stat_rect.r = wvs.stat_rect.b = 16;
664 if (wvs.stat_rect.b < wvs.time_rect.b) {
665 vo_rect_offset(&wvs.stat_rect, 0,
666 (wvs.time_rect.b - wvs.stat_rect.b) / 2 + WVS_BDR_T);
667 vo_rect_offset(&wvs.time_rect, WVS_BDR_L, WVS_BDR_T);
668 } else {
669 vo_rect_offset(&wvs.time_rect, WVS_BDR_L,
670 wvs.stat_rect.b - wvs.time_rect.b + WVS_BDR_T);
671 vo_rect_offset(&wvs.stat_rect, 0, WVS_BDR_T);
674 wvs.dur_rect = wvs.time_rect;
676 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
677 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
678 rb->sound_unit(SOUND_VOLUME));
680 lcd_(getstringsize)(" ", &spc_width, NULL);
681 lcd_(getstringsize)(buf, &wvs.vol_rect.r, &wvs.vol_rect.b);
683 wvs.prog_rect.r = SCREEN_WIDTH - WVS_BDR_L - spc_width -
684 wvs.vol_rect.r - WVS_BDR_R;
685 wvs.prog_rect.b = 3*wvs.stat_rect.b / 4;
686 vo_rect_offset(&wvs.prog_rect, wvs.time_rect.l,
687 wvs.time_rect.b);
689 vo_rect_offset(&wvs.stat_rect,
690 (wvs.prog_rect.r + wvs.prog_rect.l - wvs.stat_rect.r) / 2,
693 vo_rect_offset(&wvs.dur_rect,
694 wvs.prog_rect.r - wvs.dur_rect.r, 0);
696 vo_rect_offset(&wvs.vol_rect, wvs.prog_rect.r + spc_width,
697 (wvs.prog_rect.b + wvs.prog_rect.t - wvs.vol_rect.b) / 2);
699 wvs.height = WVS_BDR_T + MAX(wvs.prog_rect.b, wvs.vol_rect.b) -
700 MIN(wvs.time_rect.t, wvs.stat_rect.t) + WVS_BDR_B;
702 #ifdef HAVE_LCD_COLOR
703 wvs.height = ALIGN_UP(wvs.height, 2);
704 #endif
705 wvs.y = SCREEN_HEIGHT - wvs.height;
707 lcd_(setfont)(FONT_SYSFIXED);
710 static void wvs_init(void)
712 wvs.flags = 0;
713 wvs.show_for = HZ*4;
714 wvs.print_delay = 75*HZ/100;
715 wvs.resume_delay = HZ/2;
716 #ifdef HAVE_LCD_COLOR
717 wvs.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
718 wvs.fgcolor = LCD_WHITE;
719 wvs.prog_fillcolor = LCD_BLACK;
720 #else
721 wvs.bgcolor = GREY_LIGHTGRAY;
722 wvs.fgcolor = GREY_BLACK;
723 wvs.prog_fillcolor = GREY_WHITE;
724 #endif
725 wvs.curr_time = 0;
726 wvs.status = WVS_STATUS_STOPPED;
727 wvs.auto_refresh = WVS_REFRESH_TIME;
728 wvs.next_auto_refresh = *rb->current_tick;
729 wvs_text_init();
732 static void wvs_schedule_refresh(unsigned refresh)
734 long tick = *rb->current_tick;
736 if (refresh & WVS_REFRESH_VIDEO)
737 wvs.print_tick = tick + wvs.print_delay;
739 if (refresh & WVS_REFRESH_RESUME)
740 wvs.resume_tick = tick + wvs.resume_delay;
742 wvs.auto_refresh |= refresh;
745 static void wvs_cancel_refresh(unsigned refresh)
747 wvs.auto_refresh &= ~refresh;
750 /* Refresh the background area */
751 static void wvs_refresh_background(void)
753 char buf[32];
754 struct hms hms;
756 unsigned bg = lcd_(get_background)();
757 lcd_(set_drawmode)(DRMODE_SOLID | DRMODE_INVERSEVID);
759 #ifdef HAVE_LCD_COLOR
760 /* Draw a "raised" area for our graphics */
761 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 192));
762 draw_hline(0, wvs.width, 0);
764 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 80));
765 draw_hline(0, wvs.width, 1);
767 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 48));
768 draw_hline(0, wvs.width, wvs.height-2);
770 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 128));
771 draw_hline(0, wvs.width, wvs.height-1);
773 lcd_(set_background)(bg);
774 draw_clear_area(0, 2, wvs.width, wvs.height - 4);
775 #else
776 /* Give contrast with the main background */
777 lcd_(set_background)(GREY_WHITE);
778 draw_hline(0, wvs.width, 0);
780 lcd_(set_background)(GREY_DARKGRAY);
781 draw_hline(0, wvs.width, wvs.height-1);
783 lcd_(set_background)(bg);
784 draw_clear_area(0, 1, wvs.width, wvs.height - 2);
785 #endif
787 vo_rect_set_ext(&wvs.update_rect, 0, 0, wvs.width, wvs.height);
788 lcd_(set_drawmode)(DRMODE_SOLID);
790 if (stream_get_duration() != INVALID_TIMESTAMP) {
791 /* Draw the movie duration */
792 ts_to_hms(stream_get_duration(), &hms);
793 hms_format(buf, sizeof (buf), &hms);
794 draw_putsxy_oriented(wvs.dur_rect.l, wvs.dur_rect.t, buf);
796 /* else don't know the duration */
799 /* Refresh the current time display + the progress bar */
800 static void wvs_refresh_time(void)
802 char buf[32];
803 struct hms hms;
805 uint32_t duration = stream_get_duration();
807 draw_scrollbar_draw_rect(&wvs.prog_rect, 0, duration,
808 wvs.curr_time);
810 ts_to_hms(wvs.curr_time, &hms);
811 hms_format(buf, sizeof (buf), &hms);
813 draw_clear_area_rect(&wvs.time_rect);
814 draw_putsxy_oriented(wvs.time_rect.l, wvs.time_rect.t, buf);
816 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
817 &wvs.prog_rect);
818 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
819 &wvs.time_rect);
822 /* Refresh the volume display area */
823 static void wvs_refresh_volume(void)
825 char buf[32];
826 int width;
828 int volume = rb->global_settings->volume;
829 rb->snprintf(buf, sizeof (buf), "%d%s",
830 rb->sound_val2phys(SOUND_VOLUME, volume),
831 rb->sound_unit(SOUND_VOLUME));
832 lcd_(getstringsize)(buf, &width, NULL);
834 /* Right-justified */
835 draw_clear_area_rect(&wvs.vol_rect);
836 draw_putsxy_oriented(wvs.vol_rect.r - width, wvs.vol_rect.t, buf);
838 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.vol_rect);
841 /* Refresh the status icon */
842 static void wvs_refresh_status(void)
844 int icon_size = wvs.stat_rect.r - wvs.stat_rect.l;
846 draw_clear_area_rect(&wvs.stat_rect);
848 #ifdef HAVE_LCD_COLOR
849 /* Draw status icon with a drop shadow */
850 unsigned oldfg = lcd_(get_foreground)();
851 int i = 1;
853 lcd_(set_foreground)(draw_blendcolor(lcd_(get_background)(),
854 DRAW_BLACK, 96));
856 while (1)
858 draw_oriented_mono_bitmap_part(wvs.icons,
859 icon_size*wvs.status,
861 icon_size*WVS_STATUS_COUNT,
862 wvs.stat_rect.l + wvs.x + i,
863 wvs.stat_rect.t + wvs.y + i,
864 icon_size, icon_size);
866 if (--i < 0)
867 break;
869 lcd_(set_foreground)(oldfg);
872 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
873 #else
874 draw_oriented_mono_bitmap_part(wvs.icons,
875 icon_size*wvs.status,
877 icon_size*WVS_STATUS_COUNT,
878 wvs.stat_rect.l + wvs.x,
879 wvs.stat_rect.t + wvs.y,
880 icon_size, icon_size);
881 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
882 #endif
885 /* Update the current status which determines which icon is displayed */
886 static bool wvs_update_status(void)
888 int status;
890 switch (stream_status())
892 default:
893 status = WVS_STATUS_STOPPED;
894 break;
895 case STREAM_PAUSED:
896 /* If paused with a pending resume, coerce it to WVS_STATUS_PLAYING */
897 status = (wvs.auto_refresh & WVS_REFRESH_RESUME) ?
898 WVS_STATUS_PLAYING : WVS_STATUS_PAUSED;
899 break;
900 case STREAM_PLAYING:
901 status = WVS_STATUS_PLAYING;
902 break;
905 if (status != wvs.status) {
906 /* A refresh is needed */
907 wvs.status = status;
908 return true;
911 return false;
914 /* Update the current time that will be displayed */
915 static void wvs_update_time(void)
917 uint32_t start;
918 wvs.curr_time = stream_get_seek_time(&start);
919 wvs.curr_time -= start;
922 /* Refresh various parts of the WVS - showing it if it is hidden */
923 static void wvs_refresh(int hint)
925 long tick;
926 unsigned oldbg, oldfg;
928 tick = *rb->current_tick;
930 if (hint == WVS_REFRESH_DEFAULT) {
931 /* The default which forces no updates */
933 /* Make sure Rockbox doesn't turn off the player because of
934 too little activity */
935 if (wvs.status == WVS_STATUS_PLAYING)
936 rb->reset_poweroff_timer();
938 /* Redraw the current or possibly extract a new video frame */
939 if ((wvs.auto_refresh & WVS_REFRESH_VIDEO) &&
940 TIME_AFTER(tick, wvs.print_tick)) {
941 wvs.auto_refresh &= ~WVS_REFRESH_VIDEO;
942 stream_draw_frame(false);
945 /* Restart playback if the timout was reached */
946 if ((wvs.auto_refresh & WVS_REFRESH_RESUME) &&
947 TIME_AFTER(tick, wvs.resume_tick)) {
948 wvs.auto_refresh &= ~(WVS_REFRESH_RESUME | WVS_REFRESH_VIDEO);
949 stream_resume();
952 /* If not visible, return */
953 if (!(wvs.flags & WVS_SHOW))
954 return;
956 /* Hide if the visibility duration was reached */
957 if (TIME_AFTER(tick, wvs.hide_tick)) {
958 wvs_show(WVS_HIDE);
959 return;
961 } else {
962 /* A forced update of some region */
964 /* Show if currently invisible */
965 if (!(wvs.flags & WVS_SHOW)) {
966 /* Avoid call back into this function - it will be drawn */
967 wvs_show(WVS_SHOW | WVS_NODRAW);
968 hint = WVS_REFRESH_ALL;
971 /* Move back timeouts for frame print and hide */
972 wvs.print_tick = tick + wvs.print_delay;
973 wvs.hide_tick = tick + wvs.show_for;
976 if (TIME_AFTER(tick, wvs.next_auto_refresh)) {
977 /* Refresh whatever graphical elements are due automatically */
978 wvs.next_auto_refresh = tick + WVS_MIN_UPDATE_INTERVAL;
980 if (wvs.auto_refresh & WVS_REFRESH_STATUS) {
981 if (wvs_update_status())
982 hint |= WVS_REFRESH_STATUS;
985 if (wvs.auto_refresh & WVS_REFRESH_TIME) {
986 wvs_update_time();
987 hint |= WVS_REFRESH_TIME;
991 if (hint == 0)
992 return; /* No drawing needed */
994 /* Set basic drawing params that are used. Elements that perform variations
995 * will restore them. */
996 oldfg = lcd_(get_foreground)();
997 oldbg = lcd_(get_background)();
999 lcd_(setfont)(FONT_UI);
1000 lcd_(set_foreground)(wvs.fgcolor);
1001 lcd_(set_background)(wvs.bgcolor);
1003 vo_rect_clear(&wvs.update_rect);
1005 if (hint & WVS_REFRESH_BACKGROUND) {
1006 wvs_refresh_background();
1007 hint |= WVS_REFRESH_ALL; /* Requires a redraw of everything */
1010 if (hint & WVS_REFRESH_TIME) {
1011 wvs_refresh_time();
1014 if (hint & WVS_REFRESH_VOLUME) {
1015 wvs_refresh_volume();
1018 if (hint & WVS_REFRESH_STATUS) {
1019 wvs_refresh_status();
1022 /* Go back to defaults */
1023 lcd_(setfont)(FONT_SYSFIXED);
1024 lcd_(set_foreground)(oldfg);
1025 lcd_(set_background)(oldbg);
1027 /* Update the dirty rectangle */
1028 vo_lock();
1030 draw_update_rect(wvs.update_rect.l,
1031 wvs.update_rect.t,
1032 wvs.update_rect.r - wvs.update_rect.l,
1033 wvs.update_rect.b - wvs.update_rect.t);
1035 vo_unlock();
1038 /* Show/Hide the WVS */
1039 static void wvs_show(unsigned show)
1041 if (((show ^ wvs.flags) & WVS_SHOW) == 0)
1043 if (show & WVS_SHOW) {
1044 wvs.hide_tick = *rb->current_tick + wvs.show_for;
1046 return;
1049 if (show & WVS_SHOW) {
1050 /* Clip away the part of video that is covered */
1051 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, wvs.y };
1053 wvs.flags |= WVS_SHOW;
1055 if (wvs.status != WVS_STATUS_PLAYING) {
1056 /* Not playing - set brightness to mpegplayer setting */
1057 wvs_backlight_brightness_video_mode(true);
1060 stream_vo_set_clip(&rc);
1062 if (!(show & WVS_NODRAW))
1063 wvs_refresh(WVS_REFRESH_ALL);
1064 } else {
1065 /* Uncover clipped video area and redraw it */
1066 wvs.flags &= ~WVS_SHOW;
1068 draw_clear_area(0, 0, wvs.width, wvs.height);
1070 if (!(show & WVS_NODRAW)) {
1071 vo_lock();
1072 draw_update_rect(0, 0, wvs.width, wvs.height);
1073 vo_unlock();
1075 stream_vo_set_clip(NULL);
1076 stream_draw_frame(false);
1077 } else {
1078 stream_vo_set_clip(NULL);
1081 if (wvs.status != WVS_STATUS_PLAYING) {
1082 /* Not playing - restore backlight brightness */
1083 wvs_backlight_brightness_video_mode(false);
1088 /* Set the current status - update screen if specified */
1089 static void wvs_set_status(int status)
1091 bool draw = (status & WVS_NODRAW) == 0;
1093 status &= WVS_STATUS_MASK;
1095 if (wvs.status != status) {
1097 wvs.status = status;
1099 if (draw)
1100 wvs_refresh(WVS_REFRESH_STATUS);
1104 /* Get the current status value */
1105 static int wvs_get_status(void)
1107 return wvs.status & WVS_STATUS_MASK;
1110 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
1111 static uint32_t wvs_ff_rw(int btn, unsigned refresh)
1113 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1114 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1115 uint32_t start;
1116 uint32_t time = stream_get_seek_time(&start);
1117 const uint32_t duration = stream_get_duration();
1118 unsigned int max_step = 0;
1119 uint32_t ff_rw_count = 0;
1120 unsigned status = wvs.status;
1122 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME |
1123 WVS_REFRESH_TIME);
1125 time -= start; /* Absolute clock => stream-relative */
1127 switch (btn)
1129 case MPEG_FF:
1130 #ifdef MPEG_RC_FF
1131 case MPEG_RC_FF:
1132 #endif
1133 wvs_set_status(WVS_STATUS_FF);
1134 break;
1135 case MPEG_RW:
1136 #ifdef MPEG_RC_RW
1137 case MPEG_RC_RW:
1138 #endif
1139 wvs_set_status(WVS_STATUS_RW);
1140 break;
1141 default:
1142 btn = -1;
1145 btn |= BUTTON_REPEAT;
1147 while (1)
1149 stream_keep_disk_active();
1151 switch (btn)
1153 case BUTTON_NONE:
1154 wvs_refresh(WVS_REFRESH_DEFAULT);
1155 break;
1157 case MPEG_FF | BUTTON_REPEAT:
1158 case MPEG_RW | BUTTON_REPEAT:
1159 #ifdef MPEG_RC_FF
1160 case MPEG_RC_FF | BUTTON_REPEAT:
1161 case MPEG_RC_RW | BUTTON_REPEAT:
1162 #endif
1163 break;
1165 case MPEG_FF | BUTTON_REL:
1166 case MPEG_RW | BUTTON_REL:
1167 #ifdef MPEG_RC_FF
1168 case MPEG_RC_FF | BUTTON_REL:
1169 case MPEG_RC_RW | BUTTON_REL:
1170 #endif
1171 if (wvs.status == WVS_STATUS_FF)
1172 time += ff_rw_count;
1173 else if (wvs.status == WVS_STATUS_RW)
1174 time -= ff_rw_count;
1176 /* Fall-through */
1177 case -1:
1178 default:
1179 wvs_schedule_refresh(refresh);
1180 wvs_set_status(status);
1181 wvs_schedule_refresh(WVS_REFRESH_TIME);
1182 return time;
1185 if (wvs.status == WVS_STATUS_FF) {
1186 /* fast forwarding, calc max step relative to end */
1187 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1188 FF_REWIND_MAX_PERCENT, 100);
1189 } else {
1190 /* rewinding, calc max step relative to start */
1191 max_step = muldiv_uint32(time - ff_rw_count,
1192 FF_REWIND_MAX_PERCENT, 100);
1195 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1197 if (step > max_step)
1198 step = max_step;
1200 ff_rw_count += step;
1202 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1203 step += step >> ff_rw_accel;
1205 if (wvs.status == WVS_STATUS_FF) {
1206 if (duration - time <= ff_rw_count)
1207 ff_rw_count = duration - time;
1209 wvs.curr_time = time + ff_rw_count;
1210 } else {
1211 if (time <= ff_rw_count)
1212 ff_rw_count = time;
1214 wvs.curr_time = time - ff_rw_count;
1217 wvs_refresh(WVS_REFRESH_TIME);
1219 btn = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1223 static int wvs_status(void)
1225 int status = stream_status();
1227 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1228 if (status == STREAM_PAUSED) {
1229 if (wvs.auto_refresh & WVS_REFRESH_RESUME)
1230 status = STREAM_PLAYING;
1233 return status;
1236 /* Change the current audio volume by a specified amount */
1237 static void wvs_set_volume(int delta)
1239 int vol = rb->global_settings->volume;
1240 int limit;
1242 vol += delta;
1244 if (delta < 0) {
1245 /* Volume down - clip to lower limit */
1246 limit = rb->sound_min(SOUND_VOLUME);
1247 if (vol < limit)
1248 vol = limit;
1249 } else {
1250 /* Volume up - clip to upper limit */
1251 limit = rb->sound_max(SOUND_VOLUME);
1252 if (vol > limit)
1253 vol = limit;
1256 /* Sync the global settings */
1257 if (vol != rb->global_settings->volume) {
1258 rb->sound_set(SOUND_VOLUME, vol);
1259 rb->global_settings->volume = vol;
1262 /* Update the volume display */
1263 wvs_refresh(WVS_REFRESH_VOLUME);
1266 /* Begin playback at the specified time */
1267 static int wvs_play(uint32_t time)
1269 int retval;
1271 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1273 retval = stream_seek(time, SEEK_SET);
1275 if (retval >= STREAM_OK) {
1276 wvs_backlight_on_video_mode(true);
1277 wvs_backlight_brightness_video_mode(true);
1278 stream_show_vo(true);
1279 retval = stream_play();
1281 if (retval >= STREAM_OK)
1282 wvs_set_status(WVS_STATUS_PLAYING | WVS_NODRAW);
1285 return retval;
1288 /* Halt playback - pause engine and return logical state */
1289 static int wvs_halt(void)
1291 int status = stream_pause();
1293 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1294 if (status == STREAM_PAUSED) {
1295 if (wvs_get_status() == WVS_STATUS_PLAYING)
1296 status = STREAM_PLAYING;
1299 /* Cancel some auto refreshes - caller will restart them if desired */
1300 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1302 /* No backlight fiddling here - callers does the right thing */
1304 return status;
1307 /* Pause playback if playing */
1308 static int wvs_pause(void)
1310 unsigned refresh = wvs.auto_refresh;
1311 int status = wvs_halt();
1313 if (status == STREAM_PLAYING && (refresh & WVS_REFRESH_RESUME)) {
1314 /* Resume pending - change to a still video frame update */
1315 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1318 wvs_set_status(WVS_STATUS_PAUSED);
1320 wvs_backlight_on_video_mode(false);
1321 /* Leave brightness alone and restore it when WVS is hidden */
1323 return status;
1326 /* Resume playback if halted or paused */
1327 static void wvs_resume(void)
1329 /* Cancel video and resume auto refresh - the resyc when starting
1330 * playback will perform those tasks */
1331 wvs_backlight_on_video_mode(true);
1332 wvs_backlight_brightness_video_mode(true);
1333 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1334 wvs_set_status(WVS_STATUS_PLAYING);
1335 stream_resume();
1338 /* Stop playback - remember the resume point if not closed */
1339 static void wvs_stop(void)
1341 uint32_t resume_time;
1343 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1344 wvs_set_status(WVS_STATUS_STOPPED | WVS_NODRAW);
1345 wvs_show(WVS_HIDE | WVS_NODRAW);
1347 stream_stop();
1349 resume_time = stream_get_resume_time();
1351 if (resume_time != INVALID_TIMESTAMP)
1352 settings.resume_time = resume_time;
1354 wvs_backlight_on_video_mode(false);
1355 wvs_backlight_brightness_video_mode(false);
1358 /* Perform a seek if seeking is possible for this stream - if playing, a delay
1359 * will be inserted before restarting in case the user decides to seek again */
1360 static void wvs_seek(int btn)
1362 int status;
1363 unsigned refresh;
1364 uint32_t time;
1366 if (!stream_can_seek())
1367 return;
1369 /* Halt playback - not strictly nescessary but nice */
1370 status = wvs_halt();
1372 if (status == STREAM_STOPPED)
1373 return;
1375 wvs_show(WVS_SHOW);
1377 if (status == STREAM_PLAYING)
1378 refresh = WVS_REFRESH_RESUME; /* delay resume if playing */
1379 else
1380 refresh = WVS_REFRESH_VIDEO; /* refresh if paused */
1382 /* Obtain a new playback point */
1383 time = wvs_ff_rw(btn, refresh);
1385 /* Tell engine to resume at that time */
1386 stream_seek(time, SEEK_SET);
1389 #ifdef HAVE_HEADPHONE_DETECTION
1390 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1391 static void wvs_handle_phone_plug(bool inserted)
1393 if (rb->global_settings->unplug_mode == 0)
1394 return;
1396 /* Wait for any incomplete state transition to complete first */
1397 stream_wait_status();
1399 int status = wvs_status();
1401 if (inserted) {
1402 if (rb->global_settings->unplug_mode > 1) {
1403 if (status == STREAM_PAUSED) {
1404 wvs_resume();
1407 } else {
1408 if (status == STREAM_PLAYING) {
1409 wvs_pause();
1411 if (stream_can_seek() && rb->global_settings->unplug_rw) {
1412 stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
1413 SEEK_CUR);
1414 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1415 /* Update time display now */
1416 wvs_update_time();
1417 wvs_refresh(WVS_REFRESH_TIME);
1422 #endif
1424 static void button_loop(void)
1426 rb->lcd_setfont(FONT_SYSFIXED);
1427 rb->lcd_clear_display();
1428 rb->lcd_update();
1430 wvs_init();
1432 /* Start playback at the specified starting time */
1433 if (wvs_play(settings.resume_time) < STREAM_OK) {
1434 rb->splash(HZ*2, "Playback failed");
1435 return;
1438 /* Gently poll the video player for EOS and handle UI */
1439 while (stream_status() != STREAM_STOPPED)
1441 int button;
1443 mpeg_menu_sysevent_clear();
1444 button = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1446 button = mpeg_menu_sysevent_callback(button, -1);
1448 switch (button)
1450 case BUTTON_NONE:
1452 wvs_refresh(WVS_REFRESH_DEFAULT);
1453 continue;
1454 } /* BUTTON_NONE: */
1456 #ifdef HAVE_LCD_ENABLE
1457 case LCD_ENABLE_EVENT_1:
1459 /* Draw the current frame if prepared already */
1460 stream_draw_frame(true);
1461 break;
1462 } /* LCD_ENABLE_EVENT_1: */
1463 #endif
1465 case MPEG_VOLUP:
1466 case MPEG_VOLUP|BUTTON_REPEAT:
1467 #ifdef MPEG_VOLUP2
1468 case MPEG_VOLUP2:
1469 case MPEG_VOLUP2|BUTTON_REPEAT:
1470 #endif
1471 #ifdef MPEG_RC_VOLUP
1472 case MPEG_RC_VOLUP:
1473 case MPEG_RC_VOLUP|BUTTON_REPEAT:
1474 #endif
1476 wvs_set_volume(+1);
1477 break;
1478 } /* MPEG_VOLUP*: */
1480 case MPEG_VOLDOWN:
1481 case MPEG_VOLDOWN|BUTTON_REPEAT:
1482 #ifdef MPEG_VOLDOWN2
1483 case MPEG_VOLDOWN2:
1484 case MPEG_VOLDOWN2|BUTTON_REPEAT:
1485 #endif
1486 #ifdef MPEG_RC_VOLDOWN
1487 case MPEG_RC_VOLDOWN:
1488 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
1489 #endif
1491 wvs_set_volume(-1);
1492 break;
1493 } /* MPEG_VOLDOWN*: */
1495 case MPEG_MENU:
1496 #ifdef MPEG_RC_MENU
1497 case MPEG_RC_MENU:
1498 #endif
1500 int state = wvs_halt(); /* save previous state */
1501 int result;
1503 /* Hide video output */
1504 wvs_show(WVS_HIDE | WVS_NODRAW);
1505 stream_show_vo(false);
1506 wvs_backlight_brightness_video_mode(false);
1508 result = mpeg_menu(0);
1510 /* The menu can change the font, so restore */
1511 rb->lcd_setfont(FONT_SYSFIXED);
1513 switch (result)
1515 case MPEG_MENU_QUIT:
1516 wvs_stop();
1517 break;
1519 default:
1520 /* If not stopped, show video again */
1521 if (state != STREAM_STOPPED) {
1522 wvs_show(WVS_SHOW);
1523 stream_show_vo(true);
1526 /* If stream was playing, restart it */
1527 if (state == STREAM_PLAYING) {
1528 wvs_resume();
1530 break;
1532 break;
1533 } /* MPEG_MENU: */
1535 case MPEG_STOP:
1536 #ifdef MPEG_RC_STOP
1537 case MPEG_RC_STOP:
1538 #endif
1539 case ACTION_STD_CANCEL:
1541 wvs_stop();
1542 break;
1543 } /* MPEG_STOP: */
1545 case MPEG_PAUSE:
1546 #ifdef MPEG_PAUSE2
1547 case MPEG_PAUSE2:
1548 #endif
1549 #ifdef MPEG_RC_PAUSE
1550 case MPEG_RC_PAUSE:
1551 #endif
1553 int status = wvs_status();
1555 if (status == STREAM_PLAYING) {
1556 /* Playing => Paused */
1557 wvs_pause();
1559 else if (status == STREAM_PAUSED) {
1560 /* Paused => Playing */
1561 wvs_resume();
1564 break;
1565 } /* MPEG_PAUSE*: */
1567 case MPEG_RW:
1568 case MPEG_FF:
1569 #ifdef MPEG_RC_RW
1570 case MPEG_RC_RW:
1571 case MPEG_RC_FF:
1572 #endif
1574 wvs_seek(button);
1575 break;
1576 } /* MPEG_RW: MPEG_FF: */
1578 #ifdef HAVE_HEADPHONE_DETECTION
1579 case SYS_PHONE_PLUGGED:
1580 case SYS_PHONE_UNPLUGGED:
1582 wvs_handle_phone_plug(button == SYS_PHONE_PLUGGED);
1583 break;
1584 } /* SYS_PHONE_*: */
1585 #endif
1587 default:
1589 rb->default_event_handler(button);
1590 break;
1591 } /* default: */
1594 rb->yield();
1595 } /* end while */
1597 wvs_stop();
1599 #ifdef HAVE_LCD_ENABLE
1600 /* Be sure hook is removed before exiting since the stop will put it
1601 * back because of the backlight restore. */
1602 rb->lcd_set_enable_hook(NULL);
1603 #endif
1605 rb->lcd_setfont(FONT_UI);
1608 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
1610 int status = PLUGIN_ERROR; /* assume failure */
1611 int result;
1612 int err;
1613 const char *errstring;
1615 if (parameter == NULL) {
1616 /* No file = GTFO */
1617 api->splash(HZ*2, "No File");
1618 return PLUGIN_ERROR;
1621 /* Disable all talking before initializing IRAM */
1622 api->talk_disable(true);
1624 /* Initialize IRAM - stops audio and voice as well */
1625 PLUGIN_IRAM_INIT(api)
1627 rb = api;
1629 #ifdef HAVE_LCD_COLOR
1630 rb->lcd_set_backdrop(NULL);
1631 rb->lcd_set_foreground(LCD_WHITE);
1632 rb->lcd_set_background(LCD_BLACK);
1633 #endif
1635 rb->lcd_clear_display();
1636 rb->lcd_update();
1638 if (stream_init() < STREAM_OK) {
1639 DEBUGF("Could not initialize streams\n");
1640 } else {
1641 rb->splash(0, "Loading...");
1642 init_settings((char*)parameter);
1644 err = stream_open((char *)parameter);
1646 if (err >= STREAM_OK) {
1647 /* start menu */
1648 rb->lcd_clear_display();
1649 rb->lcd_update();
1650 result = mpeg_start_menu(stream_get_duration());
1652 if (result != MPEG_START_QUIT) {
1653 /* Enter button loop and process UI */
1654 button_loop();
1657 stream_close();
1659 rb->lcd_clear_display();
1660 rb->lcd_update();
1662 save_settings(); /* Save settings (if they have changed) */
1663 status = PLUGIN_OK;
1665 mpeg_menu_sysevent_handle();
1666 } else {
1667 DEBUGF("Could not open %s\n", (char*)parameter);
1668 switch (err)
1670 case STREAM_UNSUPPORTED:
1671 errstring = "Unsupported format";
1672 break;
1673 default:
1674 errstring = "Error opening file: %d";
1677 rb->splashf(HZ*2, errstring, err);
1681 stream_exit();
1683 rb->talk_disable(false);
1684 return status;