AVAssetExportSession在设备上不起作用,但在模拟器上起作用(AVFoundationErrorDomain代码= -11800,未知错误代码-12780) [英] AVAssetExportSession not working on devices, but working on simulator (AVFoundationErrorDomain Code = -11800, Unknown Error code -12780)
问题描述
To make things easier to anyone interested in checking out this problem, I added a demo project to this github repository.
问题
我看到了几个具有相同错误的问题,但是那里没有找到解决方案对我有帮助.想通了,我会试试运气的.
I've seen several questions that had the same error, but none of the solutions found there helped me. Figured I'd try my luck.
我正尝试按原样导出视频,主要是为了了解AVFoundation和AVAssetExportSession.我的导出在模拟器上可以正常运行,但在我尝试过的任何iOS设备上均无法运行(即,每个运行iOS 12的iPhone X和iPhone XR).我主要遵循在此链接上找到的Ray Wenderleich教程来执行视频导出: https://www.raywenderlich.com/2734-avfoundation-tutorial-adding-overlays-and-animations-to-videos
I'm trying to export a video as is, mainly to learn about AVFoundation and AVAssetExportSession. My export works perfectly fine on the simulator, but does not work on any iOS device I've tried (namely an iPhone X and an iPhone XR running iOS 12 each). I Mainly followed a Ray Wenderleich tutorial found on this link to perform the Video Exporting: https://www.raywenderlich.com/2734-avfoundation-tutorial-adding-overlays-and-animations-to-videos
将感谢您对该主题的任何帮助.我的代码如下:
Would appreciate any help on the topic. My code is as follows:
检索我添加到应用程序捆绑包中名为Demo.mp4的视频的URL:
Retrieving the URL of a video I've added to the App Bundle called Demo.mp4:
@objc func export() {
let urlString = Bundle.main.path(forResource: "Demo", ofType: ".mp4")!
let url = URL(fileURLWithPath: urlString)
ExportManager.shared.exportWithAVFoundation(url:url) { (outputUrl, errorString) in
if let outputUrl = outputUrl {
self.playVideo(url: outputUrl)
} else if let errorString = errorString {
print("ERROR: \(errorString)")
}
}
}
我在ExportManager中的导出功能如下(很抱歉,很长一段时间)
My Exporting function in ExportManager is as follows (Sorry quite long)
func exportWithAVFoundation(url: URL, completion: @escaping (_ outputUrl: URL?, _ errorString: String?) -> ()) {
let asset = AVAsset(url: url)
print("URL IS \(url)")
guard let avAssetTrack = asset.tracks(withMediaType: .video).first else {
completion(nil, "Couldn't Create Asset Track")
return
}
let mutableComposition = AVMutableComposition()
guard let videoTrack = mutableComposition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid) else { return }
try? videoTrack.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: avAssetTrack, at: .zero)
videoTrack.preferredTransform = avAssetTrack.preferredTransform
if let audioAssetTrack = asset.tracks(withMediaType: .audio).first {
let audioTrack = mutableComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: kCMPersistentTrackID_Invalid)
try? audioTrack?.insertTimeRange(CMTimeRange(start: .zero, duration: asset.duration), of: audioAssetTrack, at: .zero)
}
let mainInstruction = AVMutableVideoCompositionInstruction()
mainInstruction.timeRange = CMTimeRange(start: .zero, duration: asset.duration)
let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
// Fix video orientation
var videoAssetOrientation = UIImage.Orientation.up
var isVideoAssetPortrait = false
let videoTransform = avAssetTrack.preferredTransform
switch (videoTransform.a, videoTransform.b, videoTransform.c, videoTransform.c) {
case (0, 1.0, -1.0, 0):
videoAssetOrientation = .right
isVideoAssetPortrait = true
case(0, -1.0, 1.0, 0):
videoAssetOrientation = .left
isVideoAssetPortrait = true
case(1.0, 0, 0, 1.0):
videoAssetOrientation = .up
case(-1.0, 0, 0, -1.0):
videoAssetOrientation = .down
default:
break
}
var naturalSize = avAssetTrack.naturalSize
switch (videoAssetOrientation, isVideoAssetPortrait) {
case (.right, true):
naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
case (.left, true):
naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
case (.leftMirrored, true):
naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
case (.rightMirrored, true):
naturalSize = CGSize(width: avAssetTrack.naturalSize.height, height: avAssetTrack.naturalSize.width)
default:
break
}
videoLayerInstruction.setTransform(avAssetTrack.preferredTransform, at: .zero)
videoLayerInstruction.setOpacity(0, at: asset.duration)
mainInstruction.layerInstructions = [videoLayerInstruction]
let mainCompositionInstruction = AVMutableVideoComposition()
mainCompositionInstruction.renderSize = naturalSize
mainCompositionInstruction.instructions = [mainInstruction]
mainCompositionInstruction.frameDuration = CMTimeMake(value: 1, timescale: 30);
let documentsDirectoryURL = createPath()
guard let exporter = AVAssetExportSession(asset: mutableComposition, presetName: AVAssetExportPresetHighestQuality) else {
print("Couldnt create AVAssetExportSession")
completion(nil, "Couldn't Create AVAssetExportSession")
return
}
exporter.outputURL = documentsDirectoryURL
exporter.outputFileType = .mov
exporter.shouldOptimizeForNetworkUse = true
exporter.videoComposition = mainCompositionInstruction
exporter.exportAsynchronously {
if let error = exporter.error {
print(error)
completion(nil, error.localizedDescription)
return
}
completion(exporter.outputURL, nil)
print("Finished Exporting")
}
}
我尝试做的一些事情是将AudioTrack添加到合成中(我以前没有包括过).并没有帮助它在实际设备上运行,但是至少我导出的视频现在有音频.
Some things I've tried to do have been to add an AudioTrack to the composition (which I didn't include previously). Hasn't helped it work on an actual device, but at least my exported video has audio now.
我还尝试将预设质量"更改为通过",而不是最高质量",正如我从其他主题中读到的那样,这可能有所帮助,但无济于事.
I also tried to change the Preset Quality to Passthrough as opposed to Highest Quality, as I read from other threads that this could help, but to no avail.
编辑:
添加了createPath函数:
Added createPath function:
func createPath() -> URL {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDirectory = paths.first!
let myPathDocs = documentDirectory.appending("FinalVideo.mov")
let url = URL(fileURLWithPath: myPathDocs)
if FileManager.default.fileExists(atPath: myPathDocs) {
try? FileManager.default.removeItem(atPath: myPathDocs)
}
return url
}
注意:createPath()只是在目录中创建一个有效路径来保存导出的视频.如果在导出之前该路径上存在文件,则会将其删除.
Note: createPath() just creates a valid path in the directory to save the exported video in. If a file exists at that path prior to exporting, it gets deleted.
推荐答案
问题是您将字符串附加到另一个字符串,导致该文件的路径错误,例如 file:///var/移动/容器/数据/应用/<stripped>/DocumentsFinalVideo.mov
The problem is that you're appending string to another string resulting in wrong path to the file like file:///var/mobile/Containers/Data/Application/<stripped>/DocumentsFinalVideo.mov
您应该改用 appendingPathComponent():
func createPath() -> URL {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentDirectory = URL(fileURLWithPath: paths.first!)
let url = documentDirectory.appendingPathComponent("FinalVideo.mov")
if FileManager.default.fileExists(atPath: url.path) {
try? FileManager.default.removeItem(at: url)
}
return url
}
这篇关于AVAssetExportSession在设备上不起作用,但在模拟器上起作用(AVFoundationErrorDomain代码= -11800,未知错误代码-12780)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!