/* 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();
}
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;
}
/* 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_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);
}
/* Handle STREAM_CLOSE */
static int stream_on_close(void)
{
    int status = STREAM_STOPPED;

    stream_mgr_lock();

    /* Any open file? */
    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();
        /* Close file */
        disk_buf_close();
        /* Reinitialize manager */
        stream_mgr_init_state();
    }

    stream_mgr_unlock();

    return status;
}
/* 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;
}
/* 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();
}
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;
}
/* 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;
}
/* 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;
}
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();
}
/* 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;

            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);
}
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 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;
}
/* 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_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;
}
/* 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;
}
/* 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_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_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();
}
/* Wait for a state transistion to complete */
void stream_wait_status(void)
{
    stream_mgr_lock();
    stream_mgr_unlock();
}