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

标签 swift avfoundation avplayer avplayeritem avplayerlayer

我正在设置一个 AVPlayerUIView这样:

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()
}
当我更改项目时,过渡不平滑,我可以看到显示 view 的闪光灯。 下面 .有一些我不明白的地方:
  • 为什么如果我预装了所有 AVPlayerItems在开始时(当我循环所有 urls 字符串时)我仍然需要等待它们成为 currentItem 时被加载。的AVPlayer?
  • 关于闪烁效果的问题我在网上搜索了下没有解决,于是仔细查看了AVPlayer的行为。每当更改 Item 时,我都会以这种方式描述它:

  • 第一 它显示了一个白色的 View (见下图)
    第二 它显示了将要显示的视频帧(更多或更少的时间帧位于视频的一半)
    第三 它实际上显示了视频
    这种行为真的很烦人,我想知道如何解决它。

    最佳答案

    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
            }
        }
        
    }
    
    Podfile:
    platform :ios, '9.0'
    source 'https://github.com/CocoaPods/Specs.git'
    
    target 'Test' do
      use_frameworks!
      pod 'GPUImage'
    end
    
    当您调用 replaceCurrentItem ,它将清理图像缓冲区,然后等待渲染下一帧。所以,关键是要在屏幕上保留最后一帧:而不是使用 AVPlayerLayer要渲染视频帧,您可以使用 AVPlayerItemVideoOutput获取帧并使用 OpenGL ES(或 Metal)进行渲染。您还可以决定何时清理图像缓冲区。

    关于swift - 使用 replaceCurrentItem() 时 AVPlayer 闪烁 - Swift - 以编程方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64518354/

    相关文章:

    swift - 单击 TableView 外部的 UIButton 更改 TableView 单元格文本中的内容

    ios - TagListView单选swift 4

    ios - 与阿拉莫菲尔合作

    swift - AVFoundation 错误域

    ios - 我正在尝试在 ios 中流式传输以下 url,但它不会播放

    ios - 如何接受来自 UITextField 的 Double 输入,并将值赋给变量?

    ios - 如何为 iOS AVFoundation 定义自定义检测器?

    swift - 如何快速修改previewView?

    ios - 播放/暂停下一个/上一个按钮在控制中心显示为灰色

    swift - SKVideoNode帧率