Ant*_*nio 56 video avfoundation ios avplayer
我正在开发一个有集合视图的应用程序,集合视图的单元格可以包含视频.现在我正在用AVPlayer
和显示视频AVPlayerLayer
.不幸的是,滚动性能很糟糕.看起来AVPlayer
,AVPlayerItem
和AVPlayerLayer
做了很多工作的主线上.他们不断拿出锁,等待信号量等,这阻碍了主线程并导致严重的帧丢失.
有没有办法告诉AVPlayer
停止在主线程上做这么多事情?到目前为止,我所尝试的一切都没有解决问题.
我也试过使用构建一个简单的视频播放器AVSampleBufferDisplayLayer
.使用它我可以确保一切都发生在主线程之外,我可以在滚动和播放视频时达到~60fps.不幸的是,这种方法的级别要低得多,而且它不提供开箱即用的音频播放和时间擦除等功能.有没有办法获得类似的表现AVPlayer
?我更愿意使用它.
编辑:
在进一步研究之后,看起来在使用时可能无法实现良好的滚动性能AVPlayer
.创建一个AVPlayer
并与一个AVPlayerItem
实例关联开始了一大堆蹦床到主线程的工作,然后它在信号量上等待并尝试获取一堆锁.随着滚动视图中视频数量的增加,停止主线程的时间量会急剧增加.
AVPlayer
dealloc似乎也是一个巨大的问题.Dealloc'ing an AVPlayer
也尝试同步一堆东西.再次,当你创造更多的球员时,这会非常糟糕.
这非常令人沮丧,它AVPlayer
几乎无法用于我正在尝试的事情.阻止像这样的主线程这样做是一件非常业余的事情,很难相信苹果工程师会犯这种错误.无论如何,希望他们能尽快解决这个问题.
dam*_*ian 21
AVPlayerItem
尽可能在后台队列中构建(在主线程上必须执行一些操作,但是您可以执行设置操作并等待视频属性加载到后台队列 - 非常仔细地阅读文档).这涉及与KVO的伏都教舞蹈,真的不好玩.
当AVPlayer
等待AVPlayerItem
状态变为时,打嗝发生AVPlayerItemStatusReadyToPlay
.为了尽可能多地可以减少你想要做的打嗝的长度带来的AVPlayerItem
更紧密AVPlayerItemStatusReadyToPlay
其分配给之前在后台线程AVPlayer
.
我实际上实现了这一段时间已经有一段时间了,但是IIRC主要的线程块是由于底层AVURLAsset
的属性是延迟加载引起的,如果你自己不加载它们,它们会在主线程上忙于加载AVPlayer
想玩.
查看AVAsset文档,尤其是周围的内容AVAsynchronousKeyValueLoading
.我认为我们需要在使用资产时duration
和tracks
之前加载值AVPlayer
以最小化主线程块.有可能我们也必须遍历每个轨道并对AVAsynchronousKeyValueLoading
每个段进行操作,但我不记得100%.
And*_*oes 14
不知道这是否会有所帮助 - 但是这里有一些我用来加载后台队列视频的代码,这肯定有助于主线程阻塞(道歉如果它不能编译1:1,我从更大的代码库中抽象出来我我正在努力:
func loadSource() {
self.status = .Unknown
let operation = NSBlockOperation()
operation.addExecutionBlock { () -> Void in
// create the asset
let asset = AVURLAsset(URL: self.mediaUrl, options: nil)
// load values for track keys
let keys = ["tracks", "duration"]
asset.loadValuesAsynchronouslyForKeys(keys, completionHandler: { () -> Void in
// Loop through and check to make sure keys loaded
var keyStatusError: NSError?
for key in keys {
var error: NSError?
let keyStatus: AVKeyValueStatus = asset.statusOfValueForKey(key, error: &error)
if keyStatus == .Failed {
let userInfo = [NSUnderlyingErrorKey : key]
keyStatusError = NSError(domain: MovieSourceErrorDomain, code: MovieSourceAssetFailedToLoadKeyValueErrorCode, userInfo: userInfo)
println("Failed to load key: \(key), error: \(error)")
}
else if keyStatus != .Loaded {
println("Warning: Ignoring key status: \(keyStatus), for key: \(key), error: \(error)")
}
}
if keyStatusError == nil {
if operation.cancelled == false {
let composition = self.createCompositionFromAsset(asset)
// register notifications
let playerItem = AVPlayerItem(asset: composition)
self.registerNotificationsForItem(playerItem)
self.playerItem = playerItem
// create the player
let player = AVPlayer(playerItem: playerItem)
self.player = player
}
}
else {
println("Failed to load asset: \(keyStatusError)")
}
})
// add operation to the queue
SomeBackgroundQueue.addOperation(operation)
}
func createCompositionFromAsset(asset: AVAsset, repeatCount: UInt8 = 16) -> AVMutableComposition {
let composition = AVMutableComposition()
let timescale = asset.duration.timescale
let duration = asset.duration.value
let editRange = CMTimeRangeMake(CMTimeMake(0, timescale), CMTimeMake(duration, timescale))
var error: NSError?
let success = composition.insertTimeRange(editRange, ofAsset: asset, atTime: composition.duration, error: &error)
if success {
for _ in 0 ..< repeatCount - 1 {
composition.insertTimeRange(editRange, ofAsset: asset, atTime: composition.duration, error: &error)
}
}
return composition
}
Run Code Online (Sandbox Code Playgroud)
如果你看看Facebook的AsyncDisplayKit(Facebook和Instagram 提供商背后的引擎)你可以使用他们的AVideoNode在后台线程上渲染大部分视频.如果您将其子节点到ASDisplayNode并将displayNode.view添加到您正在滚动的任何视图(表/集合/滚动),您可以实现完美平滑的滚动(只需确保创建节点和资产以及后台线程上的所有内容) .唯一的问题是更改视频项时,因为这会强制自己进入主线程.如果您在该特定视图上只有几个视频,则可以使用此方法!
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
self.mainNode = ASDisplayNode()
self.videoNode = ASVideoNode()
self.videoNode!.asset = AVAsset(URL: self.videoUrl!)
self.videoNode!.frame = CGRectMake(0.0, 0.0, self.bounds.width, self.bounds.height)
self.videoNode!.gravity = AVLayerVideoGravityResizeAspectFill
self.videoNode!.shouldAutoplay = true
self.videoNode!.shouldAutorepeat = true
self.videoNode!.muted = true
self.videoNode!.playButton.hidden = true
dispatch_async(dispatch_get_main_queue(), {
self.mainNode!.addSubnode(self.videoNode!)
self.addSubview(self.mainNode!.view)
})
})
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
12666 次 |
最近记录: |