Luke Luke - 2 months ago 18
iOS Question

Throttling UISlider scrubbing in AVPlayerDemo when using AirPlay

After setting

mPlayer.usesExternalPlaybackWhileExternalScreenIsActive
to
YES
in AVPlayerDemoPlaybackViewController of Apple's AVPlayerDemo sample project, how do you throttle the scrubbing so it doesn't lag behind on the AppleTV?

What I mean is that when you move the slider really fast back and forth the AppleTV performs each and every
seekToTime
operation, but takes longer to do it then the user takes to slide.

One of the problems with the demo is it uses both the "Touch Drag Inside" and "Value Changed" events which causes it to send the same value twice. If you remove "Value Changed" it improves a bit, but still lags.

I've tried rounding to whole seconds and then only send
seekToTime
when the second changes, but that doesn't seem to help as much. What I really need to do is send fewer commands the faster the user moves the slider, but more when the user moves slower.

Any ideas on how to accomplish this?

Answer

The UISlider already somewhat throttles itself. The faster you move it the fewer values you get from point A to point B. This isn't enough to stop the seek operations from stacking up over AirPlay.

You can, however, use the seekToTime:completionHandler: to prevent the stack up like this:

if(seeking) {
    return;
}

seeking = YES;
[player seekToTime:CMTimeMakeWithSeconds(time, NSEC_PER_SEC) completionHandler:^(BOOL finished) {
    seeking = NO;
}];

This drops any new seeks until the one in progress finishes. This seems to work well. You just need to make sure to send one last seek operation after the user stops scrubbing.

While an NSTimer can do the same thing, it's less accurate, and results will vary depending on the latency of the connection. The completionHandler used in this manner ensures that seeks do not stack up, regardless of latency times.

I also found that the "Value Changed" action of the UISlider can happen before any touch start actions. So it's better to use the touch drag inside/outside actions instead, which are guaranteed to happen after a touch start.