当 AVPlayer 在 exportAsynchronouslyWithCompletionHandler 中创建时,在看到视频之前有很长的延迟
Long delay before seeing video when AVPlayer created in exportAsynchronouslyWithCompletionHandler
播放从 AVAssetExportSession
导出的视频时,您会在看到视频之前很久就听到音频。音频立即播放,但视频仅在录制循环多次(即开始和结束)后出现。换句话说,您会在看到任何图像之前多次听到视频中的音频。
我们在 iOS 8.
上使用 AutoLayout
使用以下测试,我们将问题隔离到 exportAsynchronouslyWithCompletionHandler
。在这两个代码块中,我们播放一个现有的视频——与导出无关的视频——因此导出过程已作为一个变量被删除。
代码 1 在开始时同时播放视频和音频,而 代码 2 只在开始时播放音频并在延迟后显示视频10-60 秒(视频循环播放数次后)。
两个代码块之间的唯一区别是一个使用 exportAsynchronouslyWithCompletionHandler
播放视频,而另一个不使用。
帮忙?是否有可能首先导出音频并准备好在视频之前播放?与在不同线程上发生的导出有关?
func initPlayer(videoURL: NSURL) {
// Create player
player = AVPlayer(URL: videoURL)
let playerItem = player.currentItem
let asset = playerItem.asset
playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = videoView.frame
view.layer.addSublayer(playerLayer)
player.seekToTime(kCMTimeZero)
player.actionAtItemEnd = .None
player.play()
// Get notified when video done for looping purposes
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerItemDidReachEnd:", name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
// Log status
println("Initialized video player: \(CMTimeGetSeconds(asset.duration)) seconds & \(asset.tracks.count) tracks for \(videoURL)")
}
func playExistingVideo() {
let filename = "/ChopsticksVideo.mp4"
let allPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsPath = allPaths[0] as! NSString
let exportPath = docsPath.stringByAppendingFormat(filename)
let exportURL = NSURL.fileURLWithPath(exportPath as String)!
initPlayer(exportURL)
}
代码 1:
// Create exporter
let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality)
exporter.videoComposition = videoComposition
exporter.outputFileType = AVFileTypeMPEG4
exporter.outputURL = exportURL
exporter.shouldOptimizeForNetworkUse = true
playExistingVideo()
代码2:
// Create exporter
let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality)
exporter.videoComposition = videoComposition
exporter.outputFileType = AVFileTypeMPEG4
exporter.outputURL = exportURL
exporter.shouldOptimizeForNetworkUse = true
// -- Export video
exporter.exportAsynchronouslyWithCompletionHandler({
self.playExistingVideo()
})
我要提示问题出在这里:
// Create player
player = AVPlayer(URL: videoURL)
let playerItem = player.currentItem
let asset = playerItem.asset
playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = videoView.frame
view.layer.addSublayer(playerLayer)
player.seekToTime(kCMTimeZero)
player.actionAtItemEnd = .None
player.play()
你看,当你从视频 URL 创建一个 AVPlayer 时,它进入了这个世界 尚未准备好播放 。它通常可以很快开始播放音频,但准备视频需要更长的时间。这可以解释延迟看到任何东西的原因。
好吧,不用等待视频准备好,您只需继续并立即说 play()
。这是我的建议。我建议你做的是我explain in my book(这是实际代码的link):创建播放器和图层,然后设置 KVO,以便在播放器准备好时通知你显示,然后然后添加图层并开始播放。
另外,我还有一个建议。在我看来,你是 运行 那个代码的危险,设置你的界面(与层)并在后台线程上说 play()
,.这肯定会导致各种延误。您似乎假设来自 exportAsynchronouslyWithCompletionHandler:
的完成处理程序正在主线程上被调用 - 您将直接前进并调用下一个方法,然后继续设置您的界面。这是一个非常冒险的假设。根据我的经验,你永远不应该假设 any AVFoundation 完成处理程序在主线程上。您应该在完成处理程序中使用 dispatch_async
跳出到主线程,然后仅从那里继续。如果您查看我 link 教给您的代码,您会发现我这样做是很小心的。
对于后来偶然发现这个问题的人,答案在已接受答案的评论中。下面的关键 dispatch_async 部分:
[exporter exportAsynchronouslyWithCompletionHandler:^(void){
dispatch_async(dispatch_get_main_queue(), ^{
switch (exporter.status) {
case AVAssetExportSessionStatusCompleted:
NSLog(@"Video Merge Successful");
break;
case AVAssetExportSessionStatusFailed:
NSLog(@"Failed:%@", exporter.error.description);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"Canceled:%@", exporter.error);
break;
case AVAssetExportSessionStatusExporting:
NSLog(@"Exporting!");
break;
case AVAssetExportSessionStatusWaiting:
NSLog(@"Waiting");
break;
default:
break;
}
});
}];
您可以在导出过程中播放视频组合。
playerItem = AVPlayerItem(asset: videoComposition)
player = AVPlayer(playerItem: playerItem)
AVPlayerItem:
convenience init(asset: AVAsset)
播放从 AVAssetExportSession
导出的视频时,您会在看到视频之前很久就听到音频。音频立即播放,但视频仅在录制循环多次(即开始和结束)后出现。换句话说,您会在看到任何图像之前多次听到视频中的音频。
我们在 iOS 8.
上使用 AutoLayout使用以下测试,我们将问题隔离到 exportAsynchronouslyWithCompletionHandler
。在这两个代码块中,我们播放一个现有的视频——与导出无关的视频——因此导出过程已作为一个变量被删除。
代码 1 在开始时同时播放视频和音频,而 代码 2 只在开始时播放音频并在延迟后显示视频10-60 秒(视频循环播放数次后)。
两个代码块之间的唯一区别是一个使用 exportAsynchronouslyWithCompletionHandler
播放视频,而另一个不使用。
帮忙?是否有可能首先导出音频并准备好在视频之前播放?与在不同线程上发生的导出有关?
func initPlayer(videoURL: NSURL) {
// Create player
player = AVPlayer(URL: videoURL)
let playerItem = player.currentItem
let asset = playerItem.asset
playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = videoView.frame
view.layer.addSublayer(playerLayer)
player.seekToTime(kCMTimeZero)
player.actionAtItemEnd = .None
player.play()
// Get notified when video done for looping purposes
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerItemDidReachEnd:", name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem)
// Log status
println("Initialized video player: \(CMTimeGetSeconds(asset.duration)) seconds & \(asset.tracks.count) tracks for \(videoURL)")
}
func playExistingVideo() {
let filename = "/ChopsticksVideo.mp4"
let allPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsPath = allPaths[0] as! NSString
let exportPath = docsPath.stringByAppendingFormat(filename)
let exportURL = NSURL.fileURLWithPath(exportPath as String)!
initPlayer(exportURL)
}
代码 1:
// Create exporter
let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality)
exporter.videoComposition = videoComposition
exporter.outputFileType = AVFileTypeMPEG4
exporter.outputURL = exportURL
exporter.shouldOptimizeForNetworkUse = true
playExistingVideo()
代码2:
// Create exporter
let exporter = AVAssetExportSession(asset: mainComposition, presetName: AVAssetExportPresetHighestQuality)
exporter.videoComposition = videoComposition
exporter.outputFileType = AVFileTypeMPEG4
exporter.outputURL = exportURL
exporter.shouldOptimizeForNetworkUse = true
// -- Export video
exporter.exportAsynchronouslyWithCompletionHandler({
self.playExistingVideo()
})
我要提示问题出在这里:
// Create player
player = AVPlayer(URL: videoURL)
let playerItem = player.currentItem
let asset = playerItem.asset
playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = videoView.frame
view.layer.addSublayer(playerLayer)
player.seekToTime(kCMTimeZero)
player.actionAtItemEnd = .None
player.play()
你看,当你从视频 URL 创建一个 AVPlayer 时,它进入了这个世界 尚未准备好播放 。它通常可以很快开始播放音频,但准备视频需要更长的时间。这可以解释延迟看到任何东西的原因。
好吧,不用等待视频准备好,您只需继续并立即说 play()
。这是我的建议。我建议你做的是我explain in my book(这是实际代码的link):创建播放器和图层,然后设置 KVO,以便在播放器准备好时通知你显示,然后然后添加图层并开始播放。
另外,我还有一个建议。在我看来,你是 运行 那个代码的危险,设置你的界面(与层)并在后台线程上说 play()
,.这肯定会导致各种延误。您似乎假设来自 exportAsynchronouslyWithCompletionHandler:
的完成处理程序正在主线程上被调用 - 您将直接前进并调用下一个方法,然后继续设置您的界面。这是一个非常冒险的假设。根据我的经验,你永远不应该假设 any AVFoundation 完成处理程序在主线程上。您应该在完成处理程序中使用 dispatch_async
跳出到主线程,然后仅从那里继续。如果您查看我 link 教给您的代码,您会发现我这样做是很小心的。
对于后来偶然发现这个问题的人,答案在已接受答案的评论中。下面的关键 dispatch_async 部分:
[exporter exportAsynchronouslyWithCompletionHandler:^(void){
dispatch_async(dispatch_get_main_queue(), ^{
switch (exporter.status) {
case AVAssetExportSessionStatusCompleted:
NSLog(@"Video Merge Successful");
break;
case AVAssetExportSessionStatusFailed:
NSLog(@"Failed:%@", exporter.error.description);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"Canceled:%@", exporter.error);
break;
case AVAssetExportSessionStatusExporting:
NSLog(@"Exporting!");
break;
case AVAssetExportSessionStatusWaiting:
NSLog(@"Waiting");
break;
default:
break;
}
});
}];
您可以在导出过程中播放视频组合。
playerItem = AVPlayerItem(asset: videoComposition)
player = AVPlayer(playerItem: playerItem)
AVPlayerItem:
convenience init(asset: AVAsset)