AVFoundation AVPlayer ile bir video mu döngüsünüz?


142

AVFoundation'da bir videoyu döngüye almanın nispeten kolay bir yolu var mı?

AVPlayer ve AVPlayerLayer'ları şöyle oluşturdum:

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

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

ve sonra videomu şu şekilde oynatıyorum:

[avPlayer play];

Video iyi oynatılıyor ancak sonunda duruyor. MPMoviePlayerController ile tek yapmanız gereken repeatModeözelliği doğru değere ayarlamaktır. AVPlayer'da benzer bir özellik yok gibi görünüyor. Filmin ne zaman bittiğini söyleyecek bir geri arama da görünmüyor, böylece başlangıca bakıp tekrar oynayabiliyorum.

Bazı ciddi sınırlamaları olduğundan MPMoviePlayerController kullanmıyorum. Aynı anda birden fazla video akışını oynatabilmek istiyorum.


1
Gerçek çalışma koduna bir bağlantı için şu cevaba bakınız: stackoverflow.com/questions/7822808/…
MoDJ

Yanıtlar:


275

Oynatıcı sona erdiğinde bir Bildirim alabilirsiniz. KontrolAVPlayerItemDidPlayToEndTimeNotification

Oynatıcıyı kurarken:

objc

  avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone; 

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

bu, oynatıcının sonunda duraklamasını önler.

bildirimde:

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

bu filmi geri sarar.

Oynatıcıyı serbest bırakırken bildirimin kaydını silmeyi unutmayın.

hızlı

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

6
... ve [p seekToTime: kCMTimeZero] 'dan (bir çeşit "geri") hemen sonra oynatmak istiyorsanız, tekrar [p play] yapın.
thomax

24
bu gerekli olmamalı ... eğer yaparsanız avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;durmayacak, bu yüzden tekrar oynamak için ayarlamanıza gerek yok
Bastian

Sesi tekrar çalmak için [player play];geri sardıktan sonra aramalısınız .
Kenny Winker

12
Bu çözüm işe yarıyor, ancak tamamen sorunsuz değil. Çok küçük bir duraklama var. Yanlış bir şey mi yapıyorum?
Joris van Liempd iDeveloper

3
@Praxiteles, görünüm yok olduğunda veya video oynatıcıyı veya yaptığınız her şeyi kaldırdığınızda kaydını silmeniz gerekir.) [[NSNotificationCenter defaultCenter] removeObserver:self];Örneğin, selfbildirimleri dinlerken kullanabilirsiniz.
Bastian

63

Yardımcı olursa, iOS / tvOS 10'da sorunsuz video döngüsü oluşturmak için kullanabileceğiniz yeni bir AVPlayerLooper () vardır (Swift):

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

Bu WWDC 2016'da "AVFoundation Oynatmada İlerlemeler" bölümünde sunulmuştur: https://developer.apple.com/videos/play/wwdc2016/503/

Bu kodu kullanarak bile, Apple'a bir hata raporu gönderene ve bu yanıtı alana kadar bir hıçkırık vardı:

Film süresi ses / video parçalarından daha uzun olan film dosyası sorun. FigPlayer_File, aralıksız geçişi devre dışı bırakıyor çünkü ses parçası düzenleme film süresinden daha kısa (15.682 ve 15.787).

Film süresinin ve parça sürelerinin aynı uzunlukta olması için film dosyalarını düzeltmeniz gerekir veya AVPlayerLooper'ın zaman aralığı parametresini kullanabilirsiniz (0'dan ses parçasının süresine ayarlayın)

Premiere'in videodan biraz farklı bir ses parçasına sahip dosyaları dışa aktardığı ortaya çıktı. Benim durumumda sesi tamamen kaldırmak iyiydi ve bu sorunu çözdü.


2
Benim için başka hiçbir şey işe yaramadı. Bir AVPlayerLooper kullanıyorum ve bu hatayı aldım ve video / ses uzunlukları arasındaki tutarsızlığı gidermek sorunu çözdü.
Kevin Heap

1
Premiere hakkındaki bu bilgi için teşekkür ederim. Lüper için bir timeRange ekledim ve bu "yanıp sönen video" sorunumu düzeltti.
Alexander Flenniken

@ Nabha bunu video içinde belirli bir zaman dilimi için kullanmak mümkün mü? Örneğin, video 60 saniyedir ancak yalnızca ilk 10 saniyeyi döngüye almak istiyorum
Lance Samaria

29

In Swift :

Oynatıcı sona erdiğinde bir Bildirim alabilirsiniz ... AVPlayerItemDidPlayToEndTimeNotification'ı kontrol edin

oynatıcıyı kurarken:

avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None

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

bu, oynatıcının sonunda duraklamasını önler.

bildirimde:

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)

bu filmi geri sarar.

Oynatıcıyı serbest bırakırken bildirimin kaydını silmeyi unutmayın.


4
Bu yöntemle döngüler arasında küçük bir hıçkırık görüyorum. Videomu Adobe Premier'de açtım ve videoda yinelenen kareler olmadığını doğruladım, bu nedenle kısa hıçkırık kesinlikle oynatılıyor. Bir video döngüsünü hızlı bir şekilde sorunsuz bir şekilde yapmanın bir yolu var mı?
SpaceManGalaxy

@SpaceManGalaxy Ayrıca hıçkırık fark ettim. Bu aksaklığı düzeltmenin bir yolunu buldunuz mu?
Lance Samaria

18

İşte duraklama-hıçkırık sorununu önlemek için ne yaptım:

Swift:

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

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

NOT: Gerekmediği için kullanmadım avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone.


2
@KostiaDombrovsky gerçek bir cihaz veya farklı videolar denediniz mi?
İslam S.

@IslamQ. Bir MP4 dosyası kaydeder ve daha sonra snapchat gibi bir döngüde oynatmaya çalışırım.
Kostia Dombrovsky

@KostiaDombrovsky oynatmanızı snapchat ile yan yana karşılaştırdınız mı? Sanırım başlangıç ​​ve bitiş kareleri eşleşmiyor, duraklatılmış gibi görünüyor, ama asla duraklamıyor.
İslam S.

Benim için de çalışmadı.
Kesintisiz

Bu yaklaşımı kullanırken bellek sızıntısı görüyorum. O ile ilgisi var [weakSelf.avPlayer seekToTime:kCMTimeZero]; [weakSelf.avPlayer play];hatları - Bir bellek sızıntısı yoktur artık bu satırları zaman. Bunu enstrümanlarda profilli yaptım.
Solsma Dev

3

Videolarınızı sorunsuz bir şekilde birleştirmek için AVQueuePlayer'ı kullanmanızı öneririm. Bildirim gözlemcisini ekleyin

AVPlayerItemDidPlayToEndTimeNotification

ve seçicisinde videonuzu döngüye alın

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

Bunu denedim ve @Bastian'ın önerdiği yöntem üzerinde herhangi bir gelişme göstermiyor. Bununla hıçkırık tamamen kaldırıldı mı?
amadour

2
@amadour yapabilecekleriniz AVQueuePlayer oynatıcısına başlatıldığında aynı videodan 2 tanesini eklemek ve oyuncu AVPlayerItemDidPlayToEndTimeNotification öğesini yayınlamak için aynı videoyu oynatıcının kuyruğuna eklemek.
kevinnguy

3

Video geri sarıldığında boşluğu önlemek için, bir kompozisyonda aynı öğenin birden fazla kopyasını kullanmak benim için iyi çalıştı. Burada buldum: www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (bağlantı şimdi ölü).

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

Sanırım bu bağlantı demek istiyorsun devbrief.blogspot.se/2011/12/…
flame3

2

Bu, hıçkırık sorunları olmadan benim için çalıştı, nokta seekToTime yöntemini çağırmadan önce oynatıcıyı duraklatmaktır :

  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. kayıt bildirimi

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
  3. videoLoop işlevi

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

3
Teşekkürler - bunu denedim, ama yine de benim için bir duraklama var.
Nabha

2

Swift 3 ve 4 için

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

1

objektif-c wth AVQueuePlayer benim çözüm - Görünüşe göre AVPlayerItem çoğaltmak zorunda ve ilk öğenin oynatma bittikten sonra anında başka bir kopyasını ekleyin. "Bir çeşit" mantıklı ve aksamadan benim için çalışıyor

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


1

Hızlı 5:

PlayerLayer'a eklemeden önce playerItem'i sıraya eklemek gibi önceki cevaplardan bazı küçük ayarlamalar yaptım.

let playerItem = AVPlayerItem(url: url)
let player = AVQueuePlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: player)

playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)

playerLayer.frame = cell.eventImage.bounds
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill

// Add the playerLayer to a UIView.layer

player.play()

PlayerLooper'ı UIViewController'ınızın bir özelliği yapın, aksi takdirde video yalnızca bir kez oynatılabilir.


0

Videoyu AVPlayer'a yükledikten sonra (elbette AVPlayerItem aracılığıyla):

 [self addDidPlayToEndTimeNotificationForPlayerItem:item];

AddDidPlayToEndTimeNotificationForPlayerItem yöntemi:

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

GörünümünüzWillDisappear yönteminde:

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

Görünüm denetleyicinizin uygulama dosyasındaki arayüz bildiriminde:

id _notificationToken;

Denemeden önce bunu çalışır durumda mı görmeniz gerekiyor? Bu örnek uygulamayı indirin ve çalıştırın:

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

Bu kodu kullanan uygulamamda, videonun sonu ile başlangıç ​​arasında herhangi bir duraklama yok. Aslında, videoya bağlı olarak, videonun tekrar başlangıçta olduğunu söylememin bir yolu yok, zaman kodu ekranını kaydedin.


0

bir AVPlayerItemDidPlayToEndTimeNotification gözlemcisi ekleyebilir ve videoyu seçiciden başlayarak, aşağıdaki gibi kod tekrar oynatabilirsiniz

 //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

Aşağıdaki hızlı 4.1 WKWebView benim için çalışıyor WKwebviewConfiguration WKWebView ana parçası

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

Ne yaptım aşağıdaki kod gibi döngü oynamaya yapmak için:

[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

Xcode 10.1'deki Swift 4.2 .

Evet , bir videoyu içeri AVKit/ AVFoundationkullanarak döngüye almanın nispeten kolay bir yolu var AVQueuePlayer(), Anahtar / Değer İzleme (KVO) tekniği ve bunun için bir belirteç.

Bu kesinlikle CPU için minimum yüke sahip bir grup H.264 / HEVC video için çalışır.

İşte bir kod:

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() }
            }
        }
    }
}

-1

AVPlayerViewController kodun altında kullanın, benim için çalışıyor

        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()

Gösterilecek tüm kontroller, umarım yardımcı olur


-2
/* "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;

Bu özellik zaten içinde tanımlanmış AVAudioPlayer. Umarım bu size yardımcı olabilir. Xcode 6.3 kullanıyorum.


8
ses için değil, AVPlayer için değil
Yariv Nissim
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.