summaryrefslogtreecommitdiff
path: root/apps/plugins/mpegplayer/stream_mgr.c
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-12-29 19:46:35 +0000
commita222f27c4a17ed8f9809cda7861fe5f23d4cc0c1 (patch)
treed393a23d83549f99772bb156e59ffb88725148b6 /apps/plugins/mpegplayer/stream_mgr.c
parent1d0f6b90ff43776e55b4b9f062c9bea3f99aa768 (diff)
downloadrockbox-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/stream_mgr.c')
-rw-r--r--apps/plugins/mpegplayer/stream_mgr.c1096
1 files changed, 1096 insertions, 0 deletions
diff --git a/apps/plugins/mpegplayer/stream_mgr.c b/apps/plugins/mpegplayer/stream_mgr.c
new file mode 100644
index 0000000000..00b96173b6
--- /dev/null
+++ b/apps/plugins/mpegplayer/stream_mgr.c
@@ -0,0 +1,1096 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * AV stream manager implementation
11 *
12 * Copyright (c) 2007 Michael Sevakis
13 *
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "plugin.h"
22#include "mpegplayer.h"
23#include "gray.h"
24#include "mpeg_settings.h"
25
26static struct event_queue stream_mgr_queue NOCACHEBSS_ATTR;
27static struct queue_sender_list stream_mgr_queue_send NOCACHEBSS_ATTR;
28static uint32_t stream_mgr_thread_stack[DEFAULT_STACK_SIZE*2/sizeof(uint32_t)];
29
30struct stream_mgr stream_mgr NOCACHEBSS_ATTR;
31
32/* Forward decs */
33static int stream_on_close(void);
34
35struct str_broadcast_data
36{
37 long cmd; /* Command to send to stream */
38 intptr_t data; /* Data to send with command */
39};
40
41static inline void stream_mgr_lock(void)
42{
43 rb->mutex_lock(&stream_mgr.str_mtx);
44}
45
46static inline void stream_mgr_unlock(void)
47{
48 rb->mutex_unlock(&stream_mgr.str_mtx);
49}
50
51static inline void actl_lock(void)
52{
53 rb->mutex_lock(&stream_mgr.actl_mtx);
54}
55
56static inline void actl_unlock(void)
57{
58 rb->mutex_unlock(&stream_mgr.actl_mtx);
59}
60
61static inline void stream_mgr_post_msg(long id, intptr_t data)
62{
63 rb->queue_post(stream_mgr.q, id, data);
64}
65
66static inline intptr_t stream_mgr_send_msg(long id, intptr_t data)
67{
68 return rb->queue_send(stream_mgr.q, id, data);
69}
70
71static inline void stream_mgr_reply_msg(intptr_t retval)
72{
73 rb->queue_reply(stream_mgr.q, retval);
74}
75
76int str_next_data_not_ready(struct stream *str)
77{
78 /* Save the current window since it actually might be ready by the time
79 * the registration is received by buffering. */
80 off_t win_right = str->hdr.win_right;
81
82 if (str->hdr.win_right < disk_buf.filesize - MIN_BUFAHEAD &&
83 disk_buf.filesize > MIN_BUFAHEAD)
84 {
85 /* Set right edge to where probing left off + the minimum margin */
86 str->hdr.win_right += MIN_BUFAHEAD;
87 }
88 else
89 {
90 /* Request would be passed the end of the file */
91 str->hdr.win_right = disk_buf.filesize;
92 }
93
94 switch (disk_buf_send_msg(DISK_BUF_DATA_NOTIFY, (intptr_t)str))
95 {
96 case DISK_BUF_NOTIFY_OK:
97 /* Was ready - restore window and process */
98 str->hdr.win_right = win_right;
99 return STREAM_OK;
100
101 case DISK_BUF_NOTIFY_ERROR:
102 /* Error - quit parsing */
103 str_end_of_stream(str);
104 return STREAM_DATA_END;
105
106 default:
107 /* Not ready - go wait for notification from buffering. */
108 str->pkt_flags = 0;
109 return STREAM_DATA_NOT_READY;
110 }
111}
112
113void str_data_notify_received(struct stream *str)
114{
115 /* Normalize win_right back to the packet length */
116 if (str->state == SSTATE_END)
117 return;
118
119 if (str->curr_packet == NULL)
120 {
121 /* Nothing was yet parsed since init */
122 str->hdr.win_right = str->hdr.win_left;
123 }
124 else
125 {
126 /* Restore window based upon current packet */
127 str->hdr.win_right = str->hdr.win_left +
128 (str->curr_packet_end - str->curr_packet);
129 }
130}
131
132/* Set stream manager to a "no-file" state */
133static void stream_mgr_init_state(void)
134{
135 stream_mgr.filename = NULL;
136 stream_mgr.resume_time = 0;
137 stream_mgr.seeked = false;
138}
139
140/* Add a stream to the playback pool */
141void stream_add_stream(struct stream *str)
142{
143 actl_lock();
144
145 list_remove_item(&str->l);
146 list_add_item(&stream_mgr.strl, &str->l);
147
148 actl_unlock();
149}
150
151/* Callback for various list-moving operations */
152static bool strl_enum_callback(struct list_item *item, intptr_t data)
153{
154 actl_lock();
155
156 list_remove_item(item);
157
158 if (data == 1)
159 list_add_item(&stream_mgr.actl, item);
160
161 actl_unlock();
162
163 return true;
164}
165
166/* Clear all streams from active and playback pools */
167void stream_remove_streams(void)
168{
169 list_enum_items(&stream_mgr.strl, strl_enum_callback, 0);
170}
171
172/* Move the playback pool to the active list */
173void move_strl_to_actl(void)
174{
175 list_enum_items(&stream_mgr.strl, strl_enum_callback, 1);
176}
177
178/* Remove a stream from the active list and return it to the pool */
179static bool actl_stream_remove(struct stream *str)
180{
181 if (list_is_member(&stream_mgr.actl, &str->l))
182 {
183 actl_lock();
184
185 list_remove_item(&str->l);
186 list_add_item(&stream_mgr.strl, &str->l);
187
188 actl_unlock();
189 return true;
190 }
191
192 return false;
193}
194
195/* Broadcast a message to all active streams */
196static bool actl_stream_broadcast_callback(struct list_item *item,
197 struct str_broadcast_data *sbd)
198{
199 struct stream *str = TYPE_FROM_MEMBER(struct stream, item, l);
200
201 switch (sbd->cmd)
202 {
203 case STREAM_PLAY:
204 case STREAM_PAUSE:
205 break;
206
207 case STREAM_STOP:
208 if (sbd->data != 0)
209 {
210 actl_lock();
211
212 list_remove_item(item);
213 list_add_item(&stream_mgr.strl, item);
214
215 actl_unlock();
216 sbd->data = 0;
217 }
218 break;
219
220 default:
221 return false;
222 }
223
224 str_send_msg(str, sbd->cmd, sbd->data);
225 return true;
226}
227
228static void actl_stream_broadcast(int cmd, intptr_t data)
229{
230 struct str_broadcast_data sbd;
231 sbd.cmd = cmd;
232 sbd.data = data;
233 list_enum_items(&stream_mgr.actl,
234 (list_enum_callback_t)actl_stream_broadcast_callback,
235 (intptr_t)&sbd);
236}
237
238/* Set the current base clock */
239static void set_stream_clock(uint32_t time)
240{
241 /* Fudge: Start clock 100ms early to allow for some filling time */
242 if (time > 100*TS_SECOND/1000)
243 time -= 100*TS_SECOND/1000;
244 else
245 time = 0;
246
247 pcm_output_set_clock(TS_TO_TICKS(time));
248}
249
250/* Return the play time relative to the specified play time */
251static uint32_t time_from_whence(uint32_t time, int whence)
252{
253 int64_t currtime;
254
255 switch (whence)
256 {
257 case SEEK_SET:
258 /* Set the current time (time = unsigned offset from 0) */
259 if (time > str_parser.duration)
260 time = str_parser.duration;
261 break;
262 case SEEK_CUR:
263 /* Seek forward or backward from the current time
264 * (time = signed offset from current) */
265 if (stream_mgr.seeked)
266 currtime = str_parser.last_seek_time;
267 else
268 currtime = TICKS_TO_TS(pcm_output_get_clock());
269
270 currtime -= str_parser.start_pts;
271 currtime += (int32_t)time;
272
273 if (currtime < 0)
274 currtime = 0;
275 else if ((uint64_t)currtime > str_parser.duration)
276 currtime = str_parser.duration;
277
278 time = (uint32_t)currtime;
279 break;
280 case SEEK_END:
281 /* Seek from the end (time = unsigned offset from end) */
282 if (time > str_parser.duration)
283 time = str_parser.duration;
284 time = str_parser.duration - time;
285 break;
286 }
287
288 return time;
289}
290
291/* Handle seeking details if playing or paused */
292static uint32_t stream_seek_intl(uint32_t time, int whence, int status)
293{
294 /* seek start time */
295 bool was_buffering;
296
297 if (status == STREAM_PLAYING)
298 {
299 /* Keep clock from advancing while seeking */
300 pcm_output_play_pause(false);
301 }
302
303 /* Place streams in a non-running state - keep them on actl */
304 actl_stream_broadcast(STREAM_STOP, 0);
305
306 /* Stop all buffering or else risk clobbering random-access data */
307 was_buffering = disk_buf_send_msg(STREAM_STOP, 0);
308
309 time = time_from_whence(time, whence);
310 time = parser_seek_time(time);
311
312 if (status == STREAM_PLAYING)
313 {
314 /* Restart streams if currently playing */
315
316 /* Clear any seeked status */
317 stream_mgr.seeked = false;
318
319 /* Flush old PCM data */
320 pcm_output_flush();
321
322 /* Set the master clock */
323 set_stream_clock(time);
324
325 /* Prepare the parser and associated streams */
326 parser_prepare_streaming();
327
328 /* Start buffer using previous buffering status */
329 disk_buf_send_msg(STREAM_PLAY, was_buffering);
330
331 /* Tell each stream to start - may generate end of stream signals
332 * now - we'll handle this when finished */
333 actl_stream_broadcast(STREAM_PLAY, 0);
334
335 /* Actually start the clock */
336 pcm_output_play_pause(true);
337 }
338 else
339 {
340 /* Performed the seek - leave it at that until restarted */
341 stream_mgr.seeked = true;
342 }
343
344 return time;
345}
346
347/* Handle STREAM_OPEN */
348void stream_on_open(const char *filename)
349{
350 int err = STREAM_ERROR;
351
352 stream_mgr_lock();
353
354 trigger_cpu_boost();
355
356 /* Open the video file */
357 if (disk_buf_open(filename) >= 0)
358 {
359 /* Initialize the parser */
360 err = parser_init_stream();
361
362 if (err >= STREAM_OK)
363 {
364 /* File ok - save the opened filename */
365 stream_mgr.filename = filename;
366 }
367 }
368
369 /* If error - cleanup */
370 if (err < STREAM_OK)
371 stream_on_close();
372
373 cancel_cpu_boost();
374
375 stream_mgr_unlock();
376
377 stream_mgr_reply_msg(err);
378}
379
380/* Handler STREAM_PLAY */
381static void stream_on_play(void)
382{
383 int status = stream_mgr.status;
384
385 stream_mgr_lock();
386
387 if (status == STREAM_STOPPED)
388 {
389 uint32_t start;
390
391 /* We just say we're playing now */
392 stream_mgr.status = STREAM_PLAYING;
393
394 /* Reply with previous state */
395 stream_mgr_reply_msg(status);
396
397 trigger_cpu_boost();
398
399 /* Seek to initial position and set clock to that time */
400
401 /* Save the resume time */
402 start = str_parser.last_seek_time - str_parser.start_pts;
403 stream_mgr.resume_time = start;
404
405 start = stream_seek_intl(start, SEEK_SET, STREAM_STOPPED);
406
407 /* Fill list of all streams that will be playing */
408 move_strl_to_actl();
409
410 /* Clear any seeked status */
411 stream_mgr.seeked = false;
412
413 /* Set the master clock */
414 set_stream_clock(start);
415
416 /* Prepare the parser and associated streams */
417 parser_prepare_streaming();
418
419 /* Force buffering */
420 disk_buf_send_msg(STREAM_PLAY, true);
421
422 /* Tell each stream to start - may generate end of stream signals
423 * now - we'll handle this when finished */
424 actl_stream_broadcast(STREAM_PLAY, 0);
425
426 /* Actually start the clock */
427 pcm_output_play_pause(true);
428 }
429 else
430 {
431 /* Reply with previous state */
432 stream_mgr_reply_msg(status);
433 }
434
435 stream_mgr_unlock();
436}
437
438/* Handle STREAM_PAUSE */
439static void stream_on_pause(void)
440{
441 int status = stream_mgr.status;
442
443 stream_mgr_lock();
444
445 /* Reply with previous state */
446 stream_mgr_reply_msg(status);
447
448 if (status == STREAM_PLAYING)
449 {
450 /* Pause the clock */
451 pcm_output_play_pause(false);
452
453 /* Pause each active stream */
454 actl_stream_broadcast(STREAM_PAUSE, 0);
455
456 /* Pause the disk buffer - buffer may continue filling */
457 disk_buf_send_msg(STREAM_PAUSE, false);
458
459 /* Unboost the CPU */
460 cancel_cpu_boost();
461
462 /* Offically paused */
463 stream_mgr.status = STREAM_PAUSED;
464 }
465
466 stream_mgr_unlock();
467}
468
469/* Handle STREAM_RESUME */
470static void stream_on_resume(void)
471{
472 int status = stream_mgr.status;
473
474 stream_mgr_lock();
475
476 /* Reply with previous state */
477 stream_mgr_reply_msg(status);
478
479 if (status == STREAM_PAUSED)
480 {
481 /* Boost the CPU */
482 trigger_cpu_boost();
483
484 if (stream_mgr.seeked)
485 {
486 /* Have to give the parser notice to sync up streams */
487 stream_mgr.seeked = false;
488
489 /* Flush old PCM data */
490 pcm_output_flush();
491
492 /* Set the master clock */
493 set_stream_clock(str_parser.last_seek_time);
494
495 /* Prepare the parser and associated streams */
496 parser_prepare_streaming();
497 }
498
499 /* Don't force buffering */
500 disk_buf_send_msg(STREAM_PLAY, false);
501
502 /* Tell each stream to start - may generate end of stream signals
503 * now - we'll handle this when finished */
504 actl_stream_broadcast(STREAM_PLAY, 0);
505
506 /* Actually start the clock */
507 pcm_output_play_pause(true);
508
509 /* Officially playing */
510 stream_mgr.status = STREAM_PLAYING;
511 }
512
513 stream_mgr_unlock();
514}
515
516/* Handle STREAM_STOP */
517static void stream_on_stop(bool reply)
518{
519 int status = stream_mgr.status;
520
521 stream_mgr_lock();
522
523 if (reply)
524 stream_mgr_reply_msg(status);
525
526 if (status != STREAM_STOPPED)
527 {
528 /* Not stopped = paused or playing */
529 stream_mgr.seeked = false;
530
531 /* Pause the clock */
532 pcm_output_play_pause(false);
533
534 if (stream_can_seek())
535 {
536 /* Read the current stream time */
537 uint32_t time = TICKS_TO_TS(pcm_output_get_clock());
538
539 /* Assume invalidity */
540 stream_mgr.resume_time = 0;
541
542 if (time >= str_parser.start_pts && time < str_parser.end_pts)
543 {
544 /* Save the current stream time */
545 stream_mgr.resume_time = time - str_parser.start_pts;
546 }
547 }
548
549 /* Stop buffering */
550 disk_buf_send_msg(STREAM_STOP, 0);
551
552 /* Clear any still-active streams and remove from actl */
553 actl_stream_broadcast(STREAM_STOP, 1);
554
555 /* Stop PCM output (and clock) */
556 pcm_output_stop();
557
558 /* Cancel our processor boost */
559 cancel_cpu_boost();
560
561 stream_mgr.status = STREAM_STOPPED;
562 }
563
564 stream_mgr_unlock();
565}
566
567/* Handle STREAM_SEEK */
568static void stream_on_seek(struct stream_seek_data *skd)
569{
570 uint32_t time = skd->time;
571 int whence = skd->whence;
572
573 switch (whence)
574 {
575 case SEEK_SET:
576 case SEEK_CUR:
577 case SEEK_END:
578 if (stream_mgr.filename == NULL)
579 break;
580
581 stream_mgr_reply_msg(STREAM_OK);
582
583 stream_keep_disk_active();
584
585 stream_mgr_lock();
586
587 if (!stream_can_seek())
588 {
589 }
590 else if (stream_mgr.status != STREAM_STOPPED)
591 {
592 if (stream_mgr.status != STREAM_PLAYING)
593 {
594 trigger_cpu_boost();
595 }
596
597 stream_seek_intl(time, whence, stream_mgr.status);
598
599 if (stream_mgr.status != STREAM_PLAYING)
600 {
601 cancel_cpu_boost();
602 }
603 }
604 else
605 {
606 stream_mgr.seeked = true;
607 time = time_from_whence(time, whence);
608 parser_seek_time(time);
609 }
610
611 stream_mgr_unlock();
612 return;
613 }
614
615 stream_mgr_reply_msg(STREAM_ERROR);
616}
617
618/* Handle STREAM_CLOSE */
619static int stream_on_close(void)
620{
621 int status = STREAM_STOPPED;
622
623 stream_mgr_lock();
624
625 /* Any open file? */
626 if (stream_mgr.filename != NULL)
627 {
628 /* Yes - hide video */
629 stream_show_vo(false);
630 /* Stop any playback */
631 status = stream_mgr.status;
632 stream_on_stop(false);
633 /* Tell parser file is finished */
634 parser_close_stream();
635 /* Close file */
636 disk_buf_close();
637 /* Reinitialize manager */
638 stream_mgr_init_state();
639 }
640
641 stream_mgr_unlock();
642
643 return status;
644}
645
646/* Handle STREAM_EV_COMPLETE */
647static void stream_on_ev_complete(struct stream *str)
648{
649 stream_mgr_lock();
650
651 /* Stream is active? */
652 if (actl_stream_remove(str))
653 {
654 /* No - remove this stream from the active list */
655 DEBUGF(" finished: 0x%02x\n", str->id);
656 if (list_is_empty(&stream_mgr.actl))
657 {
658 /* All streams have acked - stop playback */
659 stream_on_stop(false);
660 stream_mgr.resume_time = 0; /* Played to end - no resume */
661 }
662 else
663 {
664 /* Stream is done - stop it and place back in pool */
665 str_send_msg(str, STREAM_STOP, 1);
666 }
667 }
668
669 stream_mgr_unlock();
670}
671
672/* Callback for stream to notify about events internal to them */
673void stream_generate_event(struct stream *str, long id, intptr_t data)
674{
675 if (str == NULL)
676 return;
677
678 switch (id)
679 {
680 case STREAM_EV_COMPLETE:
681 /* The last stream has ended */
682 stream_mgr_post_msg(STREAM_EV_COMPLETE, (intptr_t)str);
683 break;
684 }
685
686 (void)data;
687}
688
689/* Clear any particular notification for which a stream registered */
690void stream_clear_notify(struct stream *str, int for_msg)
691{
692 switch (for_msg)
693 {
694 case DISK_BUF_DATA_NOTIFY:
695 disk_buf_send_msg(DISK_BUF_CLEAR_DATA_NOTIFY, (intptr_t)str);
696 break;
697 }
698}
699
700/* Show/hide the video output */
701bool stream_show_vo(bool show)
702{
703 bool vis;
704 stream_mgr_lock();
705
706 vis = parser_send_video_msg(VIDEO_DISPLAY_SHOW, show);
707#ifndef HAVE_LCD_COLOR
708 GRAY_VIDEO_FLUSH_ICACHE();
709 GRAY_INVALIDATE_ICACHE();
710 gray_show(show);
711 GRAY_FLUSH_ICACHE();
712#endif
713 stream_mgr_unlock();
714
715 return vis;
716}
717
718/* Query the visibility of video output */
719bool stream_vo_is_visible(void)
720{
721 bool vis;
722 stream_mgr_lock();
723 vis = parser_send_video_msg(VIDEO_DISPLAY_IS_VISIBLE, 0);
724 stream_mgr_unlock();
725 return vis;
726}
727
728/* Return the video dimensions */
729bool stream_vo_get_size(struct vo_ext *sz)
730{
731 bool retval = false;
732
733 stream_mgr_lock();
734
735 if (str_parser.dims.w > 0 && str_parser.dims.h > 0)
736 {
737 *sz = str_parser.dims;
738 retval = true;
739 }
740
741 stream_mgr_unlock();
742
743 return retval;
744}
745
746#ifndef HAVE_LCD_COLOR
747/* Set the rectangle for the gray video overlay - clipped to screen */
748bool stream_set_gray_rect(const struct vo_rect *rc)
749{
750 bool retval = false;
751 struct vo_rect rc_gray;
752
753 stream_mgr_lock();
754
755 vo_rect_set_ext(&rc_gray, 0, 0, LCD_WIDTH, LCD_HEIGHT);
756
757 if (vo_rect_intersect(&rc_gray, &rc_gray, rc))
758 {
759 bool vo_vis = stream_show_vo(false);
760
761 GRAY_VIDEO_FLUSH_ICACHE();
762 GRAY_INVALIDATE_ICACHE();
763
764 gray_init(rb, stream_mgr.graymem, stream_mgr.graysize,
765 false, rc_gray.r - rc_gray.l, rc_gray.b - rc_gray.t,
766 32, 2<<8, NULL);
767
768 gray_set_position(rc_gray.l, rc_gray.t);
769 GRAY_FLUSH_ICACHE();
770
771 if (vo_vis)
772 {
773 stream_show_vo(true);
774 }
775 }
776
777 stream_mgr_unlock();
778
779 return retval;
780}
781
782/* Show/hide the gray video overlay (independently of vo visibility). */
783void stream_gray_show(bool show)
784{
785 stream_mgr_lock();
786
787 GRAY_VIDEO_FLUSH_ICACHE();
788 GRAY_INVALIDATE_ICACHE();
789 gray_show(show);
790 GRAY_FLUSH_ICACHE();
791
792 stream_mgr_unlock();
793}
794#endif
795
796/* Display a thumbnail at the last seek point */
797bool stream_display_thumb(const struct vo_rect *rc)
798{
799 bool retval;
800
801 if (rc == NULL)
802 return false;
803
804 stream_mgr_lock();
805
806 stream_mgr.parms.rc = *rc;
807 retval = parser_send_video_msg(VIDEO_PRINT_THUMBNAIL,
808 (intptr_t)&stream_mgr.parms.rc);
809
810 stream_mgr_unlock();
811 return retval;
812}
813
814/* Return the time playback should resume if interrupted */
815uint32_t stream_get_resume_time(void)
816{
817 uint32_t resume_time;
818
819 /* A stop request is async and replies before setting this - must lock */
820 stream_mgr_lock();
821
822 resume_time = stream_mgr.resume_time;
823
824 stream_mgr_unlock();
825
826 return resume_time;
827}
828
829/* Returns the smallest file window that includes all active streams'
830 * windows */
831static bool stream_get_window_callback(struct list_item *item,
832 struct stream_window *sw)
833{
834 struct stream *str = TYPE_FROM_MEMBER(struct stream, item, l);
835 off_t swl = str->hdr.win_left;
836 off_t swr = str->hdr.win_right;
837
838 if (swl < sw->left)
839 sw->left = swl;
840
841 if (swr > sw->right)
842 sw->right = swr;
843
844 return true;
845}
846
847bool stream_get_window(struct stream_window *sw)
848{
849 if (sw == NULL)
850 return false;
851
852 sw->left = LONG_MAX;
853 sw->right = LONG_MIN;
854
855 actl_lock();
856 list_enum_items(&stream_mgr.actl,
857 (list_enum_callback_t)stream_get_window_callback,
858 (intptr_t)sw);
859 actl_unlock();
860
861 return sw->left <= sw->right;
862}
863
864/* Playback control thread */
865static void stream_mgr_thread(void)
866{
867 struct queue_event ev;
868
869 while (1)
870 {
871 rb->queue_wait(stream_mgr.q, &ev);
872
873 switch (ev.id)
874 {
875 case STREAM_OPEN:
876 stream_on_open((const char *)ev.data);
877 break;
878
879 case STREAM_CLOSE:
880 stream_on_close();
881 break;
882
883 case STREAM_PLAY:
884 stream_on_play();
885 break;
886
887 case STREAM_PAUSE:
888 if (ev.data)
889 stream_on_resume();
890 else
891 stream_on_pause();
892 break;
893
894 case STREAM_STOP:
895 stream_on_stop(true);
896 break;
897
898 case STREAM_SEEK:
899 stream_on_seek((struct stream_seek_data *)ev.data);
900 break;
901
902 case STREAM_EV_COMPLETE:
903 stream_on_ev_complete((struct stream *)ev.data);
904 break;
905
906 case STREAM_QUIT:
907 if (stream_mgr.status != STREAM_STOPPED)
908 stream_on_stop(false);
909 return;
910 }
911 }
912}
913
914/* Stream command interface APIs */
915
916/* Opens a new file */
917int stream_open(const char *filename)
918{
919 if (stream_mgr.thread != NULL)
920 return stream_mgr_send_msg(STREAM_OPEN, (intptr_t)filename);
921 return STREAM_ERROR;
922}
923
924/* Plays the current file starting at time 'start' */
925int stream_play(void)
926{
927 if (stream_mgr.thread != NULL)
928 return stream_mgr_send_msg(STREAM_PLAY, 0);
929 return STREAM_ERROR;
930}
931
932/* Pauses playback if playing */
933int stream_pause(void)
934{
935 if (stream_mgr.thread != NULL)
936 return stream_mgr_send_msg(STREAM_PAUSE, false);
937 return STREAM_ERROR;
938}
939
940/* Resumes playback if paused */
941int stream_resume(void)
942{
943 if (stream_mgr.thread != NULL)
944 return stream_mgr_send_msg(STREAM_PAUSE, true);
945 return STREAM_ERROR;
946}
947
948/* Stops playback if not stopped */
949int stream_stop(void)
950{
951 if (stream_mgr.thread != NULL)
952 return stream_mgr_send_msg(STREAM_STOP, 0);
953 return STREAM_ERROR;
954}
955
956/* Seeks playback time to/by the specified time */
957int stream_seek(uint32_t time, int whence)
958{
959 int ret;
960
961 if (stream_mgr.thread == NULL)
962 return STREAM_ERROR;
963
964 stream_mgr_lock();
965
966 stream_mgr.parms.skd.time = time;
967 stream_mgr.parms.skd.whence = whence;
968
969 ret = stream_mgr_send_msg(STREAM_SEEK, (intptr_t)&stream_mgr.parms.skd);
970
971 stream_mgr_unlock();
972
973 return ret;
974}
975
976/* Closes the current file */
977int stream_close(void)
978{
979 if (stream_mgr.thread != NULL)
980 return stream_mgr_send_msg(STREAM_CLOSE, 0);
981 return STREAM_ERROR;
982}
983
984/* Initializes the playback engine */
985int stream_init(void)
986{
987 void *mem;
988 size_t memsize;
989
990 stream_mgr.status = STREAM_STOPPED;
991 stream_mgr_init_state();
992 list_initialize(&stream_mgr.actl);
993
994 /* Initialize our window to the outside world first */
995 rb->mutex_init(&stream_mgr.str_mtx);
996 rb->mutex_init(&stream_mgr.actl_mtx);
997
998 stream_mgr.q = &stream_mgr_queue;
999 rb->queue_init(stream_mgr.q, false);
1000 rb->queue_enable_queue_send(stream_mgr.q, &stream_mgr_queue_send);
1001
1002 /* sets audiosize and returns buffer pointer */
1003 mem = rb->plugin_get_audio_buffer(&memsize);
1004
1005 /* Initialize non-allocator blocks first */
1006#ifndef HAVE_LCD_COLOR
1007 int grayscales;
1008
1009 /* This can run on another processor - align data */
1010 memsize = CACHEALIGN_BUFFER(&mem, memsize);
1011 stream_mgr.graymem = mem;
1012
1013 /* initialize the grayscale buffer: 32 bitplanes for 33 shades of gray. */
1014 grayscales = gray_init(rb, mem, memsize, false,
1015 LCD_WIDTH, LCD_HEIGHT,
1016 32, 2<<8, &stream_mgr.graysize) + 1;
1017
1018 /* This can run on another processor - align size */
1019 stream_mgr.graysize = CACHEALIGN_UP(stream_mgr.graysize);
1020
1021 mem += stream_mgr.graysize;
1022 memsize -= stream_mgr.graysize;
1023
1024 if (grayscales < 33 || (ssize_t)memsize <= 0)
1025 {
1026 rb->splash(HZ, "graylib init failed!");
1027 return STREAM_ERROR;
1028 }
1029#endif /* !HAVE_LCD_COLOR */
1030
1031 stream_mgr.thread = rb->create_thread(stream_mgr_thread,
1032 stream_mgr_thread_stack, sizeof(stream_mgr_thread_stack),
1033 0, "mpgstream_mgr" IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU));
1034
1035 if (stream_mgr.thread == NULL)
1036 {
1037 rb->splash(HZ, "Could not create stream manager thread!");
1038 return STREAM_ERROR;
1039 }
1040
1041 /* Wait for thread to initialize */
1042 stream_mgr_send_msg(STREAM_NULL, 0);
1043
1044 /* Initialise our malloc buffer */
1045 if (!mpeg_alloc_init(mem, memsize))
1046 {
1047 rb->splash(HZ, "Out of memory in stream_init");
1048 }
1049 /* These inits use the allocator */
1050 else if (!pcm_output_init())
1051 {
1052 rb->splash(HZ, "Could not initialize PCM!");
1053 }
1054 else if (!audio_thread_init())
1055 {
1056 rb->splash(HZ, "Cannot create audio thread!");
1057 }
1058 else if (!video_thread_init())
1059 {
1060 rb->splash(HZ, "Cannot create video thread!");
1061 }
1062 /* Disk buffer takes max allotment of what's left so it must be last */
1063 else if (!disk_buf_init())
1064 {
1065 rb->splash(HZ, "Cannot create buffering thread!");
1066 }
1067 else if (!parser_init())
1068 {
1069 rb->splash(HZ, "Parser init failed!");
1070 }
1071 else
1072 {
1073 return STREAM_OK;
1074 }
1075
1076 return STREAM_ERROR;
1077}
1078
1079/* Cleans everything up */
1080void stream_exit(void)
1081{
1082 stream_close();
1083
1084 /* Stop the threads and wait for them to terminate */
1085 video_thread_exit();
1086 audio_thread_exit();
1087 disk_buf_exit();
1088 pcm_output_exit();
1089
1090 if (stream_mgr.thread != NULL)
1091 {
1092 stream_mgr_post_msg(STREAM_QUIT, 0);
1093 rb->thread_wait(stream_mgr.thread);
1094 stream_mgr.thread = NULL;
1095 }
1096}