Improve SDL port 'configure --prefix' error message
[kugel-rb.git] / apps / plugins / mpegplayer / mpegplayer.c
blob97f648621acad7fa3d80faa6e7f1d63e7f60b704
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"
112 /* button definitions */
113 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
114 #define MPEG_MENU BUTTON_MODE
115 #define MPEG_STOP BUTTON_OFF
116 #define MPEG_PAUSE BUTTON_ON
117 #define MPEG_VOLDOWN BUTTON_DOWN
118 #define MPEG_VOLUP BUTTON_UP
119 #define MPEG_RW BUTTON_LEFT
120 #define MPEG_FF BUTTON_RIGHT
122 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
123 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
124 #define MPEG_MENU BUTTON_MENU
125 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
126 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
127 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
128 #define MPEG_VOLUP BUTTON_SCROLL_FWD
129 #define MPEG_RW BUTTON_LEFT
130 #define MPEG_FF BUTTON_RIGHT
132 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
133 #define MPEG_MENU (BUTTON_REC | BUTTON_REL)
134 #define MPEG_STOP BUTTON_POWER
135 #define MPEG_PAUSE BUTTON_PLAY
136 #define MPEG_VOLDOWN BUTTON_DOWN
137 #define MPEG_VOLUP BUTTON_UP
138 #define MPEG_RW BUTTON_LEFT
139 #define MPEG_FF BUTTON_RIGHT
141 #elif CONFIG_KEYPAD == GIGABEAT_PAD
142 #define MPEG_MENU BUTTON_MENU
143 #define MPEG_STOP BUTTON_POWER
144 #define MPEG_PAUSE BUTTON_SELECT
145 #define MPEG_PAUSE2 BUTTON_A
146 #define MPEG_VOLDOWN BUTTON_LEFT
147 #define MPEG_VOLUP BUTTON_RIGHT
148 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
149 #define MPEG_VOLUP2 BUTTON_VOL_UP
150 #define MPEG_RW BUTTON_UP
151 #define MPEG_FF BUTTON_DOWN
153 #define MPEG_RC_MENU BUTTON_RC_DSP
154 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
155 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
156 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
157 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
158 #define MPEG_RC_RW BUTTON_RC_REW
159 #define MPEG_RC_FF BUTTON_RC_FF
161 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
162 #define MPEG_MENU BUTTON_MENU
163 #define MPEG_STOP BUTTON_POWER
164 #define MPEG_PAUSE BUTTON_SELECT
165 #define MPEG_PAUSE2 BUTTON_PLAY
166 #define MPEG_VOLDOWN BUTTON_LEFT
167 #define MPEG_VOLUP BUTTON_RIGHT
168 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
169 #define MPEG_VOLUP2 BUTTON_VOL_UP
170 #define MPEG_RW BUTTON_UP
171 #define MPEG_RW2 BUTTON_PREV
172 #define MPEG_FF BUTTON_DOWN
173 #define MPEG_FF2 BUTTON_NEXT
174 #define MPEG_SHOW_OSD BUTTON_BACK
176 #define MPEG_RC_MENU BUTTON_RC_DSP
177 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
178 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
179 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
180 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
181 #define MPEG_RC_RW BUTTON_RC_REW
182 #define MPEG_RC_FF BUTTON_RC_FF
184 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
185 #define MPEG_MENU BUTTON_LEFT
186 #define MPEG_STOP BUTTON_POWER
187 #define MPEG_PAUSE BUTTON_PLAY
188 #define MPEG_VOLDOWN BUTTON_SCROLL_DOWN
189 #define MPEG_VOLUP BUTTON_SCROLL_UP
190 #define MPEG_RW BUTTON_REW
191 #define MPEG_FF BUTTON_FF
193 #elif CONFIG_KEYPAD == SANSA_E200_PAD
194 #define MPEG_MENU BUTTON_SELECT
195 #define MPEG_STOP BUTTON_POWER
196 #define MPEG_PAUSE BUTTON_RIGHT
197 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
198 #define MPEG_VOLUP BUTTON_SCROLL_FWD
199 #define MPEG_RW BUTTON_UP
200 #define MPEG_FF BUTTON_DOWN
202 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
203 #define MPEG_MENU BUTTON_SELECT
204 #define MPEG_STOP (BUTTON_HOME|BUTTON_REPEAT)
205 #define MPEG_PAUSE BUTTON_UP
206 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
207 #define MPEG_VOLUP BUTTON_SCROLL_FWD
208 #define MPEG_RW BUTTON_LEFT
209 #define MPEG_FF BUTTON_RIGHT
212 #elif CONFIG_KEYPAD == SANSA_C200_PAD || \
213 CONFIG_KEYPAD == SANSA_CLIP_PAD || \
214 CONFIG_KEYPAD == SANSA_M200_PAD
215 #define MPEG_MENU BUTTON_SELECT
216 #define MPEG_STOP BUTTON_POWER
217 #define MPEG_PAUSE BUTTON_UP
218 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
219 #define MPEG_VOLUP BUTTON_VOL_UP
220 #define MPEG_RW BUTTON_LEFT
221 #define MPEG_FF BUTTON_RIGHT
223 #elif CONFIG_KEYPAD == MROBE500_PAD
224 #define MPEG_STOP BUTTON_POWER
226 #define MPEG_RC_MENU BUTTON_RC_HEART
227 #define MPEG_RC_STOP BUTTON_RC_DOWN
228 #define MPEG_RC_PAUSE BUTTON_RC_PLAY
229 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
230 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
231 #define MPEG_RC_RW BUTTON_RC_REW
232 #define MPEG_RC_FF BUTTON_RC_FF
234 #elif CONFIG_KEYPAD == MROBE100_PAD
235 #define MPEG_MENU BUTTON_MENU
236 #define MPEG_STOP BUTTON_POWER
237 #define MPEG_PAUSE BUTTON_PLAY
238 #define MPEG_VOLDOWN BUTTON_DOWN
239 #define MPEG_VOLUP BUTTON_UP
240 #define MPEG_RW BUTTON_LEFT
241 #define MPEG_FF BUTTON_RIGHT
243 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
244 #define MPEG_MENU BUTTON_RC_MENU
245 #define MPEG_STOP BUTTON_RC_REC
246 #define MPEG_PAUSE BUTTON_RC_PLAY
247 #define MPEG_VOLDOWN BUTTON_RC_VOL_DOWN
248 #define MPEG_VOLUP BUTTON_RC_VOL_UP
249 #define MPEG_RW BUTTON_RC_REW
250 #define MPEG_FF BUTTON_RC_FF
252 #elif CONFIG_KEYPAD == COWON_D2_PAD
253 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
254 //#define MPEG_STOP BUTTON_POWER
255 #define MPEG_VOLDOWN BUTTON_MINUS
256 #define MPEG_VOLUP BUTTON_PLUS
258 #elif CONFIG_KEYPAD == IAUDIO67_PAD
259 #define MPEG_MENU BUTTON_MENU
260 #define MPEG_STOP BUTTON_STOP
261 #define MPEG_PAUSE BUTTON_PLAY
262 #define MPEG_VOLDOWN BUTTON_VOLDOWN
263 #define MPEG_VOLUP BUTTON_VOLUP
264 #define MPEG_RW BUTTON_LEFT
265 #define MPEG_FF BUTTON_RIGHT
267 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
268 #define MPEG_MENU BUTTON_MENU
269 #define MPEG_STOP BUTTON_BACK
270 #define MPEG_PAUSE BUTTON_PLAY
271 #define MPEG_VOLDOWN BUTTON_UP
272 #define MPEG_VOLUP BUTTON_DOWN
273 #define MPEG_RW BUTTON_LEFT
274 #define MPEG_FF BUTTON_RIGHT
276 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
277 #define MPEG_MENU BUTTON_MENU
278 #define MPEG_STOP BUTTON_POWER
279 #define MPEG_PAUSE BUTTON_SELECT
280 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
281 #define MPEG_VOLUP BUTTON_VOL_UP
282 #define MPEG_RW BUTTON_LEFT
283 #define MPEG_FF BUTTON_RIGHT
285 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
286 #define MPEG_MENU BUTTON_MENU
287 #define MPEG_STOP BUTTON_POWER
288 #define MPEG_PAUSE BUTTON_PLAY
289 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
290 #define MPEG_VOLUP BUTTON_VOL_UP
291 #define MPEG_RW BUTTON_PREV
292 #define MPEG_FF BUTTON_NEXT
294 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
295 #define MPEG_MENU BUTTON_MENU
296 #define MPEG_STOP BUTTON_POWER
297 #define MPEG_PAUSE BUTTON_PLAY
298 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
299 #define MPEG_VOLUP BUTTON_VOL_UP
300 #define MPEG_RW BUTTON_UP
301 #define MPEG_FF BUTTON_DOWN
303 #elif CONFIG_KEYPAD == ONDAVX747_PAD
304 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
305 //#define MPEG_STOP BUTTON_POWER
306 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
307 #define MPEG_VOLUP BUTTON_VOL_UP
309 #elif CONFIG_KEYPAD == ONDAVX777_PAD
310 #define MPEG_MENU BUTTON_POWER
312 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
313 #define MPEG_MENU BUTTON_LEFT
314 #define MPEG_STOP BUTTON_RIGHT
315 #define MPEG_PAUSE BUTTON_PLAY
316 #define MPEG_VOLDOWN BUTTON_DOWN
317 #define MPEG_VOLUP BUTTON_UP
318 #define MPEG_RW BUTTON_REW
319 #define MPEG_FF BUTTON_FFWD
321 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
322 #define MPEG_MENU BUTTON_MENU
323 #define MPEG_STOP BUTTON_REC
324 #define MPEG_PAUSE BUTTON_PLAY
325 #define MPEG_VOLDOWN BUTTON_DOWN
326 #define MPEG_VOLUP BUTTON_UP
327 #define MPEG_RW BUTTON_PREV
328 #define MPEG_FF BUTTON_NEXT
330 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
331 #define MPEG_MENU BUTTON_FUNC
332 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
333 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
334 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
335 #define MPEG_VOLUP BUTTON_VOL_UP
336 #define MPEG_RW BUTTON_REW
337 #define MPEG_FF BUTTON_FF
339 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
340 #define MPEG_MENU BUTTON_MENU
341 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
342 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
343 #define MPEG_VOLDOWN BUTTON_DOWN
344 #define MPEG_VOLUP BUTTON_UP
345 #define MPEG_RW BUTTON_REW
346 #define MPEG_FF BUTTON_FF
348 #else
349 #error No keymap defined!
350 #endif
352 #ifdef HAVE_TOUCHSCREEN
353 #ifndef MPEG_MENU
354 #define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
355 #endif
356 #ifndef MPEG_STOP
357 #define MPEG_STOP BUTTON_TOPLEFT
358 #endif
359 #ifndef MPEG_PAUSE
360 #define MPEG_PAUSE BUTTON_CENTER
361 #endif
362 #ifndef MPEG_VOLDOWN
363 #define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
364 #endif
365 #ifndef MPEG_VOLUP
366 #define MPEG_VOLUP BUTTON_TOPMIDDLE
367 #endif
368 #ifndef MPEG_RW
369 #define MPEG_RW BUTTON_MIDLEFT
370 #endif
371 #ifndef MPEG_FF
372 #define MPEG_FF BUTTON_MIDRIGHT
373 #endif
374 #endif
376 /* One thing we can do here for targets with remotes is having a display
377 * always on the remote instead of always forcing a popup on the main display */
379 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
380 /* 3% of 30min file == 54s step size */
381 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
382 #define OSD_MIN_UPDATE_INTERVAL (HZ/2)
383 #define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */
385 enum video_action
387 VIDEO_STOP = 0,
388 VIDEO_PREV,
389 VIDEO_NEXT,
392 /* OSD status - same order as icon array */
393 enum osd_status_enum
395 OSD_STATUS_STOPPED = 0,
396 OSD_STATUS_PAUSED,
397 OSD_STATUS_PLAYING,
398 OSD_STATUS_FF,
399 OSD_STATUS_RW,
400 OSD_STATUS_COUNT,
401 OSD_STATUS_MASK = 0x7
404 enum osd_bits
406 OSD_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
407 /* Refresh the... */
408 OSD_REFRESH_VOLUME = 0x0001, /* ...volume display */
409 OSD_REFRESH_TIME = 0x0002, /* ...time display+progress */
410 OSD_REFRESH_STATUS = 0x0004, /* ...playback status icon */
411 OSD_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
412 OSD_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
413 OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
414 OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
415 OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */
416 #ifdef HAVE_HEADPHONE_DETECTION
417 OSD_HP_PAUSE = 0x2000, /* OR bitflag - headphones caused pause */
418 #endif
419 OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */
420 OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
423 /* Status icons selected according to font height */
424 extern const unsigned char mpegplayer_status_icons_8x8x1[];
425 extern const unsigned char mpegplayer_status_icons_12x12x1[];
426 extern const unsigned char mpegplayer_status_icons_16x16x1[];
428 /* Main border areas that contain OSD elements */
429 #define OSD_BDR_L 2
430 #define OSD_BDR_T 2
431 #define OSD_BDR_R 2
432 #define OSD_BDR_B 2
434 struct osd
436 long hide_tick;
437 long show_for;
438 long print_tick;
439 long print_delay;
440 long resume_tick;
441 long resume_delay;
442 long next_auto_refresh;
443 int x;
444 int y;
445 int width;
446 int height;
447 unsigned fgcolor;
448 unsigned bgcolor;
449 unsigned prog_fillcolor;
450 struct vo_rect update_rect;
451 struct vo_rect prog_rect;
452 struct vo_rect time_rect;
453 struct vo_rect dur_rect;
454 struct vo_rect vol_rect;
455 const unsigned char *icons;
456 struct vo_rect stat_rect;
457 int status;
458 uint32_t curr_time;
459 unsigned auto_refresh;
460 unsigned flags;
461 int font;
464 struct fps
466 /* FPS Display */
467 struct vo_rect rect; /* OSD coordinates */
468 int pf_x; /* Screen coordinates */
469 int pf_y;
470 int pf_width;
471 int pf_height;
472 long update_tick; /* When to next update FPS reading */
473 #define FPS_FORMAT "%d.%02d"
474 #define FPS_DIMSTR "999.99" /* For establishing rect size */
475 #define FPS_BUFSIZE sizeof("999.99")
478 static struct osd osd;
479 static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */
481 static void osd_show(unsigned show);
483 #ifdef LCD_LANDSCAPE
484 #define _X (x + osd.x)
485 #define _Y (y + osd.y)
486 #define _W width
487 #define _H height
488 #else
489 #define _X (LCD_WIDTH - (y + osd.y) - height)
490 #define _Y (x + osd.x)
491 #define _W height
492 #define _H width
493 #endif
495 #ifdef HAVE_LCD_COLOR
496 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
497 static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
499 int r1 = RGB_UNPACK_RED(c1);
500 int g1 = RGB_UNPACK_GREEN(c1);
501 int b1 = RGB_UNPACK_BLUE(c1);
503 int r2 = RGB_UNPACK_RED(c2);
504 int g2 = RGB_UNPACK_GREEN(c2);
505 int b2 = RGB_UNPACK_BLUE(c2);
507 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
508 amount*(g2 - g1) / 255 + g1,
509 amount*(b2 - b1) / 255 + b1);
511 #endif
513 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
514 * most are just wrappers of lcd_* functions with transforms applied.
515 * The origin is the upper-left corner of the OSD area */
516 static void draw_update_rect(int x, int y, int width, int height)
518 mylcd_update_rect(_X, _Y, _W, _H);
521 static void draw_clear_area(int x, int y, int width, int height)
523 #ifdef HAVE_LCD_COLOR
524 rb->screen_clear_area(rb->screens[SCREEN_MAIN], _X, _Y, _W, _H);
525 #else
526 int oldmode = grey_get_drawmode();
527 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
528 grey_fillrect(_X, _Y, _W, _H);
529 grey_set_drawmode(oldmode);
530 #endif
533 static void draw_clear_area_rect(const struct vo_rect *rc)
535 draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t);
538 static void draw_fillrect(int x, int y, int width, int height)
540 mylcd_fillrect(_X, _Y, _W, _H);
543 static void draw_hline(int x1, int x2, int y)
545 #ifdef LCD_LANDSCAPE
546 mylcd_hline(x1 + osd.x, x2 + osd.x, y + osd.y);
547 #else
548 y = LCD_WIDTH - (y + osd.y) - 1;
549 mylcd_vline(y, x1 + osd.x, x2 + osd.x);
550 #endif
553 static void draw_vline(int x, int y1, int y2)
555 #ifdef LCD_LANDSCAPE
556 mylcd_vline(x + osd.x, y1 + osd.y, y2 + osd.y);
557 #else
558 y1 = LCD_WIDTH - (y1 + osd.y) - 1;
559 y2 = LCD_WIDTH - (y2 + osd.y) - 1;
560 mylcd_hline(y1, y2, x + osd.x);
561 #endif
564 static void draw_scrollbar_draw(int x, int y, int width, int height,
565 uint32_t min, uint32_t max, uint32_t val)
567 unsigned oldfg = mylcd_get_foreground();
569 draw_hline(x + 1, x + width - 2, y);
570 draw_hline(x + 1, x + width - 2, y + height - 1);
571 draw_vline(x, y + 1, y + height - 2);
572 draw_vline(x + width - 1, y + 1, y + height - 2);
574 val = muldiv_uint32(width - 2, val, max - min);
575 val = MIN(val, (uint32_t)(width - 2));
577 draw_fillrect(x + 1, y + 1, val, height - 2);
579 mylcd_set_foreground(osd.prog_fillcolor);
581 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
583 mylcd_set_foreground(oldfg);
586 static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
587 int max, int val)
589 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
590 min, max, val);
593 static void draw_setfont(int font)
595 osd.font = font;
596 mylcd_setfont(font);
599 #ifdef LCD_PORTRAIT
600 /* Portrait displays need rotated text rendering */
602 /* Limited function that only renders in DRMODE_FG and uses absolute screen
603 * coordinates */
604 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
605 int src_x, int src_y,
606 int stride, int x, int y,
607 int width, int height)
609 const unsigned char *src_end;
610 fb_data *dst, *dst_end;
611 unsigned fg_pattern, bg_pattern;
613 if (x + width > SCREEN_WIDTH)
614 width = SCREEN_WIDTH - x; /* Clip right */
615 if (x < 0)
616 width += x, x = 0; /* Clip left */
617 if (width <= 0)
618 return; /* nothing left to do */
620 if (y + height > SCREEN_HEIGHT)
621 height = SCREEN_HEIGHT - y; /* Clip bottom */
622 if (y < 0)
623 height += y, y = 0; /* Clip top */
624 if (height <= 0)
625 return; /* nothing left to do */
627 fg_pattern = rb->lcd_get_foreground();
628 bg_pattern = rb->lcd_get_background();
630 src += stride * (src_y >> 3) + src_x; /* move starting point */
631 src_y &= 7;
632 src_end = src + width;
634 dst = rb->lcd_framebuffer + (LCD_WIDTH - y) + x*LCD_WIDTH;
637 const unsigned char *src_col = src++;
638 unsigned data = *src_col >> src_y;
639 int numbits = 8 - src_y;
641 fb_data *dst_col = dst;
642 dst_end = dst_col - height;
643 dst += LCD_WIDTH;
647 dst_col--;
649 if (data & 1)
650 *dst_col = fg_pattern;
651 #if 0
652 else
653 *dst_col = bg_pattern;
654 #endif
655 data >>= 1;
656 if (--numbits == 0) {
657 src_col += stride;
658 data = *src_col;
659 numbits = 8;
662 while (dst_col > dst_end);
664 while (src < src_end);
667 static void draw_putsxy_oriented(int x, int y, const char *str)
669 unsigned short ch;
670 unsigned short *ucs;
671 int ofs = MIN(x, 0);
672 struct font* pf = rb->font_get(osd.font);
674 ucs = rb->bidi_l2v(str, 1);
676 x += osd.x;
677 y += osd.y;
679 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
681 int width;
682 const unsigned char *bits;
684 /* get proportional width and glyph bits */
685 width = rb->font_get_width(pf, ch);
687 if (ofs > width) {
688 ofs -= width;
689 continue;
692 bits = rb->font_get_bits(pf, ch);
694 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
695 width - ofs, pf->height);
697 x += width - ofs;
698 ofs = 0;
701 #else
702 static void draw_oriented_mono_bitmap_part(const unsigned char *src,
703 int src_x, int src_y,
704 int stride, int x, int y,
705 int width, int height)
707 int mode = mylcd_get_drawmode();
708 mylcd_set_drawmode(DRMODE_FG);
709 mylcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height);
710 mylcd_set_drawmode(mode);
713 static void draw_putsxy_oriented(int x, int y, const char *str)
715 int mode = mylcd_get_drawmode();
716 mylcd_set_drawmode(DRMODE_FG);
717 mylcd_putsxy(x + osd.x, y + osd.y, str);
718 mylcd_set_drawmode(mode);
720 #endif /* LCD_PORTRAIT */
722 /** FPS Display **/
724 /* Post-frame callback (on video thread) - update the FPS rectangle from the
725 * framebuffer */
726 static void fps_post_frame_callback(void)
728 vo_lock();
729 mylcd_update_rect(fps.pf_x, fps.pf_y,
730 fps.pf_width, fps.pf_height);
731 vo_unlock();
734 /* Set up to have the callback only update the intersection of the video
735 * rectangle and the FPS text rectangle - if they don't intersect, then
736 * the callback is set to NULL */
737 static void fps_update_post_frame_callback(void)
739 void (*cb)(void) = NULL;
741 if (settings.showfps) {
742 struct vo_rect cliprect;
744 if (stream_vo_get_clip(&cliprect)) {
745 /* Oriented screen coordinates -> OSD coordinates */
746 vo_rect_offset(&cliprect, -osd.x, -osd.y);
748 if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) {
749 int x = cliprect.l;
750 int y = cliprect.t;
751 int width = cliprect.r - cliprect.l;
752 int height = cliprect.b - cliprect.t;
754 /* OSD coordinates -> framebuffer coordinates */
755 fps.pf_x = _X;
756 fps.pf_y = _Y;
757 fps.pf_width = _W;
758 fps.pf_height = _H;
760 cb = fps_post_frame_callback;
765 stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb);
768 /* Refresh the FPS display */
769 static void fps_refresh(void)
771 char str[FPS_BUFSIZE];
772 struct video_output_stats stats;
773 int w, h, sw;
774 long tick;
776 tick = *rb->current_tick;
778 if (TIME_BEFORE(tick, fps.update_tick))
779 return;
781 fps.update_tick = tick + FPS_UPDATE_INTERVAL;
783 stream_video_stats(&stats);
785 rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT,
786 stats.fps / 100, stats.fps % 100);
788 w = fps.rect.r - fps.rect.l;
789 h = fps.rect.b - fps.rect.t;
791 draw_clear_area(fps.rect.l, fps.rect.t, w, h);
792 mylcd_getstringsize(str, &sw, NULL);
793 draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str);
795 vo_lock();
796 draw_update_rect(fps.rect.l, fps.rect.t, w, h);
797 vo_unlock();
800 /* Initialize the FPS display */
801 static void fps_init(void)
803 fps.update_tick = *rb->current_tick;
804 fps.rect.l = fps.rect.t = 0;
805 mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b);
806 vo_rect_offset(&fps.rect, -osd.x, -osd.y);
807 fps_update_post_frame_callback();
810 /** OSD **/
812 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
813 /* So we can refresh the overlay */
814 static void osd_lcd_enable_hook(void* param)
816 (void)param;
817 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
819 #endif
821 static void osd_backlight_on_video_mode(bool video_on)
823 if (video_on) {
824 /* Turn off backlight timeout */
825 backlight_ignore_timeout();
826 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
827 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
828 #endif
829 } else {
830 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
831 rb->add_event(LCD_EVENT_ACTIVATION, false, osd_lcd_enable_hook);
832 #endif
833 /* Revert to user's backlight settings */
834 backlight_use_settings();
838 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
839 static void osd_backlight_brightness_video_mode(bool video_on)
841 if (settings.backlight_brightness < 0)
842 return;
844 mpeg_backlight_update_brightness(
845 video_on ? settings.backlight_brightness : -1);
847 #else
848 #define osd_backlight_brightness_video_mode(video_on)
849 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
851 static void osd_text_init(void)
853 struct hms hms;
854 char buf[32];
855 int phys;
856 int spc_width;
858 draw_setfont(FONT_UI);
860 osd.x = 0;
861 osd.width = SCREEN_WIDTH;
863 vo_rect_clear(&osd.time_rect);
864 vo_rect_clear(&osd.stat_rect);
865 vo_rect_clear(&osd.prog_rect);
866 vo_rect_clear(&osd.vol_rect);
868 ts_to_hms(stream_get_duration(), &hms);
869 hms_format(buf, sizeof (buf), &hms);
870 mylcd_getstringsize(buf, &osd.time_rect.r, &osd.time_rect.b);
872 /* Choose well-sized bitmap images relative to font height */
873 if (osd.time_rect.b < 12) {
874 osd.icons = mpegplayer_status_icons_8x8x1;
875 osd.stat_rect.r = osd.stat_rect.b = 8;
876 } else if (osd.time_rect.b < 16) {
877 osd.icons = mpegplayer_status_icons_12x12x1;
878 osd.stat_rect.r = osd.stat_rect.b = 12;
879 } else {
880 osd.icons = mpegplayer_status_icons_16x16x1;
881 osd.stat_rect.r = osd.stat_rect.b = 16;
884 if (osd.stat_rect.b < osd.time_rect.b) {
885 vo_rect_offset(&osd.stat_rect, 0,
886 (osd.time_rect.b - osd.stat_rect.b) / 2 + OSD_BDR_T);
887 vo_rect_offset(&osd.time_rect, OSD_BDR_L, OSD_BDR_T);
888 } else {
889 vo_rect_offset(&osd.time_rect, OSD_BDR_L,
890 osd.stat_rect.b - osd.time_rect.b + OSD_BDR_T);
891 vo_rect_offset(&osd.stat_rect, 0, OSD_BDR_T);
894 osd.dur_rect = osd.time_rect;
896 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
897 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
898 rb->sound_unit(SOUND_VOLUME));
900 mylcd_getstringsize(" ", &spc_width, NULL);
901 mylcd_getstringsize(buf, &osd.vol_rect.r, &osd.vol_rect.b);
903 osd.prog_rect.r = SCREEN_WIDTH - OSD_BDR_L - spc_width -
904 osd.vol_rect.r - OSD_BDR_R;
905 osd.prog_rect.b = 3*osd.stat_rect.b / 4;
906 vo_rect_offset(&osd.prog_rect, osd.time_rect.l,
907 osd.time_rect.b);
909 vo_rect_offset(&osd.stat_rect,
910 (osd.prog_rect.r + osd.prog_rect.l - osd.stat_rect.r) / 2,
913 vo_rect_offset(&osd.dur_rect,
914 osd.prog_rect.r - osd.dur_rect.r, 0);
916 vo_rect_offset(&osd.vol_rect, osd.prog_rect.r + spc_width,
917 (osd.prog_rect.b + osd.prog_rect.t - osd.vol_rect.b) / 2);
919 osd.height = OSD_BDR_T + MAX(osd.prog_rect.b, osd.vol_rect.b) -
920 MIN(osd.time_rect.t, osd.stat_rect.t) + OSD_BDR_B;
922 #ifdef HAVE_LCD_COLOR
923 osd.height = ALIGN_UP(osd.height, 2);
924 #endif
925 osd.y = SCREEN_HEIGHT - osd.height;
927 draw_setfont(FONT_SYSFIXED);
930 static void osd_init(void)
932 osd.flags = 0;
933 osd.show_for = HZ*4;
934 osd.print_delay = 75*HZ/100;
935 osd.resume_delay = HZ/2;
936 #ifdef HAVE_LCD_COLOR
937 osd.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
938 osd.fgcolor = LCD_WHITE;
939 osd.prog_fillcolor = LCD_BLACK;
940 #else
941 osd.bgcolor = GREY_LIGHTGRAY;
942 osd.fgcolor = GREY_BLACK;
943 osd.prog_fillcolor = GREY_WHITE;
944 #endif
945 osd.curr_time = 0;
946 osd.status = OSD_STATUS_STOPPED;
947 osd.auto_refresh = OSD_REFRESH_TIME;
948 osd.next_auto_refresh = *rb->current_tick;
949 osd_text_init();
950 fps_init();
953 #ifdef HAVE_HEADPHONE_DETECTION
954 static void osd_set_hp_pause_flag(bool set)
956 if (set)
957 osd.flags |= OSD_HP_PAUSE;
958 else
959 osd.flags &= ~OSD_HP_PAUSE;
961 #else
962 #define osd_set_hp_pause_flag(set)
963 #endif /* HAVE_HEADPHONE_DETECTION */
965 static void osd_schedule_refresh(unsigned refresh)
967 long tick = *rb->current_tick;
969 if (refresh & OSD_REFRESH_VIDEO)
970 osd.print_tick = tick + osd.print_delay;
972 if (refresh & OSD_REFRESH_RESUME)
973 osd.resume_tick = tick + osd.resume_delay;
975 osd.auto_refresh |= refresh;
978 static void osd_cancel_refresh(unsigned refresh)
980 osd.auto_refresh &= ~refresh;
983 /* Refresh the background area */
984 static void osd_refresh_background(void)
986 char buf[32];
987 struct hms hms;
989 unsigned bg = mylcd_get_background();
990 mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
992 #ifdef HAVE_LCD_COLOR
993 /* Draw a "raised" area for our graphics */
994 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 192));
995 draw_hline(0, osd.width, 0);
997 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 80));
998 draw_hline(0, osd.width, 1);
1000 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 48));
1001 draw_hline(0, osd.width, osd.height-2);
1003 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 128));
1004 draw_hline(0, osd.width, osd.height-1);
1006 mylcd_set_background(bg);
1007 draw_clear_area(0, 2, osd.width, osd.height - 4);
1008 #else
1009 /* Give contrast with the main background */
1010 mylcd_set_background(MYLCD_WHITE);
1011 draw_hline(0, osd.width, 0);
1013 mylcd_set_background(MYLCD_DARKGRAY);
1014 draw_hline(0, osd.width, osd.height-1);
1016 mylcd_set_background(bg);
1017 draw_clear_area(0, 1, osd.width, osd.height - 2);
1018 #endif
1020 vo_rect_set_ext(&osd.update_rect, 0, 0, osd.width, osd.height);
1021 mylcd_set_drawmode(DRMODE_SOLID);
1023 if (stream_get_duration() != INVALID_TIMESTAMP) {
1024 /* Draw the movie duration */
1025 ts_to_hms(stream_get_duration(), &hms);
1026 hms_format(buf, sizeof (buf), &hms);
1027 draw_putsxy_oriented(osd.dur_rect.l, osd.dur_rect.t, buf);
1029 /* else don't know the duration */
1032 /* Refresh the current time display + the progress bar */
1033 static void osd_refresh_time(void)
1035 char buf[32];
1036 struct hms hms;
1038 uint32_t duration = stream_get_duration();
1040 draw_scrollbar_draw_rect(&osd.prog_rect, 0, duration,
1041 osd.curr_time);
1043 ts_to_hms(osd.curr_time, &hms);
1044 hms_format(buf, sizeof (buf), &hms);
1046 draw_clear_area_rect(&osd.time_rect);
1047 draw_putsxy_oriented(osd.time_rect.l, osd.time_rect.t, buf);
1049 vo_rect_union(&osd.update_rect, &osd.update_rect,
1050 &osd.prog_rect);
1051 vo_rect_union(&osd.update_rect, &osd.update_rect,
1052 &osd.time_rect);
1055 /* Refresh the volume display area */
1056 static void osd_refresh_volume(void)
1058 char buf[32];
1059 int width;
1061 int volume = rb->global_settings->volume;
1062 rb->snprintf(buf, sizeof (buf), "%d%s",
1063 rb->sound_val2phys(SOUND_VOLUME, volume),
1064 rb->sound_unit(SOUND_VOLUME));
1065 mylcd_getstringsize(buf, &width, NULL);
1067 /* Right-justified */
1068 draw_clear_area_rect(&osd.vol_rect);
1069 draw_putsxy_oriented(osd.vol_rect.r - width, osd.vol_rect.t, buf);
1071 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.vol_rect);
1074 /* Refresh the status icon */
1075 static void osd_refresh_status(void)
1077 int icon_size = osd.stat_rect.r - osd.stat_rect.l;
1079 draw_clear_area_rect(&osd.stat_rect);
1081 #ifdef HAVE_LCD_COLOR
1082 /* Draw status icon with a drop shadow */
1083 unsigned oldfg = mylcd_get_foreground();
1084 int i = 1;
1086 mylcd_set_foreground(draw_blendcolor(mylcd_get_background(),
1087 MYLCD_BLACK, 96));
1089 while (1)
1091 draw_oriented_mono_bitmap_part(osd.icons,
1092 icon_size*osd.status,
1094 icon_size*OSD_STATUS_COUNT,
1095 osd.stat_rect.l + osd.x + i,
1096 osd.stat_rect.t + osd.y + i,
1097 icon_size, icon_size);
1099 if (--i < 0)
1100 break;
1102 mylcd_set_foreground(oldfg);
1105 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1106 #else
1107 draw_oriented_mono_bitmap_part(osd.icons,
1108 icon_size*osd.status,
1110 icon_size*OSD_STATUS_COUNT,
1111 osd.stat_rect.l + osd.x,
1112 osd.stat_rect.t + osd.y,
1113 icon_size, icon_size);
1114 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1115 #endif
1118 /* Update the current status which determines which icon is displayed */
1119 static bool osd_update_status(void)
1121 int status;
1123 switch (stream_status())
1125 default:
1126 status = OSD_STATUS_STOPPED;
1127 break;
1128 case STREAM_PAUSED:
1129 /* If paused with a pending resume, coerce it to OSD_STATUS_PLAYING */
1130 status = (osd.auto_refresh & OSD_REFRESH_RESUME) ?
1131 OSD_STATUS_PLAYING : OSD_STATUS_PAUSED;
1132 break;
1133 case STREAM_PLAYING:
1134 status = OSD_STATUS_PLAYING;
1135 break;
1138 if (status != osd.status) {
1139 /* A refresh is needed */
1140 osd.status = status;
1141 return true;
1144 return false;
1147 /* Update the current time that will be displayed */
1148 static void osd_update_time(void)
1150 uint32_t start;
1151 osd.curr_time = stream_get_seek_time(&start);
1152 osd.curr_time -= start;
1155 /* Refresh various parts of the OSD - showing it if it is hidden */
1156 static void osd_refresh(int hint)
1158 long tick;
1159 unsigned oldbg, oldfg;
1161 tick = *rb->current_tick;
1163 if (settings.showfps)
1164 fps_refresh();
1166 if (hint == OSD_REFRESH_DEFAULT) {
1167 /* The default which forces no updates */
1169 /* Make sure Rockbox doesn't turn off the player because of
1170 too little activity */
1171 if (osd.status == OSD_STATUS_PLAYING)
1172 rb->reset_poweroff_timer();
1174 /* Redraw the current or possibly extract a new video frame */
1175 if ((osd.auto_refresh & OSD_REFRESH_VIDEO) &&
1176 TIME_AFTER(tick, osd.print_tick)) {
1177 osd.auto_refresh &= ~OSD_REFRESH_VIDEO;
1178 stream_draw_frame(false);
1181 /* Restart playback if the timout was reached */
1182 if ((osd.auto_refresh & OSD_REFRESH_RESUME) &&
1183 TIME_AFTER(tick, osd.resume_tick)) {
1184 osd.auto_refresh &= ~(OSD_REFRESH_RESUME | OSD_REFRESH_VIDEO);
1185 stream_resume();
1188 /* If not visible, return */
1189 if (!(osd.flags & OSD_SHOW))
1190 return;
1192 /* Hide if the visibility duration was reached */
1193 if (TIME_AFTER(tick, osd.hide_tick)) {
1194 osd_show(OSD_HIDE);
1195 return;
1197 } else {
1198 /* A forced update of some region */
1200 /* Show if currently invisible */
1201 if (!(osd.flags & OSD_SHOW)) {
1202 /* Avoid call back into this function - it will be drawn */
1203 osd_show(OSD_SHOW | OSD_NODRAW);
1204 hint = OSD_REFRESH_ALL;
1207 /* Move back timeouts for frame print and hide */
1208 osd.print_tick = tick + osd.print_delay;
1209 osd.hide_tick = tick + osd.show_for;
1212 if (TIME_AFTER(tick, osd.next_auto_refresh)) {
1213 /* Refresh whatever graphical elements are due automatically */
1214 osd.next_auto_refresh = tick + OSD_MIN_UPDATE_INTERVAL;
1216 if (osd.auto_refresh & OSD_REFRESH_STATUS) {
1217 if (osd_update_status())
1218 hint |= OSD_REFRESH_STATUS;
1221 if (osd.auto_refresh & OSD_REFRESH_TIME) {
1222 osd_update_time();
1223 hint |= OSD_REFRESH_TIME;
1227 if (hint == 0)
1228 return; /* No drawing needed */
1230 /* Set basic drawing params that are used. Elements that perform variations
1231 * will restore them. */
1232 oldfg = mylcd_get_foreground();
1233 oldbg = mylcd_get_background();
1235 draw_setfont(FONT_UI);
1236 mylcd_set_foreground(osd.fgcolor);
1237 mylcd_set_background(osd.bgcolor);
1239 vo_rect_clear(&osd.update_rect);
1241 if (hint & OSD_REFRESH_BACKGROUND) {
1242 osd_refresh_background();
1243 hint |= OSD_REFRESH_ALL; /* Requires a redraw of everything */
1246 if (hint & OSD_REFRESH_TIME) {
1247 osd_refresh_time();
1250 if (hint & OSD_REFRESH_VOLUME) {
1251 osd_refresh_volume();
1254 if (hint & OSD_REFRESH_STATUS) {
1255 osd_refresh_status();
1258 /* Go back to defaults */
1259 draw_setfont(FONT_SYSFIXED);
1260 mylcd_set_foreground(oldfg);
1261 mylcd_set_background(oldbg);
1263 /* Update the dirty rectangle */
1264 vo_lock();
1266 draw_update_rect(osd.update_rect.l,
1267 osd.update_rect.t,
1268 osd.update_rect.r - osd.update_rect.l,
1269 osd.update_rect.b - osd.update_rect.t);
1271 vo_unlock();
1274 /* Show/Hide the OSD */
1275 static void osd_show(unsigned show)
1277 if (((show ^ osd.flags) & OSD_SHOW) == 0)
1279 if (show & OSD_SHOW) {
1280 osd.hide_tick = *rb->current_tick + osd.show_for;
1282 return;
1285 if (show & OSD_SHOW) {
1286 /* Clip away the part of video that is covered */
1287 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, osd.y };
1289 osd.flags |= OSD_SHOW;
1291 if (osd.status != OSD_STATUS_PLAYING) {
1292 /* Not playing - set brightness to mpegplayer setting */
1293 osd_backlight_brightness_video_mode(true);
1296 stream_vo_set_clip(&rc);
1298 if (!(show & OSD_NODRAW))
1299 osd_refresh(OSD_REFRESH_ALL);
1300 } else {
1301 /* Uncover clipped video area and redraw it */
1302 osd.flags &= ~OSD_SHOW;
1304 draw_clear_area(0, 0, osd.width, osd.height);
1306 if (!(show & OSD_NODRAW)) {
1307 vo_lock();
1308 draw_update_rect(0, 0, osd.width, osd.height);
1309 vo_unlock();
1311 stream_vo_set_clip(NULL);
1312 stream_draw_frame(false);
1313 } else {
1314 stream_vo_set_clip(NULL);
1317 if (osd.status != OSD_STATUS_PLAYING) {
1318 /* Not playing - restore backlight brightness */
1319 osd_backlight_brightness_video_mode(false);
1324 /* Set the current status - update screen if specified */
1325 static void osd_set_status(int status)
1327 bool draw = (status & OSD_NODRAW) == 0;
1329 status &= OSD_STATUS_MASK;
1331 if (osd.status != status) {
1333 osd.status = status;
1335 if (draw)
1336 osd_refresh(OSD_REFRESH_STATUS);
1340 /* Get the current status value */
1341 static int osd_get_status(void)
1343 return osd.status & OSD_STATUS_MASK;
1346 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;)
1347 * Returns last button code
1349 static int osd_ff_rw(int btn, unsigned refresh, uint32_t *new_time)
1351 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1352 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1353 uint32_t start;
1354 uint32_t time = stream_get_seek_time(&start);
1355 const uint32_t duration = stream_get_duration();
1356 unsigned int max_step = 0;
1357 uint32_t ff_rw_count = 0;
1358 unsigned status = osd.status;
1359 int new_btn;
1361 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME |
1362 OSD_REFRESH_TIME);
1364 time -= start; /* Absolute clock => stream-relative */
1366 switch (btn)
1368 case MPEG_FF:
1369 #ifdef MPEG_FF2
1370 case MPEG_FF2:
1371 #endif
1372 #ifdef MPEG_RC_FF
1373 case MPEG_RC_FF:
1374 #endif
1375 osd_set_status(OSD_STATUS_FF);
1376 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1377 break;
1378 case MPEG_RW:
1379 #ifdef MPEG_RW2
1380 case MPEG_RW2:
1381 #endif
1382 #ifdef MPEG_RC_RW
1383 case MPEG_RC_RW:
1384 #endif
1385 osd_set_status(OSD_STATUS_RW);
1386 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1387 break;
1388 default:
1389 new_btn = BUTTON_NONE; /* Fail tests below but still do proper exit */
1392 while (1)
1394 stream_keep_disk_active();
1396 if (new_btn == (btn | BUTTON_REPEAT)) {
1397 if (osd.status == OSD_STATUS_FF) {
1398 /* fast forwarding, calc max step relative to end */
1399 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1400 FF_REWIND_MAX_PERCENT, 100);
1401 } else {
1402 /* rewinding, calc max step relative to start */
1403 max_step = muldiv_uint32(time - ff_rw_count,
1404 FF_REWIND_MAX_PERCENT, 100);
1407 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1409 if (step > max_step)
1410 step = max_step;
1412 ff_rw_count += step;
1414 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1415 step += step >> ff_rw_accel;
1417 if (osd.status == OSD_STATUS_FF) {
1418 if (duration - time <= ff_rw_count)
1419 ff_rw_count = duration - time;
1421 osd.curr_time = time + ff_rw_count;
1422 } else {
1423 if (time <= ff_rw_count)
1424 ff_rw_count = time;
1426 osd.curr_time = time - ff_rw_count;
1429 osd_refresh(OSD_REFRESH_TIME);
1431 new_btn = mpeg_button_get(TIMEOUT_BLOCK);
1433 else {
1434 if (new_btn == (btn | BUTTON_REL)) {
1435 if (osd.status == OSD_STATUS_FF)
1436 time += ff_rw_count;
1437 else if (osd.status == OSD_STATUS_RW)
1438 time -= ff_rw_count;
1441 *new_time = time;
1443 osd_schedule_refresh(refresh);
1444 osd_set_status(status);
1445 osd_schedule_refresh(OSD_REFRESH_TIME);
1447 return new_btn;
1452 /* Return adjusted STREAM_* status */
1453 static int osd_stream_status(void)
1455 int status = stream_status();
1457 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1458 if (status == STREAM_PAUSED) {
1459 if (osd.auto_refresh & OSD_REFRESH_RESUME)
1460 status = STREAM_PLAYING;
1463 return status;
1466 /* Change the current audio volume by a specified amount */
1467 static void osd_set_volume(int delta)
1469 int vol = rb->global_settings->volume;
1470 int limit;
1472 vol += delta;
1474 if (delta < 0) {
1475 /* Volume down - clip to lower limit */
1476 limit = rb->sound_min(SOUND_VOLUME);
1477 if (vol < limit)
1478 vol = limit;
1479 } else {
1480 /* Volume up - clip to upper limit */
1481 limit = rb->sound_max(SOUND_VOLUME);
1482 if (vol > limit)
1483 vol = limit;
1486 /* Sync the global settings */
1487 if (vol != rb->global_settings->volume) {
1488 rb->sound_set(SOUND_VOLUME, vol);
1489 rb->global_settings->volume = vol;
1492 /* Update the volume display */
1493 osd_refresh(OSD_REFRESH_VOLUME);
1496 /* Begin playback at the specified time */
1497 static int osd_play(uint32_t time)
1499 int retval;
1501 osd_set_hp_pause_flag(false);
1502 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1504 retval = stream_seek(time, SEEK_SET);
1506 if (retval >= STREAM_OK) {
1507 osd_backlight_on_video_mode(true);
1508 osd_backlight_brightness_video_mode(true);
1509 stream_show_vo(true);
1511 retval = stream_play();
1513 if (retval >= STREAM_OK)
1514 osd_set_status(OSD_STATUS_PLAYING | OSD_NODRAW);
1517 return retval;
1520 /* Halt playback - pause engine and return logical state */
1521 static int osd_halt(void)
1523 int status = stream_pause();
1525 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1526 if (status == STREAM_PAUSED) {
1527 if (osd_get_status() == OSD_STATUS_PLAYING)
1528 status = STREAM_PLAYING;
1531 /* Cancel some auto refreshes - caller will restart them if desired */
1532 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1534 /* No backlight fiddling here - callers does the right thing */
1536 return status;
1539 /* Pause playback if playing */
1540 static int osd_pause(void)
1542 unsigned refresh = osd.auto_refresh;
1543 int status = osd_halt();
1545 osd_set_hp_pause_flag(false);
1547 if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) {
1548 /* Resume pending - change to a still video frame update */
1549 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1552 osd_set_status(OSD_STATUS_PAUSED);
1554 osd_backlight_on_video_mode(false);
1555 /* Leave brightness alone and restore it when OSD is hidden */
1557 return status;
1560 /* Resume playback if halted or paused */
1561 static void osd_resume(void)
1563 /* Cancel video and resume auto refresh - the resyc when starting
1564 * playback will perform those tasks */
1565 osd_set_hp_pause_flag(false);
1566 osd_backlight_on_video_mode(true);
1567 osd_backlight_brightness_video_mode(true);
1568 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1569 osd_set_status(OSD_STATUS_PLAYING);
1570 stream_resume();
1573 /* Stop playback - remember the resume point if not closed */
1574 static void osd_stop(void)
1576 uint32_t resume_time;
1578 osd_set_hp_pause_flag(false);
1579 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1580 osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
1581 osd_show(OSD_HIDE);
1583 stream_stop();
1585 resume_time = stream_get_resume_time();
1587 if (resume_time != INVALID_TIMESTAMP)
1588 settings.resume_time = resume_time;
1590 osd_backlight_on_video_mode(false);
1591 osd_backlight_brightness_video_mode(false);
1594 /* Perform a seek by button if seeking is possible for this stream.
1596 * A delay will be inserted before restarting in case the user decides to
1597 * seek again soon after.
1599 * Returns last button code
1601 static int osd_seek_btn(int btn)
1603 int status;
1604 unsigned refresh = 0;
1605 uint32_t time;
1607 if (!stream_can_seek())
1608 return true;
1610 /* Halt playback - not strictly necessary but nice when doing
1611 * buttons */
1612 status = osd_halt();
1614 if (status == STREAM_STOPPED)
1615 return true;
1617 osd_show(OSD_SHOW);
1619 /* Obtain a new playback point according to the buttons */
1620 if (status == STREAM_PLAYING)
1621 refresh = OSD_REFRESH_RESUME; /* delay resume if playing */
1622 else
1623 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
1625 btn = osd_ff_rw(btn, refresh, &time);
1627 /* Tell engine to resume at that time */
1628 stream_seek(time, SEEK_SET);
1630 return btn;
1633 /* Perform a seek by time if seeking is possible for this stream
1635 * If playing, the seeking is immediate, otherise a delay is added to showing
1636 * a still if paused in case the user does another seek soon after.
1638 * If seeking isn't possible, a time of zero performs a skip to the
1639 * beginning.
1641 static void osd_seek_time(uint32_t time)
1643 int status;
1644 unsigned refresh = 0;
1646 if (!stream_can_seek() && time != 0)
1647 return;
1649 stream_wait_status();
1650 status = osd_stream_status();
1652 if (status == STREAM_STOPPED)
1653 return;
1655 if (status == STREAM_PLAYING) /* merely preserve resume */
1656 refresh = osd.auto_refresh & OSD_REFRESH_RESUME;
1657 else
1658 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
1660 /* Cancel print or resume if pending */
1661 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1663 /* Tell engine to seek to the given time - no state change */
1664 stream_seek(time, SEEK_SET);
1666 osd_update_time();
1667 osd_refresh(OSD_REFRESH_TIME);
1668 osd_schedule_refresh(refresh);
1671 /* Has this file one of the supported extensions? */
1672 static bool is_videofile(const char* file)
1674 static const char * const extensions[] =
1676 /* Should match apps/plugins/viewers.config */
1677 "mpg", "mpeg", "mpv", "m2v"
1680 const char* ext = rb->strrchr(file, '.');
1681 int i;
1683 if (!ext)
1684 return false;
1686 for (i = ARRAYLEN(extensions) - 1; i >= 0; i--)
1688 if (!rb->strcasecmp(ext + 1, extensions[i]))
1689 break;
1692 return i >= 0;
1695 /* deliver the next/previous video file in the current directory.
1696 returns false if there is none. */
1697 static bool get_videofile(int direction, char* videofile, size_t bufsize)
1699 struct tree_context *tree = rb->tree_get_context();
1700 struct entry *dircache = tree->dircache;
1701 int i, step, end, found = 0;
1702 char *videoname = rb->strrchr(videofile, '/') + 1;
1703 size_t rest = bufsize - (videoname - videofile) - 1;
1705 if (direction == VIDEO_NEXT) {
1706 i = 0;
1707 step = 1;
1708 end = tree->filesindir;
1709 } else {
1710 i = tree->filesindir-1;
1711 step = -1;
1712 end = -1;
1714 for (; i != end; i += step)
1716 const char* name = dircache[i].name;
1717 if (!rb->strcmp(name, videoname)) {
1718 found = 1;
1719 continue;
1721 if (found && rb->strlen(name) <= rest &&
1722 !(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name))
1724 rb->strcpy(videoname, name);
1725 return true;
1729 return false;
1732 #ifdef HAVE_HEADPHONE_DETECTION
1733 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1734 static void osd_handle_phone_plug(bool inserted)
1736 if (rb->global_settings->unplug_mode == 0)
1737 return;
1739 /* Wait for any incomplete state transition to complete first */
1740 stream_wait_status();
1742 int status = osd_stream_status();
1744 if (inserted) {
1745 if (rb->global_settings->unplug_mode > 1) {
1746 if (status == STREAM_PAUSED &&
1747 (osd.flags & OSD_HP_PAUSE)) {
1748 osd_resume();
1751 } else {
1752 if (status == STREAM_PLAYING) {
1753 osd_pause();
1755 osd_set_hp_pause_flag(true);
1757 if (stream_can_seek() && rb->global_settings->unplug_rw) {
1758 stream_seek(-rb->global_settings->unplug_rw*TS_SECOND,
1759 SEEK_CUR);
1760 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1761 /* Update time display now */
1762 osd_update_time();
1763 osd_refresh(OSD_REFRESH_TIME);
1768 #endif
1770 static int button_loop(void)
1772 int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
1774 rb->lcd_setfont(FONT_SYSFIXED);
1775 #ifdef HAVE_LCD_COLOR
1776 rb->lcd_set_foreground(LCD_WHITE);
1777 rb->lcd_set_background(LCD_BLACK);
1778 #endif
1779 rb->lcd_clear_display();
1780 rb->lcd_update();
1782 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1783 rb->lcd_set_mode(LCD_MODE_YUV);
1784 #endif
1786 osd_init();
1788 /* Start playback at the specified starting time */
1789 if (osd_play(settings.resume_time) < STREAM_OK) {
1790 rb->splash(HZ*2, "Playback failed");
1791 return VIDEO_STOP;
1794 /* Gently poll the video player for EOS and handle UI */
1795 while (stream_status() != STREAM_STOPPED)
1797 int button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL/2);
1799 switch (button)
1801 case BUTTON_NONE:
1803 osd_refresh(OSD_REFRESH_DEFAULT);
1804 continue;
1805 } /* BUTTON_NONE: */
1807 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1808 case LCD_ENABLE_EVENT_1:
1810 /* Draw the current frame if prepared already */
1811 stream_draw_frame(true);
1812 break;
1813 } /* LCD_ENABLE_EVENT_1: */
1814 #endif
1816 case MPEG_VOLUP:
1817 case MPEG_VOLUP|BUTTON_REPEAT:
1818 #ifdef MPEG_VOLUP2
1819 case MPEG_VOLUP2:
1820 case MPEG_VOLUP2|BUTTON_REPEAT:
1821 #endif
1822 #ifdef MPEG_RC_VOLUP
1823 case MPEG_RC_VOLUP:
1824 case MPEG_RC_VOLUP|BUTTON_REPEAT:
1825 #endif
1827 osd_set_volume(+1);
1828 break;
1829 } /* MPEG_VOLUP*: */
1831 case MPEG_VOLDOWN:
1832 case MPEG_VOLDOWN|BUTTON_REPEAT:
1833 #ifdef MPEG_VOLDOWN2
1834 case MPEG_VOLDOWN2:
1835 case MPEG_VOLDOWN2|BUTTON_REPEAT:
1836 #endif
1837 #ifdef MPEG_RC_VOLDOWN
1838 case MPEG_RC_VOLDOWN:
1839 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
1840 #endif
1842 osd_set_volume(-1);
1843 break;
1844 } /* MPEG_VOLDOWN*: */
1846 case MPEG_MENU:
1847 #ifdef MPEG_RC_MENU
1848 case MPEG_RC_MENU:
1849 #endif
1851 int state = osd_halt(); /* save previous state */
1852 int result;
1854 /* Hide video output */
1855 osd_show(OSD_HIDE | OSD_NODRAW);
1856 stream_show_vo(false);
1857 osd_backlight_brightness_video_mode(false);
1859 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1860 rb->lcd_set_mode(LCD_MODE_RGB565);
1861 #endif
1863 result = mpeg_menu();
1865 next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
1867 fps_update_post_frame_callback();
1869 /* The menu can change the font, so restore */
1870 rb->lcd_setfont(FONT_SYSFIXED);
1871 #ifdef HAVE_LCD_COLOR
1872 rb->lcd_set_foreground(LCD_WHITE);
1873 rb->lcd_set_background(LCD_BLACK);
1874 #endif
1875 rb->lcd_clear_display();
1876 rb->lcd_update();
1878 switch (result)
1880 case MPEG_MENU_QUIT:
1881 next_action = VIDEO_STOP;
1882 osd_stop();
1883 break;
1885 default:
1886 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1887 rb->lcd_set_mode(LCD_MODE_YUV);
1888 #endif
1889 /* If not stopped, show video again */
1890 if (state != STREAM_STOPPED) {
1891 osd_show(OSD_SHOW);
1892 stream_show_vo(true);
1895 /* If stream was playing, restart it */
1896 if (state == STREAM_PLAYING) {
1897 osd_resume();
1899 break;
1901 break;
1902 } /* MPEG_MENU: */
1904 #ifdef MPEG_SHOW_OSD
1905 case MPEG_SHOW_OSD:
1906 case MPEG_SHOW_OSD | BUTTON_REPEAT:
1907 /* Show if not visible */
1908 osd_show(OSD_SHOW);
1909 /* Make sure it refreshes */
1910 osd_refresh(OSD_REFRESH_DEFAULT);
1911 break;
1912 #endif
1914 case MPEG_STOP:
1915 #ifdef MPEG_RC_STOP
1916 case MPEG_RC_STOP:
1917 #endif
1918 case ACTION_STD_CANCEL:
1920 cancel_playback:
1921 next_action = VIDEO_STOP;
1922 osd_stop();
1923 break;
1924 } /* MPEG_STOP: */
1926 case MPEG_PAUSE:
1927 #ifdef MPEG_PAUSE2
1928 case MPEG_PAUSE2:
1929 #endif
1930 #ifdef MPEG_RC_PAUSE
1931 case MPEG_RC_PAUSE:
1932 #endif
1934 int status = osd_stream_status();
1936 if (status == STREAM_PLAYING) {
1937 /* Playing => Paused */
1938 osd_pause();
1940 else if (status == STREAM_PAUSED) {
1941 /* Paused => Playing */
1942 osd_resume();
1945 break;
1946 } /* MPEG_PAUSE*: */
1948 case MPEG_RW:
1949 #ifdef MPEG_RW2
1950 case MPEG_RW2:
1951 #endif
1952 #ifdef MPEG_RC_RW
1953 case MPEG_RC_RW:
1954 #endif
1956 int old_button = button;
1958 /* If button has been released: skip to next/previous file */
1959 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
1961 if ((old_button | BUTTON_REL) == button) {
1962 /* Check current playback position */
1963 osd_update_time();
1965 if (settings.play_mode == 0 || osd.curr_time >= 3*TS_SECOND) {
1966 /* Start the current video from the beginning */
1967 osd_seek_time(0*TS_SECOND);
1969 else {
1970 /* Release within 3 seconds of start: skip to previous
1971 * file */
1972 osd_stop();
1973 next_action = VIDEO_PREV;
1976 else if ((button & ~BUTTON_REPEAT) == old_button) {
1977 button = osd_seek_btn(old_button);
1980 if (button == ACTION_STD_CANCEL)
1981 goto cancel_playback; /* jump to stop handling above */
1983 rb->default_event_handler(button);
1984 break;
1985 } /* MPEG_RW: */
1987 case MPEG_FF:
1988 #ifdef MPEG_FF2
1989 case MPEG_FF2:
1990 #endif
1991 #ifdef MPEG_RC_FF
1992 case MPEG_RC_FF:
1993 #endif
1995 int old_button = button;
1997 if (settings.play_mode != 0)
1998 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
2000 if ((old_button | BUTTON_REL) == button) {
2001 /* If button has been released: skip to next file */
2002 osd_stop();
2003 next_action = VIDEO_NEXT;
2005 else if ((button & ~BUTTON_REPEAT) == old_button) {
2006 button = osd_seek_btn(old_button);
2009 if (button == ACTION_STD_CANCEL)
2010 goto cancel_playback; /* jump to stop handling above */
2012 rb->default_event_handler(button);
2013 break;
2014 } /* MPEG_FF: */
2016 #ifdef HAVE_HEADPHONE_DETECTION
2017 case SYS_PHONE_PLUGGED:
2018 case SYS_PHONE_UNPLUGGED:
2020 osd_handle_phone_plug(button == SYS_PHONE_PLUGGED);
2021 break;
2022 } /* SYS_PHONE_*: */
2023 #endif
2025 default:
2027 osd_refresh(OSD_REFRESH_DEFAULT);
2028 rb->default_event_handler(button);
2029 break;
2030 } /* default: */
2033 rb->yield();
2034 } /* end while */
2036 osd_stop();
2038 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
2039 /* Be sure hook is removed before exiting since the stop will put it
2040 * back because of the backlight restore. */
2041 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
2042 #endif
2044 rb->lcd_setfont(FONT_UI);
2046 return next_action;
2049 enum plugin_status plugin_start(const void* parameter)
2051 static char videofile[MAX_PATH];
2052 int status = PLUGIN_OK; /* assume success */
2053 bool quit = false;
2055 if (parameter == NULL) {
2056 /* No file = GTFO */
2057 rb->splash(HZ*2, "No File");
2058 return PLUGIN_ERROR;
2061 /* Disable all talking before initializing IRAM */
2062 rb->talk_disable(true);
2064 #ifdef HAVE_LCD_COLOR
2065 rb->lcd_set_backdrop(NULL);
2066 rb->lcd_set_foreground(LCD_WHITE);
2067 rb->lcd_set_background(LCD_BLACK);
2068 #endif
2070 rb->lcd_clear_display();
2071 rb->lcd_update();
2073 rb->strcpy(videofile, (const char*) parameter);
2075 if (stream_init() < STREAM_OK) {
2076 /* Fatal because this should not fail */
2077 DEBUGF("Could not initialize streams\n");
2078 status = PLUGIN_ERROR;
2079 } else {
2080 int next_action = VIDEO_STOP;
2081 bool get_videofile_says = true;
2083 while (!quit)
2085 int result;
2087 init_settings(videofile);
2089 result = stream_open(videofile);
2091 if (result >= STREAM_OK) {
2092 /* start menu */
2093 rb->lcd_clear_display();
2094 rb->lcd_update();
2095 result = mpeg_start_menu(stream_get_duration());
2097 next_action = VIDEO_STOP;
2098 if (result != MPEG_START_QUIT) {
2099 /* Enter button loop and process UI */
2100 next_action = button_loop();
2103 stream_close();
2105 rb->lcd_clear_display();
2106 rb->lcd_update();
2108 save_settings();
2109 } else {
2110 /* Problem with file; display message about it - not
2111 * considered a plugin error */
2112 long tick;
2113 const char *errstring;
2115 DEBUGF("Could not open %s\n", videofile);
2116 switch (result)
2118 case STREAM_UNSUPPORTED:
2119 errstring = "Unsupported format";
2120 break;
2121 default:
2122 errstring = "Error opening file: %d";
2125 tick = *rb->current_tick + HZ*2;
2127 rb->splashf(0, errstring, result);
2129 /* Be sure it doesn't get stuck in an unbreakable loop of bad
2130 * files, just in case! Otherwise, keep searching in the
2131 * chosen direction until a good one is found. */
2132 while (!quit && TIME_BEFORE(*rb->current_tick, tick))
2134 int button = mpeg_button_get(HZ*2);
2136 switch (button)
2138 case MPEG_STOP:
2139 case ACTION_STD_CANCEL:
2140 /* Abort the search and exit */
2141 next_action = VIDEO_STOP;
2142 quit = true;
2143 break;
2145 case BUTTON_NONE:
2146 if (settings.play_mode != 0) {
2147 if (next_action == VIDEO_STOP) {
2148 /* Default to next file */
2149 next_action = VIDEO_NEXT;
2151 else if (next_action == VIDEO_PREV &&
2152 !get_videofile_says) {
2153 /* Was first file already; avoid endlessly
2154 * retrying it */
2155 next_action = VIDEO_STOP;
2158 break;
2160 default:
2161 rb->default_event_handler(button);
2162 } /* switch */
2163 } /* while */
2166 /* return value of button_loop says, what's next */
2167 switch (next_action)
2169 case VIDEO_NEXT:
2171 get_videofile_says = get_videofile(VIDEO_NEXT, videofile,
2172 sizeof(videofile));
2173 /* quit after finished the last videofile */
2174 quit = !get_videofile_says;
2175 break;
2177 case VIDEO_PREV:
2179 get_videofile_says = get_videofile(VIDEO_PREV, videofile,
2180 sizeof(videofile));
2181 /* if there is no previous file, play the same videofile */
2182 break;
2184 case VIDEO_STOP:
2186 quit = true;
2187 break;
2190 } /* while */
2193 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2194 rb->lcd_set_mode(LCD_MODE_RGB565);
2195 #endif
2197 stream_exit();
2199 rb->talk_disable(false);
2201 /* Actually handle delayed processing of system events of interest
2202 * that were captured in other button loops */
2203 mpeg_sysevent_handle();
2205 return status;