mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-25 12:29:18 -05:00
[player] Possible fix for issue #781 where playback status is incorrect
pb_suspend() + pb_resume() during track changes made the playback status incorrect, i.e. pb_session.source_list/playing_now would not match what the input was actually writing. This attempts to solve it by resetting the session when pb_suspend() is called, so that the input, input_buffer and source_list come into sync.
This commit is contained in:
parent
86339eee87
commit
b9b2874a2e
79
src/player.c
79
src/player.c
@ -615,14 +615,12 @@ source_stop(void)
|
|||||||
static int
|
static int
|
||||||
source_start(struct player_source *ps)
|
source_start(struct player_source *ps)
|
||||||
{
|
{
|
||||||
short flags;
|
|
||||||
|
|
||||||
if (!ps)
|
if (!ps)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_PLAYER, "Opening track: '%s' (id=%d, seek=%d)\n", ps->path, ps->item_id, ps->seek_ms);
|
DPRINTF(E_DBG, L_PLAYER, "Opening track: '%s' (id=%d, seek=%d)\n", ps->path, ps->item_id, ps->seek_ms);
|
||||||
|
|
||||||
input_flush(&flags);
|
input_flush(NULL);
|
||||||
|
|
||||||
return input_seek(ps->item_id, (int)ps->seek_ms);
|
return input_seek(ps->item_id, (int)ps->seek_ms);
|
||||||
}
|
}
|
||||||
@ -639,20 +637,14 @@ source_next(struct player_source *ps)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
source_restart(void)
|
source_restart(struct player_source *ps)
|
||||||
{
|
{
|
||||||
if (!pb_session.playing_now)
|
DPRINTF(E_DBG, L_PLAYER, "Restarting track: '%s' (id=%d, pos=%d)\n", ps->path, ps->item_id, ps->pos_ms);
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_PLAYER, "Bug! source_restart called, but playing_now is null\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_PLAYER, "Restarting track: '%s' (id=%d, pos=%d)\n", pb_session.playing_now->path, pb_session.playing_now->item_id, pb_session.playing_now->pos_ms);
|
|
||||||
|
|
||||||
// Must be non-blocking, because otherwise we get a deadlock via the input
|
// Must be non-blocking, because otherwise we get a deadlock via the input
|
||||||
// thread making a sync call to player_playback_start() -> pb_resume() ->
|
// thread making a sync call to player_playback_start() -> pb_resume() ->
|
||||||
// source_restart() -> input_resume()
|
// source_restart() -> input_resume()
|
||||||
input_resume(pb_session.playing_now->item_id, pb_session.playing_now->pos_ms);
|
input_resume(ps->item_id, ps->pos_ms);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1677,13 +1669,31 @@ pb_abort(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restarts the input (in case it was closed during the pause), resets session
|
// Restarts the input (in case it was closed during the pause), resets session
|
||||||
// start timestamp and deficits, which is necessary after pb_suspend
|
// start timestamp and deficits, which is necessary after pb_suspend.
|
||||||
static int
|
static int
|
||||||
pb_resume(void)
|
pb_resume(void)
|
||||||
{
|
{
|
||||||
|
struct player_source *ps;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = source_restart();
|
// Before pb_resume() is called it is important that source_list is set to
|
||||||
|
// have just one item, and that both reading_now and playing_now point to it.
|
||||||
|
// Otherwise it would mean that the input is currently reading an item that is
|
||||||
|
// not being played, which means asking the input to resume playing_now
|
||||||
|
// will bring us into a situation where the order of data read by input_read()
|
||||||
|
// from the data input_buffer will not the match the order of the source_list.
|
||||||
|
ps = pb_session.source_list;
|
||||||
|
if (!ps || ps != pb_session.playing_now)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_PLAYER, "Bug! pb_resume() called, but source list is invalid (%p, %p)\n", ps, pb_session.playing_now);
|
||||||
|
pb_abort();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In many cases the input will already be open, so this only has effect if
|
||||||
|
// we are resuming after pause longer than INPUT_OPEN_TIMEOUT, or if the input
|
||||||
|
// lost connection during the pause
|
||||||
|
ret = source_restart(ps);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
pb_abort();
|
pb_abort();
|
||||||
@ -1696,10 +1706,38 @@ pb_resume(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily suspends/resets playback, used when input buffer underruns or in
|
// Temporarily suspends/resets playback, used when input buffer underruns or in
|
||||||
// case of problems writing to the outputs
|
// case of problems writing to the outputs. Especially in the latter case the
|
||||||
|
// input may have proceeded to a source further ahead than playing_now, so we
|
||||||
|
// may also need to reset the input, so that we resume with playing_now.
|
||||||
static void
|
static void
|
||||||
pb_suspend(void)
|
pb_suspend(void)
|
||||||
{
|
{
|
||||||
|
struct db_queue_item *queue_item;
|
||||||
|
struct player_source *ps;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// Start session again to reset input, also flushes input buffer
|
||||||
|
ps = pb_session.playing_now;
|
||||||
|
if (ps != pb_session.source_list)
|
||||||
|
{
|
||||||
|
queue_item = db_queue_fetch_byitemid(ps->item_id);
|
||||||
|
if (!queue_item)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_PLAYER, "Error suspending playback, could not retrieve queue item currently being played\n");
|
||||||
|
pb_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pb_session_start(queue_item, ps->pos_ms);
|
||||||
|
free_queue_item(queue_item, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_PLAYER, "Error suspending playback, could not start session\n");
|
||||||
|
pb_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
player_flush_pending = outputs_flush(device_flush_cb);
|
player_flush_pending = outputs_flush(device_flush_cb);
|
||||||
|
|
||||||
pb_timer_stop();
|
pb_timer_stop();
|
||||||
@ -1861,7 +1899,6 @@ playback_start_item(void *arg, int *retval)
|
|||||||
struct db_queue_item *queue_item = arg;
|
struct db_queue_item *queue_item = arg;
|
||||||
struct media_file_info *mfi;
|
struct media_file_info *mfi;
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
struct player_source *ps;
|
|
||||||
uint32_t seek_ms;
|
uint32_t seek_ms;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -1885,16 +1922,6 @@ playback_start_item(void *arg, int *retval)
|
|||||||
|
|
||||||
if (!queue_item)
|
if (!queue_item)
|
||||||
{
|
{
|
||||||
ps = pb_session.playing_now;
|
|
||||||
if (!ps)
|
|
||||||
{
|
|
||||||
DPRINTF(E_WARN, L_PLAYER, "Bug! playing_now is null but playback is not stopped!\n");
|
|
||||||
*retval = -1;
|
|
||||||
return COMMAND_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_PLAYER, "Resume playback of '%s' (id=%d, item-id=%d)\n", ps->path, ps->id, ps->item_id);
|
|
||||||
|
|
||||||
ret = pb_resume();
|
ret = pb_resume();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user