[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:
ejurgensen 2019-08-17 01:44:45 +02:00
parent 86339eee87
commit b9b2874a2e

View File

@ -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)
{ {