Зацикливание видео с AVFoundation AVPlayer? - PullRequest
136 голосов
/ 19 марта 2011

Есть ли относительно простой способ зацикливания видео в 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, потому что он имеет некоторые серьезные ограничения. Я хочу иметь возможность воспроизводить несколько видеопотоков одновременно.

Ответы [ 16 ]

265 голосов
/ 20 марта 2011

Вы можете получить уведомление, когда игрок заканчивается.Установите флажок 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];
}

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

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

Swift

avPlayer?.actionAtItemEnd = .none

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

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

Swift 4 +

@objc func playerItemDidReachEnd(notification: Notification) {
    if let playerItem = notification.object as? AVPlayerItem {
        playerItem.seek(to: CMTime.zero, completionHandler: nil)
    }
}
50 голосов
/ 08 июля 2016

Если это поможет, в 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 Playback»: https://developer.apple.com/videos/play/wwdc2016/503/

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

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

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

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

26 голосов
/ 01 мая 2015

In Swift :

Вы можете получить уведомление, когда проигрыватель заканчивается ... проверьте 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)

thisПеремотает фильм.

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

17 голосов
/ 16 октября 2014

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

Swift:

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, так как он не нужен.

3 голосов
/ 17 декабря 2014

Чтобы избежать разрыва при перемотке видео, использование нескольких копий одного и того же актива в композиции помогло мне. Я нашел это здесь: 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];
3 голосов
/ 03 апреля 2014

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

AVPlayerItemDidPlayToEndTimeNotification

и в его селекторе зациклите ваше видео

AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
[self.player insertItem:video afterItem:nil];
[self.player play];
2 голосов
/ 21 марта 2018

Для Swift 3 & 4

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.avPlayer?.currentItem, queue: .main) { _ in
     self.avPlayer?.seek(to: kCMTimeZero)
     self.avPlayer?.play()
}
1 голос
/ 11 ноября 2015

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

  1. init AVPlayer

    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()
    }
    
0 голосов
/ 11 ноября 2018

Swift 4.2 в Xcode 10.1.

Да , есть относительно простой способ зацикливания видео в AVKit / AVFoundation с использованием AVQueuePlayer(), Метод Key-Value Observation (KVO) и маркер для него.

Это определенно работает для нескольких видео H.264 / HEVC с минимальной нагрузкой на процессор.

Воткод:

import UIKit
import AVFoundation
import AVKit

class ViewController: UIViewController {

    private let player = AVQueuePlayer()
    let clips = ["01", "02", "03", "04", "05", "06", "07"]
    private var token: NSKeyValueObservation?
    var avPlayerView = AVPlayerViewController()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(true)

        self.addAllVideosToPlayer()
        present(avPlayerView, animated: true, completion: { self.player.play() })
    }

    func addAllVideosToPlayer() {
        avPlayerView.player = player

        for clip in clips {
            let urlPath = Bundle.main.path(forResource: clip, ofType: "m4v")!
            let url = URL(fileURLWithPath: urlPath)
            let playerItem = AVPlayerItem(url: url)
            player.insert(playerItem, after: player.items().last)

            token = player.observe(\.currentItem) { [weak self] player, _ in
                if self!.player.items().count == 1 { self?.addAllVideosToPlayer() }
            }
        }
    }
}
0 голосов
/ 19 июля 2018

Следующее работает для меня в 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...