AVFoundation AVPlayerでビデオをループしますか?


142

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には重大な制限があるため、ここでは使用していません。複数のビデオストリームを一度に再生したい。


1
実際の作業コードへのリンクはこの回答を参照してください。stackoverflow.com/questions/7822808/...
MoDJ

回答:


275

プレイヤーが終了すると通知を受け取ることができます。小切手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];
}

これは映画を巻き戻します。

プレーヤーをリリースするときに通知の登録を解除することを忘れないでください。

迅速

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

スウィフト4+

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

6
... [p seekToTime:kCMTimeZero](一種の「巻き戻し」)の直後に再生したい場合は、もう一度[p play]を実行します。
thomax

24
これは必要ないはずです... avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;停止しても停止しないので、もう一度再生するように設定する必要はありません
Bastian

サウンドを再度再生するには[player play];、巻き戻し後に呼び出します。
ケニーウィンカー

12
このソリューションは機能しますが、完全にシームレスではありません。少し間があります。私は何か間違ったことをしていますか?
Joris van Liempd iDeveloper

3
@Praxitelesは、ビューが破棄されたとき、またはビデオプレイヤーまたは何をしても削除したときに、登録を解除する必要があります。[[NSNotificationCenter defaultCenter] removeObserver:self];たとえば、self通知を聞いているときに使用できます。
Bastian

63

役立つ場合は、iOS / tvOS 10に、ビデオのシームレスなループ(Swift)を作成するために使用できる新しいAVPlayerLooper()があります。

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

これはWVDC 2016の「AVFoundation Playbackの進歩」で発表されました:https : //developer.apple.com/videos/play/wwdc2016/503/

このコードを使用していても、Appleにバグレポートを提出してこの応答が得られるまで、一時的な問題がありました。

オーディオ/ビデオトラックよりもムービーのデュレーションが長いムービーファイルが問題です。オーディオトラックの編集が映画の再生時間よりも短いため、FigPlayer_Fileはギャップレストランジションを無効にしています(15.682対15.787)。

ムービーの継続時間とトラックの継続時間を同じ長さにするようにムービーファイルを修正するか、AVPlayerLooperの時間範囲パラメーターを使用できます(時間範囲を0からオーディオトラックの継続時間に設定します)。

Premiereは、ビデオとは長さがわずかに異なるオーディオトラックのファイルをエクスポートしていたことがわかりました。私の場合、オーディオを完全に削除しても問題ありませんでした。


2
他には何もうまくいきませんでした。私はAVPlayerLooperを使用していますが、このバグがあり、ビデオとオーディオの長さの不一致を修正すると問題が解決しました。
ケビン・ヒープ

1
プレミアについての情報ありがとうございます。ルーパーにtimeRangeを追加すると、「ビデオの点滅」の問題が修正されました。
Alexander Flenniken

@Nabhaビデオ内の特定の時間枠でこれを使用することは可能ですか?たとえば、ビデオは60秒ですが、最初の10秒だけループしたいと思います
ランスサマリア

29

ではスウィフト

プレーヤーの終了時に通知を受け取ることができます... 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)

これは映画を巻き戻します。

プレーヤーをリリースするときに通知の登録を解除することを忘れないでください。


4
この方法では、ループ間に小さな問題が発生します。Adobe Premiereでビデオを開き、ビデオに重複するフレームがないことを確認したので、一時的な問題が確実に再生されています。誰かがシームレスに迅速にビデオループを作る方法を見つけましたか?
SpaceManGalaxy 2015年

@SpaceManGalaxyしゃっくりにも気づきました。この不具合を修正する方法を見つけましたか?
ランスサマリア

18

これが一時停止の問題を防ぐために私がやったことです:

迅速:

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必要ないので使用しませんでした。


2
@KostiaDombrovsky実際のデバイスまたは別の動画で試しましたか?
イスラムQ.

@IslamQ。MP4ファイルを録音してから、snapchatのようにループで再生しようとしています。
Kostia Dombrovsky、2015

@KostiaDombrovskyプレイバックとスナップチャットを並べて比較しましたか?開始フレームと終了フレームが一致しないため、一時停止したように見えますが、一時停止することはありません。
イスラムQ.

私もうまくいきませんでした。私は6秒間のビデオに絶え間ない音声があり、この方法で
一瞬の

このアプローチを使用すると、メモリリークが発生します。それは行と関係があり[weakSelf.avPlayer seekToTime:kCMTimeZero]; [weakSelf.avPlayer play];ます-これらの行をコメントアウトすると、メモリリークはなくなります。私はこれを計器でプロファイルしました。
Solsma Dev 2016

3

AVQueuePlayerを使用してビデオをシームレスにループすることをお勧めします。通知オブザーバーを追加する

AVPlayerItemDidPlayToEndTimeNotification

そしてそのセレクターで、あなたのビデオをループします

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

私はこれを試してみましたが、@ Bastianが提案した方法を超える改善は見られません。これでしゃっくりを完全に取り除くことができましたか?
アマドゥール2014

2
@amadourできることは、初期化時にAVQueuePlayerプレーヤーに同じビデオを2つ追加することです。プレーヤーがAVPlayerItemDidPlayToEndTimeNotificationを投稿したら、同じビデオをプレーヤーのキューに追加します。
kevinnguy

3

ビデオが巻き戻されるときのギャップを回避するために、コンポジションで同じアセットの複数のコピーを使用することは私にとってうまくいきました。私はそれをここで見つけました: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];

私はあなたがこのリンクdevbrief.blogspot.se/2011/12/…
フレーム3

2

これは問題なく問題なく機能しました。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()
    }

3
ありがとう—私はこれを試しましたが、それでも私にはしばらく時間がかかります。
Nabha

2

Swift 3および4の場合

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

1

AVQueuePlayerのObjective-Cでの私の解決策-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


1

スウィフト5:

playerLayerに追加する前にplayerItemをキューに追加するなど、以前の回答からいくつかの微調整を行いました。

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のプロパティにします。そうしないと、ビデオは1回しか再生されません。


0

(もちろんAVPlayerItemを介して)AVPlayerにビデオをロードした後:

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

試してみる前に、この稼働状況を確認する必要がありますか?このサンプルアプリをダウンロードして実行します。

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

このコードを使用する私のアプリでは、ビデオの最後と最初の間に一時停止はありません。実際、ビデオによっては、ビデオが最初に戻ったことを通知する方法がないため、タイムコード表示を保存します。


0

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

0

以下はSwift 4.1のWKWebViewで私のために働いていますWKwebviewConfigurationのWKWebViewの主要部分

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

私がやったことは、以下の私のコードのように、ループ再生することです:

[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のSwift 4.2

はい、ビデオをループするAVKit/ AVFoundation使用する比較的簡単な方法がありますAVQueuePlayer()、キー値の観測(KVO)技術とそれのためのトークンを。

これは、CPUの負担を最小限に抑えながら、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() }
            }
        }
    }
}

-1

コードの下で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()

表示されるすべてのコントロールは、それが役立つことを願っています


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

このプロパティは既にで定義されていAVAudioPlayerます。これがお役に立てば幸いです。Xcode 6.3を使用しています。


8
それはオーディオ用であり、AVPlayerではありません
Nissim
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.