AVPlayer在使用replaceCurrentItem()时闪烁-Swift-以编程方式

AVPlayer在使用replaceCurrentItem()时闪烁-Swift-以编程方式,swift,avfoundation,avplayer,avplayeritem,avplayerlayer,Swift,Avfoundation,Avplayer,Avplayeritem,Avplayerlayer,我正在ui视图中设置AVPlayer,方法如下: fileprivate func setUpAVPlayer(){ self.cardModel?.urlStrings?.forEach({ (urlString) in guard let url = URL(string: urlString) else { return } let asset = AVAsset(url: url) let playerI

我正在
ui视图中设置
AVPlayer
,方法如下:

fileprivate func setUpAVPlayer(){
    
    self.cardModel?.urlStrings?.forEach({ (urlString) in
        
        guard let url = URL(string: urlString) else { return }
        let asset = AVAsset(url: url)
        let playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: nil)
        playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.old, .new], context: nil)
        self.playerItemArray.append(playerItem)
    })
    player = AVPlayer(playerItem: playerItemArray[0])
}
layoutSubviews
中,我布局了显示播放器的playerLayer:

let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = .resizeAspectFill
self.playerView.layer.insertSublayer(playerLayer, at: 0)
playerLayer.frame = playerView.bounds
fileprivate func goToNextVideo(){
    counter = counter+1
    counter = min(playerItemArray.count-1, counter)
    
    self.player?.replaceCurrentItem(with: self.playerItemArray[self.counter])
    self.player?.seek(to: .zero)
    self.player?.play()
}
在某个事件中,我更改玩家的当前项目:

let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = .resizeAspectFill
self.playerView.layer.insertSublayer(playerLayer, at: 0)
playerLayer.frame = playerView.bounds
fileprivate func goToNextVideo(){
    counter = counter+1
    counter = min(playerItemArray.count-1, counter)
    
    self.player?.replaceCurrentItem(with: self.playerItemArray[self.counter])
    self.player?.seek(to: .zero)
    self.player?.play()
}
当我更改项目时,过渡不平稳,我可以看到下面的
视图
闪烁。有些事情我不明白:

  • 为什么如果我在开始时预装了所有的
    AVPlayerItems
    (当我循环所有URL字符串时),当它们成为AVPlayer的
    currentItem
    时,我仍然必须等待它们被加载

  • 关于闪烁效应的问题,我在网上搜索,但没有找到解决方案,因此我仔细检查了
    AVPlayer
    每次更改项目时的行为,并用以下方式描述:

    fileprivate func setUpAVPlayer(){
        
        self.cardModel?.urlStrings?.forEach({ (urlString) in
            
            guard let url = URL(string: urlString) else { return }
            let asset = AVAsset(url: url)
            let playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: nil)
            playerItem.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.old, .new], context: nil)
            self.playerItemArray.append(playerItem)
        })
        player = AVPlayer(playerItem: playerItemArray[0])
    }
    
  • 首先显示白色视图(下图)

    Second它显示要显示的视频的一帧(视频一半的时间越短,时间越长)

    第三个它实际显示视频

    这种行为真的很烦人,我想知道如何解决它

    import UIKit
    import GPUImage
    import AVFoundation
    
    class ViewController: UIViewController {
    
        lazy var movie: GPUImageMovie = {
            let movie = GPUImageMovie()
            movie.yuvConversionSetup()
            movie.addTarget(imageView)
            return movie
        }()
        
        lazy var imageView: GPUImageView = {
            let view = GPUImageView()
            view.setBackgroundColorRed(1, green: 1, blue: 1, alpha: 1)
            return view
        }()
        
        lazy var player: AVPlayer = {
            let player = AVPlayer()
            return player
        }()
        
        lazy var videoURLs: [URL] = {
            return [
                resource(filename: "video0.mp4"),
                resource(filename: "video1.mp4"),
                resource(filename: "video2.mp4"),
                resource(filename: "video3.mp4"),
            ].compactMap({ $0 })
        }()
        
        lazy var button: UIButton = {
            let button = UIButton()
            button.setTitle("replaceCurrentItem", for: .normal)
            button.setTitleColor(.gray, for: .normal)
            button.addTarget(self, action: #selector(action(_:)), for: .touchUpInside)
            return button
        }()
        
        var index = 0
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .white
            view.addSubview(imageView)
            view.addSubview(button)
            imageView.frame = view.bounds
            button.frame = CGRect(x: 0, y: view.bounds.maxY - 100, width: view.bounds.width, height: 44)
        }
    
        func resource(filename: String) -> URL? {
            return Bundle.main.url(forResource: filename, withExtension: nil)
        }
        
        @objc func action(_ sender: UIButton) {
            if let url = getVideoURL() {
                player.pause()
                movie.endProcessing()
                movie.playerItem = AVPlayerItem(url: url)
                player.replaceCurrentItem(with: movie.playerItem)
                player.play()
                movie.startProcessing()
            }
        }
        
        func getVideoURL() -> URL? {
            if videoURLs.indices.contains(index) {
                let url = videoURLs[index]
                index = (index + 1) % videoURLs.count
                return url
            } else {
                return nil
            }
        }
        
    }
    
    播客文件:

    platform :ios, '9.0'
    source 'https://github.com/CocoaPods/Specs.git'
    
    target 'Test' do
      use_frameworks!
      pod 'GPUImage'
    end
    

    调用
    replaceCurrentItem
    时,它将清理图像缓冲区,然后等待渲染下一帧。因此,关键点是保持屏幕上的最后一帧:您可以使用
    AVPlayerLayer
    获取帧并使用OpenGL ES(或Metal)进行渲染,而不是使用
    AVPlayerItemVideoOutput
    来渲染视频帧。您还可以决定何时清理图像缓冲区。

    这里也有同样的问题……这对我很有效。谢谢@Lai