Now that headphone plug pause/resume should be better behaved, support it in mpegplay...
[kugel-rb.git] / apps / plugins / mpegplayer / mpegplayer.c
blob80b43660a51e037efaade887f1f0d6349fb0b36a
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 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /****************************************************************************
23 * NOTES:
25 * mpegplayer is structured as follows:
27 * +-->Video Thread-->Video Output-->LCD
28 * |
29 * UI-->Stream Manager-->+-->Audio Thread-->PCM buffer--Audio Device
30 * | | | | (ref. clock)
31 * | | +-->Buffer Thread |
32 * Stream Data | | (clock intf./
33 * Requests | File Cache drift adj.)
34 * | Disk I/O
35 * Stream services
36 * (timing, etc.)
38 * Thread list:
39 * 1) The main thread - Handles user input, settings, basic playback control
40 * and USB connect.
42 * 2) Stream Manager thread - Handles playback state, events from streams
43 * such as when a stream is finished, stream commands, PCM state. The
44 * layer in which this thread run also handles arbitration of data
45 * requests between the streams and the disk buffer. The actual specific
46 * transport layer code may get moved out to support multiple container
47 * formats.
49 * 3) Buffer thread - Buffers data in the background, generates notifications
50 * to streams when their data has been buffered, and watches streams'
51 * progress to keep data available during playback. Handles synchronous
52 * random access requests when the file cache is missed.
54 * 4) Video thread (running on the COP for PortalPlayer targets) - Decodes
55 * the video stream and renders video frames to the LCD. Handles
56 * miscellaneous video tasks like frame and thumbnail printing.
58 * 5) Audio thread (running on the main CPU to maintain consistency with the
59 * audio FIQ hander on PP) - Decodes audio frames and places them into
60 * the PCM buffer for rendering by the audio device.
62 * Streams are neither aware of one another nor care about one another. All
63 * streams shall have their own thread (unless it is _really_ efficient to
64 * have a single thread handle a couple minor streams). All coordination of
65 * the streams is done through the stream manager. The clocking is controlled
66 * by and exposed by the stream manager to other streams and implemented at
67 * the PCM level.
69 * Notes about MPEG files:
71 * MPEG System Clock is 27MHz - i.e. 27000000 ticks/second.
73 * FPS is represented in terms of a frame period - this is always an
74 * integer number of 27MHz ticks.
76 * e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of
77 * 900900 27MHz ticks.
79 * In libmpeg2, info->sequence->frame_period contains the frame_period.
81 * Working with Rockbox's 100Hz tick, the common frame rates would need
82 * to be as follows (1):
84 * FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz
85 * --------|-----------------------------------------------------------
86 * 10* | 2700000 | 10 | 4410 | 4800
87 * 12* | 2250000 | 8.3333 | 3675 | 4000
88 * 15* | 1800000 | 6.6667 | 2940 | 3200
89 * 23.9760 | 1126125 | 4.170833333 | 1839.3375 | 2002
90 * 24 | 1125000 | 4.166667 | 1837.5 | 2000
91 * 25 | 1080000 | 4 | 1764 | 1920
92 * 29.9700 | 900900 | 3.336667 | 1471,47 | 1601.6
93 * 30 | 900000 | 3.333333 | 1470 | 1600
95 * *Unofficial framerates
97 * (1) But we don't really care since the audio clock is used anyway and has
98 * very fine resolution ;-)
99 *****************************************************************************/
100 #include "plugin.h"
101 #include "mpegplayer.h"
102 #include "helper.h"
103 #include "mpeg_settings.h"
104 #include "mpeg2.h"
105 #include "video_out.h"
106 #include "stream_thread.h"
107 #include "stream_mgr.h"
109 PLUGIN_HEADER
110 PLUGIN_IRAM_DECLARE
112 /* button definitions */
113 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
114 #define MPEG_MENU BUTTON_MODE
115 #define MPEG_STOP BUTTON_OFF
116 #define MPEG_PAUSE BUTTON_ON
117 #define MPEG_VOLDOWN BUTTON_DOWN
118 #define MPEG_VOLUP BUTTON_UP
119 #define MPEG_RW BUTTON_LEFT
120 #define MPEG_FF BUTTON_RIGHT
122 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
123 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
124 #define MPEG_MENU BUTTON_MENU
125 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
126 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
127 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
128 #define MPEG_VOLUP BUTTON_SCROLL_FWD
129 #define MPEG_RW BUTTON_LEFT
130 #define MPEG_FF BUTTON_RIGHT
132 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
133 #define MPEG_MENU (BUTTON_REC | BUTTON_REL)
134 #define MPEG_STOP BUTTON_POWER
135 #define MPEG_PAUSE BUTTON_PLAY
136 #define MPEG_VOLDOWN BUTTON_DOWN
137 #define MPEG_VOLUP BUTTON_UP
138 #define MPEG_RW BUTTON_LEFT
139 #define MPEG_FF BUTTON_RIGHT
141 #elif CONFIG_KEYPAD == GIGABEAT_PAD
142 #define MPEG_MENU BUTTON_MENU
143 #define MPEG_STOP BUTTON_POWER
144 #define MPEG_PAUSE BUTTON_SELECT
145 #define MPEG_PAUSE2 BUTTON_A
146 #define MPEG_VOLDOWN BUTTON_LEFT
147 #define MPEG_VOLUP BUTTON_RIGHT
148 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
149 #define MPEG_VOLUP2 BUTTON_VOL_UP
150 #define MPEG_RW BUTTON_UP
151 #define MPEG_FF BUTTON_DOWN
152 #define MPEG_RC_MENU BUTTON_RC_DSP
153 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
154 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
155 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
156 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
157 #define MPEG_RC_RW BUTTON_RC_REW
158 #define MPEG_RC_FF BUTTON_RC_FF
160 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
161 #define MPEG_MENU BUTTON_LEFT
162 #define MPEG_STOP BUTTON_POWER
163 #define MPEG_PAUSE BUTTON_PLAY
164 #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
165 #define MPEG_VOLUP BUTTON_SCROLL_UP
166 #define MPEG_RW BUTTON_REW
167 #define MPEG_FF BUTTON_FF
169 #elif CONFIG_KEYPAD == SANSA_E200_PAD
170 #define MPEG_MENU BUTTON_SELECT
171 #define MPEG_STOP BUTTON_POWER
172 #define MPEG_PAUSE BUTTON_UP
173 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
174 #define MPEG_VOLUP BUTTON_SCROLL_FWD
175 #define MPEG_RW BUTTON_LEFT
176 #define MPEG_FF BUTTON_RIGHT
178 #elif CONFIG_KEYPAD == SANSA_C200_PAD
179 #define MPEG_MENU BUTTON_SELECT
180 #define MPEG_STOP BUTTON_POWER
181 #define MPEG_PAUSE BUTTON_UP
182 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
183 #define MPEG_VOLUP BUTTON_VOL_UP
184 #define MPEG_RW BUTTON_LEFT
185 #define MPEG_FF BUTTON_RIGHT
187 #elif CONFIG_KEYPAD == MROBE500_PAD
188 #define MPEG_MENU BUTTON_RC_HEART
189 #define MPEG_STOP BUTTON_POWER
190 #define MPEG_PAUSE BUTTON_TOUCHPAD
191 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
192 #define MPEG_VOLUP BUTTON_RC_VOL_UP
193 #define MPEG_RW BUTTON_RC_REW
194 #define MPEG_FF BUTTON_RC_FF
196 #else
197 #error MPEGPLAYER: Unsupported keypad
198 #endif
200 struct plugin_api* rb;
202 CACHE_FUNCTION_WRAPPERS(rb);
203 ALIGN_BUFFER_WRAPPER(rb);
205 /* One thing we can do here for targets with remotes is having a display
206 * always on the remote instead of always forcing a popup on the main display */
208 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
209 /* 3% of 30min file == 54s step size */
210 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
211 #define WVS_MIN_UPDATE_INTERVAL (HZ/2)
213 /* WVS status - same order as icon array */
214 enum wvs_status_enum
216 WVS_STATUS_STOPPED = 0,
217 WVS_STATUS_PAUSED,
218 WVS_STATUS_PLAYING,
219 WVS_STATUS_FF,
220 WVS_STATUS_RW,
221 WVS_STATUS_COUNT,
222 WVS_STATUS_MASK = 0x7
225 enum wvs_bits
227 WVS_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
228 /* Refresh the... */
229 WVS_REFRESH_VOLUME = 0x0001, /* ...volume display */
230 WVS_REFRESH_TIME = 0x0002, /* ...time display+progress */
231 WVS_REFRESH_STATUS = 0x0004, /* ...playback status icon */
232 WVS_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
233 WVS_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
234 WVS_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
235 WVS_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
236 WVS_SHOW = 0x4000, /* OR bitflag - show the WVS */
237 WVS_HP_PAUSE = 0x2000,
238 WVS_HIDE = 0x0000, /* hide the WVS (aid readability) */
239 WVS_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
242 /* Status icons selected according to font height */
243 extern const unsigned char mpegplayer_status_icons_8x8x1[];
244 extern const unsigned char mpegplayer_status_icons_12x12x1[];
245 extern const unsigned char mpegplayer_status_icons_16x16x1[];
247 /* Main border areas that contain WVS elements */
248 #define WVS_BDR_L 2
249 #define WVS_BDR_T 2
250 #define WVS_BDR_R 2
251 #define WVS_BDR_B 2
253 struct wvs
255 long hide_tick;
256 long show_for;
257 long print_tick;
258 long print_delay;
259 long resume_tick;
260 long resume_delay;
261 long next_auto_refresh;
262 int x;
263 int y;
264 int width;
265 int height;
266 unsigned fgcolor;
267 unsigned bgcolor;
268 unsigned prog_fillcolor;
269 struct vo_rect update_rect;
270 struct vo_rect prog_rect;
271 struct vo_rect time_rect;
272 struct vo_rect dur_rect;
273 struct vo_rect vol_rect;
274 const unsigned char *icons;
275 struct vo_rect stat_rect;
276 int status;
277 uint32_t curr_time;
278 unsigned auto_refresh;
279 unsigned flags;
282 static struct wvs wvs;
284 static void wvs_show(unsigned show);
286 #ifdef LCD_LANDSCAPE
287 #define _X (x + wvs.x)
288 #define _Y (y + wvs.y)
289 #define _W width
290 #define _H height
291 #else
292 #define _X (LCD_WIDTH - (y + wvs.y) - height)
293 #define _Y (x + wvs.x)
294 #define _W height
295 #define _H width
296 #endif
298 #ifdef HAVE_LCD_COLOR
299 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
300 static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
302 int r1 = RGB_UNPACK_RED(c1);
303 int g1 = RGB_UNPACK_GREEN(c1);
304 int b1 = RGB_UNPACK_BLUE(c1);
306 int r2 = RGB_UNPACK_RED(c2);
307 int g2 = RGB_UNPACK_GREEN(c2);
308 int b2 = RGB_UNPACK_BLUE(c2);
310 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
311 amount*(g2 - g1) / 255 + g1,
312 amount*(b2 - b1) / 255 + b1);
314 #endif
316 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
317 * most are just wrappers of lcd_* functions with transforms applied.
318 * The origin is the upper-left corner of the WVS area */
319 static void draw_update_rect(int x, int y, int width, int height)
321 lcd_(update_rect)(_X, _Y, _W, _H);
324 static void draw_clear_area(int x, int y, int width, int height)
326 #ifdef HAVE_LCD_COLOR
327 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
328 #else
329 int oldmode = grey_get_drawmode();
330 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
331 grey_fillrect(_X, _Y, _W, _H);
332 grey_set_drawmode(oldmode);
333 #endif
336 static void draw_clear_area_rect(const struct vo_rect *rc)
338 int x = rc->l;
339 int y = rc->t;
340 int width = rc->r - rc->l;
341 int height = rc->b - rc->t;
342 #ifdef HAVE_LCD_COLOR
343 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
344 #else
345 int oldmode = grey_get_drawmode();
346 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
347 grey_fillrect(_X, _Y, _W, _H);
348 grey_set_drawmode(oldmode);
349 #endif
352 static void draw_fillrect(int x, int y, int width, int height)
354 lcd_(fillrect)(_X, _Y, _W, _H);
357 static void draw_hline(int x1, int x2, int y)
359 #ifdef LCD_LANDSCAPE
360 lcd_(hline)(x1 + wvs.x, x2 + wvs.x, y + wvs.y);
361 #else
362 y = LCD_WIDTH - (y + wvs.y) - 1;
363 lcd_(vline)(y, x1 + wvs.x, x2 + wvs.x);
364 #endif
367 static void draw_vline(int x, int y1, int y2)
369 #ifdef LCD_LANDSCAPE
370 lcd_(vline)(x + wvs.x, y1 + wvs.y, y2 + wvs.y);
371 #else
372 y1 = LCD_WIDTH - (y1 + wvs.y) - 1;
373 y2 = LCD_WIDTH - (y2 + wvs.y) - 1;
374 lcd_(hline)(y1, y2, x + wvs.x);
375 #endif
378 static void draw_scrollbar_draw(int x, int y, int width, int height,
379 uint32_t min, uint32_t max, uint32_t val)
381 unsigned oldfg = lcd_(get_foreground)();
383 draw_hline(x + 1, x + width - 2, y);
384 draw_hline(x + 1, x + width - 2, y + height - 1);
385 draw_vline(x, y + 1, y + height - 2);
386 draw_vline(x + width - 1, y + 1, y + height - 2);
388 val = muldiv_uint32(width - 2, val, max - min);
389 val = MIN(val, (uint32_t)(width - 2));
391 draw_fillrect(x + 1, y + 1, val, height - 2);
393 lcd_(set_foreground)(wvs.prog_fillcolor);
395 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
397 lcd_(set_foreground)(oldfg);
400 static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
401 int max, int val)
403 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
404 min, max, val);
407 #ifdef LCD_PORTRAIT
408 /* Portrait displays need rotated text rendering */
410 /* Limited function that only renders in DRMODE_FG and uses absolute screen
411 * coordinates */
412 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
413 int src_x, int src_y,
414 int stride, int x, int y,
415 int width, int height)
417 const unsigned char *src_end;
418 fb_data *dst, *dst_end;
419 unsigned fg_pattern, bg_pattern;
421 if (x + width > SCREEN_WIDTH)
422 width = SCREEN_WIDTH - x; /* Clip right */
423 if (x < 0)
424 width += x, x = 0; /* Clip left */
425 if (width <= 0)
426 return; /* nothing left to do */
428 if (y + height > SCREEN_HEIGHT)
429 height = SCREEN_HEIGHT - y; /* Clip bottom */
430 if (y < 0)
431 height += y, y = 0; /* Clip top */
432 if (height <= 0)
433 return; /* nothing left to do */
435 fg_pattern = rb->lcd_get_foreground();
436 bg_pattern = rb->lcd_get_background();
438 src += stride * (src_y >> 3) + src_x; /* move starting point */
439 src_y &= 7;
440 src_end = src + width;
442 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
445 const unsigned char *src_col = src++;
446 unsigned data = *src_col >> src_y;
447 int numbits = 8 - src_y;
449 fb_data *dst_col = dst;
450 dst_end = dst_col - height;
451 dst += LCD_WIDTH;
455 dst_col--;
457 if (data & 1)
458 *dst_col = fg_pattern;
459 #if 0
460 else
461 *dst_col = bg_pattern;
462 #endif
463 data >>= 1;
464 if (--numbits == 0) {
465 src_col += stride;
466 data = *src_col;
467 numbits = 8;
470 while (dst_col > dst_end);
472 while (src < src_end);
475 static void draw_putsxy_oriented(int x, int y, const char *str)
477 unsigned short ch;
478 unsigned short *ucs;
479 int ofs = MIN(x, 0);
480 struct font* pf = rb->font_get(FONT_UI);
482 ucs = rb->bidi_l2v(str, 1);
484 x += wvs.x;
485 y += wvs.y;
487 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
489 int width;
490 const unsigned char *bits;
492 /* get proportional width and glyph bits */
493 width = rb->font_get_width(pf, ch);
495 if (ofs > width) {
496 ofs -= width;
497 continue;
500 bits = rb->font_get_bits(pf, ch);
502 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
503 width - ofs, pf->height);
505 x += width - ofs;
506 ofs = 0;
509 #else
510 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
511 int src_x, int src_y,
512 int stride, int x, int y,
513 int width, int height)
515 int mode = lcd_(get_drawmode)();
516 lcd_(set_drawmode)(DRMODE_FG);
517 lcd_(mono_bitmap_part)(src, src_x, src_y, stride, x, y, width, height);
518 lcd_(set_drawmode)(mode);
521 static void draw_putsxy_oriented(int x, int y, const char *str)
523 int mode = lcd_(get_drawmode)();
524 lcd_(set_drawmode)(DRMODE_FG);
525 lcd_(putsxy)(x + wvs.x, y + wvs.y, str);
526 lcd_(set_drawmode)(mode);
528 #endif /* LCD_PORTRAIT */
531 static void wvs_text_init(void)
533 struct hms hms;
534 char buf[32];
535 int phys;
536 int spc_width;
538 lcd_(setfont)(FONT_UI);
540 wvs.x = 0;
541 wvs.width = SCREEN_WIDTH;
543 vo_rect_clear(&wvs.time_rect);
544 vo_rect_clear(&wvs.stat_rect);
545 vo_rect_clear(&wvs.prog_rect);
546 vo_rect_clear(&wvs.vol_rect);
548 ts_to_hms(stream_get_duration(), &hms);
549 hms_format(buf, sizeof (buf), &hms);
550 lcd_(getstringsize)(buf, &wvs.time_rect.r, &wvs.time_rect.b);
552 /* Choose well-sized bitmap images relative to font height */
553 if (wvs.time_rect.b < 12) {
554 wvs.icons = mpegplayer_status_icons_8x8x1;
555 wvs.stat_rect.r = wvs.stat_rect.b = 8;
556 } else if (wvs.time_rect.b < 16) {
557 wvs.icons = mpegplayer_status_icons_12x12x1;
558 wvs.stat_rect.r = wvs.stat_rect.b = 12;
559 } else {
560 wvs.icons = mpegplayer_status_icons_16x16x1;
561 wvs.stat_rect.r = wvs.stat_rect.b = 16;
564 if (wvs.stat_rect.b < wvs.time_rect.b) {
565 vo_rect_offset(&wvs.stat_rect, 0,
566 (wvs.time_rect.b - wvs.stat_rect.b) / 2 + WVS_BDR_T);
567 vo_rect_offset(&wvs.time_rect, WVS_BDR_L, WVS_BDR_T);
568 } else {
569 vo_rect_offset(&wvs.time_rect, WVS_BDR_L,
570 wvs.stat_rect.b - wvs.time_rect.b + WVS_BDR_T);
571 vo_rect_offset(&wvs.stat_rect, 0, WVS_BDR_T);
574 wvs.dur_rect = wvs.time_rect;
576 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
577 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
578 rb->sound_unit(SOUND_VOLUME));
580 lcd_(getstringsize)(" ", &spc_width, NULL);
581 lcd_(getstringsize)(buf, &wvs.vol_rect.r, &wvs.vol_rect.b);
583 wvs.prog_rect.r = SCREEN_WIDTH - WVS_BDR_L - spc_width -
584 wvs.vol_rect.r - WVS_BDR_R;
585 wvs.prog_rect.b = 3*wvs.stat_rect.b / 4;
586 vo_rect_offset(&wvs.prog_rect, wvs.time_rect.l,
587 wvs.time_rect.b);
589 vo_rect_offset(&wvs.stat_rect,
590 (wvs.prog_rect.r + wvs.prog_rect.l - wvs.stat_rect.r) / 2,
593 vo_rect_offset(&wvs.dur_rect,
594 wvs.prog_rect.r - wvs.dur_rect.r, 0);
596 vo_rect_offset(&wvs.vol_rect, wvs.prog_rect.r + spc_width,
597 (wvs.prog_rect.b + wvs.prog_rect.t - wvs.vol_rect.b) / 2);
599 wvs.height = WVS_BDR_T + MAX(wvs.prog_rect.b, wvs.vol_rect.b) -
600 MIN(wvs.time_rect.t, wvs.stat_rect.t) + WVS_BDR_B;
602 #ifdef HAVE_LCD_COLOR
603 wvs.height = ALIGN_UP(wvs.height, 2);
604 #endif
605 wvs.y = SCREEN_HEIGHT - wvs.height;
607 lcd_(setfont)(FONT_SYSFIXED);
610 static void wvs_init(void)
612 wvs.flags = 0;
613 wvs.show_for = HZ*4;
614 wvs.print_delay = 75*HZ/100;
615 wvs.resume_delay = HZ/2;
616 #ifdef HAVE_LCD_COLOR
617 wvs.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
618 wvs.fgcolor = LCD_WHITE;
619 wvs.prog_fillcolor = LCD_BLACK;
620 #else
621 wvs.bgcolor = GREY_LIGHTGRAY;
622 wvs.fgcolor = GREY_BLACK;
623 wvs.prog_fillcolor = GREY_WHITE;
624 #endif
625 wvs.curr_time = 0;
626 wvs.status = WVS_STATUS_STOPPED;
627 wvs.auto_refresh = WVS_REFRESH_TIME;
628 wvs.next_auto_refresh = *rb->current_tick;
629 wvs_text_init();
632 static void wvs_schedule_refresh(unsigned refresh)
634 long tick = *rb->current_tick;
636 if (refresh & WVS_REFRESH_VIDEO)
637 wvs.print_tick = tick + wvs.print_delay;
639 if (refresh & WVS_REFRESH_RESUME)
640 wvs.resume_tick = tick + wvs.resume_delay;
642 wvs.auto_refresh |= refresh;
645 static void wvs_cancel_refresh(unsigned refresh)
647 wvs.auto_refresh &= ~refresh;
650 /* Refresh the background area */
651 static void wvs_refresh_background(void)
653 char buf[32];
654 struct hms hms;
656 unsigned bg = lcd_(get_background)();
657 lcd_(set_drawmode)(DRMODE_SOLID | DRMODE_INVERSEVID);
659 #ifdef HAVE_LCD_COLOR
660 /* Draw a "raised" area for our graphics */
661 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 192));
662 draw_hline(0, wvs.width, 0);
664 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 80));
665 draw_hline(0, wvs.width, 1);
667 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 48));
668 draw_hline(0, wvs.width, wvs.height-2);
670 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 128));
671 draw_hline(0, wvs.width, wvs.height-1);
673 lcd_(set_background)(bg);
674 draw_clear_area(0, 2, wvs.width, wvs.height - 4);
675 #else
676 /* Give contrast with the main background */
677 lcd_(set_background)(GREY_WHITE);
678 draw_hline(0, wvs.width, 0);
680 lcd_(set_background)(GREY_DARKGRAY);
681 draw_hline(0, wvs.width, wvs.height-1);
683 lcd_(set_background)(bg);
684 draw_clear_area(0, 1, wvs.width, wvs.height - 2);
685 #endif
687 vo_rect_set_ext(&wvs.update_rect, 0, 0, wvs.width, wvs.height);
688 lcd_(set_drawmode)(DRMODE_SOLID);
690 if (stream_get_duration() != INVALID_TIMESTAMP) {
691 /* Draw the movie duration */
692 ts_to_hms(stream_get_duration(), &hms);
693 hms_format(buf, sizeof (buf), &hms);
694 draw_putsxy_oriented(wvs.dur_rect.l, wvs.dur_rect.t, buf);
696 /* else don't know the duration */
699 /* Refresh the current time display + the progress bar */
700 static void wvs_refresh_time(void)
702 char buf[32];
703 struct hms hms;
705 uint32_t duration = stream_get_duration();
707 draw_scrollbar_draw_rect(&wvs.prog_rect, 0, duration,
708 wvs.curr_time);
710 ts_to_hms(wvs.curr_time, &hms);
711 hms_format(buf, sizeof (buf), &hms);
713 draw_clear_area_rect(&wvs.time_rect);
714 draw_putsxy_oriented(wvs.time_rect.l, wvs.time_rect.t, buf);
716 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
717 &wvs.prog_rect);
718 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
719 &wvs.time_rect);
722 /* Refresh the volume display area */
723 static void wvs_refresh_volume(void)
725 char buf[32];
726 int width;
728 int volume = rb->global_settings->volume;
729 rb->snprintf(buf, sizeof (buf), "%d%s",
730 rb->sound_val2phys(SOUND_VOLUME, volume),
731 rb->sound_unit(SOUND_VOLUME));
732 lcd_(getstringsize)(buf, &width, NULL);
734 /* Right-justified */
735 draw_clear_area_rect(&wvs.vol_rect);
736 draw_putsxy_oriented(wvs.vol_rect.r - width, wvs.vol_rect.t, buf);
738 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.vol_rect);
741 /* Refresh the status icon */
742 static void wvs_refresh_status(void)
744 int icon_size = wvs.stat_rect.r - wvs.stat_rect.l;
746 draw_clear_area_rect(&wvs.stat_rect);
748 #ifdef HAVE_LCD_COLOR
749 /* Draw status icon with a drop shadow */
750 unsigned oldfg = lcd_(get_foreground)();
751 int i = 1;
753 lcd_(set_foreground)(draw_blendcolor(lcd_(get_background)(),
754 DRAW_BLACK, 96));
756 while (1)
758 draw_oriented_mono_bitmap_part(wvs.icons,
759 icon_size*wvs.status,
761 icon_size*WVS_STATUS_COUNT,
762 wvs.stat_rect.l + wvs.x + i,
763 wvs.stat_rect.t + wvs.y + i,
764 icon_size, icon_size);
766 if (--i < 0)
767 break;
769 lcd_(set_foreground)(oldfg);
772 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
773 #else
774 draw_oriented_mono_bitmap_part(wvs.icons,
775 icon_size*wvs.status,
777 icon_size*WVS_STATUS_COUNT,
778 wvs.stat_rect.l + wvs.x,
779 wvs.stat_rect.t + wvs.y,
780 icon_size, icon_size);
781 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
782 #endif
785 /* Update the current status which determines which icon is displayed */
786 static bool wvs_update_status(void)
788 int status;
790 switch (stream_status())
792 default:
793 status = WVS_STATUS_STOPPED;
794 break;
795 case STREAM_PAUSED:
796 /* If paused with a pending resume, coerce it to WVS_STATUS_PLAYING */
797 status = (wvs.auto_refresh & WVS_REFRESH_RESUME) ?
798 WVS_STATUS_PLAYING : WVS_STATUS_PAUSED;
799 break;
800 case STREAM_PLAYING:
801 status = WVS_STATUS_PLAYING;
802 break;
805 if (status != wvs.status) {
806 /* A refresh is needed */
807 wvs.status = status;
808 return true;
811 return false;
814 /* Update the current time that will be displayed */
815 static void wvs_update_time(void)
817 uint32_t start;
818 wvs.curr_time = stream_get_seek_time(&start);
819 wvs.curr_time -= start;
822 /* Refresh various parts of the WVS - showing it if it is hidden */
823 static void wvs_refresh(int hint)
825 long tick;
826 unsigned oldbg, oldfg;
828 tick = *rb->current_tick;
830 if (hint == WVS_REFRESH_DEFAULT) {
831 /* The default which forces no updates */
833 /* Make sure Rockbox doesn't turn off the player because of
834 too little activity */
835 if (wvs.status == WVS_STATUS_PLAYING)
836 rb->reset_poweroff_timer();
838 /* Redraw the current or possibly extract a new video frame */
839 if ((wvs.auto_refresh & WVS_REFRESH_VIDEO) &&
840 TIME_AFTER(tick, wvs.print_tick)) {
841 wvs.auto_refresh &= ~WVS_REFRESH_VIDEO;
842 stream_draw_frame(false);
845 /* Restart playback if the timout was reached */
846 if ((wvs.auto_refresh & WVS_REFRESH_RESUME) &&
847 TIME_AFTER(tick, wvs.resume_tick)) {
848 wvs.auto_refresh &= ~(WVS_REFRESH_RESUME | WVS_REFRESH_VIDEO);
849 stream_resume();
852 /* If not visible, return */
853 if (!(wvs.flags & WVS_SHOW))
854 return;
856 /* Hide if the visibility duration was reached */
857 if (TIME_AFTER(tick, wvs.hide_tick)) {
858 wvs_show(WVS_HIDE);
859 return;
861 } else {
862 /* A forced update of some region */
864 /* Show if currently invisible */
865 if (!(wvs.flags & WVS_SHOW)) {
866 /* Avoid call back into this function - it will be drawn */
867 wvs_show(WVS_SHOW | WVS_NODRAW);
868 hint = WVS_REFRESH_ALL;
871 /* Move back timeouts for frame print and hide */
872 wvs.print_tick = tick + wvs.print_delay;
873 wvs.hide_tick = tick + wvs.show_for;
876 if (TIME_AFTER(tick, wvs.next_auto_refresh)) {
877 /* Refresh whatever graphical elements are due automatically */
878 wvs.next_auto_refresh = tick + WVS_MIN_UPDATE_INTERVAL;
880 if (wvs.auto_refresh & WVS_REFRESH_STATUS) {
881 if (wvs_update_status())
882 hint |= WVS_REFRESH_STATUS;
885 if (wvs.auto_refresh & WVS_REFRESH_TIME) {
886 wvs_update_time();
887 hint |= WVS_REFRESH_TIME;
891 if (hint == 0)
892 return; /* No drawing needed */
894 /* Set basic drawing params that are used. Elements that perform variations
895 * will restore them. */
896 oldfg = lcd_(get_foreground)();
897 oldbg = lcd_(get_background)();
899 lcd_(setfont)(FONT_UI);
900 lcd_(set_foreground)(wvs.fgcolor);
901 lcd_(set_background)(wvs.bgcolor);
903 vo_rect_clear(&wvs.update_rect);
905 if (hint & WVS_REFRESH_BACKGROUND) {
906 wvs_refresh_background();
907 hint |= WVS_REFRESH_ALL; /* Requires a redraw of everything */
910 if (hint & WVS_REFRESH_TIME) {
911 wvs_refresh_time();
914 if (hint & WVS_REFRESH_VOLUME) {
915 wvs_refresh_volume();
918 if (hint & WVS_REFRESH_STATUS) {
919 wvs_refresh_status();
922 /* Go back to defaults */
923 lcd_(setfont)(FONT_SYSFIXED);
924 lcd_(set_foreground)(oldfg);
925 lcd_(set_background)(oldbg);
927 /* Update the dirty rectangle */
928 vo_lock();
930 draw_update_rect(wvs.update_rect.l,
931 wvs.update_rect.t,
932 wvs.update_rect.r - wvs.update_rect.l,
933 wvs.update_rect.b - wvs.update_rect.t);
935 vo_unlock();
938 /* Show/Hide the WVS */
939 static void wvs_show(unsigned show)
941 if (((show ^ wvs.flags) & WVS_SHOW) == 0)
943 if (show & WVS_SHOW) {
944 wvs.hide_tick = *rb->current_tick + wvs.show_for;
946 return;
949 if (show & WVS_SHOW) {
950 /* Clip away the part of video that is covered */
951 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, wvs.y };
953 wvs.flags |= WVS_SHOW;
955 stream_vo_set_clip(&rc);
957 if (!(show & WVS_NODRAW))
958 wvs_refresh(WVS_REFRESH_ALL);
959 } else {
960 /* Uncover clipped video area and redraw it */
961 wvs.flags &= ~WVS_SHOW;
963 draw_clear_area(0, 0, wvs.width, wvs.height);
965 if (!(show & WVS_NODRAW)) {
966 vo_lock();
967 draw_update_rect(0, 0, wvs.width, wvs.height);
968 vo_unlock();
970 stream_vo_set_clip(NULL);
971 stream_draw_frame(false);
972 } else {
973 stream_vo_set_clip(NULL);
978 /* Set the current status - update screen if specified */
979 static void wvs_set_status(int status)
981 bool draw = (status & WVS_NODRAW) == 0;
983 status &= WVS_STATUS_MASK;
985 if (wvs.status != status) {
987 wvs.status = status;
989 if (draw)
990 wvs_refresh(WVS_REFRESH_STATUS);
994 /* Get the current status value */
995 static int wvs_get_status(void)
997 return wvs.status & WVS_STATUS_MASK;
1000 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
1001 static uint32_t wvs_ff_rw(int btn, unsigned refresh)
1003 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1004 const long ff_rw_accel = rb->global_settings->ff_rewind_accel;
1005 long accel_tick = *rb->current_tick + ff_rw_accel*HZ;
1006 uint32_t start;
1007 uint32_t time = stream_get_seek_time(&start);
1008 const uint32_t duration = stream_get_duration();
1009 unsigned int max_step = 0;
1010 uint32_t ff_rw_count = 0;
1011 unsigned status = wvs.status;
1013 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME |
1014 WVS_REFRESH_TIME);
1016 time -= start; /* Absolute clock => stream-relative */
1018 switch (btn)
1020 case MPEG_FF:
1021 #ifdef MPEG_RC_FF
1022 case MPEG_RC_FF:
1023 #endif
1024 wvs_set_status(WVS_STATUS_FF);
1025 break;
1026 case MPEG_RW:
1027 #ifdef MPEG_RC_RW
1028 case MPEG_RC_RW:
1029 #endif
1030 wvs_set_status(WVS_STATUS_RW);
1031 break;
1032 default:
1033 btn = -1;
1036 btn |= BUTTON_REPEAT;
1038 while (1)
1040 long tick = *rb->current_tick;
1041 stream_keep_disk_active();
1043 switch (btn)
1045 case BUTTON_NONE:
1046 wvs_refresh(WVS_REFRESH_DEFAULT);
1047 break;
1049 case MPEG_FF | BUTTON_REPEAT:
1050 case MPEG_RW | BUTTON_REPEAT:
1051 #ifdef MPEG_RC_FF
1052 case MPEG_RC_FF | BUTTON_REPEAT:
1053 case MPEG_RC_RW | BUTTON_REPEAT:
1054 #endif
1055 break;
1057 case MPEG_FF | BUTTON_REL:
1058 case MPEG_RW | BUTTON_REL:
1059 #ifdef MPEG_RC_FF
1060 case MPEG_RC_FF | BUTTON_REL:
1061 case MPEG_RC_RW | BUTTON_REL:
1062 #endif
1063 if (wvs.status == WVS_STATUS_FF)
1064 time += ff_rw_count;
1065 else if (wvs.status == WVS_STATUS_RW)
1066 time -= ff_rw_count;
1068 /* Fall-through */
1069 case -1:
1070 default:
1071 wvs_schedule_refresh(refresh);
1072 wvs_set_status(status);
1073 wvs_schedule_refresh(WVS_REFRESH_TIME);
1074 return time;
1077 if (wvs.status == WVS_STATUS_FF) {
1078 /* fast forwarding, calc max step relative to end */
1079 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1080 FF_REWIND_MAX_PERCENT, 100);
1081 } else {
1082 /* rewinding, calc max step relative to start */
1083 max_step = muldiv_uint32(time - ff_rw_count,
1084 FF_REWIND_MAX_PERCENT, 100);
1087 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1089 if (step > max_step)
1090 step = max_step;
1092 ff_rw_count += step;
1094 if (ff_rw_accel != 0 && TIME_AFTER(tick, accel_tick)) {
1095 step *= 2;
1096 accel_tick = tick + ff_rw_accel*HZ;
1099 if (wvs.status == WVS_STATUS_FF) {
1100 if (duration - time <= ff_rw_count)
1101 ff_rw_count = duration - time;
1103 wvs.curr_time = time + ff_rw_count;
1104 } else {
1105 if (time <= ff_rw_count)
1106 ff_rw_count = time;
1108 wvs.curr_time = time - ff_rw_count;
1111 wvs_refresh(WVS_REFRESH_TIME);
1113 btn = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1117 static int wvs_status(void)
1119 int status = stream_status();
1121 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1122 if (status == STREAM_PAUSED) {
1123 if (wvs.auto_refresh & WVS_REFRESH_RESUME)
1124 status = STREAM_PLAYING;
1127 return status;
1130 /* Change the current audio volume by a specified amount */
1131 static void wvs_set_volume(int delta)
1133 int vol = rb->global_settings->volume;
1134 int limit;
1136 vol += delta;
1138 if (delta < 0) {
1139 /* Volume down - clip to lower limit */
1140 limit = rb->sound_min(SOUND_VOLUME);
1141 if (vol < limit)
1142 vol = limit;
1143 } else {
1144 /* Volume up - clip to upper limit */
1145 limit = rb->sound_max(SOUND_VOLUME);
1146 if (vol > limit)
1147 vol = limit;
1150 /* Sync the global settings */
1151 if (vol != rb->global_settings->volume) {
1152 rb->sound_set(SOUND_VOLUME, vol);
1153 rb->global_settings->volume = vol;
1156 /* Update the volume display */
1157 wvs_refresh(WVS_REFRESH_VOLUME);
1160 /* Begin playback at the specified time */
1161 static int wvs_play(uint32_t time)
1163 int retval;
1165 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1167 retval = stream_seek(time, SEEK_SET);
1169 if (retval >= STREAM_OK) {
1170 stream_show_vo(true);
1171 retval = stream_play();
1173 if (retval >= STREAM_OK)
1174 wvs_set_status(WVS_STATUS_PLAYING | WVS_NODRAW);
1177 return retval;
1180 /* Halt playback - pause engine and return logical state */
1181 static int wvs_halt(void)
1183 int status = stream_pause();
1185 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1186 if (status == STREAM_PAUSED) {
1187 if (wvs_get_status() == WVS_STATUS_PLAYING)
1188 status = STREAM_PLAYING;
1191 /* Cancel some auto refreshes - caller will restart them if desired */
1192 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1194 return status;
1197 /* Pause playback if playing */
1198 static int wvs_pause(void)
1200 unsigned refresh = wvs.auto_refresh;
1201 int status = wvs_halt();
1203 if (status == STREAM_PLAYING && (refresh & WVS_REFRESH_RESUME)) {
1204 /* Resume pending - change to a still video frame update */
1205 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1208 wvs_set_status(WVS_STATUS_PAUSED);
1210 return status;
1213 /* Resume playback if halted or paused */
1214 static void wvs_resume(void)
1216 /* Cancel video and resume auto refresh - the resyc when starting playback
1217 * will perform those tasks */
1218 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1219 wvs_set_status(WVS_STATUS_PLAYING);
1220 stream_resume();
1223 /* Stop playback - remember the resume point if not closed */
1224 static void wvs_stop(void)
1226 uint32_t resume_time;
1228 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1229 wvs_set_status(WVS_STATUS_STOPPED | WVS_NODRAW);
1230 wvs_show(WVS_HIDE | WVS_NODRAW);
1232 stream_stop();
1234 resume_time = stream_get_resume_time();
1236 if (resume_time != INVALID_TIMESTAMP)
1237 settings.resume_time = resume_time;
1240 /* Perform a seek if seeking is possible for this stream - if playing, a delay
1241 * will be inserted before restarting in case the user decides to seek again */
1242 static void wvs_seek(int btn)
1244 int status;
1245 unsigned refresh;
1246 uint32_t time;
1248 if (!stream_can_seek())
1249 return;
1251 /* Halt playback - not strictly nescessary but nice */
1252 status = wvs_halt();
1254 if (status == STREAM_STOPPED)
1255 return;
1257 wvs_show(WVS_SHOW);
1259 if (status == STREAM_PLAYING)
1260 refresh = WVS_REFRESH_RESUME; /* delay resume if playing */
1261 else
1262 refresh = WVS_REFRESH_VIDEO; /* refresh if paused */
1264 /* Obtain a new playback point */
1265 time = wvs_ff_rw(btn, refresh);
1267 /* Tell engine to resume at that time */
1268 stream_seek(time, SEEK_SET);
1271 #ifdef HAVE_HEADPHONE_DETECTION
1272 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1273 static void wvs_handle_phone_plug(bool inserted)
1275 if (rb->global_settings->unplug_mode == 0)
1276 return;
1278 /* Wait for any incomplete state transition to complete first */
1279 stream_wait_status();
1281 int status = wvs_status();
1283 if (inserted) {
1284 if (rb->global_settings->unplug_mode > 1) {
1285 if (status == STREAM_PAUSED) {
1286 backlight_force_on(rb);
1287 wvs_resume();
1290 } else {
1291 if (status == STREAM_PLAYING) {
1292 wvs_pause();
1293 backlight_use_settings(rb);
1295 if (stream_can_seek() && rb->global_settings->unplug_rw) {
1296 stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
1297 SEEK_CUR);
1298 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1299 /* Update time display now */
1300 wvs_update_time();
1301 wvs_refresh(WVS_REFRESH_TIME);
1306 #endif
1308 static void button_loop(void)
1310 rb->lcd_setfont(FONT_SYSFIXED);
1311 rb->lcd_clear_display();
1312 rb->lcd_update();
1314 /* Turn off backlight timeout */
1315 /* backlight control in lib/helper.c */
1316 backlight_force_on(rb);
1318 wvs_init();
1320 /* Start playback at the specified starting time */
1321 if (wvs_play(settings.resume_time) < STREAM_OK) {
1322 rb->splash(HZ*2, "Playback failed");
1323 return;
1326 /* Gently poll the video player for EOS and handle UI */
1327 while (stream_status() != STREAM_STOPPED)
1329 int button;
1331 mpeg_menu_sysevent_clear();
1332 button = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1334 button = mpeg_menu_sysevent_callback(button, -1);
1336 switch (button)
1338 case BUTTON_NONE:
1340 wvs_refresh(WVS_REFRESH_DEFAULT);
1341 continue;
1342 } /* BUTTON_NONE: */
1344 case MPEG_VOLUP:
1345 case MPEG_VOLUP|BUTTON_REPEAT:
1346 #ifdef MPEG_VOLUP2
1347 case MPEG_VOLUP2:
1348 case MPEG_VOLUP2|BUTTON_REPEAT:
1349 #endif
1350 #ifdef MPEG_RC_VOLUP
1351 case MPEG_RC_VOLUP:
1352 case MPEG_RC_VOLUP|BUTTON_REPEAT:
1353 #endif
1355 wvs_set_volume(+1);
1356 break;
1357 } /* MPEG_VOLUP*: */
1359 case MPEG_VOLDOWN:
1360 case MPEG_VOLDOWN|BUTTON_REPEAT:
1361 #ifdef MPEG_VOLDOWN2
1362 case MPEG_VOLDOWN2:
1363 case MPEG_VOLDOWN2|BUTTON_REPEAT:
1364 #endif
1365 #ifdef MPEG_RC_VOLDOWN
1366 case MPEG_RC_VOLDOWN:
1367 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
1368 #endif
1370 wvs_set_volume(-1);
1371 break;
1372 } /* MPEG_VOLDOWN*: */
1374 case MPEG_MENU:
1375 #ifdef MPEG_RC_MENU
1376 case MPEG_RC_MENU:
1377 #endif
1379 int state = wvs_halt(); /* save previous state */
1380 int result;
1382 /* Hide video output */
1383 wvs_show(WVS_HIDE | WVS_NODRAW);
1384 stream_show_vo(false);
1385 backlight_use_settings(rb);
1387 result = mpeg_menu(0);
1389 /* The menu can change the font, so restore */
1390 rb->lcd_setfont(FONT_SYSFIXED);
1392 switch (result)
1394 case MPEG_MENU_QUIT:
1395 wvs_stop();
1396 break;
1398 default:
1399 /* If not stopped, show video again */
1400 if (state != STREAM_STOPPED) {
1401 wvs_show(WVS_SHOW);
1402 stream_show_vo(true);
1405 /* If stream was playing, restart it */
1406 if (state == STREAM_PLAYING) {
1407 backlight_force_on(rb);
1408 wvs_resume();
1410 break;
1412 break;
1413 } /* MPEG_MENU: */
1415 case MPEG_STOP:
1416 #ifdef MPEG_RC_STOP
1417 case MPEG_RC_STOP:
1418 #endif
1419 case ACTION_STD_CANCEL:
1421 wvs_stop();
1422 break;
1423 } /* MPEG_STOP: */
1425 case MPEG_PAUSE:
1426 #ifdef MPEG_PAUSE2
1427 case MPEG_PAUSE2:
1428 #endif
1429 #ifdef MPEG_RC_PAUSE
1430 case MPEG_RC_PAUSE:
1431 #endif
1433 int status = wvs_status();
1435 if (status == STREAM_PLAYING) {
1436 /* Playing => Paused */
1437 wvs_pause();
1438 backlight_use_settings(rb);
1440 else if (status == STREAM_PAUSED) {
1441 /* Paused => Playing */
1442 backlight_force_on(rb);
1443 wvs_resume();
1446 break;
1447 } /* MPEG_PAUSE*: */
1449 case MPEG_RW:
1450 case MPEG_FF:
1451 #ifdef MPEG_RC_RW
1452 case MPEG_RC_RW:
1453 case MPEG_RC_FF:
1454 #endif
1456 wvs_seek(button);
1457 break;
1458 } /* MPEG_RW: MPEG_FF: */
1460 #ifdef HAVE_HEADPHONE_DETECTION
1461 case SYS_PHONE_PLUGGED:
1462 case SYS_PHONE_UNPLUGGED:
1464 wvs_handle_phone_plug(button == SYS_PHONE_PLUGGED);
1465 break;
1466 } /* SYS_PHONE_*: */
1467 #endif
1469 default:
1471 rb->default_event_handler(button);
1472 break;
1473 } /* default: */
1476 rb->yield();
1477 } /* end while */
1479 wvs_stop();
1481 rb->lcd_setfont(FONT_UI);
1483 /* Turn on backlight timeout (revert to settings) */
1484 backlight_use_settings(rb);
1487 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1489 int status = PLUGIN_ERROR; /* assume failure */
1490 int result;
1491 int err;
1492 const char *errstring;
1494 if (parameter == NULL) {
1495 /* No file = GTFO */
1496 api->splash(HZ*2, "No File");
1497 return PLUGIN_ERROR;
1500 /* Disable all talking before initializing IRAM */
1501 api->talk_disable(true);
1503 /* Initialize IRAM - stops audio and voice as well */
1504 PLUGIN_IRAM_INIT(api)
1506 rb = api;
1508 #ifdef HAVE_LCD_COLOR
1509 rb->lcd_set_backdrop(NULL);
1510 rb->lcd_set_foreground(LCD_WHITE);
1511 rb->lcd_set_background(LCD_BLACK);
1512 #endif
1514 rb->lcd_clear_display();
1515 rb->lcd_update();
1517 if (stream_init() < STREAM_OK) {
1518 DEBUGF("Could not initialize streams\n");
1519 } else {
1520 rb->splash(0, "Loading...");
1521 init_settings((char*)parameter);
1523 err = stream_open((char *)parameter);
1525 if (err >= STREAM_OK) {
1526 /* start menu */
1527 rb->lcd_clear_display();
1528 rb->lcd_update();
1529 result = mpeg_start_menu(stream_get_duration());
1531 if (result != MPEG_START_QUIT) {
1532 /* Enter button loop and process UI */
1533 button_loop();
1536 stream_close();
1538 rb->lcd_clear_display();
1539 rb->lcd_update();
1541 save_settings(); /* Save settings (if they have changed) */
1542 status = PLUGIN_OK;
1544 mpeg_menu_sysevent_handle();
1545 } else {
1546 DEBUGF("Could not open %s\n", (char*)parameter);
1547 switch (err)
1549 case STREAM_UNSUPPORTED:
1550 errstring = "Unsupported format";
1551 break;
1552 default:
1553 errstring = "Error opening file: %d";
1556 rb->splash(HZ*2, errstring, err);
1560 stream_exit();
1562 rb->talk_disable(false);
1563 return status;