swift - 使用 replaceCurrentItem() 时 AVPlayer 闪烁 - Swift - 以编程方式
问题描述
我正在以这种方式设置一个AVPlayer
:UIView
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
以下内容的闪光灯。有一些我不明白的地方:
为什么如果我在开始时预加载了所有内容(当我循环所有 url 字符串时),当它们成为AVPlayer
AVPlayerItems
时,我仍然必须等待它们被加载?currentItem
关于闪烁效果的问题,我在网上搜索了一下,没有找到解决办法,所以我仔细检查了
AVPlayer
每次变化的Item的行为,我是这样描述的:
首先它显示一个白色视图(下图)
其次它显示了它将显示的视频帧(更多或更少的时间帧是视频的一半)
第三它实际显示视频
这种行为真的很烦人,我想知道如何解决它。
解决方案
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
,它会清理图像缓冲区,然后等待渲染下一帧。所以,关键是要保留屏幕上的最后一帧:您可以使用OpenGL ES(或 Metal)获取帧并渲染它AVPlayerLayer
,而不是使用渲染视频帧。AVPlayerItemVideoOutput
您还可以决定何时清理图像缓冲区。
推荐阅读
- android - Android 模拟器错误
- reactjs - 有没有办法只启用某些日历选项?
- selenium - 试图从linkedin的董事会中抓取发布日期以获取第一篇和下一篇
- wordpress - Woocommerce LAG 每秒超过 1000 个活跃用户可能是 AJAX 购物车和 AJAX 添加到购物车按钮的原因?
- c++ - QGridLayout,紧凑的行和列,没有填充或间距
- javascript - Uncaught (in promise) SyntaxError: Unexpected end of JSON input error
- javascript - Javascript + Discord:对对象进行排序并获得前3名
- r - 使用 R 创建 Google Analytics 事件
- javascript - 函数中不更新状态的问题
- reactjs - 如何显示自定义
在 formik 字段组件中并禁用默认消息