Ios 使用AVPlayer循环播放视频?

Ios 使用AVPlayer循环播放视频?,ios,objective-c,swift,avfoundation,avplayer,Ios,Objective C,Swift,Avfoundation,Avplayer,在AVFoundation中是否有一种相对简单的视频循环方式 我创建了我的AVPlayer和AVPlayerLayer,如下所示: avPlayer = [[AVPlayer playerWithURL:videoUrl] retain]; avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain]; avPlayerLayer.frame = contentView.layer.bounds; [conten

在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,因为它有一些严重的限制。我希望能够一次播放多个视频流。

播放器结束时,您可以收到通知。选中AvPlayerItemDidPlayToEndTime通知

设置播放机时:

avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None

NSNotificationCenter.defaultCenter().addObserver(self, 
                                                 selector: "playerItemDidReachEnd:", 
                                                 name: AVPlayerItemDidPlayToEndTimeNotification, 
                                                 object: avPlayer.currentItem)
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];
}
func playerItemDidReachEnd(notification: NSNotification) {
    if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
        playerItem.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)
    }
}

我建议使用AVQueuePlayer无缝循环您的视频。添加通知观察员

AVPlayerItemDidPlayToEndTimeNotification
在选择器中,循环播放视频

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

以下是我为防止停顿打嗝问题所做的工作:

Swift:

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                       object: nil,
                                       queue: nil) { [weak self] note in
                                        self?.avPlayer.seek(to: kCMTimeZero)
                                        self?.avPlayer.play()
}
__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];
                    }];
private var player: AVPlayer?

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(restartVideo),
                                           name: .AVPlayerItemDidPlayToEndTime,
                                           object: self.player?.currentItem)
}

@objc func restartVideo() {
    player?.pause()
    player?.currentItem?.seek(to: CMTime.zero, completionHandler: { _ in
        self.player?.play()
    })
}

目标C:

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
                                       object: nil,
                                       queue: nil) { [weak self] note in
                                        self?.avPlayer.seek(to: kCMTimeZero)
                                        self?.avPlayer.play()
}
__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];
                    }];
private var player: AVPlayer?

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(restartVideo),
                                           name: .AVPlayerItemDidPlayToEndTime,
                                           object: self.player?.currentItem)
}

@objc func restartVideo() {
    player?.pause()
    player?.currentItem?.seek(to: CMTime.zero, completionHandler: { _ in
        self.player?.play()
    })
}


注意:我没有使用
avPlayer.actionAtItemEnd=avplayeraactionatitemendnone
,因为它不是必需的。

为了避免视频重放时出现间隙,在一篇文章中使用同一资源的多个副本对我来说效果很好。我在这里找到了它:www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html(链接现已失效)

avurlaste*tAsset=[avurlaste-assetwithur:tURL];
CMTimeRange tEditRange=CMTimeRangeMake(CMTimeMake(0,1),CMTimeMake(tAsset.duration.value,tAsset.duration.timescale));
AVMutableComposition*tComposition=[[AVMutableComposition alloc]init]autorelease];
对于(int i=0;i<100;i++){//插入一些副本。
[tComposition insertTimeRange:tEdit资产范围:tSet时间:tComposition.duration错误:nil];
}
AVPlayerItem*tAVPlayerItem=[[AVPlayerItem alloc]initWithAsset:tComposition];
AVPlayer*tAVPlayer=[[AVPlayer alloc]initWithPlayerItem:tAVPlayerItem];
此属性已在
AVAudioPlayer
中定义。希望这能帮助你。
我在Swift中使用的是Xcode 6.3。


玩家结束时,您可以收到通知。。。检查AVPlayerItemDidPlayToEndTimeNotification

NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem)
设置播放机时:

avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None

NSNotificationCenter.defaultCenter().addObserver(self, 
                                                 selector: "playerItemDidReachEnd:", 
                                                 name: AVPlayerItemDidPlayToEndTimeNotification, 
                                                 object: avPlayer.currentItem)
这将防止播放器在结束时暂停

在通知中:

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    AVPlayerItem *p = [notification object];
    [p seekToTime:kCMTimeZero];
}
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)
这将倒带电影


释放玩家时不要忘记注销通知。

这对我来说很有效,没有打嗝的问题,关键是在调用seekToTime方法之前暂停玩家:

  • 初始化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)
    
  • 视频环路功能

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

  • 将视频加载到AVPlayer后(当然是通过其AVPlayerItem):

    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];
        }];
    }
    
    在ViewWillEnglish方法中:

    if (_notificationToken) {
            [[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
            _notificationToken = nil;
        }
    
    在实现文件中的视图控制器接口声明中:

    id _notificationToken;
    
    在您尝试之前,需要先看到它的启动和运行吗?下载并运行此示例应用程序:


    在我的应用程序中,使用这段代码,在视频结束和开始之间没有任何停顿。事实上,根据视频的不同,我无法判断视频是否在开头,请保存时间代码显示。

    使用代码下方的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()
    

    所有要显示的控件,希望有帮助

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

    这在2016年WWDC的“AVFoundation回放的进展”中介绍:

    即使使用这段代码,我也会打个嗝,直到我向苹果公司提交了一份bug报告,并得到了以下回复:

    电影持续时间长于音频/视频曲目的电影文件不可用 这个问题。FigPlayer_文件正在禁用无间隙转换,因为 音频曲目编辑比电影持续时间短(15.682 vs 15.787)

    您需要修复电影文件以获得电影持续时间和 跟踪持续时间为相同长度,也可以使用时间范围 AVPlayerLooper的参数(设置时间范围从0到 音轨)


    原来Premiere导出的文件的音轨长度与视频稍有不同。在我的例子中,完全删除音频是可以的,这就解决了问题。

    您可以添加一个AVPlayerItemDidPlayToEndTimeNotification observer并重放视频 从选择器中的开始,代码如下

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

    我在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];
    }
    
    NSURL*videoLoopUrl;
    //作为[[NSBundle mainBundle]URLForResource:@“资产/yourVideo”,扩展名为:@“mp4”];
    AVQueuePlayer*\u loopVideoPlayer;
    +(void)nextVideoInstance:(NSNotification*)notif
    {
    AVPlayerItem*currItem=[AVPlayerItem playerItemWithURL:videoLoopUrl];
    [[NSNotificationCenter defaultCenter]添加观察者:self
    选择器:@selector(nextVideoInstance:)
    名称:AVPlayerItemDidPlayToEndTimeNotification
    对象:项目];
    [\u loopVideoPlayer插入项:currItem afterItem:nil];
    [\u loopVideoPlayer advanceToNextItem];
    }
    +(无效)视频播放器{
    videoCopy1=[AVPlayerItem playerItemWithURL:videoLoopUrl];
    视频公司
    
    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()
    
    private var player: AVPlayer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(restartVideo),
                                               name: .AVPlayerItemDidPlayToEndTime,
                                               object: self.player?.currentItem)
    }
    
    @objc func restartVideo() {
        player?.pause()
        player?.currentItem?.seek(to: CMTime.zero, completionHandler: { _ in
            self.player?.play()
        })
    }
    
    player?.addBoundaryTimeObserver(forTimes: [NSValue(time: asset.duration)], queue: .main) { [weak self] in
        self?.player?.seek(to: .zero, completionHandler: { [weak self] _ in
            self?.player?.play()
        })
    }