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):
Listing 1 Using the completion handler variant of AVPlayer
seekToTime
: for smoother scrubbing (Swift).
import AVFoundation |
class MyClass { |
var isSeekInProgress = false |
let player = <#A valid player object #> |
var chaseTime = kCMTimeZero |
// your player.currentItem.status |
var playerCurrentItemStatus:AVPlayerItemStatus = .Unknown |
... |
func stopPlayingAndSeekSmoothlyToTime(newChaseTime:CMTime) |
{ |
player.pause() |
if CMTimeCompare(newChaseTime, chaseTime) != 0 |
{ |
chaseTime = newChaseTime; |
if !isSeekInProgress |
{ |
trySeekToChaseTime() |
} |
} |
} |
func trySeekToChaseTime() |
{ |
if playerCurrentItemStatus == .Unknown |
{ |
// wait until item becomes ready (KVO player.currentItem.status) |
} |
else if playerCurrentItemStatus == .ReadyToPlay |
{ |
actuallySeekToTime() |
} |
} |
func actuallySeekToTime() |
{ |
isSeekInProgress = true |
let seekTimeInProgress = chaseTime |
player.seekToTime(seekTimeInProgress, toleranceBefore: kCMTimeZero, |
toleranceAfter: kCMTimeZero, completionHandler: |
{ (isFinished:Bool) -> Void in |
if CMTimeCompare(seekTimeInProgress, chaseTime) == 0 |
{ |
isSeekInProgress = false |
} |
else |
{ |
trySeekToChaseTime() |
} |
}) |
} |
} |
Listing 2 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 |
Document Revision History
Date | Notes |
---|---|
2016-03-23 | New document that how to achieve smooth video scrubbing with AVPlayer seekToTime: |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-03-23