Регулирование очистки UISlider в AVPlayerDemo при использовании AirPlay

После установки mPlayer.usesExternalPlaybackWhileExternalScreenIsActive на YES в AVPlayerDemoPlaybackViewController для Apple AVPlayerDemo пример проекта, как вы регулируете очистку, чтобы она не отставала на AppleTV?

Я имею в виду, что когда вы перемещаете ползунок очень быстро вперед и назад, AppleTV выполняет каждую операцию seekToTime, но это занимает больше времени, чем пользователь тратит на слайд.

Одна из проблем с демонстрацией заключается в том, что в ней используются события «Touch Drag Inside» и «Value Changed», из-за чего одно и то же значение отправляется дважды. Если вы удалите «Значение изменено», оно немного улучшится, но все еще отстает.

Я пробовал округлять до целых секунд, а затем отправлять seekToTime только при изменении секунды, но это, похоже, не очень помогает. Что мне действительно нужно сделать, так это отправить меньше команд, чем быстрее пользователь перемещает ползунок, но больше, когда пользователь перемещается медленнее.

Любые идеи о том, как это сделать?


person Luke    schedule 18.02.2013    source источник


Ответы (2)


UISlider уже несколько задушил себя. Чем быстрее вы перемещаете его, тем меньше значений вы получаете из точки А в точку Б. Этого недостаточно, чтобы предотвратить накопление операций поиска в AirPlay.

Однако вы можете использовать seekToTime:completionHandler:, чтобы предотвратить стек следующим образом:

if(seeking) {
    return;
}

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

Это отбрасывает все новые поиски до тех пор, пока не завершится текущий. Кажется, это работает хорошо. Вам просто нужно обязательно отправить последнюю операцию поиска после того, как пользователь прекратит очистку.

Хотя NSTimer может делать то же самое, он менее точен, и результаты будут различаться в зависимости от задержки соединения. CompleteHandler, используемый таким образом, гарантирует, что запросы не накапливаются, независимо от времени задержки.

Я также обнаружил, что действие UISlider «Значение изменено» может произойти до любых действий запуска касания. Поэтому вместо этого лучше использовать действия с сенсорным перетаскиванием внутри/снаружи, которые гарантированно произойдут после запуска касания.

person Luke    schedule 19.02.2013

Улучшенный ответ Люка с дополнительным кодом:

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
person k06a    schedule 04.10.2016