首页 文章

用AVFoundation AVPlayer循环播放视频?

提问于
浏览 1938
128

在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 属性设置为正确的值即可 . 没有't appear to be a similar property on AVPlayer. There also doesn'似乎是一个回调,它将告诉我电影什么时候结束,所以我可以寻找开头并重新播放 .

我没有使用MPMoviePlayerController,因为它有一些严重的限制 . 我希望能够一次播放多个视频流 .

16 回答

  • 257

    在Xcode 10.1中 Swift 4.2 .

    Yes ,有一种相对简单的方法可以使用 AVQueuePlayer() ,键值观察(KVO)技术和一个令牌来循环 AVKit / AVFoundation 中的视频 .

    这绝对适用于一堆H.264 / HEVC视频,CPU负担最小 .

    这是一个代码:

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

    我做的是让它循环播放,就像我的代码如下:

    [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];
        }
    }];
    
  • 24
    /* "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 .

  • 15

    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)
    

    这将回放电影 .

    不要忘记在释放播放器时取消注册通知 .

  • 3

    玩家结束时可以获得通知 . 检查 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

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

    我建议使用AVQueuePlayer无缝循环播放视频 . 添加通知观察器

    AVPlayerItemDidPlayToEndTimeNotification
    

    并在其选择器中循环播放视频

    AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL];
    [self.player insertItem:video afterItem:nil];
    [self.player play];
    
  • 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

    以下是我为防止暂停打嗝问题而采取的措施:

    Swift:

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

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

    NOTE: 我没有使用 avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone ,因为它不需要 .

  • 0

    为了避免视频倒带时的差距,在合成中使用同一资产的多个副本对我来说效果很好 . 我在这里找到了它: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];
    
  • 0

    这对我来说没有打嗝问题, point is in pausing 在调用seekToTime方法之前的播放器:

    • 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()
    
    • 注册通知
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
    
    • videoLoop功能
    func videoLoop() {
      self.backgroundPlayer?.pause()
      self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero)
      self.backgroundPlayer?.play()
    }
    
  • 0

    将视频加载到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;
    

    在尝试之前需要看到这个正常运行?下载并运行此示例应用程序:

    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

    我的解决方案在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

  • 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

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

    所有控件都要展示,希望它有所帮助

  • -1

    你可以添加一个AVPlayerItemDidPlayToEndTimeNotification观察者并从选择器中的start开始重放视频,代码如下

    //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];
    }
    
  • -2

    如果它有帮助,在iOS / tvOS 10中,有一个新的AVPlayerLooper()可用于创建无缝的视频循环(Swift):

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

    这是在2016年WWDC上展示的"Advances in AVFoundation Playback":https://developer.apple.com/videos/play/wwdc2016/503/

    即使使用这段代码,在我向Apple提交错误报告并获得此回复之前,我还是有一个打嗝:

    电影持续时间长于音频/视频轨道的电影文件是问题所在 . FigPlayer_File正在禁用无间隙转换,因为音轨编辑比电影持续时间短(15.682 vs 15.787) . 您需要修复影片文件以使影片持续时间和轨道持续时间长度相同,或者您可以使用AVPlayerLooper的时间范围参数(设置时间范围从0到音轨的持续时间)

    事实证明,Premiere一直在导出文件的音轨长度与视频略有不同 . 在我的情况下,完全删除音频是好的,这解决了问题 .

相关问题