使用 AirPlay 时在 AVPlayerDemo 中限制 UISlider 擦洗

Posted

技术标签:

【中文标题】使用 AirPlay 时在 AVPlayerDemo 中限制 UISlider 擦洗【英文标题】:Throttling UISlider scrubbing in AVPlayerDemo when using AirPlay 【发布时间】:2013-02-18 03:01:14 【问题描述】:

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

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

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

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

关于如何实现这一点的任何想法?

【问题讨论】:

【参考方案1】:

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

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

if(seeking) 
    return;


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

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

虽然 NSTimer 可以做同样的事情,但它不太准确,并且结果会因连接的延迟而异。以这种方式使用的 completionHandler 可确保无论延迟时间如何,搜索都不会堆积。

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

【讨论】:

【参考方案2】:

用一些额外的代码改进了 Luke 的回答:

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

【讨论】:

以上是关于使用 AirPlay 时在 AVPlayerDemo 中限制 UISlider 擦洗的主要内容,如果未能解决你的问题,请参考以下文章

使用 AVPlayer 的 Airplay 并不总是有效。 “iPhone”选项在 Airplay 菜单中列出两次

使用 AVPlayer 进行 Airplay?

使用 MPMoviePlayerController 禁用 AirPlay

设置允许 airplay 不仍然通过 airplay 发送声音

如何检查是不是正在使用airplay?

在 iOS 上使用 Airplay 时显示推送通知?