From 9d3d925295112a0080bc1d70fad170db9e1af2a9 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Thu, 13 Oct 2022 11:04:12 -0400 Subject: Revert "RFC: Get rid of mpegplayer plugin" This reverts commit d25d24812e8120c0eb133a412287ac030eb185c9. Change-Id: I1563223e343fb1e2eda72a45823b38350025ff93 --- apps/plugins/mpegplayer/stream_mgr.c | 1163 ++++++++++++++++++++++++++++++++++ 1 file changed, 1163 insertions(+) create mode 100644 apps/plugins/mpegplayer/stream_mgr.c (limited to 'apps/plugins/mpegplayer/stream_mgr.c') diff --git a/apps/plugins/mpegplayer/stream_mgr.c b/apps/plugins/mpegplayer/stream_mgr.c new file mode 100644 index 0000000000..3cac8c0f57 --- /dev/null +++ b/apps/plugins/mpegplayer/stream_mgr.c @@ -0,0 +1,1163 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * AV stream manager implementation + * + * Copyright (c) 2007 Michael Sevakis + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "plugin.h" +#include "mpegplayer.h" +#include "lib/grey.h" +#include "mpeg_settings.h" + +#ifndef HAVE_LCD_COLOR +GREY_INFO_STRUCT_IRAM +#endif + +static struct event_queue stream_mgr_queue SHAREDBSS_ATTR; +static struct queue_sender_list stream_mgr_queue_send SHAREDBSS_ATTR; +static uint32_t stream_mgr_thread_stack[DEFAULT_STACK_SIZE*2/sizeof(uint32_t)]; + +struct stream_mgr stream_mgr SHAREDBSS_ATTR; + +/* Forward decs */ +static int stream_on_close(void); + +struct str_broadcast_data +{ + long cmd; /* Command to send to stream */ + intptr_t data; /* Data to send with command */ +}; + +static inline void stream_mgr_lock(void) +{ + rb->mutex_lock(&stream_mgr.str_mtx); +} + +static inline void stream_mgr_unlock(void) +{ + rb->mutex_unlock(&stream_mgr.str_mtx); +} + +static inline void actl_lock(void) +{ + rb->mutex_lock(&stream_mgr.actl_mtx); +} + +static inline void actl_unlock(void) +{ + rb->mutex_unlock(&stream_mgr.actl_mtx); +} + +static inline void stream_mgr_post_msg(long id, intptr_t data) +{ + rb->queue_post(stream_mgr.q, id, data); +} + +static inline intptr_t stream_mgr_send_msg(long id, intptr_t data) +{ + return rb->queue_send(stream_mgr.q, id, data); +} + +static inline void stream_mgr_reply_msg(intptr_t retval) +{ + rb->queue_reply(stream_mgr.q, retval); +} + +int str_next_data_not_ready(struct stream *str) +{ + /* Save the current window since it actually might be ready by the time + * the registration is received by buffering. */ + off_t win_right = str->hdr.win_right; + + if (str->hdr.win_right < disk_buf.filesize - MIN_BUFAHEAD && + disk_buf.filesize > MIN_BUFAHEAD) + { + /* Set right edge to where probing left off + the minimum margin */ + str->hdr.win_right += MIN_BUFAHEAD; + } + else + { + /* Request would be passed the end of the file */ + str->hdr.win_right = disk_buf.filesize; + } + + switch (disk_buf_send_msg(DISK_BUF_DATA_NOTIFY, (intptr_t)str)) + { + case DISK_BUF_NOTIFY_OK: + /* Was ready - restore window and process */ + str->hdr.win_right = win_right; + return STREAM_OK; + + case DISK_BUF_NOTIFY_ERROR: + /* Error - quit parsing */ + str_end_of_stream(str); + return STREAM_DATA_END; + + default: + /* Not ready - go wait for notification from buffering. */ + str->pkt_flags = 0; + return STREAM_DATA_NOT_READY; + } +} + +void str_data_notify_received(struct stream *str) +{ + /* Normalize win_right back to the packet length */ + if (str->state == SSTATE_END) + return; + + if (str->curr_packet == NULL) + { + /* Nothing was yet parsed since init */ + str->hdr.win_right = str->hdr.win_left; + } + else + { + /* Restore window based upon current packet */ + str->hdr.win_right = str->hdr.win_left + + (str->curr_packet_end - str->curr_packet); + } +} + +/* Set stream manager to a "no-file" state */ +static void stream_mgr_init_state(void) +{ + stream_mgr.filename = NULL; + stream_mgr.resume_time = INVALID_TIMESTAMP; + stream_mgr.seeked = false; +} + +/* Add a stream to the playback pool */ +void stream_add_stream(struct stream *str) +{ + actl_lock(); + + list_remove_item(stream_mgr.strl, str); + list_add_item(stream_mgr.strl, str); + + actl_unlock(); +} + +/* Callback for various list-moving operations */ +static bool strl_enum_callback(struct stream *str, void *data) +{ + actl_lock(); + + list_remove_item(stream_mgr.strl, str); + + if (*(int*)data == 1) + list_add_item(stream_mgr.actl, str); + + actl_unlock(); + + return true; +} + +/* Clear all streams from active and playback pools */ +void stream_remove_streams(void) +{ + int add_item = 0; + list_enum_items(stream_mgr.strl, + (list_enum_callback_t)strl_enum_callback, (void *)&add_item); +} + +/* Move the playback pool to the active list */ +void move_strl_to_actl(void) +{ + int add_item = 1; + list_enum_items(stream_mgr.strl, + (list_enum_callback_t)strl_enum_callback, (void *)&add_item); +} + +/* Remove a stream from the active list and return it to the pool */ +static bool actl_stream_remove(struct stream *str) +{ + bool retval; + + actl_lock(); + + retval = list_remove_item(stream_mgr.actl, str); + + if (retval) + list_add_item(stream_mgr.strl, str); + + actl_unlock(); + + return retval; +} + +/* Broadcast a message to all active streams */ +static bool actl_stream_broadcast_callback(struct stream *str, + struct str_broadcast_data *sbd) +{ + switch (sbd->cmd) + { + case STREAM_PLAY: + case STREAM_PAUSE: + break; + + case STREAM_STOP: + if (sbd->data != 0) + { + actl_lock(); + + list_remove_item(stream_mgr.actl, str); + list_add_item(stream_mgr.strl, str); + + actl_unlock(); + sbd->data = 0; + } + break; + + default: + return false; + } + + str_send_msg(str, sbd->cmd, sbd->data); + return true; +} + +static void actl_stream_broadcast(int cmd, intptr_t data) +{ + struct str_broadcast_data sbd; + sbd.cmd = cmd; + sbd.data = data; + list_enum_items(stream_mgr.actl, + (list_enum_callback_t)actl_stream_broadcast_callback, + (void*)&sbd); +} + +/* Set the current base clock */ +static void set_stream_clock(uint32_t time) +{ + /* Fudge: Start clock 100ms early to allow for some filling time */ + if (time > 100*TS_SECOND/1000) + time -= 100*TS_SECOND/1000; + else + time = 0; + + pcm_output_set_clock(TS_TO_TICKS(time)); +} + +static void stream_start_playback(uint32_t time, bool fill_buffer) +{ + if (stream_mgr.seeked) + { + /* Clear any seeked status */ + stream_mgr.seeked = false; + + /* Flush old PCM data */ + pcm_output_flush(); + + /* Set the master clock */ + set_stream_clock(time); + + /* Make sure streams are back in active pool */ + move_strl_to_actl(); + + /* Prepare the parser and associated streams */ + parser_prepare_streaming(); + } + + /* Start buffer which optional force fill */ + disk_buf_send_msg(STREAM_PLAY, fill_buffer); + + /* Tell each stream to start - may generate end of stream signals + * now - we'll handle this when finished */ + actl_stream_broadcast(STREAM_PLAY, 0); + + /* Actually start the clock */ + pcm_output_play_pause(true); +} + +/* Return the play time relative to the specified play time */ +static uint32_t time_from_whence(uint32_t time, int whence) +{ + int64_t currtime; + uint32_t start; + + switch (whence) + { + case SEEK_SET: + /* Set the current time (time = unsigned offset from 0) */ + if (time > str_parser.duration) + time = str_parser.duration; + break; + case SEEK_CUR: + /* Seek forward or backward from the current time + * (time = signed offset from current) */ + currtime = stream_get_seek_time(&start); + currtime -= start; + currtime += (int32_t)time; + + if (currtime < 0) + currtime = 0; + else if ((uint64_t)currtime > str_parser.duration) + currtime = str_parser.duration; + + time = (uint32_t)currtime; + break; + case SEEK_END: + /* Seek from the end (time = unsigned offset from end) */ + if (time > str_parser.duration) + time = str_parser.duration; + time = str_parser.duration - time; + break; + } + + return time; +} + +/* Handle seeking details if playing or paused */ +static uint32_t stream_seek_intl(uint32_t time, int whence, + int status, bool *was_buffering) +{ + if (status != STREAM_STOPPED) + { + bool wb; + + /* Place streams in a non-running state - keep them on actl if + * still there */ + actl_stream_broadcast(STREAM_PAUSE, 0); + + /* Stop all buffering or else risk clobbering random-access data */ + wb = disk_buf_send_msg(STREAM_STOP, 0); + + if (was_buffering != NULL) + *was_buffering = wb; + } + + time = time_from_whence(time, whence); + + stream_mgr.seeked = true; + + return parser_seek_time(time); +} + +/* Store the resume time at the last seek/current clock point */ +static void stream_remember_resume_time(void) +{ + /* Assume invalidity */ + stream_mgr.resume_time = 0; + + if (stream_can_seek()) + { + /* Read the current stream time or the last seeked position */ + uint32_t start; + uint32_t time = stream_get_seek_time(&start); + + if (time >= str_parser.start_pts && time <= str_parser.end_pts) + { + /* Save the current stream time */ + stream_mgr.resume_time = time - start; + } + } +} + +/* Handle STREAM_OPEN */ +void stream_on_open(const char *filename) +{ + int err = STREAM_ERROR; + + stream_mgr_lock(); + + trigger_cpu_boost(); + + /* Open the video file */ + if (disk_buf_open(filename) >= 0) + { + /* Initialize the parser */ + err = parser_init_stream(); + + if (err >= STREAM_OK) + { + /* File ok - save the opened filename */ + stream_mgr.filename = filename; + } + } + + /* If error - cleanup */ + if (err < STREAM_OK) + stream_on_close(); + + cancel_cpu_boost(); + + stream_mgr_unlock(); + + stream_mgr_reply_msg(err); +} + +/* Handler STREAM_PLAY */ +static void stream_on_play(void) +{ + int status = stream_mgr.status; + + stream_mgr_lock(); + + if (status == STREAM_STOPPED) + { + uint32_t start; + + /* We just say we're playing now */ + stream_mgr.status = STREAM_PLAYING; + + /* Reply with previous state */ + stream_mgr_reply_msg(status); + + trigger_cpu_boost(); + + /* Seek to initial position and set clock to that time */ + + /* Save the resume time */ + stream_remember_resume_time(); + + /* Prepare seek to start point */ + start = stream_seek_intl(stream_mgr.resume_time, SEEK_SET, + STREAM_STOPPED, NULL); + + /* Sync and start - force buffer fill */ + stream_start_playback(start, true); + } + else + { + /* Reply with previous state */ + stream_mgr_reply_msg(status); + } + + stream_mgr_unlock(); +} + +/* Handle STREAM_PAUSE */ +static void stream_on_pause(void) +{ + int status = stream_mgr.status; + + stream_mgr_lock(); + + /* Reply with previous state */ + stream_mgr_reply_msg(status); + + if (status == STREAM_PLAYING) + { + /* Pause the clock */ + pcm_output_play_pause(false); + + /* Pause each active stream */ + actl_stream_broadcast(STREAM_PAUSE, 0); + + /* Pause the disk buffer - buffer may continue filling */ + disk_buf_send_msg(STREAM_PAUSE, false); + + /* Unboost the CPU */ + cancel_cpu_boost(); + + /* Offically paused */ + stream_mgr.status = STREAM_PAUSED; + } + + stream_mgr_unlock(); +} + +/* Handle STREAM_RESUME */ +static void stream_on_resume(void) +{ + int status = stream_mgr.status; + + stream_mgr_lock(); + + /* Reply with previous state */ + stream_mgr_reply_msg(status); + + if (status == STREAM_PAUSED) + { + /* Boost the CPU */ + trigger_cpu_boost(); + + /* Sync and start - no force buffering */ + stream_start_playback(str_parser.last_seek_time, false); + + /* Officially playing */ + stream_mgr.status = STREAM_PLAYING; + } + + stream_mgr_unlock(); +} + +/* Handle STREAM_STOP */ +static void stream_on_stop(bool reply) +{ + int status = stream_mgr.status; + + stream_mgr_lock(); + + if (reply) + stream_mgr_reply_msg(status); + + if (status != STREAM_STOPPED) + { + /* Pause the clock */ + pcm_output_play_pause(false); + + /* Update the resume time info */ + stream_remember_resume_time(); + + /* Not stopped = paused or playing */ + stream_mgr.seeked = false; + + /* Stop buffering */ + disk_buf_send_msg(STREAM_STOP, 0); + + /* Clear any still-active streams and remove from actl */ + actl_stream_broadcast(STREAM_STOP, 1); + + /* Stop PCM output (and clock) */ + pcm_output_stop(); + + /* Cancel our processor boost */ + cancel_cpu_boost(); + + stream_mgr.status = STREAM_STOPPED; + } + + stream_mgr_unlock(); +} + +/* Handle STREAM_SEEK */ +static void stream_on_seek(struct stream_seek_data *skd) +{ + uint32_t time = skd->time; + int whence = skd->whence; + + switch (whence) + { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + if (stream_mgr.filename == NULL) + break; + + /* Keep things spinning if already doing so */ + stream_keep_disk_active(); + + /* Have data - reply in order to acquire lock */ + stream_mgr_reply_msg(STREAM_OK); + + stream_mgr_lock(); + + /* Either seeking must be possible or a full rewind must be done */ + if (stream_can_seek() || time_from_whence(time, whence) == 0) + { + bool buffer = false; + + if (stream_mgr.status == STREAM_PLAYING) + { + /* Keep clock from advancing while seeking */ + pcm_output_play_pause(false); + } + + time = stream_seek_intl(time, whence, stream_mgr.status, &buffer); + stream_remember_resume_time(); + + if (stream_mgr.status == STREAM_PLAYING) + { + /* Sync and restart - no force buffering */ + stream_start_playback(time, buffer); + } + } + + stream_mgr_unlock(); + return; + } + + /* Invalid parameter or no file */ + stream_mgr_reply_msg(STREAM_ERROR); +} + +/* Handle STREAM_CLOSE */ +static int stream_on_close(void) +{ + int status = STREAM_STOPPED; + + stream_mgr_lock(); + + /* Any open file that was accepted for playback? */ + if (stream_mgr.filename != NULL) + { + /* Yes - hide video */ + stream_show_vo(false); + /* Stop any playback */ + status = stream_mgr.status; + stream_on_stop(false); + /* Tell parser file is finished */ + parser_close_stream(); + /* Reinitialize manager */ + stream_mgr_init_state(); + } + + /* Let disk buffer reset itself - file might be open even if no good */ + disk_buf_close(); + + stream_mgr_unlock(); + + return status; +} + +/* Handle STREAM_EV_COMPLETE */ +static void stream_on_ev_complete(struct stream *str) +{ + stream_mgr_lock(); + + /* Stream is active? */ + if (actl_stream_remove(str)) + { + /* No - remove this stream from the active list */ + DEBUGF(" finished: 0x%02x\n", str->id); + if (list_is_empty(stream_mgr.actl)) + { + /* All streams have acked - stop playback */ + stream_on_stop(false); + stream_mgr.resume_time = 0; /* Played to end - no resume */ + } + else + { + /* Stream is done - stop it and place back in pool */ + str_send_msg(str, STREAM_STOP, 1); + } + } + + stream_mgr_unlock(); +} + +/* Callback for stream to notify about events internal to them */ +void stream_generate_event(struct stream *str, long id, intptr_t data) +{ + if (str == NULL) + return; + + switch (id) + { + case STREAM_EV_COMPLETE: + /* The last stream has ended */ + stream_mgr_post_msg(STREAM_EV_COMPLETE, (intptr_t)str); + break; + } + + (void)data; +} + +/* Clear any particular notification for which a stream registered */ +void stream_clear_notify(struct stream *str, int for_msg) +{ + switch (for_msg) + { + case DISK_BUF_DATA_NOTIFY: + disk_buf_send_msg(DISK_BUF_CLEAR_DATA_NOTIFY, (intptr_t)str); + break; + } +} + +/* Special handling for certain messages since they involve multiple + * operations behind the scenes */ +static intptr_t send_video_msg(long id, intptr_t data) +{ + intptr_t retval = 0; + + if (video_str.thread != 0 && disk_buf.in_file >= 0) + { + + switch (id) + { + case VIDEO_DISPLAY_SHOW: + if (data != 0 && disk_buf_status() == STREAM_STOPPED) + { /* Only prepare image if showing and not playing */ + parser_prepare_image(str_parser.last_seek_time); + } + break; + + case VIDEO_PRINT_FRAME: + if (data) + break; + case VIDEO_PRINT_THUMBNAIL: + if (disk_buf_status() != STREAM_STOPPED) + break; /* Prepare image if not playing */ + + /* Ignore return and try video thread anyway */ + parser_prepare_image(str_parser.last_seek_time); + + /* Image ready - pass message to video thread */ + break; + } + + retval = str_send_msg(&video_str, id, data); + } + + return retval; +} + +/* Show/hide the video output */ +bool stream_show_vo(bool show) +{ + bool vis; + stream_mgr_lock(); + + vis = send_video_msg(VIDEO_DISPLAY_SHOW, show); +#ifndef HAVE_LCD_COLOR + grey_show(show); +#endif + stream_mgr_unlock(); + + return vis; +} + +/* Query the visibility of video output */ +bool stream_vo_is_visible(void) +{ + bool vis; + stream_mgr_lock(); + vis = send_video_msg(VIDEO_DISPLAY_IS_VISIBLE, 0); + stream_mgr_unlock(); + return vis; +} + +/* Return the video dimensions */ +bool stream_vo_get_size(struct vo_ext *sz) +{ + bool retval = false; + + stream_mgr_lock(); + + if (str_parser.dims.w > 0 && str_parser.dims.h > 0) + { + *sz = str_parser.dims; + retval = true; + } + + stream_mgr_unlock(); + + return retval; +} + +void stream_vo_set_clip(const struct vo_rect *rc) +{ + stream_mgr_lock(); + + if (rc) + { + stream_mgr.parms.rc = *rc; + rc = &stream_mgr.parms.rc; + } + + send_video_msg(VIDEO_SET_CLIP_RECT, (intptr_t)rc); + + stream_mgr_unlock(); +} + +bool stream_vo_get_clip(struct vo_rect *rc) +{ + bool retval; + + if (!rc) + return false; + + stream_mgr_lock(); + + retval = send_video_msg(VIDEO_GET_CLIP_RECT, + (intptr_t)&stream_mgr.parms.rc); + + *rc = stream_mgr.parms.rc; + + stream_mgr_unlock(); + + return retval; +} + +#ifndef HAVE_LCD_COLOR +/* Show/hide the gray video overlay (independently of vo visibility). */ +void stream_gray_show(bool show) +{ + stream_mgr_lock(); + + grey_show(show); + + stream_mgr_unlock(); +} + +#endif /* !HAVE_LCD_COLOR */ + +/* Display a thumbnail at the last seek point */ +bool stream_display_thumb(const struct vo_rect *rc) +{ + bool retval; + + if (rc == NULL) + return false; + + stream_mgr_lock(); + + stream_mgr.parms.rc = *rc; + retval = send_video_msg(VIDEO_PRINT_THUMBNAIL, + (intptr_t)&stream_mgr.parms.rc); + + stream_mgr_unlock(); + + return retval; +} + +bool stream_draw_frame(bool no_prepare) +{ + bool retval; + stream_mgr_lock(); + + retval = send_video_msg(VIDEO_PRINT_FRAME, no_prepare); + + stream_mgr_unlock(); + + return retval; +} + +bool stream_set_callback(long id, void *fn) +{ + bool retval = false; + + stream_mgr_lock(); + + switch (id) + { + case VIDEO_SET_POST_FRAME_CALLBACK: + retval = send_video_msg(id, (intptr_t)fn); + } + + stream_mgr_unlock(); + + return retval; +} + +/* Return the time playback should resume if interrupted */ +uint32_t stream_get_resume_time(void) +{ + uint32_t resume_time; + + /* A stop request is async and replies before setting this - must lock */ + stream_mgr_lock(); + + resume_time = stream_mgr.resume_time; + + stream_mgr_unlock(); + + return resume_time; +} + +uint32_t stream_get_seek_time(uint32_t *start) +{ + uint32_t time; + + stream_mgr_lock(); + + if (stream_mgr.seeked) + { + time = str_parser.last_seek_time; + } + else + { + time = TICKS_TO_TS(pcm_output_get_clock()); + + /* Clock can be start early so keep in range */ + if (time < str_parser.start_pts) + time = str_parser.start_pts; + } + + if (start != NULL) + *start = str_parser.start_pts; + + stream_mgr_unlock(); + + return time; +} + +/* Wait for a state transistion to complete */ +void stream_wait_status(void) +{ + stream_mgr_lock(); + stream_mgr_unlock(); +} + +/* Returns the smallest file window that includes all active streams' + * windows */ +static bool stream_get_window_callback(struct stream *str, + struct stream_window *sw) +{ + off_t swl = str->hdr.win_left; + off_t swr = str->hdr.win_right; + + if (swl < sw->left) + sw->left = swl; + + if (swr > sw->right) + sw->right = swr; + + return true; +} + +bool stream_get_window(struct stream_window *sw) +{ + if (sw == NULL) + return false; + + sw->left = LONG_MAX; + sw->right = LONG_MIN; + + actl_lock(); + list_enum_items(stream_mgr.actl, + (list_enum_callback_t)stream_get_window_callback, + (void*)sw); + actl_unlock(); + + return sw->left <= sw->right; +} + +/* Playback control thread */ +static void stream_mgr_thread(void) +{ + struct queue_event ev; + + while (1) + { + rb->queue_wait(stream_mgr.q, &ev); + + switch (ev.id) + { + case STREAM_OPEN: + stream_on_open((const char *)ev.data); + break; + + case STREAM_CLOSE: + stream_on_close(); + break; + + case STREAM_PLAY: + stream_on_play(); + break; + + case STREAM_PAUSE: + if (ev.data) + stream_on_resume(); + else + stream_on_pause(); + break; + + case STREAM_STOP: + stream_on_stop(true); + break; + + case STREAM_SEEK: + stream_on_seek((struct stream_seek_data *)ev.data); + break; + + case STREAM_EV_COMPLETE: + stream_on_ev_complete((struct stream *)ev.data); + break; + + case STREAM_QUIT: + if (stream_mgr.status != STREAM_STOPPED) + stream_on_stop(false); + return; + } + } +} + +/* Stream command interface APIs */ + +/* Opens a new file */ +int stream_open(const char *filename) +{ + if (stream_mgr.thread != 0) + return stream_mgr_send_msg(STREAM_OPEN, (intptr_t)filename); + return STREAM_ERROR; +} + +/* Plays the current file starting at time 'start' */ +int stream_play(void) +{ + if (stream_mgr.thread != 0) + return stream_mgr_send_msg(STREAM_PLAY, 0); + return STREAM_ERROR; +} + +/* Pauses playback if playing */ +int stream_pause(void) +{ + if (stream_mgr.thread != 0) + return stream_mgr_send_msg(STREAM_PAUSE, false); + return STREAM_ERROR; +} + +/* Resumes playback if paused */ +int stream_resume(void) +{ + if (stream_mgr.thread != 0) + return stream_mgr_send_msg(STREAM_PAUSE, true); + return STREAM_ERROR; +} + +/* Stops playback if not stopped */ +int stream_stop(void) +{ + if (stream_mgr.thread != 0) + return stream_mgr_send_msg(STREAM_STOP, 0); + return STREAM_ERROR; +} + +/* Seeks playback time to/by the specified time */ +int stream_seek(uint32_t time, int whence) +{ + int ret; + + if (stream_mgr.thread == 0) + return STREAM_ERROR; + + stream_mgr_lock(); + + stream_mgr.parms.skd.time = time; + stream_mgr.parms.skd.whence = whence; + + ret = stream_mgr_send_msg(STREAM_SEEK, (intptr_t)&stream_mgr.parms.skd); + + stream_mgr_unlock(); + + return ret; +} + +/* Closes the current file */ +int stream_close(void) +{ + if (stream_mgr.thread != 0) + return stream_mgr_send_msg(STREAM_CLOSE, 0); + return STREAM_ERROR; +} + +/* Initializes the playback engine */ +int stream_init(void) +{ + void *mem; + size_t memsize; + + stream_mgr.status = STREAM_STOPPED; + stream_mgr_init_state(); + + /* Initialize our window to the outside world first */ + rb->mutex_init(&stream_mgr.str_mtx); + rb->mutex_init(&stream_mgr.actl_mtx); + + stream_mgr.q = &stream_mgr_queue; + rb->queue_init(stream_mgr.q, false); + + /* sets audiosize and returns buffer pointer */ + mem = rb->plugin_get_audio_buffer(&memsize); + + /* Initialize non-allocator blocks first */ +#ifndef HAVE_LCD_COLOR + long greysize; + + /* Greylib init handles all necessary cache alignment */ + if (!grey_init(mem, memsize, GREY_BUFFERED|GREY_ON_COP, + LCD_WIDTH, LCD_HEIGHT, &greysize)) + { + rb->splash(HZ, "greylib init failed!"); + return STREAM_ERROR; + } + + mem += greysize; + memsize -= greysize; + + grey_clear_display(); +#endif /* !HAVE_LCD_COLOR */ + + stream_mgr.thread = rb->create_thread(stream_mgr_thread, + stream_mgr_thread_stack, sizeof(stream_mgr_thread_stack), + 0, "mpgstream_mgr" IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU)); + + rb->queue_enable_queue_send(stream_mgr.q, &stream_mgr_queue_send, + stream_mgr.thread); + + if (stream_mgr.thread == 0) + { + rb->splash(HZ, "Could not create stream manager thread!"); + return STREAM_ERROR; + } + + /* Wait for thread to initialize */ + stream_mgr_send_msg(STREAM_NULL, 0); + + /* Initialise our malloc buffer */ + if (!mpeg_alloc_init(mem, memsize)) + { + rb->splash(HZ, "Out of memory in stream_init"); + } + /* These inits use the allocator */ + else if (!pcm_output_init()) + { + rb->splash(HZ, "Could not initialize PCM!"); + } + else if (!audio_thread_init()) + { + rb->splash(HZ, "Cannot create audio thread!"); + } + else if (!video_thread_init()) + { + rb->splash(HZ, "Cannot create video thread!"); + } + /* Disk buffer takes max allotment of what's left so it must be last */ + else if (!disk_buf_init()) + { + rb->splash(HZ, "Cannot create buffering thread!"); + } + else if (!parser_init()) + { + rb->splash(HZ, "Parser init failed!"); + } + else + { + return STREAM_OK; + } + + return STREAM_ERROR; +} + +/* Cleans everything up */ +void stream_exit(void) +{ + stream_close(); + + /* Stop the threads and wait for them to terminate */ + video_thread_exit(); + audio_thread_exit(); + disk_buf_exit(); + pcm_output_exit(); + + if (stream_mgr.thread != 0) + { + stream_mgr_post_msg(STREAM_QUIT, 0); + rb->thread_wait(stream_mgr.thread); + stream_mgr.thread = 0; + } + +#ifndef HAVE_LCD_COLOR + grey_release(); +#endif +} -- cgit v1.2.3