summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer/mpegplayer.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/mpegplayer/mpegplayer.c')
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c2638
1 files changed, 0 insertions, 2638 deletions
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
deleted file mode 100644
index e66b4df146..0000000000
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ /dev/null
@@ -1,2638 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * mpegplayer main entrypoint and UI implementation
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
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.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24/****************************************************************************
25 * NOTES:
26 *
27 * mpegplayer is structured as follows:
28 *
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.)
39 *
40 * Thread list:
41 * 1) The main thread - Handles user input, settings, basic playback control
42 * and USB connect.
43 *
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.
50 *
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.
55 *
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.
59 *
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.
63 *
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.
70 *
71 * Notes about MPEG files:
72 *
73 * MPEG System Clock is 27MHz - i.e. 27000000 ticks/second.
74 *
75 * FPS is represented in terms of a frame period - this is always an
76 * integer number of 27MHz ticks.
77 *
78 * e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of
79 * 900900 27MHz ticks.
80 *
81 * In libmpeg2, info->sequence->frame_period contains the frame_period.
82 *
83 * Working with Rockbox's 100Hz tick, the common frame rates would need
84 * to be as follows (1):
85 *
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
96 *
97 * *Unofficial framerates
98 *
99 * (1) But we don't really care since the audio clock is used anyway and has
100 * very fine resolution ;-)
101 *****************************************************************************/
102#include "plugin.h"
103#include "mpegplayer.h"
104#include "lib/helper.h"
105#include "mpeg_settings.h"
106#include "video_out.h"
107#include "stream_thread.h"
108#include "stream_mgr.h"
109
110
111/* button definitions */
112#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
113#define MPEG_MENU BUTTON_MODE
114#define MPEG_STOP BUTTON_OFF
115#define MPEG_PAUSE BUTTON_ON
116#define MPEG_VOLDOWN BUTTON_DOWN
117#define MPEG_VOLUP BUTTON_UP
118#define MPEG_RW BUTTON_LEFT
119#define MPEG_FF BUTTON_RIGHT
120
121#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
122 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
123#define MPEG_MENU BUTTON_MENU
124#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
125#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
126#define MPEG_VOLDOWN BUTTON_SCROLL_BACK
127#define MPEG_VOLUP BUTTON_SCROLL_FWD
128#define MPEG_RW BUTTON_LEFT
129#define MPEG_FF BUTTON_RIGHT
130
131#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
132#define MPEG_MENU (BUTTON_REC | BUTTON_REL)
133#define MPEG_STOP BUTTON_POWER
134#define MPEG_PAUSE BUTTON_PLAY
135#define MPEG_VOLDOWN BUTTON_DOWN
136#define MPEG_VOLUP BUTTON_UP
137#define MPEG_RW BUTTON_LEFT
138#define MPEG_FF BUTTON_RIGHT
139
140#elif CONFIG_KEYPAD == GIGABEAT_PAD
141#define MPEG_MENU BUTTON_MENU
142#define MPEG_STOP BUTTON_POWER
143#define MPEG_PAUSE BUTTON_SELECT
144#define MPEG_PAUSE2 BUTTON_A
145#define MPEG_VOLDOWN BUTTON_LEFT
146#define MPEG_VOLUP BUTTON_RIGHT
147#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
148#define MPEG_VOLUP2 BUTTON_VOL_UP
149#define MPEG_RW BUTTON_UP
150#define MPEG_FF BUTTON_DOWN
151
152#define MPEG_RC_MENU BUTTON_RC_DSP
153#define MPEG_RC_STOP (BUTTON_RC_PLAY | BUTTON_REPEAT)
154#define MPEG_RC_PAUSE (BUTTON_RC_PLAY | BUTTON_REL)
155#define MPEG_RC_VOLDOWN BUTTON_RC_VOL_DOWN
156#define MPEG_RC_VOLUP BUTTON_RC_VOL_UP
157#define MPEG_RC_RW BUTTON_RC_REW
158#define MPEG_RC_FF BUTTON_RC_FF
159
160#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
161#define MPEG_MENU BUTTON_MENU
162#define MPEG_STOP BUTTON_POWER
163#define MPEG_PAUSE BUTTON_SELECT
164#define MPEG_PAUSE2 BUTTON_PLAY
165#define MPEG_VOLDOWN BUTTON_LEFT
166#define MPEG_VOLUP BUTTON_RIGHT
167#define MPEG_VOLDOWN2 BUTTON_VOL_DOWN
168#define MPEG_VOLUP2 BUTTON_VOL_UP
169#define MPEG_RW BUTTON_UP
170#define MPEG_RW2 BUTTON_PREV
171#define MPEG_FF BUTTON_DOWN
172#define MPEG_FF2 BUTTON_NEXT
173#define MPEG_SHOW_OSD BUTTON_BACK
174
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
182
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
191
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
200
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
209
210
211#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
212CONFIG_KEYPAD == SANSA_CLIP_PAD || \
213CONFIG_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
221
222#elif CONFIG_KEYPAD == MROBE500_PAD
223#define MPEG_STOP BUTTON_POWER
224
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
232
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
241
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
250
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
256
257#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
258#define MPEG_MENU BUTTON_MENU
259#define MPEG_STOP BUTTON_BACK
260#define MPEG_PAUSE BUTTON_PLAY
261#define MPEG_VOLDOWN BUTTON_UP
262#define MPEG_VOLUP BUTTON_DOWN
263#define MPEG_RW BUTTON_LEFT
264#define MPEG_FF BUTTON_RIGHT
265
266#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
267#define MPEG_MENU BUTTON_MENU
268#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT)
269#define MPEG_PAUSE (BUTTON_PLAY|BUTTON_REL)
270#define MPEG_VOLDOWN BUTTON_VOL_DOWN
271#define MPEG_VOLUP BUTTON_VOL_UP
272#define MPEG_RW BUTTON_DOWN
273#define MPEG_FF BUTTON_UP
274
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
283
284#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
285#define MPEG_MENU BUTTON_MENU
286#define MPEG_STOP BUTTON_POWER
287#define MPEG_PAUSE BUTTON_PLAY
288#define MPEG_VOLDOWN BUTTON_VOL_DOWN
289#define MPEG_VOLUP BUTTON_VOL_UP
290#define MPEG_RW BUTTON_PREV
291#define MPEG_FF BUTTON_NEXT
292
293#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
294#define MPEG_MENU BUTTON_MENU
295#define MPEG_STOP BUTTON_POWER
296#define MPEG_PAUSE BUTTON_PLAY
297#define MPEG_VOLDOWN BUTTON_VOL_DOWN
298#define MPEG_VOLUP BUTTON_VOL_UP
299#define MPEG_RW BUTTON_UP
300#define MPEG_FF BUTTON_DOWN
301
302#elif CONFIG_KEYPAD == ONDAVX747_PAD
303#define MPEG_MENU (BUTTON_MENU|BUTTON_REL)
304//#define MPEG_STOP BUTTON_POWER
305#define MPEG_VOLDOWN BUTTON_VOL_DOWN
306#define MPEG_VOLUP BUTTON_VOL_UP
307
308#elif CONFIG_KEYPAD == ONDAVX777_PAD
309#define MPEG_MENU BUTTON_POWER
310
311#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
312 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
313#define MPEG_MENU BUTTON_REW
314#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
315#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
316#define MPEG_VOLDOWN BUTTON_DOWN
317#define MPEG_VOLUP BUTTON_UP
318#define MPEG_RW BUTTON_LEFT
319#define MPEG_FF BUTTON_RIGHT
320#define MPEG_SHOW_OSD BUTTON_FFWD
321
322#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
323#define MPEG_MENU BUTTON_MENU
324#define MPEG_STOP BUTTON_REC
325#define MPEG_PAUSE BUTTON_PLAY
326#define MPEG_VOLDOWN BUTTON_DOWN
327#define MPEG_VOLUP BUTTON_UP
328#define MPEG_RW BUTTON_PREV
329#define MPEG_FF BUTTON_NEXT
330
331#elif CONFIG_KEYPAD == MPIO_HD200_PAD
332#define MPEG_MENU BUTTON_FUNC
333#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
334#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
335#define MPEG_VOLDOWN BUTTON_VOL_DOWN
336#define MPEG_VOLUP BUTTON_VOL_UP
337#define MPEG_RW BUTTON_REW
338#define MPEG_FF BUTTON_FF
339
340#elif CONFIG_KEYPAD == MPIO_HD300_PAD
341#define MPEG_MENU BUTTON_MENU
342#define MPEG_PAUSE (BUTTON_PLAY | BUTTON_REL)
343#define MPEG_STOP (BUTTON_PLAY | BUTTON_REPEAT)
344#define MPEG_VOLDOWN BUTTON_DOWN
345#define MPEG_VOLUP BUTTON_UP
346#define MPEG_RW BUTTON_REW
347#define MPEG_FF BUTTON_FF
348
349#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
350#define MPEG_MENU BUTTON_POWER
351#define MPEG_PAUSE (BUTTON_PLAYPAUSE | BUTTON_REL)
352#define MPEG_STOP (BUTTON_PLAYPAUSE | BUTTON_REPEAT)
353#define MPEG_VOLDOWN BUTTON_VOL_DOWN
354#define MPEG_VOLUP BUTTON_VOL_UP
355#define MPEG_RW BUTTON_LEFT
356#define MPEG_FF BUTTON_RIGHT
357
358#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
359#define MPEG_MENU BUTTON_POWER
360#define MPEG_PAUSE (BUTTON_SELECT | BUTTON_REL)
361#define MPEG_STOP (BUTTON_SELECT | BUTTON_REPEAT)
362#define MPEG_VOLDOWN BUTTON_VOL_DOWN
363#define MPEG_VOLUP BUTTON_VOL_UP
364#define MPEG_RW BUTTON_LEFT
365#define MPEG_FF BUTTON_RIGHT
366
367#elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
368#define MPEG_MENU BUTTON_MENU
369#define MPEG_PAUSE BUTTON_SELECT
370#define MPEG_STOP BUTTON_POWER
371#define MPEG_VOLDOWN BUTTON_DOWN
372#define MPEG_VOLUP BUTTON_UP
373#define MPEG_RW BUTTON_LEFT
374#define MPEG_FF BUTTON_RIGHT
375
376#elif CONFIG_KEYPAD == HM60X_PAD
377#define MPEG_MENU BUTTON_POWER
378#define MPEG_PAUSE BUTTON_SELECT
379#define MPEG_STOP (BUTTON_SELECT | BUTTON_POWER)
380#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN)
381#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP)
382#define MPEG_RW BUTTON_LEFT
383#define MPEG_FF BUTTON_RIGHT
384
385#elif CONFIG_KEYPAD == HM801_PAD
386#define MPEG_MENU BUTTON_POWER
387#define MPEG_PAUSE BUTTON_PLAY
388#define MPEG_STOP (BUTTON_POWER | BUTTON_PLAY)
389#define MPEG_VOLDOWN (BUTTON_POWER | BUTTON_DOWN)
390#define MPEG_VOLUP (BUTTON_POWER | BUTTON_UP)
391#define MPEG_RW BUTTON_PREV
392#define MPEG_FF BUTTON_NEXT
393
394#elif CONFIG_KEYPAD == SONY_NWZ_PAD
395#define MPEG_MENU BUTTON_BACK
396#define MPEG_PAUSE BUTTON_PLAY
397#define MPEG_STOP BUTTON_POWER
398#define MPEG_VOLDOWN BUTTON_UP
399#define MPEG_VOLUP BUTTON_DOWN
400#define MPEG_RW BUTTON_LEFT
401#define MPEG_FF BUTTON_RIGHT
402
403#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
404#define MPEG_MENU BUTTON_MENU
405#define MPEG_PAUSE BUTTON_PLAYPAUSE
406#define MPEG_STOP BUTTON_BACK
407#define MPEG_VOLDOWN BUTTON_DOWN
408#define MPEG_VOLUP BUTTON_UP
409#define MPEG_RW BUTTON_LEFT
410#define MPEG_FF BUTTON_RIGHT
411
412#elif CONFIG_KEYPAD == DX50_PAD
413#define MPEG_MENU BUTTON_POWER
414#define MPEG_VOLDOWN BUTTON_VOL_DOWN
415#define MPEG_VOLUP BUTTON_VOL_UP
416#define MPEG_RW BUTTON_LEFT
417#define MPEG_FF BUTTON_RIGHT
418#define MPEG_PAUSE BUTTON_PLAY
419#define MPEG_STOP (BUTTON_PLAY|BUTTON_REPEAT)
420
421#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
422#define MPEG_MENU BUTTON_POWER
423#define MPEG_PAUSE BUTTON_MENU
424#define MPEG_STOP (BUTTON_MENU|BUTTON_REPEAT)
425
426#elif CONFIG_KEYPAD == AGPTEK_ROCKER_PAD
427#define MPEG_MENU BUTTON_POWER
428#define MPEG_PAUSE BUTTON_SELECT
429#define MPEG_STOP BUTTON_DOWN
430#define MPEG_VOLDOWN BUTTON_VOLDOWN
431#define MPEG_VOLUP BUTTON_VOLUP
432#define MPEG_RW BUTTON_LEFT
433#define MPEG_FF BUTTON_RIGHT
434
435#elif CONFIG_KEYPAD == XDUOO_X3_PAD
436#define MPEG_MENU BUTTON_PLAY
437#define MPEG_STOP BUTTON_POWER
438#define MPEG_PAUSE BUTTON_HOME
439#define MPEG_VOLDOWN BUTTON_VOL_DOWN
440#define MPEG_VOLUP BUTTON_VOL_UP
441#define MPEG_RW BUTTON_PREV
442#define MPEG_FF BUTTON_NEXT
443
444#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
445#define MPEG_MENU BUTTON_PLAY
446#define MPEG_STOP BUTTON_POWER
447#define MPEG_PAUSE BUTTON_HOME
448#define MPEG_VOLDOWN BUTTON_VOL_DOWN
449#define MPEG_VOLUP BUTTON_VOL_UP
450#define MPEG_RW BUTTON_PREV
451#define MPEG_FF BUTTON_NEXT
452
453#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
454#define MPEG_MENU BUTTON_PLAY
455#define MPEG_STOP BUTTON_POWER
456#define MPEG_PAUSE BUTTON_HOME
457#define MPEG_VOLDOWN BUTTON_VOL_DOWN
458#define MPEG_VOLUP BUTTON_VOL_UP
459#define MPEG_RW BUTTON_PREV
460#define MPEG_FF BUTTON_NEXT
461
462#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
463#define MPEG_MENU BUTTON_PLAY
464#define MPEG_STOP BUTTON_POWER
465#define MPEG_PAUSE BUTTON_HOME
466#define MPEG_VOLDOWN BUTTON_VOL_DOWN
467#define MPEG_VOLUP BUTTON_VOL_UP
468#define MPEG_RW BUTTON_PREV
469#define MPEG_FF BUTTON_NEXT
470
471#elif CONFIG_KEYPAD == EROSQ_PAD
472#define MPEG_MENU BUTTON_MENU
473#define MPEG_STOP BUTTON_POWER
474#define MPEG_PAUSE BUTTON_PLAY
475#define MPEG_VOLDOWN BUTTON_VOL_DOWN
476#define MPEG_VOLUP BUTTON_VOL_UP
477#define MPEG_RW BUTTON_PREV
478#define MPEG_FF BUTTON_NEXT
479
480#elif CONFIG_KEYPAD == FIIO_M3K_PAD
481#define MPEG_MENU BUTTON_MENU
482#define MPEG_STOP BUTTON_POWER
483#define MPEG_PAUSE BUTTON_PLAY
484#define MPEG_VOLDOWN BUTTON_VOL_DOWN
485#define MPEG_VOLUP BUTTON_VOL_UP
486#define MPEG_RW BUTTON_LEFT
487#define MPEG_FF BUTTON_RIGHT
488
489#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
490/* use touchscreen */
491
492#else
493#error No keymap defined!
494#endif
495
496#ifdef HAVE_TOUCHSCREEN
497#ifndef MPEG_MENU
498#define MPEG_MENU (BUTTON_TOPRIGHT|BUTTON_REL)
499#endif
500#ifndef MPEG_STOP
501#define MPEG_STOP BUTTON_TOPLEFT
502#endif
503#ifndef MPEG_PAUSE
504#define MPEG_PAUSE BUTTON_CENTER
505#endif
506#ifndef MPEG_VOLDOWN
507#define MPEG_VOLDOWN BUTTON_BOTTOMMIDDLE
508#endif
509#ifndef MPEG_VOLUP
510#define MPEG_VOLUP BUTTON_TOPMIDDLE
511#endif
512#ifndef MPEG_RW
513#define MPEG_RW BUTTON_MIDLEFT
514#endif
515#ifndef MPEG_FF
516#define MPEG_FF BUTTON_MIDRIGHT
517#endif
518#endif
519
520/* One thing we can do here for targets with remotes is having a display
521 * always on the remote instead of always forcing a popup on the main display */
522
523#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
524 /* 3% of 30min file == 54s step size */
525#define MIN_FF_REWIND_STEP (TS_SECOND/2)
526#define OSD_MIN_UPDATE_INTERVAL (HZ/2)
527#define FPS_UPDATE_INTERVAL (HZ) /* Get new FPS reading each second */
528
529enum video_action
530{
531 VIDEO_STOP = 0,
532 VIDEO_PREV,
533 VIDEO_NEXT,
534 VIDEO_ACTION_MANUAL = 0x8000, /* Flag that says user did it */
535};
536
537/* OSD status - same order as icon array */
538enum osd_status_enum
539{
540 OSD_STATUS_STOPPED = 0,
541 OSD_STATUS_PAUSED,
542 OSD_STATUS_PLAYING,
543 OSD_STATUS_FF,
544 OSD_STATUS_RW,
545 OSD_STATUS_COUNT,
546 OSD_STATUS_MASK = 0x7
547};
548
549enum osd_bits
550{
551 OSD_REFRESH_DEFAULT = 0x0000, /* Only refresh elements when due */
552 /* Refresh the... */
553 OSD_REFRESH_VOLUME = 0x0001, /* ...volume display */
554 OSD_REFRESH_TIME = 0x0002, /* ...time display+progress */
555 OSD_REFRESH_STATUS = 0x0004, /* ...playback status icon */
556 OSD_REFRESH_BACKGROUND = 0x0008, /* ...background (implies ALL) */
557 OSD_REFRESH_VIDEO = 0x0010, /* ...video image upon timeout */
558 OSD_REFRESH_RESUME = 0x0020, /* Resume playback upon timeout */
559 OSD_NODRAW = 0x8000, /* OR bitflag - don't draw anything */
560 OSD_SHOW = 0x4000, /* OR bitflag - show the OSD */
561#ifdef HAVE_HEADPHONE_DETECTION
562 OSD_HP_PAUSE = 0x2000, /* OR bitflag - headphones caused pause */
563#endif
564 OSD_HIDE = 0x0000, /* hide the OSD (aid readability) */
565 OSD_REFRESH_ALL = 0x000f, /* Only immediate graphical elements */
566};
567
568/* Status icons selected according to font height */
569extern const unsigned char mpegplayer_status_icons_8x8x1[];
570extern const unsigned char mpegplayer_status_icons_12x12x1[];
571extern const unsigned char mpegplayer_status_icons_16x16x1[];
572
573/* Main border areas that contain OSD elements */
574#define OSD_BDR_L 2
575#define OSD_BDR_T 2
576#define OSD_BDR_R 2
577#define OSD_BDR_B 2
578
579struct osd
580{
581 long hide_tick;
582 long show_for;
583 long print_tick;
584 long print_delay;
585 long resume_tick;
586 long resume_delay;
587 long next_auto_refresh;
588 int x;
589 int y;
590 int width;
591 int height;
592 unsigned fgcolor;
593 unsigned bgcolor;
594 unsigned prog_fillcolor;
595 struct vo_rect update_rect;
596 struct vo_rect prog_rect;
597 struct vo_rect time_rect;
598 struct vo_rect dur_rect;
599 struct vo_rect vol_rect;
600 const unsigned char *icons;
601 struct vo_rect stat_rect;
602 int status;
603 uint32_t curr_time;
604 unsigned auto_refresh;
605 unsigned flags;
606 int font;
607};
608
609struct fps
610{
611 /* FPS Display */
612 struct vo_rect rect; /* OSD coordinates */
613 int pf_x; /* Screen coordinates */
614 int pf_y;
615 int pf_width;
616 int pf_height;
617 long update_tick; /* When to next update FPS reading */
618 #define FPS_FORMAT "%d.%02d"
619 #define FPS_DIMSTR "999.99" /* For establishing rect size */
620 #define FPS_BUFSIZE sizeof("999.99")
621};
622
623static struct osd osd;
624static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */
625
626#ifdef LCD_PORTRAIT
627static fb_data* get_framebuffer(void)
628{
629 struct viewport *vp_main = *(rb->screens[SCREEN_MAIN]->current_viewport);
630 return vp_main->buffer->fb_ptr;
631}
632#endif
633
634static void osd_show(unsigned show);
635
636#ifdef LCD_LANDSCAPE
637 #define __X (x + osd.x)
638 #define __Y (y + osd.y)
639 #define __W width
640 #define __H height
641#else
642 #define __X (LCD_WIDTH - (y + osd.y) - height)
643 #define __Y (x + osd.x)
644 #define __W height
645 #define __H width
646#endif
647
648#ifdef HAVE_LCD_COLOR
649/* Blend two colors in 0-100% (0-255) mix of c2 into c1 */
650static unsigned draw_blendcolor(unsigned c1, unsigned c2, unsigned char amount)
651{
652 int r1 = RGB_UNPACK_RED(c1);
653 int g1 = RGB_UNPACK_GREEN(c1);
654 int b1 = RGB_UNPACK_BLUE(c1);
655
656 int r2 = RGB_UNPACK_RED(c2);
657 int g2 = RGB_UNPACK_GREEN(c2);
658 int b2 = RGB_UNPACK_BLUE(c2);
659
660 return LCD_RGBPACK(amount*(r2 - r1) / 255 + r1,
661 amount*(g2 - g1) / 255 + g1,
662 amount*(b2 - b1) / 255 + b1);
663}
664#endif
665
666#ifdef PLUGIN_USE_IRAM
667/* IRAM preserving mechanism to enable talking menus */
668static char *iram_saved_copy;
669extern char iramstart[], iramend[];
670
671static void iram_saving_init(void)
672{
673#ifndef SIMULATOR
674 size_t size;
675 iram_saved_copy = (char *)rb->plugin_get_buffer(&size);
676
677 if (size >= (size_t)(iramend-iramstart))
678 iram_saved_copy += size - (size_t)(iramend - iramstart);
679 else
680#endif
681 iram_saved_copy = NULL;
682
683 return;
684}
685
686void mpegplayer_iram_preserve(void)
687{
688 if (iram_saved_copy)
689 {
690 rb->memcpy(iram_saved_copy, iramstart, iramend-iramstart);
691#ifdef HAVE_CPUCACHE_INVALIDATE
692 /* make the icache (if it exists) up to date with the new code */
693 rb->cpucache_invalidate();
694#endif /* HAVE_CPUCACHE_INVALIDATE */
695 }
696 return;
697}
698
699void mpegplayer_iram_restore(void)
700{
701 if (iram_saved_copy)
702 {
703 rb->audio_hard_stop();
704 rb->memcpy(iramstart, iram_saved_copy, iramend-iramstart);
705#ifdef HAVE_CPUCACHE_INVALIDATE
706 /* make the icache (if it exists) up to date with the new code */
707 rb->cpucache_invalidate();
708#endif /* HAVE_CPUCACHE_INVALIDATE */
709 }
710 return;
711}
712#endif
713
714/* Drawing functions that operate rotated on LCD_PORTRAIT displays -
715 * most are just wrappers of lcd_* functions with transforms applied.
716 * The origin is the upper-left corner of the OSD area */
717static void draw_update_rect(int x, int y, int width, int height)
718{
719 mylcd_update_rect(__X, __Y, __W, __H);
720}
721
722static void draw_clear_area(int x, int y, int width, int height)
723{
724#ifdef HAVE_LCD_COLOR
725 rb->screen_clear_area(rb->screens[SCREEN_MAIN], __X, __Y, __W, __H);
726#else
727 int oldmode = grey_get_drawmode();
728 grey_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
729 grey_fillrect(__X, __Y, __W, __H);
730 grey_set_drawmode(oldmode);
731#endif
732}
733
734static void draw_clear_area_rect(const struct vo_rect *rc)
735{
736 draw_clear_area(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t);
737}
738
739static void draw_fillrect(int x, int y, int width, int height)
740{
741 mylcd_fillrect(__X, __Y, __W, __H);
742}
743
744static void draw_hline(int x1, int x2, int y)
745{
746#ifdef LCD_LANDSCAPE
747 mylcd_hline(x1 + osd.x, x2 + osd.x, y + osd.y);
748#else
749 y = LCD_WIDTH - (y + osd.y) - 1;
750 mylcd_vline(y, x1 + osd.x, x2 + osd.x);
751#endif
752}
753
754static void draw_vline(int x, int y1, int y2)
755{
756#ifdef LCD_LANDSCAPE
757 mylcd_vline(x + osd.x, y1 + osd.y, y2 + osd.y);
758#else
759 y1 = LCD_WIDTH - (y1 + osd.y) - 1;
760 y2 = LCD_WIDTH - (y2 + osd.y) - 1;
761 mylcd_hline(y1, y2, x + osd.x);
762#endif
763}
764
765static void draw_scrollbar_draw(int x, int y, int width, int height,
766 uint32_t min, uint32_t max, uint32_t val)
767{
768 unsigned oldfg = mylcd_get_foreground();
769
770 draw_hline(x + 1, x + width - 2, y);
771 draw_hline(x + 1, x + width - 2, y + height - 1);
772 draw_vline(x, y + 1, y + height - 2);
773 draw_vline(x + width - 1, y + 1, y + height - 2);
774
775 val = muldiv_uint32(width - 2, val, max - min);
776 val = MIN(val, (uint32_t)(width - 2));
777
778 draw_fillrect(x + 1, y + 1, val, height - 2);
779
780 mylcd_set_foreground(osd.prog_fillcolor);
781
782 draw_fillrect(x + 1 + val, y + 1, width - 2 - val, height - 2);
783
784 mylcd_set_foreground(oldfg);
785}
786
787static void draw_scrollbar_draw_rect(const struct vo_rect *rc, int min,
788 int max, int val)
789{
790 draw_scrollbar_draw(rc->l, rc->t, rc->r - rc->l, rc->b - rc->t,
791 min, max, val);
792}
793
794static void draw_setfont(int font)
795{
796 osd.font = font;
797 mylcd_setfont(font);
798}
799
800#ifdef LCD_PORTRAIT
801/* Portrait displays need rotated text rendering */
802
803/* Limited function that only renders in DRMODE_FG and uses absolute screen
804 * coordinates */
805static void draw_oriented_mono_bitmap_part(const unsigned char *src,
806 int src_x, int src_y,
807 int stride, int x, int y,
808 int width, int height)
809{
810 const unsigned char *src_end;
811 fb_data *dst, *dst_end;
812 unsigned fg_pattern;
813
814 if (x + width > SCREEN_WIDTH)
815 width = SCREEN_WIDTH - x; /* Clip right */
816 if (x < 0)
817 width += x, x = 0; /* Clip left */
818 if (width <= 0)
819 return; /* nothing left to do */
820
821 if (y + height > SCREEN_HEIGHT)
822 height = SCREEN_HEIGHT - y; /* Clip bottom */
823 if (y < 0)
824 height += y, y = 0; /* Clip top */
825 if (height <= 0)
826 return; /* nothing left to do */
827
828 fg_pattern = rb->lcd_get_foreground();
829 /*bg_pattern =*/ rb->lcd_get_background();
830
831 src += stride * (src_y >> 3) + src_x; /* move starting point */
832 src_y &= 7;
833 src_end = src + width;
834
835 dst = get_framebuffer() + (LCD_WIDTH - y) + x*LCD_WIDTH;
836 do
837 {
838 const unsigned char *src_col = src++;
839 unsigned data = *src_col >> src_y;
840 int numbits = 8 - src_y;
841
842 fb_data *dst_col = dst;
843 dst_end = dst_col - height;
844 dst += LCD_WIDTH;
845
846 do
847 {
848 dst_col--;
849
850 if (data & 1)
851 *dst_col = FB_SCALARPACK(fg_pattern);
852#if 0
853 else
854 *dst_col = bg_pattern;
855#endif
856 data >>= 1;
857 if (--numbits == 0) {
858 src_col += stride;
859 data = *src_col;
860 numbits = 8;
861 }
862 }
863 while (dst_col > dst_end);
864 }
865 while (src < src_end);
866}
867
868
869#define ALPHA_COLOR_FONT_DEPTH 2
870#define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
871#define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
872#define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
873#define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
874#ifdef CPU_ARM
875#define BLEND_INIT do {} while (0)
876#define BLEND_FINISH do {} while(0)
877#define BLEND_START(acc, color, alpha) \
878 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
879#define BLEND_CONT(acc, color, alpha) \
880 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
881#define BLEND_OUT(acc) do {} while (0)
882#elif defined(CPU_COLDFIRE)
883#define ALPHA_BITMAP_READ_WORDS
884#define BLEND_INIT \
885 unsigned long _macsr = coldfire_get_macsr(); \
886 coldfire_set_macsr(EMAC_UNSIGNED)
887#define BLEND_FINISH \
888 coldfire_set_macsr(_macsr)
889#define BLEND_START(acc, color, alpha) \
890 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
891#define BLEND_CONT BLEND_START
892#define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
893#else
894#define BLEND_INIT do {} while (0)
895#define BLEND_FINISH do {} while(0)
896#define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
897#define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
898#define BLEND_OUT(acc) do {} while (0)
899#endif
900
901/* Blend the given two colors */
902static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
903{
904#if LCD_DEPTH == 16
905 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
906#if (LCD_PIXELFORMAT == RGB565SWAPPED)
907 c1 = swap16(c1);
908 c2 = swap16(c2);
909#endif
910 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
911 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
912 unsigned p;
913 BLEND_START(p, c1l, a);
914 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
915 BLEND_OUT(p);
916 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
917 p |= (p >> 16);
918#if (LCD_PIXELFORMAT == RGB565SWAPPED)
919 return swap16(p);
920#else
921 return p;
922#endif
923
924#else /* LCD_DEPTH == 24 */
925 unsigned s = c1;
926 unsigned d = c2;
927 unsigned s1 = s & 0xff00ff;
928 unsigned d1 = d & 0xff00ff;
929 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
930 d1 = (d1 + ((s1 - d1) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00ff;
931 s &= 0xff00;
932 d &= 0xff00;
933 d = (d + ((s - d) * a >> ALPHA_COLOR_LOOKUP_SHIFT)) & 0xff00;
934
935 return d1 | d;
936#endif
937}
938
939static void draw_oriented_alpha_bitmap_part(const unsigned char *src,
940 int src_x, int src_y,
941 int stride, int x, int y,
942 int width, int height)
943{
944 fb_data *dst, *dst_start;
945 unsigned fg_pattern;
946
947 if (x + width > SCREEN_WIDTH)
948 width = SCREEN_WIDTH - x; /* Clip right */
949 if (x < 0)
950 width += x, x = 0; /* Clip left */
951 if (width <= 0)
952 return; /* nothing left to do */
953
954 if (y + height > SCREEN_HEIGHT)
955 height = SCREEN_HEIGHT - y; /* Clip bottom */
956 if (y < 0)
957 height += y, y = 0; /* Clip top */
958 if (height <= 0)
959 return; /* nothing left to do */
960
961 /* initialize blending */
962 BLEND_INIT;
963
964 fg_pattern = rb->lcd_get_foreground();
965 /*bg_pattern=*/ rb->lcd_get_background();
966
967 dst_start = get_framebuffer() + (LCD_WIDTH - y - 1) + x*LCD_WIDTH;
968 int col, row = height;
969 unsigned data, pixels;
970 unsigned skip_end = (stride - width);
971 unsigned skip_start = src_y * stride + src_x;
972
973#ifdef ALPHA_BITMAP_READ_WORDS
974 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
975 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
976 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
977 data = letoh32(*src_w++);
978#else
979 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
980 data = *src;
981#endif
982 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
983 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
984#ifdef ALPHA_BITMAP_READ_WORDS
985 pixels = 8 - pixels;
986#endif
987
988 do
989 {
990 col = width;
991 dst = dst_start--;
992#ifdef ALPHA_BITMAP_READ_WORDS
993#define UPDATE_SRC_ALPHA do { \
994 if (--pixels) \
995 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
996 else \
997 { \
998 data = letoh32(*src_w++); \
999 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
1000 } \
1001 } while (0)
1002#elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
1003#define UPDATE_SRC_ALPHA do { \
1004 if (pixels ^= 1) \
1005 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
1006 else \
1007 data = *(++src); \
1008 } while (0)
1009#else
1010#define UPDATE_SRC_ALPHA do { \
1011 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
1012 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
1013 else \
1014 data = *(++src); \
1015 } while (0)
1016#endif
1017 do
1018 {
1019 unsigned color = blend_two_colors(FB_UNPACK_SCALAR_LCD(*dst), fg_pattern,
1020 data & ALPHA_COLOR_LOOKUP_SIZE );
1021 *dst= FB_SCALARPACK(color);
1022 dst += LCD_WIDTH;
1023 UPDATE_SRC_ALPHA;
1024 }
1025 while (--col);
1026#ifdef ALPHA_BITMAP_READ_WORDS
1027 if (skip_end < pixels)
1028 {
1029 pixels -= skip_end;
1030 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
1031 } else {
1032 pixels = skip_end - pixels;
1033 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
1034 pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
1035 data = letoh32(*src_w++);
1036 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
1037 pixels = 8 - pixels;
1038 }
1039#else
1040 if (skip_end)
1041 {
1042 pixels += skip_end;
1043 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
1044 {
1045 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
1046 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
1047 data = *src;
1048 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
1049 } else
1050 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
1051 }
1052#endif
1053 } while (--row);
1054}
1055
1056static void draw_putsxy_oriented(int x, int y, const char *str)
1057{
1058 unsigned short ch;
1059 unsigned short *ucs;
1060 int ofs = MIN(x, 0);
1061 struct font* pf = rb->font_get(osd.font);
1062
1063 ucs = rb->bidi_l2v(str, 1);
1064
1065 x += osd.x;
1066 y += osd.y;
1067
1068 while ((ch = *ucs++) != 0 && x < SCREEN_WIDTH)
1069 {
1070 int width;
1071 const unsigned char *bits;
1072
1073 /* get proportional width and glyph bits */
1074 width = rb->font_get_width(pf, ch);
1075
1076 if (ofs > width) {
1077 ofs -= width;
1078 continue;
1079 }
1080
1081 bits = rb->font_get_bits(pf, ch);
1082
1083 if (pf->depth)
1084 draw_oriented_alpha_bitmap_part(bits, ofs, 0, width, x, y,
1085 width - ofs, pf->height);
1086 else
1087 draw_oriented_mono_bitmap_part(bits, ofs, 0, width, x, y,
1088 width - ofs, pf->height);
1089
1090 x += width - ofs;
1091 ofs = 0;
1092 }
1093}
1094#else
1095static void draw_oriented_mono_bitmap_part(const unsigned char *src,
1096 int src_x, int src_y,
1097 int stride, int x, int y,
1098 int width, int height)
1099{
1100 int mode = mylcd_get_drawmode();
1101 mylcd_set_drawmode(DRMODE_FG);
1102 mylcd_mono_bitmap_part(src, src_x, src_y, stride, x, y, width, height);
1103 mylcd_set_drawmode(mode);
1104}
1105
1106static void draw_putsxy_oriented(int x, int y, const char *str)
1107{
1108 int mode = mylcd_get_drawmode();
1109 mylcd_set_drawmode(DRMODE_FG);
1110 mylcd_putsxy(x + osd.x, y + osd.y, str);
1111 mylcd_set_drawmode(mode);
1112}
1113#endif /* LCD_PORTRAIT */
1114
1115/** FPS Display **/
1116
1117/* Post-frame callback (on video thread) - update the FPS rectangle from the
1118 * framebuffer */
1119static void fps_post_frame_callback(void)
1120{
1121 vo_lock();
1122 mylcd_update_rect(fps.pf_x, fps.pf_y,
1123 fps.pf_width, fps.pf_height);
1124 vo_unlock();
1125}
1126
1127/* Set up to have the callback only update the intersection of the video
1128 * rectangle and the FPS text rectangle - if they don't intersect, then
1129 * the callback is set to NULL */
1130static void fps_update_post_frame_callback(void)
1131{
1132 void (*cb)(void) = NULL;
1133
1134 if (settings.showfps) {
1135 struct vo_rect cliprect;
1136
1137 if (stream_vo_get_clip(&cliprect)) {
1138 /* Oriented screen coordinates -> OSD coordinates */
1139 vo_rect_offset(&cliprect, -osd.x, -osd.y);
1140
1141 if (vo_rect_intersect(&cliprect, &cliprect, &fps.rect)) {
1142 int x = cliprect.l;
1143 int y = cliprect.t;
1144 int width = cliprect.r - cliprect.l;
1145 int height = cliprect.b - cliprect.t;
1146
1147 /* OSD coordinates -> framebuffer coordinates */
1148 fps.pf_x = __X;
1149 fps.pf_y = __Y;
1150 fps.pf_width = __W;
1151 fps.pf_height = __H;
1152
1153 cb = fps_post_frame_callback;
1154 }
1155 }
1156 }
1157
1158 stream_set_callback(VIDEO_SET_POST_FRAME_CALLBACK, cb);
1159}
1160
1161/* Refresh the FPS display */
1162static void fps_refresh(void)
1163{
1164 char str[FPS_BUFSIZE];
1165 struct video_output_stats stats;
1166 int w, h, sw;
1167 long tick;
1168
1169 tick = *rb->current_tick;
1170
1171 if (TIME_BEFORE(tick, fps.update_tick))
1172 return;
1173
1174 fps.update_tick = tick + FPS_UPDATE_INTERVAL;
1175
1176 stream_video_stats(&stats);
1177
1178 rb->snprintf(str, FPS_BUFSIZE, FPS_FORMAT,
1179 stats.fps / 100, stats.fps % 100);
1180
1181 w = fps.rect.r - fps.rect.l;
1182 h = fps.rect.b - fps.rect.t;
1183
1184 draw_clear_area(fps.rect.l, fps.rect.t, w, h);
1185 mylcd_getstringsize(str, &sw, NULL);
1186 draw_putsxy_oriented(fps.rect.r - sw, fps.rect.t, str);
1187
1188 vo_lock();
1189 draw_update_rect(fps.rect.l, fps.rect.t, w, h);
1190 vo_unlock();
1191}
1192
1193/* Initialize the FPS display */
1194static void fps_init(void)
1195{
1196 fps.update_tick = *rb->current_tick;
1197 fps.rect.l = fps.rect.t = 0;
1198 mylcd_getstringsize(FPS_DIMSTR, &fps.rect.r, &fps.rect.b);
1199 vo_rect_offset(&fps.rect, -osd.x, -osd.y);
1200 fps_update_post_frame_callback();
1201}
1202
1203/** OSD **/
1204
1205#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1206/* So we can refresh the overlay */
1207static void osd_lcd_enable_hook(unsigned short id, void* param)
1208{
1209 (void)id;
1210 (void)param;
1211 rb->queue_post(rb->button_queue, LCD_ENABLE_EVENT_1, 0);
1212}
1213#endif
1214
1215static void osdbacklight_hw_on_video_mode(bool video_on)
1216{
1217 if (video_on) {
1218#ifdef HAVE_BACKLIGHT
1219 /* Turn off backlight timeout */
1220 backlight_ignore_timeout();
1221#endif
1222#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1223 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
1224#endif
1225 } else {
1226#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
1227 rb->add_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
1228#endif
1229#ifdef HAVE_BACKLIGHT
1230 /* Revert to user's backlight settings */
1231 backlight_use_settings();
1232#endif
1233 }
1234}
1235
1236#ifdef HAVE_BACKLIGHT_BRIGHTNESS
1237static void osd_backlight_brightness_video_mode(bool video_on)
1238{
1239 if (settings.backlight_brightness < 0)
1240 return;
1241
1242 mpeg_backlight_update_brightness(
1243 video_on ? settings.backlight_brightness : -1);
1244}
1245#else
1246#define osd_backlight_brightness_video_mode(video_on)
1247#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
1248
1249static void osd_text_init(void)
1250{
1251 struct hms hms;
1252 char buf[32];
1253 int phys;
1254 int spc_width;
1255
1256 draw_setfont(FONT_UI);
1257
1258 osd.x = 0;
1259 osd.width = SCREEN_WIDTH;
1260
1261 vo_rect_clear(&osd.time_rect);
1262 vo_rect_clear(&osd.stat_rect);
1263 vo_rect_clear(&osd.prog_rect);
1264 vo_rect_clear(&osd.vol_rect);
1265
1266 ts_to_hms(stream_get_duration(), &hms);
1267 hms_format(buf, sizeof (buf), &hms);
1268 mylcd_getstringsize(buf, &osd.time_rect.r, &osd.time_rect.b);
1269
1270 /* Choose well-sized bitmap images relative to font height */
1271 if (osd.time_rect.b < 12) {
1272 osd.icons = mpegplayer_status_icons_8x8x1;
1273 osd.stat_rect.r = osd.stat_rect.b = 8;
1274 } else if (osd.time_rect.b < 16) {
1275 osd.icons = mpegplayer_status_icons_12x12x1;
1276 osd.stat_rect.r = osd.stat_rect.b = 12;
1277 } else {
1278 osd.icons = mpegplayer_status_icons_16x16x1;
1279 osd.stat_rect.r = osd.stat_rect.b = 16;
1280 }
1281
1282 if (osd.stat_rect.b < osd.time_rect.b) {
1283 vo_rect_offset(&osd.stat_rect, 0,
1284 (osd.time_rect.b - osd.stat_rect.b) / 2 + OSD_BDR_T);
1285 vo_rect_offset(&osd.time_rect, OSD_BDR_L, OSD_BDR_T);
1286 } else {
1287 vo_rect_offset(&osd.time_rect, OSD_BDR_L,
1288 osd.stat_rect.b - osd.time_rect.b + OSD_BDR_T);
1289 vo_rect_offset(&osd.stat_rect, 0, OSD_BDR_T);
1290 }
1291
1292 osd.dur_rect = osd.time_rect;
1293
1294 phys = rb->sound_val2phys(SOUND_VOLUME, rb->sound_min(SOUND_VOLUME));
1295 rb->snprintf(buf, sizeof(buf), "%d%s", phys,
1296 rb->sound_unit(SOUND_VOLUME));
1297
1298 mylcd_getstringsize(" ", &spc_width, NULL);
1299 mylcd_getstringsize(buf, &osd.vol_rect.r, &osd.vol_rect.b);
1300
1301 osd.prog_rect.r = SCREEN_WIDTH - OSD_BDR_L - spc_width -
1302 osd.vol_rect.r - OSD_BDR_R;
1303 osd.prog_rect.b = 3*osd.stat_rect.b / 4;
1304 vo_rect_offset(&osd.prog_rect, osd.time_rect.l,
1305 osd.time_rect.b);
1306
1307 vo_rect_offset(&osd.stat_rect,
1308 (osd.prog_rect.r + osd.prog_rect.l - osd.stat_rect.r) / 2,
1309 0);
1310
1311 vo_rect_offset(&osd.dur_rect,
1312 osd.prog_rect.r - osd.dur_rect.r, 0);
1313
1314 vo_rect_offset(&osd.vol_rect, osd.prog_rect.r + spc_width,
1315 (osd.prog_rect.b + osd.prog_rect.t - osd.vol_rect.b) / 2);
1316
1317 osd.height = OSD_BDR_T + MAX(osd.prog_rect.b, osd.vol_rect.b) -
1318 MIN(osd.time_rect.t, osd.stat_rect.t) + OSD_BDR_B;
1319
1320#ifdef HAVE_LCD_COLOR
1321 osd.height = ALIGN_UP(osd.height, 2);
1322#endif
1323 osd.y = SCREEN_HEIGHT - osd.height;
1324
1325 draw_setfont(FONT_SYSFIXED);
1326}
1327
1328static void osd_init(void)
1329{
1330 osd.flags = 0;
1331 osd.show_for = HZ*4;
1332 osd.print_delay = 75*HZ/100;
1333 osd.resume_delay = HZ/2;
1334#ifdef HAVE_LCD_COLOR
1335 osd.bgcolor = LCD_RGBPACK(0x73, 0x75, 0xbd);
1336 osd.fgcolor = LCD_WHITE;
1337 osd.prog_fillcolor = LCD_BLACK;
1338#else
1339 osd.bgcolor = GREY_LIGHTGRAY;
1340 osd.fgcolor = GREY_BLACK;
1341 osd.prog_fillcolor = GREY_WHITE;
1342#endif
1343 osd.curr_time = 0;
1344 osd.status = OSD_STATUS_STOPPED;
1345 osd.auto_refresh = OSD_REFRESH_TIME;
1346 osd.next_auto_refresh = *rb->current_tick;
1347 osd_text_init();
1348 fps_init();
1349}
1350
1351#ifdef HAVE_HEADPHONE_DETECTION
1352static void osd_set_hp_pause_flag(bool set)
1353{
1354 if (set)
1355 osd.flags |= OSD_HP_PAUSE;
1356 else
1357 osd.flags &= ~OSD_HP_PAUSE;
1358}
1359#else
1360#define osd_set_hp_pause_flag(set)
1361#endif /* HAVE_HEADPHONE_DETECTION */
1362
1363static void osd_schedule_refresh(unsigned refresh)
1364{
1365 long tick = *rb->current_tick;
1366
1367 if (refresh & OSD_REFRESH_VIDEO)
1368 osd.print_tick = tick + osd.print_delay;
1369
1370 if (refresh & OSD_REFRESH_RESUME)
1371 osd.resume_tick = tick + osd.resume_delay;
1372
1373 osd.auto_refresh |= refresh;
1374}
1375
1376static void osd_cancel_refresh(unsigned refresh)
1377{
1378 osd.auto_refresh &= ~refresh;
1379}
1380
1381/* Refresh the background area */
1382static void osd_refresh_background(void)
1383{
1384 char buf[32];
1385 struct hms hms;
1386
1387 unsigned bg = mylcd_get_background();
1388 mylcd_set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID);
1389
1390#ifdef HAVE_LCD_COLOR
1391 /* Draw a "raised" area for our graphics */
1392 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 192));
1393 draw_hline(0, osd.width, 0);
1394
1395 mylcd_set_background(draw_blendcolor(bg, MYLCD_WHITE, 80));
1396 draw_hline(0, osd.width, 1);
1397
1398 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 48));
1399 draw_hline(0, osd.width, osd.height-2);
1400
1401 mylcd_set_background(draw_blendcolor(bg, MYLCD_BLACK, 128));
1402 draw_hline(0, osd.width, osd.height-1);
1403
1404 mylcd_set_background(bg);
1405 draw_clear_area(0, 2, osd.width, osd.height - 4);
1406#else
1407 /* Give contrast with the main background */
1408 mylcd_set_background(MYLCD_WHITE);
1409 draw_hline(0, osd.width, 0);
1410
1411 mylcd_set_background(MYLCD_DARKGRAY);
1412 draw_hline(0, osd.width, osd.height-1);
1413
1414 mylcd_set_background(bg);
1415 draw_clear_area(0, 1, osd.width, osd.height - 2);
1416#endif
1417
1418 vo_rect_set_ext(&osd.update_rect, 0, 0, osd.width, osd.height);
1419 mylcd_set_drawmode(DRMODE_SOLID);
1420
1421 if (stream_get_duration() != INVALID_TIMESTAMP) {
1422 /* Draw the movie duration */
1423 ts_to_hms(stream_get_duration(), &hms);
1424 hms_format(buf, sizeof (buf), &hms);
1425 draw_putsxy_oriented(osd.dur_rect.l, osd.dur_rect.t, buf);
1426 }
1427 /* else don't know the duration */
1428}
1429
1430/* Refresh the current time display + the progress bar */
1431static void osd_refresh_time(void)
1432{
1433 char buf[32];
1434 struct hms hms;
1435
1436 uint32_t duration = stream_get_duration();
1437
1438 draw_scrollbar_draw_rect(&osd.prog_rect, 0, duration,
1439 osd.curr_time);
1440
1441 ts_to_hms(osd.curr_time, &hms);
1442 hms_format(buf, sizeof (buf), &hms);
1443
1444 draw_clear_area_rect(&osd.time_rect);
1445 draw_putsxy_oriented(osd.time_rect.l, osd.time_rect.t, buf);
1446
1447 vo_rect_union(&osd.update_rect, &osd.update_rect,
1448 &osd.prog_rect);
1449 vo_rect_union(&osd.update_rect, &osd.update_rect,
1450 &osd.time_rect);
1451}
1452
1453/* Refresh the volume display area */
1454static void osd_refresh_volume(void)
1455{
1456 char buf[32];
1457 int width;
1458
1459 int volume = rb->global_settings->volume;
1460 rb->snprintf(buf, sizeof (buf), "%d%s",
1461 rb->sound_val2phys(SOUND_VOLUME, volume),
1462 rb->sound_unit(SOUND_VOLUME));
1463 mylcd_getstringsize(buf, &width, NULL);
1464
1465 /* Right-justified */
1466 draw_clear_area_rect(&osd.vol_rect);
1467 draw_putsxy_oriented(osd.vol_rect.r - width, osd.vol_rect.t, buf);
1468
1469 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.vol_rect);
1470}
1471
1472/* Refresh the status icon */
1473static void osd_refresh_status(void)
1474{
1475 int icon_size = osd.stat_rect.r - osd.stat_rect.l;
1476
1477 draw_clear_area_rect(&osd.stat_rect);
1478
1479#ifdef HAVE_LCD_COLOR
1480 /* Draw status icon with a drop shadow */
1481 unsigned oldfg = mylcd_get_foreground();
1482 int i = 1;
1483
1484 mylcd_set_foreground(draw_blendcolor(mylcd_get_background(),
1485 MYLCD_BLACK, 96));
1486
1487 while (1)
1488 {
1489 draw_oriented_mono_bitmap_part(osd.icons,
1490 icon_size*osd.status,
1491 0,
1492 icon_size*OSD_STATUS_COUNT,
1493 osd.stat_rect.l + osd.x + i,
1494 osd.stat_rect.t + osd.y + i,
1495 icon_size, icon_size);
1496
1497 if (--i < 0)
1498 break;
1499
1500 mylcd_set_foreground(oldfg);
1501 }
1502
1503 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1504#else
1505 draw_oriented_mono_bitmap_part(osd.icons,
1506 icon_size*osd.status,
1507 0,
1508 icon_size*OSD_STATUS_COUNT,
1509 osd.stat_rect.l + osd.x,
1510 osd.stat_rect.t + osd.y,
1511 icon_size, icon_size);
1512 vo_rect_union(&osd.update_rect, &osd.update_rect, &osd.stat_rect);
1513#endif
1514}
1515
1516/* Update the current status which determines which icon is displayed */
1517static bool osd_update_status(void)
1518{
1519 int status;
1520
1521 switch (stream_status())
1522 {
1523 default:
1524 status = OSD_STATUS_STOPPED;
1525 break;
1526 case STREAM_PAUSED:
1527 /* If paused with a pending resume, coerce it to OSD_STATUS_PLAYING */
1528 status = (osd.auto_refresh & OSD_REFRESH_RESUME) ?
1529 OSD_STATUS_PLAYING : OSD_STATUS_PAUSED;
1530 break;
1531 case STREAM_PLAYING:
1532 status = OSD_STATUS_PLAYING;
1533 break;
1534 }
1535
1536 if (status != osd.status) {
1537 /* A refresh is needed */
1538 osd.status = status;
1539 return true;
1540 }
1541
1542 return false;
1543}
1544
1545/* Update the current time that will be displayed */
1546static void osd_update_time(void)
1547{
1548 uint32_t start;
1549 osd.curr_time = stream_get_seek_time(&start);
1550 osd.curr_time -= start;
1551}
1552
1553/* Refresh various parts of the OSD - showing it if it is hidden */
1554static void osd_refresh(int hint)
1555{
1556 long tick;
1557 unsigned oldbg, oldfg;
1558
1559 tick = *rb->current_tick;
1560
1561 if (settings.showfps)
1562 fps_refresh();
1563
1564 if (hint == OSD_REFRESH_DEFAULT) {
1565 /* The default which forces no updates */
1566
1567 /* Make sure Rockbox doesn't turn off the player because of
1568 too little activity */
1569 if (osd.status == OSD_STATUS_PLAYING)
1570 rb->reset_poweroff_timer();
1571
1572 /* Redraw the current or possibly extract a new video frame */
1573 if ((osd.auto_refresh & OSD_REFRESH_VIDEO) &&
1574 TIME_AFTER(tick, osd.print_tick)) {
1575 osd.auto_refresh &= ~OSD_REFRESH_VIDEO;
1576 stream_draw_frame(false);
1577 }
1578
1579 /* Restart playback if the timout was reached */
1580 if ((osd.auto_refresh & OSD_REFRESH_RESUME) &&
1581 TIME_AFTER(tick, osd.resume_tick)) {
1582 osd.auto_refresh &= ~(OSD_REFRESH_RESUME | OSD_REFRESH_VIDEO);
1583 stream_resume();
1584 }
1585
1586 /* If not visible, return */
1587 if (!(osd.flags & OSD_SHOW))
1588 return;
1589
1590 /* Hide if the visibility duration was reached */
1591 if (TIME_AFTER(tick, osd.hide_tick)) {
1592 osd_show(OSD_HIDE);
1593 return;
1594 }
1595 } else {
1596 /* A forced update of some region */
1597
1598 /* Show if currently invisible */
1599 if (!(osd.flags & OSD_SHOW)) {
1600 /* Avoid call back into this function - it will be drawn */
1601 osd_show(OSD_SHOW | OSD_NODRAW);
1602 hint = OSD_REFRESH_ALL;
1603 }
1604
1605 /* Move back timeouts for frame print and hide */
1606 osd.print_tick = tick + osd.print_delay;
1607 osd.hide_tick = tick + osd.show_for;
1608 }
1609
1610 if (TIME_AFTER(tick, osd.next_auto_refresh)) {
1611 /* Refresh whatever graphical elements are due automatically */
1612 osd.next_auto_refresh = tick + OSD_MIN_UPDATE_INTERVAL;
1613
1614 if (osd.auto_refresh & OSD_REFRESH_STATUS) {
1615 if (osd_update_status())
1616 hint |= OSD_REFRESH_STATUS;
1617 }
1618
1619 if (osd.auto_refresh & OSD_REFRESH_TIME) {
1620 osd_update_time();
1621 hint |= OSD_REFRESH_TIME;
1622 }
1623 }
1624
1625 if (hint == 0)
1626 return; /* No drawing needed */
1627
1628 /* Set basic drawing params that are used. Elements that perform variations
1629 * will restore them. */
1630 oldfg = mylcd_get_foreground();
1631 oldbg = mylcd_get_background();
1632
1633 draw_setfont(FONT_UI);
1634 mylcd_set_foreground(osd.fgcolor);
1635 mylcd_set_background(osd.bgcolor);
1636
1637 vo_rect_clear(&osd.update_rect);
1638
1639 if (hint & OSD_REFRESH_BACKGROUND) {
1640 osd_refresh_background();
1641 hint |= OSD_REFRESH_ALL; /* Requires a redraw of everything */
1642 }
1643
1644 if (hint & OSD_REFRESH_TIME) {
1645 osd_refresh_time();
1646 }
1647
1648 if (hint & OSD_REFRESH_VOLUME) {
1649 osd_refresh_volume();
1650 }
1651
1652 if (hint & OSD_REFRESH_STATUS) {
1653 osd_refresh_status();
1654 }
1655
1656 /* Go back to defaults */
1657 draw_setfont(FONT_SYSFIXED);
1658 mylcd_set_foreground(oldfg);
1659 mylcd_set_background(oldbg);
1660
1661 /* Update the dirty rectangle */
1662 vo_lock();
1663
1664 draw_update_rect(osd.update_rect.l,
1665 osd.update_rect.t,
1666 osd.update_rect.r - osd.update_rect.l,
1667 osd.update_rect.b - osd.update_rect.t);
1668
1669 vo_unlock();
1670}
1671
1672/* Show/Hide the OSD */
1673static void osd_show(unsigned show)
1674{
1675 if (((show ^ osd.flags) & OSD_SHOW) == 0)
1676 {
1677 if (show & OSD_SHOW) {
1678 osd.hide_tick = *rb->current_tick + osd.show_for;
1679 }
1680 return;
1681 }
1682
1683 if (show & OSD_SHOW) {
1684 /* Clip away the part of video that is covered */
1685 struct vo_rect rc = { 0, 0, SCREEN_WIDTH, osd.y };
1686
1687 osd.flags |= OSD_SHOW;
1688
1689 if (osd.status != OSD_STATUS_PLAYING) {
1690 /* Not playing - set brightness to mpegplayer setting */
1691 osd_backlight_brightness_video_mode(true);
1692 }
1693
1694 stream_vo_set_clip(&rc);
1695
1696 if (!(show & OSD_NODRAW))
1697 osd_refresh(OSD_REFRESH_ALL);
1698 } else {
1699 /* Uncover clipped video area and redraw it */
1700 osd.flags &= ~OSD_SHOW;
1701
1702 draw_clear_area(0, 0, osd.width, osd.height);
1703
1704 if (!(show & OSD_NODRAW)) {
1705 vo_lock();
1706 draw_update_rect(0, 0, osd.width, osd.height);
1707 vo_unlock();
1708
1709 stream_vo_set_clip(NULL);
1710 stream_draw_frame(false);
1711 } else {
1712 stream_vo_set_clip(NULL);
1713 }
1714
1715 if (osd.status != OSD_STATUS_PLAYING) {
1716 /* Not playing - restore backlight brightness */
1717 osd_backlight_brightness_video_mode(false);
1718 }
1719 }
1720}
1721
1722/* Set the current status - update screen if specified */
1723static void osd_set_status(int status)
1724{
1725 bool draw = (status & OSD_NODRAW) == 0;
1726
1727 status &= OSD_STATUS_MASK;
1728
1729 if (osd.status != status) {
1730
1731 osd.status = status;
1732
1733 if (draw)
1734 osd_refresh(OSD_REFRESH_STATUS);
1735 }
1736}
1737
1738/* Get the current status value */
1739static int osd_get_status(void)
1740{
1741 return osd.status & OSD_STATUS_MASK;
1742}
1743
1744/* Handle Fast-forward/Rewind keys using WPS settings (and some nicked code ;)
1745 * Returns last button code
1746 */
1747static int osd_ff_rw(int btn, unsigned refresh, uint32_t *new_time)
1748{
1749 unsigned int step = TS_SECOND*rb->global_settings->ff_rewind_min_step;
1750 const long ff_rw_accel = (rb->global_settings->ff_rewind_accel + 3);
1751 uint32_t start;
1752 uint32_t time = stream_get_seek_time(&start);
1753 const uint32_t duration = stream_get_duration();
1754 unsigned int max_step = 0;
1755 uint32_t ff_rw_count = 0;
1756 unsigned status = osd.status;
1757 int new_btn;
1758
1759 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME |
1760 OSD_REFRESH_TIME);
1761
1762 time -= start; /* Absolute clock => stream-relative */
1763
1764 switch (btn)
1765 {
1766 case MPEG_FF:
1767#ifdef MPEG_FF2
1768 case MPEG_FF2:
1769#endif
1770#ifdef MPEG_RC_FF
1771 case MPEG_RC_FF:
1772#endif
1773 osd_set_status(OSD_STATUS_FF);
1774 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1775 break;
1776 case MPEG_RW:
1777#ifdef MPEG_RW2
1778 case MPEG_RW2:
1779#endif
1780#ifdef MPEG_RC_RW
1781 case MPEG_RC_RW:
1782#endif
1783 osd_set_status(OSD_STATUS_RW);
1784 new_btn = btn | BUTTON_REPEAT; /* simplify code below */
1785 break;
1786 default:
1787 new_btn = BUTTON_NONE; /* Fail tests below but still do proper exit */
1788 }
1789
1790 while (1)
1791 {
1792 stream_keep_disk_active();
1793
1794 if (new_btn == (btn | BUTTON_REPEAT)) {
1795 if (osd.status == OSD_STATUS_FF) {
1796 /* fast forwarding, calc max step relative to end */
1797 max_step = muldiv_uint32(duration - (time + ff_rw_count),
1798 FF_REWIND_MAX_PERCENT, 100);
1799 } else {
1800 /* rewinding, calc max step relative to start */
1801 max_step = muldiv_uint32(time - ff_rw_count,
1802 FF_REWIND_MAX_PERCENT, 100);
1803 }
1804
1805 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
1806
1807 if (step > max_step)
1808 step = max_step;
1809
1810 ff_rw_count += step;
1811
1812 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
1813 step += step >> ff_rw_accel;
1814
1815 if (osd.status == OSD_STATUS_FF) {
1816 if (duration - time <= ff_rw_count)
1817 ff_rw_count = duration - time;
1818
1819 osd.curr_time = time + ff_rw_count;
1820 } else {
1821 if (time <= ff_rw_count)
1822 ff_rw_count = time;
1823
1824 osd.curr_time = time - ff_rw_count;
1825 }
1826
1827 osd_refresh(OSD_REFRESH_TIME);
1828
1829 new_btn = mpeg_button_get(TIMEOUT_BLOCK);
1830 }
1831 else {
1832 if (new_btn == (btn | BUTTON_REL)) {
1833 if (osd.status == OSD_STATUS_FF)
1834 time += ff_rw_count;
1835 else if (osd.status == OSD_STATUS_RW)
1836 time -= ff_rw_count;
1837 }
1838
1839 *new_time = time;
1840
1841 osd_schedule_refresh(refresh);
1842 osd_set_status(status);
1843 osd_schedule_refresh(OSD_REFRESH_TIME);
1844
1845 return new_btn;
1846 }
1847 }
1848}
1849
1850/* Return adjusted STREAM_* status */
1851static int osd_stream_status(void)
1852{
1853 int status = stream_status();
1854
1855 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1856 if (status == STREAM_PAUSED) {
1857 if (osd.auto_refresh & OSD_REFRESH_RESUME)
1858 status = STREAM_PLAYING;
1859 }
1860
1861 return status;
1862}
1863
1864/* Change the current audio volume by a specified amount */
1865static void osd_set_volume(int delta)
1866{
1867 int vol = rb->global_settings->volume;
1868 int limit;
1869
1870 vol += delta;
1871
1872 if (delta < 0) {
1873 /* Volume down - clip to lower limit */
1874 limit = rb->sound_min(SOUND_VOLUME);
1875 if (vol < limit)
1876 vol = limit;
1877 } else {
1878 /* Volume up - clip to upper limit */
1879 limit = rb->sound_max(SOUND_VOLUME);
1880 if (vol > limit)
1881 vol = limit;
1882 }
1883
1884 /* Sync the global settings */
1885 if (vol != rb->global_settings->volume) {
1886 rb->sound_set(SOUND_VOLUME, vol);
1887 rb->global_settings->volume = vol;
1888 }
1889
1890 /* Update the volume display */
1891 osd_refresh(OSD_REFRESH_VOLUME);
1892}
1893
1894/* Begin playback at the specified time */
1895static int osd_play(uint32_t time)
1896{
1897 int retval;
1898
1899 osd_set_hp_pause_flag(false);
1900 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1901
1902 retval = stream_seek(time, SEEK_SET);
1903
1904 if (retval >= STREAM_OK) {
1905 osdbacklight_hw_on_video_mode(true);
1906 osd_backlight_brightness_video_mode(true);
1907 stream_show_vo(true);
1908
1909 retval = stream_play();
1910
1911 if (retval >= STREAM_OK)
1912 osd_set_status(OSD_STATUS_PLAYING | OSD_NODRAW);
1913 }
1914
1915 return retval;
1916}
1917
1918/* Halt playback - pause engine and return logical state */
1919static int osd_halt(void)
1920{
1921 int status = stream_pause();
1922
1923 /* Coerce to STREAM_PLAYING if paused with a pending resume */
1924 if (status == STREAM_PAUSED) {
1925 if (osd_get_status() == OSD_STATUS_PLAYING)
1926 status = STREAM_PLAYING;
1927 }
1928
1929 /* Cancel some auto refreshes - caller will restart them if desired */
1930 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1931
1932 /* No backlight fiddling here - callers does the right thing */
1933
1934 return status;
1935}
1936
1937/* Pause playback if playing */
1938static int osd_pause(void)
1939{
1940 unsigned refresh = osd.auto_refresh;
1941 int status = osd_halt();
1942
1943 osd_set_hp_pause_flag(false);
1944
1945 if (status == STREAM_PLAYING && (refresh & OSD_REFRESH_RESUME)) {
1946 /* Resume pending - change to a still video frame update */
1947 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1948 }
1949
1950 osd_set_status(OSD_STATUS_PAUSED);
1951
1952 osdbacklight_hw_on_video_mode(false);
1953 /* Leave brightness alone and restore it when OSD is hidden */
1954
1955 if (stream_can_seek() && rb->global_settings->pause_rewind) {
1956 stream_seek(-rb->global_settings->pause_rewind*TS_SECOND,
1957 SEEK_CUR);
1958 osd_schedule_refresh(OSD_REFRESH_VIDEO);
1959 /* Update time display now */
1960 osd_update_time();
1961 osd_refresh(OSD_REFRESH_TIME);
1962 }
1963
1964 return status;
1965}
1966
1967/* Resume playback if halted or paused */
1968static void osd_resume(void)
1969{
1970 /* Cancel video and resume auto refresh - the resyc when starting
1971 * playback will perform those tasks */
1972 osd_set_hp_pause_flag(false);
1973 osdbacklight_hw_on_video_mode(true);
1974 osd_backlight_brightness_video_mode(true);
1975 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1976 osd_set_status(OSD_STATUS_PLAYING);
1977 stream_resume();
1978}
1979
1980/* Stop playback - remember the resume point if not closed */
1981static void osd_stop(void)
1982{
1983 uint32_t resume_time;
1984
1985 osd_set_hp_pause_flag(false);
1986 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
1987 osd_set_status(OSD_STATUS_STOPPED | OSD_NODRAW);
1988 osd_show(OSD_HIDE);
1989
1990 stream_stop();
1991
1992 resume_time = stream_get_resume_time();
1993
1994 if (resume_time != INVALID_TIMESTAMP)
1995 settings.resume_time = resume_time;
1996
1997 osdbacklight_hw_on_video_mode(false);
1998 osd_backlight_brightness_video_mode(false);
1999}
2000
2001/* Perform a seek by button if seeking is possible for this stream.
2002 *
2003 * A delay will be inserted before restarting in case the user decides to
2004 * seek again soon after.
2005 *
2006 * Returns last button code
2007 */
2008static int osd_seek_btn(int btn)
2009{
2010 int status;
2011 unsigned refresh = 0;
2012 uint32_t time;
2013
2014 if (!stream_can_seek())
2015 return true;
2016
2017 /* Halt playback - not strictly necessary but nice when doing
2018 * buttons */
2019 status = osd_halt();
2020
2021 if (status == STREAM_STOPPED)
2022 return true;
2023
2024 osd_show(OSD_SHOW);
2025
2026 /* Obtain a new playback point according to the buttons */
2027 if (status == STREAM_PLAYING)
2028 refresh = OSD_REFRESH_RESUME; /* delay resume if playing */
2029 else
2030 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
2031
2032 btn = osd_ff_rw(btn, refresh, &time);
2033
2034 /* Tell engine to resume at that time */
2035 stream_seek(time, SEEK_SET);
2036
2037 return btn;
2038}
2039
2040/* Perform a seek by time if seeking is possible for this stream
2041 *
2042 * If playing, the seeking is immediate, otherise a delay is added to showing
2043 * a still if paused in case the user does another seek soon after.
2044 *
2045 * If seeking isn't possible, a time of zero performs a skip to the
2046 * beginning.
2047 */
2048static void osd_seek_time(uint32_t time)
2049{
2050 int status;
2051 unsigned refresh = 0;
2052
2053 if (!stream_can_seek() && time != 0)
2054 return;
2055
2056 stream_wait_status();
2057 status = osd_stream_status();
2058
2059 if (status == STREAM_STOPPED)
2060 return;
2061
2062 if (status == STREAM_PLAYING) /* merely preserve resume */
2063 refresh = osd.auto_refresh & OSD_REFRESH_RESUME;
2064 else
2065 refresh = OSD_REFRESH_VIDEO; /* refresh if paused */
2066
2067 /* Cancel print or resume if pending */
2068 osd_cancel_refresh(OSD_REFRESH_VIDEO | OSD_REFRESH_RESUME);
2069
2070 /* Tell engine to seek to the given time - no state change */
2071 stream_seek(time, SEEK_SET);
2072
2073 osd_update_time();
2074 osd_refresh(OSD_REFRESH_TIME);
2075 osd_schedule_refresh(refresh);
2076}
2077
2078/* Has this file one of the supported extensions? */
2079static bool is_videofile(const char* file)
2080{
2081 static const char * const extensions[] =
2082 {
2083 /* Should match apps/plugins/viewers.config */
2084 "mpg", "mpeg", "mpv", "m2v"
2085 };
2086
2087 const char* ext = rb->strrchr(file, '.');
2088 int i;
2089
2090 if (!ext)
2091 return false;
2092
2093 for (i = ARRAYLEN(extensions) - 1; i >= 0; i--)
2094 {
2095 if (!rb->strcasecmp(ext + 1, extensions[i]))
2096 break;
2097 }
2098
2099 return i >= 0;
2100}
2101
2102/* deliver the next/previous video file in the current directory.
2103 returns false if there is none. */
2104static bool get_videofile(int direction, char* videofile, size_t bufsize)
2105{
2106 struct tree_context *tree = rb->tree_get_context();
2107 struct entry *dircache = rb->tree_get_entries(tree);
2108 int i, step, end, found = 0;
2109 char *videoname = rb->strrchr(videofile, '/') + 1;
2110 size_t rest = bufsize - (videoname - videofile) - 1;
2111
2112 if (direction == VIDEO_NEXT) {
2113 i = 0;
2114 step = 1;
2115 end = tree->filesindir;
2116 } else {
2117 i = tree->filesindir-1;
2118 step = -1;
2119 end = -1;
2120 }
2121 for (; i != end; i += step)
2122 {
2123 const char* name = dircache[i].name;
2124 if (!rb->strcmp(name, videoname)) {
2125 found = 1;
2126 continue;
2127 }
2128 if (found && rb->strlen(name) <= rest &&
2129 !(dircache[i].attr & ATTR_DIRECTORY) && is_videofile(name))
2130 {
2131 rb->strcpy(videoname, name);
2132 return true;
2133 }
2134 }
2135
2136 return false;
2137}
2138
2139#ifdef HAVE_HEADPHONE_DETECTION
2140/* Handle SYS_PHONE_PLUGGED/UNPLUGGED */
2141static void osd_handle_phone_plug(bool inserted)
2142{
2143 if (rb->global_settings->unplug_mode == 0)
2144 return;
2145
2146 /* Wait for any incomplete state transition to complete first */
2147 stream_wait_status();
2148
2149 int status = osd_stream_status();
2150
2151 if (inserted) {
2152 if (rb->global_settings->unplug_mode > 1) {
2153 if (status == STREAM_PAUSED &&
2154 (osd.flags & OSD_HP_PAUSE)) {
2155 osd_resume();
2156 }
2157 }
2158 } else {
2159 if (status == STREAM_PLAYING) {
2160 osd_pause();
2161
2162 osd_set_hp_pause_flag(true);
2163 }
2164 }
2165}
2166#endif
2167
2168static int button_loop(void)
2169{
2170 int next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
2171
2172 rb->lcd_setfont(FONT_SYSFIXED);
2173#ifdef HAVE_LCD_COLOR
2174 rb->lcd_set_foreground(LCD_WHITE);
2175 rb->lcd_set_background(LCD_BLACK);
2176#endif
2177 rb->lcd_clear_display();
2178 rb->lcd_update();
2179
2180#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2181 rb->lcd_set_mode(LCD_MODE_YUV);
2182#endif
2183
2184 osd_init();
2185
2186 /* Start playback at the specified starting time */
2187 if (osd_play(settings.resume_time) < STREAM_OK) {
2188 rb->splash(HZ*2, "Playback failed");
2189 return VIDEO_STOP;
2190 }
2191
2192 /* Gently poll the video player for EOS and handle UI */
2193 while (stream_status() != STREAM_STOPPED)
2194 {
2195 int button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL/2);
2196
2197 switch (button)
2198 {
2199 case BUTTON_NONE:
2200 {
2201 osd_refresh(OSD_REFRESH_DEFAULT);
2202 continue;
2203 } /* BUTTON_NONE: */
2204
2205#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
2206 case LCD_ENABLE_EVENT_1:
2207 {
2208 /* Draw the current frame if prepared already */
2209 stream_draw_frame(true);
2210 break;
2211 } /* LCD_ENABLE_EVENT_1: */
2212#endif
2213
2214 case MPEG_VOLUP:
2215 case MPEG_VOLUP|BUTTON_REPEAT:
2216#ifdef MPEG_VOLUP2
2217 case MPEG_VOLUP2:
2218 case MPEG_VOLUP2|BUTTON_REPEAT:
2219#endif
2220#ifdef MPEG_RC_VOLUP
2221 case MPEG_RC_VOLUP:
2222 case MPEG_RC_VOLUP|BUTTON_REPEAT:
2223#endif
2224 {
2225 osd_set_volume(+1);
2226 break;
2227 } /* MPEG_VOLUP*: */
2228
2229 case MPEG_VOLDOWN:
2230 case MPEG_VOLDOWN|BUTTON_REPEAT:
2231#ifdef MPEG_VOLDOWN2
2232 case MPEG_VOLDOWN2:
2233 case MPEG_VOLDOWN2|BUTTON_REPEAT:
2234#endif
2235#ifdef MPEG_RC_VOLDOWN
2236 case MPEG_RC_VOLDOWN:
2237 case MPEG_RC_VOLDOWN|BUTTON_REPEAT:
2238#endif
2239 {
2240 osd_set_volume(-1);
2241 break;
2242 } /* MPEG_VOLDOWN*: */
2243
2244 case MPEG_MENU:
2245#ifdef MPEG_RC_MENU
2246 case MPEG_RC_MENU:
2247#endif
2248 {
2249 int state = osd_halt(); /* save previous state */
2250 int result;
2251
2252 /* Hide video output */
2253 osd_show(OSD_HIDE | OSD_NODRAW);
2254 stream_show_vo(false);
2255 osd_backlight_brightness_video_mode(false);
2256
2257#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2258 rb->lcd_set_mode(LCD_MODE_RGB565);
2259#endif
2260
2261 result = mpeg_menu();
2262
2263 next_action = (settings.play_mode == 0) ? VIDEO_STOP : VIDEO_NEXT;
2264
2265 fps_update_post_frame_callback();
2266
2267 /* The menu can change the font, so restore */
2268 rb->lcd_setfont(FONT_SYSFIXED);
2269#ifdef HAVE_LCD_COLOR
2270 rb->lcd_set_foreground(LCD_WHITE);
2271 rb->lcd_set_background(LCD_BLACK);
2272#endif
2273 rb->lcd_clear_display();
2274 rb->lcd_update();
2275
2276 switch (result)
2277 {
2278 case MPEG_MENU_QUIT:
2279 next_action = VIDEO_STOP;
2280 osd_stop();
2281 break;
2282
2283 default:
2284#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2285 rb->lcd_set_mode(LCD_MODE_YUV);
2286#endif
2287 /* If not stopped, show video again */
2288 if (state != STREAM_STOPPED) {
2289 osd_show(OSD_SHOW);
2290 stream_show_vo(true);
2291 }
2292
2293 /* If stream was playing, restart it */
2294 if (state == STREAM_PLAYING) {
2295 osd_resume();
2296 }
2297 break;
2298 }
2299 break;
2300 } /* MPEG_MENU: */
2301
2302#ifdef MPEG_SHOW_OSD
2303 case MPEG_SHOW_OSD:
2304 case MPEG_SHOW_OSD | BUTTON_REPEAT:
2305 /* Show if not visible */
2306 osd_show(OSD_SHOW);
2307 /* Make sure it refreshes */
2308 osd_refresh(OSD_REFRESH_DEFAULT);
2309 break;
2310#endif
2311
2312 case MPEG_STOP:
2313#ifdef MPEG_RC_STOP
2314 case MPEG_RC_STOP:
2315#endif
2316 case ACTION_STD_CANCEL:
2317 {
2318 cancel_playback:
2319 next_action = VIDEO_STOP;
2320 osd_stop();
2321 break;
2322 } /* MPEG_STOP: */
2323
2324 case MPEG_PAUSE:
2325#ifdef MPEG_PAUSE2
2326 case MPEG_PAUSE2:
2327#endif
2328#ifdef MPEG_RC_PAUSE
2329 case MPEG_RC_PAUSE:
2330#endif
2331 {
2332 int status = osd_stream_status();
2333
2334 if (status == STREAM_PLAYING) {
2335 /* Playing => Paused */
2336 osd_pause();
2337 }
2338 else if (status == STREAM_PAUSED) {
2339 /* Paused => Playing */
2340 osd_resume();
2341 }
2342
2343 break;
2344 } /* MPEG_PAUSE*: */
2345
2346 case MPEG_RW:
2347#ifdef MPEG_RW2
2348 case MPEG_RW2:
2349#endif
2350#ifdef MPEG_RC_RW
2351 case MPEG_RC_RW:
2352#endif
2353 {
2354 int old_button = button;
2355
2356 /* If button has been released: skip to next/previous file */
2357 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
2358
2359 if ((old_button | BUTTON_REL) == button) {
2360 /* Check current playback position */
2361 osd_update_time();
2362
2363 if (settings.play_mode == 0 || osd.curr_time >= 3*TS_SECOND) {
2364 /* Start the current video from the beginning */
2365 osd_seek_time(0*TS_SECOND);
2366 }
2367 else {
2368 /* Release within 3 seconds of start: skip to previous
2369 * file */
2370 osd_stop();
2371 next_action = VIDEO_PREV | VIDEO_ACTION_MANUAL;
2372 }
2373 }
2374 else if ((button & ~BUTTON_REPEAT) == old_button) {
2375 button = osd_seek_btn(old_button);
2376 }
2377
2378 if (button == ACTION_STD_CANCEL)
2379 goto cancel_playback; /* jump to stop handling above */
2380
2381 rb->default_event_handler(button);
2382 break;
2383 } /* MPEG_RW: */
2384
2385 case MPEG_FF:
2386#ifdef MPEG_FF2
2387 case MPEG_FF2:
2388#endif
2389#ifdef MPEG_RC_FF
2390 case MPEG_RC_FF:
2391#endif
2392 {
2393 int old_button = button;
2394
2395 if (settings.play_mode != 0)
2396 button = mpeg_button_get(OSD_MIN_UPDATE_INTERVAL);
2397
2398 if ((old_button | BUTTON_REL) == button) {
2399 /* If button has been released: skip to next file */
2400 osd_stop();
2401 next_action = VIDEO_NEXT | VIDEO_ACTION_MANUAL;
2402 }
2403 else if ((button & ~BUTTON_REPEAT) == old_button) {
2404 button = osd_seek_btn(old_button);
2405 }
2406
2407 if (button == ACTION_STD_CANCEL)
2408 goto cancel_playback; /* jump to stop handling above */
2409
2410 rb->default_event_handler(button);
2411 break;
2412 } /* MPEG_FF: */
2413
2414#ifdef HAVE_HEADPHONE_DETECTION
2415 case SYS_PHONE_PLUGGED:
2416 case SYS_PHONE_UNPLUGGED:
2417 {
2418 osd_handle_phone_plug(button == SYS_PHONE_PLUGGED);
2419 break;
2420 } /* SYS_PHONE_*: */
2421#endif
2422
2423 default:
2424 {
2425 osd_refresh(OSD_REFRESH_DEFAULT);
2426 rb->default_event_handler(button);
2427 break;
2428 } /* default: */
2429 }
2430
2431 rb->yield();
2432 } /* end while */
2433
2434 osd_stop();
2435
2436#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
2437 /* Be sure hook is removed before exiting since the stop will put it
2438 * back because of the backlight restore. */
2439 rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
2440#endif
2441
2442 rb->lcd_setfont(FONT_UI);
2443
2444 return next_action;
2445}
2446
2447enum plugin_status plugin_start(const void* parameter)
2448{
2449 static char videofile[MAX_PATH];
2450 int status = PLUGIN_OK; /* assume success */
2451 bool quit = false;
2452
2453#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR)
2454 bool preserved_talk_state;
2455#endif
2456
2457 if (parameter == NULL) {
2458 /* No file = GTFO */
2459 rb->splash(HZ*2, "No File");
2460 return PLUGIN_ERROR;
2461 }
2462
2463 /* Disable all talking before initializing IRAM */
2464 rb->talk_disable(true);
2465
2466#ifdef PLUGIN_USE_IRAM
2467 iram_saving_init();
2468
2469#ifndef SIMULATOR
2470 preserved_talk_state = rb->global_settings->talk_menu;
2471 if (!iram_saved_copy)
2472 rb->global_settings->talk_menu = false;
2473#endif
2474#endif
2475
2476#ifdef HAVE_LCD_COLOR
2477 rb->lcd_set_backdrop(NULL);
2478 rb->lcd_set_foreground(LCD_WHITE);
2479 rb->lcd_set_background(LCD_BLACK);
2480#endif
2481
2482 rb->lcd_clear_display();
2483 rb->lcd_update();
2484
2485 rb->strcpy(videofile, (const char*) parameter);
2486
2487 if (stream_init() < STREAM_OK) {
2488 /* Fatal because this should not fail */
2489 DEBUGF("Could not initialize streams\n");
2490 status = PLUGIN_ERROR;
2491 } else {
2492 int next_action = VIDEO_STOP;
2493 bool get_videofile_says = true;
2494
2495 while (!quit)
2496 {
2497 init_settings(videofile);
2498
2499 int result = stream_open(videofile);
2500 bool manual_skip = false;
2501
2502 if (result >= STREAM_OK) {
2503 /* start menu */
2504 rb->lcd_clear_display();
2505 rb->lcd_update();
2506 result = mpeg_start_menu(stream_get_duration());
2507
2508 next_action = VIDEO_STOP;
2509 if (result != MPEG_START_QUIT) {
2510 /* Enter button loop and process UI */
2511 next_action = button_loop();
2512 manual_skip = next_action & VIDEO_ACTION_MANUAL;
2513 next_action &= ~VIDEO_ACTION_MANUAL;
2514 }
2515
2516 stream_close();
2517
2518 rb->lcd_clear_display();
2519 rb->lcd_update();
2520
2521 save_settings();
2522 } else {
2523 /* Problem with file; display message about it - not
2524 * considered a plugin error */
2525 long tick;
2526 const char *errstring;
2527
2528 DEBUGF("Could not open %s\n", videofile);
2529 switch (result)
2530 {
2531 case STREAM_UNSUPPORTED:
2532 errstring = "Unsupported format";
2533 break;
2534 default:
2535 errstring = "Error opening file: %d";
2536 }
2537
2538 tick = *rb->current_tick + HZ*2;
2539
2540 rb->splashf(0, errstring, result);
2541
2542 /* Be sure it doesn't get stuck in an unbreakable loop of bad
2543 * files, just in case! Otherwise, keep searching in the
2544 * chosen direction until a good one is found. */
2545 while (!quit && TIME_BEFORE(*rb->current_tick, tick))
2546 {
2547 int button = mpeg_button_get(HZ*2);
2548
2549 switch (button)
2550 {
2551 case MPEG_STOP:
2552 case ACTION_STD_CANCEL:
2553 /* Abort the search and exit */
2554 next_action = VIDEO_STOP;
2555 quit = true;
2556 break;
2557
2558 case BUTTON_NONE:
2559 if (settings.play_mode != 0) {
2560 if (next_action == VIDEO_STOP) {
2561 /* Default to next file */
2562 next_action = VIDEO_NEXT;
2563 }
2564 else if (next_action == VIDEO_PREV &&
2565 !get_videofile_says) {
2566 /* Was first file already; avoid endlessly
2567 * retrying it */
2568 next_action = VIDEO_STOP;
2569 }
2570 }
2571 break;
2572
2573 default:
2574 rb->default_event_handler(button);
2575 } /* switch */
2576 } /* while */
2577 }
2578
2579 /* return value of button_loop says, what's next */
2580 switch (next_action)
2581 {
2582 case VIDEO_NEXT:
2583 {
2584 get_videofile_says = get_videofile(VIDEO_NEXT, videofile,
2585 sizeof(videofile));
2586 /* quit after finished the last videofile */
2587 quit = !get_videofile_says;
2588
2589 if (manual_skip)
2590 {
2591 rb->system_sound_play(get_videofile_says ?
2592 SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE);
2593 }
2594
2595 break;
2596 }
2597 case VIDEO_PREV:
2598 {
2599 get_videofile_says = get_videofile(VIDEO_PREV, videofile,
2600 sizeof(videofile));
2601 /* if there is no previous file, play the same videofile */
2602
2603 if (manual_skip)
2604 {
2605 rb->system_sound_play(get_videofile_says ?
2606 SOUND_TRACK_SKIP : SOUND_TRACK_NO_MORE);
2607 }
2608
2609 break;
2610 }
2611 case VIDEO_STOP:
2612 {
2613 quit = true;
2614 break;
2615 }
2616 }
2617 } /* while */
2618 }
2619
2620#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_YUV)
2621 rb->lcd_set_mode(LCD_MODE_RGB565);
2622#endif
2623
2624 stream_exit();
2625
2626#if defined(PLUGIN_USE_IRAM) && !defined(SIMULATOR)
2627 if (!iram_saved_copy)
2628 rb->global_settings->talk_menu = preserved_talk_state;
2629#endif
2630
2631 rb->talk_disable(false);
2632
2633 /* Actually handle delayed processing of system events of interest
2634 * that were captured in other button loops */
2635 mpeg_sysevent_handle();
2636
2637 return status;
2638}