Add a wrapper header, mylcd.h, in the lib subdirectory, which lets plugins' code...
[kugel-rb.git] / apps / plugins / mpegplayer / mpegplayer.c
blob961214f407edfd7f7b1f4962cb75e1f033f4ff1e
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
176 #define MPEG_SHOW_OSD BUTTON_BACK
178 #define MPEG_RC_MENU BUTTON_RC_DSP
179 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
180 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
181 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
182 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
183 #define MPEG_RC_RW BUTTON_RC_REW
184 #define MPEG_RC_FF BUTTON_RC_FF
186 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
187 #define MPEG_MENU BUTTON_LEFT
188 #define MPEG_STOP BUTTON_POWER
189 #define MPEG_PAUSE BUTTON_PLAY
190 #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
191 #define MPEG_VOLUP BUTTON_SCROLL_UP
192 #define MPEG_RW BUTTON_REW
193 #define MPEG_FF BUTTON_FF
195 #elif CONFIG_KEYPAD == SANSA_E200_PAD
196 #define MPEG_MENU BUTTON_SELECT
197 #define MPEG_STOP BUTTON_POWER
198 #define MPEG_PAUSE BUTTON_RIGHT
199 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
200 #define MPEG_VOLUP BUTTON_SCROLL_FWD
201 #define MPEG_RW BUTTON_UP
202 #define MPEG_FF BUTTON_DOWN
204 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
205 #define MPEG_MENU BUTTON_SELECT
206 #define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT)
207 #define MPEG_PAUSE BUTTON_UP
208 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
209 #define MPEG_VOLUP BUTTON_SCROLL_FWD
210 #define MPEG_RW BUTTON_LEFT
211 #define MPEG_FF BUTTON_RIGHT
214 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
215 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
216 CONFIG_KEYPAD == SANSA_M200_PAD
217 #define MPEG_MENU BUTTON_SELECT
218 #define MPEG_STOP BUTTON_POWER
219 #define MPEG_PAUSE BUTTON_UP
220 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
221 #define MPEG_VOLUP BUTTON_VOL_UP
222 #define MPEG_RW BUTTON_LEFT
223 #define MPEG_FF BUTTON_RIGHT
225 #elif CONFIG_KEYPAD == MROBE500_PAD
226 #define MPEG_STOP BUTTON_POWER
228 #define MPEG_RC_MENU BUTTON_RC_HEART
229 #define MPEG_RC_STOP BUTTON_RC_DOWN
230 #define MPEG_RC_PAUSE BUTTON_RC_PLAY
231 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
232 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
233 #define MPEG_RC_RW BUTTON_RC_REW
234 #define MPEG_RC_FF BUTTON_RC_FF
236 #elif CONFIG_KEYPAD == MROBE100_PAD
237 #define MPEG_MENU BUTTON_MENU
238 #define MPEG_STOP BUTTON_POWER
239 #define MPEG_PAUSE BUTTON_PLAY
240 #define MPEG_VOLDOWN BUTTON_DOWN
241 #define MPEG_VOLUP BUTTON_UP
242 #define MPEG_RW BUTTON_LEFT
243 #define MPEG_FF BUTTON_RIGHT
245 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
246 #define MPEG_MENU BUTTON_RC_MENU
247 #define MPEG_STOP BUTTON_RC_REC
248 #define MPEG_PAUSE BUTTON_RC_PLAY
249 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
250 #define MPEG_VOLUP BUTTON_RC_VOL_UP
251 #define MPEG_RW BUTTON_RC_REW
252 #define MPEG_FF BUTTON_RC_FF
254 #elif CONFIG_KEYPAD == COWON_D2_PAD
255 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
256 //#define MPEG_STOP BUTTON_POWER
257 #define MPEG_VOLDOWN BUTTON_MINUS
258 #define MPEG_VOLUP BUTTON_PLUS
260 #elif CONFIG_KEYPAD == IAUDIO67_PAD
261 #define MPEG_MENU BUTTON_MENU
262 #define MPEG_STOP BUTTON_STOP
263 #define MPEG_PAUSE BUTTON_PLAY
264 #define MPEG_VOLDOWN BUTTON_VOLDOWN
265 #define MPEG_VOLUP BUTTON_VOLUP
266 #define MPEG_RW BUTTON_LEFT
267 #define MPEG_FF BUTTON_RIGHT
269 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
270 #define MPEG_MENU BUTTON_MENU
271 #define MPEG_STOP BUTTON_BACK
272 #define MPEG_PAUSE BUTTON_PLAY
273 #define MPEG_VOLDOWN BUTTON_UP
274 #define MPEG_VOLUP BUTTON_DOWN
275 #define MPEG_RW BUTTON_LEFT
276 #define MPEG_FF BUTTON_RIGHT
278 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
279 #define MPEG_MENU BUTTON_MENU
280 #define MPEG_STOP BUTTON_POWER
281 #define MPEG_PAUSE BUTTON_SELECT
282 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
283 #define MPEG_VOLUP BUTTON_VOL_UP
284 #define MPEG_RW BUTTON_LEFT
285 #define MPEG_FF BUTTON_RIGHT
287 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
288 #define MPEG_MENU BUTTON_MENU
289 #define MPEG_STOP BUTTON_POWER
290 #define MPEG_PAUSE BUTTON_PLAY
291 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
292 #define MPEG_VOLUP BUTTON_VOL_UP
293 #define MPEG_RW BUTTON_UP
294 #define MPEG_FF BUTTON_DOWN
296 #elif CONFIG_KEYPAD == ONDAVX747_PAD
297 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
298 //#define MPEG_STOP BUTTON_POWER
299 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
300 #define MPEG_VOLUP BUTTON_VOL_UP
302 #elif CONFIG_KEYPAD == ONDAVX777_PAD
303 #define MPEG_MENU BUTTON_POWER
305 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
306 #define MPEG_MENU BUTTON_LEFT
307 #define MPEG_STOP BUTTON_RIGHT
308 #define MPEG_PAUSE BUTTON_PLAY
309 #define MPEG_VOLDOWN BUTTON_DOWN
310 #define MPEG_VOLUP BUTTON_UP
311 #define MPEG_RW BUTTON_REW
312 #define MPEG_FF BUTTON_FFWD
314 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
315 #define MPEG_MENU BUTTON_MENU
316 #define MPEG_STOP BUTTON_REC
317 #define MPEG_PAUSE BUTTON_PLAY
318 #define MPEG_VOLDOWN BUTTON_DOWN
319 #define MPEG_VOLUP BUTTON_UP
320 #define MPEG_RW BUTTON_PREV
321 #define MPEG_FF BUTTON_NEXT
323 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
324 #define MPEG_MENU BUTTON_SELECT
325 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
326 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
327 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
328 #define MPEG_VOLUP BUTTON_VOL_UP
329 #define MPEG_RW BUTTON_PREV
330 #define MPEG_FF BUTTON_NEXT
332 #else
333 #error No keymap defined!
334 #endif
336 #ifdef HAVE_TOUCHSCREEN
337 #ifndef MPEG_MENU
338 #define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
339 #endif
340 #ifndef MPEG_STOP
341 #define MPEG_STOP BUTTON_TOPLEFT
342 #endif
343 #ifndef MPEG_PAUSE
344 #define MPEG_PAUSE BUTTON_CENTER
345 #endif
346 #ifndef MPEG_VOLDOWN
347 #define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
348 #endif
349 #ifndef MPEG_VOLUP
350 #define MPEG_VOLUP BUTTON_TOPMIDDLE
351 #endif
352 #ifndef MPEG_RW
353 #define MPEG_RW BUTTON_MIDLEFT
354 #endif
355 #ifndef MPEG_FF
356 #define MPEG_FF BUTTON_MIDRIGHT
357 #endif
358 #endif
360 /* One thing we can do here for targets with remotes is having a display
361 * always on the remote instead of always forcing a popup on the main display */
363 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
364 /* 3% of 30min file == 54s step size */
365 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
366 #define OSD_MIN_UPDATE_INTERVAL (HZ/2)
368 /* OSD status - same order as icon array */
369 enum osd_status_enum
371 OSD_STATUS_STOPPED = 0,
372 OSD_STATUS_PAUSED,
373 OSD_STATUS_PLAYING,
374 OSD_STATUS_FF,
375 OSD_STATUS_RW,
376 OSD_STATUS_COUNT,
377 OSD_STATUS_MASK = 0x7
380 enum osd_bits
382 OSD_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
383 /* Refresh the... */
384 OSD_REFRESH_VOLUME = 0x0001, /* ...volume display */
385 OSD_REFRESH_TIME = 0x0002, /* ...time display+progress */
386 OSD_REFRESH_STATUS = 0x0004, /* ...playback status icon */
387 OSD_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
388 OSD_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
389 OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
390 OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
391 OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */
392 OSD_HP_PAUSE = 0x2000,
393 OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */
394 OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
397 /* Status icons selected according to font height */
398 extern const unsigned char mpegplayer_status_icons_8x8x1[];
399 extern const unsigned char mpegplayer_status_icons_12x12x1[];
400 extern const unsigned char mpegplayer_status_icons_16x16x1[];
402 /* Main border areas that contain OSD elements */
403 #define OSD_BDR_L 2
404 #define OSD_BDR_T 2
405 #define OSD_BDR_R 2
406 #define OSD_BDR_B 2
408 struct osd
410 long hide_tick;
411 long show_for;
412 long print_tick;
413 long print_delay;
414 long resume_tick;
415 long resume_delay;
416 long next_auto_refresh;
417 int x;
418 int y;
419 int width;
420 int height;
421 unsigned fgcolor;
422 unsigned bgcolor;
423 unsigned prog_fillcolor;
424 struct vo_rect update_rect;
425 struct vo_rect prog_rect;
426 struct vo_rect time_rect;
427 struct vo_rect dur_rect;
428 struct vo_rect vol_rect;
429 const unsigned char *icons;
430 struct vo_rect stat_rect;
431 int status;
432 uint32_t curr_time;
433 unsigned auto_refresh;
434 unsigned flags;
437 static struct osd osd;
439 static void osd_show(unsigned show);
441 #ifdef LCD_LANDSCAPE
442 #define _X (x + osd.x)
443 #define _Y (y + osd.y)
444 #define _W width
445 #define _H height
446 #else
447 #define _X (LCD_WIDTH - (y + osd.y) - height)
448 #define _Y (x + osd.x)
449 #define _W height
450 #define _H width
451 #endif
453 #ifdef HAVE_LCD_COLOR
454 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
455 static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
457 int r1 = RGB_UNPACK_RED(c1);
458 int g1 = RGB_UNPACK_GREEN(c1);
459 int b1 = RGB_UNPACK_BLUE(c1);
461 int r2 = RGB_UNPACK_RED(c2);
462 int g2 = RGB_UNPACK_GREEN(c2);
463 int b2 = RGB_UNPACK_BLUE(c2);
465 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
466 amount*(g2 - g1) / 255 + g1,
467 amount*(b2 - b1) / 255 + b1);
469 #endif
471 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
472 * most are just wrappers of lcd_* functions with transforms applied.
473 * The origin is the upper-left corner of the OSD area */
474 static void draw_update_rect(int x, int y, int width, int height)
476 mylcd_update_rect(_X, _Y, _W, _H);
479 static void draw_clear_area(int x, int y, int width, int height)
481 #ifdef HAVE_LCD_COLOR
482 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
483 #else
484 int oldmode = grey_get_drawmode();
485 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
486 grey_fillrect(_X, _Y, _W, _H);
487 grey_set_drawmode(oldmode);
488 #endif
491 static void draw_clear_area_rect(const struct vo_rect *rc)
493 draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t);
496 static void draw_fillrect(int x, int y, int width, int height)
498 mylcd_fillrect(_X, _Y, _W, _H);
501 static void draw_hline(int x1, int x2, int y)
503 #ifdef LCD_LANDSCAPE
504 mylcd_hline(x1 + osd.x, x2 + osd.x, y + osd.y);
505 #else
506 y = LCD_WIDTH - (y + osd.y) - 1;
507 mylcd_vline(y, x1 + osd.x, x2 + osd.x);
508 #endif
511 static void draw_vline(int x, int y1, int y2)
513 #ifdef LCD_LANDSCAPE
514 mylcd_vline(x + osd.x, y1 + osd.y, y2 + osd.y);
515 #else
516 y1 = LCD_WIDTH - (y1 + osd.y) - 1;
517 y2 = LCD_WIDTH - (y2 + osd.y) - 1;
518 mylcd_hline(y1, y2, x + osd.x);
519 #endif
522 static void draw_scrollbar_draw(int x, int y, int width, int height,
523 uint32_t min, uint32_t max, uint32_t val)
525 unsigned oldfg = mylcd_get_foreground();
527 draw_hline(x + 1, x + width - 2, y);
528 draw_hline(x + 1, x + width - 2, y + height - 1);
529 draw_vline(x, y + 1, y + height - 2);
530 draw_vline(x + width - 1, y + 1, y + height - 2);
532 val = muldiv_uint32(width - 2, val, max - min);
533 val = MIN(val, (uint32_t)(width - 2));
535 draw_fillrect(x + 1, y + 1, val, height - 2);
537 mylcd_set_foreground(osd.prog_fillcolor);
539 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
541 mylcd_set_foreground(oldfg);
544 static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
545 int max, int val)
547 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
548 min, max, val);
551 #ifdef LCD_PORTRAIT
552 /* Portrait displays need rotated text rendering */
554 /* Limited function that only renders in DRMODE_FG and uses absolute screen
555 * coordinates */
556 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
557 int src_x, int src_y,
558 int stride, int x, int y,
559 int width, int height)
561 const unsigned char *src_end;
562 fb_data *dst, *dst_end;
563 unsigned fg_pattern, bg_pattern;
565 if (x + width > SCREEN_WIDTH)
566 width = SCREEN_WIDTH - x; /* Clip right */
567 if (x < 0)
568 width += x, x = 0; /* Clip left */
569 if (width <= 0)
570 return; /* nothing left to do */
572 if (y + height > SCREEN_HEIGHT)
573 height = SCREEN_HEIGHT - y; /* Clip bottom */
574 if (y < 0)
575 height += y, y = 0; /* Clip top */
576 if (height <= 0)
577 return; /* nothing left to do */
579 fg_pattern = rb->lcd_get_foreground();
580 bg_pattern = rb->lcd_get_background();
582 src += stride * (src_y >> 3) + src_x; /* move starting point */
583 src_y &= 7;
584 src_end = src + width;
586 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
589 const unsigned char *src_col = src++;
590 unsigned data = *src_col >> src_y;
591 int numbits = 8 - src_y;
593 fb_data *dst_col = dst;
594 dst_end = dst_col - height;
595 dst += LCD_WIDTH;
599 dst_col--;
601 if (data & 1)
602 *dst_col = fg_pattern;
603 #if 0
604 else
605 *dst_col = bg_pattern;
606 #endif
607 data >>= 1;
608 if (--numbits == 0) {
609 src_col += stride;
610 data = *src_col;
611 numbits = 8;
614 while (dst_col > dst_end);
616 while (src < src_end);
619 static void draw_putsxy_oriented(int x, int y, const char *str)
621 unsigned short ch;
622 unsigned short *ucs;
623 int ofs = MIN(x, 0);
624 struct font* pf = rb->font_get(FONT_UI);
626 ucs = rb->bidi_l2v(str, 1);
628 x += osd.x;
629 y += osd.y;
631 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
633 int width;
634 const unsigned char *bits;
636 /* get proportional width and glyph bits */
637 width = rb->font_get_width(pf, ch);
639 if (ofs > width) {
640 ofs -= width;
641 continue;
644 bits = rb->font_get_bits(pf, ch);
646 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
647 width - ofs, pf->height);
649 x += width - ofs;
650 ofs = 0;
653 #else
654 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
655 int src_x, int src_y,
656 int stride, int x, int y,
657 int width, int height)
659 int mode = mylcd_get_drawmode();
660 mylcd_set_drawmode(DRMODE_FG);
661 mylcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height);
662 mylcd_set_drawmode(mode);
665 static void draw_putsxy_oriented(int x, int y, const char *str)
667 int mode = mylcd_get_drawmode();
668 mylcd_set_drawmode(DRMODE_FG);
669 mylcd_putsxy(x + osd.x, y + osd.y, str);
670 mylcd_set_drawmode(mode);
672 #endif /* LCD_PORTRAIT */
674 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
675 /* So we can refresh the overlay */
676 static void osd_lcd_enable_hook(void* param)
678 (void)param;
679 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
681 #endif
683 static void osd_backlight_on_video_mode(bool video_on)
685 if (video_on) {
686 /* Turn off backlight timeout */
687 /* backlight control in lib/helper.c */
688 backlight_force_on();
689 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
690 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
691 #endif
692 } else {
693 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
694 rb->add_event(LCD_EVENT_ACTIVATION, false, osd_lcd_enable_hook);
695 #endif
696 /* Revert to user's backlight settings */
697 backlight_use_settings();
701 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
702 static void osd_backlight_brightness_video_mode(bool video_on)
704 if (settings.backlight_brightness < 0)
705 return;
707 mpeg_backlight_update_brightness(
708 video_on ? settings.backlight_brightness : -1);
710 #else
711 #define osd_backlight_brightness_video_mode(video_on)
712 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
714 static void osd_text_init(void)
716 struct hms hms;
717 char buf[32];
718 int phys;
719 int spc_width;
721 mylcd_setfont(FONT_UI);
723 osd.x = 0;
724 osd.width = SCREEN_WIDTH;
726 vo_rect_clear(&osd.time_rect);
727 vo_rect_clear(&osd.stat_rect);
728 vo_rect_clear(&osd.prog_rect);
729 vo_rect_clear(&osd.vol_rect);
731 ts_to_hms(stream_get_duration(), &hms);
732 hms_format(buf, sizeof (buf), &hms);
733 mylcd_getstringsize(buf, &osd.time_rect.r, &osd.time_rect.b);
735 /* Choose well-sized bitmap images relative to font height */
736 if (osd.time_rect.b < 12) {
737 osd.icons = mpegplayer_status_icons_8x8x1;
738 osd.stat_rect.r = osd.stat_rect.b = 8;
739 } else if (osd.time_rect.b < 16) {
740 osd.icons = mpegplayer_status_icons_12x12x1;
741 osd.stat_rect.r = osd.stat_rect.b = 12;
742 } else {
743 osd.icons = mpegplayer_status_icons_16x16x1;
744 osd.stat_rect.r = osd.stat_rect.b = 16;
747 if (osd.stat_rect.b < osd.time_rect.b) {
748 vo_rect_offset(&osd.stat_rect, 0,
749 (osd.time_rect.b - osd.stat_rect.b) / 2 + OSD_BDR_T);
750 vo_rect_offset(&osd.time_rect, OSD_BDR_L, OSD_BDR_T);
751 } else {
752 vo_rect_offset(&osd.time_rect, OSD_BDR_L,
753 osd.stat_rect.b - osd.time_rect.b + OSD_BDR_T);
754 vo_rect_offset(&osd.stat_rect, 0, OSD_BDR_T);
757 osd.dur_rect = osd.time_rect;
759 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
760 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
761 rb->sound_unit(SOUND_VOLUME));
763 mylcd_getstringsize(" ", &spc_width, NULL);
764 mylcd_getstringsize(buf, &osd.vol_rect.r, &osd.vol_rect.b);
766 osd.prog_rect.r = SCREEN_WIDTH - OSD_BDR_L - spc_width -
767 osd.vol_rect.r - OSD_BDR_R;
768 osd.prog_rect.b = 3*osd.stat_rect.b / 4;
769 vo_rect_offset(&osd.prog_rect, osd.time_rect.l,
770 osd.time_rect.b);
772 vo_rect_offset(&osd.stat_rect,
773 (osd.prog_rect.r + osd.prog_rect.l - osd.stat_rect.r) / 2,
776 vo_rect_offset(&osd.dur_rect,
777 osd.prog_rect.r - osd.dur_rect.r, 0);
779 vo_rect_offset(&osd.vol_rect, osd.prog_rect.r + spc_width,
780 (osd.prog_rect.b + osd.prog_rect.t - osd.vol_rect.b) / 2);
782 osd.height = OSD_BDR_T + MAX(osd.prog_rect.b, osd.vol_rect.b) -
783 MIN(osd.time_rect.t, osd.stat_rect.t) + OSD_BDR_B;
785 #ifdef HAVE_LCD_COLOR
786 osd.height = ALIGN_UP(osd.height, 2);
787 #endif
788 osd.y = SCREEN_HEIGHT - osd.height;
790 mylcd_setfont(FONT_SYSFIXED);
793 static void osd_init(void)
795 osd.flags = 0;
796 osd.show_for = HZ*4;
797 osd.print_delay = 75*HZ/100;
798 osd.resume_delay = HZ/2;
799 #ifdef HAVE_LCD_COLOR
800 osd.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
801 osd.fgcolor = LCD_WHITE;
802 osd.prog_fillcolor = LCD_BLACK;
803 #else
804 osd.bgcolor = GREY_LIGHTGRAY;
805 osd.fgcolor = GREY_BLACK;
806 osd.prog_fillcolor = GREY_WHITE;
807 #endif
808 osd.curr_time = 0;
809 osd.status = OSD_STATUS_STOPPED;
810 osd.auto_refresh = OSD_REFRESH_TIME;
811 osd.next_auto_refresh = *rb->current_tick;
812 osd_text_init();
815 static void osd_schedule_refresh(unsigned refresh)
817 long tick = *rb->current_tick;
819 if (refresh & OSD_REFRESH_VIDEO)
820 osd.print_tick = tick + osd.print_delay;
822 if (refresh & OSD_REFRESH_RESUME)
823 osd.resume_tick = tick + osd.resume_delay;
825 osd.auto_refresh |= refresh;
828 static void osd_cancel_refresh(unsigned refresh)
830 osd.auto_refresh &= ~refresh;
833 /* Refresh the background area */
834 static void osd_refresh_background(void)
836 char buf[32];
837 struct hms hms;
839 unsigned bg = mylcd_get_background();
840 mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
842 #ifdef HAVE_LCD_COLOR
843 /* Draw a "raised" area for our graphics */
844 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 192));
845 draw_hline(0, osd.width, 0);
847 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 80));
848 draw_hline(0, osd.width, 1);
850 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 48));
851 draw_hline(0, osd.width, osd.height-2);
853 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 128));
854 draw_hline(0, osd.width, osd.height-1);
856 mylcd_set_background(bg);
857 draw_clear_area(0, 2, osd.width, osd.height - 4);
858 #else
859 /* Give contrast with the main background */
860 mylcd_set_background(MYLCD_WHITE);
861 draw_hline(0, osd.width, 0);
863 mylcd_set_background(MYLCD_DARKGRAY);
864 draw_hline(0, osd.width, osd.height-1);
866 mylcd_set_background(bg);
867 draw_clear_area(0, 1, osd.width, osd.height - 2);
868 #endif
870 vo_rect_set_ext(&osd.update_rect, 0, 0, osd.width, osd.height);
871 mylcd_set_drawmode(DRMODE_SOLID);
873 if (stream_get_duration() != INVALID_TIMESTAMP) {
874 /* Draw the movie duration */
875 ts_to_hms(stream_get_duration(), &hms);
876 hms_format(buf, sizeof (buf), &hms);
877 draw_putsxy_oriented(osd.dur_rect.l, osd.dur_rect.t, buf);
879 /* else don't know the duration */
882 /* Refresh the current time display + the progress bar */
883 static void osd_refresh_time(void)
885 char buf[32];
886 struct hms hms;
888 uint32_t duration = stream_get_duration();
890 draw_scrollbar_draw_rect(&osd.prog_rect, 0, duration,
891 osd.curr_time);
893 ts_to_hms(osd.curr_time, &hms);
894 hms_format(buf, sizeof (buf), &hms);
896 draw_clear_area_rect(&osd.time_rect);
897 draw_putsxy_oriented(osd.time_rect.l, osd.time_rect.t, buf);
899 vo_rect_union(&osd.update_rect, &osd.update_rect,
900 &osd.prog_rect);
901 vo_rect_union(&osd.update_rect, &osd.update_rect,
902 &osd.time_rect);
905 /* Refresh the volume display area */
906 static void osd_refresh_volume(void)
908 char buf[32];
909 int width;
911 int volume = rb->global_settings->volume;
912 rb->snprintf(buf, sizeof (buf), "%d%s",
913 rb->sound_val2phys(SOUND_VOLUME, volume),
914 rb->sound_unit(SOUND_VOLUME));
915 mylcd_getstringsize(buf, &width, NULL);
917 /* Right-justified */
918 draw_clear_area_rect(&osd.vol_rect);
919 draw_putsxy_oriented(osd.vol_rect.r - width, osd.vol_rect.t, buf);
921 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.vol_rect);
924 /* Refresh the status icon */
925 static void osd_refresh_status(void)
927 int icon_size = osd.stat_rect.r - osd.stat_rect.l;
929 draw_clear_area_rect(&osd.stat_rect);
931 #ifdef HAVE_LCD_COLOR
932 /* Draw status icon with a drop shadow */
933 unsigned oldfg = mylcd_get_foreground();
934 int i = 1;
936 mylcd_set_foreground(draw_blendcolor(mylcd_get_background(),
937 MYLCD_BLACK, 96));
939 while (1)
941 draw_oriented_mono_bitmap_part(osd.icons,
942 icon_size*osd.status,
944 icon_size*OSD_STATUS_COUNT,
945 osd.stat_rect.l + osd.x + i,
946 osd.stat_rect.t + osd.y + i,
947 icon_size, icon_size);
949 if (--i < 0)
950 break;
952 mylcd_set_foreground(oldfg);
955 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
956 #else
957 draw_oriented_mono_bitmap_part(osd.icons,
958 icon_size*osd.status,
960 icon_size*OSD_STATUS_COUNT,
961 osd.stat_rect.l + osd.x,
962 osd.stat_rect.t + osd.y,
963 icon_size, icon_size);
964 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
965 #endif
968 /* Update the current status which determines which icon is displayed */
969 static bool osd_update_status(void)
971 int status;
973 switch (stream_status())
975 default:
976 status = OSD_STATUS_STOPPED;
977 break;
978 case STREAM_PAUSED:
979 /* If paused with a pending resume, coerce it to OSD_STATUS_PLAYING */
980 status = (osd.auto_refresh & OSD_REFRESH_RESUME) ?
981 OSD_STATUS_PLAYING : OSD_STATUS_PAUSED;
982 break;
983 case STREAM_PLAYING:
984 status = OSD_STATUS_PLAYING;
985 break;
988 if (status != osd.status) {
989 /* A refresh is needed */
990 osd.status = status;
991 return true;
994 return false;
997 /* Update the current time that will be displayed */
998 static void osd_update_time(void)
1000 uint32_t start;
1001 osd.curr_time = stream_get_seek_time(&start);
1002 osd.curr_time -= start;
1005 /* Refresh various parts of the OSD - showing it if it is hidden */
1006 static void osd_refresh(int hint)
1008 long tick;
1009 unsigned oldbg, oldfg;
1011 tick = *rb->current_tick;
1013 if (hint == OSD_REFRESH_DEFAULT) {
1014 /* The default which forces no updates */
1016 /* Make sure Rockbox doesn't turn off the player because of
1017 too little activity */
1018 if (osd.status == OSD_STATUS_PLAYING)
1019 rb->reset_poweroff_timer();
1021 /* Redraw the current or possibly extract a new video frame */
1022 if ((osd.auto_refresh & OSD_REFRESH_VIDEO) &&
1023 TIME_AFTER(tick, osd.print_tick)) {
1024 osd.auto_refresh &= ~OSD_REFRESH_VIDEO;
1025 stream_draw_frame(false);
1028 /* Restart playback if the timout was reached */
1029 if ((osd.auto_refresh & OSD_REFRESH_RESUME) &&
1030 TIME_AFTER(tick, osd.resume_tick)) {
1031 osd.auto_refresh &= ~(OSD_REFRESH_RESUME | OSD_REFRESH_VIDEO);
1032 stream_resume();
1035 /* If not visible, return */
1036 if (!(osd.flags & OSD_SHOW))
1037 return;
1039 /* Hide if the visibility duration was reached */
1040 if (TIME_AFTER(tick, osd.hide_tick)) {
1041 osd_show(OSD_HIDE);
1042 return;
1044 } else {
1045 /* A forced update of some region */
1047 /* Show if currently invisible */
1048 if (!(osd.flags & OSD_SHOW)) {
1049 /* Avoid call back into this function - it will be drawn */
1050 osd_show(OSD_SHOW | OSD_NODRAW);
1051 hint = OSD_REFRESH_ALL;
1054 /* Move back timeouts for frame print and hide */
1055 osd.print_tick = tick + osd.print_delay;
1056 osd.hide_tick = tick + osd.show_for;
1059 if (TIME_AFTER(tick, osd.next_auto_refresh)) {
1060 /* Refresh whatever graphical elements are due automatically */
1061 osd.next_auto_refresh = tick + OSD_MIN_UPDATE_INTERVAL;
1063 if (osd.auto_refresh & OSD_REFRESH_STATUS) {
1064 if (osd_update_status())
1065 hint |= OSD_REFRESH_STATUS;
1068 if (osd.auto_refresh & OSD_REFRESH_TIME) {
1069 osd_update_time();
1070 hint |= OSD_REFRESH_TIME;
1074 if (hint == 0)
1075 return; /* No drawing needed */
1077 /* Set basic drawing params that are used. Elements that perform variations
1078 * will restore them. */
1079 oldfg = mylcd_get_foreground();
1080 oldbg = mylcd_get_background();
1082 mylcd_setfont(FONT_UI);
1083 mylcd_set_foreground(osd.fgcolor);
1084 mylcd_set_background(osd.bgcolor);
1086 vo_rect_clear(&osd.update_rect);
1088 if (hint & OSD_REFRESH_BACKGROUND) {
1089 osd_refresh_background();
1090 hint |= OSD_REFRESH_ALL; /* Requires a redraw of everything */
1093 if (hint & OSD_REFRESH_TIME) {
1094 osd_refresh_time();
1097 if (hint & OSD_REFRESH_VOLUME) {
1098 osd_refresh_volume();
1101 if (hint & OSD_REFRESH_STATUS) {
1102 osd_refresh_status();
1105 /* Go back to defaults */
1106 mylcd_setfont(FONT_SYSFIXED);
1107 mylcd_set_foreground(oldfg);
1108 mylcd_set_background(oldbg);
1110 /* Update the dirty rectangle */
1111 vo_lock();
1113 draw_update_rect(osd.update_rect.l,
1114 osd.update_rect.t,
1115 osd.update_rect.r - osd.update_rect.l,
1116 osd.update_rect.b - osd.update_rect.t);
1118 vo_unlock();
1121 /* Show/Hide the OSD */
1122 static void osd_show(unsigned show)
1124 if (((show ^ osd.flags) & OSD_SHOW) == 0)
1126 if (show & OSD_SHOW) {
1127 osd.hide_tick = *rb->current_tick + osd.show_for;
1129 return;
1132 if (show & OSD_SHOW) {
1133 /* Clip away the part of video that is covered */
1134 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, osd.y };
1136 osd.flags |= OSD_SHOW;
1138 if (osd.status != OSD_STATUS_PLAYING) {
1139 /* Not playing - set brightness to mpegplayer setting */
1140 osd_backlight_brightness_video_mode(true);
1143 stream_vo_set_clip(&rc);
1145 if (!(show & OSD_NODRAW))
1146 osd_refresh(OSD_REFRESH_ALL);
1147 } else {
1148 /* Uncover clipped video area and redraw it */
1149 osd.flags &= ~OSD_SHOW;
1151 draw_clear_area(0, 0, osd.width, osd.height);
1153 if (!(show & OSD_NODRAW)) {
1154 vo_lock();
1155 draw_update_rect(0, 0, osd.width, osd.height);
1156 vo_unlock();
1158 stream_vo_set_clip(NULL);
1159 stream_draw_frame(false);
1160 } else {
1161 stream_vo_set_clip(NULL);
1164 if (osd.status != OSD_STATUS_PLAYING) {
1165 /* Not playing - restore backlight brightness */
1166 osd_backlight_brightness_video_mode(false);
1171 /* Set the current status - update screen if specified */
1172 static void osd_set_status(int status)
1174 bool draw = (status & OSD_NODRAW) == 0;
1176 status &= OSD_STATUS_MASK;
1178 if (osd.status != status) {
1180 osd.status = status;
1182 if (draw)
1183 osd_refresh(OSD_REFRESH_STATUS);
1187 /* Get the current status value */
1188 static int osd_get_status(void)
1190 return osd.status & OSD_STATUS_MASK;
1193 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
1194 static uint32_t osd_ff_rw(int btn, unsigned refresh)
1196 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1197 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1198 uint32_t start;
1199 uint32_t time = stream_get_seek_time(&start);
1200 const uint32_t duration = stream_get_duration();
1201 unsigned int max_step = 0;
1202 uint32_t ff_rw_count = 0;
1203 unsigned status = osd.status;
1205 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME |
1206 OSD_REFRESH_TIME);
1208 time -= start; /* Absolute clock => stream-relative */
1210 switch (btn)
1212 case MPEG_FF:
1213 #ifdef MPEG_FF2
1214 case MPEG_FF2:
1215 #endif
1216 #ifdef MPEG_RC_FF
1217 case MPEG_RC_FF:
1218 #endif
1219 if (!(btn & BUTTON_REPEAT))
1220 osd_set_status(OSD_STATUS_FF);
1221 btn = MPEG_FF | BUTTON_REPEAT; /* simplify code below */
1222 break;
1223 case MPEG_RW:
1224 #ifdef MPEG_RW2
1225 case MPEG_RW2:
1226 #endif
1227 #ifdef MPEG_RC_RW
1228 case MPEG_RC_RW:
1229 #endif
1230 if (!(btn & BUTTON_REPEAT))
1231 osd_set_status(OSD_STATUS_RW);
1232 btn = MPEG_RW | BUTTON_REPEAT; /* simplify code below */
1233 break;
1234 default:
1235 btn = -1;
1238 while (1)
1240 stream_keep_disk_active();
1242 switch (btn)
1244 case BUTTON_NONE:
1245 osd_refresh(OSD_REFRESH_DEFAULT);
1246 break;
1248 case MPEG_FF | BUTTON_REPEAT:
1249 case MPEG_RW | BUTTON_REPEAT:
1250 #ifdef MPEG_FF2
1251 case MPEG_FF2 | BUTTON_REPEAT:
1252 #endif
1253 #ifdef MPEG_RW2
1254 case MPEG_RW2 | BUTTON_REPEAT:
1255 #endif
1256 #ifdef MPEG_RC_FF
1257 case MPEG_RC_FF | BUTTON_REPEAT:
1258 case MPEG_RC_RW | BUTTON_REPEAT:
1259 #endif
1260 break;
1262 case MPEG_FF | BUTTON_REL:
1263 case MPEG_RW | BUTTON_REL:
1264 #ifdef MPEG_FF2
1265 case MPEG_FF2 | BUTTON_REL:
1266 #endif
1267 #ifdef MPEG_RW2
1268 case MPEG_RW2 | BUTTON_REL:
1269 #endif
1270 #ifdef MPEG_RC_FF
1271 case MPEG_RC_FF | BUTTON_REL:
1272 case MPEG_RC_RW | BUTTON_REL:
1273 #endif
1274 if (osd.status == OSD_STATUS_FF)
1275 time += ff_rw_count;
1276 else if (osd.status == OSD_STATUS_RW)
1277 time -= ff_rw_count;
1279 /* Fall-through */
1280 case -1:
1281 default:
1282 osd_schedule_refresh(refresh);
1283 osd_set_status(status);
1284 osd_schedule_refresh(OSD_REFRESH_TIME);
1285 return time;
1288 if (osd.status == OSD_STATUS_FF) {
1289 /* fast forwarding, calc max step relative to end */
1290 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1291 FF_REWIND_MAX_PERCENT, 100);
1292 } else {
1293 /* rewinding, calc max step relative to start */
1294 max_step = muldiv_uint32(time - ff_rw_count,
1295 FF_REWIND_MAX_PERCENT, 100);
1298 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1300 if (step > max_step)
1301 step = max_step;
1303 ff_rw_count += step;
1305 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1306 step += step >> ff_rw_accel;
1308 if (osd.status == OSD_STATUS_FF) {
1309 if (duration - time <= ff_rw_count)
1310 ff_rw_count = duration - time;
1312 osd.curr_time = time + ff_rw_count;
1313 } else {
1314 if (time <= ff_rw_count)
1315 ff_rw_count = time;
1317 osd.curr_time = time - ff_rw_count;
1320 osd_refresh(OSD_REFRESH_TIME);
1322 btn = rb->button_get_w_tmo(OSD_MIN_UPDATE_INTERVAL);
1326 static int osd_status(void)
1328 int status = stream_status();
1330 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1331 if (status == STREAM_PAUSED) {
1332 if (osd.auto_refresh & OSD_REFRESH_RESUME)
1333 status = STREAM_PLAYING;
1336 return status;
1339 /* Change the current audio volume by a specified amount */
1340 static void osd_set_volume(int delta)
1342 int vol = rb->global_settings->volume;
1343 int limit;
1345 vol += delta;
1347 if (delta < 0) {
1348 /* Volume down - clip to lower limit */
1349 limit = rb->sound_min(SOUND_VOLUME);
1350 if (vol < limit)
1351 vol = limit;
1352 } else {
1353 /* Volume up - clip to upper limit */
1354 limit = rb->sound_max(SOUND_VOLUME);
1355 if (vol > limit)
1356 vol = limit;
1359 /* Sync the global settings */
1360 if (vol != rb->global_settings->volume) {
1361 rb->sound_set(SOUND_VOLUME, vol);
1362 rb->global_settings->volume = vol;
1365 /* Update the volume display */
1366 osd_refresh(OSD_REFRESH_VOLUME);
1369 /* Begin playback at the specified time */
1370 static int osd_play(uint32_t time)
1372 int retval;
1374 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1376 retval = stream_seek(time, SEEK_SET);
1378 if (retval >= STREAM_OK) {
1379 osd_backlight_on_video_mode(true);
1380 osd_backlight_brightness_video_mode(true);
1381 stream_show_vo(true);
1382 retval = stream_play();
1384 if (retval >= STREAM_OK)
1385 osd_set_status(OSD_STATUS_PLAYING | OSD_NODRAW);
1388 return retval;
1391 /* Halt playback - pause engine and return logical state */
1392 static int osd_halt(void)
1394 int status = stream_pause();
1396 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1397 if (status == STREAM_PAUSED) {
1398 if (osd_get_status() == OSD_STATUS_PLAYING)
1399 status = STREAM_PLAYING;
1402 /* Cancel some auto refreshes - caller will restart them if desired */
1403 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1405 /* No backlight fiddling here - callers does the right thing */
1407 return status;
1410 /* Pause playback if playing */
1411 static int osd_pause(void)
1413 unsigned refresh = osd.auto_refresh;
1414 int status = osd_halt();
1416 if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) {
1417 /* Resume pending - change to a still video frame update */
1418 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1421 osd_set_status(OSD_STATUS_PAUSED);
1423 osd_backlight_on_video_mode(false);
1424 /* Leave brightness alone and restore it when OSD is hidden */
1426 return status;
1429 /* Resume playback if halted or paused */
1430 static void osd_resume(void)
1432 /* Cancel video and resume auto refresh - the resyc when starting
1433 * playback will perform those tasks */
1434 osd_backlight_on_video_mode(true);
1435 osd_backlight_brightness_video_mode(true);
1436 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1437 osd_set_status(OSD_STATUS_PLAYING);
1438 stream_resume();
1441 /* Stop playback - remember the resume point if not closed */
1442 static void osd_stop(void)
1444 uint32_t resume_time;
1446 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1447 osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
1448 osd_show(OSD_HIDE | OSD_NODRAW);
1450 stream_stop();
1452 resume_time = stream_get_resume_time();
1454 if (resume_time != INVALID_TIMESTAMP)
1455 settings.resume_time = resume_time;
1457 osd_backlight_on_video_mode(false);
1458 osd_backlight_brightness_video_mode(false);
1461 /* Perform a seek if seeking is possible for this stream - if playing, a delay
1462 * will be inserted before restarting in case the user decides to seek again */
1463 static void osd_seek(int btn)
1465 int status;
1466 unsigned refresh;
1467 uint32_t time;
1469 if (!stream_can_seek())
1470 return;
1472 /* Halt playback - not strictly nescessary but nice */
1473 status = osd_halt();
1475 if (status == STREAM_STOPPED)
1476 return;
1478 osd_show(OSD_SHOW);
1480 if (status == STREAM_PLAYING)
1481 refresh = OSD_REFRESH_RESUME; /* delay resume if playing */
1482 else
1483 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
1485 /* Obtain a new playback point */
1486 time = osd_ff_rw(btn, refresh);
1488 /* Tell engine to resume at that time */
1489 stream_seek(time, SEEK_SET);
1492 #ifdef HAVE_HEADPHONE_DETECTION
1493 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1494 static void osd_handle_phone_plug(bool inserted)
1496 if (rb->global_settings->unplug_mode == 0)
1497 return;
1499 /* Wait for any incomplete state transition to complete first */
1500 stream_wait_status();
1502 int status = osd_status();
1504 if (inserted) {
1505 if (rb->global_settings->unplug_mode > 1) {
1506 if (status == STREAM_PAUSED) {
1507 osd_resume();
1510 } else {
1511 if (status == STREAM_PLAYING) {
1512 osd_pause();
1514 if (stream_can_seek() && rb->global_settings->unplug_rw) {
1515 stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
1516 SEEK_CUR);
1517 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1518 /* Update time display now */
1519 osd_update_time();
1520 osd_refresh(OSD_REFRESH_TIME);
1525 #endif
1527 static void button_loop(void)
1529 rb->lcd_setfont(FONT_SYSFIXED);
1530 #ifdef HAVE_LCD_COLOR
1531 rb->lcd_set_foreground(LCD_WHITE);
1532 rb->lcd_set_background(LCD_BLACK);
1533 #endif
1534 rb->lcd_clear_display();
1535 rb->lcd_update();
1537 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1538 rb->lcd_set_mode(LCD_MODE_YUV);
1539 #endif
1541 osd_init();
1543 /* Start playback at the specified starting time */
1544 if (osd_play(settings.resume_time) < STREAM_OK) {
1545 rb->splash(HZ*2, "Playback failed");
1546 return;
1549 /* Gently poll the video player for EOS and handle UI */
1550 while (stream_status() != STREAM_STOPPED)
1552 int button;
1554 mpeg_menu_sysevent_clear();
1555 button = rb->button_get_w_tmo(OSD_MIN_UPDATE_INTERVAL/2);
1557 button = mpeg_menu_sysevent_callback(button, NULL);
1559 switch (button)
1561 case BUTTON_NONE:
1563 osd_refresh(OSD_REFRESH_DEFAULT);
1564 continue;
1565 } /* BUTTON_NONE: */
1567 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1568 case LCD_ENABLE_EVENT_1:
1570 /* Draw the current frame if prepared already */
1571 stream_draw_frame(true);
1572 break;
1573 } /* LCD_ENABLE_EVENT_1: */
1574 #endif
1576 case MPEG_VOLUP:
1577 case MPEG_VOLUP|BUTTON_REPEAT:
1578 #ifdef MPEG_VOLUP2
1579 case MPEG_VOLUP2:
1580 case MPEG_VOLUP2|BUTTON_REPEAT:
1581 #endif
1582 #ifdef MPEG_RC_VOLUP
1583 case MPEG_RC_VOLUP:
1584 case MPEG_RC_VOLUP|BUTTON_REPEAT:
1585 #endif
1587 osd_set_volume(+1);
1588 break;
1589 } /* MPEG_VOLUP*: */
1591 case MPEG_VOLDOWN:
1592 case MPEG_VOLDOWN|BUTTON_REPEAT:
1593 #ifdef MPEG_VOLDOWN2
1594 case MPEG_VOLDOWN2:
1595 case MPEG_VOLDOWN2|BUTTON_REPEAT:
1596 #endif
1597 #ifdef MPEG_RC_VOLDOWN
1598 case MPEG_RC_VOLDOWN:
1599 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
1600 #endif
1602 osd_set_volume(-1);
1603 break;
1604 } /* MPEG_VOLDOWN*: */
1606 case MPEG_MENU:
1607 #ifdef MPEG_RC_MENU
1608 case MPEG_RC_MENU:
1609 #endif
1611 int state = osd_halt(); /* save previous state */
1612 int result;
1614 /* Hide video output */
1615 osd_show(OSD_HIDE | OSD_NODRAW);
1616 stream_show_vo(false);
1617 osd_backlight_brightness_video_mode(false);
1619 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1620 rb->lcd_set_mode(LCD_MODE_RGB565);
1621 #endif
1623 result = mpeg_menu();
1625 /* The menu can change the font, so restore */
1626 rb->lcd_setfont(FONT_SYSFIXED);
1627 #ifdef HAVE_LCD_COLOR
1628 rb->lcd_set_foreground(LCD_WHITE);
1629 rb->lcd_set_background(LCD_BLACK);
1630 #endif
1631 rb->lcd_clear_display();
1632 rb->lcd_update();
1634 switch (result)
1636 case MPEG_MENU_QUIT:
1637 osd_stop();
1638 break;
1640 default:
1641 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1642 rb->lcd_set_mode(LCD_MODE_YUV);
1643 #endif
1644 /* If not stopped, show video again */
1645 if (state != STREAM_STOPPED) {
1646 osd_show(OSD_SHOW);
1647 stream_show_vo(true);
1650 /* If stream was playing, restart it */
1651 if (state == STREAM_PLAYING) {
1652 osd_resume();
1654 break;
1656 break;
1657 } /* MPEG_MENU: */
1659 #ifdef MPEG_SHOW_OSD
1660 case MPEG_SHOW_OSD:
1661 case MPEG_SHOW_OSD | BUTTON_REPEAT:
1662 /* Show if not visible */
1663 osd_show(OSD_SHOW);
1664 /* Make sure it refreshes */
1665 osd_refresh(OSD_REFRESH_DEFAULT);
1666 break;
1667 #endif
1669 case MPEG_STOP:
1670 #ifdef MPEG_RC_STOP
1671 case MPEG_RC_STOP:
1672 #endif
1673 case ACTION_STD_CANCEL:
1675 osd_stop();
1676 break;
1677 } /* MPEG_STOP: */
1679 case MPEG_PAUSE:
1680 #ifdef MPEG_PAUSE2
1681 case MPEG_PAUSE2:
1682 #endif
1683 #ifdef MPEG_RC_PAUSE
1684 case MPEG_RC_PAUSE:
1685 #endif
1687 int status = osd_status();
1689 if (status == STREAM_PLAYING) {
1690 /* Playing => Paused */
1691 osd_pause();
1693 else if (status == STREAM_PAUSED) {
1694 /* Paused => Playing */
1695 osd_resume();
1698 break;
1699 } /* MPEG_PAUSE*: */
1701 case MPEG_RW:
1702 case MPEG_FF:
1703 #ifdef MPEG_RW2
1704 case MPEG_RW2:
1705 #endif
1706 #ifdef MPEG_FF2
1707 case MPEG_FF2:
1708 #endif
1709 #ifdef MPEG_RC_RW
1710 case MPEG_RC_RW:
1711 case MPEG_RC_FF:
1712 #endif
1714 osd_seek(button);
1715 break;
1716 } /* MPEG_RW: MPEG_FF: */
1718 #ifdef HAVE_HEADPHONE_DETECTION
1719 case SYS_PHONE_PLUGGED:
1720 case SYS_PHONE_UNPLUGGED:
1722 osd_handle_phone_plug(button == SYS_PHONE_PLUGGED);
1723 break;
1724 } /* SYS_PHONE_*: */
1725 #endif
1727 default:
1729 rb->default_event_handler(button);
1730 break;
1731 } /* default: */
1734 rb->yield();
1735 } /* end while */
1737 osd_stop();
1739 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1740 /* Be sure hook is removed before exiting since the stop will put it
1741 * back because of the backlight restore. */
1742 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
1743 #endif
1745 rb->lcd_setfont(FONT_UI);
1748 enum plugin_status plugin_start(const void* parameter)
1750 int status = PLUGIN_ERROR; /* assume failure */
1751 int result;
1752 int err;
1753 const char *errstring;
1755 if (parameter == NULL) {
1756 /* No file = GTFO */
1757 rb->splash(HZ*2, "No File");
1758 return PLUGIN_ERROR;
1761 /* Disable all talking before initializing IRAM */
1762 rb->talk_disable(true);
1764 /* Initialize IRAM - stops audio and voice as well */
1765 PLUGIN_IRAM_INIT(rb)
1767 #ifdef HAVE_LCD_COLOR
1768 rb->lcd_set_backdrop(NULL);
1769 rb->lcd_set_foreground(LCD_WHITE);
1770 rb->lcd_set_background(LCD_BLACK);
1771 #endif
1773 rb->lcd_clear_display();
1774 rb->lcd_update();
1776 if (stream_init() < STREAM_OK) {
1777 DEBUGF("Could not initialize streams\n");
1778 } else {
1779 rb->splash(0, "Loading...");
1780 init_settings((char*)parameter);
1782 err = stream_open((char *)parameter);
1784 if (err >= STREAM_OK) {
1785 /* start menu */
1786 rb->lcd_clear_display();
1787 rb->lcd_update();
1788 result = mpeg_start_menu(stream_get_duration());
1790 if (result != MPEG_START_QUIT) {
1791 /* Enter button loop and process UI */
1792 button_loop();
1795 stream_close();
1797 rb->lcd_clear_display();
1798 rb->lcd_update();
1800 save_settings();
1801 status = PLUGIN_OK;
1803 mpeg_menu_sysevent_handle();
1804 } else {
1805 DEBUGF("Could not open %s\n", (char*)parameter);
1806 switch (err)
1808 case STREAM_UNSUPPORTED:
1809 errstring = "Unsupported format";
1810 break;
1811 default:
1812 errstring = "Error opening file: %d";
1815 rb->splashf(HZ*2, errstring, err);
1819 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1820 rb->lcd_set_mode(LCD_MODE_RGB565);
1821 #endif
1823 stream_exit();
1825 rb->talk_disable(false);
1826 return status;