3

在Apple 的AVPlayerDemo示例项目的 AVPlayerDemoPlaybackViewController 中设置mPlayer.usesExternalPlaybackWhileExternalScreenIsActive为之后,您如何限制擦洗,使其不会落后于 AppleTV?YES

我的意思是,当您来回快速移动滑块时,AppleTV 会执行每项seekToTime操作,但执行此操作所需的时间比用户滑动所需的时间要长。

该演示的问题之一是它同时使用了“Touch Drag Inside”和“Value Changed”事件,这导致它两次发送相同的值。如果您删除“值已更改”,它会有所改善,但仍然滞后。

我试过四舍五入到整秒,然后只seekToTime在第二秒改变时发送,但这似乎没有多大帮助。我真正需要做的是,用户移动滑块的速度越快,发送的命令就越少,而当用户移动的速度越慢时,发送的命令就越多。

关于如何做到这一点的任何想法?

4

2 回答 2

4

UISlider 已经在某种程度上限制了自己。移动得越快,从 A 点到 B 点获得的值就越少。这不足以阻止搜索操作在 AirPlay 上叠加。

但是,您可以使用seekToTime:completionHandler:来防止像这样堆积:

if(seeking) {
    return;
}

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

这会丢弃任何新的搜索,直到正在进行的搜索完成。这似乎运作良好。您只需要确保在用户停止擦洗后发送最后一次查找操作。

虽然 NSTimer 可以做同样的事情,但它不太准确,并且结果会根据连接的延迟而有所不同。以这种方式使用的完成处理程序可确保搜索不会堆积,无论延迟时间如何。

我还发现 UISlider 的“Value Changed”动作可以在任何触摸启动动作之前发生。所以最好改用触摸拖动内部/外部动作,这保证在触摸开始后发生。

于 2013-02-19T23:39:20.993 回答
0

用一些额外的代码改进了卢克的答案:

static NSTimeInterval ToleranceForAsset(AVAsset *asset) {
    NSTimeInterval tolerance = 0.0;
    for (AVAssetTrack *track in asset.tracks) {
        NSTimeInterval trackTolerance = CMTimeGetSeconds(track.minFrameDuration);
        tolerance = MAX(tolerance, trackTolerance);
    }
    return tolerance;
}

@interface MyPlayerWrapper ()

@property (strong, nonatomic) AVPlayer *player;
@property (assign, nonatomic) NSTimeInterval playerTime;
@property (assign, nonatomic, getter=isSeeking) BOOL seeking;
@property (assign, nonatomic) CGFloat latestSetTime;

@end

@implementation MyPlayerWrapper

- (NSTimeInterval)playerTime {
    return CMTimeGetSeconds(self.player.currentItem.currentTime);
}

- (void)setPlayerTime:(NSTimeInterval)playerTime {
    NSTimeInterval tolerance = ToleranceForAsset(self.player.currentItem.asset);
    if (tolerance) {
        // round to nearest seek tolerance (for example 1/30 sec)
        playerTime = floor(playerTime / tolerance) * tolerance;
    }

    self.latestSetTime = playerTime;
    if (self.isSeeking) {
        return;
    }

    self.seeking = YES;
    [self.player seekToTime:CMTimeMakeWithSeconds(playerTime, self.player.currentItem.duration.timescale) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero completionHandler:^(BOOL finished) {
        self.seeking = NO;
        if (ABS(self.player.currentItem.currentTime - latestSetTime) > MAX(tolerance, DBL_EPSILON)) {
            self.playerTime = latestSetTime;
        }
    }];
}

@end
于 2016-10-04T07:16:28.157 回答