1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 /****************************************************************************
27 * mpegplayer is structured as follows:
29 * +-->Video Thread-->Video Output-->LCD
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.)
41 * 1) The main thread - Handles user input, settings, basic playback control
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
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
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
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 *****************************************************************************/
103 #include "mpegplayer.h"
104 #include "lib/helper.h"
105 #include "mpeg_settings.h"
107 #include "video_out.h"
108 #include "stream_thread.h"
109 #include "stream_mgr.h"
114 /* button definitions */
115 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
116 #define MPEG_MENU BUTTON_MODE
117 #define MPEG_STOP BUTTON_OFF
118 #define MPEG_PAUSE BUTTON_ON
119 #define MPEG_VOLDOWN BUTTON_DOWN
120 #define MPEG_VOLUP BUTTON_UP
121 #define MPEG_RW BUTTON_LEFT
122 #define MPEG_FF BUTTON_RIGHT
124 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
125 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
126 #define MPEG_MENU BUTTON_MENU
127 #define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
128 #define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
129 #define MPEG_VOLDOWN BUTTON_SCROLL_BACK
130 #define MPEG_VOLUP BUTTON_SCROLL_FWD
131 #define MPEG_RW BUTTON_LEFT
132 #define MPEG_FF BUTTON_RIGHT
134 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
135 #define MPEG_MENU (BUTTON_REC | BUTTON_REL)
136 #define MPEG_STOP BUTTON_POWER
137 #define MPEG_PAUSE BUTTON_PLAY
138 #define MPEG_VOLDOWN BUTTON_DOWN
139 #define MPEG_VOLUP BUTTON_UP
140 #define MPEG_RW BUTTON_LEFT
141 #define MPEG_FF BUTTON_RIGHT
143 #elif CONFIG_KEYPAD == GIGABEAT_PAD
144 #define MPEG_MENU BUTTON_MENU
145 #define MPEG_STOP BUTTON_POWER
146 #define MPEG_PAUSE BUTTON_SELECT
147 #define MPEG_PAUSE2 BUTTON_A
148 #define MPEG_VOLDOWN BUTTON_LEFT
149 #define MPEG_VOLUP BUTTON_RIGHT
150 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
151 #define MPEG_VOLUP2 BUTTON_VOL_UP
152 #define MPEG_RW BUTTON_UP
153 #define MPEG_FF BUTTON_DOWN
155 #define MPEG_RC_MENU BUTTON_RC_DSP
156 #define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
157 #define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
158 #define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
159 #define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
160 #define MPEG_RC_RW BUTTON_RC_REW
161 #define MPEG_RC_FF BUTTON_RC_FF
163 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
164 #define MPEG_MENU BUTTON_MENU
165 #define MPEG_STOP BUTTON_POWER
166 #define MPEG_PAUSE BUTTON_SELECT
167 #define MPEG_PAUSE2 BUTTON_PLAY
168 #define MPEG_VOLDOWN BUTTON_LEFT
169 #define MPEG_VOLUP BUTTON_RIGHT
170 #define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
171 #define MPEG_VOLUP2 BUTTON_VOL_UP
172 #define MPEG_RW BUTTON_UP
173 #define MPEG_FF BUTTON_DOWN
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_SA9200_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_UP
291 #define MPEG_FF BUTTON_DOWN
293 #elif CONFIG_KEYPAD == ONDAVX747_PAD
294 #define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
295 //#define MPEG_STOP BUTTON_POWER
296 #define MPEG_VOLDOWN BUTTON_VOL_DOWN
297 #define MPEG_VOLUP BUTTON_VOL_UP
299 #elif CONFIG_KEYPAD == ONDAVX777_PAD
300 #define MPEG_MENU BUTTON_POWER
302 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
303 #define MPEG_MENU BUTTON_LEFT
304 #define MPEG_STOP BUTTON_RIGHT
305 #define MPEG_PAUSE BUTTON_PLAY
306 #define MPEG_VOLDOWN BUTTON_DOWN
307 #define MPEG_VOLUP BUTTON_UP
308 #define MPEG_RW BUTTON_REW
309 #define MPEG_FF BUTTON_FFWD
311 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
312 #define MPEG_MENU BUTTON_MENU
313 #define MPEG_STOP BUTTON_REC
314 #define MPEG_PAUSE BUTTON_PLAY
315 #define MPEG_VOLDOWN BUTTON_DOWN
316 #define MPEG_VOLUP BUTTON_UP
317 #define MPEG_RW BUTTON_PREV
318 #define MPEG_FF BUTTON_NEXT
321 #error No keymap defined!
324 #ifdef HAVE_TOUCHSCREEN
326 #define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
329 #define MPEG_STOP BUTTON_TOPLEFT
332 #define MPEG_PAUSE BUTTON_CENTER
335 #define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
338 #define MPEG_VOLUP BUTTON_TOPMIDDLE
341 #define MPEG_RW BUTTON_MIDLEFT
344 #define MPEG_FF BUTTON_MIDRIGHT
348 /* One thing we can do here for targets with remotes is having a display
349 * always on the remote instead of always forcing a popup on the main display */
351 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
352 /* 3% of 30min file == 54s step size */
353 #define MIN_FF_REWIND_STEP (TS_SECOND/2)
354 #define WVS_MIN_UPDATE_INTERVAL (HZ/2)
356 /* WVS status - same order as icon array */
359 WVS_STATUS_STOPPED
= 0,
365 WVS_STATUS_MASK
= 0x7
370 WVS_REFRESH_DEFAULT
= 0x0000, /* Only refresh elements when due */
372 WVS_REFRESH_VOLUME
= 0x0001, /* ...volume display */
373 WVS_REFRESH_TIME
= 0x0002, /* ...time display+progress */
374 WVS_REFRESH_STATUS
= 0x0004, /* ...playback status icon */
375 WVS_REFRESH_BACKGROUND
= 0x0008, /* ...background (implies ALL) */
376 WVS_REFRESH_VIDEO
= 0x0010, /* ...video image upon timeout */
377 WVS_REFRESH_RESUME
= 0x0020, /* Resume playback upon timeout */
378 WVS_NODRAW
= 0x8000, /* OR bitflag - don't draw anything */
379 WVS_SHOW
= 0x4000, /* OR bitflag - show the WVS */
380 WVS_HP_PAUSE
= 0x2000,
381 WVS_HIDE
= 0x0000, /* hide the WVS (aid readability) */
382 WVS_REFRESH_ALL
= 0x000f, /* Only immediate graphical elements */
385 /* Status icons selected according to font height */
386 extern const unsigned char mpegplayer_status_icons_8x8x1
[];
387 extern const unsigned char mpegplayer_status_icons_12x12x1
[];
388 extern const unsigned char mpegplayer_status_icons_16x16x1
[];
390 /* Main border areas that contain WVS elements */
404 long next_auto_refresh
;
411 unsigned prog_fillcolor
;
412 struct vo_rect update_rect
;
413 struct vo_rect prog_rect
;
414 struct vo_rect time_rect
;
415 struct vo_rect dur_rect
;
416 struct vo_rect vol_rect
;
417 const unsigned char *icons
;
418 struct vo_rect stat_rect
;
421 unsigned auto_refresh
;
425 static struct wvs wvs
;
427 static void wvs_show(unsigned show
);
430 #define _X (x + wvs.x)
431 #define _Y (y + wvs.y)
435 #define _X (LCD_WIDTH - (y + wvs.y) - height)
436 #define _Y (x + wvs.x)
441 #ifdef HAVE_LCD_COLOR
442 /* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
443 static unsigned draw_blendcolor(unsigned c1
, unsigned c2
, unsigned char amount
)
445 int r1
= RGB_UNPACK_RED(c1
);
446 int g1
= RGB_UNPACK_GREEN(c1
);
447 int b1
= RGB_UNPACK_BLUE(c1
);
449 int r2
= RGB_UNPACK_RED(c2
);
450 int g2
= RGB_UNPACK_GREEN(c2
);
451 int b2
= RGB_UNPACK_BLUE(c2
);
453 return LCD_RGBPACK(amount
*(r2
- r1
) / 255 + r1
,
454 amount
*(g2
- g1
) / 255 + g1
,
455 amount
*(b2
- b1
) / 255 + b1
);
459 /* Drawing functions that operate rotated on LCD_PORTRAIT displays -
460 * most are just wrappers of lcd_* functions with transforms applied.
461 * The origin is the upper-left corner of the WVS area */
462 static void draw_update_rect(int x
, int y
, int width
, int height
)
464 lcd_(update_rect
)(_X
, _Y
, _W
, _H
);
467 static void draw_clear_area(int x
, int y
, int width
, int height
)
469 #ifdef HAVE_LCD_COLOR
470 rb
->screen_clear_area(rb
->screens
[SCREEN_MAIN
], _X
, _Y
, _W
, _H
);
472 int oldmode
= grey_get_drawmode();
473 grey_set_drawmode(DRMODE_SOLID
| DRMODE_INVERSEVID
);
474 grey_fillrect(_X
, _Y
, _W
, _H
);
475 grey_set_drawmode(oldmode
);
479 static void draw_clear_area_rect(const struct vo_rect
*rc
)
481 draw_clear_area(rc
->l
, rc
->t
, rc
->r
- rc
->l
, rc
->b
- rc
->t
);
484 static void draw_fillrect(int x
, int y
, int width
, int height
)
486 lcd_(fillrect
)(_X
, _Y
, _W
, _H
);
489 static void draw_hline(int x1
, int x2
, int y
)
492 lcd_(hline
)(x1
+ wvs
.x
, x2
+ wvs
.x
, y
+ wvs
.y
);
494 y
= LCD_WIDTH
- (y
+ wvs
.y
) - 1;
495 lcd_(vline
)(y
, x1
+ wvs
.x
, x2
+ wvs
.x
);
499 static void draw_vline(int x
, int y1
, int y2
)
502 lcd_(vline
)(x
+ wvs
.x
, y1
+ wvs
.y
, y2
+ wvs
.y
);
504 y1
= LCD_WIDTH
- (y1
+ wvs
.y
) - 1;
505 y2
= LCD_WIDTH
- (y2
+ wvs
.y
) - 1;
506 lcd_(hline
)(y1
, y2
, x
+ wvs
.x
);
510 static void draw_scrollbar_draw(int x
, int y
, int width
, int height
,
511 uint32_t min
, uint32_t max
, uint32_t val
)
513 unsigned oldfg
= lcd_(get_foreground
)();
515 draw_hline(x
+ 1, x
+ width
- 2, y
);
516 draw_hline(x
+ 1, x
+ width
- 2, y
+ height
- 1);
517 draw_vline(x
, y
+ 1, y
+ height
- 2);
518 draw_vline(x
+ width
- 1, y
+ 1, y
+ height
- 2);
520 val
= muldiv_uint32(width
- 2, val
, max
- min
);
521 val
= MIN(val
, (uint32_t)(width
- 2));
523 draw_fillrect(x
+ 1, y
+ 1, val
, height
- 2);
525 lcd_(set_foreground
)(wvs
.prog_fillcolor
);
527 draw_fillrect(x
+ 1 + val
, y
+ 1, width
- 2 - val
, height
- 2);
529 lcd_(set_foreground
)(oldfg
);
532 static void draw_scrollbar_draw_rect(const struct vo_rect
*rc
, int min
,
535 draw_scrollbar_draw(rc
->l
, rc
->t
, rc
->r
- rc
->l
, rc
->b
- rc
->t
,
540 /* Portrait displays need rotated text rendering */
542 /* Limited function that only renders in DRMODE_FG and uses absolute screen
544 static void draw_oriented_mono_bitmap_part(const unsigned char *src
,
545 int src_x
, int src_y
,
546 int stride
, int x
, int y
,
547 int width
, int height
)
549 const unsigned char *src_end
;
550 fb_data
*dst
, *dst_end
;
551 unsigned fg_pattern
, bg_pattern
;
553 if (x
+ width
> SCREEN_WIDTH
)
554 width
= SCREEN_WIDTH
- x
; /* Clip right */
556 width
+= x
, x
= 0; /* Clip left */
558 return; /* nothing left to do */
560 if (y
+ height
> SCREEN_HEIGHT
)
561 height
= SCREEN_HEIGHT
- y
; /* Clip bottom */
563 height
+= y
, y
= 0; /* Clip top */
565 return; /* nothing left to do */
567 fg_pattern
= rb
->lcd_get_foreground();
568 bg_pattern
= rb
->lcd_get_background();
570 src
+= stride
* (src_y
>> 3) + src_x
; /* move starting point */
572 src_end
= src
+ width
;
574 dst
= rb
->lcd_framebuffer
+ (LCD_WIDTH
- y
) + x
*LCD_WIDTH
;
577 const unsigned char *src_col
= src
++;
578 unsigned data
= *src_col
>> src_y
;
579 int numbits
= 8 - src_y
;
581 fb_data
*dst_col
= dst
;
582 dst_end
= dst_col
- height
;
590 *dst_col
= fg_pattern
;
593 *dst_col
= bg_pattern
;
596 if (--numbits
== 0) {
602 while (dst_col
> dst_end
);
604 while (src
< src_end
);
607 static void draw_putsxy_oriented(int x
, int y
, const char *str
)
612 struct font
* pf
= rb
->font_get(FONT_UI
);
614 ucs
= rb
->bidi_l2v(str
, 1);
619 while ((ch
= *ucs
++) != 0 && x
< SCREEN_WIDTH
)
622 const unsigned char *bits
;
624 /* get proportional width and glyph bits */
625 width
= rb
->font_get_width(pf
, ch
);
632 bits
= rb
->font_get_bits(pf
, ch
);
634 draw_oriented_mono_bitmap_part(bits
, ofs
, 0, width
, x
, y
,
635 width
- ofs
, pf
->height
);
642 static void draw_oriented_mono_bitmap_part(const unsigned char *src
,
643 int src_x
, int src_y
,
644 int stride
, int x
, int y
,
645 int width
, int height
)
647 int mode
= lcd_(get_drawmode
)();
648 lcd_(set_drawmode
)(DRMODE_FG
);
649 lcd_(mono_bitmap_part
)(src
, src_x
, src_y
, stride
, x
, y
, width
, height
);
650 lcd_(set_drawmode
)(mode
);
653 static void draw_putsxy_oriented(int x
, int y
, const char *str
)
655 int mode
= lcd_(get_drawmode
)();
656 lcd_(set_drawmode
)(DRMODE_FG
);
657 lcd_(putsxy
)(x
+ wvs
.x
, y
+ wvs
.y
, str
);
658 lcd_(set_drawmode
)(mode
);
660 #endif /* LCD_PORTRAIT */
662 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
663 /* So we can refresh the overlay */
664 static void wvs_lcd_enable_hook(void* param
)
667 rb
->queue_post(rb
->button_queue
, LCD_ENABLE_EVENT_1
, 0);
671 static void wvs_backlight_on_video_mode(bool video_on
)
674 /* Turn off backlight timeout */
675 /* backlight control in lib/helper.c */
676 backlight_force_on();
677 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
678 rb
->remove_event(LCD_EVENT_ACTIVATION
, wvs_lcd_enable_hook
);
681 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
682 rb
->add_event(LCD_EVENT_ACTIVATION
, false, wvs_lcd_enable_hook
);
684 /* Revert to user's backlight settings */
685 backlight_use_settings();
689 #ifdef HAVE_BACKLIGHT_BRIGHTNESS
690 static void wvs_backlight_brightness_video_mode(bool video_on
)
692 if (settings
.backlight_brightness
< 0)
695 mpeg_backlight_update_brightness(
696 video_on
? settings
.backlight_brightness
: -1);
699 #define wvs_backlight_brightness_video_mode(video_on)
700 #endif /* HAVE_BACKLIGHT_BRIGHTNESS */
702 static void wvs_text_init(void)
709 lcd_(setfont
)(FONT_UI
);
712 wvs
.width
= SCREEN_WIDTH
;
714 vo_rect_clear(&wvs
.time_rect
);
715 vo_rect_clear(&wvs
.stat_rect
);
716 vo_rect_clear(&wvs
.prog_rect
);
717 vo_rect_clear(&wvs
.vol_rect
);
719 ts_to_hms(stream_get_duration(), &hms
);
720 hms_format(buf
, sizeof (buf
), &hms
);
721 lcd_(getstringsize
)(buf
, &wvs
.time_rect
.r
, &wvs
.time_rect
.b
);
723 /* Choose well-sized bitmap images relative to font height */
724 if (wvs
.time_rect
.b
< 12) {
725 wvs
.icons
= mpegplayer_status_icons_8x8x1
;
726 wvs
.stat_rect
.r
= wvs
.stat_rect
.b
= 8;
727 } else if (wvs
.time_rect
.b
< 16) {
728 wvs
.icons
= mpegplayer_status_icons_12x12x1
;
729 wvs
.stat_rect
.r
= wvs
.stat_rect
.b
= 12;
731 wvs
.icons
= mpegplayer_status_icons_16x16x1
;
732 wvs
.stat_rect
.r
= wvs
.stat_rect
.b
= 16;
735 if (wvs
.stat_rect
.b
< wvs
.time_rect
.b
) {
736 vo_rect_offset(&wvs
.stat_rect
, 0,
737 (wvs
.time_rect
.b
- wvs
.stat_rect
.b
) / 2 + WVS_BDR_T
);
738 vo_rect_offset(&wvs
.time_rect
, WVS_BDR_L
, WVS_BDR_T
);
740 vo_rect_offset(&wvs
.time_rect
, WVS_BDR_L
,
741 wvs
.stat_rect
.b
- wvs
.time_rect
.b
+ WVS_BDR_T
);
742 vo_rect_offset(&wvs
.stat_rect
, 0, WVS_BDR_T
);
745 wvs
.dur_rect
= wvs
.time_rect
;
747 phys
= rb
->sound_val2phys(SOUND_VOLUME
, rb
->sound_min(SOUND_VOLUME
));
748 rb
->snprintf(buf
, sizeof(buf
), "%d%s", phys
,
749 rb
->sound_unit(SOUND_VOLUME
));
751 lcd_(getstringsize
)(" ", &spc_width
, NULL
);
752 lcd_(getstringsize
)(buf
, &wvs
.vol_rect
.r
, &wvs
.vol_rect
.b
);
754 wvs
.prog_rect
.r
= SCREEN_WIDTH
- WVS_BDR_L
- spc_width
-
755 wvs
.vol_rect
.r
- WVS_BDR_R
;
756 wvs
.prog_rect
.b
= 3*wvs
.stat_rect
.b
/ 4;
757 vo_rect_offset(&wvs
.prog_rect
, wvs
.time_rect
.l
,
760 vo_rect_offset(&wvs
.stat_rect
,
761 (wvs
.prog_rect
.r
+ wvs
.prog_rect
.l
- wvs
.stat_rect
.r
) / 2,
764 vo_rect_offset(&wvs
.dur_rect
,
765 wvs
.prog_rect
.r
- wvs
.dur_rect
.r
, 0);
767 vo_rect_offset(&wvs
.vol_rect
, wvs
.prog_rect
.r
+ spc_width
,
768 (wvs
.prog_rect
.b
+ wvs
.prog_rect
.t
- wvs
.vol_rect
.b
) / 2);
770 wvs
.height
= WVS_BDR_T
+ MAX(wvs
.prog_rect
.b
, wvs
.vol_rect
.b
) -
771 MIN(wvs
.time_rect
.t
, wvs
.stat_rect
.t
) + WVS_BDR_B
;
773 #ifdef HAVE_LCD_COLOR
774 wvs
.height
= ALIGN_UP(wvs
.height
, 2);
776 wvs
.y
= SCREEN_HEIGHT
- wvs
.height
;
778 lcd_(setfont
)(FONT_SYSFIXED
);
781 static void wvs_init(void)
785 wvs
.print_delay
= 75*HZ
/100;
786 wvs
.resume_delay
= HZ
/2;
787 #ifdef HAVE_LCD_COLOR
788 wvs
.bgcolor
= LCD_RGBPACK(0x73, 0x75, 0xbd);
789 wvs
.fgcolor
= LCD_WHITE
;
790 wvs
.prog_fillcolor
= LCD_BLACK
;
792 wvs
.bgcolor
= GREY_LIGHTGRAY
;
793 wvs
.fgcolor
= GREY_BLACK
;
794 wvs
.prog_fillcolor
= GREY_WHITE
;
797 wvs
.status
= WVS_STATUS_STOPPED
;
798 wvs
.auto_refresh
= WVS_REFRESH_TIME
;
799 wvs
.next_auto_refresh
= *rb
->current_tick
;
803 static void wvs_schedule_refresh(unsigned refresh
)
805 long tick
= *rb
->current_tick
;
807 if (refresh
& WVS_REFRESH_VIDEO
)
808 wvs
.print_tick
= tick
+ wvs
.print_delay
;
810 if (refresh
& WVS_REFRESH_RESUME
)
811 wvs
.resume_tick
= tick
+ wvs
.resume_delay
;
813 wvs
.auto_refresh
|= refresh
;
816 static void wvs_cancel_refresh(unsigned refresh
)
818 wvs
.auto_refresh
&= ~refresh
;
821 /* Refresh the background area */
822 static void wvs_refresh_background(void)
827 unsigned bg
= lcd_(get_background
)();
828 lcd_(set_drawmode
)(DRMODE_SOLID
| DRMODE_INVERSEVID
);
830 #ifdef HAVE_LCD_COLOR
831 /* Draw a "raised" area for our graphics */
832 lcd_(set_background
)(draw_blendcolor(bg
, DRAW_WHITE
, 192));
833 draw_hline(0, wvs
.width
, 0);
835 lcd_(set_background
)(draw_blendcolor(bg
, DRAW_WHITE
, 80));
836 draw_hline(0, wvs
.width
, 1);
838 lcd_(set_background
)(draw_blendcolor(bg
, DRAW_BLACK
, 48));
839 draw_hline(0, wvs
.width
, wvs
.height
-2);
841 lcd_(set_background
)(draw_blendcolor(bg
, DRAW_BLACK
, 128));
842 draw_hline(0, wvs
.width
, wvs
.height
-1);
844 lcd_(set_background
)(bg
);
845 draw_clear_area(0, 2, wvs
.width
, wvs
.height
- 4);
847 /* Give contrast with the main background */
848 lcd_(set_background
)(GREY_WHITE
);
849 draw_hline(0, wvs
.width
, 0);
851 lcd_(set_background
)(GREY_DARKGRAY
);
852 draw_hline(0, wvs
.width
, wvs
.height
-1);
854 lcd_(set_background
)(bg
);
855 draw_clear_area(0, 1, wvs
.width
, wvs
.height
- 2);
858 vo_rect_set_ext(&wvs
.update_rect
, 0, 0, wvs
.width
, wvs
.height
);
859 lcd_(set_drawmode
)(DRMODE_SOLID
);
861 if (stream_get_duration() != INVALID_TIMESTAMP
) {
862 /* Draw the movie duration */
863 ts_to_hms(stream_get_duration(), &hms
);
864 hms_format(buf
, sizeof (buf
), &hms
);
865 draw_putsxy_oriented(wvs
.dur_rect
.l
, wvs
.dur_rect
.t
, buf
);
867 /* else don't know the duration */
870 /* Refresh the current time display + the progress bar */
871 static void wvs_refresh_time(void)
876 uint32_t duration
= stream_get_duration();
878 draw_scrollbar_draw_rect(&wvs
.prog_rect
, 0, duration
,
881 ts_to_hms(wvs
.curr_time
, &hms
);
882 hms_format(buf
, sizeof (buf
), &hms
);
884 draw_clear_area_rect(&wvs
.time_rect
);
885 draw_putsxy_oriented(wvs
.time_rect
.l
, wvs
.time_rect
.t
, buf
);
887 vo_rect_union(&wvs
.update_rect
, &wvs
.update_rect
,
889 vo_rect_union(&wvs
.update_rect
, &wvs
.update_rect
,
893 /* Refresh the volume display area */
894 static void wvs_refresh_volume(void)
899 int volume
= rb
->global_settings
->volume
;
900 rb
->snprintf(buf
, sizeof (buf
), "%d%s",
901 rb
->sound_val2phys(SOUND_VOLUME
, volume
),
902 rb
->sound_unit(SOUND_VOLUME
));
903 lcd_(getstringsize
)(buf
, &width
, NULL
);
905 /* Right-justified */
906 draw_clear_area_rect(&wvs
.vol_rect
);
907 draw_putsxy_oriented(wvs
.vol_rect
.r
- width
, wvs
.vol_rect
.t
, buf
);
909 vo_rect_union(&wvs
.update_rect
, &wvs
.update_rect
, &wvs
.vol_rect
);
912 /* Refresh the status icon */
913 static void wvs_refresh_status(void)
915 int icon_size
= wvs
.stat_rect
.r
- wvs
.stat_rect
.l
;
917 draw_clear_area_rect(&wvs
.stat_rect
);
919 #ifdef HAVE_LCD_COLOR
920 /* Draw status icon with a drop shadow */
921 unsigned oldfg
= lcd_(get_foreground
)();
924 lcd_(set_foreground
)(draw_blendcolor(lcd_(get_background
)(),
929 draw_oriented_mono_bitmap_part(wvs
.icons
,
930 icon_size
*wvs
.status
,
932 icon_size
*WVS_STATUS_COUNT
,
933 wvs
.stat_rect
.l
+ wvs
.x
+ i
,
934 wvs
.stat_rect
.t
+ wvs
.y
+ i
,
935 icon_size
, icon_size
);
940 lcd_(set_foreground
)(oldfg
);
943 vo_rect_union(&wvs
.update_rect
, &wvs
.update_rect
, &wvs
.stat_rect
);
945 draw_oriented_mono_bitmap_part(wvs
.icons
,
946 icon_size
*wvs
.status
,
948 icon_size
*WVS_STATUS_COUNT
,
949 wvs
.stat_rect
.l
+ wvs
.x
,
950 wvs
.stat_rect
.t
+ wvs
.y
,
951 icon_size
, icon_size
);
952 vo_rect_union(&wvs
.update_rect
, &wvs
.update_rect
, &wvs
.stat_rect
);
956 /* Update the current status which determines which icon is displayed */
957 static bool wvs_update_status(void)
961 switch (stream_status())
964 status
= WVS_STATUS_STOPPED
;
967 /* If paused with a pending resume, coerce it to WVS_STATUS_PLAYING */
968 status
= (wvs
.auto_refresh
& WVS_REFRESH_RESUME
) ?
969 WVS_STATUS_PLAYING
: WVS_STATUS_PAUSED
;
972 status
= WVS_STATUS_PLAYING
;
976 if (status
!= wvs
.status
) {
977 /* A refresh is needed */
985 /* Update the current time that will be displayed */
986 static void wvs_update_time(void)
989 wvs
.curr_time
= stream_get_seek_time(&start
);
990 wvs
.curr_time
-= start
;
993 /* Refresh various parts of the WVS - showing it if it is hidden */
994 static void wvs_refresh(int hint
)
997 unsigned oldbg
, oldfg
;
999 tick
= *rb
->current_tick
;
1001 if (hint
== WVS_REFRESH_DEFAULT
) {
1002 /* The default which forces no updates */
1004 /* Make sure Rockbox doesn't turn off the player because of
1005 too little activity */
1006 if (wvs
.status
== WVS_STATUS_PLAYING
)
1007 rb
->reset_poweroff_timer();
1009 /* Redraw the current or possibly extract a new video frame */
1010 if ((wvs
.auto_refresh
& WVS_REFRESH_VIDEO
) &&
1011 TIME_AFTER(tick
, wvs
.print_tick
)) {
1012 wvs
.auto_refresh
&= ~WVS_REFRESH_VIDEO
;
1013 stream_draw_frame(false);
1016 /* Restart playback if the timout was reached */
1017 if ((wvs
.auto_refresh
& WVS_REFRESH_RESUME
) &&
1018 TIME_AFTER(tick
, wvs
.resume_tick
)) {
1019 wvs
.auto_refresh
&= ~(WVS_REFRESH_RESUME
| WVS_REFRESH_VIDEO
);
1023 /* If not visible, return */
1024 if (!(wvs
.flags
& WVS_SHOW
))
1027 /* Hide if the visibility duration was reached */
1028 if (TIME_AFTER(tick
, wvs
.hide_tick
)) {
1033 /* A forced update of some region */
1035 /* Show if currently invisible */
1036 if (!(wvs
.flags
& WVS_SHOW
)) {
1037 /* Avoid call back into this function - it will be drawn */
1038 wvs_show(WVS_SHOW
| WVS_NODRAW
);
1039 hint
= WVS_REFRESH_ALL
;
1042 /* Move back timeouts for frame print and hide */
1043 wvs
.print_tick
= tick
+ wvs
.print_delay
;
1044 wvs
.hide_tick
= tick
+ wvs
.show_for
;
1047 if (TIME_AFTER(tick
, wvs
.next_auto_refresh
)) {
1048 /* Refresh whatever graphical elements are due automatically */
1049 wvs
.next_auto_refresh
= tick
+ WVS_MIN_UPDATE_INTERVAL
;
1051 if (wvs
.auto_refresh
& WVS_REFRESH_STATUS
) {
1052 if (wvs_update_status())
1053 hint
|= WVS_REFRESH_STATUS
;
1056 if (wvs
.auto_refresh
& WVS_REFRESH_TIME
) {
1058 hint
|= WVS_REFRESH_TIME
;
1063 return; /* No drawing needed */
1065 /* Set basic drawing params that are used. Elements that perform variations
1066 * will restore them. */
1067 oldfg
= lcd_(get_foreground
)();
1068 oldbg
= lcd_(get_background
)();
1070 lcd_(setfont
)(FONT_UI
);
1071 lcd_(set_foreground
)(wvs
.fgcolor
);
1072 lcd_(set_background
)(wvs
.bgcolor
);
1074 vo_rect_clear(&wvs
.update_rect
);
1076 if (hint
& WVS_REFRESH_BACKGROUND
) {
1077 wvs_refresh_background();
1078 hint
|= WVS_REFRESH_ALL
; /* Requires a redraw of everything */
1081 if (hint
& WVS_REFRESH_TIME
) {
1085 if (hint
& WVS_REFRESH_VOLUME
) {
1086 wvs_refresh_volume();
1089 if (hint
& WVS_REFRESH_STATUS
) {
1090 wvs_refresh_status();
1093 /* Go back to defaults */
1094 lcd_(setfont
)(FONT_SYSFIXED
);
1095 lcd_(set_foreground
)(oldfg
);
1096 lcd_(set_background
)(oldbg
);
1098 /* Update the dirty rectangle */
1101 draw_update_rect(wvs
.update_rect
.l
,
1103 wvs
.update_rect
.r
- wvs
.update_rect
.l
,
1104 wvs
.update_rect
.b
- wvs
.update_rect
.t
);
1109 /* Show/Hide the WVS */
1110 static void wvs_show(unsigned show
)
1112 if (((show
^ wvs
.flags
) & WVS_SHOW
) == 0)
1114 if (show
& WVS_SHOW
) {
1115 wvs
.hide_tick
= *rb
->current_tick
+ wvs
.show_for
;
1120 if (show
& WVS_SHOW
) {
1121 /* Clip away the part of video that is covered */
1122 struct vo_rect rc
= { 0, 0, SCREEN_WIDTH
, wvs
.y
};
1124 wvs
.flags
|= WVS_SHOW
;
1126 if (wvs
.status
!= WVS_STATUS_PLAYING
) {
1127 /* Not playing - set brightness to mpegplayer setting */
1128 wvs_backlight_brightness_video_mode(true);
1131 stream_vo_set_clip(&rc
);
1133 if (!(show
& WVS_NODRAW
))
1134 wvs_refresh(WVS_REFRESH_ALL
);
1136 /* Uncover clipped video area and redraw it */
1137 wvs
.flags
&= ~WVS_SHOW
;
1139 draw_clear_area(0, 0, wvs
.width
, wvs
.height
);
1141 if (!(show
& WVS_NODRAW
)) {
1143 draw_update_rect(0, 0, wvs
.width
, wvs
.height
);
1146 stream_vo_set_clip(NULL
);
1147 stream_draw_frame(false);
1149 stream_vo_set_clip(NULL
);
1152 if (wvs
.status
!= WVS_STATUS_PLAYING
) {
1153 /* Not playing - restore backlight brightness */
1154 wvs_backlight_brightness_video_mode(false);
1159 /* Set the current status - update screen if specified */
1160 static void wvs_set_status(int status
)
1162 bool draw
= (status
& WVS_NODRAW
) == 0;
1164 status
&= WVS_STATUS_MASK
;
1166 if (wvs
.status
!= status
) {
1168 wvs
.status
= status
;
1171 wvs_refresh(WVS_REFRESH_STATUS
);
1175 /* Get the current status value */
1176 static int wvs_get_status(void)
1178 return wvs
.status
& WVS_STATUS_MASK
;
1181 /* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;) */
1182 static uint32_t wvs_ff_rw(int btn
, unsigned refresh
)
1184 unsigned int step
= TS_SECOND
*rb
->global_settings
->ff_rewind_min_step
;
1185 const long ff_rw_accel
= (rb
->global_settings
->ff_rewind_accel
+ 3);
1187 uint32_t time
= stream_get_seek_time(&start
);
1188 const uint32_t duration
= stream_get_duration();
1189 unsigned int max_step
= 0;
1190 uint32_t ff_rw_count
= 0;
1191 unsigned status
= wvs
.status
;
1193 wvs_cancel_refresh(WVS_REFRESH_VIDEO
| WVS_REFRESH_RESUME
|
1196 time
-= start
; /* Absolute clock => stream-relative */
1204 wvs_set_status(WVS_STATUS_FF
);
1210 wvs_set_status(WVS_STATUS_RW
);
1216 btn
|= BUTTON_REPEAT
;
1220 stream_keep_disk_active();
1225 wvs_refresh(WVS_REFRESH_DEFAULT
);
1228 case MPEG_FF
| BUTTON_REPEAT
:
1229 case MPEG_RW
| BUTTON_REPEAT
:
1231 case MPEG_RC_FF
| BUTTON_REPEAT
:
1232 case MPEG_RC_RW
| BUTTON_REPEAT
:
1236 case MPEG_FF
| BUTTON_REL
:
1237 case MPEG_RW
| BUTTON_REL
:
1239 case MPEG_RC_FF
| BUTTON_REL
:
1240 case MPEG_RC_RW
| BUTTON_REL
:
1242 if (wvs
.status
== WVS_STATUS_FF
)
1243 time
+= ff_rw_count
;
1244 else if (wvs
.status
== WVS_STATUS_RW
)
1245 time
-= ff_rw_count
;
1250 wvs_schedule_refresh(refresh
);
1251 wvs_set_status(status
);
1252 wvs_schedule_refresh(WVS_REFRESH_TIME
);
1256 if (wvs
.status
== WVS_STATUS_FF
) {
1257 /* fast forwarding, calc max step relative to end */
1258 max_step
= muldiv_uint32(duration
- (time
+ ff_rw_count
),
1259 FF_REWIND_MAX_PERCENT
, 100);
1261 /* rewinding, calc max step relative to start */
1262 max_step
= muldiv_uint32(time
- ff_rw_count
,
1263 FF_REWIND_MAX_PERCENT
, 100);
1266 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
1268 if (step
> max_step
)
1271 ff_rw_count
+= step
;
1273 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1274 step
+= step
>> ff_rw_accel
;
1276 if (wvs
.status
== WVS_STATUS_FF
) {
1277 if (duration
- time
<= ff_rw_count
)
1278 ff_rw_count
= duration
- time
;
1280 wvs
.curr_time
= time
+ ff_rw_count
;
1282 if (time
<= ff_rw_count
)
1285 wvs
.curr_time
= time
- ff_rw_count
;
1288 wvs_refresh(WVS_REFRESH_TIME
);
1290 btn
= rb
->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL
);
1294 static int wvs_status(void)
1296 int status
= stream_status();
1298 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1299 if (status
== STREAM_PAUSED
) {
1300 if (wvs
.auto_refresh
& WVS_REFRESH_RESUME
)
1301 status
= STREAM_PLAYING
;
1307 /* Change the current audio volume by a specified amount */
1308 static void wvs_set_volume(int delta
)
1310 int vol
= rb
->global_settings
->volume
;
1316 /* Volume down - clip to lower limit */
1317 limit
= rb
->sound_min(SOUND_VOLUME
);
1321 /* Volume up - clip to upper limit */
1322 limit
= rb
->sound_max(SOUND_VOLUME
);
1327 /* Sync the global settings */
1328 if (vol
!= rb
->global_settings
->volume
) {
1329 rb
->sound_set(SOUND_VOLUME
, vol
);
1330 rb
->global_settings
->volume
= vol
;
1333 /* Update the volume display */
1334 wvs_refresh(WVS_REFRESH_VOLUME
);
1337 /* Begin playback at the specified time */
1338 static int wvs_play(uint32_t time
)
1342 wvs_cancel_refresh(WVS_REFRESH_VIDEO
| WVS_REFRESH_RESUME
);
1344 retval
= stream_seek(time
, SEEK_SET
);
1346 if (retval
>= STREAM_OK
) {
1347 wvs_backlight_on_video_mode(true);
1348 wvs_backlight_brightness_video_mode(true);
1349 stream_show_vo(true);
1350 retval
= stream_play();
1352 if (retval
>= STREAM_OK
)
1353 wvs_set_status(WVS_STATUS_PLAYING
| WVS_NODRAW
);
1359 /* Halt playback - pause engine and return logical state */
1360 static int wvs_halt(void)
1362 int status
= stream_pause();
1364 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1365 if (status
== STREAM_PAUSED
) {
1366 if (wvs_get_status() == WVS_STATUS_PLAYING
)
1367 status
= STREAM_PLAYING
;
1370 /* Cancel some auto refreshes - caller will restart them if desired */
1371 wvs_cancel_refresh(WVS_REFRESH_VIDEO
| WVS_REFRESH_RESUME
);
1373 /* No backlight fiddling here - callers does the right thing */
1378 /* Pause playback if playing */
1379 static int wvs_pause(void)
1381 unsigned refresh
= wvs
.auto_refresh
;
1382 int status
= wvs_halt();
1384 if (status
== STREAM_PLAYING
&& (refresh
& WVS_REFRESH_RESUME
)) {
1385 /* Resume pending - change to a still video frame update */
1386 wvs_schedule_refresh(WVS_REFRESH_VIDEO
);
1389 wvs_set_status(WVS_STATUS_PAUSED
);
1391 wvs_backlight_on_video_mode(false);
1392 /* Leave brightness alone and restore it when WVS is hidden */
1397 /* Resume playback if halted or paused */
1398 static void wvs_resume(void)
1400 /* Cancel video and resume auto refresh - the resyc when starting
1401 * playback will perform those tasks */
1402 wvs_backlight_on_video_mode(true);
1403 wvs_backlight_brightness_video_mode(true);
1404 wvs_cancel_refresh(WVS_REFRESH_VIDEO
| WVS_REFRESH_RESUME
);
1405 wvs_set_status(WVS_STATUS_PLAYING
);
1409 /* Stop playback - remember the resume point if not closed */
1410 static void wvs_stop(void)
1412 uint32_t resume_time
;
1414 wvs_cancel_refresh(WVS_REFRESH_VIDEO
| WVS_REFRESH_RESUME
);
1415 wvs_set_status(WVS_STATUS_STOPPED
| WVS_NODRAW
);
1416 wvs_show(WVS_HIDE
| WVS_NODRAW
);
1420 resume_time
= stream_get_resume_time();
1422 if (resume_time
!= INVALID_TIMESTAMP
)
1423 settings
.resume_time
= resume_time
;
1425 wvs_backlight_on_video_mode(false);
1426 wvs_backlight_brightness_video_mode(false);
1429 /* Perform a seek if seeking is possible for this stream - if playing, a delay
1430 * will be inserted before restarting in case the user decides to seek again */
1431 static void wvs_seek(int btn
)
1437 if (!stream_can_seek())
1440 /* Halt playback - not strictly nescessary but nice */
1441 status
= wvs_halt();
1443 if (status
== STREAM_STOPPED
)
1448 if (status
== STREAM_PLAYING
)
1449 refresh
= WVS_REFRESH_RESUME
; /* delay resume if playing */
1451 refresh
= WVS_REFRESH_VIDEO
; /* refresh if paused */
1453 /* Obtain a new playback point */
1454 time
= wvs_ff_rw(btn
, refresh
);
1456 /* Tell engine to resume at that time */
1457 stream_seek(time
, SEEK_SET
);
1460 #ifdef HAVE_HEADPHONE_DETECTION
1461 /* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
1462 static void wvs_handle_phone_plug(bool inserted
)
1464 if (rb
->global_settings
->unplug_mode
== 0)
1467 /* Wait for any incomplete state transition to complete first */
1468 stream_wait_status();
1470 int status
= wvs_status();
1473 if (rb
->global_settings
->unplug_mode
> 1) {
1474 if (status
== STREAM_PAUSED
) {
1479 if (status
== STREAM_PLAYING
) {
1482 if (stream_can_seek() && rb
->global_settings
->unplug_rw
) {
1483 stream_seek(-rb
->global_settings
->unplug_rw
*TS_SECOND
,
1485 wvs_schedule_refresh(WVS_REFRESH_VIDEO
);
1486 /* Update time display now */
1488 wvs_refresh(WVS_REFRESH_TIME
);
1495 static void button_loop(void)
1497 rb
->lcd_setfont(FONT_SYSFIXED
);
1498 #ifdef HAVE_LCD_COLOR
1499 rb
->lcd_set_foreground(LCD_WHITE
);
1500 rb
->lcd_set_background(LCD_BLACK
);
1502 rb
->lcd_clear_display();
1505 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1506 rb
->lcd_set_mode(LCD_MODE_YUV
);
1511 /* Start playback at the specified starting time */
1512 if (wvs_play(settings
.resume_time
) < STREAM_OK
) {
1513 rb
->splash(HZ
*2, "Playback failed");
1517 /* Gently poll the video player for EOS and handle UI */
1518 while (stream_status() != STREAM_STOPPED
)
1522 mpeg_menu_sysevent_clear();
1523 button
= rb
->button_get_w_tmo(WVS_MIN_UPDATE_INTERVAL
);
1525 button
= mpeg_menu_sysevent_callback(button
, NULL
);
1531 wvs_refresh(WVS_REFRESH_DEFAULT
);
1533 } /* BUTTON_NONE: */
1535 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1536 case LCD_ENABLE_EVENT_1
:
1538 /* Draw the current frame if prepared already */
1539 stream_draw_frame(true);
1541 } /* LCD_ENABLE_EVENT_1: */
1545 case MPEG_VOLUP
|BUTTON_REPEAT
:
1548 case MPEG_VOLUP2
|BUTTON_REPEAT
:
1550 #ifdef MPEG_RC_VOLUP
1552 case MPEG_RC_VOLUP
|BUTTON_REPEAT
:
1557 } /* MPEG_VOLUP*: */
1560 case MPEG_VOLDOWN
|BUTTON_REPEAT
:
1561 #ifdef MPEG_VOLDOWN2
1563 case MPEG_VOLDOWN2
|BUTTON_REPEAT
:
1565 #ifdef MPEG_RC_VOLDOWN
1566 case MPEG_RC_VOLDOWN
:
1567 case MPEG_RC_VOLDOWN
|BUTTON_REPEAT
:
1572 } /* MPEG_VOLDOWN*: */
1579 int state
= wvs_halt(); /* save previous state */
1582 /* Hide video output */
1583 wvs_show(WVS_HIDE
| WVS_NODRAW
);
1584 stream_show_vo(false);
1585 wvs_backlight_brightness_video_mode(false);
1587 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1588 rb
->lcd_set_mode(LCD_MODE_RGB565
);
1591 result
= mpeg_menu();
1593 /* The menu can change the font, so restore */
1594 rb
->lcd_setfont(FONT_SYSFIXED
);
1595 #ifdef HAVE_LCD_COLOR
1596 rb
->lcd_set_foreground(LCD_WHITE
);
1597 rb
->lcd_set_background(LCD_BLACK
);
1599 rb
->lcd_clear_display();
1604 case MPEG_MENU_QUIT
:
1609 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1610 rb
->lcd_set_mode(LCD_MODE_YUV
);
1612 /* If not stopped, show video again */
1613 if (state
!= STREAM_STOPPED
) {
1615 stream_show_vo(true);
1618 /* If stream was playing, restart it */
1619 if (state
== STREAM_PLAYING
) {
1631 case ACTION_STD_CANCEL
:
1641 #ifdef MPEG_RC_PAUSE
1645 int status
= wvs_status();
1647 if (status
== STREAM_PLAYING
) {
1648 /* Playing => Paused */
1651 else if (status
== STREAM_PAUSED
) {
1652 /* Paused => Playing */
1657 } /* MPEG_PAUSE*: */
1668 } /* MPEG_RW: MPEG_FF: */
1670 #ifdef HAVE_HEADPHONE_DETECTION
1671 case SYS_PHONE_PLUGGED
:
1672 case SYS_PHONE_UNPLUGGED
:
1674 wvs_handle_phone_plug(button
== SYS_PHONE_PLUGGED
);
1676 } /* SYS_PHONE_*: */
1681 rb
->default_event_handler(button
);
1691 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1692 /* Be sure hook is removed before exiting since the stop will put it
1693 * back because of the backlight restore. */
1694 rb
->remove_event(LCD_EVENT_ACTIVATION
, wvs_lcd_enable_hook
);
1697 rb
->lcd_setfont(FONT_UI
);
1700 enum plugin_status
plugin_start(const void* parameter
)
1702 int status
= PLUGIN_ERROR
; /* assume failure */
1705 const char *errstring
;
1707 if (parameter
== NULL
) {
1708 /* No file = GTFO */
1709 rb
->splash(HZ
*2, "No File");
1710 return PLUGIN_ERROR
;
1713 /* Disable all talking before initializing IRAM */
1714 rb
->talk_disable(true);
1716 /* Initialize IRAM - stops audio and voice as well */
1717 PLUGIN_IRAM_INIT(rb
)
1719 #ifdef HAVE_LCD_COLOR
1720 rb
->lcd_set_backdrop(NULL
);
1721 rb
->lcd_set_foreground(LCD_WHITE
);
1722 rb
->lcd_set_background(LCD_BLACK
);
1725 rb
->lcd_clear_display();
1728 if (stream_init() < STREAM_OK
) {
1729 DEBUGF("Could not initialize streams\n");
1731 rb
->splash(0, "Loading...");
1732 init_settings((char*)parameter
);
1734 err
= stream_open((char *)parameter
);
1736 if (err
>= STREAM_OK
) {
1738 rb
->lcd_clear_display();
1740 result
= mpeg_start_menu(stream_get_duration());
1742 if (result
!= MPEG_START_QUIT
) {
1743 /* Enter button loop and process UI */
1749 rb
->lcd_clear_display();
1755 mpeg_menu_sysevent_handle();
1757 DEBUGF("Could not open %s\n", (char*)parameter
);
1760 case STREAM_UNSUPPORTED
:
1761 errstring
= "Unsupported format";
1764 errstring
= "Error opening file: %d";
1767 rb
->splashf(HZ
*2, errstring
, err
);
1771 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
1772 rb
->lcd_set_mode(LCD_MODE_RGB565
);
1777 rb
->talk_disable(false);