wake-up-neo.net

Ein Video mit dem AVFoundation AVPlayer durchlaufen?

Gibt es eine relativ einfache Möglichkeit, ein Video in AVFoundation zu schleifen? 

Ich habe meinen AVPlayer und AVPlayerLayer wie folgt erstellt:

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

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

und dann spiele ich mein video mit:

[avPlayer play];

Das Video läuft gut, endet aber am Ende. Mit dem MPMoviePlayerController müssen Sie lediglich die repeatMode -Eigenschaft auf den richtigen Wert setzen. Es scheint keine ähnliche Eigenschaft in AVPlayer zu sein. Es scheint auch kein Rückruf zu geben, der mir sagt, wann der Film zu Ende ist, also kann ich den Anfang suchen und ihn erneut abspielen.

Ich verwende MPMoviePlayerController nicht, da er einige gravierende Einschränkungen hat. Ich möchte mehrere Video-Streams gleichzeitig wiedergeben können.

129
orj

Sie können eine Benachrichtigung erhalten, wenn der Spieler beendet ist. AVPlayerItemDidPlayToEndTimeNotification prüfen

Beim Einrichten des Players:

ObjC

  avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone; 

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

dadurch wird verhindert, dass der Player am Ende pausiert.

in der Benachrichtigung:

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

dadurch wird der Film zurückgespult.

Vergessen Sie nicht, die Registrierung der Benachrichtigung aufzuheben, wenn Sie den Player freigeben.

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)
    }
}
261
Bastian

Wenn es hilft, gibt es in iOS/tvOS 10 einen neuen AVPlayerLooper (), mit dem Sie nahtloses Video-Looping (Swift) erstellen können:

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

Dies wurde auf der WWDC 2016 in "Fortschritte in der AVFoundation-Wiedergabe" vorgestellt: https://developer.Apple.com/videos/play/wwdc2016/503/

Selbst mit diesem Code hatte ich einen Hickup, bis ich einen Fehlerbericht bei Apple einreichte und diese Antwort erhielt:

Die Filmdatei mit einer längeren Filmdauer als Audio-/Videospuren lautet das Problem. FigPlayer_File deaktiviert den lückenlosen Übergang, weil Audiospurbearbeitung ist kürzer als die Filmdauer (15.682 vs 15.787).

Sie müssen die Filmdateien entweder auf die Filmdauer und .__ einstellen. Track-Dauern gleich lang sein oder Sie können den Zeitbereich verwenden Parameter des AVPlayerLooper (Zeitbereich von 0 bis Dauer von Audiospur einstellen)

Es stellte sich heraus, dass Premiere Dateien mit einer etwas anderen Audiospur als das Video exportiert hatte. In meinem Fall war es in Ordnung, das Audio vollständig zu entfernen, und das Problem wurde behoben.

45
Nabha

In Swift :

Sie können eine Benachrichtigung erhalten, wenn der Player beendet ist ... Aktivieren Sie AVPlayerItemDidPlayToEndTimeNotification

beim Einrichten des Players:

avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None

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

dadurch wird verhindert, dass der Player am Ende pausiert.

in der Benachrichtigung:

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)

dadurch wird der Film zurückgespult.

Vergessen Sie nicht, die Registrierung der Benachrichtigung aufzuheben, wenn Sie den Player freigeben.

24
King-Wizard

Am Ende habe ich getan, um das Problem der Pause-Schluckaufe zu verhindern:

Swift:

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

Ziel 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];
                    }];

HINWEIS: Ich habe avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone nicht verwendet, da es nicht benötigt wird.

16
Islam Q.

Ich empfehle die Verwendung von AVQueuePlayer, um Ihre Videos nahtlos zu wiederholen. Fügen Sie den Benachrichtigungsbeobachter hinzu 

AVPlayerItemDidPlayToEndTimeNotification

und schleifen Sie Ihr Video in der Auswahl

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

Um die Lücke zu vermeiden, wenn das Video zurückgespult wird, hat die Verwendung mehrerer Kopien desselben Objekts in einer Komposition für mich gut funktioniert. Ich habe es hier gefunden: www.developers-life.com/avplayer-looping-video-ohne-hiccupdelays.html (Link jetzt tot).

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
user2581875

Für Swift 3 & 4

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

dies funktionierte für mich ohne Probleme mit dem Schluckauf, Punkt ist in Pause der Spieler vor dem Aufrufen der seekToTime-Methode:

  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. registrierungsbenachrichtigung

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
    
  3. videoLoop-Funktion

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

meine Lösung in Objective-C mit AVQueuePlayer - es scheint, dass Sie das AVPlayerItem duplizieren müssen und nach dem Abspielen des ersten Elements sofort eine weitere Kopie hinzufügen. "Art" macht Sinn und arbeitet für mich ohne Schluckauf

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

0
neon M3

Was ich gemacht habe, ist das Loop-Play zu machen, wie mein Code unten:

[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];
    }
}];
0
Victor John

Folgendes funktioniert für mich in WKWebView in Swift 4.1 Der Hauptteil von WKWebView in 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)
0
Nrv

Swift 4.2 in Xcode 10.1.

Ja , es gibt eine relativ einfache Möglichkeit, ein Video in AVKit/AVFoundation zu schleifen, wobei AVQueuePlayer(), die Key-Value-Observation (KVO) -Technik und ein Token dafür verwendet werden.

Dies funktioniert definitiv für eine Reihe von H.264/HEVC-Videos mit einer minimalen Belastung für die CPU.

Hier ist ein Code:

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
ARGeo

sie können einen Beobachter für AVPlayerItemDidPlayToEndTimeNotification hinzufügen und Video vom Start in der Auswahl aus wiedergeben

 //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];
}
0
shujucn