Зацикливание видео с AVFoundation "плеере АВ"?

есть ли относительно простой способ зацикливания видео в AVFoundation?

Я создал свой AVPlayer и AVPlayerLayer так:

avPlayer = [[AVPlayer playerWithURL:videoUrl] retain];
avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain];

avPlayerLayer.frame = contentView.layer.bounds;
[contentView.layer addSublayer: avPlayerLayer];

и затем я играю свое видео с:

[avPlayer play];

видео воспроизводится нормально, но останавливается в конце. С MPMoviePlayerController все, что вам нужно сделать, это установить его repeatMode свойство для правильного значения. Похоже, что на AVPlayer нет аналогичного свойства. Там также не кажется, что обратный вызов, который скажет я, когда фильм закончен, чтобы я мог искать начало и играть его снова.

Я не использую MPMoviePlayerController, потому что у него есть некоторые серьезные ограничения. Я хочу иметь возможность воспроизводить сразу несколько видеопотоков.

15 ответов


вы можете получить уведомление, когда игрок заканчивается. Проверка AVPlayerItemDidPlayToEndTimeNotification

при настройке плеера:

ObjC

  avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone; 

  [[NSNotificationCenter defaultCenter] addObserver:self
                                           selector:@selector(playerItemDidReachEnd:)
                                               name:AVPlayerItemDidPlayToEndTimeNotification
                                             object:[avPlayer currentItem]];

это помешает игроку сделать паузу в конце.

в уведомлении:

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    AVPlayerItem *p = [notification object];
    [p seekToTime:kCMTimeZero];
}

это будет перемотать фильм.

Не забудьте отменить регистрацию уведомления при освобождении игрока.

Свифт

NotificationCenter.default.addObserver(self,
                                       selector: #selector(playerItemDidReachEnd(notification:)),
                                       name: Notification.Name.AVPlayerItemDidPlayToEndTime,
                                       object: avPlayer?.currentItem)

 @objc func playerItemDidReachEnd(notification: Notification) {
        if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
            playerItem.seek(to: kCMTimeZero, completionHandler: nil)
        }
    }

Если это поможет, в iOS / tvOS 10 есть новый AVPlayerLooper (), который вы можете использовать для создания бесшовного цикла видео (Swift):

player = AVQueuePlayer()
playerLayer = AVPlayerLayer(player: player)
playerItem = AVPLayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
player.play()    

Это было представлено на WWDC 2016 в "достижения в воспроизведении AVFoundation": https://developer.apple.com/videos/play/wwdc2016/503/

даже используя этот код, у меня была икота, пока я не подал отчет об ошибке с Apple и не получил этот ответ:

файл фильма, имеющий продолжительность фильма больше, чем аудио / видео треки проблема. FigPlayer_File отключает беззазорный переход, потому что редактирование звуковой дорожки короче, чем продолжительность фильма (15.682 vs 15.787).

вам нужно либо исправить файлы фильмов, чтобы иметь продолжительность фильма и длительность трека должна быть одинаковой или вы можете использовать диапазон времени параметр AVPlayerLooper (установить диапазон времени от 0 до продолжительности аудио трек)

оказывается, что Premiere экспортировал файлы с помощью звуковая дорожка немного отличается по длине от видео. В моем случае было нормально Полностью удалить звук, и это исправило проблему.


на Свифт:

вы можете получить уведомление, когда игрок заканчивается... проверьте AVPlayerItemDidPlayToEndTimeNotification

при настройке плеера:

avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None

NSNotificationCenter.defaultCenter().addObserver(self, 
                                                 selector: "playerItemDidReachEnd:", 
                                                 name: AVPlayerItemDidPlayToEndTimeNotification, 
                                                 object: avPlayer.currentItem)

это помешает игроку сделать паузу в конце.

в уведомлении:

func playerItemDidReachEnd(notification: NSNotification) {
    if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
        playerItem.seekToTime(kCMTimeZero)
    }
}

Swift3

NotificationCenter.default.addObserver(self,
    selector: #selector(PlaylistViewController.playerItemDidReachEnd),
     name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
     object: avPlayer?.currentItem)

это будет перемотать фильм.

не забудьте отменить регистрацию уведомления при освобождении игрок.


вот что я сделал, чтобы предотвратить проблему паузы-икоты:

Свифт:

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                       object: nil,
                                       queue: nil) { [weak self] note in
                                        self?.avPlayer.seek(to: kCMTimeZero)
                                        self?.avPlayer.play()
}

Цель C:

__weak typeof(self) weakSelf = self; // prevent memory cycle
NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
[noteCenter addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
                        object:nil
                         queue:nil
                    usingBlock:^(NSNotification *note) {
                        [weakSelf.avPlayer seekToTime:kCMTimeZero];
                        [weakSelf.avPlayer play];
                    }];

Примечание: Я не использую avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone как это не нужен.


Я рекомендую использовать AVQueuePlayer для плавного цикла видео. Добавить наблюдателя уведомлений

AVPlayerItemDidPlayToEndTimeNotification

и в его селекторе, цикл видео

AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];

чтобы избежать разрыва при перемотке видео, Использование нескольких копий одного и того же актива в композиции хорошо сработало для меня. Я нашел его здесь: www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (ссылка мертва).

AVURLAsset *tAsset = [AVURLAsset assetWithURL:tURL];
CMTimeRange tEditRange = CMTimeRangeMake(CMTimeMake(0, 1), CMTimeMake(tAsset.duration.value, tAsset.duration.timescale));
AVMutableComposition *tComposition = [[[AVMutableComposition alloc] init] autorelease];
for (int i = 0; i < 100; i++) { // Insert some copies.
    [tComposition insertTimeRange:tEditRange ofAsset:tAsset atTime:tComposition.duration error:nil];
}
AVPlayerItem *tAVPlayerItem = [[AVPlayerItem alloc] initWithAsset:tComposition];
AVPlayer *tAVPlayer = [[AVPlayer alloc] initWithPlayerItem:tAVPlayerItem];

Для Swift 3 & 4

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
     self.avPlayer?.seek(to: kCMTimeZero)
     self.avPlayer?.play()
}

это сработало для меня без проблем с икотой,точка в паузы игрок перед вызовом метода seekToTime:

  1. "плеере АВ" инит

    let url = NSBundle.mainBundle().URLForResource("loop", withExtension: "mp4")
    let playerItem = AVPlayerItem(URL: url!)
    
    self.backgroundPlayer = AVPlayer(playerItem: playerItem)
    let playerLayer = AVPlayerLayer(player: self.backgroundPlayer)
    
    playerLayer.frame = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
    self.layer.addSublayer(playerLayer)
    self.backgroundPlayer!.actionAtItemEnd = .None
    self.backgroundPlayer!.play()
    
  2. Регистрация уведомления

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
    
  3. функции videoLoop

    func videoLoop() {
      self.backgroundPlayer?.pause()
      self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero)
      self.backgroundPlayer?.play()
    }
    

после загрузки видео в AVPlayer (через его AVPlayerItem, конечно):

 [self addDidPlayToEndTimeNotificationForPlayerItem:item];

метод addDidPlayToEndTimeNotificationForplayeritem:

- (void)addDidPlayToEndTimeNotificationForPlayerItem:(AVPlayerItem *)item
{
    if (_notificationToken)
        _notificationToken = nil;

    /*
     Setting actionAtItemEnd to None prevents the movie from getting paused at item end. A very simplistic, and not gapless, looped playback.
     */
    _player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    _notificationToken = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:item queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
        // Simple item playback rewind.
        [[_player currentItem] seekToTime:kCMTimeZero];
    }];
}

в вашем методе viewWillDisappear:

if (_notificationToken) {
        [[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
        _notificationToken = nil;
    }

в объявлении интерфейса контроллера вашего вида в файле реализации:

id _notificationToken;

нужно увидеть это вверх и работает, прежде чем пытаться? Загрузите и запустите этот образец app:

https://developer.apple.com/library/prerelease/ios/samplecode/AVBasicVideoOutput/Listings/AVBasicVideoOutput_APLViewController_m.html#//apple_ref/doc/uid/DTS40013109-AVBasicVideoOutput_APLViewController_m-DontLinkElementID_8

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


вы можете добавить avplayeritemdidplaytoendtimenotification наблюдателя и воспроизведения видео от начала в селекторе, код, как показано ниже

 //add observer
[[NSNotificationCenter defaultCenter] addObserver:self                                                 selector:@selector(playbackFinished:)                                                     name:AVPlayerItemDidPlayToEndTimeNotification
object:_aniPlayer.currentItem];

-(void)playbackFinished:(NSNotification *)notification{
    [_aniPlayer seekToTime:CMTimeMake(0, 1)];//replay from start
    [_aniPlayer play];
}

мое решение в objective-c с AVQueuePlayer-кажется, вам нужно дублировать AVPlayerItem и по завершении воспроизведения первого элемента мгновенно добавить другую копию. "Вид" имеет смысл и работает для меня без каких-либо икоты

NSURL *videoLoopUrl; 
// as [[NSBundle mainBundle] URLForResource:@"assets/yourVideo" withExtension:@"mp4"]];
AVQueuePlayer *_loopVideoPlayer;

+(void) nextVideoInstance:(NSNotification*)notif
{
 AVPlayerItem *currItem = [AVPlayerItem playerItemWithURL: videoLoopUrl];

[[NSNotificationCenter defaultCenter] addObserver:self
                                      selector:@selector(nextVideoInstance:)
                                      name:AVPlayerItemDidPlayToEndTimeNotification
                                      object: currItem];

 [_loopVideoPlayer insertItem:currItem afterItem:nil];
 [_loopVideoPlayer advanceToNextItem];

}

+(void) initVideoPlayer {
 videoCopy1 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
 videoCopy2 = [AVPlayerItem playerItemWithURL: videoLoopUrl];
 NSArray <AVPlayerItem *> *dummyArray = [NSArray arrayWithObjects: videoCopy1, videoCopy2, nil];
 _loopVideoPlayer = [AVQueuePlayer queuePlayerWithItems: dummyArray];

 [[NSNotificationCenter defaultCenter] addObserver: self
                                      selector: @selector(nextVideoInstance:)
                                      name: AVPlayerItemDidPlayToEndTimeNotification
                                      object: videoCopy1];

 [[NSNotificationCenter defaultCenter] addObserver: self
                                      selector: @selector(nextVideoInstance:)
                                      name: AVPlayerItemDidPlayToEndTimeNotification
                                      object: videoCopy2];
}

https://gist.github.com/neonm3/06c3b5c911fdd3ca7c7800dccf7202ad


следующее работает для меня в WKWebView в swift 4.1 Основная часть WKWebView в WKwebviewConfiguration

wkwebView.navigationDelegate = self
wkwebView.allowsBackForwardNavigationGestures = true
self.wkwebView =  WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
let config = WKWebViewConfiguration()
config.allowsInlineMediaPlayback = true
wkwebView = WKWebView(frame: wkwebView.frame, configuration: config)
self.view.addSubview(wkwebView)
self.wkwebView.load(NSURLRequest(url: URL(string: self.getUrl())!) as URLRequest)

то, что я сделал, это сделать его цикл игры, как мой код ниже:

[player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0)
queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
    float current = CMTimeGetSeconds(time);
    float total = CMTimeGetSeconds([playerItem duration]);
    if (current >= total) {
        [[self.player currentItem] seekToTime:kCMTimeZero];
        [self.player play];
    }
}];

используйте AVPlayerViewController ниже кода, он работает для меня

        let type : String! = "mp4"
        let targetURL : String? = NSBundle.mainBundle().pathForResource("Official Apple MacBook Air Video   YouTube", ofType: "mp4")

        let videoURL = NSURL(fileURLWithPath:targetURL!)


        let player = AVPlayer(URL: videoURL)
        let playerController = AVPlayerViewController()

        playerController.player = player
        self.addChildViewController(playerController)
        self.playView.addSubview(playerController.view)
        playerController.view.frame = playView.bounds

        player.play()

все элементы управления, которые будут показаны, надеюсь, что его полезным


/* "numberOfLoops" is the number of times that the sound will return to the beginning upon reaching the end. 
A value of zero means to play the sound just once.
A value of one will result in playing the sound twice, and so on..
Any negative number will loop indefinitely until stopped.
*/
@property NSInteger numberOfLoops;

Это свойство уже определено внутри AVAudioPlayer. Надеюсь, это поможет вам. Я использую Xcode 6.3.