diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-12-29 19:46:35 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-12-29 19:46:35 +0000 |
commit | a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1 (patch) | |
tree | d393a23d83549f99772bb156e59ffb88725148b6 /apps/plugins/mpegplayer/mpegplayer.c | |
parent | 1d0f6b90ff43776e55b4b9f062c9bea3f99aa768 (diff) | |
download | rockbox-a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1.tar.gz rockbox-a222f27c4a17ed8f9809cda7861fe5f23d4cc0c1.zip |
mpegplayer: Make playback engine fully seekable and frame-accurate and split into logical parts. Be sure to have all current features work. Actual UI for seeking will be added soon. Recommended GOP size is about 15-30 frames depending on target or seeking can be slow with really long GOPs (nature of MPEG video). More refined encoding recommendations for a particular player should be posted soon.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15977 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/mpegplayer/mpegplayer.c')
-rw-r--r-- | apps/plugins/mpegplayer/mpegplayer.c | 2583 |
1 files changed, 213 insertions, 2370 deletions
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c index eb904ed3c1..03ec5ba821 100644 --- a/apps/plugins/mpegplayer/mpegplayer.c +++ b/apps/plugins/mpegplayer/mpegplayer.c | |||
@@ -1,110 +1,110 @@ | |||
1 | /* | 1 | /*************************************************************************** |
2 | * mpegplayer.c - based on : | 2 | * __________ __ ___. |
3 | * - mpeg2dec.c | 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
4 | * - m2psd.c (http://www.brouhaha.com/~eric/software/m2psd/) | 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
5 | * | 9 | * |
6 | * Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> | 10 | * mpegplayer main entrypoint and UI implementation |
7 | * Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> | ||
8 | * | 11 | * |
9 | * m2psd: MPEG 2 Program Stream Demultiplexer | 12 | * Copyright (c) 2007 Michael Sevakis |
10 | * Copyright (C) 2003 Eric Smith <eric@brouhaha.com> | ||
11 | * | 13 | * |
12 | * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. | 14 | * All files in this archive are subject to the GNU General Public License. |
13 | * See http://libmpeg2.sourceforge.net/ for updates. | 15 | * See the file COPYING in the source tree root for full license agreement. |
14 | * | 16 | * |
15 | * mpeg2dec is free software; you can redistribute it and/or modify | 17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
16 | * it under the terms of the GNU General Public License as published by | 18 | * KIND, either express or implied. |
17 | * the Free Software Foundation; either version 2 of the License, or | ||
18 | * (at your option) any later version. | ||
19 | * | 19 | * |
20 | * mpeg2dec is distributed in the hope that it will be useful, | 20 | ****************************************************************************/ |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 21 | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 22 | /**************************************************************************** |
23 | * GNU General Public License for more details. | 23 | * NOTES: |
24 | * | 24 | * |
25 | * You should have received a copy of the GNU General Public License | 25 | * mpegplayer is structured as follows: |
26 | * along with this program; if not, write to the Free Software | 26 | * |
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 27 | * +-->Video Thread-->Video Output-->LCD |
28 | */ | 28 | * | |
29 | 29 | * UI-->Stream Manager-->+-->Audio Thread-->PCM buffer--Audio Device | |
30 | /* | 30 | * | | | | (ref. clock) |
31 | 31 | * | | +-->Buffer Thread | | |
32 | NOTES: | 32 | * Stream Data | | (clock intf./ |
33 | 33 | * Requests | File Cache drift adj.) | |
34 | mpegplayer is structured as follows: | 34 | * | Disk I/O |
35 | 35 | * Stream services | |
36 | 1) Video thread (running on the COP for PortalPlayer targets). | 36 | * (timing, etc.) |
37 | 2) Audio thread (running on the main CPU to maintain consistency with | 37 | * |
38 | the audio FIQ hander on PP). | 38 | * Thread list: |
39 | 3) The main thread which takes care of buffering. | 39 | * 1) The main thread - Handles user input, settings, basic playback control |
40 | 40 | * and USB connect. | |
41 | Using the main thread for buffering wastes the 8KB main stack which is | 41 | * |
42 | in IRAM. However, 8KB is not enough for the audio thread to run (it | 42 | * 2) Stream Manager thread - Handles playback state, events from streams |
43 | needs somewhere between 8KB and 9KB), so we create a new thread in | 43 | * such as when a stream is finished, stream commands, PCM state. The |
44 | order to`give it a larger stack and steal the core codec thread's | 44 | * layer in which this thread run also handles arbitration of data |
45 | stack (9KB of precious IRAM). | 45 | * requests between the streams and the disk buffer. The actual specific |
46 | 46 | * transport layer code may get moved out to support multiple container | |
47 | The button loop (and hence pause/resume, main menu and, in the future, | 47 | * formats. |
48 | seeking) is placed in the audio thread. This keeps it on the main CPU | 48 | * |
49 | in PP targets and also allows buffering to continue in the background | 49 | * 3) Buffer thread - Buffers data in the background, generates notifications |
50 | whilst the main thread is filling the buffer. | 50 | * to streams when their data has been buffered, and watches streams' |
51 | 51 | * progress to keep data available during playback. Handles synchronous | |
52 | A/V sync is not yet implemented but is planned to be achieved by | 52 | * random access requests when the file cache is missed. |
53 | syncing the master clock with the audio, and then (as is currently | 53 | * |
54 | implemented), syncing video with the master clock. This can happen in | 54 | * 4) Video thread (running on the COP for PortalPlayer targets) - Decodes |
55 | the audio thread, along with resyncing after pause. | 55 | * the video stream and renders video frames to the LCD. Handles |
56 | 56 | * miscellaneous video tasks like frame and thumbnail printing. | |
57 | Seeking should probably happen in the main thread, as that's where the | 57 | * |
58 | buffering happens. | 58 | * 5) Audio thread (running on the main CPU to maintain consistency with the |
59 | 59 | * audio FIQ hander on PP) - Decodes audio frames and places them into | |
60 | On PortalPlayer targets, the main CPU is not being fully utilised - | 60 | * the PCM buffer for rendering by the audio device. |
61 | the bottleneck is the video decoding on the COP. One way to improve | 61 | * |
62 | that might be to move the rendering of the frames (i.e. the | 62 | * Streams are neither aware of one another nor care about one another. All |
63 | lcd_yuv_blit() call) from the COP back to the main CPU. Ideas and | 63 | * streams shall have their own thread (unless it is _really_ efficient to |
64 | patches for that are welcome! | 64 | * have a single thread handle a couple minor streams). All coordination of |
65 | 65 | * the streams is done through the stream manager. The clocking is controlled | |
66 | Notes about MPEG files: | 66 | * by and exposed by the stream manager to other streams and implemented at |
67 | 67 | * the PCM level. | |
68 | MPEG System Clock is 27MHz - i.e. 27000000 ticks/second. | 68 | * |
69 | 69 | * Notes about MPEG files: | |
70 | FPS is represented in terms of a frame period - this is always an | 70 | * |
71 | integer number of 27MHz ticks. | 71 | * MPEG System Clock is 27MHz - i.e. 27000000 ticks/second. |
72 | 72 | * | |
73 | e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of | 73 | * FPS is represented in terms of a frame period - this is always an |
74 | 900900 27MHz ticks. | 74 | * integer number of 27MHz ticks. |
75 | 75 | * | |
76 | In libmpeg2, info->sequence->frame_period contains the frame_period. | 76 | * e.g. 29.97fps (30000/1001) NTSC video has an exact frame period of |
77 | 77 | * 900900 27MHz ticks. | |
78 | Working with Rockbox's 100Hz tick, the common frame rates would need | 78 | * |
79 | to be as follows: | 79 | * In libmpeg2, info->sequence->frame_period contains the frame_period. |
80 | 80 | * | |
81 | FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz | 81 | * Working with Rockbox's 100Hz tick, the common frame rates would need |
82 | --------|----------------------------------------------------------- | 82 | * to be as follows (1): |
83 | 10* | 2700000 | 10 | 4410 | 4800 | 83 | * |
84 | 12* | 2250000 | 8.3333 | 3675 | 4000 | 84 | * FPS | 27Mhz | 100Hz | 44.1KHz | 48KHz |
85 | 15* | 1800000 | 6.6667 | 2940 | 3200 | 85 | * --------|----------------------------------------------------------- |
86 | 23.9760 | 1126125 | 4.170833333 | 1839.3375 | 2002 | 86 | * 10* | 2700000 | 10 | 4410 | 4800 |
87 | 24 | 1125000 | 4.166667 | 1837.5 | 2000 | 87 | * 12* | 2250000 | 8.3333 | 3675 | 4000 |
88 | 25 | 1080000 | 4 | 1764 | 1920 | 88 | * 15* | 1800000 | 6.6667 | 2940 | 3200 |
89 | 29.9700 | 900900 | 3.336667 | 1471,47 | 1601.6 | 89 | * 23.9760 | 1126125 | 4.170833333 | 1839.3375 | 2002 |
90 | 30 | 900000 | 3.333333 | 1470 | 1600 | 90 | * 24 | 1125000 | 4.166667 | 1837.5 | 2000 |
91 | 91 | * 25 | 1080000 | 4 | 1764 | 1920 | |
92 | 92 | * 29.9700 | 900900 | 3.336667 | 1471,47 | 1601.6 | |
93 | *Unofficial framerates | 93 | * 30 | 900000 | 3.333333 | 1470 | 1600 |
94 | 94 | * | |
95 | */ | 95 | * *Unofficial framerates |
96 | 96 | * | |
97 | 97 | * (1) But we don't really care since the audio clock is used anyway and has | |
98 | #include "mpeg2dec_config.h" | 98 | * very fine resolution ;-) |
99 | 99 | *****************************************************************************/ | |
100 | #include "plugin.h" | 100 | #include "plugin.h" |
101 | #include "gray.h" | 101 | #include "mpegplayer.h" |
102 | #include "helper.h" | 102 | #include "helper.h" |
103 | |||
104 | #include "mpeg2.h" | ||
105 | #include "mpeg_settings.h" | 103 | #include "mpeg_settings.h" |
104 | #include "mpeg2.h" | ||
106 | #include "video_out.h" | 105 | #include "video_out.h" |
107 | #include "../../codecs/libmad/mad.h" | 106 | #include "stream_thread.h" |
107 | #include "stream_mgr.h" | ||
108 | 108 | ||
109 | PLUGIN_HEADER | 109 | PLUGIN_HEADER |
110 | PLUGIN_IRAM_DECLARE | 110 | PLUGIN_IRAM_DECLARE |
@@ -177,908 +177,43 @@ PLUGIN_IRAM_DECLARE | |||
177 | struct plugin_api* rb; | 177 | struct plugin_api* rb; |
178 | 178 | ||
179 | CACHE_FUNCTION_WRAPPERS(rb); | 179 | CACHE_FUNCTION_WRAPPERS(rb); |
180 | ALIGN_BUFFER_WRAPPER(rb); | ||
180 | 181 | ||
181 | extern void *mpeg_malloc(size_t size, mpeg2_alloc_t reason); | 182 | static bool button_loop(void) |
182 | extern size_t mpeg_alloc_init(unsigned char *buf, size_t mallocsize, | ||
183 | size_t libmpeg2size); | ||
184 | |||
185 | static mpeg2dec_t * mpeg2dec NOCACHEBSS_ATTR; | ||
186 | static int total_offset NOCACHEBSS_ATTR = 0; | ||
187 | static int num_drawn NOCACHEBSS_ATTR = 0; | ||
188 | static int count_start NOCACHEBSS_ATTR = 0; | ||
189 | |||
190 | /* Streams */ | ||
191 | typedef struct | ||
192 | { | ||
193 | struct thread_entry *thread; /* Stream's thread */ | ||
194 | int status; /* Current stream status */ | ||
195 | struct queue_event ev; /* Event sent to steam */ | ||
196 | int have_msg; /* 1=event pending */ | ||
197 | int replied; /* 1=replied to last event */ | ||
198 | int reply; /* reply value */ | ||
199 | struct mutex msg_lock; /* serialization for event senders */ | ||
200 | uint8_t* curr_packet; /* Current stream packet beginning */ | ||
201 | uint8_t* curr_packet_end; /* Current stream packet end */ | ||
202 | |||
203 | uint8_t* prev_packet; /* Previous stream packet beginning */ | ||
204 | size_t prev_packet_length; /* Lenth of previous packet */ | ||
205 | size_t buffer_remaining; /* How much data is left in the buffer */ | ||
206 | uint32_t curr_pts; /* Current presentation timestamp */ | ||
207 | uint32_t curr_time; /* Current time in samples */ | ||
208 | uint32_t tagged; /* curr_pts is valid */ | ||
209 | |||
210 | int id; | ||
211 | } Stream; | ||
212 | |||
213 | static Stream audio_str IBSS_ATTR; | ||
214 | static Stream video_str IBSS_ATTR; | ||
215 | |||
216 | /* Messages */ | ||
217 | enum | ||
218 | { | ||
219 | STREAM_PLAY, | ||
220 | STREAM_PAUSE, | ||
221 | STREAM_QUIT | ||
222 | }; | ||
223 | |||
224 | /* Status */ | ||
225 | enum | ||
226 | { | ||
227 | STREAM_ERROR = -4, | ||
228 | STREAM_STOPPED = -3, | ||
229 | STREAM_TERMINATED = -2, | ||
230 | STREAM_DONE = -1, | ||
231 | STREAM_PLAYING = 0, | ||
232 | STREAM_PAUSED, | ||
233 | STREAM_BUFFERING | ||
234 | }; | ||
235 | |||
236 | /* Returns true if a message is waiting */ | ||
237 | static inline bool str_have_msg(Stream *str) | ||
238 | { | 183 | { |
239 | return str->have_msg != 0; | 184 | bool ret = true; |
240 | } | ||
241 | 185 | ||
242 | /* Waits until a message is sent */ | 186 | rb->lcd_setfont(FONT_SYSFIXED); |
243 | static void str_wait_msg(Stream *str) | 187 | rb->lcd_clear_display(); |
244 | { | 188 | rb->lcd_update(); |
245 | int spin_count = 0; | ||
246 | 189 | ||
247 | while (str->have_msg == 0) | 190 | /* Start playback at the specified starting time */ |
191 | if (stream_seek(settings.resume_time, SEEK_SET) < STREAM_OK || | ||
192 | (stream_show_vo(true), stream_play()) < STREAM_OK) | ||
248 | { | 193 | { |
249 | if (spin_count < 100) | 194 | rb->splash(HZ*2, "Playback failed"); |
250 | { | ||
251 | rb->yield(); | ||
252 | spin_count++; | ||
253 | continue; | ||
254 | } | ||
255 | |||
256 | rb->sleep(0); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /* Returns a message waiting or blocks until one is available - removes the | ||
261 | event */ | ||
262 | static void str_get_msg(Stream *str, struct queue_event *ev) | ||
263 | { | ||
264 | str_wait_msg(str); | ||
265 | ev->id = str->ev.id; | ||
266 | ev->data = str->ev.data; | ||
267 | str->have_msg = 0; | ||
268 | } | ||
269 | |||
270 | /* Peeks at the current message without blocking, returns the data but | ||
271 | does not remove the event */ | ||
272 | static bool str_look_msg(Stream *str, struct queue_event *ev) | ||
273 | { | ||
274 | if (!str_have_msg(str)) | ||
275 | return false; | 195 | return false; |
276 | |||
277 | ev->id = str->ev.id; | ||
278 | ev->data = str->ev.data; | ||
279 | return true; | ||
280 | } | ||
281 | |||
282 | /* Replies to the last message pulled - has no effect if last message has not | ||
283 | been pulled or already replied */ | ||
284 | static void str_reply_msg(Stream *str, int reply) | ||
285 | { | ||
286 | if (str->replied == 1 || str->have_msg != 0) | ||
287 | return; | ||
288 | |||
289 | str->reply = reply; | ||
290 | str->replied = 1; | ||
291 | } | ||
292 | |||
293 | /* Sends a message to a stream and waits for a reply */ | ||
294 | static intptr_t str_send_msg(Stream *str, int id, intptr_t data) | ||
295 | { | ||
296 | int spin_count = 0; | ||
297 | intptr_t reply; | ||
298 | |||
299 | #if 0 | ||
300 | if (str->thread == rb->thread_get_current()) | ||
301 | return str->dispatch_fn(str, msg); | ||
302 | #endif | ||
303 | |||
304 | /* Only one thread at a time, please */ | ||
305 | rb->mutex_lock(&str->msg_lock); | ||
306 | |||
307 | str->ev.id = id; | ||
308 | str->ev.data = data; | ||
309 | str->reply = 0; | ||
310 | str->replied = 0; | ||
311 | str->have_msg = 1; | ||
312 | |||
313 | while (str->replied == 0 && str->status != STREAM_TERMINATED) | ||
314 | { | ||
315 | if (spin_count < 100) | ||
316 | { | ||
317 | rb->yield(); | ||
318 | spin_count++; | ||
319 | continue; | ||
320 | } | ||
321 | |||
322 | rb->sleep(0); | ||
323 | } | ||
324 | |||
325 | reply = str->reply; | ||
326 | |||
327 | rb->mutex_unlock(&str->msg_lock); | ||
328 | |||
329 | return reply; | ||
330 | } | ||
331 | |||
332 | /* NOTE: Putting the following variables in IRAM cause audio corruption | ||
333 | on the ipod (reason unknown) | ||
334 | */ | ||
335 | static uint8_t *disk_buf_start IBSS_ATTR; /* Start pointer */ | ||
336 | static uint8_t *disk_buf_end IBSS_ATTR; /* End of buffer pointer less | ||
337 | MPEG_GUARDBUF_SIZE. The | ||
338 | guard space is used to wrap | ||
339 | data at the buffer start to | ||
340 | pass continuous data | ||
341 | packets */ | ||
342 | static uint8_t *disk_buf_tail IBSS_ATTR; /* Location of last data + 1 | ||
343 | filled into the buffer */ | ||
344 | static size_t disk_buf_size IBSS_ATTR; /* The total buffer length | ||
345 | including the guard | ||
346 | space */ | ||
347 | static size_t file_remaining IBSS_ATTR; | ||
348 | |||
349 | #if NUM_CORES > 1 | ||
350 | /* Some stream variables are shared between cores */ | ||
351 | struct mutex stream_lock IBSS_ATTR; | ||
352 | static inline void init_stream_lock(void) | ||
353 | { rb->mutex_init(&stream_lock); } | ||
354 | static inline void lock_stream(void) | ||
355 | { rb->mutex_lock(&stream_lock); } | ||
356 | static inline void unlock_stream(void) | ||
357 | { rb->mutex_unlock(&stream_lock); } | ||
358 | #else | ||
359 | /* No RMW issue here */ | ||
360 | static inline void init_stream_lock(void) | ||
361 | { } | ||
362 | static inline void lock_stream(void) | ||
363 | { } | ||
364 | static inline void unlock_stream(void) | ||
365 | { } | ||
366 | #endif | ||
367 | |||
368 | static int audio_sync_start IBSS_ATTR; /* If 0, the audio thread | ||
369 | yields waiting on the video | ||
370 | thread to synchronize with | ||
371 | the stream */ | ||
372 | static uint32_t audio_sync_time IBSS_ATTR; /* The time that the video | ||
373 | thread has reached after | ||
374 | synchronizing. The | ||
375 | audio thread now needs | ||
376 | to advance to this | ||
377 | time */ | ||
378 | static int video_sync_start IBSS_ATTR; /* While 0, the video thread | ||
379 | yields until the audio | ||
380 | thread has reached the | ||
381 | audio_sync_time */ | ||
382 | static int video_thumb_print IBSS_ATTR; /* If 1, the video thread is | ||
383 | only decoding one frame for | ||
384 | use in the menu. If 0, | ||
385 | normal operation */ | ||
386 | static int end_pts_time IBSS_ATTR; /* The movie end time as represented by | ||
387 | the maximum audio PTS tag in the | ||
388 | stream converted to half minutes */ | ||
389 | static int start_pts_time IBSS_ATTR; /* The movie start time as represented by | ||
390 | the first audio PTS tag in the | ||
391 | stream converted to half minutes */ | ||
392 | char *filename; /* hack for resume time storage */ | ||
393 | |||
394 | |||
395 | /* Various buffers */ | ||
396 | /* TODO: Can we reduce the PCM buffer size? */ | ||
397 | #define PCMBUFFER_SIZE ((512*1024)-PCMBUFFER_GUARD_SIZE) | ||
398 | #define PCMBUFFER_GUARD_SIZE (1152*4 + sizeof (struct pcm_frame_header)) | ||
399 | #define MPA_MAX_FRAME_SIZE 1729 /* Largest frame - MPEG1, Layer II, 384kbps, 32kHz, pad */ | ||
400 | #define MPABUF_SIZE (64*1024 + ALIGN_UP(MPA_MAX_FRAME_SIZE + 2*MAD_BUFFER_GUARD, 4)) | ||
401 | #define LIBMPEG2BUFFER_SIZE (2*1024*1024) | ||
402 | |||
403 | /* 65536+6 is required since each PES has a 6 byte header with a 16 bit packet length field */ | ||
404 | #define MPEG_GUARDBUF_SIZE (65*1024) /* Keep a bit extra - excessive for now */ | ||
405 | #define MPEG_LOW_WATERMARK (1024*1024) | ||
406 | |||
407 | static void pcm_playback_play_pause(bool play); | ||
408 | |||
409 | /* libmad related functions/definitions */ | ||
410 | #define INPUT_CHUNK_SIZE 8192 | ||
411 | |||
412 | struct mad_stream stream IBSS_ATTR; | ||
413 | struct mad_frame frame IBSS_ATTR; | ||
414 | struct mad_synth synth IBSS_ATTR; | ||
415 | |||
416 | unsigned char mad_main_data[MAD_BUFFER_MDLEN]; /* 2567 bytes */ | ||
417 | |||
418 | /* There isn't enough room for this in IRAM on PortalPlayer, but there | ||
419 | is for Coldfire. */ | ||
420 | |||
421 | #ifdef CPU_COLDFIRE | ||
422 | static mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR; /* 4608 bytes */ | ||
423 | #else | ||
424 | static mad_fixed_t mad_frame_overlap[2][32][18] __attribute__((aligned(16))); /* 4608 bytes */ | ||
425 | #endif | ||
426 | |||
427 | static void init_mad(void* mad_frame_overlap) | ||
428 | { | ||
429 | rb->memset(&stream, 0, sizeof(struct mad_stream)); | ||
430 | rb->memset(&frame, 0, sizeof(struct mad_frame)); | ||
431 | rb->memset(&synth, 0, sizeof(struct mad_synth)); | ||
432 | |||
433 | mad_stream_init(&stream); | ||
434 | mad_frame_init(&frame); | ||
435 | |||
436 | /* We do this so libmad doesn't try to call codec_calloc() */ | ||
437 | frame.overlap = mad_frame_overlap; | ||
438 | |||
439 | rb->memset(mad_main_data, 0, sizeof(mad_main_data)); | ||
440 | stream.main_data = &mad_main_data; | ||
441 | } | ||
442 | |||
443 | /* MPEG related headers */ | ||
444 | |||
445 | /* Macros for comparing memory bytes to a series of constant bytes in an | ||
446 | efficient manner - evaluate to true if corresponding bytes match */ | ||
447 | #if defined (CPU_ARM) | ||
448 | /* ARM must load 32-bit values at addres % 4 == 0 offsets but this data | ||
449 | isn't aligned nescessarily, so just byte compare */ | ||
450 | #define CMP_3_CONST(_a, _b) \ | ||
451 | ({ \ | ||
452 | int _x; \ | ||
453 | asm volatile ( \ | ||
454 | "ldrb %[x], [%[a], #0] \r\n" \ | ||
455 | "eors %[x], %[x], %[b0] \r\n" \ | ||
456 | "ldreqb %[x], [%[a], #1] \r\n" \ | ||
457 | "eoreqs %[x], %[x], %[b1] \r\n" \ | ||
458 | "ldreqb %[x], [%[a], #2] \r\n" \ | ||
459 | "eoreqs %[x], %[x], %[b2] \r\n" \ | ||
460 | : [x]"=&r"(_x) \ | ||
461 | : [a]"r"(_a), \ | ||
462 | [b0]"i"((_b) >> 24), \ | ||
463 | [b1]"i"((_b) << 8 >> 24), \ | ||
464 | [b2]"i"((_b) << 16 >> 24) \ | ||
465 | ); \ | ||
466 | _x == 0; \ | ||
467 | }) | ||
468 | #define CMP_4_CONST(_a, _b) \ | ||
469 | ({ \ | ||
470 | int _x; \ | ||
471 | asm volatile ( \ | ||
472 | "ldrb %[x], [%[a], #0] \r\n" \ | ||
473 | "eors %[x], %[x], %[b0] \r\n" \ | ||
474 | "ldreqb %[x], [%[a], #1] \r\n" \ | ||
475 | "eoreqs %[x], %[x], %[b1] \r\n" \ | ||
476 | "ldreqb %[x], [%[a], #2] \r\n" \ | ||
477 | "eoreqs %[x], %[x], %[b2] \r\n" \ | ||
478 | "ldreqb %[x], [%[a], #3] \r\n" \ | ||
479 | "eoreqs %[x], %[x], %[b3] \r\n" \ | ||
480 | : [x]"=&r"(_x) \ | ||
481 | : [a]"r"(_a), \ | ||
482 | [b0]"i"((_b) >> 24), \ | ||
483 | [b1]"i"((_b) << 8 >> 24), \ | ||
484 | [b2]"i"((_b) << 16 >> 24), \ | ||
485 | [b3]"i"((_b) << 24 >> 24) \ | ||
486 | ); \ | ||
487 | _x == 0; \ | ||
488 | }) | ||
489 | #elif defined (CPU_COLDFIRE) | ||
490 | /* Coldfire can just load a 32 bit value at any offset but ASM is not the best way | ||
491 | to integrate this with the C code */ | ||
492 | #define CMP_3_CONST(a, b) \ | ||
493 | (((*(uint32_t *)(a) >> 8) ^ ((uint32_t)(b) >> 8)) == 0) | ||
494 | #define CMP_4_CONST(a, b) \ | ||
495 | ((*(uint32_t *)(a) ^ (b)) == 0) | ||
496 | #else | ||
497 | /* Don't know what this is - use bytewise comparisons */ | ||
498 | #define CMP_3_CONST(a, b) \ | ||
499 | (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \ | ||
500 | ((a)[1] ^ (((b) >> 16) & 0xff)) | \ | ||
501 | ((a)[2] ^ (((b) >> 8) & 0xff)) ) == 0) | ||
502 | #define CMP_4_CONST(a, b) \ | ||
503 | (( ((a)[0] ^ (((b) >> 24) & 0xff)) | \ | ||
504 | ((a)[1] ^ (((b) >> 16) & 0xff)) | \ | ||
505 | ((a)[2] ^ (((b) >> 8) & 0xff)) | \ | ||
506 | ((a)[3] ^ ((b) & 0xff)) ) == 0) | ||
507 | #endif | ||
508 | |||
509 | /* Codes for various header byte sequences - MSB represents lowest memory | ||
510 | address */ | ||
511 | #define PACKET_START_CODE_PREFIX 0x00000100ul | ||
512 | #define END_CODE 0x000001b9ul | ||
513 | #define PACK_START_CODE 0x000001baul | ||
514 | #define SYSTEM_HEADER_START_CODE 0x000001bbul | ||
515 | |||
516 | /* p = base pointer, b0 - b4 = byte offsets from p */ | ||
517 | /* We only care about the MS 32 bits of the 33 and so the ticks are 45kHz */ | ||
518 | #define TS_FROM_HEADER(p, b0, b1, b2, b3, b4) \ | ||
519 | ((uint32_t)(((p)[b0] >> 1 << 29) | \ | ||
520 | ((p)[b1] << 21) | \ | ||
521 | ((p)[b2] >> 1 << 14) | \ | ||
522 | ((p)[b3] << 6) | \ | ||
523 | ((p)[b4] >> 2 ))) | ||
524 | |||
525 | /* This function synchronizes the mpeg stream. The function returns | ||
526 | true on error */ | ||
527 | bool sync_data_stream(uint8_t **p) | ||
528 | { | ||
529 | for (;;) | ||
530 | { | ||
531 | while ( !CMP_4_CONST(*p, PACK_START_CODE) && (*p) < disk_buf_tail ) | ||
532 | (*p)++; | ||
533 | if ( (*p) >= disk_buf_tail ) | ||
534 | break; | ||
535 | uint8_t *p_save = (*p); | ||
536 | if ( ((*p)[4] & 0xc0) == 0x40 ) /* mpeg-2 */ | ||
537 | (*p) += 14 + ((*p)[13] & 7); | ||
538 | else if ( ((*p)[4] & 0xf0) == 0x20 ) /* mpeg-1 */ | ||
539 | (*p) += 12; | ||
540 | else | ||
541 | (*p) += 5; | ||
542 | if ( (*p) >= disk_buf_tail ) | ||
543 | break; | ||
544 | if ( CMP_3_CONST(*p, PACKET_START_CODE_PREFIX) ) | ||
545 | { | ||
546 | (*p) = p_save; | ||
547 | break; | ||
548 | } | ||
549 | else | ||
550 | (*p) = p_save+1; | ||
551 | } | 196 | } |
552 | 197 | ||
553 | if ( (*p) >= disk_buf_tail ) | 198 | /* Gently poll the video player for EOS and handle UI */ |
554 | return true; | 199 | while (stream_status() != STREAM_STOPPED) |
555 | else | ||
556 | return false; | ||
557 | } | ||
558 | |||
559 | /* This function demuxes the streams and gives the next stream data | ||
560 | pointer. Type 0 is normal operation. Type 1 and 2 have been added | ||
561 | for rapid seeks into the data stream. Type 1 and 2 ignore the | ||
562 | video_sync_start state (a signal to yield for refilling the | ||
563 | buffer). Type 1 will append more data to the buffer tail (minumal | ||
564 | bufer size reads that are increased only as needed). */ | ||
565 | static int get_next_data( Stream* str, uint8_t type ) | ||
566 | { | ||
567 | uint8_t *p; | ||
568 | uint8_t *header; | ||
569 | int stream; | ||
570 | |||
571 | static int mpeg1_skip_table[16] = | ||
572 | { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
573 | |||
574 | if ( (p=str->curr_packet_end) == NULL) | ||
575 | p = disk_buf_start; | ||
576 | |||
577 | while (1) | ||
578 | { | 200 | { |
579 | int length, bytes; | 201 | int button = rb->button_get_w_tmo(HZ/2); |
580 | |||
581 | /* Yield for buffer filling */ | ||
582 | if ( (type == 0) && (str->buffer_remaining < 120*1024) && (file_remaining > 0) ) | ||
583 | while ( (str->buffer_remaining < 512*1024) && (file_remaining > 0) ) | ||
584 | rb->yield(); | ||
585 | |||
586 | /* The packet start position (plus an arbitrary header length) | ||
587 | has exceeded the amount of data in the buffer */ | ||
588 | if ( type == 1 && (p+50) >= disk_buf_tail ) | ||
589 | { | ||
590 | DEBUGF("disk buffer overflow\n"); | ||
591 | return 1; | ||
592 | } | ||
593 | |||
594 | /* are we at the end of file? */ | ||
595 | { | ||
596 | size_t tmp_length; | ||
597 | if (p < str->prev_packet) | ||
598 | tmp_length = (disk_buf_end - str->prev_packet) + | ||
599 | (p - disk_buf_start); | ||
600 | else | ||
601 | tmp_length = (p - str->prev_packet); | ||
602 | if (0 == str->buffer_remaining-tmp_length-str->prev_packet_length) | ||
603 | { | ||
604 | str->curr_packet_end = str->curr_packet = NULL; | ||
605 | break; | ||
606 | } | ||
607 | } | ||
608 | 202 | ||
609 | /* wrap the disk buffer */ | 203 | switch (button) |
610 | if (p >= disk_buf_end) | ||
611 | p = disk_buf_start + (p - disk_buf_end); | ||
612 | |||
613 | /* wrap packet header if needed */ | ||
614 | if ( (p+50) >= disk_buf_end ) | ||
615 | rb->memcpy(disk_buf_end, disk_buf_start, 50); | ||
616 | |||
617 | /* Pack header, skip it */ | ||
618 | if (CMP_4_CONST(p, PACK_START_CODE)) | ||
619 | { | 204 | { |
620 | if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */ | 205 | case BUTTON_NONE: |
621 | { | ||
622 | p += 14 + (p[13] & 7); | ||
623 | } | ||
624 | else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */ | ||
625 | { | ||
626 | p += 12; | ||
627 | } | ||
628 | else | ||
629 | { | ||
630 | rb->splash( 30, "Weird Pack header!" ); | ||
631 | p += 5; | ||
632 | } | ||
633 | } | ||
634 | |||
635 | /* System header, parse and skip it - four bytes */ | ||
636 | if (CMP_4_CONST(p, SYSTEM_HEADER_START_CODE)) | ||
637 | { | ||
638 | int header_length; | ||
639 | |||
640 | p += 4; /*skip start code*/ | ||
641 | header_length = *p++ << 8; | ||
642 | header_length += *p++; | ||
643 | |||
644 | p += header_length; | ||
645 | |||
646 | if ( p >= disk_buf_end ) | ||
647 | p = disk_buf_start + (p - disk_buf_end); | ||
648 | } | ||
649 | |||
650 | /* Packet header, parse it */ | ||
651 | if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) | ||
652 | { | ||
653 | /* Problem */ | ||
654 | rb->splash( HZ*3, "missing packet start code prefix : %X%X at %lX", | ||
655 | *p, *(p+2), (long unsigned int)(p-disk_buf_start) ); | ||
656 | |||
657 | /* not 64bit safe | ||
658 | DEBUGF("end diff: %X,%X,%X,%X,%X,%X\n",(int)str->curr_packet_end, | ||
659 | (int)audio_str.curr_packet_end,(int)video_str.curr_packet_end, | ||
660 | (int)disk_buf_start,(int)disk_buf_end,(int)disk_buf_tail); | ||
661 | */ | ||
662 | |||
663 | str->curr_packet_end = str->curr_packet = NULL; | ||
664 | break; | ||
665 | } | ||
666 | |||
667 | /* We retrieve basic infos */ | ||
668 | stream = p[3]; | ||
669 | length = (p[4] << 8) | p[5]; | ||
670 | |||
671 | if (stream != str->id) | ||
672 | { | ||
673 | /* End of stream ? */ | ||
674 | if (stream == 0xB9) | ||
675 | { | ||
676 | str->curr_packet_end = str->curr_packet = NULL; | ||
677 | break; | ||
678 | } | ||
679 | |||
680 | /* It's not the packet we're looking for, skip it */ | ||
681 | p += length + 6; | ||
682 | continue; | 206 | continue; |
683 | } | ||
684 | |||
685 | /* Ok, it's our packet */ | ||
686 | str->curr_packet_end = p + length+6; | ||
687 | header = p; | ||
688 | |||
689 | if ((header[6] & 0xc0) == 0x80) /* mpeg2 */ | ||
690 | { | ||
691 | length = 9 + header[8]; | ||
692 | |||
693 | /* header points to the mpeg2 pes header */ | ||
694 | if (header[7] & 0x80) | ||
695 | { | ||
696 | /* header has a pts */ | ||
697 | uint32_t pts = TS_FROM_HEADER(header, 9, 10, 11, 12, 13); | ||
698 | |||
699 | if (stream >= 0xe0) | ||
700 | { | ||
701 | /* video stream - header may have a dts as well */ | ||
702 | uint32_t dts = (header[7] & 0x40) == 0 ? | ||
703 | pts : TS_FROM_HEADER(header, 14, 15, 16, 17, 18); | ||
704 | |||
705 | mpeg2_tag_picture (mpeg2dec, pts, dts); | ||
706 | } | ||
707 | else | ||
708 | { | ||
709 | str->curr_pts = pts; | ||
710 | str->tagged = 1; | ||
711 | } | ||
712 | } | ||
713 | } | ||
714 | else /* mpeg1 */ | ||
715 | { | ||
716 | int len_skip; | ||
717 | uint8_t * ptsbuf; | ||
718 | |||
719 | length = 7; | ||
720 | |||
721 | while (header[length - 1] == 0xff) | ||
722 | { | ||
723 | length++; | ||
724 | if (length > 23) | ||
725 | { | ||
726 | rb->splash( 30, "Too much stuffing" ); | ||
727 | DEBUGF("Too much stuffing" ); | ||
728 | break; | ||
729 | } | ||
730 | } | ||
731 | |||
732 | if ( (header[length - 1] & 0xc0) == 0x40 ) | ||
733 | length += 2; | ||
734 | |||
735 | len_skip = length; | ||
736 | length += mpeg1_skip_table[header[length - 1] >> 4]; | ||
737 | |||
738 | /* header points to the mpeg1 pes header */ | ||
739 | ptsbuf = header + len_skip; | ||
740 | |||
741 | if ((ptsbuf[-1] & 0xe0) == 0x20) | ||
742 | { | ||
743 | /* header has a pts */ | ||
744 | uint32_t pts = TS_FROM_HEADER(ptsbuf, -1, 0, 1, 2, 3); | ||
745 | |||
746 | if (stream >= 0xe0) | ||
747 | { | ||
748 | /* video stream - header may have a dts as well */ | ||
749 | uint32_t dts = (ptsbuf[-1] & 0xf0) != 0x30 ? | ||
750 | pts : TS_FROM_HEADER(ptsbuf, 4, 5, 6, 7, 18); | ||
751 | |||
752 | mpeg2_tag_picture (mpeg2dec, pts, dts); | ||
753 | } | ||
754 | else | ||
755 | { | ||
756 | str->curr_pts = pts; | ||
757 | str->tagged = 1; | ||
758 | } | ||
759 | } | ||
760 | } | ||
761 | |||
762 | p += length; | ||
763 | bytes = 6 + (header[4] << 8) + header[5] - length; | ||
764 | |||
765 | if (bytes > 0) | ||
766 | { | ||
767 | str->curr_packet_end = p + bytes; | ||
768 | |||
769 | if (str->curr_packet != NULL) | ||
770 | { | ||
771 | lock_stream(); | ||
772 | |||
773 | str->buffer_remaining -= str->prev_packet_length; | ||
774 | if (str->curr_packet < str->prev_packet) | ||
775 | str->prev_packet_length = (disk_buf_end - str->prev_packet) + | ||
776 | (str->curr_packet - disk_buf_start); | ||
777 | else | ||
778 | str->prev_packet_length = (str->curr_packet - str->prev_packet); | ||
779 | |||
780 | unlock_stream(); | ||
781 | |||
782 | str->prev_packet = str->curr_packet; | ||
783 | } | ||
784 | |||
785 | str->curr_packet = p; | ||
786 | |||
787 | if (str->curr_packet_end > disk_buf_end) | ||
788 | rb->memcpy(disk_buf_end, disk_buf_start, | ||
789 | str->curr_packet_end - disk_buf_end ); | ||
790 | } | ||
791 | |||
792 | break; | ||
793 | } /* end while */ | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | /* Our clock rate in ticks/second - this won't be a constant for long */ | ||
798 | #define CLOCK_RATE 44100 | ||
799 | |||
800 | /* For simple lowpass filtering of sync variables */ | ||
801 | #define AVERAGE(var, x, count) (((var) * (count-1) + (x)) / (count)) | ||
802 | /* Convert 45kHz PTS/DTS ticks to our clock ticks */ | ||
803 | #define TS_TO_TICKS(pts) ((uint64_t)CLOCK_RATE*(pts) / 45000) | ||
804 | /* Convert 27MHz ticks to our clock ticks */ | ||
805 | #define TIME_TO_TICKS(stamp) ((uint64_t)CLOCK_RATE*(stamp) / 27000000) | ||
806 | |||
807 | /** MPEG audio stream buffer */ | ||
808 | uint8_t* mpa_buffer NOCACHEBSS_ATTR; | ||
809 | |||
810 | static bool init_mpabuf(void) | ||
811 | { | ||
812 | mpa_buffer = mpeg_malloc(MPABUF_SIZE,-2); | ||
813 | return mpa_buffer != NULL; | ||
814 | } | ||
815 | |||
816 | #define PTS_QUEUE_LEN (1 << 5) /* 32 should be way more than sufficient - | ||
817 | if not, the case is handled */ | ||
818 | #define PTS_QUEUE_MASK (PTS_QUEUE_LEN-1) | ||
819 | struct pts_queue_slot | ||
820 | { | ||
821 | uint32_t pts; /* Time stamp for packet */ | ||
822 | ssize_t size; /* Number of bytes left in packet */ | ||
823 | } pts_queue[PTS_QUEUE_LEN] __attribute__((aligned(16))); | ||
824 | |||
825 | /* This starts out wr == rd but will never be emptied to zero during | ||
826 | streaming again in order to support initializing the first packet's | ||
827 | pts value without a special case */ | ||
828 | static unsigned pts_queue_rd NOCACHEBSS_ATTR; | ||
829 | static unsigned pts_queue_wr NOCACHEBSS_ATTR; | ||
830 | |||
831 | /* Increments the queue head postion - should be used to preincrement */ | ||
832 | static bool pts_queue_add_head(void) | ||
833 | { | ||
834 | if (pts_queue_wr - pts_queue_rd >= PTS_QUEUE_LEN-1) | ||
835 | return false; | ||
836 | |||
837 | pts_queue_wr++; | ||
838 | return true; | ||
839 | } | ||
840 | |||
841 | /* Increments the queue tail position - leaves one slot as current */ | ||
842 | static bool pts_queue_remove_tail(void) | ||
843 | { | ||
844 | if (pts_queue_wr - pts_queue_rd <= 1u) | ||
845 | return false; | ||
846 | |||
847 | pts_queue_rd++; | ||
848 | return true; | ||
849 | } | ||
850 | |||
851 | /* Returns the "head" at the index just behind the write index */ | ||
852 | static struct pts_queue_slot * pts_queue_head(void) | ||
853 | { | ||
854 | return &pts_queue[(pts_queue_wr - 1) & PTS_QUEUE_MASK]; | ||
855 | } | ||
856 | |||
857 | /* Returns a pointer to the current tail */ | ||
858 | static struct pts_queue_slot * pts_queue_tail(void) | ||
859 | { | ||
860 | return &pts_queue[pts_queue_rd & PTS_QUEUE_MASK]; | ||
861 | } | ||
862 | |||
863 | /* Resets the pts queue - call when starting and seeking */ | ||
864 | static void pts_queue_reset(void) | ||
865 | { | ||
866 | struct pts_queue_slot *pts; | ||
867 | pts_queue_rd = pts_queue_wr; | ||
868 | pts = pts_queue_tail(); | ||
869 | pts->pts = 0; | ||
870 | pts->size = 0; | ||
871 | } | ||
872 | |||
873 | struct pcm_frame_header /* Header added to pcm data every time a decoded | ||
874 | mpa frame is sent out */ | ||
875 | { | ||
876 | uint32_t size; /* size of this frame - including header */ | ||
877 | uint32_t time; /* timestamp for this frame - derived from PTS */ | ||
878 | unsigned char data[]; /* open array of audio data */ | ||
879 | }; | ||
880 | |||
881 | #define PCMBUF_PLAY_ALL 1l /* Forces buffer to play back all data */ | ||
882 | #define PCMBUF_PLAY_NONE LONG_MAX /* Keeps buffer from playing any data */ | ||
883 | static volatile uint64_t pcmbuf_read IBSS_ATTR; | ||
884 | static volatile uint64_t pcmbuf_written IBSS_ATTR; | ||
885 | static volatile ssize_t pcmbuf_threshold IBSS_ATTR; | ||
886 | static struct pcm_frame_header *pcm_buffer IBSS_ATTR; | ||
887 | static struct pcm_frame_header *pcmbuf_end IBSS_ATTR; | ||
888 | static struct pcm_frame_header * volatile pcmbuf_head IBSS_ATTR; | ||
889 | static struct pcm_frame_header * volatile pcmbuf_tail IBSS_ATTR; | ||
890 | |||
891 | static volatile uint32_t samplesplayed IBSS_ATTR; /* Our base clock */ | ||
892 | static volatile uint32_t samplestart IBSS_ATTR; /* Clock at playback start */ | ||
893 | static volatile int32_t sampleadjust IBSS_ATTR; /* Clock drift adjustment */ | ||
894 | |||
895 | static ssize_t pcmbuf_used(void) | ||
896 | { | ||
897 | return (ssize_t)(pcmbuf_written - pcmbuf_read); | ||
898 | } | ||
899 | |||
900 | static bool init_pcmbuf(void) | ||
901 | { | ||
902 | pcm_buffer = mpeg_malloc(PCMBUFFER_SIZE + PCMBUFFER_GUARD_SIZE, -2); | ||
903 | |||
904 | if (pcm_buffer == NULL) | ||
905 | return false; | ||
906 | |||
907 | pcmbuf_head = pcm_buffer; | ||
908 | pcmbuf_tail = pcm_buffer; | ||
909 | pcmbuf_end = SKIPBYTES(pcm_buffer, PCMBUFFER_SIZE); | ||
910 | pcmbuf_read = 0; | ||
911 | pcmbuf_written = 0; | ||
912 | |||
913 | return true; | ||
914 | } | ||
915 | |||
916 | /* Advance a PCM buffer pointer by size bytes circularly */ | ||
917 | static inline void pcm_advance_buffer(struct pcm_frame_header * volatile *p, | ||
918 | size_t size) | ||
919 | { | ||
920 | *p = SKIPBYTES(*p, size); | ||
921 | if (*p >= pcmbuf_end) | ||
922 | *p = pcm_buffer; | ||
923 | } | ||
924 | |||
925 | static void get_more(unsigned char** start, size_t* size) | ||
926 | { | ||
927 | /* 25ms @ 44.1kHz */ | ||
928 | static unsigned char silence[4412] __attribute__((aligned (4))) = { 0 }; | ||
929 | size_t sz; | ||
930 | |||
931 | if (pcmbuf_used() >= pcmbuf_threshold) | ||
932 | { | ||
933 | uint32_t time = pcmbuf_tail->time; | ||
934 | sz = pcmbuf_tail->size; | ||
935 | |||
936 | *start = (unsigned char *)pcmbuf_tail->data; | ||
937 | |||
938 | pcm_advance_buffer(&pcmbuf_tail, sz); | ||
939 | |||
940 | pcmbuf_read += sz; | ||
941 | |||
942 | sz -= sizeof (*pcmbuf_tail); | ||
943 | |||
944 | *size = sz; | ||
945 | |||
946 | /* Drift the clock towards the audio timestamp values */ | ||
947 | sampleadjust = AVERAGE(sampleadjust, (int32_t)(time - samplesplayed), 8); | ||
948 | |||
949 | /* Update master clock */ | ||
950 | samplesplayed += sz >> 2; | ||
951 | return; | ||
952 | } | ||
953 | |||
954 | /* Keep clock going at all times */ | ||
955 | sz = sizeof (silence); | ||
956 | *start = silence; | ||
957 | *size = sz; | ||
958 | |||
959 | samplesplayed += sz >> 2; | ||
960 | |||
961 | if (pcmbuf_read > pcmbuf_written) | ||
962 | pcmbuf_read = pcmbuf_written; | ||
963 | } | ||
964 | |||
965 | /* Flushes the buffer - clock keeps counting */ | ||
966 | static void pcm_playback_flush(void) | ||
967 | { | ||
968 | bool was_playing = rb->pcm_is_playing(); | ||
969 | |||
970 | if (was_playing) | ||
971 | rb->pcm_play_stop(); | ||
972 | |||
973 | pcmbuf_read = 0; | ||
974 | pcmbuf_written = 0; | ||
975 | pcmbuf_head = pcmbuf_tail; | ||
976 | |||
977 | if (was_playing) | ||
978 | rb->pcm_play_data(get_more, NULL, 0); | ||
979 | } | ||
980 | |||
981 | /* Seek the reference clock to the specified time - next audio data ready to | ||
982 | go to DMA should be on the buffer with the same time index or else the PCM | ||
983 | buffer should be empty */ | ||
984 | static void pcm_playback_seek_time(uint32_t time) | ||
985 | { | ||
986 | bool was_playing = rb->pcm_is_playing(); | ||
987 | |||
988 | if (was_playing) | ||
989 | rb->pcm_play_stop(); | ||
990 | |||
991 | samplesplayed = time; | ||
992 | samplestart = time; | ||
993 | sampleadjust = 0; | ||
994 | |||
995 | if (was_playing) | ||
996 | rb->pcm_play_data(get_more, NULL, 0); | ||
997 | } | ||
998 | |||
999 | /* Start pcm playback with the reference clock set to the specified time */ | ||
1000 | static void pcm_playback_play(uint32_t time) | ||
1001 | { | ||
1002 | pcm_playback_seek_time(time); | ||
1003 | |||
1004 | if (!rb->pcm_is_playing()) | ||
1005 | rb->pcm_play_data(get_more, NULL, 0); | ||
1006 | } | ||
1007 | |||
1008 | /* Pauses playback - and the clock */ | ||
1009 | static void pcm_playback_play_pause(bool play) | ||
1010 | { | ||
1011 | rb->pcm_play_pause(play); | ||
1012 | } | ||
1013 | |||
1014 | /* Stops all playback and resets the clock */ | ||
1015 | static void pcm_playback_stop(void) | ||
1016 | { | ||
1017 | if (rb->pcm_is_playing()) | ||
1018 | rb->pcm_play_stop(); | ||
1019 | |||
1020 | pcm_playback_flush(); | ||
1021 | |||
1022 | sampleadjust = | ||
1023 | samplestart = | ||
1024 | samplesplayed = 0; | ||
1025 | } | ||
1026 | |||
1027 | static uint32_t get_stream_time(void) | ||
1028 | { | ||
1029 | return samplesplayed + sampleadjust - (rb->pcm_get_bytes_waiting() >> 2); | ||
1030 | } | ||
1031 | |||
1032 | static uint32_t get_playback_time(void) | ||
1033 | { | ||
1034 | return samplesplayed + sampleadjust - | ||
1035 | samplestart - (rb->pcm_get_bytes_waiting() >> 2); | ||
1036 | } | ||
1037 | |||
1038 | static inline int32_t clip_sample(int32_t sample) | ||
1039 | { | ||
1040 | if ((int16_t)sample != sample) | ||
1041 | sample = 0x7fff ^ (sample >> 31); | ||
1042 | |||
1043 | return sample; | ||
1044 | } | ||
1045 | 207 | ||
1046 | static int button_loop(void) | ||
1047 | { | ||
1048 | int result; | ||
1049 | int vol, minvol, maxvol; | ||
1050 | int button; | ||
1051 | |||
1052 | if (video_sync_start==1) { | ||
1053 | |||
1054 | if (str_have_msg(&audio_str)) | ||
1055 | { | ||
1056 | struct queue_event ev; | ||
1057 | str_get_msg(&audio_str, &ev); | ||
1058 | |||
1059 | if (ev.id == STREAM_QUIT) | ||
1060 | { | ||
1061 | audio_str.status = STREAM_STOPPED; | ||
1062 | goto quit; | ||
1063 | } | ||
1064 | else | ||
1065 | { | ||
1066 | str_reply_msg(&audio_str, 0); | ||
1067 | } | ||
1068 | } | ||
1069 | |||
1070 | button = rb->button_get(false); | ||
1071 | |||
1072 | switch (button) | ||
1073 | { | ||
1074 | case MPEG_VOLUP: | 208 | case MPEG_VOLUP: |
1075 | case MPEG_VOLUP|BUTTON_REPEAT: | 209 | case MPEG_VOLUP|BUTTON_REPEAT: |
1076 | #ifdef MPEG_VOLUP2 | 210 | #ifdef MPEG_VOLUP2 |
1077 | case MPEG_VOLUP2: | 211 | case MPEG_VOLUP2: |
1078 | case MPEG_VOLUP2|BUTTON_REPEAT: | 212 | case MPEG_VOLUP2|BUTTON_REPEAT: |
1079 | #endif | 213 | #endif |
1080 | vol = rb->global_settings->volume; | 214 | { |
1081 | maxvol = rb->sound_max(SOUND_VOLUME); | 215 | int vol = rb->global_settings->volume; |
216 | int maxvol = rb->sound_max(SOUND_VOLUME); | ||
1082 | 217 | ||
1083 | if (vol < maxvol) { | 218 | if (vol < maxvol) { |
1084 | vol++; | 219 | vol++; |
@@ -1086,6 +221,7 @@ static int button_loop(void) | |||
1086 | rb->global_settings->volume = vol; | 221 | rb->global_settings->volume = vol; |
1087 | } | 222 | } |
1088 | break; | 223 | break; |
224 | } /* MPEG_VOLUP*: */ | ||
1089 | 225 | ||
1090 | case MPEG_VOLDOWN: | 226 | case MPEG_VOLDOWN: |
1091 | case MPEG_VOLDOWN|BUTTON_REPEAT: | 227 | case MPEG_VOLDOWN|BUTTON_REPEAT: |
@@ -1093,8 +229,9 @@ static int button_loop(void) | |||
1093 | case MPEG_VOLDOWN2: | 229 | case MPEG_VOLDOWN2: |
1094 | case MPEG_VOLDOWN2|BUTTON_REPEAT: | 230 | case MPEG_VOLDOWN2|BUTTON_REPEAT: |
1095 | #endif | 231 | #endif |
1096 | vol = rb->global_settings->volume; | 232 | { |
1097 | minvol = rb->sound_min(SOUND_VOLUME); | 233 | int vol = rb->global_settings->volume; |
234 | int minvol = rb->sound_min(SOUND_VOLUME); | ||
1098 | 235 | ||
1099 | if (vol > minvol) { | 236 | if (vol > minvol) { |
1100 | vol--; | 237 | vol--; |
@@ -1102,1225 +239,108 @@ static int button_loop(void) | |||
1102 | rb->global_settings->volume = vol; | 239 | rb->global_settings->volume = vol; |
1103 | } | 240 | } |
1104 | break; | 241 | break; |
242 | } /* MPEG_VOLDOWN*: */ | ||
1105 | 243 | ||
1106 | case MPEG_MENU: | 244 | case MPEG_MENU: |
1107 | pcm_playback_play_pause(false); | 245 | { |
1108 | audio_str.status = STREAM_PAUSED; | 246 | int state = stream_pause(); /* save previous state */ |
1109 | str_send_msg(&video_str, STREAM_PAUSE, 0); | 247 | int result; |
1110 | #ifndef HAVE_LCD_COLOR | ||
1111 | gray_show(false); | ||
1112 | #endif | ||
1113 | result = mpeg_menu(); | ||
1114 | count_start = get_playback_time(); | ||
1115 | num_drawn = 0; | ||
1116 | 248 | ||
1117 | #ifndef HAVE_LCD_COLOR | 249 | /* Hide video output */ |
1118 | gray_show(true); | 250 | stream_show_vo(false); |
1119 | #endif | 251 | backlight_use_settings(rb); |
252 | |||
253 | result = mpeg_menu(); | ||
1120 | 254 | ||
1121 | /* The menu can change the font, so restore */ | 255 | /* The menu can change the font, so restore */ |
1122 | rb->lcd_setfont(FONT_SYSFIXED); | 256 | rb->lcd_setfont(FONT_SYSFIXED); |
1123 | 257 | ||
1124 | switch (result) | 258 | switch (result) |
1125 | { | 259 | { |
1126 | case MPEG_MENU_QUIT: | 260 | case MPEG_MENU_QUIT: |
1127 | settings.resume_time = (int)(get_stream_time()/CLOCK_RATE/ | 261 | stream_stop(); |
1128 | 30-start_pts_time); | 262 | break; |
1129 | str_send_msg(&video_str, STREAM_QUIT, 0); | 263 | default: |
1130 | audio_str.status = STREAM_STOPPED; | 264 | /* If not stopped, show video again */ |
1131 | break; | 265 | if (state != STREAM_STOPPED) |
1132 | default: | 266 | stream_show_vo(true); |
1133 | audio_str.status = STREAM_PLAYING; | 267 | |
1134 | str_send_msg(&video_str, STREAM_PLAY, 0); | 268 | /* If stream was playing, restart it */ |
1135 | pcm_playback_play_pause(true); | 269 | if (state == STREAM_PLAYING) { |
1136 | break; | 270 | backlight_force_on(rb); |
271 | stream_resume(); | ||
272 | } | ||
273 | break; | ||
1137 | } | 274 | } |
1138 | break; | 275 | break; |
276 | } /* MPEG_MENU: */ | ||
1139 | 277 | ||
1140 | case MPEG_STOP: | 278 | case MPEG_STOP: |
1141 | settings.resume_time = (int)(get_stream_time()/CLOCK_RATE/ | 279 | { |
1142 | 30-start_pts_time); | 280 | stream_stop(); |
1143 | str_send_msg(&video_str, STREAM_QUIT, 0); | ||
1144 | audio_str.status = STREAM_STOPPED; | ||
1145 | break; | 281 | break; |
282 | } /* MPEG_STOP: */ | ||
1146 | 283 | ||
1147 | case MPEG_PAUSE: | 284 | case MPEG_PAUSE: |
1148 | #ifdef MPEG_PAUSE2 | 285 | #ifdef MPEG_PAUSE2 |
1149 | case MPEG_PAUSE2: | 286 | case MPEG_PAUSE2: |
1150 | #endif | 287 | #endif |
1151 | settings.resume_time = (int)(get_stream_time()/CLOCK_RATE/ | ||
1152 | 30-start_pts_time); | ||
1153 | save_settings(); | ||
1154 | str_send_msg(&video_str, STREAM_PAUSE, 0); | ||
1155 | audio_str.status = STREAM_PAUSED; | ||
1156 | pcm_playback_play_pause(false); | ||
1157 | |||
1158 | button = BUTTON_NONE; | ||
1159 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
1160 | rb->cpu_boost(false); | ||
1161 | #endif | ||
1162 | do { | ||
1163 | button = rb->button_get(true); | ||
1164 | if (button == MPEG_STOP) { | ||
1165 | str_send_msg(&video_str, STREAM_QUIT, 0); | ||
1166 | audio_str.status = STREAM_STOPPED; | ||
1167 | goto quit; | ||
1168 | } | ||
1169 | #ifndef MPEG_PAUSE2 | ||
1170 | } while (button != MPEG_PAUSE); | ||
1171 | #else | ||
1172 | } while (button != MPEG_PAUSE && button != MPEG_PAUSE2); | ||
1173 | #endif | ||
1174 | |||
1175 | str_send_msg(&video_str, STREAM_PLAY, 0); | ||
1176 | audio_str.status = STREAM_PLAYING; | ||
1177 | pcm_playback_play_pause(true); | ||
1178 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
1179 | rb->cpu_boost(true); | ||
1180 | #endif | ||
1181 | break; | ||
1182 | |||
1183 | default: | ||
1184 | if(rb->default_event_handler(button) == SYS_USB_CONNECTED) { | ||
1185 | str_send_msg(&video_str, STREAM_QUIT, 0); | ||
1186 | audio_str.status = STREAM_STOPPED; | ||
1187 | } | ||
1188 | } | ||
1189 | } | ||
1190 | quit: | ||
1191 | return audio_str.status; | ||
1192 | } | ||
1193 | |||
1194 | static void audio_thread(void) | ||
1195 | { | ||
1196 | uint8_t *mpabuf = mpa_buffer; | ||
1197 | ssize_t mpabuf_used = 0; | ||
1198 | int mad_errors = 0; /* A count of the errors in each frame */ | ||
1199 | struct pts_queue_slot *pts; | ||
1200 | |||
1201 | /* We need this here to init the EMAC for Coldfire targets */ | ||
1202 | mad_synth_init(&synth); | ||
1203 | |||
1204 | /* Init pts queue */ | ||
1205 | pts_queue_reset(); | ||
1206 | pts = pts_queue_tail(); | ||
1207 | |||
1208 | /* Keep buffer from playing */ | ||
1209 | pcmbuf_threshold = PCMBUF_PLAY_NONE; | ||
1210 | |||
1211 | /* Start clock */ | ||
1212 | pcm_playback_play(0); | ||
1213 | |||
1214 | /* Get first packet */ | ||
1215 | get_next_data(&audio_str, 0 ); | ||
1216 | |||
1217 | /* skip audio packets here */ | ||
1218 | while (audio_sync_start==0) | ||
1219 | { | ||
1220 | audio_str.status = STREAM_PLAYING; | ||
1221 | rb->yield(); | ||
1222 | } | ||
1223 | |||
1224 | if (audio_sync_time>10000) | ||
1225 | { | ||
1226 | while (TS_TO_TICKS(audio_str.curr_pts) < audio_sync_time - 10000) | ||
1227 | { | ||
1228 | get_next_data(&audio_str, 0 ); | ||
1229 | rb->priority_yield(); | ||
1230 | } | ||
1231 | } | ||
1232 | |||
1233 | if (audio_str.curr_packet == NULL) | ||
1234 | goto done; | ||
1235 | |||
1236 | /* This is the decoding loop. */ | ||
1237 | while (1) | ||
1238 | { | ||
1239 | int mad_stat; | ||
1240 | size_t len; | ||
1241 | |||
1242 | if (button_loop() == STREAM_STOPPED) | ||
1243 | goto audio_thread_quit; | ||
1244 | |||
1245 | if (pts->size <= 0) | ||
1246 | { | 288 | { |
1247 | /* Carry any overshoot to the next size since we're technically | 289 | if (stream_status() == STREAM_PLAYING) { |
1248 | -pts->size bytes into it already. If size is negative an audio | 290 | /* Playing => Paused */ |
1249 | frame was split accross packets. Old has to be saved before | 291 | stream_pause(); |
1250 | moving the tail. */ | 292 | backlight_use_settings(rb); |
1251 | if (pts_queue_remove_tail()) | ||
1252 | { | ||
1253 | struct pts_queue_slot *old = pts; | ||
1254 | pts = pts_queue_tail(); | ||
1255 | pts->size += old->size; | ||
1256 | old->size = 0; | ||
1257 | } | 293 | } |
1258 | } | 294 | else if (stream_status() == STREAM_PAUSED) { |
1259 | 295 | /* Paused => Playing */ | |
1260 | /** Buffering **/ | 296 | backlight_force_on(rb); |
1261 | if (mpabuf_used >= MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD) | 297 | stream_resume(); |
1262 | { | ||
1263 | /* Above low watermark - do nothing */ | ||
1264 | } | ||
1265 | else if (audio_str.curr_packet != NULL) | ||
1266 | { | ||
1267 | do | ||
1268 | { | ||
1269 | /* Get data from next audio packet */ | ||
1270 | len = audio_str.curr_packet_end - audio_str.curr_packet; | ||
1271 | |||
1272 | if (audio_str.tagged) | ||
1273 | { | ||
1274 | struct pts_queue_slot *stamp = pts; | ||
1275 | |||
1276 | if (pts_queue_add_head()) | ||
1277 | { | ||
1278 | stamp = pts_queue_head(); | ||
1279 | stamp->pts = TS_TO_TICKS(audio_str.curr_pts); | ||
1280 | /* pts->size should have been zeroed when slot was | ||
1281 | freed */ | ||
1282 | } | ||
1283 | /* else queue full - just count up from the last to make | ||
1284 | it look like more data in the same packet */ | ||
1285 | stamp->size += len; | ||
1286 | audio_str.tagged = 0; | ||
1287 | } | ||
1288 | else | ||
1289 | { | ||
1290 | /* Add to the one just behind the head - this may be the | ||
1291 | tail or the previouly added head - whether or not we'll | ||
1292 | ever reach this is quite in question since audio always | ||
1293 | seems to have every packet timestamped */ | ||
1294 | pts_queue_head()->size += len; | ||
1295 | } | ||
1296 | |||
1297 | /* Slide any remainder over to beginning - avoid function | ||
1298 | call overhead if no data remaining as well */ | ||
1299 | if (mpabuf > mpa_buffer && mpabuf_used > 0) | ||
1300 | rb->memmove(mpa_buffer, mpabuf, mpabuf_used); | ||
1301 | |||
1302 | /* Splice this packet onto any remainder */ | ||
1303 | rb->memcpy(mpa_buffer + mpabuf_used, audio_str.curr_packet, | ||
1304 | len); | ||
1305 | |||
1306 | mpabuf_used += len; | ||
1307 | mpabuf = mpa_buffer; | ||
1308 | |||
1309 | /* Get data from next audio packet */ | ||
1310 | get_next_data(&audio_str, 0 ); | ||
1311 | } | ||
1312 | while (audio_str.curr_packet != NULL && | ||
1313 | mpabuf_used < MPA_MAX_FRAME_SIZE + MAD_BUFFER_GUARD); | ||
1314 | } | ||
1315 | else if (mpabuf_used <= 0) | ||
1316 | { | ||
1317 | /* Used up remainder of mpa buffer so quit */ | ||
1318 | break; | ||
1319 | } | ||
1320 | |||
1321 | /** Decoding **/ | ||
1322 | mad_stream_buffer(&stream, mpabuf, mpabuf_used); | ||
1323 | |||
1324 | mad_stat = mad_frame_decode(&frame, &stream); | ||
1325 | |||
1326 | if (stream.next_frame == NULL) | ||
1327 | { | ||
1328 | /* What to do here? (This really is fatal) */ | ||
1329 | DEBUGF("/* What to do here? */\n"); | ||
1330 | break; | ||
1331 | } | ||
1332 | |||
1333 | /* Next mad stream buffer is the next frame postion */ | ||
1334 | mpabuf = (uint8_t *)stream.next_frame; | ||
1335 | |||
1336 | /* Adjust sizes by the frame size */ | ||
1337 | len = stream.next_frame - stream.this_frame; | ||
1338 | mpabuf_used -= len; | ||
1339 | pts->size -= len; | ||
1340 | |||
1341 | if (mad_stat != 0) | ||
1342 | { | ||
1343 | if (stream.error == MAD_FLAG_INCOMPLETE | ||
1344 | || stream.error == MAD_ERROR_BUFLEN) | ||
1345 | { | ||
1346 | /* This makes the codec support partially corrupted files */ | ||
1347 | if (++mad_errors > 30) | ||
1348 | break; | ||
1349 | |||
1350 | stream.error = 0; | ||
1351 | rb->priority_yield(); | ||
1352 | continue; | ||
1353 | } | ||
1354 | else if (MAD_RECOVERABLE(stream.error)) | ||
1355 | { | ||
1356 | stream.error = 0; | ||
1357 | rb->priority_yield(); | ||
1358 | continue; | ||
1359 | } | ||
1360 | else | ||
1361 | { | ||
1362 | /* Some other unrecoverable error */ | ||
1363 | DEBUGF("Unrecoverable error\n"); | ||
1364 | } | ||
1365 | |||
1366 | break; | ||
1367 | } | ||
1368 | |||
1369 | mad_errors = 0; /* Clear errors */ | ||
1370 | |||
1371 | /* Generate the pcm samples */ | ||
1372 | mad_synth_frame(&synth, &frame); | ||
1373 | |||
1374 | /** Output **/ | ||
1375 | |||
1376 | /* TODO: Output through core dsp. We'll still use our own PCM buffer | ||
1377 | since the core pcm buffer has no timestamping or clock facilities */ | ||
1378 | |||
1379 | /* Add a frame of audio to the pcm buffer. Maximum is 1152 samples. */ | ||
1380 | if (synth.pcm.length > 0) | ||
1381 | { | ||
1382 | int16_t *audio_data = (int16_t *)pcmbuf_head->data; | ||
1383 | size_t size = sizeof (*pcmbuf_head) + synth.pcm.length*4; | ||
1384 | size_t wait_for = size + 32*1024; | ||
1385 | |||
1386 | /* Leave at least 32KB free (this will be the currently | ||
1387 | playing chunk) */ | ||
1388 | while (pcmbuf_used() + wait_for > PCMBUFFER_SIZE) | ||
1389 | { | ||
1390 | if (str_have_msg(&audio_str)) | ||
1391 | { | ||
1392 | struct queue_event ev; | ||
1393 | str_look_msg(&audio_str, &ev); | ||
1394 | |||
1395 | if (ev.id == STREAM_QUIT) | ||
1396 | goto audio_thread_quit; | ||
1397 | } | ||
1398 | |||
1399 | rb->priority_yield(); | ||
1400 | } | ||
1401 | |||
1402 | if (video_sync_start == 0 && | ||
1403 | pts->pts+(uint32_t)synth.pcm.length<audio_sync_time) { | ||
1404 | synth.pcm.length = 0; | ||
1405 | size = 0; | ||
1406 | rb->yield(); | ||
1407 | } | ||
1408 | |||
1409 | /* TODO: This part will be replaced with dsp calls soon */ | ||
1410 | if (MAD_NCHANNELS(&frame.header) == 2) | ||
1411 | { | ||
1412 | int32_t *left = &synth.pcm.samples[0][0]; | ||
1413 | int32_t *right = &synth.pcm.samples[1][0]; | ||
1414 | int i = synth.pcm.length; | ||
1415 | |||
1416 | do | ||
1417 | { | ||
1418 | /* libmad outputs s3.28 */ | ||
1419 | *audio_data++ = clip_sample(*left++ >> 13); | ||
1420 | *audio_data++ = clip_sample(*right++ >> 13); | ||
1421 | } | ||
1422 | while (--i > 0); | ||
1423 | } | ||
1424 | else /* mono */ | ||
1425 | { | ||
1426 | int32_t *mono = &synth.pcm.samples[0][0]; | ||
1427 | int i = synth.pcm.length; | ||
1428 | |||
1429 | do | ||
1430 | { | ||
1431 | int32_t s = clip_sample(*mono++ >> 13); | ||
1432 | *audio_data++ = s; | ||
1433 | *audio_data++ = s; | ||
1434 | } | ||
1435 | while (--i > 0); | ||
1436 | } | ||
1437 | /**/ | ||
1438 | |||
1439 | pcmbuf_head->time = pts->pts; | ||
1440 | pcmbuf_head->size = size; | ||
1441 | |||
1442 | /* As long as we're on this timestamp, the time is just incremented | ||
1443 | by the number of samples */ | ||
1444 | pts->pts += synth.pcm.length; | ||
1445 | |||
1446 | pcm_advance_buffer(&pcmbuf_head, size); | ||
1447 | |||
1448 | if (pcmbuf_threshold != PCMBUF_PLAY_ALL && pcmbuf_used() >= 64*1024) | ||
1449 | { | ||
1450 | /* We've reached our size treshold so start playing back the | ||
1451 | audio in the buffer and set the buffer to play all data */ | ||
1452 | audio_str.status = STREAM_PLAYING; | ||
1453 | pcmbuf_threshold = PCMBUF_PLAY_ALL; | ||
1454 | pcm_playback_seek_time(pcmbuf_tail->time); | ||
1455 | video_sync_start = 1; | ||
1456 | } | ||
1457 | |||
1458 | /* Make this data available to DMA */ | ||
1459 | pcmbuf_written += size; | ||
1460 | } | ||
1461 | |||
1462 | rb->yield(); | ||
1463 | } /* end decoding loop */ | ||
1464 | |||
1465 | done: | ||
1466 | if (audio_str.status == STREAM_STOPPED) | ||
1467 | goto audio_thread_quit; | ||
1468 | |||
1469 | /* Force any residue to play if audio ended before reaching the | ||
1470 | threshold */ | ||
1471 | if (pcmbuf_threshold != PCMBUF_PLAY_ALL && pcmbuf_used() > 0) | ||
1472 | { | ||
1473 | pcm_playback_play(pcmbuf_tail->time); | ||
1474 | pcmbuf_threshold = PCMBUF_PLAY_ALL; | ||
1475 | } | ||
1476 | |||
1477 | if (rb->pcm_is_playing() && !rb->pcm_is_paused()) | ||
1478 | { | ||
1479 | /* Wait for audio to finish */ | ||
1480 | while (pcmbuf_used() > 0) | ||
1481 | { | ||
1482 | if (button_loop() == STREAM_STOPPED) | ||
1483 | goto audio_thread_quit; | ||
1484 | rb->sleep(HZ/10); | ||
1485 | } | ||
1486 | } | ||
1487 | |||
1488 | audio_str.status = STREAM_DONE; | ||
1489 | |||
1490 | /* Process events until finished */ | ||
1491 | while (button_loop() != STREAM_STOPPED) | ||
1492 | rb->sleep(HZ/4); | ||
1493 | |||
1494 | audio_thread_quit: | ||
1495 | pcm_playback_stop(); | ||
1496 | |||
1497 | audio_str.status = STREAM_TERMINATED; | ||
1498 | } | ||
1499 | |||
1500 | /* End of libmad stuff */ | ||
1501 | |||
1502 | /* The audio stack is stolen from the core codec thread (but not in uisim) */ | ||
1503 | #define AUDIO_STACKSIZE (9*1024) | ||
1504 | uint32_t* audio_stack; | ||
1505 | |||
1506 | #ifndef SIMULATOR | ||
1507 | static uint32_t codec_stack_copy[AUDIO_STACKSIZE / sizeof(uint32_t)]; | ||
1508 | #endif | ||
1509 | |||
1510 | /* TODO: Check if 4KB is appropriate - it works for my test streams, | ||
1511 | so maybe we can reduce it. */ | ||
1512 | #define VIDEO_STACKSIZE (4*1024) | ||
1513 | static uint32_t video_stack[VIDEO_STACKSIZE / sizeof(uint32_t)] IBSS_ATTR; | ||
1514 | |||
1515 | static void video_thread(void) | ||
1516 | { | ||
1517 | struct queue_event ev; | ||
1518 | const mpeg2_info_t * info; | ||
1519 | mpeg2_state_t state; | ||
1520 | char str[80]; | ||
1521 | uint32_t curr_time = 0; | ||
1522 | uint32_t period = 0; /* Frame period in clock ticks */ | ||
1523 | uint32_t eta_audio = UINT_MAX, eta_video = 0; | ||
1524 | int32_t eta_early = 0, eta_late = 0; | ||
1525 | int frame_drop_level = 0; | ||
1526 | int skip_level = 0; | ||
1527 | int num_skipped = 0; | ||
1528 | /* Used to decide when to display FPS */ | ||
1529 | unsigned long last_showfps = *rb->current_tick - HZ; | ||
1530 | /* Used to decide whether or not to force a frame update */ | ||
1531 | unsigned long last_render = last_showfps; | ||
1532 | |||
1533 | mpeg2dec = mpeg2_init(); | ||
1534 | if (mpeg2dec == NULL) | ||
1535 | { | ||
1536 | rb->splash(0, "mpeg2_init failed"); | ||
1537 | /* Commit suicide */ | ||
1538 | video_str.status = STREAM_TERMINATED; | ||
1539 | return; | ||
1540 | } | ||
1541 | |||
1542 | /* Clear the display - this is mainly just to indicate that the | ||
1543 | video thread has started successfully. */ | ||
1544 | if (!video_thumb_print) | ||
1545 | { | ||
1546 | rb->lcd_clear_display(); | ||
1547 | rb->lcd_update(); | ||
1548 | } | ||
1549 | |||
1550 | /* Request the first packet data */ | ||
1551 | get_next_data( &video_str, 0 ); | ||
1552 | |||
1553 | if (video_str.curr_packet == NULL) | ||
1554 | goto video_thread_quit; | ||
1555 | |||
1556 | mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); | ||
1557 | total_offset += video_str.curr_packet_end - video_str.curr_packet; | ||
1558 | |||
1559 | info = mpeg2_info (mpeg2dec); | ||
1560 | |||
1561 | while (1) | ||
1562 | { | ||
1563 | /* quickly check mailbox first */ | ||
1564 | if (video_thumb_print) | ||
1565 | { | ||
1566 | if (video_str.status == STREAM_STOPPED) | ||
1567 | break; | ||
1568 | } | ||
1569 | else if (str_have_msg(&video_str)) | ||
1570 | { | ||
1571 | while (1) | ||
1572 | { | ||
1573 | str_get_msg(&video_str, &ev); | ||
1574 | |||
1575 | switch (ev.id) | ||
1576 | { | ||
1577 | case STREAM_QUIT: | ||
1578 | video_str.status = STREAM_STOPPED; | ||
1579 | goto video_thread_quit; | ||
1580 | case STREAM_PAUSE: | ||
1581 | #if NUM_CORES > 1 | ||
1582 | flush_icache(); | ||
1583 | #endif | ||
1584 | video_str.status = STREAM_PAUSED; | ||
1585 | str_reply_msg(&video_str, 1); | ||
1586 | continue; | ||
1587 | } | ||
1588 | |||
1589 | break; | ||
1590 | } | 298 | } |
1591 | 299 | ||
1592 | video_str.status = STREAM_PLAYING; | ||
1593 | str_reply_msg(&video_str, 1); | ||
1594 | } | ||
1595 | |||
1596 | state = mpeg2_parse (mpeg2dec); | ||
1597 | rb->yield(); | ||
1598 | |||
1599 | /* Prevent idle poweroff */ | ||
1600 | rb->reset_poweroff_timer(); | ||
1601 | |||
1602 | switch (state) | ||
1603 | { | ||
1604 | case STATE_BUFFER: | ||
1605 | /* Request next packet data */ | ||
1606 | get_next_data( &video_str, 0 ); | ||
1607 | |||
1608 | mpeg2_buffer (mpeg2dec, video_str.curr_packet, video_str.curr_packet_end); | ||
1609 | total_offset += video_str.curr_packet_end - video_str.curr_packet; | ||
1610 | info = mpeg2_info (mpeg2dec); | ||
1611 | |||
1612 | if (video_str.curr_packet == NULL) | ||
1613 | { | ||
1614 | /* No more data. */ | ||
1615 | goto video_thread_quit; | ||
1616 | } | ||
1617 | continue; | ||
1618 | |||
1619 | case STATE_SEQUENCE: | ||
1620 | /* New GOP, inform output of any changes */ | ||
1621 | vo_setup(info->sequence); | ||
1622 | break; | 300 | break; |
1623 | 301 | } /* MPEG_PAUSE*: */ | |
1624 | case STATE_PICTURE: | 302 | |
1625 | { | 303 | case SYS_POWEROFF: |
1626 | int skip = 0; /* Assume no skip */ | 304 | case SYS_USB_CONNECTED: |
1627 | 305 | /* Stop and get the resume time before closing the file early */ | |
1628 | if (frame_drop_level >= 1 || skip_level > 0) | 306 | stream_stop(); |
1629 | { | 307 | settings.resume_time = stream_get_resume_time(); |
1630 | /* A frame will be dropped in the decoder */ | 308 | stream_close(); |
1631 | 309 | ret = false; | |
1632 | /* Frame type: I/P/B/D */ | 310 | /* Fall-through */ |
1633 | int type = info->current_picture->flags & PIC_MASK_CODING_TYPE; | ||
1634 | |||
1635 | switch (type) | ||
1636 | { | ||
1637 | case PIC_FLAG_CODING_TYPE_I: | ||
1638 | case PIC_FLAG_CODING_TYPE_D: | ||
1639 | /* Level 5: Things are extremely late and all frames will be | ||
1640 | dropped until the next key frame */ | ||
1641 | if (frame_drop_level >= 1) | ||
1642 | frame_drop_level = 0; /* Key frame - reset drop level */ | ||
1643 | if (skip_level >= 5) | ||
1644 | { | ||
1645 | frame_drop_level = 1; | ||
1646 | skip_level = 0; /* reset */ | ||
1647 | } | ||
1648 | break; | ||
1649 | case PIC_FLAG_CODING_TYPE_P: | ||
1650 | /* Level 4: Things are very late and all frames will be | ||
1651 | dropped until the next key frame */ | ||
1652 | if (skip_level >= 4) | ||
1653 | { | ||
1654 | frame_drop_level = 1; | ||
1655 | skip_level = 0; /* reset */ | ||
1656 | } | ||
1657 | break; | ||
1658 | case PIC_FLAG_CODING_TYPE_B: | ||
1659 | /* We want to drop something, so this B frame won't even | ||
1660 | be decoded. Drawing can happen on the next frame if so | ||
1661 | desired. Bring the level down as skips are done. */ | ||
1662 | skip = 1; | ||
1663 | if (skip_level > 0) | ||
1664 | skip_level--; | ||
1665 | } | ||
1666 | |||
1667 | skip |= frame_drop_level; | ||
1668 | } | ||
1669 | |||
1670 | mpeg2_skip(mpeg2dec, skip); | ||
1671 | break; | ||
1672 | } | ||
1673 | |||
1674 | case STATE_SLICE: | ||
1675 | case STATE_END: | ||
1676 | case STATE_INVALID_END: | ||
1677 | { | ||
1678 | int32_t offset; /* Tick adjustment to keep sync */ | ||
1679 | |||
1680 | /* draw current picture */ | ||
1681 | if (!info->display_fbuf) | ||
1682 | break; | ||
1683 | |||
1684 | /* No limiting => no dropping - draw this frame */ | ||
1685 | if (!settings.limitfps && (video_thumb_print == 0)) | ||
1686 | { | ||
1687 | audio_sync_start = 1; | ||
1688 | video_sync_start = 1; | ||
1689 | goto picture_draw; | ||
1690 | } | ||
1691 | |||
1692 | /* Get presentation times in audio samples - quite accurate | ||
1693 | enough - add previous frame duration if not stamped */ | ||
1694 | curr_time = (info->display_picture->flags & PIC_FLAG_TAGS) ? | ||
1695 | TS_TO_TICKS(info->display_picture->tag) : (curr_time + period); | ||
1696 | |||
1697 | period = TIME_TO_TICKS(info->sequence->frame_period); | ||
1698 | |||
1699 | if ( (video_thumb_print == 1 || video_sync_start == 0) && | ||
1700 | ((int)(info->current_picture->flags & PIC_MASK_CODING_TYPE) | ||
1701 | == PIC_FLAG_CODING_TYPE_B)) | ||
1702 | break; | ||
1703 | |||
1704 | eta_video = curr_time; | ||
1705 | |||
1706 | audio_sync_time = eta_video; | ||
1707 | audio_sync_start = 1; | ||
1708 | |||
1709 | while (video_sync_start == 0) | ||
1710 | rb->yield(); | ||
1711 | |||
1712 | eta_audio = get_stream_time(); | ||
1713 | |||
1714 | /* How early/late are we? > 0 = late, < 0 early */ | ||
1715 | offset = eta_audio - eta_video; | ||
1716 | |||
1717 | if (!settings.skipframes) | ||
1718 | { | ||
1719 | /* Make no effort to determine whether this frame should be | ||
1720 | drawn or not since no action can be taken to correct the | ||
1721 | situation. We'll just wait if we're early and correct for | ||
1722 | lateness as much as possible. */ | ||
1723 | if (offset < 0) | ||
1724 | offset = 0; | ||
1725 | |||
1726 | eta_late = AVERAGE(eta_late, offset, 4); | ||
1727 | offset = eta_late; | ||
1728 | |||
1729 | if ((uint32_t)offset > eta_video) | ||
1730 | offset = eta_video; | ||
1731 | |||
1732 | eta_video -= offset; | ||
1733 | goto picture_wait; | ||
1734 | } | ||
1735 | |||
1736 | /** Possibly skip this frame **/ | ||
1737 | |||
1738 | /* Frameskipping has the following order of preference: | ||
1739 | * | ||
1740 | * Frame Type Who Notes/Rationale | ||
1741 | * B decoder arbitrarily drop - no decode or draw | ||
1742 | * Any renderer arbitrarily drop - will be I/D/P | ||
1743 | * P decoder must wait for I/D-frame - choppy | ||
1744 | * I/D decoder must wait for I/D-frame - choppy | ||
1745 | * | ||
1746 | * If a frame can be drawn and it has been at least 1/2 second, | ||
1747 | * the image will be updated no matter how late it is just to | ||
1748 | * avoid looking stuck. | ||
1749 | */ | ||
1750 | |||
1751 | /* If we're late, set the eta to play the frame early so | ||
1752 | we may catch up. If early, especially because of a drop, | ||
1753 | mitigate a "snap" by moving back gradually. */ | ||
1754 | if (offset >= 0) /* late or on time */ | ||
1755 | { | ||
1756 | eta_early = 0; /* Not early now :( */ | ||
1757 | |||
1758 | eta_late = AVERAGE(eta_late, offset, 4); | ||
1759 | offset = eta_late; | ||
1760 | |||
1761 | if ((uint32_t)offset > eta_video) | ||
1762 | offset = eta_video; | ||
1763 | |||
1764 | eta_video -= offset; | ||
1765 | } | ||
1766 | else | ||
1767 | { | ||
1768 | eta_late = 0; /* Not late now :) */ | ||
1769 | |||
1770 | if (offset > eta_early) | ||
1771 | { | ||
1772 | /* Just dropped a frame and we're now early or we're | ||
1773 | coming back from being early */ | ||
1774 | eta_early = offset; | ||
1775 | if ((uint32_t)-offset > eta_video) | ||
1776 | offset = -eta_video; | ||
1777 | |||
1778 | eta_video += offset; | ||
1779 | } | ||
1780 | else | ||
1781 | { | ||
1782 | /* Just early with an offset, do exponential drift back */ | ||
1783 | if (eta_early != 0) | ||
1784 | { | ||
1785 | eta_early = AVERAGE(eta_early, 0, 8); | ||
1786 | eta_video = ((uint32_t)-eta_early > eta_video) ? | ||
1787 | 0 : (eta_video + eta_early); | ||
1788 | } | ||
1789 | |||
1790 | offset = eta_early; | ||
1791 | } | ||
1792 | } | ||
1793 | |||
1794 | if (info->display_picture->flags & PIC_FLAG_SKIP) | ||
1795 | { | ||
1796 | /* This frame was set to skip so skip it after having updated | ||
1797 | timing information */ | ||
1798 | num_skipped++; | ||
1799 | eta_early = INT32_MIN; | ||
1800 | goto picture_skip; | ||
1801 | } | ||
1802 | |||
1803 | if (skip_level == 3 && TIME_BEFORE(*rb->current_tick, last_render + HZ/2)) | ||
1804 | { | ||
1805 | /* Render drop was set previously but nothing was dropped in the | ||
1806 | decoder or it's been to long since drawing the last frame. */ | ||
1807 | skip_level = 0; | ||
1808 | num_skipped++; | ||
1809 | eta_early = INT32_MIN; | ||
1810 | goto picture_skip; | ||
1811 | } | ||
1812 | |||
1813 | /* At this point a frame _will_ be drawn - a skip may happen on | ||
1814 | the next however */ | ||
1815 | skip_level = 0; | ||
1816 | |||
1817 | if (offset > CLOCK_RATE*110/1000) | ||
1818 | { | ||
1819 | /* Decide which skip level is needed in order to catch up */ | ||
1820 | |||
1821 | /* TODO: Calculate this rather than if...else - this is rather | ||
1822 | exponential though */ | ||
1823 | if (offset > CLOCK_RATE*367/1000) | ||
1824 | skip_level = 5; /* Decoder skip: I/D */ | ||
1825 | if (offset > CLOCK_RATE*233/1000) | ||
1826 | skip_level = 4; /* Decoder skip: P */ | ||
1827 | else if (offset > CLOCK_RATE*167/1000) | ||
1828 | skip_level = 3; /* Render skip */ | ||
1829 | else if (offset > CLOCK_RATE*133/1000) | ||
1830 | skip_level = 2; /* Decoder skip: B */ | ||
1831 | else | ||
1832 | skip_level = 1; /* Decoder skip: B */ | ||
1833 | } | ||
1834 | |||
1835 | picture_wait: | ||
1836 | /* Wait until audio catches up */ | ||
1837 | if (video_thumb_print) | ||
1838 | video_str.status = STREAM_STOPPED; | ||
1839 | else | ||
1840 | while (eta_video > eta_audio) | ||
1841 | { | ||
1842 | rb->priority_yield(); | ||
1843 | |||
1844 | /* Make sure not to get stuck waiting here forever */ | ||
1845 | if (str_have_msg(&video_str)) | ||
1846 | { | ||
1847 | str_look_msg(&video_str, &ev); | ||
1848 | |||
1849 | /* If not to play, process up top */ | ||
1850 | if (ev.id != STREAM_PLAY) | ||
1851 | goto rendering_finished; | ||
1852 | |||
1853 | /* Told to play but already playing */ | ||
1854 | str_get_msg(&video_str, &ev); | ||
1855 | str_reply_msg(&video_str, 1); | ||
1856 | } | ||
1857 | |||
1858 | eta_audio = get_stream_time(); | ||
1859 | } | ||
1860 | |||
1861 | picture_draw: | ||
1862 | /* Record last frame time */ | ||
1863 | last_render = *rb->current_tick; | ||
1864 | |||
1865 | if (video_thumb_print) | ||
1866 | vo_draw_frame_thumb(info->display_fbuf->buf); | ||
1867 | else | ||
1868 | vo_draw_frame(info->display_fbuf->buf); | ||
1869 | |||
1870 | num_drawn++; | ||
1871 | |||
1872 | picture_skip: | ||
1873 | if (!settings.showfps) | ||
1874 | break; | ||
1875 | |||
1876 | /* Calculate and display fps */ | ||
1877 | if (TIME_AFTER(*rb->current_tick, last_showfps + HZ)) | ||
1878 | { | ||
1879 | uint32_t clock_ticks = get_playback_time() - count_start; | ||
1880 | int fps = 0; | ||
1881 | |||
1882 | if (clock_ticks != 0) | ||
1883 | fps = num_drawn*CLOCK_RATE*10ll / clock_ticks; | ||
1884 | |||
1885 | rb->snprintf(str, sizeof(str), "%d.%d %d %d ", | ||
1886 | fps / 10, fps % 10, num_skipped, | ||
1887 | info->display_picture->temporal_reference); | ||
1888 | rb->lcd_putsxy(0, 0, str); | ||
1889 | rb->lcd_update_rect(0, 0, LCD_WIDTH, 8); | ||
1890 | |||
1891 | last_showfps = *rb->current_tick; | ||
1892 | } | ||
1893 | break; | ||
1894 | } | ||
1895 | |||
1896 | default: | 311 | default: |
312 | rb->default_event_handler(button); | ||
1897 | break; | 313 | break; |
1898 | } | 314 | } |
1899 | rendering_finished: | ||
1900 | 315 | ||
1901 | rb->yield(); | 316 | rb->yield(); |
1902 | } | 317 | } /* end while */ |
1903 | |||
1904 | video_thread_quit: | ||
1905 | /* if video ends before time sync'd, | ||
1906 | besure the audio thread is closed */ | ||
1907 | if (video_sync_start == 0) | ||
1908 | { | ||
1909 | audio_str.status = STREAM_STOPPED; | ||
1910 | audio_sync_start = 1; | ||
1911 | } | ||
1912 | |||
1913 | #if NUM_CORES > 1 | ||
1914 | flush_icache(); | ||
1915 | #endif | ||
1916 | |||
1917 | mpeg2_close (mpeg2dec); | ||
1918 | |||
1919 | /* Commit suicide */ | ||
1920 | video_str.status = STREAM_TERMINATED; | ||
1921 | } | ||
1922 | |||
1923 | void initialize_stream( Stream *str, uint8_t *buffer_start, size_t disk_buf_len, int id ) | ||
1924 | { | ||
1925 | str->curr_packet_end = str->curr_packet = NULL; | ||
1926 | str->prev_packet_length = 0; | ||
1927 | str->prev_packet = str->curr_packet_end = buffer_start; | ||
1928 | str->buffer_remaining = disk_buf_len; | ||
1929 | str->id = id; | ||
1930 | } | ||
1931 | |||
1932 | void display_thumb(int in_file) | ||
1933 | { | ||
1934 | size_t disk_buf_len; | ||
1935 | |||
1936 | video_thumb_print = 1; | ||
1937 | audio_sync_start = 1; | ||
1938 | video_sync_start = 1; | ||
1939 | |||
1940 | disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); | ||
1941 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
1942 | file_remaining = 0; | ||
1943 | initialize_stream(&video_str,disk_buf_start,disk_buf_len,0xe0); | ||
1944 | |||
1945 | video_str.status = STREAM_PLAYING; | ||
1946 | |||
1947 | if ((video_str.thread = rb->create_thread(video_thread, | ||
1948 | (uint8_t*)video_stack,VIDEO_STACKSIZE, 0,"mpgvideo" | ||
1949 | IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, COP))) == NULL) | ||
1950 | { | ||
1951 | rb->splash(HZ, "Cannot create video thread!"); | ||
1952 | } | ||
1953 | else | ||
1954 | { | ||
1955 | rb->thread_wait(video_str.thread); | ||
1956 | } | ||
1957 | 318 | ||
1958 | if ( video_str.curr_packet_end == video_str.curr_packet) | 319 | rb->lcd_setfont(FONT_UI); |
1959 | rb->splash(0, "frame not available"); | ||
1960 | } | ||
1961 | 320 | ||
1962 | int find_start_pts( int in_file ) | 321 | return ret; |
1963 | { | ||
1964 | uint8_t *p; | ||
1965 | size_t read_length = 60*1024; | ||
1966 | size_t disk_buf_len; | ||
1967 | |||
1968 | start_pts_time = 0; | ||
1969 | |||
1970 | /* temporary read buffer size cannot exceed buffer size */ | ||
1971 | if ( read_length > disk_buf_size ) | ||
1972 | read_length = disk_buf_size; | ||
1973 | |||
1974 | /* read tail of file */ | ||
1975 | rb->lseek( in_file, 0, SEEK_SET ); | ||
1976 | disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); | ||
1977 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
1978 | |||
1979 | /* sync reader to this segment of the stream */ | ||
1980 | p=disk_buf_start; | ||
1981 | if (sync_data_stream(&p)) | ||
1982 | { | ||
1983 | DEBUGF("Could not sync stream\n"); | ||
1984 | return PLUGIN_ERROR; | ||
1985 | } | ||
1986 | |||
1987 | /* find first PTS in audio stream. if the PTS can not be determined, | ||
1988 | set start_pts_time to 0 */ | ||
1989 | audio_sync_start = 0; | ||
1990 | audio_sync_time = 0; | ||
1991 | video_sync_start = 0; | ||
1992 | { | ||
1993 | Stream tmp; | ||
1994 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
1995 | int count=0; | ||
1996 | do | ||
1997 | { | ||
1998 | count++; | ||
1999 | get_next_data(&tmp, 2); | ||
2000 | } | ||
2001 | while (tmp.tagged != 1 && count < 30); | ||
2002 | if (tmp.tagged == 1) | ||
2003 | start_pts_time = (int)((tmp.curr_pts/45000)/30); | ||
2004 | } | ||
2005 | return 0; | ||
2006 | } | 322 | } |
2007 | 323 | ||
2008 | int find_end_pts( int in_file ) | ||
2009 | { | ||
2010 | uint8_t *p; | ||
2011 | size_t read_length = 60*1024; | ||
2012 | size_t disk_buf_len; | ||
2013 | |||
2014 | end_pts_time = 0; | ||
2015 | |||
2016 | /* temporary read buffer size cannot exceed buffer size */ | ||
2017 | if ( read_length > disk_buf_size ) | ||
2018 | read_length = disk_buf_size; | ||
2019 | |||
2020 | /* read tail of file */ | ||
2021 | rb->lseek( in_file, -1*read_length, SEEK_END ); | ||
2022 | disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); | ||
2023 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2024 | |||
2025 | /* sync reader to this segment of the stream */ | ||
2026 | p=disk_buf_start; | ||
2027 | if (sync_data_stream(&p)) | ||
2028 | { | ||
2029 | DEBUGF("Could not sync stream\n"); | ||
2030 | return PLUGIN_ERROR; | ||
2031 | } | ||
2032 | |||
2033 | /* find last PTS in audio stream; will movie always have audio? if | ||
2034 | the play time can not be determined, set end_pts_time to 0 */ | ||
2035 | audio_sync_start = 0; | ||
2036 | audio_sync_time = 0; | ||
2037 | video_sync_start = 0; | ||
2038 | { | ||
2039 | Stream tmp; | ||
2040 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
2041 | |||
2042 | do | ||
2043 | { | ||
2044 | get_next_data(&tmp, 2); | ||
2045 | if (tmp.tagged == 1) | ||
2046 | /* 10 sec less to insure the video frame exist */ | ||
2047 | end_pts_time = (int)((tmp.curr_pts/45000-10)/30); | ||
2048 | } | ||
2049 | while (tmp.curr_packet_end != NULL); | ||
2050 | } | ||
2051 | return 0; | ||
2052 | } | ||
2053 | |||
2054 | ssize_t seek_PTS( int in_file, int start_time, int accept_button ) | ||
2055 | { | ||
2056 | static ssize_t last_seek_pos = 0; | ||
2057 | static int last_start_time = 0; | ||
2058 | ssize_t seek_pos; | ||
2059 | size_t disk_buf_len; | ||
2060 | uint8_t *p; | ||
2061 | size_t read_length = 60*1024; | ||
2062 | |||
2063 | /* temporary read buffer size cannot exceed buffer size */ | ||
2064 | if ( read_length > disk_buf_size ) | ||
2065 | read_length = disk_buf_size; | ||
2066 | |||
2067 | if ( start_time == last_start_time ) | ||
2068 | { | ||
2069 | seek_pos = last_seek_pos; | ||
2070 | rb->lseek(in_file,seek_pos,SEEK_SET); | ||
2071 | } | ||
2072 | else if ( start_time != 0 ) | ||
2073 | { | ||
2074 | seek_pos = rb->filesize(in_file)*start_time/ | ||
2075 | (end_pts_time-start_pts_time); | ||
2076 | int seek_pos_sec_inc = rb->filesize(in_file)/ | ||
2077 | (end_pts_time-start_pts_time)/30; | ||
2078 | |||
2079 | if (seek_pos<0) | ||
2080 | seek_pos=0; | ||
2081 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2082 | seek_pos = rb->filesize(in_file) - read_length; | ||
2083 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2084 | disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); | ||
2085 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2086 | |||
2087 | /* sync reader to this segment of the stream */ | ||
2088 | p=disk_buf_start; | ||
2089 | if (sync_data_stream(&p)) | ||
2090 | { | ||
2091 | DEBUGF("Could not sync stream\n"); | ||
2092 | return PLUGIN_ERROR; | ||
2093 | } | ||
2094 | |||
2095 | /* find PTS >= start_time */ | ||
2096 | audio_sync_start = 0; | ||
2097 | audio_sync_time = 0; | ||
2098 | video_sync_start = 0; | ||
2099 | { | ||
2100 | Stream tmp; | ||
2101 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
2102 | int cont_seek_loop = 1; | ||
2103 | int coarse_seek = 1; | ||
2104 | do | ||
2105 | { | ||
2106 | if ( accept_button ) | ||
2107 | { | ||
2108 | rb->yield(); | ||
2109 | if (rb->button_queue_count()) | ||
2110 | return -101; | ||
2111 | } | ||
2112 | |||
2113 | while ( get_next_data(&tmp, 1) == 1 ) | ||
2114 | { | ||
2115 | if ( tmp.curr_packet_end == disk_buf_start ) | ||
2116 | seek_pos += disk_buf_tail - disk_buf_start; | ||
2117 | else | ||
2118 | seek_pos += tmp.curr_packet_end - disk_buf_start; | ||
2119 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2120 | seek_pos = rb->filesize(in_file) - read_length; | ||
2121 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2122 | disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); | ||
2123 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2124 | |||
2125 | /* sync reader to this segment of the stream */ | ||
2126 | p=disk_buf_start; | ||
2127 | initialize_stream(&tmp,p,disk_buf_len,0xc0); | ||
2128 | } | ||
2129 | |||
2130 | /* are we after start_time in the stream? */ | ||
2131 | if ( coarse_seek && (int)(tmp.curr_pts/45000) >= | ||
2132 | (start_time+start_pts_time)*30 ) | ||
2133 | { | ||
2134 | int time_to_backup = (int)(tmp.curr_pts/45000) - | ||
2135 | (start_time+start_pts_time)*30; | ||
2136 | if (time_to_backup == 0) | ||
2137 | time_to_backup++; | ||
2138 | seek_pos -= seek_pos_sec_inc * time_to_backup; | ||
2139 | seek_pos_sec_inc -= seek_pos_sec_inc/20; /* for stability */ | ||
2140 | if (seek_pos<0) | ||
2141 | seek_pos=0; | ||
2142 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2143 | seek_pos = rb->filesize(in_file) - read_length; | ||
2144 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2145 | disk_buf_len = rb->read( in_file, disk_buf_start, read_length ); | ||
2146 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2147 | |||
2148 | /* sync reader to this segment of the stream */ | ||
2149 | p=disk_buf_start; | ||
2150 | if (sync_data_stream(&p)) | ||
2151 | { | ||
2152 | DEBUGF("Could not sync stream\n"); | ||
2153 | return PLUGIN_ERROR; | ||
2154 | } | ||
2155 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
2156 | continue; | ||
2157 | } | ||
2158 | |||
2159 | /* are we well before start_time in the stream? */ | ||
2160 | if ( coarse_seek && (start_time+start_pts_time)*30 - | ||
2161 | (int)(tmp.curr_pts/45000) > 2 ) | ||
2162 | { | ||
2163 | int time_to_advance = (start_time+start_pts_time)*30 - | ||
2164 | (int)(tmp.curr_pts/45000) - 2; | ||
2165 | if (time_to_advance <= 0) | ||
2166 | time_to_advance = 1; | ||
2167 | seek_pos += seek_pos_sec_inc * time_to_advance; | ||
2168 | if (seek_pos<0) | ||
2169 | seek_pos=0; | ||
2170 | if ((size_t)seek_pos > rb->filesize(in_file) - read_length) | ||
2171 | seek_pos = rb->filesize(in_file) - read_length; | ||
2172 | rb->lseek( in_file, seek_pos, SEEK_SET ); | ||
2173 | disk_buf_len = rb->read ( in_file, disk_buf_start, read_length ); | ||
2174 | disk_buf_tail = disk_buf_start + disk_buf_len; | ||
2175 | |||
2176 | /* sync reader to this segment of the stream */ | ||
2177 | p=disk_buf_start; | ||
2178 | if (sync_data_stream(&p)) | ||
2179 | { | ||
2180 | DEBUGF("Could not sync stream\n"); | ||
2181 | return PLUGIN_ERROR; | ||
2182 | } | ||
2183 | initialize_stream(&tmp,p,disk_buf_len-(disk_buf_start-p),0xc0); | ||
2184 | continue; | ||
2185 | } | ||
2186 | |||
2187 | coarse_seek = 0; | ||
2188 | |||
2189 | /* are we at start_time in the stream? */ | ||
2190 | if ( (int)(tmp.curr_pts/45000) >= (start_time+start_pts_time)* | ||
2191 | 30 ) | ||
2192 | cont_seek_loop = 0; | ||
2193 | |||
2194 | } | ||
2195 | while ( cont_seek_loop ); | ||
2196 | |||
2197 | |||
2198 | DEBUGF("start diff: %u %u\n",(unsigned int)(tmp.curr_pts/45000), | ||
2199 | (start_time+start_pts_time)*30); | ||
2200 | seek_pos+=tmp.curr_packet_end-disk_buf_start; | ||
2201 | |||
2202 | last_seek_pos = seek_pos; | ||
2203 | last_start_time = start_time; | ||
2204 | |||
2205 | rb->lseek(in_file,seek_pos,SEEK_SET); | ||
2206 | } | ||
2207 | } | ||
2208 | else | ||
2209 | { | ||
2210 | seek_pos = 0; | ||
2211 | rb->lseek(in_file,0,SEEK_SET); | ||
2212 | last_seek_pos = seek_pos; | ||
2213 | last_start_time = start_time; | ||
2214 | } | ||
2215 | return seek_pos; | ||
2216 | } | ||
2217 | |||
2218 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | 324 | enum plugin_status plugin_start(struct plugin_api* api, void* parameter) |
2219 | { | 325 | { |
2220 | int status = PLUGIN_ERROR; /* assume failure */ | 326 | int status = PLUGIN_ERROR; /* assume failure */ |
2221 | int result; | 327 | int result; |
2222 | int start_time = -1; | 328 | int err; |
2223 | void* audiobuf; | 329 | const char *errstring; |
2224 | ssize_t audiosize; | ||
2225 | int in_file; | ||
2226 | size_t disk_buf_len; | ||
2227 | ssize_t seek_pos; | ||
2228 | size_t audio_stack_size = 0; /* Keep gcc happy and init */ | ||
2229 | int i; | ||
2230 | #ifndef HAVE_LCD_COLOR | ||
2231 | long graysize; | ||
2232 | int grayscales; | ||
2233 | #endif | ||
2234 | |||
2235 | audio_sync_start = 0; | ||
2236 | audio_sync_time = 0; | ||
2237 | video_sync_start = 0; | ||
2238 | 330 | ||
2239 | if (parameter == NULL) | 331 | if (parameter == NULL) { |
2240 | { | 332 | /* No file = GTFO */ |
2241 | api->splash(HZ*2, "No File"); | 333 | api->splash(HZ*2, "No File"); |
2242 | return PLUGIN_ERROR; | 334 | return PLUGIN_ERROR; |
2243 | } | 335 | } |
336 | |||
337 | /* Disable all talking before initializing IRAM */ | ||
2244 | api->talk_disable(true); | 338 | api->talk_disable(true); |
2245 | 339 | ||
2246 | /* Initialize IRAM - stops audio and voice as well */ | 340 | /* Initialize IRAM - stops audio and voice as well */ |
2247 | PLUGIN_IRAM_INIT(api) | 341 | PLUGIN_IRAM_INIT(api) |
2248 | 342 | ||
2249 | rb = api; | 343 | rb = api; |
2250 | rb->splash(0, "Loading..."); | ||
2251 | |||
2252 | /* sets audiosize and returns buffer pointer */ | ||
2253 | audiobuf = rb->plugin_get_audio_buffer(&audiosize); | ||
2254 | |||
2255 | #if INPUT_SRC_CAPS != 0 | ||
2256 | /* Select playback */ | ||
2257 | rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); | ||
2258 | rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); | ||
2259 | #endif | ||
2260 | |||
2261 | rb->pcm_set_frequency(SAMPR_44); | ||
2262 | |||
2263 | #ifndef HAVE_LCD_COLOR | ||
2264 | /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */ | ||
2265 | grayscales = gray_init(rb, audiobuf, audiosize, false, LCD_WIDTH, LCD_HEIGHT, | ||
2266 | 32, 2<<8, &graysize) + 1; | ||
2267 | audiobuf += graysize; | ||
2268 | audiosize -= graysize; | ||
2269 | if (grayscales < 33 || audiosize <= 0) | ||
2270 | { | ||
2271 | rb->talk_disable(false); | ||
2272 | rb->splash(HZ, "gray buf error"); | ||
2273 | return PLUGIN_ERROR; | ||
2274 | } | ||
2275 | #endif | ||
2276 | |||
2277 | /* Initialise our malloc buffer */ | ||
2278 | audiosize = mpeg_alloc_init(audiobuf,audiosize, LIBMPEG2BUFFER_SIZE); | ||
2279 | if (audiosize == 0) | ||
2280 | { | ||
2281 | rb->talk_disable(false); | ||
2282 | return PLUGIN_ERROR; | ||
2283 | } | ||
2284 | |||
2285 | /* Set disk pointers to NULL */ | ||
2286 | disk_buf_end = disk_buf_start = NULL; | ||
2287 | |||
2288 | /* Grab most of the buffer for the compressed video - leave some for | ||
2289 | PCM audio data and some for libmpeg2 malloc use. */ | ||
2290 | disk_buf_size = audiosize - (PCMBUFFER_SIZE+PCMBUFFER_GUARD_SIZE+ | ||
2291 | MPABUF_SIZE); | ||
2292 | |||
2293 | DEBUGF("audiosize=%ld, disk_buf_size=%ld\n",audiosize,disk_buf_size); | ||
2294 | disk_buf_start = mpeg_malloc(disk_buf_size,-1); | ||
2295 | |||
2296 | if (disk_buf_start == NULL) | ||
2297 | { | ||
2298 | rb->talk_disable(false); | ||
2299 | return PLUGIN_ERROR; | ||
2300 | } | ||
2301 | |||
2302 | if (!init_mpabuf()) | ||
2303 | { | ||
2304 | rb->talk_disable(false); | ||
2305 | return PLUGIN_ERROR; | ||
2306 | } | ||
2307 | if (!init_pcmbuf()) | ||
2308 | { | ||
2309 | rb->talk_disable(false); | ||
2310 | return PLUGIN_ERROR; | ||
2311 | } | ||
2312 | |||
2313 | /* The remaining buffer is for use by libmpeg2 */ | ||
2314 | |||
2315 | /* Open the video file */ | ||
2316 | in_file = rb->open((char*)parameter,O_RDONLY); | ||
2317 | |||
2318 | if (in_file < 0){ | ||
2319 | DEBUGF("Could not open %s\n",(char*)parameter); | ||
2320 | rb->talk_disable(false); | ||
2321 | return PLUGIN_ERROR; | ||
2322 | } | ||
2323 | filename = (char*)parameter; | ||
2324 | 344 | ||
2325 | #ifdef HAVE_LCD_COLOR | 345 | #ifdef HAVE_LCD_COLOR |
2326 | rb->lcd_set_backdrop(NULL); | 346 | rb->lcd_set_backdrop(NULL); |
@@ -2328,239 +348,62 @@ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) | |||
2328 | rb->lcd_set_background(LCD_BLACK); | 348 | rb->lcd_set_background(LCD_BLACK); |
2329 | #endif | 349 | #endif |
2330 | 350 | ||
2331 | init_settings((char*)parameter); | ||
2332 | |||
2333 | /* Initialise libmad */ | ||
2334 | rb->memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap)); | ||
2335 | init_mad(mad_frame_overlap); | ||
2336 | |||
2337 | disk_buf_end = disk_buf_start + disk_buf_size-MPEG_GUARDBUF_SIZE; | ||
2338 | |||
2339 | /* initalize start_pts_time and end_pts_time with the length (in half | ||
2340 | minutes) of the movie. zero if the time could not be determined */ | ||
2341 | find_start_pts( in_file ); | ||
2342 | find_end_pts( in_file ); | ||
2343 | |||
2344 | |||
2345 | /* start menu */ | ||
2346 | rb->lcd_clear_display(); | 351 | rb->lcd_clear_display(); |
2347 | rb->lcd_update(); | 352 | rb->lcd_update(); |
2348 | result = mpeg_start_menu(end_pts_time-start_pts_time, in_file); | ||
2349 | |||
2350 | switch (result) | ||
2351 | { | ||
2352 | case MPEG_START_QUIT: | ||
2353 | rb->talk_disable(false); | ||
2354 | return 0; | ||
2355 | default: | ||
2356 | start_time = settings.resume_time; | ||
2357 | break; | ||
2358 | } | ||
2359 | |||
2360 | /* basic time checks */ | ||
2361 | if ( start_time < 0 ) | ||
2362 | start_time = 0; | ||
2363 | else if ( start_time > (end_pts_time-start_pts_time) ) | ||
2364 | start_time = (end_pts_time-start_pts_time); | ||
2365 | |||
2366 | /* Turn off backlight timeout */ | ||
2367 | backlight_force_on(rb); /* backlight control in lib/helper.c */ | ||
2368 | |||
2369 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
2370 | rb->cpu_boost(true); | ||
2371 | #endif | ||
2372 | |||
2373 | /* From this point on we've altered settings, colors, cpu_boost, etc. and | ||
2374 | cannot just return PLUGIN_ERROR - instead drop though to cleanup code | ||
2375 | */ | ||
2376 | |||
2377 | #ifdef SIMULATOR | ||
2378 | /* The simulator thread implementation doesn't have stack buffers, and | ||
2379 | these parameters are ignored. */ | ||
2380 | (void)i; /* Keep gcc happy */ | ||
2381 | audio_stack = NULL; | ||
2382 | audio_stack_size = 0; | ||
2383 | #else | ||
2384 | /* Borrow the codec thread's stack (in IRAM on most targets) */ | ||
2385 | audio_stack = NULL; | ||
2386 | for (i = 0; i < MAXTHREADS; i++) | ||
2387 | { | ||
2388 | if (rb->strcmp(rb->threads[i].name,"codec")==0) | ||
2389 | { | ||
2390 | /* Wait to ensure the codec thread has blocked */ | ||
2391 | while (rb->threads[i].state!=STATE_BLOCKED) | ||
2392 | rb->yield(); | ||
2393 | |||
2394 | /* Now we can steal the stack */ | ||
2395 | audio_stack = rb->threads[i].stack; | ||
2396 | audio_stack_size = rb->threads[i].stack_size; | ||
2397 | |||
2398 | /* Backup the codec thread's stack */ | ||
2399 | rb->memcpy(codec_stack_copy,audio_stack,audio_stack_size); | ||
2400 | |||
2401 | break; | ||
2402 | } | ||
2403 | } | ||
2404 | |||
2405 | if (audio_stack == NULL) | ||
2406 | { | ||
2407 | /* This shouldn't happen, but deal with it anyway by using | ||
2408 | the copy instead */ | ||
2409 | audio_stack = codec_stack_copy; | ||
2410 | audio_stack_size = AUDIO_STACKSIZE; | ||
2411 | } | ||
2412 | #endif | ||
2413 | |||
2414 | rb->splash(0, "Loading..."); | ||
2415 | |||
2416 | /* seek start time */ | ||
2417 | seek_pos = seek_PTS( in_file, start_time, 0 ); | ||
2418 | |||
2419 | video_thumb_print = 0; | ||
2420 | audio_sync_start = 0; | ||
2421 | audio_sync_time = 0; | ||
2422 | video_sync_start = 0; | ||
2423 | 353 | ||
2424 | /* Read some stream data */ | 354 | if (stream_init() < STREAM_OK) { |
2425 | disk_buf_len = rb->read (in_file, disk_buf_start, disk_buf_size - MPEG_GUARDBUF_SIZE); | 355 | DEBUGF("Could not initialize streams\n"); |
356 | } else { | ||
357 | rb->splash(0, "Loading..."); | ||
2426 | 358 | ||
2427 | disk_buf_tail = disk_buf_start + disk_buf_len; | 359 | init_settings((char*)parameter); |
2428 | file_remaining = rb->filesize(in_file); | ||
2429 | file_remaining -= disk_buf_len + seek_pos; | ||
2430 | 360 | ||
2431 | initialize_stream( &video_str, disk_buf_start, disk_buf_len, 0xe0 ); | 361 | err = stream_open((char *)parameter); |
2432 | initialize_stream( &audio_str, disk_buf_start, disk_buf_len, 0xc0 ); | ||
2433 | 362 | ||
2434 | rb->mutex_init(&audio_str.msg_lock); | 363 | if (err >= STREAM_OK) { |
2435 | rb->mutex_init(&video_str.msg_lock); | 364 | /* start menu */ |
365 | rb->lcd_clear_display(); | ||
366 | rb->lcd_update(); | ||
367 | result = mpeg_start_menu(stream_get_duration()); | ||
2436 | 368 | ||
2437 | audio_str.status = STREAM_BUFFERING; | 369 | if (result != MPEG_START_QUIT) { |
2438 | video_str.status = STREAM_PLAYING; | 370 | /* Turn off backlight timeout */ |
371 | /* backlight control in lib/helper.c */ | ||
372 | backlight_force_on(rb); | ||
2439 | 373 | ||
2440 | #ifndef HAVE_LCD_COLOR | 374 | /* Enter button loop and process UI */ |
2441 | gray_show(true); | 375 | if (button_loop()) { |
2442 | #endif | 376 | settings.resume_time = stream_get_resume_time(); |
377 | } | ||
2443 | 378 | ||
2444 | init_stream_lock(); | 379 | /* Turn on backlight timeout (revert to settings) */ |
380 | backlight_use_settings(rb); | ||
381 | } | ||
2445 | 382 | ||
2446 | #if NUM_CORES > 1 | 383 | stream_close(); |
2447 | flush_icache(); | ||
2448 | #endif | ||
2449 | 384 | ||
2450 | /* We put the video thread on the second processor for multi-core targets. */ | 385 | rb->lcd_clear_display(); |
2451 | if ((video_str.thread = rb->create_thread(video_thread, | 386 | rb->lcd_update(); |
2452 | (uint8_t*)video_stack, VIDEO_STACKSIZE, 0, | ||
2453 | "mpgvideo" IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, COP))) == NULL) | ||
2454 | { | ||
2455 | rb->splash(HZ, "Cannot create video thread!"); | ||
2456 | } | ||
2457 | else if ((audio_str.thread = rb->create_thread(audio_thread, | ||
2458 | (uint8_t*)audio_stack,audio_stack_size, 0,"mpgaudio" | ||
2459 | IF_PRIO(,PRIORITY_PLAYBACK) IF_COP(, CPU))) == NULL) | ||
2460 | { | ||
2461 | rb->splash(HZ, "Cannot create audio thread!"); | ||
2462 | } | ||
2463 | else | ||
2464 | { | ||
2465 | rb->lcd_setfont(FONT_SYSFIXED); | ||
2466 | |||
2467 | /* Wait until both threads have finished their work */ | ||
2468 | while ((audio_str.status >= 0) || (video_str.status >= 0)) | ||
2469 | { | ||
2470 | size_t audio_remaining = audio_str.buffer_remaining; | ||
2471 | size_t video_remaining = video_str.buffer_remaining; | ||
2472 | 387 | ||
2473 | if (MIN(audio_remaining,video_remaining) < MPEG_LOW_WATERMARK) | 388 | save_settings(); /* Save settings (if they have changed) */ |
389 | status = PLUGIN_OK; | ||
390 | } else { | ||
391 | DEBUGF("Could not open %s\n", (char*)parameter); | ||
392 | switch (err) | ||
2474 | { | 393 | { |
2475 | 394 | case STREAM_UNSUPPORTED: | |
2476 | size_t bytes_to_read = disk_buf_size - MPEG_GUARDBUF_SIZE - | 395 | errstring = "Unsupported format"; |
2477 | MAX(audio_remaining,video_remaining); | 396 | break; |
2478 | 397 | default: | |
2479 | bytes_to_read = MIN(bytes_to_read,(size_t)(disk_buf_end-disk_buf_tail)); | 398 | errstring = "Error opening file: %d"; |
2480 | |||
2481 | while (( bytes_to_read > 0) && (file_remaining > 0) && | ||
2482 | ((audio_str.status != STREAM_DONE) || (video_str.status != STREAM_DONE))) | ||
2483 | { | ||
2484 | |||
2485 | size_t n; | ||
2486 | if ( video_sync_start != 0 ) | ||
2487 | n = rb->read(in_file, disk_buf_tail, MIN(32*1024,bytes_to_read)); | ||
2488 | else | ||
2489 | { | ||
2490 | n = rb->read(in_file, disk_buf_tail,bytes_to_read); | ||
2491 | if (n==0) | ||
2492 | rb->splash(30,"buffer fill error"); | ||
2493 | } | ||
2494 | |||
2495 | bytes_to_read -= n; | ||
2496 | file_remaining -= n; | ||
2497 | |||
2498 | lock_stream(); | ||
2499 | audio_str.buffer_remaining += n; | ||
2500 | video_str.buffer_remaining += n; | ||
2501 | unlock_stream(); | ||
2502 | |||
2503 | disk_buf_tail += n; | ||
2504 | |||
2505 | rb->yield(); | ||
2506 | } | ||
2507 | |||
2508 | if (disk_buf_tail == disk_buf_end) | ||
2509 | disk_buf_tail = disk_buf_start; | ||
2510 | } | 399 | } |
2511 | 400 | ||
2512 | rb->sleep(HZ/10); | 401 | rb->splash(HZ*2, errstring, err); |
2513 | } | 402 | } |
2514 | |||
2515 | rb->lcd_setfont(FONT_UI); | ||
2516 | status = PLUGIN_OK; | ||
2517 | } | ||
2518 | |||
2519 | /* Stop the threads and wait for them to terminate */ | ||
2520 | if (video_str.thread != NULL) | ||
2521 | { | ||
2522 | str_send_msg(&video_str, STREAM_QUIT, 0); | ||
2523 | rb->thread_wait(video_str.thread); | ||
2524 | } | 403 | } |
2525 | 404 | ||
2526 | if (audio_str.thread != NULL) | 405 | stream_exit(); |
2527 | { | ||
2528 | str_send_msg(&audio_str, STREAM_QUIT, 0); | ||
2529 | rb->thread_wait(audio_str.thread); | ||
2530 | } | ||
2531 | |||
2532 | #if NUM_CORES > 1 | ||
2533 | invalidate_icache(); | ||
2534 | #endif | ||
2535 | |||
2536 | vo_cleanup(); | ||
2537 | |||
2538 | #ifndef HAVE_LCD_COLOR | ||
2539 | gray_release(); | ||
2540 | #endif | ||
2541 | |||
2542 | rb->lcd_clear_display(); | ||
2543 | rb->lcd_update(); | ||
2544 | |||
2545 | mpeg2_close (mpeg2dec); | ||
2546 | |||
2547 | rb->close (in_file); | ||
2548 | |||
2549 | #ifndef SIMULATOR | ||
2550 | /* Restore the codec thread's stack */ | ||
2551 | rb->memcpy(audio_stack, codec_stack_copy, audio_stack_size); | ||
2552 | #endif | ||
2553 | |||
2554 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
2555 | rb->cpu_boost(false); | ||
2556 | #endif | ||
2557 | |||
2558 | save_settings(); /* Save settings (if they have changed) */ | ||
2559 | |||
2560 | rb->pcm_set_frequency(HW_SAMPR_DEFAULT); | ||
2561 | 406 | ||
2562 | /* Turn on backlight timeout (revert to settings) */ | ||
2563 | backlight_use_settings(rb); /* backlight control in lib/helper.c */ | ||
2564 | rb->talk_disable(false); | 407 | rb->talk_disable(false); |
2565 | return status; | 408 | return status; |
2566 | } | 409 | } |