From 6d1ac35781fb141745d1e05480acc8dfeabbefd9 Mon Sep 17 00:00:00 2001 From: kwarklabs <96121168+kwarklabs@users.noreply.github.com> Date: Sun, 1 May 2022 12:12:18 +0100 Subject: [PATCH 1/7] Improve retries for non-live remote streams. The short story behind this commit is that with this change, attempts will be made to resume non-live remote streams which end because of a failure at the Player level, such as because of a data corruption, (as opposed to the Network level) when previously playback would have ended. This is most noticable on long running streams like those hosted on Mixcloud. It potentially relieves some of the symptoms reported in issue #130. The longer story with some history is as follows: The _willRetry subroutine was added to Slim::Player::StreamingController in version 7.6 of slimserver with the last commit to change the subroutine in September 2011. The functional purpose of the subroutine is to retry playing a stream when if fails at the Player level. However it would appear via code inspection and testing that that _willRetry will not run as intended when called from at least two out of the three subroutines from which it is called and it seems likely that it's never worked. One of the first things that _willRetry does on line 1394 is check whether $retryData is defined. However the only place we have found $retryData to be assigned is at line 921 of StreamingController in the _RetryOrNext subroutine, which is only called from the ReadyToStream state. $retryData does appear in the constructor of Slim::Player::Song at line 74 but is not mentioned anywhere else in the codebase that we can find or in any of the plugins that we've searched. And looking at the expected contents of $retryData in StreamingController, we don't think that it was ever intended to be defined outside of StreamingController. The expected contents are "start" and "count" where the first is the time when StreamingController started retrying and the second is the number of retries. The three places that _willRetry is called from are: _Stream, _StopNextIfMore and _SyncStopNext. _StopNextIfMore and _SyncStopNext are both called when in the StreamingFailed state (lines 205 and 206). But $retryData will have been set to undef in _Playing at line 370 which will have been called before the StreamingFailed state can be reached. So, as far as we can tell, _willRetry will always return 0 at line 1396 when called from _StopNextIfMore or _SyncStopNext. Also, the current implentation only handles remote radio streams where playback is restarted from the begining. See on line 918 the check for isLive and line 921 where seekdata is omitted in the call to _Stream. In this commit we have attempted to understand how the _willRetry subroutine was originally intended to work and implement the same logic. So we've kept the same retry intervals and retry limits for example. When streaming to players that need the remote stream to be transcoded, such as legacy hardware players which cannot play AAC for example, the same problem manifests itself but the code to relieve the issue needed to be added to the _RetryOrNext subroutine. An else statement has been added to the inner if statement in the _RetryOrNext subroutine so previously functioning restarts of radio streams should be unchanged but now transcoded non-live streams will also be restarted. The test case we have been using to to develop this commit is using the Mixcloud plugin with the following stream which usually fails to play to completion: https://www.mixcloud.com/johndigweed/transitions-with-john-digweed-and-denney/ It is not entirely predictable if and when this stream will fail so testing this commit with it requires patience and a lot of time. Failure at the Player level seems to occur more frequently when using "Normal streaming" on the Settings -> Advanced -> Network screen than when using "Persistent mode". We do not know why this would be the case but would suggest that logically the difference is likely to be in the way the remote server handles the streamed data. --- Slim/Player/StreamingController.pm | 71 +++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/Slim/Player/StreamingController.pm b/Slim/Player/StreamingController.pm index 690d23c576a..11d21ec0ed3 100644 --- a/Slim/Player/StreamingController.pm +++ b/Slim/Player/StreamingController.pm @@ -916,11 +916,23 @@ sub _RetryOrNext { # -> Idle; IF [shouldretry && canretry] THEN continue && $song->isRemote() && $elapsed > 10) # have we managed to play at least 10s? { - if (!$song->duration() && $song->isLive()) { # unknown duration => assume radio + if (!$song->duration() || $song->isLive()) { # unknown duration => assume radio main::INFOLOG && $log->is_info && $log->info('Attempting to re-stream ', $song->currentTrack()->url, ' after time ', $elapsed); $song->retryData({ count => 0, start => Time::HiRes::time()}); _Stream($self, $event, {song => $song}); return; + } else { + if ($song->duration() - $elapsed < 10) # check we have more than 10 seconds left to play. + { + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - track is within 10 seconds of end.")}; + } else { + # get seek data from protocol handler. + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Getting seek data from protocol handler.")}; + my $seekdata = $song->getSeekData($elapsed); + main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: ". $elapsed); + _Stream($self, undef, {song => $song, seekdata => $seekdata, reconnect => 1}); + return; + } } } @@ -948,8 +960,6 @@ sub _Continue { $song->setStatus(Slim::Player::Song::STATUS_PLAYING); } } else { - # This handles resuming after reboot with the caveat that if connection has been lost (no reboot) - # while playing and before reception of next song's 1st byte, we'll resume the current song main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: ". $self->playingSongElapsed()); _JumpToTime($self, $event, {newtime => $self->playingSongElapsed(), restartIfNoSeek => 1}); } @@ -1404,12 +1414,23 @@ sub _willRetry { $song ||= streamingSong($self); return 0 if !$song; - my $retry = $song->retryData(); - if (!$retry) { + # $song->retryData() will be undef is we are not retrying already. + if (!$song->retryData()) { $log->info('no retry data'); - return 0; + $song->retryData({ count => 0, start => Time::HiRes::time()}); } + $song->retryData->{'count'} += 1; + + # Have we managed to play at least 10 seconds of a track and retried fewer times than there are intervals? + my $elapsed = $self->playingSongElapsed(); + if ($song->retryData->{'count'} > scalar @retryIntervals || $elapsed < 10 || ($song->duration() - $elapsed < 10)) { + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - too many retries already or track is within 10 seconds of start or end.")}; + return 0; + } + + + # Define $limit as the time until which a retry is allowed. my $limit; my $next = nextsong($self); if (defined $next && $next != $song->index) { @@ -1417,37 +1438,45 @@ sub _willRetry { } else { $limit = RETRY_LIMIT; } - - my $interval = $retryIntervals[$retry->{'count'} > $#retryIntervals ? -1 : $retry->{'count'}]; + + # Setting $interval to -1 causes retryTime to be in the past so no retry will be attempted. + my $interval = $retryIntervals[$song->retryData->{'count'} > $#retryIntervals ? -1 : $song->retryData->{'count'}]; my $retryTime = time() + $interval; - if ($retry->{'start'} + $limit < $retryTime) { + # $retryTime is when we will next retry. + # $retry->{'start'} + $limit is the time before which it is okay to retry. + if ($song->retryData->{'start'} + $limit < $retryTime) { + if ( main::DEBUGLOG && $synclog->is_debug ) {$log->debug("too late, give up")}; # too late, give up $song->retryData(undef); _errorOpening($self, $song->currentTrack()->url, 'RETRY_LIMIT_EXCEEDED'); _Stop($self); - $self->{'consecutiveErrors'} = 1; # the failed retry counts as one error + $self->{'consecutiveErrors'}++; # the failed retry counts as one error return 0; } - - $retry->{'count'} += 1; - my $id = ++$self->{'nextTrackCallbackId'}; - $self->{'nextTrack'} = undef; - _setStreamingState($self, TRACKWAIT); - + + + my $queue = $self->{'songqueue'}; + $queue->[0]->setStatus(Slim::Player::Song::STATUS_READY) if scalar @$queue; Slim::Utils::Timers::setTimer( $self, $retryTime, sub { - $song->setStatus(Slim::Player::Song::STATUS_READY); - $self->{'consecutiveErrors'} = 0; - _nextTrackReady($self, $id, $song); + _playersMessage($self, $song->currentTrack()->url, undef, 'RETRYING', undef, undef, 1); + if (!$song->duration() || $song->isLive()) { # unknown duration => assume radio + main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: 0"); + _Stream($self, undef, {song => $song, reconnect => 1}); + } else { + # get seek data from protocol handler. + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Getting seek data from protocol handler.")}; + my $seekdata = $song->getSeekData($elapsed); + main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: ". $elapsed); + _Stream($self, undef, {song => $song, seekdata => $seekdata, reconnect => 1}); + } }, undef ); - _playersMessage($self, $song->currentTrack()->url, undef, 'RETRYING', undef, 0, $interval + 1); - return 1; } From 25d7e8e42699deeff5be5bebbf0251c591bfd7d2 Mon Sep 17 00:00:00 2001 From: kwarklabs <96121168+kwarklabs@users.noreply.github.com> Date: Sun, 1 May 2022 19:38:30 +0100 Subject: [PATCH 2/7] Update StreamingController.pm Updated based on comments from @philippe44 --- Slim/Player/StreamingController.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Slim/Player/StreamingController.pm b/Slim/Player/StreamingController.pm index 11d21ec0ed3..77a3b6714c0 100644 --- a/Slim/Player/StreamingController.pm +++ b/Slim/Player/StreamingController.pm @@ -916,7 +916,7 @@ sub _RetryOrNext { # -> Idle; IF [shouldretry && canretry] THEN continue && $song->isRemote() && $elapsed > 10) # have we managed to play at least 10s? { - if (!$song->duration() || $song->isLive()) { # unknown duration => assume radio + if (!$song->duration() && $song->isLive()) { # unknown duration => assume radio main::INFOLOG && $log->is_info && $log->info('Attempting to re-stream ', $song->currentTrack()->url, ' after time ', $elapsed); $song->retryData({ count => 0, start => Time::HiRes::time()}); _Stream($self, $event, {song => $song}); @@ -960,6 +960,8 @@ sub _Continue { $song->setStatus(Slim::Player::Song::STATUS_PLAYING); } } else { + # This handles resuming after reboot with the caveat that if connection has been lost (no reboot) + # while playing and before reception of next song's 1st byte, we'll resume the current song main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: ". $self->playingSongElapsed()); _JumpToTime($self, $event, {newtime => $self->playingSongElapsed(), restartIfNoSeek => 1}); } @@ -1463,7 +1465,7 @@ sub _willRetry { $retryTime, sub { _playersMessage($self, $song->currentTrack()->url, undef, 'RETRYING', undef, undef, 1); - if (!$song->duration() || $song->isLive()) { # unknown duration => assume radio + if (!$song->duration() && $song->isLive()) { # unknown duration => assume radio main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: 0"); _Stream($self, undef, {song => $song, reconnect => 1}); } else { From ada52a5b94c43df40f5f352b4d2ed96306825012 Mon Sep 17 00:00:00 2001 From: kwarklabs <96121168+kwarklabs@users.noreply.github.com> Date: Wed, 4 May 2022 16:14:23 +0100 Subject: [PATCH 3/7] Update StreamingController.pm Added undef checks for $elapsed and $song->duration() at line 1429. --- Slim/Player/StreamingController.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Slim/Player/StreamingController.pm b/Slim/Player/StreamingController.pm index 77a3b6714c0..cb001f69b3b 100644 --- a/Slim/Player/StreamingController.pm +++ b/Slim/Player/StreamingController.pm @@ -1426,8 +1426,9 @@ sub _willRetry { # Have we managed to play at least 10 seconds of a track and retried fewer times than there are intervals? my $elapsed = $self->playingSongElapsed(); - if ($song->retryData->{'count'} > scalar @retryIntervals || $elapsed < 10 || ($song->duration() - $elapsed < 10)) { - if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - too many retries already or track is within 10 seconds of start or end.")}; + if ($song->retryData->{'count'} > scalar @retryIntervals || !$elapsed || $elapsed < 10 || + !$song->duration() || ($song->duration() - $elapsed < 10)) { + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync, too many retries or track within 10 seconds of start or end.")}; return 0; } From cf6ee1652ab6d962fd93b89d0e5c2ac9928eff54 Mon Sep 17 00:00:00 2001 From: kwarklabs <96121168+kwarklabs@users.noreply.github.com> Date: Fri, 6 May 2022 15:27:59 +0100 Subject: [PATCH 4/7] Update StreamingController.pm Updated based on feedback from @michaelherger and @philippe44. --- Slim/Player/StreamingController.pm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Slim/Player/StreamingController.pm b/Slim/Player/StreamingController.pm index cb001f69b3b..eba340a8d5e 100644 --- a/Slim/Player/StreamingController.pm +++ b/Slim/Player/StreamingController.pm @@ -922,9 +922,11 @@ sub _RetryOrNext { # -> Idle; IF [shouldretry && canretry] THEN continue _Stream($self, $event, {song => $song}); return; } else { - if ($song->duration() - $elapsed < 10) # check we have more than 10 seconds left to play. + $log->debug("Elapsed: " . $elapsed); + $log->debug("Duration: " . $song->duration()); + if (!$elapsed || !$song->duration() || $song->duration() < $elapsed || $song->duration() - $elapsed < 10) # check we have more than 10 seconds left to play. { - if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - track is within 10 seconds of end.")}; + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync or track is within 10 seconds of end.")}; } else { # get seek data from protocol handler. if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Getting seek data from protocol handler.")}; @@ -1426,8 +1428,10 @@ sub _willRetry { # Have we managed to play at least 10 seconds of a track and retried fewer times than there are intervals? my $elapsed = $self->playingSongElapsed(); + $log->debug("Elapsed: " . $elapsed); + $log->debug("Duration: " . $song->duration()); if ($song->retryData->{'count'} > scalar @retryIntervals || !$elapsed || $elapsed < 10 || - !$song->duration() || ($song->duration() - $elapsed < 10)) { + !$song->duration() || $song->duration() < $elapsed || ($song->duration() - $elapsed < 10)) { if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync, too many retries or track within 10 seconds of start or end.")}; return 0; } From e0c5faa037472f1396d9b29cf8f37ffba1eebca1 Mon Sep 17 00:00:00 2001 From: kwarklabs <96121168+kwarklabs@users.noreply.github.com> Date: Fri, 6 May 2022 16:00:01 +0100 Subject: [PATCH 5/7] Update StreamingController.pm --- Slim/Player/StreamingController.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Slim/Player/StreamingController.pm b/Slim/Player/StreamingController.pm index eba340a8d5e..a326dff878e 100644 --- a/Slim/Player/StreamingController.pm +++ b/Slim/Player/StreamingController.pm @@ -924,7 +924,7 @@ sub _RetryOrNext { # -> Idle; IF [shouldretry && canretry] THEN continue } else { $log->debug("Elapsed: " . $elapsed); $log->debug("Duration: " . $song->duration()); - if (!$elapsed || !$song->duration() || $song->duration() < $elapsed || $song->duration() - $elapsed < 10) # check we have more than 10 seconds left to play. + if (!$elapsed || !$song->duration() || !($elapsed < $song->duration()) || $song->duration() - $elapsed < 10) # check we have more than 10 seconds left to play. { if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync or track is within 10 seconds of end.")}; } else { @@ -1431,7 +1431,7 @@ sub _willRetry { $log->debug("Elapsed: " . $elapsed); $log->debug("Duration: " . $song->duration()); if ($song->retryData->{'count'} > scalar @retryIntervals || !$elapsed || $elapsed < 10 || - !$song->duration() || $song->duration() < $elapsed || ($song->duration() - $elapsed < 10)) { + !$song->duration() || !($elapsed < $song->duration()) || ($song->duration() - $elapsed < 10)) { if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync, too many retries or track within 10 seconds of start or end.")}; return 0; } From a611efd0d05ba36cd2e40144419a9e6b3ae2d0e0 Mon Sep 17 00:00:00 2001 From: kwarklabs <96121168+kwarklabs@users.noreply.github.com> Date: Wed, 11 May 2022 15:58:17 +0100 Subject: [PATCH 6/7] Update StreamingController.pm For transcoded streams sub _RetryOrNext now checks that there is more than the duration a full player buffer remaining to stream before retrying. For non-transcoded streams sub _willRetry now caclulates check that the player has not received all of the stream bytes before retrying. --- Slim/Player/StreamingController.pm | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Slim/Player/StreamingController.pm b/Slim/Player/StreamingController.pm index a326dff878e..887ff20fda7 100644 --- a/Slim/Player/StreamingController.pm +++ b/Slim/Player/StreamingController.pm @@ -924,9 +924,13 @@ sub _RetryOrNext { # -> Idle; IF [shouldretry && canretry] THEN continue } else { $log->debug("Elapsed: " . $elapsed); $log->debug("Duration: " . $song->duration()); - if (!$elapsed || !$song->duration() || !($elapsed < $song->duration()) || $song->duration() - $elapsed < 10) # check we have more than 10 seconds left to play. + $log->debug("bufferSize: " . master($self)->bufferSize); + $log->debug("songbitrate: " . $song->bitrate()); + $log->debug("streambitrate: " . $song->streambitrate()); + if (!$elapsed || !$song->duration() || !master($self)->bufferSize || !$song->streambitrate() || + !($elapsed < $song->duration()) || ($song->duration() - $elapsed) < ((master($self)->bufferSize * 8) / $song->streambitrate())) # check we have more than buffer left to play. { - if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync or track is within 10 seconds of end.")}; + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync or track is within buffer length end.")}; } else { # get seek data from protocol handler. if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Getting seek data from protocol handler.")}; @@ -1429,10 +1433,14 @@ sub _willRetry { # Have we managed to play at least 10 seconds of a track and retried fewer times than there are intervals? my $elapsed = $self->playingSongElapsed(); $log->debug("Elapsed: " . $elapsed); - $log->debug("Duration: " . $song->duration()); + $log->debug("songBytes: " . master($self)->songBytes); + $log->debug("Duration: " . $song->duration()); + $log->debug("songbitrate: " . $song->bitrate()); + $log->debug("streambitrate: " . $song->streambitrate()); if ($song->retryData->{'count'} > scalar @retryIntervals || !$elapsed || $elapsed < 10 || - !$song->duration() || !($elapsed < $song->duration()) || ($song->duration() - $elapsed < 10)) { - if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync, too many retries or track within 10 seconds of start or end.")}; + (master($self)->songBytes && $song->duration() && $song->streambitrate() && + (master($self)->songBytes >= ($song->duration() * $song->streambitrate() / 8)))) { + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync, too many retries, track within 10 seconds of start or at end.")}; return 0; } From 23db8b7b673dd2351646e6f49cd617dd166f55dd Mon Sep 17 00:00:00 2001 From: kwarklabs <96121168+kwarklabs@users.noreply.github.com> Date: Thu, 12 May 2022 12:49:39 +0100 Subject: [PATCH 7/7] Update StreamingController.pm --- Slim/Player/StreamingController.pm | 77 +++++++++++++++++------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/Slim/Player/StreamingController.pm b/Slim/Player/StreamingController.pm index 887ff20fda7..0c993fcf114 100644 --- a/Slim/Player/StreamingController.pm +++ b/Slim/Player/StreamingController.pm @@ -908,9 +908,8 @@ sub _RetryOrNext { # -> Idle; IF [shouldretry && canretry] THEN continue my ($self, $event, $params) = @_; _setStreamingState($self, IDLE); - my $song = streamingSong($self); - my $elapsed = playingSongElapsed($self); - my $stillToPlay = master($self)->outputBufferFullness() / (44100 * 8); + my $song = streamingSong($self); + my $elapsed = playingSongElapsed($self); if ($song == playingSong($self) && $song->isRemote() @@ -922,21 +921,29 @@ sub _RetryOrNext { # -> Idle; IF [shouldretry && canretry] THEN continue _Stream($self, $event, {song => $song}); return; } else { - $log->debug("Elapsed: " . $elapsed); - $log->debug("Duration: " . $song->duration()); - $log->debug("bufferSize: " . master($self)->bufferSize); - $log->debug("songbitrate: " . $song->bitrate()); - $log->debug("streambitrate: " . $song->streambitrate()); - if (!$elapsed || !$song->duration() || !master($self)->bufferSize || !$song->streambitrate() || - !($elapsed < $song->duration()) || ($song->duration() - $elapsed) < ((master($self)->bufferSize * 8) / $song->streambitrate())) # check we have more than buffer left to play. - { + + my $duration = $song->duration(); + my $bufferSize = master($self)->bufferSize; + my $streambitrate = $song->streambitrate(); + + if (main::DEBUGLOG && $log->is_debug) { + $log->debug("Elapsed: " . $elapsed); + $log->debug("Duration: " . $duration); + $log->debug("bufferSize: " . $bufferSize); + $log->debug("songbitrate: " . $song->bitrate()); + $log->debug("streambitrate: " . $streambitrate); + } + + # check we have more than buffer left to play. + if (!$elapsed || !$duration || !$bufferSize || !$streambitrate || + !($elapsed < $duration) || ($duration - $elapsed) < (($bufferSize * 8) / $streambitrate)) { if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync or track is within buffer length end.")}; } else { - # get seek data from protocol handler. - if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Getting seek data from protocol handler.")}; - my $seekdata = $song->getSeekData($elapsed); - main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: ". $elapsed); - _Stream($self, undef, {song => $song, seekdata => $seekdata, reconnect => 1}); + # get seek data from protocol handler. + if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Getting seek data from protocol handler.")}; + my $seekdata = $song->getSeekData($elapsed); + main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: ". $elapsed); + _Stream($self, undef, {song => $song, seekdata => $seekdata, reconnect => 1}); return; } } @@ -1422,32 +1429,38 @@ sub _willRetry { $song ||= streamingSong($self); return 0 if !$song; - # $song->retryData() will be undef is we are not retrying already. - if (!$song->retryData()) { + my $retry = $song->retryData(); + # $song->retryData() will be undef if we are not retrying already. + if (!$retry) { $log->info('no retry data'); $song->retryData({ count => 0, start => Time::HiRes::time()}); } $song->retryData->{'count'} += 1; + + my $elapsed = $self->playingSongElapsed(); + my $songBytes = master($self)->songBytes; + my $duration = $song->duration(); + my $streambitrate = $song->streambitrate(); + + if (main::DEBUGLOG && $log->is_debug) { + $log->debug("Elapsed: " . $elapsed); + $log->debug("songBytes: " . $songBytes); + $log->debug("Duration: " . $duration); + $log->debug("songbitrate: " . $song->bitrate()); + $log->debug("streambitrate: " . $streambitrate); + } # Have we managed to play at least 10 seconds of a track and retried fewer times than there are intervals? - my $elapsed = $self->playingSongElapsed(); - $log->debug("Elapsed: " . $elapsed); - $log->debug("songBytes: " . master($self)->songBytes); - $log->debug("Duration: " . $song->duration()); - $log->debug("songbitrate: " . $song->bitrate()); - $log->debug("streambitrate: " . $song->streambitrate()); - if ($song->retryData->{'count'} > scalar @retryIntervals || !$elapsed || $elapsed < 10 || - (master($self)->songBytes && $song->duration() && $song->streambitrate() && - (master($self)->songBytes >= ($song->duration() * $song->streambitrate() / 8)))) { + if ($song->retryData->{'count'} > scalar @retryIntervals || !$elapsed || $elapsed < 10 || + ($songBytes && $duration && $streambitrate && ($songBytes >= ($duration * $streambitrate / 8)))) { if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Will not retry - no player sync, too many retries, track within 10 seconds of start or at end.")}; return 0; } - - # Define $limit as the time until which a retry is allowed. my $limit; my $next = nextsong($self); + # Define $limit as the time until which a retry is allowed. if (defined $next && $next != $song->index) { $limit = RETRY_LIMIT_PLAYLIST; } else { @@ -1470,21 +1483,21 @@ sub _willRetry { return 0; } - my $queue = $self->{'songqueue'}; $queue->[0]->setStatus(Slim::Player::Song::STATUS_READY) if scalar @$queue; + Slim::Utils::Timers::setTimer( $self, $retryTime, sub { _playersMessage($self, $song->currentTrack()->url, undef, 'RETRYING', undef, undef, 1); - if (!$song->duration() && $song->isLive()) { # unknown duration => assume radio + if (!$song->duration() && $song->isLive()) { # unknown duration => assume radio main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: 0"); _Stream($self, undef, {song => $song, reconnect => 1}); } else { # get seek data from protocol handler. if ( main::DEBUGLOG && $log->is_debug ) {$log->debug("Getting seek data from protocol handler.")}; - my $seekdata = $song->getSeekData($elapsed); + my $seekdata = $song->getSeekData($elapsed); main::INFOLOG && $log->is_info && $log->info("Restarting playback at time offset: ". $elapsed); _Stream($self, undef, {song => $song, seekdata => $seekdata, reconnect => 1}); }