Add support for the next/prev side buttons to mpegplayer on the Gigabeat S to control...
[kugel-rb.git] / apps / plugins / mpegplayer / mpegplayer.c
blob8bd506da359f99cf78534fa5f7d9117d775f9e0d
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
155 #define MPEG_RC_MENU BUTTON_RC_DSP
156 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
157 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
158 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
159 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
160 #define MPEG_RC_RW BUTTON_RC_REW
161 #define MPEG_RC_FF BUTTON_RC_FF
163 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
164 #define MPEG_MENU BUTTON_MENU
165 #define MPEG_STOP BUTTON_POWER
166 #define MPEG_PAUSE BUTTON_SELECT
167 #define MPEG_PAUSE2 BUTTON_PLAY
168 #define MPEG_VOLDOWN BUTTON_LEFT
169 #define MPEG_VOLUP BUTTON_RIGHT
170 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
171 #define MPEG_VOLUP2 BUTTON_VOL_UP
172 #define MPEG_RW BUTTON_UP
173 #define MPEG_RW2 BUTTON_PREV
174 #define MPEG_FF BUTTON_DOWN
175 #define MPEG_FF2 BUTTON_NEXT
177 #define MPEG_RC_MENU BUTTON_RC_DSP
178 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
179 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
180 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
181 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
182 #define MPEG_RC_RW BUTTON_RC_REW
183 #define MPEG_RC_FF BUTTON_RC_FF
185 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
186 #define MPEG_MENU BUTTON_LEFT
187 #define MPEG_STOP BUTTON_POWER
188 #define MPEG_PAUSE BUTTON_PLAY
189 #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
190 #define MPEG_VOLUP BUTTON_SCROLL_UP
191 #define MPEG_RW BUTTON_REW
192 #define MPEG_FF BUTTON_FF
194 #elif CONFIG_KEYPAD == SANSA_E200_PAD
195 #define MPEG_MENU BUTTON_SELECT
196 #define MPEG_STOP BUTTON_POWER
197 #define MPEG_PAUSE BUTTON_RIGHT
198 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
199 #define MPEG_VOLUP BUTTON_SCROLL_FWD
200 #define MPEG_RW BUTTON_UP
201 #define MPEG_FF BUTTON_DOWN
203 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
204 #define MPEG_MENU BUTTON_SELECT
205 #define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT)
206 #define MPEG_PAUSE BUTTON_UP
207 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
208 #define MPEG_VOLUP BUTTON_SCROLL_FWD
209 #define MPEG_RW BUTTON_LEFT
210 #define MPEG_FF BUTTON_RIGHT
213 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
214 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
215 CONFIG_KEYPAD == SANSA_M200_PAD
216 #define MPEG_MENU BUTTON_SELECT
217 #define MPEG_STOP BUTTON_POWER
218 #define MPEG_PAUSE BUTTON_UP
219 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
220 #define MPEG_VOLUP BUTTON_VOL_UP
221 #define MPEG_RW BUTTON_LEFT
222 #define MPEG_FF BUTTON_RIGHT
224 #elif CONFIG_KEYPAD == MROBE500_PAD
225 #define MPEG_STOP BUTTON_POWER
227 #define MPEG_RC_MENU BUTTON_RC_HEART
228 #define MPEG_RC_STOP BUTTON_RC_DOWN
229 #define MPEG_RC_PAUSE BUTTON_RC_PLAY
230 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
231 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
232 #define MPEG_RC_RW BUTTON_RC_REW
233 #define MPEG_RC_FF BUTTON_RC_FF
235 #elif CONFIG_KEYPAD == MROBE100_PAD
236 #define MPEG_MENU BUTTON_MENU
237 #define MPEG_STOP BUTTON_POWER
238 #define MPEG_PAUSE BUTTON_PLAY
239 #define MPEG_VOLDOWN BUTTON_DOWN
240 #define MPEG_VOLUP BUTTON_UP
241 #define MPEG_RW BUTTON_LEFT
242 #define MPEG_FF BUTTON_RIGHT
244 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
245 #define MPEG_MENU BUTTON_RC_MENU
246 #define MPEG_STOP BUTTON_RC_REC
247 #define MPEG_PAUSE BUTTON_RC_PLAY
248 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
249 #define MPEG_VOLUP BUTTON_RC_VOL_UP
250 #define MPEG_RW BUTTON_RC_REW
251 #define MPEG_FF BUTTON_RC_FF
253 #elif CONFIG_KEYPAD == COWON_D2_PAD
254 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
255 //#define MPEG_STOP BUTTON_POWER
256 #define MPEG_VOLDOWN BUTTON_MINUS
257 #define MPEG_VOLUP BUTTON_PLUS
259 #elif CONFIG_KEYPAD == IAUDIO67_PAD
260 #define MPEG_MENU BUTTON_MENU
261 #define MPEG_STOP BUTTON_STOP
262 #define MPEG_PAUSE BUTTON_PLAY
263 #define MPEG_VOLDOWN BUTTON_VOLDOWN
264 #define MPEG_VOLUP BUTTON_VOLUP
265 #define MPEG_RW BUTTON_LEFT
266 #define MPEG_FF BUTTON_RIGHT
268 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
269 #define MPEG_MENU BUTTON_MENU
270 #define MPEG_STOP BUTTON_BACK
271 #define MPEG_PAUSE BUTTON_PLAY
272 #define MPEG_VOLDOWN BUTTON_UP
273 #define MPEG_VOLUP BUTTON_DOWN
274 #define MPEG_RW BUTTON_LEFT
275 #define MPEG_FF BUTTON_RIGHT
277 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
278 #define MPEG_MENU BUTTON_MENU
279 #define MPEG_STOP BUTTON_POWER
280 #define MPEG_PAUSE BUTTON_SELECT
281 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
282 #define MPEG_VOLUP BUTTON_VOL_UP
283 #define MPEG_RW BUTTON_LEFT
284 #define MPEG_FF BUTTON_RIGHT
286 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
287 #define MPEG_MENU BUTTON_MENU
288 #define MPEG_STOP BUTTON_POWER
289 #define MPEG_PAUSE BUTTON_PLAY
290 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
291 #define MPEG_VOLUP BUTTON_VOL_UP
292 #define MPEG_RW BUTTON_UP
293 #define MPEG_FF BUTTON_DOWN
295 #elif CONFIG_KEYPAD == ONDAVX747_PAD
296 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
297 //#define MPEG_STOP BUTTON_POWER
298 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
299 #define MPEG_VOLUP BUTTON_VOL_UP
301 #elif CONFIG_KEYPAD == ONDAVX777_PAD
302 #define MPEG_MENU BUTTON_POWER
304 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
305 #define MPEG_MENU BUTTON_LEFT
306 #define MPEG_STOP BUTTON_RIGHT
307 #define MPEG_PAUSE BUTTON_PLAY
308 #define MPEG_VOLDOWN BUTTON_DOWN
309 #define MPEG_VOLUP BUTTON_UP
310 #define MPEG_RW BUTTON_REW
311 #define MPEG_FF BUTTON_FFWD
313 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
314 #define MPEG_MENU BUTTON_MENU
315 #define MPEG_STOP BUTTON_REC
316 #define MPEG_PAUSE BUTTON_PLAY
317 #define MPEG_VOLDOWN BUTTON_DOWN
318 #define MPEG_VOLUP BUTTON_UP
319 #define MPEG_RW BUTTON_PREV
320 #define MPEG_FF BUTTON_NEXT
322 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
323 #define MPEG_MENU BUTTON_SELECT
324 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
325 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
326 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
327 #define MPEG_VOLUP BUTTON_VOL_UP
328 #define MPEG_RW BUTTON_PREV
329 #define MPEG_FF BUTTON_NEXT
331 #else
332 #error No keymap defined!
333 #endif
335 #ifdef HAVE_TOUCHSCREEN
336 #ifndef MPEG_MENU
337 #define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
338 #endif
339 #ifndef MPEG_STOP
340 #define MPEG_STOP BUTTON_TOPLEFT
341 #endif
342 #ifndef MPEG_PAUSE
343 #define MPEG_PAUSE BUTTON_CENTER
344 #endif
345 #ifndef MPEG_VOLDOWN
346 #define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
347 #endif
348 #ifndef MPEG_VOLUP
349 #define MPEG_VOLUP BUTTON_TOPMIDDLE
350 #endif
351 #ifndef MPEG_RW
352 #define MPEG_RW BUTTON_MIDLEFT
353 #endif
354 #ifndef MPEG_FF
355 #define MPEG_FF BUTTON_MIDRIGHT
356 #endif
357 #endif
359 /* One thing we can do here for targets with remotes is having a display
360 * always on the remote instead of always forcing a popup on the main display */
362 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
363 /* 3% of 30min file == 54s step size */
364 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
365 #define WVS_MIN_UPDATE_INTERVAL (HZ/2)
367 /* WVS status - same order as icon array */
368 enum wvs_status_enum
370 WVS_STATUS_STOPPED = 0,
371 WVS_STATUS_PAUSED,
372 WVS_STATUS_PLAYING,
373 WVS_STATUS_FF,
374 WVS_STATUS_RW,
375 WVS_STATUS_COUNT,
376 WVS_STATUS_MASK = 0x7
379 enum wvs_bits
381 WVS_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
382 /* Refresh the... */
383 WVS_REFRESH_VOLUME = 0x0001, /* ...volume display */
384 WVS_REFRESH_TIME = 0x0002, /* ...time display+progress */
385 WVS_REFRESH_STATUS = 0x0004, /* ...playback status icon */
386 WVS_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
387 WVS_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
388 WVS_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
389 WVS_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
390 WVS_SHOW = 0x4000, /* OR bitflag - show the WVS */
391 WVS_HP_PAUSE = 0x2000,
392 WVS_HIDE = 0x0000, /* hide the WVS (aid readability) */
393 WVS_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
396 /* Status icons selected according to font height */
397 extern const unsigned char mpegplayer_status_icons_8x8x1[];
398 extern const unsigned char mpegplayer_status_icons_12x12x1[];
399 extern const unsigned char mpegplayer_status_icons_16x16x1[];
401 /* Main border areas that contain WVS elements */
402 #define WVS_BDR_L 2
403 #define WVS_BDR_T 2
404 #define WVS_BDR_R 2
405 #define WVS_BDR_B 2
407 struct wvs
409 long hide_tick;
410 long show_for;
411 long print_tick;
412 long print_delay;
413 long resume_tick;
414 long resume_delay;
415 long next_auto_refresh;
416 int x;
417 int y;
418 int width;
419 int height;
420 unsigned fgcolor;
421 unsigned bgcolor;
422 unsigned prog_fillcolor;
423 struct vo_rect update_rect;
424 struct vo_rect prog_rect;
425 struct vo_rect time_rect;
426 struct vo_rect dur_rect;
427 struct vo_rect vol_rect;
428 const unsigned char *icons;
429 struct vo_rect stat_rect;
430 int status;
431 uint32_t curr_time;
432 unsigned auto_refresh;
433 unsigned flags;
436 static struct wvs wvs;
438 static void wvs_show(unsigned show);
440 #ifdef LCD_LANDSCAPE
441 #define _X (x + wvs.x)
442 #define _Y (y + wvs.y)
443 #define _W width
444 #define _H height
445 #else
446 #define _X (LCD_WIDTH - (y + wvs.y) - height)
447 #define _Y (x + wvs.x)
448 #define _W height
449 #define _H width
450 #endif
452 #ifdef HAVE_LCD_COLOR
453 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
454 static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
456 int r1 = RGB_UNPACK_RED(c1);
457 int g1 = RGB_UNPACK_GREEN(c1);
458 int b1 = RGB_UNPACK_BLUE(c1);
460 int r2 = RGB_UNPACK_RED(c2);
461 int g2 = RGB_UNPACK_GREEN(c2);
462 int b2 = RGB_UNPACK_BLUE(c2);
464 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
465 amount*(g2 - g1) / 255 + g1,
466 amount*(b2 - b1) / 255 + b1);
468 #endif
470 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
471 * most are just wrappers of lcd_* functions with transforms applied.
472 * The origin is the upper-left corner of the WVS area */
473 static void draw_update_rect(int x, int y, int width, int height)
475 lcd_(update_rect)(_X, _Y, _W, _H);
478 static void draw_clear_area(int x, int y, int width, int height)
480 #ifdef HAVE_LCD_COLOR
481 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
482 #else
483 int oldmode = grey_get_drawmode();
484 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
485 grey_fillrect(_X, _Y, _W, _H);
486 grey_set_drawmode(oldmode);
487 #endif
490 static void draw_clear_area_rect(const struct vo_rect *rc)
492 draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t);
495 static void draw_fillrect(int x, int y, int width, int height)
497 lcd_(fillrect)(_X, _Y, _W, _H);
500 static void draw_hline(int x1, int x2, int y)
502 #ifdef LCD_LANDSCAPE
503 lcd_(hline)(x1 + wvs.x, x2 + wvs.x, y + wvs.y);
504 #else
505 y = LCD_WIDTH - (y + wvs.y) - 1;
506 lcd_(vline)(y, x1 + wvs.x, x2 + wvs.x);
507 #endif
510 static void draw_vline(int x, int y1, int y2)
512 #ifdef LCD_LANDSCAPE
513 lcd_(vline)(x + wvs.x, y1 + wvs.y, y2 + wvs.y);
514 #else
515 y1 = LCD_WIDTH - (y1 + wvs.y) - 1;
516 y2 = LCD_WIDTH - (y2 + wvs.y) - 1;
517 lcd_(hline)(y1, y2, x + wvs.x);
518 #endif
521 static void draw_scrollbar_draw(int x, int y, int width, int height,
522 uint32_t min, uint32_t max, uint32_t val)
524 unsigned oldfg = lcd_(get_foreground)();
526 draw_hline(x + 1, x + width - 2, y);
527 draw_hline(x + 1, x + width - 2, y + height - 1);
528 draw_vline(x, y + 1, y + height - 2);
529 draw_vline(x + width - 1, y + 1, y + height - 2);
531 val = muldiv_uint32(width - 2, val, max - min);
532 val = MIN(val, (uint32_t)(width - 2));
534 draw_fillrect(x + 1, y + 1, val, height - 2);
536 lcd_(set_foreground)(wvs.prog_fillcolor);
538 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
540 lcd_(set_foreground)(oldfg);
543 static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
544 int max, int val)
546 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
547 min, max, val);
550 #ifdef LCD_PORTRAIT
551 /* Portrait displays need rotated text rendering */
553 /* Limited function that only renders in DRMODE_FG and uses absolute screen
554 * coordinates */
555 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
556 int src_x, int src_y,
557 int stride, int x, int y,
558 int width, int height)
560 const unsigned char *src_end;
561 fb_data *dst, *dst_end;
562 unsigned fg_pattern, bg_pattern;
564 if (x + width > SCREEN_WIDTH)
565 width = SCREEN_WIDTH - x; /* Clip right */
566 if (x < 0)
567 width += x, x = 0; /* Clip left */
568 if (width <= 0)
569 return; /* nothing left to do */
571 if (y + height > SCREEN_HEIGHT)
572 height = SCREEN_HEIGHT - y; /* Clip bottom */
573 if (y < 0)
574 height += y, y = 0; /* Clip top */
575 if (height <= 0)
576 return; /* nothing left to do */
578 fg_pattern = rb->lcd_get_foreground();
579 bg_pattern = rb->lcd_get_background();
581 src += stride * (src_y >> 3) + src_x; /* move starting point */
582 src_y &= 7;
583 src_end = src + width;
585 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
588 const unsigned char *src_col = src++;
589 unsigned data = *src_col >> src_y;
590 int numbits = 8 - src_y;
592 fb_data *dst_col = dst;
593 dst_end = dst_col - height;
594 dst += LCD_WIDTH;
598 dst_col--;
600 if (data & 1)
601 *dst_col = fg_pattern;
602 #if 0
603 else
604 *dst_col = bg_pattern;
605 #endif
606 data >>= 1;
607 if (--numbits == 0) {
608 src_col += stride;
609 data = *src_col;
610 numbits = 8;
613 while (dst_col > dst_end);
615 while (src < src_end);
618 static void draw_putsxy_oriented(int x, int y, const char *str)
620 unsigned short ch;
621 unsigned short *ucs;
622 int ofs = MIN(x, 0);
623 struct font* pf = rb->font_get(FONT_UI);
625 ucs = rb->bidi_l2v(str, 1);
627 x += wvs.x;
628 y += wvs.y;
630 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
632 int width;
633 const unsigned char *bits;
635 /* get proportional width and glyph bits */
636 width = rb->font_get_width(pf, ch);
638 if (ofs > width) {
639 ofs -= width;
640 continue;
643 bits = rb->font_get_bits(pf, ch);
645 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
646 width - ofs, pf->height);
648 x += width - ofs;
649 ofs = 0;
652 #else
653 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
654 int src_x, int src_y,
655 int stride, int x, int y,
656 int width, int height)
658 int mode = lcd_(get_drawmode)();
659 lcd_(set_drawmode)(DRMODE_FG);
660 lcd_(mono_bitmap_part)(src, src_x, src_y, stride, x, y, width, height);
661 lcd_(set_drawmode)(mode);
664 static void draw_putsxy_oriented(int x, int y, const char *str)
666 int mode = lcd_(get_drawmode)();
667 lcd_(set_drawmode)(DRMODE_FG);
668 lcd_(putsxy)(x + wvs.x, y + wvs.y, str);
669 lcd_(set_drawmode)(mode);
671 #endif /* LCD_PORTRAIT */
673 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
674 /* So we can refresh the overlay */
675 static void wvs_lcd_enable_hook(void* param)
677 (void)param;
678 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
680 #endif
682 static void wvs_backlight_on_video_mode(bool video_on)
684 if (video_on) {
685 /* Turn off backlight timeout */
686 /* backlight control in lib/helper.c */
687 backlight_force_on();
688 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
689 rb->remove_event(LCD_EVENT_ACTIVATION, wvs_lcd_enable_hook);
690 #endif
691 } else {
692 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
693 rb->add_event(LCD_EVENT_ACTIVATION, false, wvs_lcd_enable_hook);
694 #endif
695 /* Revert to user's backlight settings */
696 backlight_use_settings();
700 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
701 static void wvs_backlight_brightness_video_mode(bool video_on)
703 if (settings.backlight_brightness < 0)
704 return;
706 mpeg_backlight_update_brightness(
707 video_on ? settings.backlight_brightness : -1);
709 #else
710 #define wvs_backlight_brightness_video_mode(video_on)
711 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
713 static void wvs_text_init(void)
715 struct hms hms;
716 char buf[32];
717 int phys;
718 int spc_width;
720 lcd_(setfont)(FONT_UI);
722 wvs.x = 0;
723 wvs.width = SCREEN_WIDTH;
725 vo_rect_clear(&wvs.time_rect);
726 vo_rect_clear(&wvs.stat_rect);
727 vo_rect_clear(&wvs.prog_rect);
728 vo_rect_clear(&wvs.vol_rect);
730 ts_to_hms(stream_get_duration(), &hms);
731 hms_format(buf, sizeof (buf), &hms);
732 lcd_(getstringsize)(buf, &wvs.time_rect.r, &wvs.time_rect.b);
734 /* Choose well-sized bitmap images relative to font height */
735 if (wvs.time_rect.b < 12) {
736 wvs.icons = mpegplayer_status_icons_8x8x1;
737 wvs.stat_rect.r = wvs.stat_rect.b = 8;
738 } else if (wvs.time_rect.b < 16) {
739 wvs.icons = mpegplayer_status_icons_12x12x1;
740 wvs.stat_rect.r = wvs.stat_rect.b = 12;
741 } else {
742 wvs.icons = mpegplayer_status_icons_16x16x1;
743 wvs.stat_rect.r = wvs.stat_rect.b = 16;
746 if (wvs.stat_rect.b < wvs.time_rect.b) {
747 vo_rect_offset(&wvs.stat_rect, 0,
748 (wvs.time_rect.b - wvs.stat_rect.b) / 2 + WVS_BDR_T);
749 vo_rect_offset(&wvs.time_rect, WVS_BDR_L, WVS_BDR_T);
750 } else {
751 vo_rect_offset(&wvs.time_rect, WVS_BDR_L,
752 wvs.stat_rect.b - wvs.time_rect.b + WVS_BDR_T);
753 vo_rect_offset(&wvs.stat_rect, 0, WVS_BDR_T);
756 wvs.dur_rect = wvs.time_rect;
758 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
759 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
760 rb->sound_unit(SOUND_VOLUME));
762 lcd_(getstringsize)(" ", &spc_width, NULL);
763 lcd_(getstringsize)(buf, &wvs.vol_rect.r, &wvs.vol_rect.b);
765 wvs.prog_rect.r = SCREEN_WIDTH - WVS_BDR_L - spc_width -
766 wvs.vol_rect.r - WVS_BDR_R;
767 wvs.prog_rect.b = 3*wvs.stat_rect.b / 4;
768 vo_rect_offset(&wvs.prog_rect, wvs.time_rect.l,
769 wvs.time_rect.b);
771 vo_rect_offset(&wvs.stat_rect,
772 (wvs.prog_rect.r + wvs.prog_rect.l - wvs.stat_rect.r) / 2,
775 vo_rect_offset(&wvs.dur_rect,
776 wvs.prog_rect.r - wvs.dur_rect.r, 0);
778 vo_rect_offset(&wvs.vol_rect, wvs.prog_rect.r + spc_width,
779 (wvs.prog_rect.b + wvs.prog_rect.t - wvs.vol_rect.b) / 2);
781 wvs.height = WVS_BDR_T + MAX(wvs.prog_rect.b, wvs.vol_rect.b) -
782 MIN(wvs.time_rect.t, wvs.stat_rect.t) + WVS_BDR_B;
784 #ifdef HAVE_LCD_COLOR
785 wvs.height = ALIGN_UP(wvs.height, 2);
786 #endif
787 wvs.y = SCREEN_HEIGHT - wvs.height;
789 lcd_(setfont)(FONT_SYSFIXED);
792 static void wvs_init(void)
794 wvs.flags = 0;
795 wvs.show_for = HZ*4;
796 wvs.print_delay = 75*HZ/100;
797 wvs.resume_delay = HZ/2;
798 #ifdef HAVE_LCD_COLOR
799 wvs.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
800 wvs.fgcolor = LCD_WHITE;
801 wvs.prog_fillcolor = LCD_BLACK;
802 #else
803 wvs.bgcolor = GREY_LIGHTGRAY;
804 wvs.fgcolor = GREY_BLACK;
805 wvs.prog_fillcolor = GREY_WHITE;
806 #endif
807 wvs.curr_time = 0;
808 wvs.status = WVS_STATUS_STOPPED;
809 wvs.auto_refresh = WVS_REFRESH_TIME;
810 wvs.next_auto_refresh = *rb->current_tick;
811 wvs_text_init();
814 static void wvs_schedule_refresh(unsigned refresh)
816 long tick = *rb->current_tick;
818 if (refresh & WVS_REFRESH_VIDEO)
819 wvs.print_tick = tick + wvs.print_delay;
821 if (refresh & WVS_REFRESH_RESUME)
822 wvs.resume_tick = tick + wvs.resume_delay;
824 wvs.auto_refresh |= refresh;
827 static void wvs_cancel_refresh(unsigned refresh)
829 wvs.auto_refresh &= ~refresh;
832 /* Refresh the background area */
833 static void wvs_refresh_background(void)
835 char buf[32];
836 struct hms hms;
838 unsigned bg = lcd_(get_background)();
839 lcd_(set_drawmode)(DRMODE_SOLID | DRMODE_INVERSEVID);
841 #ifdef HAVE_LCD_COLOR
842 /* Draw a "raised" area for our graphics */
843 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 192));
844 draw_hline(0, wvs.width, 0);
846 lcd_(set_background)(draw_blendcolor(bg, DRAW_WHITE, 80));
847 draw_hline(0, wvs.width, 1);
849 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 48));
850 draw_hline(0, wvs.width, wvs.height-2);
852 lcd_(set_background)(draw_blendcolor(bg, DRAW_BLACK, 128));
853 draw_hline(0, wvs.width, wvs.height-1);
855 lcd_(set_background)(bg);
856 draw_clear_area(0, 2, wvs.width, wvs.height - 4);
857 #else
858 /* Give contrast with the main background */
859 lcd_(set_background)(GREY_WHITE);
860 draw_hline(0, wvs.width, 0);
862 lcd_(set_background)(GREY_DARKGRAY);
863 draw_hline(0, wvs.width, wvs.height-1);
865 lcd_(set_background)(bg);
866 draw_clear_area(0, 1, wvs.width, wvs.height - 2);
867 #endif
869 vo_rect_set_ext(&wvs.update_rect, 0, 0, wvs.width, wvs.height);
870 lcd_(set_drawmode)(DRMODE_SOLID);
872 if (stream_get_duration() != INVALID_TIMESTAMP) {
873 /* Draw the movie duration */
874 ts_to_hms(stream_get_duration(), &hms);
875 hms_format(buf, sizeof (buf), &hms);
876 draw_putsxy_oriented(wvs.dur_rect.l, wvs.dur_rect.t, buf);
878 /* else don't know the duration */
881 /* Refresh the current time display + the progress bar */
882 static void wvs_refresh_time(void)
884 char buf[32];
885 struct hms hms;
887 uint32_t duration = stream_get_duration();
889 draw_scrollbar_draw_rect(&wvs.prog_rect, 0, duration,
890 wvs.curr_time);
892 ts_to_hms(wvs.curr_time, &hms);
893 hms_format(buf, sizeof (buf), &hms);
895 draw_clear_area_rect(&wvs.time_rect);
896 draw_putsxy_oriented(wvs.time_rect.l, wvs.time_rect.t, buf);
898 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
899 &wvs.prog_rect);
900 vo_rect_union(&wvs.update_rect, &wvs.update_rect,
901 &wvs.time_rect);
904 /* Refresh the volume display area */
905 static void wvs_refresh_volume(void)
907 char buf[32];
908 int width;
910 int volume = rb->global_settings->volume;
911 rb->snprintf(buf, sizeof (buf), "%d%s",
912 rb->sound_val2phys(SOUND_VOLUME, volume),
913 rb->sound_unit(SOUND_VOLUME));
914 lcd_(getstringsize)(buf, &width, NULL);
916 /* Right-justified */
917 draw_clear_area_rect(&wvs.vol_rect);
918 draw_putsxy_oriented(wvs.vol_rect.r - width, wvs.vol_rect.t, buf);
920 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.vol_rect);
923 /* Refresh the status icon */
924 static void wvs_refresh_status(void)
926 int icon_size = wvs.stat_rect.r - wvs.stat_rect.l;
928 draw_clear_area_rect(&wvs.stat_rect);
930 #ifdef HAVE_LCD_COLOR
931 /* Draw status icon with a drop shadow */
932 unsigned oldfg = lcd_(get_foreground)();
933 int i = 1;
935 lcd_(set_foreground)(draw_blendcolor(lcd_(get_background)(),
936 DRAW_BLACK, 96));
938 while (1)
940 draw_oriented_mono_bitmap_part(wvs.icons,
941 icon_size*wvs.status,
943 icon_size*WVS_STATUS_COUNT,
944 wvs.stat_rect.l + wvs.x + i,
945 wvs.stat_rect.t + wvs.y + i,
946 icon_size, icon_size);
948 if (--i < 0)
949 break;
951 lcd_(set_foreground)(oldfg);
954 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
955 #else
956 draw_oriented_mono_bitmap_part(wvs.icons,
957 icon_size*wvs.status,
959 icon_size*WVS_STATUS_COUNT,
960 wvs.stat_rect.l + wvs.x,
961 wvs.stat_rect.t + wvs.y,
962 icon_size, icon_size);
963 vo_rect_union(&wvs.update_rect, &wvs.update_rect, &wvs.stat_rect);
964 #endif
967 /* Update the current status which determines which icon is displayed */
968 static bool wvs_update_status(void)
970 int status;
972 switch (stream_status())
974 default:
975 status = WVS_STATUS_STOPPED;
976 break;
977 case STREAM_PAUSED:
978 /* If paused with a pending resume, coerce it to WVS_STATUS_PLAYING */
979 status = (wvs.auto_refresh & WVS_REFRESH_RESUME) ?
980 WVS_STATUS_PLAYING : WVS_STATUS_PAUSED;
981 break;
982 case STREAM_PLAYING:
983 status = WVS_STATUS_PLAYING;
984 break;
987 if (status != wvs.status) {
988 /* A refresh is needed */
989 wvs.status = status;
990 return true;
993 return false;
996 /* Update the current time that will be displayed */
997 static void wvs_update_time(void)
999 uint32_t start;
1000 wvs.curr_time = stream_get_seek_time(&start);
1001 wvs.curr_time -= start;
1004 /* Refresh various parts of the WVS - showing it if it is hidden */
1005 static void wvs_refresh(int hint)
1007 long tick;
1008 unsigned oldbg, oldfg;
1010 tick = *rb->current_tick;
1012 if (hint == WVS_REFRESH_DEFAULT) {
1013 /* The default which forces no updates */
1015 /* Make sure Rockbox doesn't turn off the player because of
1016 too little activity */
1017 if (wvs.status == WVS_STATUS_PLAYING)
1018 rb->reset_poweroff_timer();
1020 /* Redraw the current or possibly extract a new video frame */
1021 if ((wvs.auto_refresh & WVS_REFRESH_VIDEO) &&
1022 TIME_AFTER(tick, wvs.print_tick)) {
1023 wvs.auto_refresh &= ~WVS_REFRESH_VIDEO;
1024 stream_draw_frame(false);
1027 /* Restart playback if the timout was reached */
1028 if ((wvs.auto_refresh & WVS_REFRESH_RESUME) &&
1029 TIME_AFTER(tick, wvs.resume_tick)) {
1030 wvs.auto_refresh &= ~(WVS_REFRESH_RESUME | WVS_REFRESH_VIDEO);
1031 stream_resume();
1034 /* If not visible, return */
1035 if (!(wvs.flags & WVS_SHOW))
1036 return;
1038 /* Hide if the visibility duration was reached */
1039 if (TIME_AFTER(tick, wvs.hide_tick)) {
1040 wvs_show(WVS_HIDE);
1041 return;
1043 } else {
1044 /* A forced update of some region */
1046 /* Show if currently invisible */
1047 if (!(wvs.flags & WVS_SHOW)) {
1048 /* Avoid call back into this function - it will be drawn */
1049 wvs_show(WVS_SHOW | WVS_NODRAW);
1050 hint = WVS_REFRESH_ALL;
1053 /* Move back timeouts for frame print and hide */
1054 wvs.print_tick = tick + wvs.print_delay;
1055 wvs.hide_tick = tick + wvs.show_for;
1058 if (TIME_AFTER(tick, wvs.next_auto_refresh)) {
1059 /* Refresh whatever graphical elements are due automatically */
1060 wvs.next_auto_refresh = tick + WVS_MIN_UPDATE_INTERVAL;
1062 if (wvs.auto_refresh & WVS_REFRESH_STATUS) {
1063 if (wvs_update_status())
1064 hint |= WVS_REFRESH_STATUS;
1067 if (wvs.auto_refresh & WVS_REFRESH_TIME) {
1068 wvs_update_time();
1069 hint |= WVS_REFRESH_TIME;
1073 if (hint == 0)
1074 return; /* No drawing needed */
1076 /* Set basic drawing params that are used. Elements that perform variations
1077 * will restore them. */
1078 oldfg = lcd_(get_foreground)();
1079 oldbg = lcd_(get_background)();
1081 lcd_(setfont)(FONT_UI);
1082 lcd_(set_foreground)(wvs.fgcolor);
1083 lcd_(set_background)(wvs.bgcolor);
1085 vo_rect_clear(&wvs.update_rect);
1087 if (hint & WVS_REFRESH_BACKGROUND) {
1088 wvs_refresh_background();
1089 hint |= WVS_REFRESH_ALL; /* Requires a redraw of everything */
1092 if (hint & WVS_REFRESH_TIME) {
1093 wvs_refresh_time();
1096 if (hint & WVS_REFRESH_VOLUME) {
1097 wvs_refresh_volume();
1100 if (hint & WVS_REFRESH_STATUS) {
1101 wvs_refresh_status();
1104 /* Go back to defaults */
1105 lcd_(setfont)(FONT_SYSFIXED);
1106 lcd_(set_foreground)(oldfg);
1107 lcd_(set_background)(oldbg);
1109 /* Update the dirty rectangle */
1110 vo_lock();
1112 draw_update_rect(wvs.update_rect.l,
1113 wvs.update_rect.t,
1114 wvs.update_rect.r - wvs.update_rect.l,
1115 wvs.update_rect.b - wvs.update_rect.t);
1117 vo_unlock();
1120 /* Show/Hide the WVS */
1121 static void wvs_show(unsigned show)
1123 if (((show ^ wvs.flags) & WVS_SHOW) == 0)
1125 if (show & WVS_SHOW) {
1126 wvs.hide_tick = *rb->current_tick + wvs.show_for;
1128 return;
1131 if (show & WVS_SHOW) {
1132 /* Clip away the part of video that is covered */
1133 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, wvs.y };
1135 wvs.flags |= WVS_SHOW;
1137 if (wvs.status != WVS_STATUS_PLAYING) {
1138 /* Not playing - set brightness to mpegplayer setting */
1139 wvs_backlight_brightness_video_mode(true);
1142 stream_vo_set_clip(&rc);
1144 if (!(show & WVS_NODRAW))
1145 wvs_refresh(WVS_REFRESH_ALL);
1146 } else {
1147 /* Uncover clipped video area and redraw it */
1148 wvs.flags &= ~WVS_SHOW;
1150 draw_clear_area(0, 0, wvs.width, wvs.height);
1152 if (!(show & WVS_NODRAW)) {
1153 vo_lock();
1154 draw_update_rect(0, 0, wvs.width, wvs.height);
1155 vo_unlock();
1157 stream_vo_set_clip(NULL);
1158 stream_draw_frame(false);
1159 } else {
1160 stream_vo_set_clip(NULL);
1163 if (wvs.status != WVS_STATUS_PLAYING) {
1164 /* Not playing - restore backlight brightness */
1165 wvs_backlight_brightness_video_mode(false);
1170 /* Set the current status - update screen if specified */
1171 static void wvs_set_status(int status)
1173 bool draw = (status & WVS_NODRAW) == 0;
1175 status &= WVS_STATUS_MASK;
1177 if (wvs.status != status) {
1179 wvs.status = status;
1181 if (draw)
1182 wvs_refresh(WVS_REFRESH_STATUS);
1186 /* Get the current status value */
1187 static int wvs_get_status(void)
1189 return wvs.status & WVS_STATUS_MASK;
1192 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
1193 static uint32_t wvs_ff_rw(int btn, unsigned refresh)
1195 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1196 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1197 uint32_t start;
1198 uint32_t time = stream_get_seek_time(&start);
1199 const uint32_t duration = stream_get_duration();
1200 unsigned int max_step = 0;
1201 uint32_t ff_rw_count = 0;
1202 unsigned status = wvs.status;
1204 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME |
1205 WVS_REFRESH_TIME);
1207 time -= start; /* Absolute clock => stream-relative */
1209 switch (btn)
1211 case MPEG_FF:
1212 #ifdef MPEG_FF2
1213 case MPEG_FF2:
1214 #endif
1215 #ifdef MPEG_RC_FF
1216 case MPEG_RC_FF:
1217 #endif
1218 if (!(btn & BUTTON_REPEAT))
1219 wvs_set_status(WVS_STATUS_FF);
1220 btn = MPEG_FF | BUTTON_REPEAT; /* simplify code below */
1221 break;
1222 case MPEG_RW:
1223 #ifdef MPEG_RW2
1224 case MPEG_RW2:
1225 #endif
1226 #ifdef MPEG_RC_RW
1227 case MPEG_RC_RW:
1228 #endif
1229 if (!(btn & BUTTON_REPEAT))
1230 wvs_set_status(WVS_STATUS_RW);
1231 btn = MPEG_RW | BUTTON_REPEAT; /* simplify code below */
1232 break;
1233 default:
1234 btn = -1;
1237 while (1)
1239 stream_keep_disk_active();
1241 switch (btn)
1243 case BUTTON_NONE:
1244 wvs_refresh(WVS_REFRESH_DEFAULT);
1245 break;
1247 case MPEG_FF | BUTTON_REPEAT:
1248 case MPEG_RW | BUTTON_REPEAT:
1249 #ifdef MPEG_FF2
1250 case MPEG_FF2 | BUTTON_REPEAT:
1251 #endif
1252 #ifdef MPEG_RW2
1253 case MPEG_RW2 | BUTTON_REPEAT:
1254 #endif
1255 #ifdef MPEG_RC_FF
1256 case MPEG_RC_FF | BUTTON_REPEAT:
1257 case MPEG_RC_RW | BUTTON_REPEAT:
1258 #endif
1259 break;
1261 case MPEG_FF | BUTTON_REL:
1262 case MPEG_RW | BUTTON_REL:
1263 #ifdef MPEG_FF2
1264 case MPEG_FF2 | BUTTON_REL:
1265 #endif
1266 #ifdef MPEG_RW2
1267 case MPEG_RW2 | BUTTON_REL:
1268 #endif
1269 #ifdef MPEG_RC_FF
1270 case MPEG_RC_FF | BUTTON_REL:
1271 case MPEG_RC_RW | BUTTON_REL:
1272 #endif
1273 if (wvs.status == WVS_STATUS_FF)
1274 time += ff_rw_count;
1275 else if (wvs.status == WVS_STATUS_RW)
1276 time -= ff_rw_count;
1278 /* Fall-through */
1279 case -1:
1280 default:
1281 wvs_schedule_refresh(refresh);
1282 wvs_set_status(status);
1283 wvs_schedule_refresh(WVS_REFRESH_TIME);
1284 return time;
1287 if (wvs.status == WVS_STATUS_FF) {
1288 /* fast forwarding, calc max step relative to end */
1289 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1290 FF_REWIND_MAX_PERCENT, 100);
1291 } else {
1292 /* rewinding, calc max step relative to start */
1293 max_step = muldiv_uint32(time - ff_rw_count,
1294 FF_REWIND_MAX_PERCENT, 100);
1297 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1299 if (step > max_step)
1300 step = max_step;
1302 ff_rw_count += step;
1304 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1305 step += step >> ff_rw_accel;
1307 if (wvs.status == WVS_STATUS_FF) {
1308 if (duration - time <= ff_rw_count)
1309 ff_rw_count = duration - time;
1311 wvs.curr_time = time + ff_rw_count;
1312 } else {
1313 if (time <= ff_rw_count)
1314 ff_rw_count = time;
1316 wvs.curr_time = time - ff_rw_count;
1319 wvs_refresh(WVS_REFRESH_TIME);
1321 btn = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1325 static int wvs_status(void)
1327 int status = stream_status();
1329 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1330 if (status == STREAM_PAUSED) {
1331 if (wvs.auto_refresh & WVS_REFRESH_RESUME)
1332 status = STREAM_PLAYING;
1335 return status;
1338 /* Change the current audio volume by a specified amount */
1339 static void wvs_set_volume(int delta)
1341 int vol = rb->global_settings->volume;
1342 int limit;
1344 vol += delta;
1346 if (delta < 0) {
1347 /* Volume down - clip to lower limit */
1348 limit = rb->sound_min(SOUND_VOLUME);
1349 if (vol < limit)
1350 vol = limit;
1351 } else {
1352 /* Volume up - clip to upper limit */
1353 limit = rb->sound_max(SOUND_VOLUME);
1354 if (vol > limit)
1355 vol = limit;
1358 /* Sync the global settings */
1359 if (vol != rb->global_settings->volume) {
1360 rb->sound_set(SOUND_VOLUME, vol);
1361 rb->global_settings->volume = vol;
1364 /* Update the volume display */
1365 wvs_refresh(WVS_REFRESH_VOLUME);
1368 /* Begin playback at the specified time */
1369 static int wvs_play(uint32_t time)
1371 int retval;
1373 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1375 retval = stream_seek(time, SEEK_SET);
1377 if (retval >= STREAM_OK) {
1378 wvs_backlight_on_video_mode(true);
1379 wvs_backlight_brightness_video_mode(true);
1380 stream_show_vo(true);
1381 retval = stream_play();
1383 if (retval >= STREAM_OK)
1384 wvs_set_status(WVS_STATUS_PLAYING | WVS_NODRAW);
1387 return retval;
1390 /* Halt playback - pause engine and return logical state */
1391 static int wvs_halt(void)
1393 int status = stream_pause();
1395 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1396 if (status == STREAM_PAUSED) {
1397 if (wvs_get_status() == WVS_STATUS_PLAYING)
1398 status = STREAM_PLAYING;
1401 /* Cancel some auto refreshes - caller will restart them if desired */
1402 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1404 /* No backlight fiddling here - callers does the right thing */
1406 return status;
1409 /* Pause playback if playing */
1410 static int wvs_pause(void)
1412 unsigned refresh = wvs.auto_refresh;
1413 int status = wvs_halt();
1415 if (status == STREAM_PLAYING && (refresh & WVS_REFRESH_RESUME)) {
1416 /* Resume pending - change to a still video frame update */
1417 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1420 wvs_set_status(WVS_STATUS_PAUSED);
1422 wvs_backlight_on_video_mode(false);
1423 /* Leave brightness alone and restore it when WVS is hidden */
1425 return status;
1428 /* Resume playback if halted or paused */
1429 static void wvs_resume(void)
1431 /* Cancel video and resume auto refresh - the resyc when starting
1432 * playback will perform those tasks */
1433 wvs_backlight_on_video_mode(true);
1434 wvs_backlight_brightness_video_mode(true);
1435 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1436 wvs_set_status(WVS_STATUS_PLAYING);
1437 stream_resume();
1440 /* Stop playback - remember the resume point if not closed */
1441 static void wvs_stop(void)
1443 uint32_t resume_time;
1445 wvs_cancel_refresh(WVS_REFRESH_VIDEO | WVS_REFRESH_RESUME);
1446 wvs_set_status(WVS_STATUS_STOPPED | WVS_NODRAW);
1447 wvs_show(WVS_HIDE | WVS_NODRAW);
1449 stream_stop();
1451 resume_time = stream_get_resume_time();
1453 if (resume_time != INVALID_TIMESTAMP)
1454 settings.resume_time = resume_time;
1456 wvs_backlight_on_video_mode(false);
1457 wvs_backlight_brightness_video_mode(false);
1460 /* Perform a seek if seeking is possible for this stream - if playing, a delay
1461 * will be inserted before restarting in case the user decides to seek again */
1462 static void wvs_seek(int btn)
1464 int status;
1465 unsigned refresh;
1466 uint32_t time;
1468 if (!stream_can_seek())
1469 return;
1471 /* Halt playback - not strictly nescessary but nice */
1472 status = wvs_halt();
1474 if (status == STREAM_STOPPED)
1475 return;
1477 wvs_show(WVS_SHOW);
1479 if (status == STREAM_PLAYING)
1480 refresh = WVS_REFRESH_RESUME; /* delay resume if playing */
1481 else
1482 refresh = WVS_REFRESH_VIDEO; /* refresh if paused */
1484 /* Obtain a new playback point */
1485 time = wvs_ff_rw(btn, refresh);
1487 /* Tell engine to resume at that time */
1488 stream_seek(time, SEEK_SET);
1491 #ifdef HAVE_HEADPHONE_DETECTION
1492 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1493 static void wvs_handle_phone_plug(bool inserted)
1495 if (rb->global_settings->unplug_mode == 0)
1496 return;
1498 /* Wait for any incomplete state transition to complete first */
1499 stream_wait_status();
1501 int status = wvs_status();
1503 if (inserted) {
1504 if (rb->global_settings->unplug_mode > 1) {
1505 if (status == STREAM_PAUSED) {
1506 wvs_resume();
1509 } else {
1510 if (status == STREAM_PLAYING) {
1511 wvs_pause();
1513 if (stream_can_seek() && rb->global_settings->unplug_rw) {
1514 stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
1515 SEEK_CUR);
1516 wvs_schedule_refresh(WVS_REFRESH_VIDEO);
1517 /* Update time display now */
1518 wvs_update_time();
1519 wvs_refresh(WVS_REFRESH_TIME);
1524 #endif
1526 static void button_loop(void)
1528 rb->lcd_setfont(FONT_SYSFIXED);
1529 #ifdef HAVE_LCD_COLOR
1530 rb->lcd_set_foreground(LCD_WHITE);
1531 rb->lcd_set_background(LCD_BLACK);
1532 #endif
1533 rb->lcd_clear_display();
1534 rb->lcd_update();
1536 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1537 rb->lcd_set_mode(LCD_MODE_YUV);
1538 #endif
1540 wvs_init();
1542 /* Start playback at the specified starting time */
1543 if (wvs_play(settings.resume_time) < STREAM_OK) {
1544 rb->splash(HZ*2, "Playback failed");
1545 return;
1548 /* Gently poll the video player for EOS and handle UI */
1549 while (stream_status() != STREAM_STOPPED)
1551 int button;
1553 mpeg_menu_sysevent_clear();
1554 button = rb->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL);
1556 button = mpeg_menu_sysevent_callback(button, NULL);
1558 switch (button)
1560 case BUTTON_NONE:
1562 wvs_refresh(WVS_REFRESH_DEFAULT);
1563 continue;
1564 } /* BUTTON_NONE: */
1566 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1567 case LCD_ENABLE_EVENT_1:
1569 /* Draw the current frame if prepared already */
1570 stream_draw_frame(true);
1571 break;
1572 } /* LCD_ENABLE_EVENT_1: */
1573 #endif
1575 case MPEG_VOLUP:
1576 case MPEG_VOLUP|BUTTON_REPEAT:
1577 #ifdef MPEG_VOLUP2
1578 case MPEG_VOLUP2:
1579 case MPEG_VOLUP2|BUTTON_REPEAT:
1580 #endif
1581 #ifdef MPEG_RC_VOLUP
1582 case MPEG_RC_VOLUP:
1583 case MPEG_RC_VOLUP|BUTTON_REPEAT:
1584 #endif
1586 wvs_set_volume(+1);
1587 break;
1588 } /* MPEG_VOLUP*: */
1590 case MPEG_VOLDOWN:
1591 case MPEG_VOLDOWN|BUTTON_REPEAT:
1592 #ifdef MPEG_VOLDOWN2
1593 case MPEG_VOLDOWN2:
1594 case MPEG_VOLDOWN2|BUTTON_REPEAT:
1595 #endif
1596 #ifdef MPEG_RC_VOLDOWN
1597 case MPEG_RC_VOLDOWN:
1598 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
1599 #endif
1601 wvs_set_volume(-1);
1602 break;
1603 } /* MPEG_VOLDOWN*: */
1605 case MPEG_MENU:
1606 #ifdef MPEG_RC_MENU
1607 case MPEG_RC_MENU:
1608 #endif
1610 int state = wvs_halt(); /* save previous state */
1611 int result;
1613 /* Hide video output */
1614 wvs_show(WVS_HIDE | WVS_NODRAW);
1615 stream_show_vo(false);
1616 wvs_backlight_brightness_video_mode(false);
1618 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1619 rb->lcd_set_mode(LCD_MODE_RGB565);
1620 #endif
1622 result = mpeg_menu();
1624 /* The menu can change the font, so restore */
1625 rb->lcd_setfont(FONT_SYSFIXED);
1626 #ifdef HAVE_LCD_COLOR
1627 rb->lcd_set_foreground(LCD_WHITE);
1628 rb->lcd_set_background(LCD_BLACK);
1629 #endif
1630 rb->lcd_clear_display();
1631 rb->lcd_update();
1633 switch (result)
1635 case MPEG_MENU_QUIT:
1636 wvs_stop();
1637 break;
1639 default:
1640 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1641 rb->lcd_set_mode(LCD_MODE_YUV);
1642 #endif
1643 /* If not stopped, show video again */
1644 if (state != STREAM_STOPPED) {
1645 wvs_show(WVS_SHOW);
1646 stream_show_vo(true);
1649 /* If stream was playing, restart it */
1650 if (state == STREAM_PLAYING) {
1651 wvs_resume();
1653 break;
1655 break;
1656 } /* MPEG_MENU: */
1658 case MPEG_STOP:
1659 #ifdef MPEG_RC_STOP
1660 case MPEG_RC_STOP:
1661 #endif
1662 case ACTION_STD_CANCEL:
1664 wvs_stop();
1665 break;
1666 } /* MPEG_STOP: */
1668 case MPEG_PAUSE:
1669 #ifdef MPEG_PAUSE2
1670 case MPEG_PAUSE2:
1671 #endif
1672 #ifdef MPEG_RC_PAUSE
1673 case MPEG_RC_PAUSE:
1674 #endif
1676 int status = wvs_status();
1678 if (status == STREAM_PLAYING) {
1679 /* Playing => Paused */
1680 wvs_pause();
1682 else if (status == STREAM_PAUSED) {
1683 /* Paused => Playing */
1684 wvs_resume();
1687 break;
1688 } /* MPEG_PAUSE*: */
1690 case MPEG_RW:
1691 case MPEG_FF:
1692 #ifdef MPEG_RW2
1693 case MPEG_RW2:
1694 #endif
1695 #ifdef MPEG_FF2
1696 case MPEG_FF2:
1697 #endif
1698 #ifdef MPEG_RC_RW
1699 case MPEG_RC_RW:
1700 case MPEG_RC_FF:
1701 #endif
1703 wvs_seek(button);
1704 break;
1705 } /* MPEG_RW: MPEG_FF: */
1707 #ifdef HAVE_HEADPHONE_DETECTION
1708 case SYS_PHONE_PLUGGED:
1709 case SYS_PHONE_UNPLUGGED:
1711 wvs_handle_phone_plug(button == SYS_PHONE_PLUGGED);
1712 break;
1713 } /* SYS_PHONE_*: */
1714 #endif
1716 default:
1718 rb->default_event_handler(button);
1719 break;
1720 } /* default: */
1723 rb->yield();
1724 } /* end while */
1726 wvs_stop();
1728 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1729 /* Be sure hook is removed before exiting since the stop will put it
1730 * back because of the backlight restore. */
1731 rb->remove_event(LCD_EVENT_ACTIVATION, wvs_lcd_enable_hook);
1732 #endif
1734 rb->lcd_setfont(FONT_UI);
1737 enum plugin_status plugin_start(const void* parameter)
1739 int status = PLUGIN_ERROR; /* assume failure */
1740 int result;
1741 int err;
1742 const char *errstring;
1744 if (parameter == NULL) {
1745 /* No file = GTFO */
1746 rb->splash(HZ*2, "No File");
1747 return PLUGIN_ERROR;
1750 /* Disable all talking before initializing IRAM */
1751 rb->talk_disable(true);
1753 /* Initialize IRAM - stops audio and voice as well */
1754 PLUGIN_IRAM_INIT(rb)
1756 #ifdef HAVE_LCD_COLOR
1757 rb->lcd_set_backdrop(NULL);
1758 rb->lcd_set_foreground(LCD_WHITE);
1759 rb->lcd_set_background(LCD_BLACK);
1760 #endif
1762 rb->lcd_clear_display();
1763 rb->lcd_update();
1765 if (stream_init() < STREAM_OK) {
1766 DEBUGF("Could not initialize streams\n");
1767 } else {
1768 rb->splash(0, "Loading...");
1769 init_settings((char*)parameter);
1771 err = stream_open((char *)parameter);
1773 if (err >= STREAM_OK) {
1774 /* start menu */
1775 rb->lcd_clear_display();
1776 rb->lcd_update();
1777 result = mpeg_start_menu(stream_get_duration());
1779 if (result != MPEG_START_QUIT) {
1780 /* Enter button loop and process UI */
1781 button_loop();
1784 stream_close();
1786 rb->lcd_clear_display();
1787 rb->lcd_update();
1789 save_settings();
1790 status = PLUGIN_OK;
1792 mpeg_menu_sysevent_handle();
1793 } else {
1794 DEBUGF("Could not open %s\n", (char*)parameter);
1795 switch (err)
1797 case STREAM_UNSUPPORTED:
1798 errstring = "Unsupported format";
1799 break;
1800 default:
1801 errstring = "Error opening file: %d";
1804 rb->splashf(HZ*2, errstring, err);
1808 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1809 rb->lcd_set_mode(LCD_MODE_RGB565);
1810 #endif
1812 stream_exit();
1814 rb->talk_disable(false);
1815 return status;