Rearange menu of mpegplayer. Add new menu with "settings" and "quit", and remove...
[kugel-rb.git] / apps / plugins / mpegplayer / mpegplayer.c
bloba66a588ace1846890e40bbd2945f8a5f4b93175f
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 "lib/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
173 #define MPEG_RC_MENU BUTTON_RC_DSP
174 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
175 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
176 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
177 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
178 #define MPEG_RC_RW BUTTON_RC_REW
179 #define MPEG_RC_FF BUTTON_RC_FF
181 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
182 #define MPEG_MENU BUTTON_LEFT
183 #define MPEG_STOP BUTTON_POWER
184 #define MPEG_PAUSE BUTTON_PLAY
185 #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
186 #define MPEG_VOLUP BUTTON_SCROLL_UP
187 #define MPEG_RW BUTTON_REW
188 #define MPEG_FF BUTTON_FF
190 #elif CONFIG_KEYPAD == SANSA_E200_PAD
191 #define MPEG_MENU BUTTON_SELECT
192 #define MPEG_STOP BUTTON_POWER
193 #define MPEG_PAUSE BUTTON_RIGHT
194 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
195 #define MPEG_VOLUP BUTTON_SCROLL_FWD
196 #define MPEG_RW BUTTON_UP
197 #define MPEG_FF BUTTON_DOWN
199 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
200 #define MPEG_MENU BUTTON_SELECT
201 #define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT)
202 #define MPEG_PAUSE BUTTON_UP
203 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
204 #define MPEG_VOLUP BUTTON_SCROLL_FWD
205 #define MPEG_RW BUTTON_LEFT
206 #define MPEG_FF BUTTON_RIGHT
209 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
210 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
211 CONFIG_KEYPAD == SANSA_M200_PAD
212 #define MPEG_MENU BUTTON_SELECT
213 #define MPEG_STOP BUTTON_POWER
214 #define MPEG_PAUSE BUTTON_UP
215 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
216 #define MPEG_VOLUP BUTTON_VOL_UP
217 #define MPEG_RW BUTTON_LEFT
218 #define MPEG_FF BUTTON_RIGHT
220 #elif CONFIG_KEYPAD == MROBE500_PAD
221 #define MPEG_STOP BUTTON_POWER
223 #define MPEG_RC_MENU BUTTON_RC_HEART
224 #define MPEG_RC_STOP BUTTON_RC_DOWN
225 #define MPEG_RC_PAUSE BUTTON_RC_PLAY
226 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
227 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
228 #define MPEG_RC_RW BUTTON_RC_REW
229 #define MPEG_RC_FF BUTTON_RC_FF
231 #elif CONFIG_KEYPAD == MROBE100_PAD
232 #define MPEG_MENU BUTTON_MENU
233 #define MPEG_STOP BUTTON_POWER
234 #define MPEG_PAUSE BUTTON_PLAY
235 #define MPEG_VOLDOWN BUTTON_DOWN
236 #define MPEG_VOLUP BUTTON_UP
237 #define MPEG_RW BUTTON_LEFT
238 #define MPEG_FF BUTTON_RIGHT
240 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
241 #define MPEG_MENU BUTTON_RC_MENU
242 #define MPEG_STOP BUTTON_RC_REC
243 #define MPEG_PAUSE BUTTON_RC_PLAY
244 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
245 #define MPEG_VOLUP BUTTON_RC_VOL_UP
246 #define MPEG_RW BUTTON_RC_REW
247 #define MPEG_FF BUTTON_RC_FF
249 #elif CONFIG_KEYPAD == COWOND2_PAD
250 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
251 //#define MPEG_STOP BUTTON_POWER
252 #define MPEG_VOLDOWN BUTTON_MINUS
253 #define MPEG_VOLUP BUTTON_PLUS
255 #elif CONFIG_KEYPAD == IAUDIO67_PAD
256 #define MPEG_MENU BUTTON_MENU
257 #define MPEG_STOP BUTTON_STOP
258 #define MPEG_PAUSE BUTTON_PLAY
259 #define MPEG_VOLDOWN BUTTON_VOLDOWN
260 #define MPEG_VOLUP BUTTON_VOLUP
261 #define MPEG_RW BUTTON_LEFT
262 #define MPEG_FF BUTTON_RIGHT
264 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
265 #define MPEG_MENU BUTTON_MENU
266 #define MPEG_STOP BUTTON_BACK
267 #define MPEG_PAUSE BUTTON_PLAY
268 #define MPEG_VOLDOWN BUTTON_UP
269 #define MPEG_VOLUP BUTTON_DOWN
270 #define MPEG_RW BUTTON_LEFT
271 #define MPEG_FF BUTTON_RIGHT
273 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
274 #define MPEG_MENU BUTTON_MENU
275 #define MPEG_STOP BUTTON_POWER
276 #define MPEG_PAUSE BUTTON_SELECT
277 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
278 #define MPEG_VOLUP BUTTON_VOL_UP
279 #define MPEG_RW BUTTON_LEFT
280 #define MPEG_FF BUTTON_RIGHT
282 #elif CONFIG_KEYPAD == ONDAVX747_PAD
283 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
284 //#define MPEG_STOP BUTTON_POWER
285 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
286 #define MPEG_VOLUP BUTTON_VOL_UP
288 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
289 #define MPEG_MENU BUTTON_LEFT
290 #define MPEG_STOP BUTTON_RIGHT
291 #define MPEG_PAUSE BUTTON_PLAY
292 #define MPEG_VOLDOWN BUTTON_DOWN
293 #define MPEG_VOLUP BUTTON_UP
294 #define MPEG_RW BUTTON_REW
295 #define MPEG_FF BUTTON_FFWD
297 #else
298 #error No keymap defined!
299 #endif
301 #ifdef HAVE_TOUCHSCREEN
302 #ifndef MPEG_MENU
303 #define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
304 #endif
305 #ifndef MPEG_STOP
306 #define MPEG_STOP BUTTON_TOPLEFT
307 #endif
308 #ifndef MPEG_PAUSE
309 #define MPEG_PAUSE BUTTON_CENTER
310 #endif
311 #ifndef MPEG_VOLDOWN
312 #define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
313 #endif
314 #ifndef MPEG_VOLUP
315 #define MPEG_VOLUP BUTTON_TOPMIDDLE
316 #endif
317 #ifndef MPEG_RW
318 #define MPEG_RW BUTTON_MIDLEFT
319 #endif
320 #ifndef MPEG_FF
321 #define MPEG_FF BUTTON_MIDRIGHT
322 #endif
323 #endif
325 /* One thing we can do here for targets with remotes is having a display
326 * always on the remote instead of always forcing a popup on the main display */
328 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
329 /* 3% of 30min file == 54s step size */
330 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
331 #define WVS_MIN_UPDATE_INTERVAL (HZ/2)
333 /* WVS status - same order as icon array */
334 enum wvs_status_enum
336 WVS_STATUS_STOPPED = 0,
337 WVS_STATUS_PAUSED,
338 WVS_STATUS_PLAYING,
339 WVS_STATUS_FF,
340 WVS_STATUS_RW,
341 WVS_STATUS_COUNT,
342 WVS_STATUS_MASK = 0x7
345 enum wvs_bits
347 WVS_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
348 /* Refresh the... */
349 WVS_REFRESH_VOLUME = 0x0001, /* ...volume display */
350 WVS_REFRESH_TIME = 0x0002, /* ...time display+progress */
351 WVS_REFRESH_STATUS = 0x0004, /* ...playback status icon */
352 WVS_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
353 WVS_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
354 WVS_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
355 WVS_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
356 WVS_SHOW = 0x4000, /* OR bitflag - show the WVS */
357 WVS_HP_PAUSE = 0x2000,
358 WVS_HIDE = 0x0000, /* hide the WVS (aid readability) */
359 WVS_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
362 /* Status icons selected according to font height */
363 extern const unsigned char mpegplayer_status_icons_8x8x1[];
364 extern const unsigned char mpegplayer_status_icons_12x12x1[];
365 extern const unsigned char mpegplayer_status_icons_16x16x1[];
367 /* Main border areas that contain WVS elements */
368 #define WVS_BDR_L 2
369 #define WVS_BDR_T 2
370 #define WVS_BDR_R 2
371 #define WVS_BDR_B 2
373 struct wvs
375 long hide_tick;
376 long show_for;
377 long print_tick;
378 long print_delay;
379 long resume_tick;
380 long resume_delay;
381 long next_auto_refresh;
382 int x;
383 int y;
384 int width;
385 int height;
386 unsigned fgcolor;
387 unsigned bgcolor;
388 unsigned prog_fillcolor;
389 struct vo_rect update_rect;
390 struct vo_rect prog_rect;
391 struct vo_rect time_rect;
392 struct vo_rect dur_rect;
393 struct vo_rect vol_rect;
394 const unsigned char *icons;
395 struct vo_rect stat_rect;
396 int status;
397 uint32_t curr_time;
398 unsigned auto_refresh;
399 unsigned flags;
402 static struct wvs wvs;
404 static void wvs_show(unsigned show);
406 #ifdef LCD_LANDSCAPE
407 #define _X (x + wvs.x)
408 #define _Y (y + wvs.y)
409 #define _W width
410 #define _H height
411 #else
412 #define _X (LCD_WIDTH - (y + wvs.y) - height)
413 #define _Y (x + wvs.x)
414 #define _W height
415 #define _H width
416 #endif
418 #ifdef HAVE_LCD_COLOR
419 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
420 static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
422 int r1 = RGB_UNPACK_RED(c1);
423 int g1 = RGB_UNPACK_GREEN(c1);
424 int b1 = RGB_UNPACK_BLUE(c1);
426 int r2 = RGB_UNPACK_RED(c2);
427 int g2 = RGB_UNPACK_GREEN(c2);
428 int b2 = RGB_UNPACK_BLUE(c2);
430 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
431 amount*(g2 - g1) / 255 + g1,
432 amount*(b2 - b1) / 255 + b1);
434 #endif
436 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
437 * most are just wrappers of lcd_* functions with transforms applied.
438 * The origin is the upper-left corner of the WVS area */
439 static void draw_update_rect(int x, int y, int width, int height)
441 lcd_(update_rect)(_X, _Y, _W, _H);
444 static void draw_clear_area(int x, int y, int width, int height)
446 #ifdef HAVE_LCD_COLOR
447 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
448 #else
449 int oldmode = grey_get_drawmode();
450 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
451 grey_fillrect(_X, _Y, _W, _H);
452 grey_set_drawmode(oldmode);
453 #endif
456 static void draw_clear_area_rect(const struct vo_rect *rc)
458 int x = rc->l;
459 int y = rc->t;
460 int width = rc->r - rc->l;
461 int height = rc->b - rc->t;
462 #ifdef HAVE_LCD_COLOR
463 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
464 #else
465 int oldmode = grey_get_drawmode();
466 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
467 grey_fillrect(_X, _Y, _W, _H);
468 grey_set_drawmode(oldmode);
469 #endif
472 static void draw_fillrect(int x, int y, int width, int height)
474 lcd_(fillrect)(_X, _Y, _W, _H);
477 static void draw_hline(int x1, int x2, int y)
479 #ifdef LCD_LANDSCAPE
480 lcd_(hline)(x1 + wvs.x, x2 + wvs.x, y + wvs.y);
481 #else
482 y = LCD_WIDTH - (y + wvs.y) - 1;
483 lcd_(vline)(y, x1 + wvs.x, x2 + wvs.x);
484 #endif
487 static void draw_vline(int x, int y1, int y2)
489 #ifdef LCD_LANDSCAPE
490 lcd_(vline)(x + wvs.x, y1 + wvs.y, y2 + wvs.y);
491 #else
492 y1 = LCD_WIDTH - (y1 + wvs.y) - 1;
493 y2 = LCD_WIDTH - (y2 + wvs.y) - 1;
494 lcd_(hline)(y1, y2, x + wvs.x);
495 #endif
498 static void draw_scrollbar_draw(int x, int y, int width, int height,
499 uint32_t min, uint32_t max, uint32_t val)
501 unsigned oldfg = lcd_(get_foreground)();
503 draw_hline(x + 1, x + width - 2, y);
504 draw_hline(x + 1, x + width - 2, y + height - 1);
505 draw_vline(x, y + 1, y + height - 2);
506 draw_vline(x + width - 1, y + 1, y + height - 2);
508 val = muldiv_uint32(width - 2, val, max - min);
509 val = MIN(val, (uint32_t)(width - 2));
511 draw_fillrect(x + 1, y + 1, val, height - 2);
513 lcd_(set_foreground)(wvs.prog_fillcolor);
515 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
517 lcd_(set_foreground)(oldfg);
520 static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
521 int max, int val)
523 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
524 min, max, val);
527 #ifdef LCD_PORTRAIT
528 /* Portrait displays need rotated text rendering */
530 /* Limited function that only renders in DRMODE_FG and uses absolute screen
531 * coordinates */
532 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
533 int src_x, int src_y,
534 int stride, int x, int y,
535 int width, int height)
537 const unsigned char *src_end;
538 fb_data *dst, *dst_end;
539 unsigned fg_pattern, bg_pattern;
541 if (x + width > SCREEN_WIDTH)
542 width = SCREEN_WIDTH - x; /* Clip right */
543 if (x < 0)
544 width += x, x = 0; /* Clip left */
545 if (width <= 0)
546 return; /* nothing left to do */
548 if (y + height > SCREEN_HEIGHT)
549 height = SCREEN_HEIGHT - y; /* Clip bottom */
550 if (y < 0)
551 height += y, y = 0; /* Clip top */
552 if (height <= 0)
553 return; /* nothing left to do */
555 fg_pattern = rb->lcd_get_foreground();
556 bg_pattern = rb->lcd_get_background();
558 src += stride * (src_y >> 3) + src_x; /* move starting point */
559 src_y &= 7;
560 src_end = src + width;
562 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
565 const unsigned char *src_col = src++;
566 unsigned data = *src_col >> src_y;
567 int numbits = 8 - src_y;
569 fb_data *dst_col = dst;
570 dst_end = dst_col - height;
571 dst += LCD_WIDTH;
575 dst_col--;
577 if (data & 1)
578 *dst_col = fg_pattern;
579 #if 0
580 else
581 *dst_col = bg_pattern;
582 #endif
583 data >>= 1;
584 if (--numbits == 0) {
585 src_col += stride;
586 data = *src_col;
587 numbits = 8;
590 while (dst_col > dst_end);
592 while (src < src_end);
595 static void draw_putsxy_oriented(int x, int y, const char *str)
597 unsigned short ch;
598 unsigned short *ucs;
599 int ofs = MIN(x, 0);
600 struct font* pf = rb->font_get(FONT_UI);
602 ucs = rb->bidi_l2v(str, 1);
604 x += wvs.x;
605 y += wvs.y;
607 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
609 int width;
610 const unsigned char *bits;
612 /* get proportional width and glyph bits */
613 width = rb->font_get_width(pf, ch);
615 if (ofs > width) {
616 ofs -= width;
617 continue;
620 bits = rb->font_get_bits(pf, ch);
622 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
623 width - ofs, pf->height);
625 x += width - ofs;
626 ofs = 0;
629 #else
630 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
631 int src_x, int src_y,
632 int stride, int x, int y,
633 int width, int height)
635 int mode = lcd_(get_drawmode)();
636 lcd_(set_drawmode)(DRMODE_FG);
637 lcd_(mono_bitmap_part)(src, src_x, src_y, stride, x, y, width, height);
638 lcd_(set_drawmode)(mode);
641 static void draw_putsxy_oriented(int x, int y, const char *str)
643 int mode = lcd_(get_drawmode)();
644 lcd_(set_drawmode)(DRMODE_FG);
645 lcd_(putsxy)(x + wvs.x, y + wvs.y, str);
646 lcd_(set_drawmode)(mode);
648 #endif /* LCD_PORTRAIT */
650 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
651 /* So we can refresh the overlay */
652 static void wvs_lcd_enable_hook(void)
654 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
656 #endif
658 static void wvs_backlight_on_video_mode(bool video_on)
660 if (video_on) {
661 /* Turn off backlight timeout */
662 /* backlight control in lib/helper.c */
663 backlight_force_on();
664 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
665 rb->lcd_activation_set_hook(NULL);
666 #endif
667 } else {
668 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
669 rb->lcd_activation_set_hook(wvs_lcd_enable_hook);
670 #endif
671 /* Revert to user's backlight settings */
672 backlight_use_settings();
676 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
677 static void wvs_backlight_brightness_video_mode(bool video_on)
679 if (settings.backlight_brightness < 0)
680 return;
682 mpeg_backlight_update_brightness(
683 video_on ? settings.backlight_brightness : -1);
685 #else
686 #define wvs_backlight_brightness_video_mode(video_on)
687 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
689 static void wvs_text_init(void)
691 struct hms hms;
692 char buf[32];
693 int phys;
694 int spc_width;
696 lcd_(setfont)(FONT_UI);
698 wvs.x = 0;
699 wvs.width = SCREEN_WIDTH;
701 vo_rect_clear(&wvs.time_rect);
702 vo_rect_clear(&wvs.stat_rect);
703 vo_rect_clear(&wvs.prog_rect);
704 vo_rect_clear(&wvs.vol_rect);
706 ts_to_hms(stream_get_duration(), &hms);
707 hms_format(buf, sizeof (buf), &hms);
708 lcd_(getstringsize)(buf, &wvs.time_rect.r, &wvs.time_rect.b);
710 /* Choose well-sized bitmap images relative to font height */
711 if (wvs.time_rect.b < 12) {
712 wvs.icons = mpegplayer_status_icons_8x8x1;
713 wvs.stat_rect.r = wvs.stat_rect.b = 8;
714 } else if (wvs.time_rect.b < 16) {
715 wvs.icons = mpegplayer_status_icons_12x12x1;
716 wvs.stat_rect.r = wvs.stat_rect.b = 12;
717 } else {
718 wvs.icons = mpegplayer_status_icons_16x16x1;
719 wvs.stat_rect.r = wvs.stat_rect.b = 16;
722 if (wvs.stat_rect.b < wvs.time_rect.b) {
723 vo_rect_offset(&wvs.stat_rect, 0,
724 (wvs.time_rect.b - wvs.stat_rect.b) / 2 + WVS_BDR_T);
725 vo_rect_offset(&wvs.time_rect, WVS_BDR_L, WVS_BDR_T);
726 } else {
727 vo_rect_offset(&wvs.time_rect, WVS_BDR_L,
728 wvs.stat_rect.b - wvs.time_rect.b + WVS_BDR_T);
729 vo_rect_offset(&wvs.stat_rect, 0, WVS_BDR_T);
732 wvs.dur_rect = wvs.time_rect;
734 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
735 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
736 rb->sound_unit(SOUND_VOLUME));
738 lcd_(getstringsize)(" ", &spc_width, NULL);
739 lcd_(getstringsize)(buf, &wvs.vol_rect.r, &wvs.vol_rect.b);
741 wvs.prog_rect.r = SCREEN_WIDTH - WVS_BDR_L - spc_width -
742 wvs.vol_rect.r - WVS_BDR_R;
743 wvs.prog_rect.b = 3*wvs.stat_rect.b / 4;
744 vo_rect_offset(&wvs.prog_rect, wvs.time_rect.l,
745 wvs.time_rect.b);
747 vo_rect_offset(&wvs.stat_rect,
748 (wvs.prog_rect.r + wvs.prog_rect.l - wvs.stat_rect.r) / 2,
751 vo_rect_offset(&wvs.dur_rect,
752 wvs.prog_rect.r - wvs.dur_rect.r, 0);
754 vo_rect_offset(&wvs.vol_rect, wvs.prog_rect.r + spc_width,
755 (wvs.prog_rect.b + wvs.prog_rect.t - wvs.vol_rect.b) / 2);
757 wvs.height = WVS_BDR_T + MAX(wvs.prog_rect.b, wvs.vol_rect.b) -
758 MIN(wvs.time_rect.t, wvs.stat_rect.t) + WVS_BDR_B;
760 #ifdef HAVE_LCD_COLOR
761 wvs.height = ALIGN_UP(wvs.height, 2);
762 #endif
763 wvs.y = SCREEN_HEIGHT - wvs.height;
765 lcd_(setfont)(FONT_SYSFIXED);
768 static void wvs_init(void)
770 wvs.flags = 0;
771 wvs.show_for = HZ*4;
772 wvs.print_delay = 75*HZ/100;
773 wvs.resume_delay = HZ/2;
774 #ifdef HAVE_LCD_COLOR
775 wvs.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
776 wvs.fgcolor = LCD_WHITE;
777 wvs.prog_fillcolor = LCD_BLACK;
778 #else
779 wvs.bgcolor = GREY_LIGHTGRAY;
780 wvs.fgcolor = GREY_BLACK;
781 wvs.prog_fillcolor = GREY_WHITE;
782 #endif
783 wvs.curr_time = 0;
784 wvs.status = WVS_STATUS_STOPPED;
785 wvs.auto_refresh = WVS_REFRESH_TIME;
786 wvs.next_auto_refresh = *rb->current_tick;
787 wvs_text_init();
790 static void wvs_schedule_refresh(unsigned refresh)
792 long tick = *rb->current_tick;
794 if (refresh & WVS_REFRESH_VIDEO)
795 wvs.print_tick = tick + wvs.print_delay;
797 if (refresh & WVS_REFRESH_RESUME)
798 wvs.resume_tick = tick + wvs.resume_delay;
800 wvs.auto_refresh |= refresh;
803 static void wvs_cancel_refresh(unsigned refresh)
805 wvs.auto_refresh &= ~refresh;
808 /* Refresh the background area */
809 static void wvs_refresh_background(void)
811 char buf[32];
812 struct hms hms;
814 unsigned bg = lcd_(get_background)();
815 lcd_(set_drawmode)(DRMODE_SOLID | DRMODE_INVERSEVID);
817 #ifdef HAVE_LCD_COLOR
818 /* Draw a "raised" area for our graphics */
819 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 192));
820 draw_hline(0, wvs.width, 0);
822 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 80));
823 draw_hline(0, wvs.width, 1);
825 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 48));
826 draw_hline(0, wvs.width, wvs.height-2);
828 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 128));
829 draw_hline(0, wvs.width, wvs.height-1);
831 lcd_(set_background)(bg);
832 draw_clear_area(0, 2, wvs.width, wvs.height - 4);
833 #else
834 /* Give contrast with the main background */
835 lcd_(set_background)(GREY_WHITE);
836 draw_hline(0, wvs.width, 0);
838 lcd_(set_background)(GREY_DARKGRAY);
839 draw_hline(0, wvs.width, wvs.height-1);
841 lcd_(set_background)(bg);
842 draw_clear_area(0, 1, wvs.width, wvs.height - 2);
843 #endif
845 vo_rect_set_ext(&wvs.update_rect, 0, 0, wvs.width, wvs.height);
846 lcd_(set_drawmode)(DRMODE_SOLID);
848 if (stream_get_duration() != INVALID_TIMESTAMP) {
849 /* Draw the movie duration */
850 ts_to_hms(stream_get_duration(), &hms);
851 hms_format(buf, sizeof (buf), &hms);
852 draw_putsxy_oriented(wvs.dur_rect.l, wvs.dur_rect.t, buf);
854 /* else don't know the duration */
857 /* Refresh the current time display + the progress bar */
858 static void wvs_refresh_time(void)
860 char buf[32];
861 struct hms hms;
863 uint32_t duration = stream_get_duration();
865 draw_scrollbar_draw_rect(&wvs.prog_rect, 0, duration,
866 wvs.curr_time);
868 ts_to_hms(wvs.curr_time, &hms);
869 hms_format(buf, sizeof (buf), &hms);
871 draw_clear_area_rect(&wvs.time_rect);
872 draw_putsxy_oriented(wvs.time_rect.l, wvs.time_rect.t, buf);
874 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
875 &wvs.prog_rect);
876 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
877 &wvs.time_rect);
880 /* Refresh the volume display area */
881 static void wvs_refresh_volume(void)
883 char buf[32];
884 int width;
886 int volume = rb->global_settings->volume;
887 rb->snprintf(buf, sizeof (buf), "%d%s",
888 rb->sound_val2phys(SOUND_VOLUME, volume),
889 rb->sound_unit(SOUND_VOLUME));
890 lcd_(getstringsize)(buf, &width, NULL);
892 /* Right-justified */
893 draw_clear_area_rect(&wvs.vol_rect);
894 draw_putsxy_oriented(wvs.vol_rect.r - width, wvs.vol_rect.t, buf);
896 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.vol_rect);
899 /* Refresh the status icon */
900 static void wvs_refresh_status(void)
902 int icon_size = wvs.stat_rect.r - wvs.stat_rect.l;
904 draw_clear_area_rect(&wvs.stat_rect);
906 #ifdef HAVE_LCD_COLOR
907 /* Draw status icon with a drop shadow */
908 unsigned oldfg = lcd_(get_foreground)();
909 int i = 1;
911 lcd_(set_foreground)(draw_blendcolor(lcd_(get_background)(),
912 DRAW_BLACK, 96));
914 while (1)
916 draw_oriented_mono_bitmap_part(wvs.icons,
917 icon_size*wvs.status,
919 icon_size*WVS_STATUS_COUNT,
920 wvs.stat_rect.l + wvs.x + i,
921 wvs.stat_rect.t + wvs.y + i,
922 icon_size, icon_size);
924 if (--i < 0)
925 break;
927 lcd_(set_foreground)(oldfg);
930 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
931 #else
932 draw_oriented_mono_bitmap_part(wvs.icons,
933 icon_size*wvs.status,
935 icon_size*WVS_STATUS_COUNT,
936 wvs.stat_rect.l + wvs.x,
937 wvs.stat_rect.t + wvs.y,
938 icon_size, icon_size);
939 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
940 #endif
943 /* Update the current status which determines which icon is displayed */
944 static bool wvs_update_status(void)
946 int status;
948 switch (stream_status())
950 default:
951 status = WVS_STATUS_STOPPED;
952 break;
953 case STREAM_PAUSED:
954 /* If paused with a pending resume, coerce it to WVS_STATUS_PLAYING */
955 status = (wvs.auto_refresh & WVS_REFRESH_RESUME) ?
956 WVS_STATUS_PLAYING : WVS_STATUS_PAUSED;
957 break;
958 case STREAM_PLAYING:
959 status = WVS_STATUS_PLAYING;
960 break;
963 if (status != wvs.status) {
964 /* A refresh is needed */
965 wvs.status = status;
966 return true;
969 return false;
972 /* Update the current time that will be displayed */
973 static void wvs_update_time(void)
975 uint32_t start;
976 wvs.curr_time = stream_get_seek_time(&start);
977 wvs.curr_time -= start;
980 /* Refresh various parts of the WVS - showing it if it is hidden */
981 static void wvs_refresh(int hint)
983 long tick;
984 unsigned oldbg, oldfg;
986 tick = *rb->current_tick;
988 if (hint == WVS_REFRESH_DEFAULT) {
989 /* The default which forces no updates */
991 /* Make sure Rockbox doesn't turn off the player because of
992 too little activity */
993 if (wvs.status == WVS_STATUS_PLAYING)
994 rb->reset_poweroff_timer();
996 /* Redraw the current or possibly extract a new video frame */
997 if ((wvs.auto_refresh & WVS_REFRESH_VIDEO) &&
998 TIME_AFTER(tick, wvs.print_tick)) {
999 wvs.auto_refresh &= ~WVS_REFRESH_VIDEO;
1000 stream_draw_frame(false);
1003 /* Restart playback if the timout was reached */
1004 if ((wvs.auto_refresh & WVS_REFRESH_RESUME) &&
1005 TIME_AFTER(tick, wvs.resume_tick)) {
1006 wvs.auto_refresh &= ~(WVS_REFRESH_RESUME | WVS_REFRESH_VIDEO);
1007 stream_resume();
1010 /* If not visible, return */
1011 if (!(wvs.flags & WVS_SHOW))
1012 return;
1014 /* Hide if the visibility duration was reached */
1015 if (TIME_AFTER(tick, wvs.hide_tick)) {
1016 wvs_show(WVS_HIDE);
1017 return;
1019 } else {
1020 /* A forced update of some region */
1022 /* Show if currently invisible */
1023 if (!(wvs.flags & WVS_SHOW)) {
1024 /* Avoid call back into this function - it will be drawn */
1025 wvs_show(WVS_SHOW | WVS_NODRAW);
1026 hint = WVS_REFRESH_ALL;
1029 /* Move back timeouts for frame print and hide */
1030 wvs.print_tick = tick + wvs.print_delay;
1031 wvs.hide_tick = tick + wvs.show_for;
1034 if (TIME_AFTER(tick, wvs.next_auto_refresh)) {
1035 /* Refresh whatever graphical elements are due automatically */
1036 wvs.next_auto_refresh = tick + WVS_MIN_UPDATE_INTERVAL;
1038 if (wvs.auto_refresh & WVS_REFRESH_STATUS) {
1039 if (wvs_update_status())
1040 hint |= WVS_REFRESH_STATUS;
1043 if (wvs.auto_refresh & WVS_REFRESH_TIME) {
1044 wvs_update_time();
1045 hint |= WVS_REFRESH_TIME;
1049 if (hint == 0)
1050 return; /* No drawing needed */
1052 /* Set basic drawing params that are used. Elements that perform variations
1053 * will restore them. */
1054 oldfg = lcd_(get_foreground)();
1055 oldbg = lcd_(get_background)();
1057 lcd_(setfont)(FONT_UI);
1058 lcd_(set_foreground)(wvs.fgcolor);
1059 lcd_(set_background)(wvs.bgcolor);
1061 vo_rect_clear(&wvs.update_rect);
1063 if (hint & WVS_REFRESH_BACKGROUND) {
1064 wvs_refresh_background();
1065 hint |= WVS_REFRESH_ALL; /* Requires a redraw of everything */
1068 if (hint & WVS_REFRESH_TIME) {
1069 wvs_refresh_time();
1072 if (hint & WVS_REFRESH_VOLUME) {
1073 wvs_refresh_volume();
1076 if (hint & WVS_REFRESH_STATUS) {
1077 wvs_refresh_status();
1080 /* Go back to defaults */
1081 lcd_(setfont)(FONT_SYSFIXED);
1082 lcd_(set_foreground)(oldfg);
1083 lcd_(set_background)(oldbg);
1085 /* Update the dirty rectangle */
1086 vo_lock();
1088 draw_update_rect(wvs.update_rect.l,
1089 wvs.update_rect.t,
1090 wvs.update_rect.r - wvs.update_rect.l,
1091 wvs.update_rect.b - wvs.update_rect.t);
1093 vo_unlock();
1096 /* Show/Hide the WVS */
1097 static void wvs_show(unsigned show)
1099 if (((show ^ wvs.flags) & WVS_SHOW) == 0)
1101 if (show & WVS_SHOW) {
1102 wvs.hide_tick = *rb->current_tick + wvs.show_for;
1104 return;
1107 if (show & WVS_SHOW) {
1108 /* Clip away the part of video that is covered */
1109 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, wvs.y };
1111 wvs.flags |= WVS_SHOW;
1113 if (wvs.status != WVS_STATUS_PLAYING) {
1114 /* Not playing - set brightness to mpegplayer setting */
1115 wvs_backlight_brightness_video_mode(true);
1118 stream_vo_set_clip(&rc);
1120 if (!(show & WVS_NODRAW))
1121 wvs_refresh(WVS_REFRESH_ALL);
1122 } else {
1123 /* Uncover clipped video area and redraw it */
1124 wvs.flags &= ~WVS_SHOW;
1126 draw_clear_area(0, 0, wvs.width, wvs.height);
1128 if (!(show & WVS_NODRAW)) {
1129 vo_lock();
1130 draw_update_rect(0, 0, wvs.width, wvs.height);
1131 vo_unlock();
1133 stream_vo_set_clip(NULL);
1134 stream_draw_frame(false);
1135 } else {
1136 stream_vo_set_clip(NULL);
1139 if (wvs.status != WVS_STATUS_PLAYING) {
1140 /* Not playing - restore backlight brightness */
1141 wvs_backlight_brightness_video_mode(false);
1146 /* Set the current status - update screen if specified */
1147 static void wvs_set_status(int status)
1149 bool draw = (status & WVS_NODRAW) == 0;
1151 status &= WVS_STATUS_MASK;
1153 if (wvs.status != status) {
1155 wvs.status = status;
1157 if (draw)
1158 wvs_refresh(WVS_REFRESH_STATUS);
1162 /* Get the current status value */
1163 static int wvs_get_status(void)
1165 return wvs.status & WVS_STATUS_MASK;
1168 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
1169 static uint32_t wvs_ff_rw(int btn, unsigned refresh)
1171 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1172 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1173 uint32_t start;
1174 uint32_t time = stream_get_seek_time(&start);
1175 const uint32_t duration = stream_get_duration();
1176 unsigned int max_step = 0;
1177 uint32_t ff_rw_count = 0;
1178 unsigned status = wvs.status;
1180 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME |
1181 WVS_REFRESH_TIME);
1183 time -= start; /* Absolute clock => stream-relative */
1185 switch (btn)
1187 case MPEG_FF:
1188 #ifdef MPEG_RC_FF
1189 case MPEG_RC_FF:
1190 #endif
1191 wvs_set_status(WVS_STATUS_FF);
1192 break;
1193 case MPEG_RW:
1194 #ifdef MPEG_RC_RW
1195 case MPEG_RC_RW:
1196 #endif
1197 wvs_set_status(WVS_STATUS_RW);
1198 break;
1199 default:
1200 btn = -1;
1203 btn |= BUTTON_REPEAT;
1205 while (1)
1207 stream_keep_disk_active();
1209 switch (btn)
1211 case BUTTON_NONE:
1212 wvs_refresh(WVS_REFRESH_DEFAULT);
1213 break;
1215 case MPEG_FF | BUTTON_REPEAT:
1216 case MPEG_RW | BUTTON_REPEAT:
1217 #ifdef MPEG_RC_FF
1218 case MPEG_RC_FF | BUTTON_REPEAT:
1219 case MPEG_RC_RW | BUTTON_REPEAT:
1220 #endif
1221 break;
1223 case MPEG_FF | BUTTON_REL:
1224 case MPEG_RW | BUTTON_REL:
1225 #ifdef MPEG_RC_FF
1226 case MPEG_RC_FF | BUTTON_REL:
1227 case MPEG_RC_RW | BUTTON_REL:
1228 #endif
1229 if (wvs.status == WVS_STATUS_FF)
1230 time += ff_rw_count;
1231 else if (wvs.status == WVS_STATUS_RW)
1232 time -= ff_rw_count;
1234 /* Fall-through */
1235 case -1:
1236 default:
1237 wvs_schedule_refresh(refresh);
1238 wvs_set_status(status);
1239 wvs_schedule_refresh(WVS_REFRESH_TIME);
1240 return time;
1243 if (wvs.status == WVS_STATUS_FF) {
1244 /* fast forwarding, calc max step relative to end */
1245 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1246 FF_REWIND_MAX_PERCENT, 100);
1247 } else {
1248 /* rewinding, calc max step relative to start */
1249 max_step = muldiv_uint32(time - ff_rw_count,
1250 FF_REWIND_MAX_PERCENT, 100);
1253 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1255 if (step > max_step)
1256 step = max_step;
1258 ff_rw_count += step;
1260 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1261 step += step >> ff_rw_accel;
1263 if (wvs.status == WVS_STATUS_FF) {
1264 if (duration - time <= ff_rw_count)
1265 ff_rw_count = duration - time;
1267 wvs.curr_time = time + ff_rw_count;
1268 } else {
1269 if (time <= ff_rw_count)
1270 ff_rw_count = time;
1272 wvs.curr_time = time - ff_rw_count;
1275 wvs_refresh(WVS_REFRESH_TIME);
1277 btn = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1281 static int wvs_status(void)
1283 int status = stream_status();
1285 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1286 if (status == STREAM_PAUSED) {
1287 if (wvs.auto_refresh & WVS_REFRESH_RESUME)
1288 status = STREAM_PLAYING;
1291 return status;
1294 /* Change the current audio volume by a specified amount */
1295 static void wvs_set_volume(int delta)
1297 int vol = rb->global_settings->volume;
1298 int limit;
1300 vol += delta;
1302 if (delta < 0) {
1303 /* Volume down - clip to lower limit */
1304 limit = rb->sound_min(SOUND_VOLUME);
1305 if (vol < limit)
1306 vol = limit;
1307 } else {
1308 /* Volume up - clip to upper limit */
1309 limit = rb->sound_max(SOUND_VOLUME);
1310 if (vol > limit)
1311 vol = limit;
1314 /* Sync the global settings */
1315 if (vol != rb->global_settings->volume) {
1316 rb->sound_set(SOUND_VOLUME, vol);
1317 rb->global_settings->volume = vol;
1320 /* Update the volume display */
1321 wvs_refresh(WVS_REFRESH_VOLUME);
1324 /* Begin playback at the specified time */
1325 static int wvs_play(uint32_t time)
1327 int retval;
1329 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1331 retval = stream_seek(time, SEEK_SET);
1333 if (retval >= STREAM_OK) {
1334 wvs_backlight_on_video_mode(true);
1335 wvs_backlight_brightness_video_mode(true);
1336 stream_show_vo(true);
1337 retval = stream_play();
1339 if (retval >= STREAM_OK)
1340 wvs_set_status(WVS_STATUS_PLAYING | WVS_NODRAW);
1343 return retval;
1346 /* Halt playback - pause engine and return logical state */
1347 static int wvs_halt(void)
1349 int status = stream_pause();
1351 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1352 if (status == STREAM_PAUSED) {
1353 if (wvs_get_status() == WVS_STATUS_PLAYING)
1354 status = STREAM_PLAYING;
1357 /* Cancel some auto refreshes - caller will restart them if desired */
1358 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1360 /* No backlight fiddling here - callers does the right thing */
1362 return status;
1365 /* Pause playback if playing */
1366 static int wvs_pause(void)
1368 unsigned refresh = wvs.auto_refresh;
1369 int status = wvs_halt();
1371 if (status == STREAM_PLAYING && (refresh & WVS_REFRESH_RESUME)) {
1372 /* Resume pending - change to a still video frame update */
1373 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1376 wvs_set_status(WVS_STATUS_PAUSED);
1378 wvs_backlight_on_video_mode(false);
1379 /* Leave brightness alone and restore it when WVS is hidden */
1381 return status;
1384 /* Resume playback if halted or paused */
1385 static void wvs_resume(void)
1387 /* Cancel video and resume auto refresh - the resyc when starting
1388 * playback will perform those tasks */
1389 wvs_backlight_on_video_mode(true);
1390 wvs_backlight_brightness_video_mode(true);
1391 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1392 wvs_set_status(WVS_STATUS_PLAYING);
1393 stream_resume();
1396 /* Stop playback - remember the resume point if not closed */
1397 static void wvs_stop(void)
1399 uint32_t resume_time;
1401 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1402 wvs_set_status(WVS_STATUS_STOPPED | WVS_NODRAW);
1403 wvs_show(WVS_HIDE | WVS_NODRAW);
1405 stream_stop();
1407 resume_time = stream_get_resume_time();
1409 if (resume_time != INVALID_TIMESTAMP)
1410 settings.resume_time = resume_time;
1412 wvs_backlight_on_video_mode(false);
1413 wvs_backlight_brightness_video_mode(false);
1416 /* Perform a seek if seeking is possible for this stream - if playing, a delay
1417 * will be inserted before restarting in case the user decides to seek again */
1418 static void wvs_seek(int btn)
1420 int status;
1421 unsigned refresh;
1422 uint32_t time;
1424 if (!stream_can_seek())
1425 return;
1427 /* Halt playback - not strictly nescessary but nice */
1428 status = wvs_halt();
1430 if (status == STREAM_STOPPED)
1431 return;
1433 wvs_show(WVS_SHOW);
1435 if (status == STREAM_PLAYING)
1436 refresh = WVS_REFRESH_RESUME; /* delay resume if playing */
1437 else
1438 refresh = WVS_REFRESH_VIDEO; /* refresh if paused */
1440 /* Obtain a new playback point */
1441 time = wvs_ff_rw(btn, refresh);
1443 /* Tell engine to resume at that time */
1444 stream_seek(time, SEEK_SET);
1447 #ifdef HAVE_HEADPHONE_DETECTION
1448 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1449 static void wvs_handle_phone_plug(bool inserted)
1451 if (rb->global_settings->unplug_mode == 0)
1452 return;
1454 /* Wait for any incomplete state transition to complete first */
1455 stream_wait_status();
1457 int status = wvs_status();
1459 if (inserted) {
1460 if (rb->global_settings->unplug_mode > 1) {
1461 if (status == STREAM_PAUSED) {
1462 wvs_resume();
1465 } else {
1466 if (status == STREAM_PLAYING) {
1467 wvs_pause();
1469 if (stream_can_seek() && rb->global_settings->unplug_rw) {
1470 stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
1471 SEEK_CUR);
1472 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1473 /* Update time display now */
1474 wvs_update_time();
1475 wvs_refresh(WVS_REFRESH_TIME);
1480 #endif
1482 static void button_loop(void)
1484 rb->lcd_setfont(FONT_SYSFIXED);
1485 rb->lcd_clear_display();
1486 rb->lcd_update();
1488 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1489 rb->lcd_set_mode(LCD_MODE_YUV);
1490 #endif
1492 wvs_init();
1494 /* Start playback at the specified starting time */
1495 if (wvs_play(settings.resume_time) < STREAM_OK) {
1496 rb->splash(HZ*2, "Playback failed");
1497 return;
1500 /* Gently poll the video player for EOS and handle UI */
1501 while (stream_status() != STREAM_STOPPED)
1503 int button;
1505 mpeg_menu_sysevent_clear();
1506 button = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1508 button = mpeg_menu_sysevent_callback(button, NULL);
1510 switch (button)
1512 case BUTTON_NONE:
1514 wvs_refresh(WVS_REFRESH_DEFAULT);
1515 continue;
1516 } /* BUTTON_NONE: */
1518 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1519 case LCD_ENABLE_EVENT_1:
1521 /* Draw the current frame if prepared already */
1522 stream_draw_frame(true);
1523 break;
1524 } /* LCD_ENABLE_EVENT_1: */
1525 #endif
1527 case MPEG_VOLUP:
1528 case MPEG_VOLUP|BUTTON_REPEAT:
1529 #ifdef MPEG_VOLUP2
1530 case MPEG_VOLUP2:
1531 case MPEG_VOLUP2|BUTTON_REPEAT:
1532 #endif
1533 #ifdef MPEG_RC_VOLUP
1534 case MPEG_RC_VOLUP:
1535 case MPEG_RC_VOLUP|BUTTON_REPEAT:
1536 #endif
1538 wvs_set_volume(+1);
1539 break;
1540 } /* MPEG_VOLUP*: */
1542 case MPEG_VOLDOWN:
1543 case MPEG_VOLDOWN|BUTTON_REPEAT:
1544 #ifdef MPEG_VOLDOWN2
1545 case MPEG_VOLDOWN2:
1546 case MPEG_VOLDOWN2|BUTTON_REPEAT:
1547 #endif
1548 #ifdef MPEG_RC_VOLDOWN
1549 case MPEG_RC_VOLDOWN:
1550 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
1551 #endif
1553 wvs_set_volume(-1);
1554 break;
1555 } /* MPEG_VOLDOWN*: */
1557 case MPEG_MENU:
1558 #ifdef MPEG_RC_MENU
1559 case MPEG_RC_MENU:
1560 #endif
1562 int state = wvs_halt(); /* save previous state */
1563 int result;
1565 /* Hide video output */
1566 wvs_show(WVS_HIDE | WVS_NODRAW);
1567 stream_show_vo(false);
1568 wvs_backlight_brightness_video_mode(false);
1570 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1571 rb->lcd_set_mode(LCD_MODE_RGB565);
1572 #endif
1574 result = mpeg_menu();
1576 /* The menu can change the font, so restore */
1577 rb->lcd_setfont(FONT_SYSFIXED);
1579 switch (result)
1581 case MPEG_MENU_QUIT:
1582 wvs_stop();
1583 break;
1585 default:
1586 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1587 rb->lcd_set_mode(LCD_MODE_YUV);
1588 #endif
1589 /* If not stopped, show video again */
1590 if (state != STREAM_STOPPED) {
1591 wvs_show(WVS_SHOW);
1592 stream_show_vo(true);
1595 /* If stream was playing, restart it */
1596 if (state == STREAM_PLAYING) {
1597 wvs_resume();
1599 break;
1601 break;
1602 } /* MPEG_MENU: */
1604 case MPEG_STOP:
1605 #ifdef MPEG_RC_STOP
1606 case MPEG_RC_STOP:
1607 #endif
1608 case ACTION_STD_CANCEL:
1610 wvs_stop();
1611 break;
1612 } /* MPEG_STOP: */
1614 case MPEG_PAUSE:
1615 #ifdef MPEG_PAUSE2
1616 case MPEG_PAUSE2:
1617 #endif
1618 #ifdef MPEG_RC_PAUSE
1619 case MPEG_RC_PAUSE:
1620 #endif
1622 int status = wvs_status();
1624 if (status == STREAM_PLAYING) {
1625 /* Playing => Paused */
1626 wvs_pause();
1628 else if (status == STREAM_PAUSED) {
1629 /* Paused => Playing */
1630 wvs_resume();
1633 break;
1634 } /* MPEG_PAUSE*: */
1636 case MPEG_RW:
1637 case MPEG_FF:
1638 #ifdef MPEG_RC_RW
1639 case MPEG_RC_RW:
1640 case MPEG_RC_FF:
1641 #endif
1643 wvs_seek(button);
1644 break;
1645 } /* MPEG_RW: MPEG_FF: */
1647 #ifdef HAVE_HEADPHONE_DETECTION
1648 case SYS_PHONE_PLUGGED:
1649 case SYS_PHONE_UNPLUGGED:
1651 wvs_handle_phone_plug(button == SYS_PHONE_PLUGGED);
1652 break;
1653 } /* SYS_PHONE_*: */
1654 #endif
1656 default:
1658 rb->default_event_handler(button);
1659 break;
1660 } /* default: */
1663 rb->yield();
1664 } /* end while */
1666 wvs_stop();
1668 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1669 /* Be sure hook is removed before exiting since the stop will put it
1670 * back because of the backlight restore. */
1671 rb->lcd_activation_set_hook(NULL);
1672 #endif
1674 rb->lcd_setfont(FONT_UI);
1677 enum plugin_status plugin_start(const void* parameter)
1679 int status = PLUGIN_ERROR; /* assume failure */
1680 int result;
1681 int err;
1682 const char *errstring;
1684 if (parameter == NULL) {
1685 /* No file = GTFO */
1686 rb->splash(HZ*2, "No File");
1687 return PLUGIN_ERROR;
1690 /* Disable all talking before initializing IRAM */
1691 rb->talk_disable(true);
1693 /* Initialize IRAM - stops audio and voice as well */
1694 PLUGIN_IRAM_INIT(rb)
1696 #ifdef HAVE_LCD_COLOR
1697 rb->lcd_set_backdrop(NULL);
1698 rb->lcd_set_foreground(LCD_WHITE);
1699 rb->lcd_set_background(LCD_BLACK);
1700 #endif
1702 rb->lcd_clear_display();
1703 rb->lcd_update();
1705 if (stream_init() < STREAM_OK) {
1706 DEBUGF("Could not initialize streams\n");
1707 } else {
1708 rb->splash(0, "Loading...");
1709 init_settings((char*)parameter);
1711 err = stream_open((char *)parameter);
1713 if (err >= STREAM_OK) {
1714 /* start menu */
1715 rb->lcd_clear_display();
1716 rb->lcd_update();
1717 result = mpeg_start_menu(stream_get_duration());
1719 if (result != MPEG_START_QUIT) {
1720 /* Enter button loop and process UI */
1721 button_loop();
1724 stream_close();
1726 rb->lcd_clear_display();
1727 rb->lcd_update();
1729 save_settings(); /* Save settings (if they have changed) */
1730 status = PLUGIN_OK;
1732 mpeg_menu_sysevent_handle();
1733 } else {
1734 DEBUGF("Could not open %s\n", (char*)parameter);
1735 switch (err)
1737 case STREAM_UNSUPPORTED:
1738 errstring = "Unsupported format";
1739 break;
1740 default:
1741 errstring = "Error opening file: %d";
1744 rb->splashf(HZ*2, errstring, err);
1748 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1749 rb->lcd_set_mode(LCD_MODE_RGB565);
1750 #endif
1752 stream_exit();
1754 rb->talk_disable(false);
1755 return status;