使用AVFoundation AVPlayer循环播放视频?

152

有没有一种相对简单的方法在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
请查看此答案,其中包含实际可工作的代码链接:https://dev59.com/61zUa4cB1Zd3GeqP2mCV - MoDJ
22个回答

1

适用于Swift 3和4

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

1

我在答案中找不到我的解决方案。观察指定给资产持续时间的边界时间可能会有所帮助。当观察者被触发时,将其定位到开始并重新播放。

player?.addBoundaryTimeObserver(forTimes: [NSValue(time: asset.duration)], queue: .main) { [weak self] in
    self?.player?.seek(to: .zero, completionHandler: { [weak self] _ in
        self?.player?.play()
    })
}

这个相当不错。 - Vivek

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
你可以添加一个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

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

SwiftUI 示例:

//  VideoLooper.swift

import SwiftUI
import AVKit

struct VideoLooper: UIViewRepresentable {
    private let player: AVQueuePlayer
    private let videoURL: URL
    
    init(resourceInBundle name: String, ofType type: String = "mp4") {
        self.init(url: URL(fileURLWithPath: Bundle.main.path(forResource: name, ofType: type)!))
    }
    
    init(url: URL) {
        self.videoURL = url
        self.player = AVQueuePlayer()
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<VideoLooper>) {
    }

    func makeUIView(context: Context) -> UIView {
       
        return PlayerUIView(player: player, videoURL: videoURL)
    }

}

class PlayerUIView: UIView {
    private let playerLayer: AVPlayerLayer
    private let playerItem: AVPlayerItem
    private let playerLooper: AVPlayerLooper

    init(player: AVQueuePlayer, videoURL: URL) {
        playerLayer = AVPlayerLayer(player: player)
        playerItem = AVPlayerItem(url: videoURL)
        playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
        super.init(frame: .zero)
      
        playerLayer.player = player
        layer.addSublayer(playerLayer)
        player.play()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

使用方法:

// Video-Resource in Bundle
VideoLooper(resourceInBundle: "moviename", ofType: "mp4")

// Video-Resource from URL
VideoLooper(url: URL(string: "https://my-video-server.local/christmas.mp4")!)

0

Swift 5

import UIKit
import AVKit
import AVFoundation

class VideoViewControler: UIViewController {
    
    // init video background and its path
    var player: AVPlayer?
    let videoURL: NSURL = Bundle.main.url(forResource: "farmer_watering", withExtension: "mp4")! as NSURL
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white

        
        // begin implementing the avplayer
        player = AVPlayer(url: videoURL as URL)
        player?.actionAtItemEnd = .none
        player?.isMuted = true
        
        let playerLayer = AVPlayerLayer(player: player)
        playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
        playerLayer.zPosition = -1
        
        playerLayer.frame = view.frame
        
        view.layer.addSublayer(playerLayer)
        
        player?.play()
        
        // add observer to watch for video end in order to loop video
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(loopVideo),
            name: .AVPlayerItemDidPlayToEndTime,
            object: self.player?.currentItem
        )
    }
    
    // if video ends, will restart
    func playerItemDidReachEnd() {
        player?.seek(to: CMTime.zero)
    }
    
    // add this loop at the end, after viewDidLoad
    @objc func loopVideo() {
        playerItemDidReachEnd()
        player?.play()
    }
}

0
以下是我在Swift 4.1中使用WKWebView的工作示例。 WKWebView的主要部分在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

Xcode 10.1 中的 Swift 4.2

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

0

针对2023年。

如果特别使用AVPlayerViewController。

将此行代码 player = AVPlayer(url: u) 更改为以下两行:

// player = AVPlayer(url: u)
player = AVQueuePlayer()
loopy = AVPlayerLooper(player: player as! AVQueuePlayer,
                       templateItem: AVPlayerItem(url: u))

添加

var loopy: AVPlayerLooper?

提交到版本控制,就完成了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接