I have a TvOS app that plays music with an AVQueuePlayer
(the songs are stored on a remote server). Usually, the songs play smoothly for several hours in a row. Sometimes, it lags for a very brief moment and continues playing. But some other times, it just lags continuously. And when this happens, relaunching the app or rebooting the Apple TV does not solve the problem.
My application does not lag (only the playback of the player), the internet connection on the Apple TV is perfect (checked with SpeedTest) and the remote server is responding normally (my computer has no problem buffering the files from it and the ping is very fast).
So, I've added an observer on the rate
property of my AVQueuePlayer
to check some properties of its currentItem
to have more information about the problem.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if([keyPath isEqualToString:@"rate"] && object == queuePlayer) {
NSLog(@"rate: %@", @(queuePlayer.rate));
NSLog(@"duration: %@", @(CMTimeGetSeconds(queuePlayer.currentItem.duration)));
NSLog(@"time: %@", @(CMTimeGetSeconds(queuePlayer.currentItem.currentTime)));
NSLog(@"playbackLikelyToKeepUp: %@", @(queuePlayer.currentItem.playbackLikelyToKeepUp));
NSLog(@"playbackBufferEmpty: %@", @(queuePlayer.currentItem.playbackBufferEmpty));
NSLog(@"playbackBufferFull: %@", @(queuePlayer.currentItem.playbackBufferFull));
NSLog(@"bufferSize: %@", @([self bufferSize]));
double currentTime = CMTimeGetSeconds(queuePlayer.currentItem.currentTime);
if(queuePlayer.rate == 0 && currentTime != 0 && currentTime < CMTimeGetSeconds(queuePlayer.currentItem.duration) - 0.1) {
[queuePlayer prerollAtRate:1 completionHandler:^(BOOL finished) {
NSLog(@"Finished prerolling");
[queuePlayer play];
}];
}
}
}
- (NSTimeInterval) bufferSize {
NSArray *loadedTimeRanges = [[queuePlayer currentItem] loadedTimeRanges];
CMTimeRange timeRange = [[loadedTimeRanges objectAtIndex:0] CMTimeRangeValue];
Float64 startSeconds = CMTimeGetSeconds(timeRange.start);
Float64 durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval result = startSeconds + durationSeconds;
return result;
}
This results in the following logs:
2016-04-13 15:59:49.947 Virtual Juke[517:427012] rate: 0
2016-04-13 15:59:49.949 Virtual Juke[517:427012] duration: 203.5461224489796
2016-04-13 15:59:49.949 Virtual Juke[517:427012] time: 29.325428829
2016-04-13 15:59:49.950 Virtual Juke[517:427012] playbackLikelyToKeepUp: 1
2016-04-13 15:59:49.950 Virtual Juke[517:427012] playbackBufferEmpty: 1
2016-04-13 15:59:49.950 Virtual Juke[517:427012] playbackBufferFull: 0
2016-04-13 15:59:49.953 Virtual Juke[517:427012] bufferSize: 203.52
2016-04-13 15:59:49.958 Virtual Juke[517:427012] Finished prerolling
2016-04-13 15:59:49.959 Virtual Juke[517:427012] rate: 1
2016-04-13 15:59:50.733 Virtual Juke[517:427012] duration: 203.5461224489796
2016-04-13 15:59:50.733 Virtual Juke[517:427012] time: 28.566945193
2016-04-13 15:59:50.734 Virtual Juke[517:427012] playbackLikelyToKeepUp: 1
2016-04-13 15:59:50.734 Virtual Juke[517:427012] playbackBufferEmpty: 1
2016-04-13 15:59:50.734 Virtual Juke[517:427012] playbackBufferFull: 0
2016-04-13 15:59:50.737 Virtual Juke[517:427012] bufferSize: 203.52
So as you can see, the currentItem
has some contradicting property values. It says that its buffer is empty when it pauses AND when it resumes, but it was ready to play... When I calculate the buffer, it looks like it already buffered the whole song.
What is this problem exactly and why does it occur?