Technical Q&A QA1820
How do I achieve smooth video scrubbing with AVPlayer seekToTime:?
Q: My app allows the user to scrub video files using a slider control in combination with AVPlayer seekToTime: but there is a considerable lag in the display of the video frames. How can I achieve smoother scrubbing?
A: Avoid making calls to AVPlayer seekToTime: in rapid succession. This will cancel the seeks in progress, resulting in a lot of seeking and not a lot of displaying of the target frames. Instead, use the completion handler variant of AVPlayer seekToTime: and wait for a seek in progress to complete first before issuing another. Listing 1 and Listing 2 give examples of this technique (Note: these examples assume a valid player object has been created and the player's current item status is being maintained via key-value observing. See the AV Foundation Programming Guide for more information).
Using the completion handler variant of AVPlayer seekToTime: for smoother scrubbing (Objective-C).
@import AVFoundation;
@interface MyClass ()
{
AVPlayer player;
BOOL isSeekInProgress;
CMTime chaseTime;
AVPlayerStatus playerCurrentItemStatus; // your player.currentItem.status
...
}
@implementation MyClass
...
- (void)stopPlayingAndSeekSmoothlyToTime:(CMTime)newChaseTime
{
[self->player pause];
if (CMTIME_COMPARE_INLINE(newChaseTime, !=, self->chaseTime))
{
self->chaseTime = newChaseTime;
if (!self->isSeekInProgress)
[self trySeekToChaseTime];
}
}
- (void)trySeekToChaseTime
{
if (playerCurrentItemStatus == AVPlayerItemStatusUnknown)
{
// wait until item becomes ready (KVO player.currentItem.status)
}
else if (playerCurrentItemStatus == AVPlayerItemStatusReadyToPlay)
{
[self actuallySeekToTime];
}
}
- (void)actuallySeekToTime
{
self->isSeekInProgress = YES;
CMTime seekTimeInProgress = self->chaseTime;
[self->player seekToTime:seekTimeInProgress toleranceBefore:kCMTimeZero
toleranceAfter:kCMTimeZero completionHandler:
^(BOOL isFinished)
{
if (CMTIME_COMPARE_INLINE(seekTimeInProgress, ==, self->chaseTime))
self->isSeekInProgress = NO;
else
[self trySeekToChaseTime];
}];
}
@end