diff options
Diffstat (limited to 'apps/plugins/mpegplayer/mpegplayer.c')
-rw-r--r-- | apps/plugins/mpegplayer/mpegplayer.c | 2638 |
1 files changed, 2638 insertions, 0 deletions
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c new file mode 100644 index 0000000000..e66b4df146 --- /dev/null +++ b/apps/plugins/mpegplayer/mpegplayer.c | |||
@@ -0,0 +1,2638 @@ | |||
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 || \ | ||
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 | ||
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 | |||
529 | enum 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 */ | ||
538 | enum 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 | |||
549 | enum 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 */ | ||
569 | extern const unsigned char mpegplayer_status_icons_8x8x1[]; | ||
570 | extern const unsigned char mpegplayer_status_icons_12x12x1[]; | ||
571 | extern 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 | |||
579 | struct 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 | |||
609 | struct 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 | |||
623 | static struct osd osd; | ||
624 | static struct fps fps NOCACHEBSS_ATTR; /* Accessed on other processor */ | ||
625 | |||
626 | #ifdef LCD_PORTRAIT | ||
627 | static 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 | |||
634 | static 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 */ | ||
650 | static 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 */ | ||
668 | static char *iram_saved_copy; | ||
669 | extern char iramstart[], iramend[]; | ||
670 | |||
671 | static 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 | |||
686 | void 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 | |||
699 | void 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 */ | ||
717 | static void draw_update_rect(int x, int y, int width, int height) | ||
718 | { | ||
719 | mylcd_update_rect(__X, __Y, __W, __H); | ||
720 | } | ||
721 | |||
722 | static 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 | |||
734 | static 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 | |||
739 | static void draw_fillrect(int x, int y, int width, int height) | ||
740 | { | ||
741 | mylcd_fillrect(__X, __Y, __W, __H); | ||
742 | } | ||
743 | |||
744 | static 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 | |||
754 | static 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 | |||
765 | static 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 | |||
787 | static 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 | |||
794 | static 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 */ | ||
805 | static 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 */ | ||
902 | static 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 | |||
939 | static 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 | |||
1056 | static 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 | ||
1095 | static 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 | |||
1106 | static 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 */ | ||
1119 | static 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 */ | ||
1130 | static 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 */ | ||
1162 | static 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 */ | ||
1194 | static 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 */ | ||
1207 | static 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 | |||
1215 | static 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 | ||
1237 | static 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 | |||
1249 | static 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 | |||
1328 | static 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 | ||
1352 | static 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 | |||
1363 | static 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 | |||
1376 | static void osd_cancel_refresh(unsigned refresh) | ||
1377 | { | ||
1378 | osd.auto_refresh &= ~refresh; | ||
1379 | } | ||
1380 | |||
1381 | /* Refresh the background area */ | ||
1382 | static 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 */ | ||
1431 | static 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 */ | ||
1454 | static 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 */ | ||
1473 | static 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 */ | ||
1517 | static 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 */ | ||
1546 | static 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 */ | ||
1554 | static 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 */ | ||
1673 | static 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 */ | ||
1723 | static 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 */ | ||
1739 | static 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 | */ | ||
1747 | static 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 */ | ||
1851 | static 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 */ | ||
1865 | static 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 */ | ||
1895 | static 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 */ | ||
1919 | static 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 */ | ||
1938 | static 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 */ | ||
1968 | static 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 */ | ||
1981 | static 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 | */ | ||
2008 | static 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 | */ | ||
2048 | static 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? */ | ||
2079 | static 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. */ | ||
2104 | static 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 */ | ||
2141 | static 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 | |||
2168 | static 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 | |||
2447 | enum 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 | } | ||