Fix a bunch of 'variable set but not used' warnings reported from GCC 4.6.0.
[kugel-rb.git] / apps / plugins / mpegplayer / mpegplayer.c
blob2a84307857a9623d45ce7084ae23db9bce4d70db
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 "video_out.h"
107 #include "stream_thread.h"
108 #include "stream_mgr.h"
111 /* button definitions */
112 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
113 #define MPEG_MENU BUTTON_MODE
114 #define MPEG_STOP BUTTON_OFF
115 #define MPEG_PAUSE BUTTON_ON
116 #define MPEG_VOLDOWN BUTTON_DOWN
117 #define MPEG_VOLUP BUTTON_UP
118 #define MPEG_RW BUTTON_LEFT
119 #define MPEG_FF BUTTON_RIGHT
121 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
122 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
123 #define MPEG_MENU BUTTON_MENU
124 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
125 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
126 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
127 #define MPEG_VOLUP BUTTON_SCROLL_FWD
128 #define MPEG_RW BUTTON_LEFT
129 #define MPEG_FF BUTTON_RIGHT
131 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
132 #define MPEG_MENU (BUTTON_REC | BUTTON_REL)
133 #define MPEG_STOP BUTTON_POWER
134 #define MPEG_PAUSE BUTTON_PLAY
135 #define MPEG_VOLDOWN BUTTON_DOWN
136 #define MPEG_VOLUP BUTTON_UP
137 #define MPEG_RW BUTTON_LEFT
138 #define MPEG_FF BUTTON_RIGHT
140 #elif CONFIG_KEYPAD == GIGABEAT_PAD
141 #define MPEG_MENU BUTTON_MENU
142 #define MPEG_STOP BUTTON_POWER
143 #define MPEG_PAUSE BUTTON_SELECT
144 #define MPEG_PAUSE2 BUTTON_A
145 #define MPEG_VOLDOWN BUTTON_LEFT
146 #define MPEG_VOLUP BUTTON_RIGHT
147 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
148 #define MPEG_VOLUP2 BUTTON_VOL_UP
149 #define MPEG_RW BUTTON_UP
150 #define MPEG_FF BUTTON_DOWN
152 #define MPEG_RC_MENU BUTTON_RC_DSP
153 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
154 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
155 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
156 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
157 #define MPEG_RC_RW BUTTON_RC_REW
158 #define MPEG_RC_FF BUTTON_RC_FF
160 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
161 #define MPEG_MENU BUTTON_MENU
162 #define MPEG_STOP BUTTON_POWER
163 #define MPEG_PAUSE BUTTON_SELECT
164 #define MPEG_PAUSE2 BUTTON_PLAY
165 #define MPEG_VOLDOWN BUTTON_LEFT
166 #define MPEG_VOLUP BUTTON_RIGHT
167 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
168 #define MPEG_VOLUP2 BUTTON_VOL_UP
169 #define MPEG_RW BUTTON_UP
170 #define MPEG_RW2 BUTTON_PREV
171 #define MPEG_FF BUTTON_DOWN
172 #define MPEG_FF2 BUTTON_NEXT
173 #define MPEG_SHOW_OSD BUTTON_BACK
175 #define MPEG_RC_MENU BUTTON_RC_DSP
176 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
177 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
178 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
179 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
180 #define MPEG_RC_RW BUTTON_RC_REW
181 #define MPEG_RC_FF BUTTON_RC_FF
183 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
184 #define MPEG_MENU BUTTON_LEFT
185 #define MPEG_STOP BUTTON_POWER
186 #define MPEG_PAUSE BUTTON_PLAY
187 #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
188 #define MPEG_VOLUP BUTTON_SCROLL_UP
189 #define MPEG_RW BUTTON_REW
190 #define MPEG_FF BUTTON_FF
192 #elif CONFIG_KEYPAD == SANSA_E200_PAD
193 #define MPEG_MENU BUTTON_SELECT
194 #define MPEG_STOP BUTTON_POWER
195 #define MPEG_PAUSE BUTTON_RIGHT
196 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
197 #define MPEG_VOLUP BUTTON_SCROLL_FWD
198 #define MPEG_RW BUTTON_UP
199 #define MPEG_FF BUTTON_DOWN
201 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
202 #define MPEG_MENU BUTTON_SELECT
203 #define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT)
204 #define MPEG_PAUSE BUTTON_UP
205 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
206 #define MPEG_VOLUP BUTTON_SCROLL_FWD
207 #define MPEG_RW BUTTON_LEFT
208 #define MPEG_FF BUTTON_RIGHT
211 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
212 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
213 CONFIG_KEYPAD == SANSA_M200_PAD
214 #define MPEG_MENU BUTTON_SELECT
215 #define MPEG_STOP BUTTON_POWER
216 #define MPEG_PAUSE BUTTON_UP
217 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
218 #define MPEG_VOLUP BUTTON_VOL_UP
219 #define MPEG_RW BUTTON_LEFT
220 #define MPEG_FF BUTTON_RIGHT
222 #elif CONFIG_KEYPAD == MROBE500_PAD
223 #define MPEG_STOP BUTTON_POWER
225 #define MPEG_RC_MENU BUTTON_RC_HEART
226 #define MPEG_RC_STOP BUTTON_RC_DOWN
227 #define MPEG_RC_PAUSE BUTTON_RC_PLAY
228 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
229 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
230 #define MPEG_RC_RW BUTTON_RC_REW
231 #define MPEG_RC_FF BUTTON_RC_FF
233 #elif CONFIG_KEYPAD == MROBE100_PAD
234 #define MPEG_MENU BUTTON_MENU
235 #define MPEG_STOP BUTTON_POWER
236 #define MPEG_PAUSE BUTTON_PLAY
237 #define MPEG_VOLDOWN BUTTON_DOWN
238 #define MPEG_VOLUP BUTTON_UP
239 #define MPEG_RW BUTTON_LEFT
240 #define MPEG_FF BUTTON_RIGHT
242 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
243 #define MPEG_MENU BUTTON_RC_MENU
244 #define MPEG_STOP BUTTON_RC_REC
245 #define MPEG_PAUSE BUTTON_RC_PLAY
246 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
247 #define MPEG_VOLUP BUTTON_RC_VOL_UP
248 #define MPEG_RW BUTTON_RC_REW
249 #define MPEG_FF BUTTON_RC_FF
251 #elif CONFIG_KEYPAD == COWON_D2_PAD
252 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
253 //#define MPEG_STOP BUTTON_POWER
254 #define MPEG_VOLDOWN BUTTON_MINUS
255 #define MPEG_VOLUP BUTTON_PLUS
257 #elif CONFIG_KEYPAD == IAUDIO67_PAD
258 #define MPEG_MENU BUTTON_MENU
259 #define MPEG_STOP BUTTON_STOP
260 #define MPEG_PAUSE BUTTON_PLAY
261 #define MPEG_VOLDOWN BUTTON_VOLDOWN
262 #define MPEG_VOLUP BUTTON_VOLUP
263 #define MPEG_RW BUTTON_LEFT
264 #define MPEG_FF BUTTON_RIGHT
266 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
267 #define MPEG_MENU BUTTON_MENU
268 #define MPEG_STOP BUTTON_BACK
269 #define MPEG_PAUSE BUTTON_PLAY
270 #define MPEG_VOLDOWN BUTTON_UP
271 #define MPEG_VOLUP BUTTON_DOWN
272 #define MPEG_RW BUTTON_LEFT
273 #define MPEG_FF BUTTON_RIGHT
275 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
276 #define MPEG_MENU BUTTON_MENU
277 #define MPEG_STOP BUTTON_POWER
278 #define MPEG_PAUSE BUTTON_SELECT
279 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
280 #define MPEG_VOLUP BUTTON_VOL_UP
281 #define MPEG_RW BUTTON_LEFT
282 #define MPEG_FF BUTTON_RIGHT
284 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
285 #define MPEG_MENU BUTTON_MENU
286 #define MPEG_STOP BUTTON_POWER
287 #define MPEG_PAUSE BUTTON_PLAY
288 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
289 #define MPEG_VOLUP BUTTON_VOL_UP
290 #define MPEG_RW BUTTON_PREV
291 #define MPEG_FF BUTTON_NEXT
293 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
294 #define MPEG_MENU BUTTON_MENU
295 #define MPEG_STOP BUTTON_POWER
296 #define MPEG_PAUSE BUTTON_PLAY
297 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
298 #define MPEG_VOLUP BUTTON_VOL_UP
299 #define MPEG_RW BUTTON_UP
300 #define MPEG_FF BUTTON_DOWN
302 #elif CONFIG_KEYPAD == ONDAVX747_PAD
303 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
304 //#define MPEG_STOP BUTTON_POWER
305 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
306 #define MPEG_VOLUP BUTTON_VOL_UP
308 #elif CONFIG_KEYPAD == ONDAVX777_PAD
309 #define MPEG_MENU BUTTON_POWER
311 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
312 #define MPEG_MENU BUTTON_LEFT
313 #define MPEG_STOP BUTTON_RIGHT
314 #define MPEG_PAUSE BUTTON_PLAY
315 #define MPEG_VOLDOWN BUTTON_DOWN
316 #define MPEG_VOLUP BUTTON_UP
317 #define MPEG_RW BUTTON_REW
318 #define MPEG_FF BUTTON_FFWD
320 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
321 #define MPEG_MENU BUTTON_MENU
322 #define MPEG_STOP BUTTON_REC
323 #define MPEG_PAUSE BUTTON_PLAY
324 #define MPEG_VOLDOWN BUTTON_DOWN
325 #define MPEG_VOLUP BUTTON_UP
326 #define MPEG_RW BUTTON_PREV
327 #define MPEG_FF BUTTON_NEXT
329 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
330 #define MPEG_MENU BUTTON_FUNC
331 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
332 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
333 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
334 #define MPEG_VOLUP BUTTON_VOL_UP
335 #define MPEG_RW BUTTON_REW
336 #define MPEG_FF BUTTON_FF
338 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
339 #define MPEG_MENU BUTTON_MENU
340 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
341 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
342 #define MPEG_VOLDOWN BUTTON_DOWN
343 #define MPEG_VOLUP BUTTON_UP
344 #define MPEG_RW BUTTON_REW
345 #define MPEG_FF BUTTON_FF
347 #else
348 #error No keymap defined!
349 #endif
351 #ifdef HAVE_TOUCHSCREEN
352 #ifndef MPEG_MENU
353 #define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
354 #endif
355 #ifndef MPEG_STOP
356 #define MPEG_STOP BUTTON_TOPLEFT
357 #endif
358 #ifndef MPEG_PAUSE
359 #define MPEG_PAUSE BUTTON_CENTER
360 #endif
361 #ifndef MPEG_VOLDOWN
362 #define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
363 #endif
364 #ifndef MPEG_VOLUP
365 #define MPEG_VOLUP BUTTON_TOPMIDDLE
366 #endif
367 #ifndef MPEG_RW
368 #define MPEG_RW BUTTON_MIDLEFT
369 #endif
370 #ifndef MPEG_FF
371 #define MPEG_FF BUTTON_MIDRIGHT
372 #endif
373 #endif
375 /* One thing we can do here for targets with remotes is having a display
376 * always on the remote instead of always forcing a popup on the main display */
378 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
379 /* 3% of 30min file == 54s step size */
380 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
381 #define OSD_MIN_UPDATE_INTERVAL (HZ/2)
382 #define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */
384 enum video_action
386 VIDEO_STOP = 0,
387 VIDEO_PREV,
388 VIDEO_NEXT,
391 /* OSD status - same order as icon array */
392 enum osd_status_enum
394 OSD_STATUS_STOPPED = 0,
395 OSD_STATUS_PAUSED,
396 OSD_STATUS_PLAYING,
397 OSD_STATUS_FF,
398 OSD_STATUS_RW,
399 OSD_STATUS_COUNT,
400 OSD_STATUS_MASK = 0x7
403 enum osd_bits
405 OSD_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
406 /* Refresh the... */
407 OSD_REFRESH_VOLUME = 0x0001, /* ...volume display */
408 OSD_REFRESH_TIME = 0x0002, /* ...time display+progress */
409 OSD_REFRESH_STATUS = 0x0004, /* ...playback status icon */
410 OSD_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
411 OSD_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
412 OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
413 OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
414 OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */
415 #ifdef HAVE_HEADPHONE_DETECTION
416 OSD_HP_PAUSE = 0x2000, /* OR bitflag - headphones caused pause */
417 #endif
418 OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */
419 OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
422 /* Status icons selected according to font height */
423 extern const unsigned char mpegplayer_status_icons_8x8x1[];
424 extern const unsigned char mpegplayer_status_icons_12x12x1[];
425 extern const unsigned char mpegplayer_status_icons_16x16x1[];
427 /* Main border areas that contain OSD elements */
428 #define OSD_BDR_L 2
429 #define OSD_BDR_T 2
430 #define OSD_BDR_R 2
431 #define OSD_BDR_B 2
433 struct osd
435 long hide_tick;
436 long show_for;
437 long print_tick;
438 long print_delay;
439 long resume_tick;
440 long resume_delay;
441 long next_auto_refresh;
442 int x;
443 int y;
444 int width;
445 int height;
446 unsigned fgcolor;
447 unsigned bgcolor;
448 unsigned prog_fillcolor;
449 struct vo_rect update_rect;
450 struct vo_rect prog_rect;
451 struct vo_rect time_rect;
452 struct vo_rect dur_rect;
453 struct vo_rect vol_rect;
454 const unsigned char *icons;
455 struct vo_rect stat_rect;
456 int status;
457 uint32_t curr_time;
458 unsigned auto_refresh;
459 unsigned flags;
460 int font;
463 struct fps
465 /* FPS Display */
466 struct vo_rect rect; /* OSD coordinates */
467 int pf_x; /* Screen coordinates */
468 int pf_y;
469 int pf_width;
470 int pf_height;
471 long update_tick; /* When to next update FPS reading */
472 #define FPS_FORMAT "%d.%02d"
473 #define FPS_DIMSTR "999.99" /* For establishing rect size */
474 #define FPS_BUFSIZE sizeof("999.99")
477 static struct osd osd;
478 static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */
480 static void osd_show(unsigned show);
482 #ifdef LCD_LANDSCAPE
483 #define _X (x + osd.x)
484 #define _Y (y + osd.y)
485 #define _W width
486 #define _H height
487 #else
488 #define _X (LCD_WIDTH - (y + osd.y) - height)
489 #define _Y (x + osd.x)
490 #define _W height
491 #define _H width
492 #endif
494 #ifdef HAVE_LCD_COLOR
495 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
496 static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
498 int r1 = RGB_UNPACK_RED(c1);
499 int g1 = RGB_UNPACK_GREEN(c1);
500 int b1 = RGB_UNPACK_BLUE(c1);
502 int r2 = RGB_UNPACK_RED(c2);
503 int g2 = RGB_UNPACK_GREEN(c2);
504 int b2 = RGB_UNPACK_BLUE(c2);
506 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
507 amount*(g2 - g1) / 255 + g1,
508 amount*(b2 - b1) / 255 + b1);
510 #endif
512 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
513 * most are just wrappers of lcd_* functions with transforms applied.
514 * The origin is the upper-left corner of the OSD area */
515 static void draw_update_rect(int x, int y, int width, int height)
517 mylcd_update_rect(_X, _Y, _W, _H);
520 static void draw_clear_area(int x, int y, int width, int height)
522 #ifdef HAVE_LCD_COLOR
523 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
524 #else
525 int oldmode = grey_get_drawmode();
526 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
527 grey_fillrect(_X, _Y, _W, _H);
528 grey_set_drawmode(oldmode);
529 #endif
532 static void draw_clear_area_rect(const struct vo_rect *rc)
534 draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t);
537 static void draw_fillrect(int x, int y, int width, int height)
539 mylcd_fillrect(_X, _Y, _W, _H);
542 static void draw_hline(int x1, int x2, int y)
544 #ifdef LCD_LANDSCAPE
545 mylcd_hline(x1 + osd.x, x2 + osd.x, y + osd.y);
546 #else
547 y = LCD_WIDTH - (y + osd.y) - 1;
548 mylcd_vline(y, x1 + osd.x, x2 + osd.x);
549 #endif
552 static void draw_vline(int x, int y1, int y2)
554 #ifdef LCD_LANDSCAPE
555 mylcd_vline(x + osd.x, y1 + osd.y, y2 + osd.y);
556 #else
557 y1 = LCD_WIDTH - (y1 + osd.y) - 1;
558 y2 = LCD_WIDTH - (y2 + osd.y) - 1;
559 mylcd_hline(y1, y2, x + osd.x);
560 #endif
563 static void draw_scrollbar_draw(int x, int y, int width, int height,
564 uint32_t min, uint32_t max, uint32_t val)
566 unsigned oldfg = mylcd_get_foreground();
568 draw_hline(x + 1, x + width - 2, y);
569 draw_hline(x + 1, x + width - 2, y + height - 1);
570 draw_vline(x, y + 1, y + height - 2);
571 draw_vline(x + width - 1, y + 1, y + height - 2);
573 val = muldiv_uint32(width - 2, val, max - min);
574 val = MIN(val, (uint32_t)(width - 2));
576 draw_fillrect(x + 1, y + 1, val, height - 2);
578 mylcd_set_foreground(osd.prog_fillcolor);
580 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
582 mylcd_set_foreground(oldfg);
585 static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
586 int max, int val)
588 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
589 min, max, val);
592 static void draw_setfont(int font)
594 osd.font = font;
595 mylcd_setfont(font);
598 #ifdef LCD_PORTRAIT
599 /* Portrait displays need rotated text rendering */
601 /* Limited function that only renders in DRMODE_FG and uses absolute screen
602 * coordinates */
603 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
604 int src_x, int src_y,
605 int stride, int x, int y,
606 int width, int height)
608 const unsigned char *src_end;
609 fb_data *dst, *dst_end;
610 unsigned fg_pattern;
612 if (x + width > SCREEN_WIDTH)
613 width = SCREEN_WIDTH - x; /* Clip right */
614 if (x < 0)
615 width += x, x = 0; /* Clip left */
616 if (width <= 0)
617 return; /* nothing left to do */
619 if (y + height > SCREEN_HEIGHT)
620 height = SCREEN_HEIGHT - y; /* Clip bottom */
621 if (y < 0)
622 height += y, y = 0; /* Clip top */
623 if (height <= 0)
624 return; /* nothing left to do */
626 fg_pattern = rb->lcd_get_foreground();
627 /*bg_pattern =*/ rb->lcd_get_background();
629 src += stride * (src_y >> 3) + src_x; /* move starting point */
630 src_y &= 7;
631 src_end = src + width;
633 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
636 const unsigned char *src_col = src++;
637 unsigned data = *src_col >> src_y;
638 int numbits = 8 - src_y;
640 fb_data *dst_col = dst;
641 dst_end = dst_col - height;
642 dst += LCD_WIDTH;
646 dst_col--;
648 if (data & 1)
649 *dst_col = fg_pattern;
650 #if 0
651 else
652 *dst_col = bg_pattern;
653 #endif
654 data >>= 1;
655 if (--numbits == 0) {
656 src_col += stride;
657 data = *src_col;
658 numbits = 8;
661 while (dst_col > dst_end);
663 while (src < src_end);
666 /* draw alpha bitmap for anti-alias font */
667 #define ALPHA_COLOR_FONT_DEPTH 2
668 #define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
669 #define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
670 #define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
671 #define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
672 #ifdef CPU_ARM
673 #define BLEND_INIT do {} while (0)
674 #define BLEND_START(acc, color, alpha) \
675 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
676 #define BLEND_CONT(acc, color, alpha) \
677 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
678 #define BLEND_OUT(acc) do {} while (0)
679 #elif defined(CPU_COLDFIRE)
680 #define ALPHA_BITMAP_READ_WORDS
681 #define BLEND_INIT coldfire_set_macsr(EMAC_UNSIGNED)
682 #define BLEND_START(acc, color, alpha) \
683 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
684 #define BLEND_CONT BLEND_START
685 #define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
686 #else
687 #define BLEND_INIT do {} while (0)
688 #define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
689 #define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
690 #define BLEND_OUT(acc) do {} while (0)
691 #endif
693 /* Blend the given two colors */
694 static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
696 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
697 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
698 c1 = swap16(c1);
699 c2 = swap16(c2);
700 #endif
701 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
702 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
703 unsigned p;
704 BLEND_START(p, c1l, a);
705 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
706 BLEND_OUT(p);
707 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
708 p |= (p >> 16);
709 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
710 return swap16(p);
711 #else
712 return p;
713 #endif
716 static void draw_oriented_alpha_bitmap_part(const unsigned char *src,
717 int src_x, int src_y,
718 int stride, int x, int y,
719 int width, int height)
721 fb_data *dst, *dst_start;
722 unsigned fg_pattern;
724 if (x + width > SCREEN_WIDTH)
725 width = SCREEN_WIDTH - x; /* Clip right */
726 if (x < 0)
727 width += x, x = 0; /* Clip left */
728 if (width <= 0)
729 return; /* nothing left to do */
731 if (y + height > SCREEN_HEIGHT)
732 height = SCREEN_HEIGHT - y; /* Clip bottom */
733 if (y < 0)
734 height += y, y = 0; /* Clip top */
735 if (height <= 0)
736 return; /* nothing left to do */
738 /* initialize blending */
739 BLEND_INIT;
741 fg_pattern = rb->lcd_get_foreground();
742 /*bg_pattern=*/ rb->lcd_get_background();
744 dst_start = rb->lcd_framebuffer + (LCD_WIDTH - y - 1) + x*LCD_WIDTH;
745 int col, row = height;
746 unsigned data, pixels;
747 unsigned skip_end = (stride - width);
748 unsigned skip_start = src_y * stride + src_x;
750 #ifdef ALPHA_BITMAP_READ_WORDS
751 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
752 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
753 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
754 data = letoh32(*src_w++);
755 #else
756 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
757 data = *src;
758 #endif
759 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
760 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
761 #ifdef ALPHA_BITMAP_READ_WORDS
762 pixels = 8 - pixels;
763 #endif
767 col = width;
768 dst = dst_start--;
769 #ifdef ALPHA_BITMAP_READ_WORDS
770 #define UPDATE_SRC_ALPHA do { \
771 if (--pixels) \
772 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
773 else \
775 data = letoh32(*src_w++); \
776 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
778 } while (0)
779 #elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
780 #define UPDATE_SRC_ALPHA do { \
781 if (pixels ^= 1) \
782 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
783 else \
784 data = *(++src); \
785 } while (0)
786 #else
787 #define UPDATE_SRC_ALPHA do { \
788 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
789 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
790 else \
791 data = *(++src); \
792 } while (0)
793 #endif
796 *dst=blend_two_colors(*dst, fg_pattern,
797 data & ALPHA_COLOR_LOOKUP_SIZE );
798 dst += LCD_WIDTH;
799 UPDATE_SRC_ALPHA;
801 while (--col);
802 #ifdef ALPHA_BITMAP_READ_WORDS
803 if (skip_end < pixels)
805 pixels -= skip_end;
806 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
807 } else {
808 pixels = skip_end - pixels;
809 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
810 pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
811 data = letoh32(*src_w++);
812 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
813 pixels = 8 - pixels;
815 #else
816 if (skip_end)
818 pixels += skip_end;
819 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
821 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
822 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
823 data = *src;
824 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
825 } else
826 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
828 #endif
829 } while (--row);
832 static void draw_putsxy_oriented(int x, int y, const char *str)
834 unsigned short ch;
835 unsigned short *ucs;
836 int ofs = MIN(x, 0);
837 struct font* pf = rb->font_get(osd.font);
839 ucs = rb->bidi_l2v(str, 1);
841 x += osd.x;
842 y += osd.y;
844 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
846 int width;
847 const unsigned char *bits;
849 /* get proportional width and glyph bits */
850 width = rb->font_get_width(pf, ch);
852 if (ofs > width) {
853 ofs -= width;
854 continue;
857 bits = rb->font_get_bits(pf, ch);
859 if (pf->depth)
860 draw_oriented_alpha_bitmap_part(bits, ofs, 0, width, x, y,
861 width - ofs, pf->height);
862 else
863 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
864 width - ofs, pf->height);
866 x += width - ofs;
867 ofs = 0;
870 #else
871 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
872 int src_x, int src_y,
873 int stride, int x, int y,
874 int width, int height)
876 int mode = mylcd_get_drawmode();
877 mylcd_set_drawmode(DRMODE_FG);
878 mylcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height);
879 mylcd_set_drawmode(mode);
882 static void draw_putsxy_oriented(int x, int y, const char *str)
884 int mode = mylcd_get_drawmode();
885 mylcd_set_drawmode(DRMODE_FG);
886 mylcd_putsxy(x + osd.x, y + osd.y, str);
887 mylcd_set_drawmode(mode);
889 #endif /* LCD_PORTRAIT */
891 /** FPS Display **/
893 /* Post-frame callback (on video thread) - update the FPS rectangle from the
894 * framebuffer */
895 static void fps_post_frame_callback(void)
897 vo_lock();
898 mylcd_update_rect(fps.pf_x, fps.pf_y,
899 fps.pf_width, fps.pf_height);
900 vo_unlock();
903 /* Set up to have the callback only update the intersection of the video
904 * rectangle and the FPS text rectangle - if they don't intersect, then
905 * the callback is set to NULL */
906 static void fps_update_post_frame_callback(void)
908 void (*cb)(void) = NULL;
910 if (settings.showfps) {
911 struct vo_rect cliprect;
913 if (stream_vo_get_clip(&cliprect)) {
914 /* Oriented screen coordinates -> OSD coordinates */
915 vo_rect_offset(&cliprect, -osd.x, -osd.y);
917 if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) {
918 int x = cliprect.l;
919 int y = cliprect.t;
920 int width = cliprect.r - cliprect.l;
921 int height = cliprect.b - cliprect.t;
923 /* OSD coordinates -> framebuffer coordinates */
924 fps.pf_x = _X;
925 fps.pf_y = _Y;
926 fps.pf_width = _W;
927 fps.pf_height = _H;
929 cb = fps_post_frame_callback;
934 stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb);
937 /* Refresh the FPS display */
938 static void fps_refresh(void)
940 char str[FPS_BUFSIZE];
941 struct video_output_stats stats;
942 int w, h, sw;
943 long tick;
945 tick = *rb->current_tick;
947 if (TIME_BEFORE(tick, fps.update_tick))
948 return;
950 fps.update_tick = tick + FPS_UPDATE_INTERVAL;
952 stream_video_stats(&stats);
954 rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT,
955 stats.fps / 100, stats.fps % 100);
957 w = fps.rect.r - fps.rect.l;
958 h = fps.rect.b - fps.rect.t;
960 draw_clear_area(fps.rect.l, fps.rect.t, w, h);
961 mylcd_getstringsize(str, &sw, NULL);
962 draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str);
964 vo_lock();
965 draw_update_rect(fps.rect.l, fps.rect.t, w, h);
966 vo_unlock();
969 /* Initialize the FPS display */
970 static void fps_init(void)
972 fps.update_tick = *rb->current_tick;
973 fps.rect.l = fps.rect.t = 0;
974 mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b);
975 vo_rect_offset(&fps.rect, -osd.x, -osd.y);
976 fps_update_post_frame_callback();
979 /** OSD **/
981 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
982 /* So we can refresh the overlay */
983 static void osd_lcd_enable_hook(void* param)
985 (void)param;
986 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
988 #endif
990 static void osd_backlight_on_video_mode(bool video_on)
992 if (video_on) {
993 /* Turn off backlight timeout */
994 backlight_ignore_timeout();
995 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
996 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
997 #endif
998 } else {
999 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1000 rb->add_event(LCD_EVENT_ACTIVATION, false, osd_lcd_enable_hook);
1001 #endif
1002 /* Revert to user's backlight settings */
1003 backlight_use_settings();
1007 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
1008 static void osd_backlight_brightness_video_mode(bool video_on)
1010 if (settings.backlight_brightness < 0)
1011 return;
1013 mpeg_backlight_update_brightness(
1014 video_on ? settings.backlight_brightness : -1);
1016 #else
1017 #define osd_backlight_brightness_video_mode(video_on)
1018 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
1020 static void osd_text_init(void)
1022 struct hms hms;
1023 char buf[32];
1024 int phys;
1025 int spc_width;
1027 draw_setfont(FONT_UI);
1029 osd.x = 0;
1030 osd.width = SCREEN_WIDTH;
1032 vo_rect_clear(&osd.time_rect);
1033 vo_rect_clear(&osd.stat_rect);
1034 vo_rect_clear(&osd.prog_rect);
1035 vo_rect_clear(&osd.vol_rect);
1037 ts_to_hms(stream_get_duration(), &hms);
1038 hms_format(buf, sizeof (buf), &hms);
1039 mylcd_getstringsize(buf, &osd.time_rect.r, &osd.time_rect.b);
1041 /* Choose well-sized bitmap images relative to font height */
1042 if (osd.time_rect.b < 12) {
1043 osd.icons = mpegplayer_status_icons_8x8x1;
1044 osd.stat_rect.r = osd.stat_rect.b = 8;
1045 } else if (osd.time_rect.b < 16) {
1046 osd.icons = mpegplayer_status_icons_12x12x1;
1047 osd.stat_rect.r = osd.stat_rect.b = 12;
1048 } else {
1049 osd.icons = mpegplayer_status_icons_16x16x1;
1050 osd.stat_rect.r = osd.stat_rect.b = 16;
1053 if (osd.stat_rect.b < osd.time_rect.b) {
1054 vo_rect_offset(&osd.stat_rect, 0,
1055 (osd.time_rect.b - osd.stat_rect.b) / 2 + OSD_BDR_T);
1056 vo_rect_offset(&osd.time_rect, OSD_BDR_L, OSD_BDR_T);
1057 } else {
1058 vo_rect_offset(&osd.time_rect, OSD_BDR_L,
1059 osd.stat_rect.b - osd.time_rect.b + OSD_BDR_T);
1060 vo_rect_offset(&osd.stat_rect, 0, OSD_BDR_T);
1063 osd.dur_rect = osd.time_rect;
1065 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
1066 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
1067 rb->sound_unit(SOUND_VOLUME));
1069 mylcd_getstringsize(" ", &spc_width, NULL);
1070 mylcd_getstringsize(buf, &osd.vol_rect.r, &osd.vol_rect.b);
1072 osd.prog_rect.r = SCREEN_WIDTH - OSD_BDR_L - spc_width -
1073 osd.vol_rect.r - OSD_BDR_R;
1074 osd.prog_rect.b = 3*osd.stat_rect.b / 4;
1075 vo_rect_offset(&osd.prog_rect, osd.time_rect.l,
1076 osd.time_rect.b);
1078 vo_rect_offset(&osd.stat_rect,
1079 (osd.prog_rect.r + osd.prog_rect.l - osd.stat_rect.r) / 2,
1082 vo_rect_offset(&osd.dur_rect,
1083 osd.prog_rect.r - osd.dur_rect.r, 0);
1085 vo_rect_offset(&osd.vol_rect, osd.prog_rect.r + spc_width,
1086 (osd.prog_rect.b + osd.prog_rect.t - osd.vol_rect.b) / 2);
1088 osd.height = OSD_BDR_T + MAX(osd.prog_rect.b, osd.vol_rect.b) -
1089 MIN(osd.time_rect.t, osd.stat_rect.t) + OSD_BDR_B;
1091 #ifdef HAVE_LCD_COLOR
1092 osd.height = ALIGN_UP(osd.height, 2);
1093 #endif
1094 osd.y = SCREEN_HEIGHT - osd.height;
1096 draw_setfont(FONT_SYSFIXED);
1099 static void osd_init(void)
1101 osd.flags = 0;
1102 osd.show_for = HZ*4;
1103 osd.print_delay = 75*HZ/100;
1104 osd.resume_delay = HZ/2;
1105 #ifdef HAVE_LCD_COLOR
1106 osd.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
1107 osd.fgcolor = LCD_WHITE;
1108 osd.prog_fillcolor = LCD_BLACK;
1109 #else
1110 osd.bgcolor = GREY_LIGHTGRAY;
1111 osd.fgcolor = GREY_BLACK;
1112 osd.prog_fillcolor = GREY_WHITE;
1113 #endif
1114 osd.curr_time = 0;
1115 osd.status = OSD_STATUS_STOPPED;
1116 osd.auto_refresh = OSD_REFRESH_TIME;
1117 osd.next_auto_refresh = *rb->current_tick;
1118 osd_text_init();
1119 fps_init();
1122 #ifdef HAVE_HEADPHONE_DETECTION
1123 static void osd_set_hp_pause_flag(bool set)
1125 if (set)
1126 osd.flags |= OSD_HP_PAUSE;
1127 else
1128 osd.flags &= ~OSD_HP_PAUSE;
1130 #else
1131 #define osd_set_hp_pause_flag(set)
1132 #endif /* HAVE_HEADPHONE_DETECTION */
1134 static void osd_schedule_refresh(unsigned refresh)
1136 long tick = *rb->current_tick;
1138 if (refresh & OSD_REFRESH_VIDEO)
1139 osd.print_tick = tick + osd.print_delay;
1141 if (refresh & OSD_REFRESH_RESUME)
1142 osd.resume_tick = tick + osd.resume_delay;
1144 osd.auto_refresh |= refresh;
1147 static void osd_cancel_refresh(unsigned refresh)
1149 osd.auto_refresh &= ~refresh;
1152 /* Refresh the background area */
1153 static void osd_refresh_background(void)
1155 char buf[32];
1156 struct hms hms;
1158 unsigned bg = mylcd_get_background();
1159 mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
1161 #ifdef HAVE_LCD_COLOR
1162 /* Draw a "raised" area for our graphics */
1163 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 192));
1164 draw_hline(0, osd.width, 0);
1166 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 80));
1167 draw_hline(0, osd.width, 1);
1169 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 48));
1170 draw_hline(0, osd.width, osd.height-2);
1172 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 128));
1173 draw_hline(0, osd.width, osd.height-1);
1175 mylcd_set_background(bg);
1176 draw_clear_area(0, 2, osd.width, osd.height - 4);
1177 #else
1178 /* Give contrast with the main background */
1179 mylcd_set_background(MYLCD_WHITE);
1180 draw_hline(0, osd.width, 0);
1182 mylcd_set_background(MYLCD_DARKGRAY);
1183 draw_hline(0, osd.width, osd.height-1);
1185 mylcd_set_background(bg);
1186 draw_clear_area(0, 1, osd.width, osd.height - 2);
1187 #endif
1189 vo_rect_set_ext(&osd.update_rect, 0, 0, osd.width, osd.height);
1190 mylcd_set_drawmode(DRMODE_SOLID);
1192 if (stream_get_duration() != INVALID_TIMESTAMP) {
1193 /* Draw the movie duration */
1194 ts_to_hms(stream_get_duration(), &hms);
1195 hms_format(buf, sizeof (buf), &hms);
1196 draw_putsxy_oriented(osd.dur_rect.l, osd.dur_rect.t, buf);
1198 /* else don't know the duration */
1201 /* Refresh the current time display + the progress bar */
1202 static void osd_refresh_time(void)
1204 char buf[32];
1205 struct hms hms;
1207 uint32_t duration = stream_get_duration();
1209 draw_scrollbar_draw_rect(&osd.prog_rect, 0, duration,
1210 osd.curr_time);
1212 ts_to_hms(osd.curr_time, &hms);
1213 hms_format(buf, sizeof (buf), &hms);
1215 draw_clear_area_rect(&osd.time_rect);
1216 draw_putsxy_oriented(osd.time_rect.l, osd.time_rect.t, buf);
1218 vo_rect_union(&osd.update_rect, &osd.update_rect,
1219 &osd.prog_rect);
1220 vo_rect_union(&osd.update_rect, &osd.update_rect,
1221 &osd.time_rect);
1224 /* Refresh the volume display area */
1225 static void osd_refresh_volume(void)
1227 char buf[32];
1228 int width;
1230 int volume = rb->global_settings->volume;
1231 rb->snprintf(buf, sizeof (buf), "%d%s",
1232 rb->sound_val2phys(SOUND_VOLUME, volume),
1233 rb->sound_unit(SOUND_VOLUME));
1234 mylcd_getstringsize(buf, &width, NULL);
1236 /* Right-justified */
1237 draw_clear_area_rect(&osd.vol_rect);
1238 draw_putsxy_oriented(osd.vol_rect.r - width, osd.vol_rect.t, buf);
1240 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.vol_rect);
1243 /* Refresh the status icon */
1244 static void osd_refresh_status(void)
1246 int icon_size = osd.stat_rect.r - osd.stat_rect.l;
1248 draw_clear_area_rect(&osd.stat_rect);
1250 #ifdef HAVE_LCD_COLOR
1251 /* Draw status icon with a drop shadow */
1252 unsigned oldfg = mylcd_get_foreground();
1253 int i = 1;
1255 mylcd_set_foreground(draw_blendcolor(mylcd_get_background(),
1256 MYLCD_BLACK, 96));
1258 while (1)
1260 draw_oriented_mono_bitmap_part(osd.icons,
1261 icon_size*osd.status,
1263 icon_size*OSD_STATUS_COUNT,
1264 osd.stat_rect.l + osd.x + i,
1265 osd.stat_rect.t + osd.y + i,
1266 icon_size, icon_size);
1268 if (--i < 0)
1269 break;
1271 mylcd_set_foreground(oldfg);
1274 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1275 #else
1276 draw_oriented_mono_bitmap_part(osd.icons,
1277 icon_size*osd.status,
1279 icon_size*OSD_STATUS_COUNT,
1280 osd.stat_rect.l + osd.x,
1281 osd.stat_rect.t + osd.y,
1282 icon_size, icon_size);
1283 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1284 #endif
1287 /* Update the current status which determines which icon is displayed */
1288 static bool osd_update_status(void)
1290 int status;
1292 switch (stream_status())
1294 default:
1295 status = OSD_STATUS_STOPPED;
1296 break;
1297 case STREAM_PAUSED:
1298 /* If paused with a pending resume, coerce it to OSD_STATUS_PLAYING */
1299 status = (osd.auto_refresh & OSD_REFRESH_RESUME) ?
1300 OSD_STATUS_PLAYING : OSD_STATUS_PAUSED;
1301 break;
1302 case STREAM_PLAYING:
1303 status = OSD_STATUS_PLAYING;
1304 break;
1307 if (status != osd.status) {
1308 /* A refresh is needed */
1309 osd.status = status;
1310 return true;
1313 return false;
1316 /* Update the current time that will be displayed */
1317 static void osd_update_time(void)
1319 uint32_t start;
1320 osd.curr_time = stream_get_seek_time(&start);
1321 osd.curr_time -= start;
1324 /* Refresh various parts of the OSD - showing it if it is hidden */
1325 static void osd_refresh(int hint)
1327 long tick;
1328 unsigned oldbg, oldfg;
1330 tick = *rb->current_tick;
1332 if (settings.showfps)
1333 fps_refresh();
1335 if (hint == OSD_REFRESH_DEFAULT) {
1336 /* The default which forces no updates */
1338 /* Make sure Rockbox doesn't turn off the player because of
1339 too little activity */
1340 if (osd.status == OSD_STATUS_PLAYING)
1341 rb->reset_poweroff_timer();
1343 /* Redraw the current or possibly extract a new video frame */
1344 if ((osd.auto_refresh & OSD_REFRESH_VIDEO) &&
1345 TIME_AFTER(tick, osd.print_tick)) {
1346 osd.auto_refresh &= ~OSD_REFRESH_VIDEO;
1347 stream_draw_frame(false);
1350 /* Restart playback if the timout was reached */
1351 if ((osd.auto_refresh & OSD_REFRESH_RESUME) &&
1352 TIME_AFTER(tick, osd.resume_tick)) {
1353 osd.auto_refresh &= ~(OSD_REFRESH_RESUME | OSD_REFRESH_VIDEO);
1354 stream_resume();
1357 /* If not visible, return */
1358 if (!(osd.flags & OSD_SHOW))
1359 return;
1361 /* Hide if the visibility duration was reached */
1362 if (TIME_AFTER(tick, osd.hide_tick)) {
1363 osd_show(OSD_HIDE);
1364 return;
1366 } else {
1367 /* A forced update of some region */
1369 /* Show if currently invisible */
1370 if (!(osd.flags & OSD_SHOW)) {
1371 /* Avoid call back into this function - it will be drawn */
1372 osd_show(OSD_SHOW | OSD_NODRAW);
1373 hint = OSD_REFRESH_ALL;
1376 /* Move back timeouts for frame print and hide */
1377 osd.print_tick = tick + osd.print_delay;
1378 osd.hide_tick = tick + osd.show_for;
1381 if (TIME_AFTER(tick, osd.next_auto_refresh)) {
1382 /* Refresh whatever graphical elements are due automatically */
1383 osd.next_auto_refresh = tick + OSD_MIN_UPDATE_INTERVAL;
1385 if (osd.auto_refresh & OSD_REFRESH_STATUS) {
1386 if (osd_update_status())
1387 hint |= OSD_REFRESH_STATUS;
1390 if (osd.auto_refresh & OSD_REFRESH_TIME) {
1391 osd_update_time();
1392 hint |= OSD_REFRESH_TIME;
1396 if (hint == 0)
1397 return; /* No drawing needed */
1399 /* Set basic drawing params that are used. Elements that perform variations
1400 * will restore them. */
1401 oldfg = mylcd_get_foreground();
1402 oldbg = mylcd_get_background();
1404 draw_setfont(FONT_UI);
1405 mylcd_set_foreground(osd.fgcolor);
1406 mylcd_set_background(osd.bgcolor);
1408 vo_rect_clear(&osd.update_rect);
1410 if (hint & OSD_REFRESH_BACKGROUND) {
1411 osd_refresh_background();
1412 hint |= OSD_REFRESH_ALL; /* Requires a redraw of everything */
1415 if (hint & OSD_REFRESH_TIME) {
1416 osd_refresh_time();
1419 if (hint & OSD_REFRESH_VOLUME) {
1420 osd_refresh_volume();
1423 if (hint & OSD_REFRESH_STATUS) {
1424 osd_refresh_status();
1427 /* Go back to defaults */
1428 draw_setfont(FONT_SYSFIXED);
1429 mylcd_set_foreground(oldfg);
1430 mylcd_set_background(oldbg);
1432 /* Update the dirty rectangle */
1433 vo_lock();
1435 draw_update_rect(osd.update_rect.l,
1436 osd.update_rect.t,
1437 osd.update_rect.r - osd.update_rect.l,
1438 osd.update_rect.b - osd.update_rect.t);
1440 vo_unlock();
1443 /* Show/Hide the OSD */
1444 static void osd_show(unsigned show)
1446 if (((show ^ osd.flags) & OSD_SHOW) == 0)
1448 if (show & OSD_SHOW) {
1449 osd.hide_tick = *rb->current_tick + osd.show_for;
1451 return;
1454 if (show & OSD_SHOW) {
1455 /* Clip away the part of video that is covered */
1456 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, osd.y };
1458 osd.flags |= OSD_SHOW;
1460 if (osd.status != OSD_STATUS_PLAYING) {
1461 /* Not playing - set brightness to mpegplayer setting */
1462 osd_backlight_brightness_video_mode(true);
1465 stream_vo_set_clip(&rc);
1467 if (!(show & OSD_NODRAW))
1468 osd_refresh(OSD_REFRESH_ALL);
1469 } else {
1470 /* Uncover clipped video area and redraw it */
1471 osd.flags &= ~OSD_SHOW;
1473 draw_clear_area(0, 0, osd.width, osd.height);
1475 if (!(show & OSD_NODRAW)) {
1476 vo_lock();
1477 draw_update_rect(0, 0, osd.width, osd.height);
1478 vo_unlock();
1480 stream_vo_set_clip(NULL);
1481 stream_draw_frame(false);
1482 } else {
1483 stream_vo_set_clip(NULL);
1486 if (osd.status != OSD_STATUS_PLAYING) {
1487 /* Not playing - restore backlight brightness */
1488 osd_backlight_brightness_video_mode(false);
1493 /* Set the current status - update screen if specified */
1494 static void osd_set_status(int status)
1496 bool draw = (status & OSD_NODRAW) == 0;
1498 status &= OSD_STATUS_MASK;
1500 if (osd.status != status) {
1502 osd.status = status;
1504 if (draw)
1505 osd_refresh(OSD_REFRESH_STATUS);
1509 /* Get the current status value */
1510 static int osd_get_status(void)
1512 return osd.status & OSD_STATUS_MASK;
1515 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;)
1516 * Returns last button code
1518 static int osd_ff_rw(int btn, unsigned refresh, uint32_t *new_time)
1520 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1521 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1522 uint32_t start;
1523 uint32_t time = stream_get_seek_time(&start);
1524 const uint32_t duration = stream_get_duration();
1525 unsigned int max_step = 0;
1526 uint32_t ff_rw_count = 0;
1527 unsigned status = osd.status;
1528 int new_btn;
1530 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME |
1531 OSD_REFRESH_TIME);
1533 time -= start; /* Absolute clock => stream-relative */
1535 switch (btn)
1537 case MPEG_FF:
1538 #ifdef MPEG_FF2
1539 case MPEG_FF2:
1540 #endif
1541 #ifdef MPEG_RC_FF
1542 case MPEG_RC_FF:
1543 #endif
1544 osd_set_status(OSD_STATUS_FF);
1545 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1546 break;
1547 case MPEG_RW:
1548 #ifdef MPEG_RW2
1549 case MPEG_RW2:
1550 #endif
1551 #ifdef MPEG_RC_RW
1552 case MPEG_RC_RW:
1553 #endif
1554 osd_set_status(OSD_STATUS_RW);
1555 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1556 break;
1557 default:
1558 new_btn = BUTTON_NONE; /* Fail tests below but still do proper exit */
1561 while (1)
1563 stream_keep_disk_active();
1565 if (new_btn == (btn | BUTTON_REPEAT)) {
1566 if (osd.status == OSD_STATUS_FF) {
1567 /* fast forwarding, calc max step relative to end */
1568 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1569 FF_REWIND_MAX_PERCENT, 100);
1570 } else {
1571 /* rewinding, calc max step relative to start */
1572 max_step = muldiv_uint32(time - ff_rw_count,
1573 FF_REWIND_MAX_PERCENT, 100);
1576 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1578 if (step > max_step)
1579 step = max_step;
1581 ff_rw_count += step;
1583 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1584 step += step >> ff_rw_accel;
1586 if (osd.status == OSD_STATUS_FF) {
1587 if (duration - time <= ff_rw_count)
1588 ff_rw_count = duration - time;
1590 osd.curr_time = time + ff_rw_count;
1591 } else {
1592 if (time <= ff_rw_count)
1593 ff_rw_count = time;
1595 osd.curr_time = time - ff_rw_count;
1598 osd_refresh(OSD_REFRESH_TIME);
1600 new_btn = mpeg_button_get(TIMEOUT_BLOCK);
1602 else {
1603 if (new_btn == (btn | BUTTON_REL)) {
1604 if (osd.status == OSD_STATUS_FF)
1605 time += ff_rw_count;
1606 else if (osd.status == OSD_STATUS_RW)
1607 time -= ff_rw_count;
1610 *new_time = time;
1612 osd_schedule_refresh(refresh);
1613 osd_set_status(status);
1614 osd_schedule_refresh(OSD_REFRESH_TIME);
1616 return new_btn;
1621 /* Return adjusted STREAM_* status */
1622 static int osd_stream_status(void)
1624 int status = stream_status();
1626 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1627 if (status == STREAM_PAUSED) {
1628 if (osd.auto_refresh & OSD_REFRESH_RESUME)
1629 status = STREAM_PLAYING;
1632 return status;
1635 /* Change the current audio volume by a specified amount */
1636 static void osd_set_volume(int delta)
1638 int vol = rb->global_settings->volume;
1639 int limit;
1641 vol += delta;
1643 if (delta < 0) {
1644 /* Volume down - clip to lower limit */
1645 limit = rb->sound_min(SOUND_VOLUME);
1646 if (vol < limit)
1647 vol = limit;
1648 } else {
1649 /* Volume up - clip to upper limit */
1650 limit = rb->sound_max(SOUND_VOLUME);
1651 if (vol > limit)
1652 vol = limit;
1655 /* Sync the global settings */
1656 if (vol != rb->global_settings->volume) {
1657 rb->sound_set(SOUND_VOLUME, vol);
1658 rb->global_settings->volume = vol;
1661 /* Update the volume display */
1662 osd_refresh(OSD_REFRESH_VOLUME);
1665 /* Begin playback at the specified time */
1666 static int osd_play(uint32_t time)
1668 int retval;
1670 osd_set_hp_pause_flag(false);
1671 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1673 retval = stream_seek(time, SEEK_SET);
1675 if (retval >= STREAM_OK) {
1676 osd_backlight_on_video_mode(true);
1677 osd_backlight_brightness_video_mode(true);
1678 stream_show_vo(true);
1680 retval = stream_play();
1682 if (retval >= STREAM_OK)
1683 osd_set_status(OSD_STATUS_PLAYING | OSD_NODRAW);
1686 return retval;
1689 /* Halt playback - pause engine and return logical state */
1690 static int osd_halt(void)
1692 int status = stream_pause();
1694 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1695 if (status == STREAM_PAUSED) {
1696 if (osd_get_status() == OSD_STATUS_PLAYING)
1697 status = STREAM_PLAYING;
1700 /* Cancel some auto refreshes - caller will restart them if desired */
1701 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1703 /* No backlight fiddling here - callers does the right thing */
1705 return status;
1708 /* Pause playback if playing */
1709 static int osd_pause(void)
1711 unsigned refresh = osd.auto_refresh;
1712 int status = osd_halt();
1714 osd_set_hp_pause_flag(false);
1716 if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) {
1717 /* Resume pending - change to a still video frame update */
1718 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1721 osd_set_status(OSD_STATUS_PAUSED);
1723 osd_backlight_on_video_mode(false);
1724 /* Leave brightness alone and restore it when OSD is hidden */
1726 return status;
1729 /* Resume playback if halted or paused */
1730 static void osd_resume(void)
1732 /* Cancel video and resume auto refresh - the resyc when starting
1733 * playback will perform those tasks */
1734 osd_set_hp_pause_flag(false);
1735 osd_backlight_on_video_mode(true);
1736 osd_backlight_brightness_video_mode(true);
1737 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1738 osd_set_status(OSD_STATUS_PLAYING);
1739 stream_resume();
1742 /* Stop playback - remember the resume point if not closed */
1743 static void osd_stop(void)
1745 uint32_t resume_time;
1747 osd_set_hp_pause_flag(false);
1748 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1749 osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
1750 osd_show(OSD_HIDE);
1752 stream_stop();
1754 resume_time = stream_get_resume_time();
1756 if (resume_time != INVALID_TIMESTAMP)
1757 settings.resume_time = resume_time;
1759 osd_backlight_on_video_mode(false);
1760 osd_backlight_brightness_video_mode(false);
1763 /* Perform a seek by button if seeking is possible for this stream.
1765 * A delay will be inserted before restarting in case the user decides to
1766 * seek again soon after.
1768 * Returns last button code
1770 static int osd_seek_btn(int btn)
1772 int status;
1773 unsigned refresh = 0;
1774 uint32_t time;
1776 if (!stream_can_seek())
1777 return true;
1779 /* Halt playback - not strictly necessary but nice when doing
1780 * buttons */
1781 status = osd_halt();
1783 if (status == STREAM_STOPPED)
1784 return true;
1786 osd_show(OSD_SHOW);
1788 /* Obtain a new playback point according to the buttons */
1789 if (status == STREAM_PLAYING)
1790 refresh = OSD_REFRESH_RESUME; /* delay resume if playing */
1791 else
1792 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
1794 btn = osd_ff_rw(btn, refresh, &time);
1796 /* Tell engine to resume at that time */
1797 stream_seek(time, SEEK_SET);
1799 return btn;
1802 /* Perform a seek by time if seeking is possible for this stream
1804 * If playing, the seeking is immediate, otherise a delay is added to showing
1805 * a still if paused in case the user does another seek soon after.
1807 * If seeking isn't possible, a time of zero performs a skip to the
1808 * beginning.
1810 static void osd_seek_time(uint32_t time)
1812 int status;
1813 unsigned refresh = 0;
1815 if (!stream_can_seek() && time != 0)
1816 return;
1818 stream_wait_status();
1819 status = osd_stream_status();
1821 if (status == STREAM_STOPPED)
1822 return;
1824 if (status == STREAM_PLAYING) /* merely preserve resume */
1825 refresh = osd.auto_refresh & OSD_REFRESH_RESUME;
1826 else
1827 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
1829 /* Cancel print or resume if pending */
1830 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1832 /* Tell engine to seek to the given time - no state change */
1833 stream_seek(time, SEEK_SET);
1835 osd_update_time();
1836 osd_refresh(OSD_REFRESH_TIME);
1837 osd_schedule_refresh(refresh);
1840 /* Has this file one of the supported extensions? */
1841 static bool is_videofile(const char* file)
1843 static const char * const extensions[] =
1845 /* Should match apps/plugins/viewers.config */
1846 "mpg", "mpeg", "mpv", "m2v"
1849 const char* ext = rb->strrchr(file, '.');
1850 int i;
1852 if (!ext)
1853 return false;
1855 for (i = ARRAYLEN(extensions) - 1; i >= 0; i--)
1857 if (!rb->strcasecmp(ext + 1, extensions[i]))
1858 break;
1861 return i >= 0;
1864 /* deliver the next/previous video file in the current directory.
1865 returns false if there is none. */
1866 static bool get_videofile(int direction, char* videofile, size_t bufsize)
1868 struct tree_context *tree = rb->tree_get_context();
1869 struct entry *dircache = tree->dircache;
1870 int i, step, end, found = 0;
1871 char *videoname = rb->strrchr(videofile, '/') + 1;
1872 size_t rest = bufsize - (videoname - videofile) - 1;
1874 if (direction == VIDEO_NEXT) {
1875 i = 0;
1876 step = 1;
1877 end = tree->filesindir;
1878 } else {
1879 i = tree->filesindir-1;
1880 step = -1;
1881 end = -1;
1883 for (; i != end; i += step)
1885 const char* name = dircache[i].name;
1886 if (!rb->strcmp(name, videoname)) {
1887 found = 1;
1888 continue;
1890 if (found && rb->strlen(name) <= rest &&
1891 !(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name))
1893 rb->strcpy(videoname, name);
1894 return true;
1898 return false;
1901 #ifdef HAVE_HEADPHONE_DETECTION
1902 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1903 static void osd_handle_phone_plug(bool inserted)
1905 if (rb->global_settings->unplug_mode == 0)
1906 return;
1908 /* Wait for any incomplete state transition to complete first */
1909 stream_wait_status();
1911 int status = osd_stream_status();
1913 if (inserted) {
1914 if (rb->global_settings->unplug_mode > 1) {
1915 if (status == STREAM_PAUSED &&
1916 (osd.flags & OSD_HP_PAUSE)) {
1917 osd_resume();
1920 } else {
1921 if (status == STREAM_PLAYING) {
1922 osd_pause();
1924 osd_set_hp_pause_flag(true);
1926 if (stream_can_seek() && rb->global_settings->unplug_rw) {
1927 stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
1928 SEEK_CUR);
1929 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1930 /* Update time display now */
1931 osd_update_time();
1932 osd_refresh(OSD_REFRESH_TIME);
1937 #endif
1939 static int button_loop(void)
1941 int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
1943 rb->lcd_setfont(FONT_SYSFIXED);
1944 #ifdef HAVE_LCD_COLOR
1945 rb->lcd_set_foreground(LCD_WHITE);
1946 rb->lcd_set_background(LCD_BLACK);
1947 #endif
1948 rb->lcd_clear_display();
1949 rb->lcd_update();
1951 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1952 rb->lcd_set_mode(LCD_MODE_YUV);
1953 #endif
1955 osd_init();
1957 /* Start playback at the specified starting time */
1958 if (osd_play(settings.resume_time) < STREAM_OK) {
1959 rb->splash(HZ*2, "Playback failed");
1960 return VIDEO_STOP;
1963 /* Gently poll the video player for EOS and handle UI */
1964 while (stream_status() != STREAM_STOPPED)
1966 int button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL/2);
1968 switch (button)
1970 case BUTTON_NONE:
1972 osd_refresh(OSD_REFRESH_DEFAULT);
1973 continue;
1974 } /* BUTTON_NONE: */
1976 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1977 case LCD_ENABLE_EVENT_1:
1979 /* Draw the current frame if prepared already */
1980 stream_draw_frame(true);
1981 break;
1982 } /* LCD_ENABLE_EVENT_1: */
1983 #endif
1985 case MPEG_VOLUP:
1986 case MPEG_VOLUP|BUTTON_REPEAT:
1987 #ifdef MPEG_VOLUP2
1988 case MPEG_VOLUP2:
1989 case MPEG_VOLUP2|BUTTON_REPEAT:
1990 #endif
1991 #ifdef MPEG_RC_VOLUP
1992 case MPEG_RC_VOLUP:
1993 case MPEG_RC_VOLUP|BUTTON_REPEAT:
1994 #endif
1996 osd_set_volume(+1);
1997 break;
1998 } /* MPEG_VOLUP*: */
2000 case MPEG_VOLDOWN:
2001 case MPEG_VOLDOWN|BUTTON_REPEAT:
2002 #ifdef MPEG_VOLDOWN2
2003 case MPEG_VOLDOWN2:
2004 case MPEG_VOLDOWN2|BUTTON_REPEAT:
2005 #endif
2006 #ifdef MPEG_RC_VOLDOWN
2007 case MPEG_RC_VOLDOWN:
2008 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
2009 #endif
2011 osd_set_volume(-1);
2012 break;
2013 } /* MPEG_VOLDOWN*: */
2015 case MPEG_MENU:
2016 #ifdef MPEG_RC_MENU
2017 case MPEG_RC_MENU:
2018 #endif
2020 int state = osd_halt(); /* save previous state */
2021 int result;
2023 /* Hide video output */
2024 osd_show(OSD_HIDE | OSD_NODRAW);
2025 stream_show_vo(false);
2026 osd_backlight_brightness_video_mode(false);
2028 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2029 rb->lcd_set_mode(LCD_MODE_RGB565);
2030 #endif
2032 result = mpeg_menu();
2034 next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
2036 fps_update_post_frame_callback();
2038 /* The menu can change the font, so restore */
2039 rb->lcd_setfont(FONT_SYSFIXED);
2040 #ifdef HAVE_LCD_COLOR
2041 rb->lcd_set_foreground(LCD_WHITE);
2042 rb->lcd_set_background(LCD_BLACK);
2043 #endif
2044 rb->lcd_clear_display();
2045 rb->lcd_update();
2047 switch (result)
2049 case MPEG_MENU_QUIT:
2050 next_action = VIDEO_STOP;
2051 osd_stop();
2052 break;
2054 default:
2055 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2056 rb->lcd_set_mode(LCD_MODE_YUV);
2057 #endif
2058 /* If not stopped, show video again */
2059 if (state != STREAM_STOPPED) {
2060 osd_show(OSD_SHOW);
2061 stream_show_vo(true);
2064 /* If stream was playing, restart it */
2065 if (state == STREAM_PLAYING) {
2066 osd_resume();
2068 break;
2070 break;
2071 } /* MPEG_MENU: */
2073 #ifdef MPEG_SHOW_OSD
2074 case MPEG_SHOW_OSD:
2075 case MPEG_SHOW_OSD | BUTTON_REPEAT:
2076 /* Show if not visible */
2077 osd_show(OSD_SHOW);
2078 /* Make sure it refreshes */
2079 osd_refresh(OSD_REFRESH_DEFAULT);
2080 break;
2081 #endif
2083 case MPEG_STOP:
2084 #ifdef MPEG_RC_STOP
2085 case MPEG_RC_STOP:
2086 #endif
2087 case ACTION_STD_CANCEL:
2089 cancel_playback:
2090 next_action = VIDEO_STOP;
2091 osd_stop();
2092 break;
2093 } /* MPEG_STOP: */
2095 case MPEG_PAUSE:
2096 #ifdef MPEG_PAUSE2
2097 case MPEG_PAUSE2:
2098 #endif
2099 #ifdef MPEG_RC_PAUSE
2100 case MPEG_RC_PAUSE:
2101 #endif
2103 int status = osd_stream_status();
2105 if (status == STREAM_PLAYING) {
2106 /* Playing => Paused */
2107 osd_pause();
2109 else if (status == STREAM_PAUSED) {
2110 /* Paused => Playing */
2111 osd_resume();
2114 break;
2115 } /* MPEG_PAUSE*: */
2117 case MPEG_RW:
2118 #ifdef MPEG_RW2
2119 case MPEG_RW2:
2120 #endif
2121 #ifdef MPEG_RC_RW
2122 case MPEG_RC_RW:
2123 #endif
2125 int old_button = button;
2127 /* If button has been released: skip to next/previous file */
2128 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
2130 if ((old_button | BUTTON_REL) == button) {
2131 /* Check current playback position */
2132 osd_update_time();
2134 if (settings.play_mode == 0 || osd.curr_time >= 3*TS_SECOND) {
2135 /* Start the current video from the beginning */
2136 osd_seek_time(0*TS_SECOND);
2138 else {
2139 /* Release within 3 seconds of start: skip to previous
2140 * file */
2141 osd_stop();
2142 next_action = VIDEO_PREV;
2145 else if ((button & ~BUTTON_REPEAT) == old_button) {
2146 button = osd_seek_btn(old_button);
2149 if (button == ACTION_STD_CANCEL)
2150 goto cancel_playback; /* jump to stop handling above */
2152 rb->default_event_handler(button);
2153 break;
2154 } /* MPEG_RW: */
2156 case MPEG_FF:
2157 #ifdef MPEG_FF2
2158 case MPEG_FF2:
2159 #endif
2160 #ifdef MPEG_RC_FF
2161 case MPEG_RC_FF:
2162 #endif
2164 int old_button = button;
2166 if (settings.play_mode != 0)
2167 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
2169 if ((old_button | BUTTON_REL) == button) {
2170 /* If button has been released: skip to next file */
2171 osd_stop();
2172 next_action = VIDEO_NEXT;
2174 else if ((button & ~BUTTON_REPEAT) == old_button) {
2175 button = osd_seek_btn(old_button);
2178 if (button == ACTION_STD_CANCEL)
2179 goto cancel_playback; /* jump to stop handling above */
2181 rb->default_event_handler(button);
2182 break;
2183 } /* MPEG_FF: */
2185 #ifdef HAVE_HEADPHONE_DETECTION
2186 case SYS_PHONE_PLUGGED:
2187 case SYS_PHONE_UNPLUGGED:
2189 osd_handle_phone_plug(button == SYS_PHONE_PLUGGED);
2190 break;
2191 } /* SYS_PHONE_*: */
2192 #endif
2194 default:
2196 osd_refresh(OSD_REFRESH_DEFAULT);
2197 rb->default_event_handler(button);
2198 break;
2199 } /* default: */
2202 rb->yield();
2203 } /* end while */
2205 osd_stop();
2207 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
2208 /* Be sure hook is removed before exiting since the stop will put it
2209 * back because of the backlight restore. */
2210 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
2211 #endif
2213 rb->lcd_setfont(FONT_UI);
2215 return next_action;
2218 enum plugin_status plugin_start(const void* parameter)
2220 static char videofile[MAX_PATH];
2221 int status = PLUGIN_OK; /* assume success */
2222 bool quit = false;
2224 if (parameter == NULL) {
2225 /* No file = GTFO */
2226 rb->splash(HZ*2, "No File");
2227 return PLUGIN_ERROR;
2230 /* Disable all talking before initializing IRAM */
2231 rb->talk_disable(true);
2233 #ifdef HAVE_LCD_COLOR
2234 rb->lcd_set_backdrop(NULL);
2235 rb->lcd_set_foreground(LCD_WHITE);
2236 rb->lcd_set_background(LCD_BLACK);
2237 #endif
2239 rb->lcd_clear_display();
2240 rb->lcd_update();
2242 rb->strcpy(videofile, (const char*) parameter);
2244 if (stream_init() < STREAM_OK) {
2245 /* Fatal because this should not fail */
2246 DEBUGF("Could not initialize streams\n");
2247 status = PLUGIN_ERROR;
2248 } else {
2249 int next_action = VIDEO_STOP;
2250 bool get_videofile_says = true;
2252 while (!quit)
2254 int result;
2256 init_settings(videofile);
2258 result = stream_open(videofile);
2260 if (result >= STREAM_OK) {
2261 /* start menu */
2262 rb->lcd_clear_display();
2263 rb->lcd_update();
2264 result = mpeg_start_menu(stream_get_duration());
2266 next_action = VIDEO_STOP;
2267 if (result != MPEG_START_QUIT) {
2268 /* Enter button loop and process UI */
2269 next_action = button_loop();
2272 stream_close();
2274 rb->lcd_clear_display();
2275 rb->lcd_update();
2277 save_settings();
2278 } else {
2279 /* Problem with file; display message about it - not
2280 * considered a plugin error */
2281 long tick;
2282 const char *errstring;
2284 DEBUGF("Could not open %s\n", videofile);
2285 switch (result)
2287 case STREAM_UNSUPPORTED:
2288 errstring = "Unsupported format";
2289 break;
2290 default:
2291 errstring = "Error opening file: %d";
2294 tick = *rb->current_tick + HZ*2;
2296 rb->splashf(0, errstring, result);
2298 /* Be sure it doesn't get stuck in an unbreakable loop of bad
2299 * files, just in case! Otherwise, keep searching in the
2300 * chosen direction until a good one is found. */
2301 while (!quit && TIME_BEFORE(*rb->current_tick, tick))
2303 int button = mpeg_button_get(HZ*2);
2305 switch (button)
2307 case MPEG_STOP:
2308 case ACTION_STD_CANCEL:
2309 /* Abort the search and exit */
2310 next_action = VIDEO_STOP;
2311 quit = true;
2312 break;
2314 case BUTTON_NONE:
2315 if (settings.play_mode != 0) {
2316 if (next_action == VIDEO_STOP) {
2317 /* Default to next file */
2318 next_action = VIDEO_NEXT;
2320 else if (next_action == VIDEO_PREV &&
2321 !get_videofile_says) {
2322 /* Was first file already; avoid endlessly
2323 * retrying it */
2324 next_action = VIDEO_STOP;
2327 break;
2329 default:
2330 rb->default_event_handler(button);
2331 } /* switch */
2332 } /* while */
2335 /* return value of button_loop says, what's next */
2336 switch (next_action)
2338 case VIDEO_NEXT:
2340 get_videofile_says = get_videofile(VIDEO_NEXT, videofile,
2341 sizeof(videofile));
2342 /* quit after finished the last videofile */
2343 quit = !get_videofile_says;
2344 break;
2346 case VIDEO_PREV:
2348 get_videofile_says = get_videofile(VIDEO_PREV, videofile,
2349 sizeof(videofile));
2350 /* if there is no previous file, play the same videofile */
2351 break;
2353 case VIDEO_STOP:
2355 quit = true;
2356 break;
2359 } /* while */
2362 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2363 rb->lcd_set_mode(LCD_MODE_RGB565);
2364 #endif
2366 stream_exit();
2368 rb->talk_disable(false);
2370 /* Actually handle delayed processing of system events of interest
2371 * that were captured in other button loops */
2372 mpeg_sysevent_handle();
2374 return status;