Nic*_*ari 6 avfoundation ios avcomposition
我正在业余时间为 iOS 开发一个视频编辑应用程序。
在参加其他 rpojects 几周后,我刚刚恢复工作,并且 - 即使我没有对代码进行任何重大更改 -现在每次我尝试导出我的视频作品时它都会崩溃。
我检查并构建了我当时成功上传到 TestFlight 的完全相同的提交(并且它在多个设备上工作而没有崩溃),所以这可能是我从那时起更新的最新 Xcode / iOS SDK 的问题?
代码在_xpc_api_misuse上崩溃,在一个线程上:
com.apple.coremedia.basicvideocompositor.output
Run Code Online (Sandbox Code Playgroud)
调试导航器:
在崩溃时,调试导航器上有 70 多个线程,所以可能出现问题并且应用程序使用了太多线程(从未见过这么多)。
我的应用程序使用文本层在导出的视频上覆盖“水印”。玩了一圈后,我发现如果我注释掉水印代码,可以避免崩溃:
guard let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) else {
return failure(ProjectError.failedToCreateExportSession)
}
guard let documents = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else {
return failure(ProjectError.temporaryOutputDirectoryNotFound)
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd_HHmmss"
let fileName = dateFormatter.string(from: Date())
let fileExtension = "mov"
let fileURL = documents.appendingPathComponent(fileName).appendingPathExtension(fileExtension)
exporter.outputURL = fileURL
exporter.outputFileType = AVFileType.mov
exporter.shouldOptimizeForNetworkUse = true // check if needed
// OFFENDING BLOCK (commenting out averts crash)
if addWaterMark {
let frame = CGRect(origin: .zero, size: videoComposition.renderSize)
let watermark = WatermarkLayer(frame: frame)
let parentLayer = CALayer()
let videoLayer = CALayer()
parentLayer.frame = frame
videoLayer.frame = frame
parentLayer.addSublayer(videoLayer)
parentLayer.addSublayer(watermark)
videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)
}
// END OF OFFENDING BLOCK
exporter.videoComposition = videoComposition
exporter.exportAsynchronously {
// etc.
Run Code Online (Sandbox Code Playgroud)
水印层的代码是:
class WatermarkLayer: CATextLayer {
private let defaultFontSize: CGFloat = 48
private let rightMargin: CGFloat = 10
private let bottomMargin: CGFloat = 10
init(frame: CGRect) {
super.init()
guard let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String else {
fatalError("!!!")
}
self.foregroundColor = CGColor.srgb(r: 255, g: 255, b: 255, a: 0.5)
self.backgroundColor = CGColor.clear
self.string = String(format: String.watermarkFormat, appName)
self.font = CTFontCreateWithName(String.watermarkFontName as CFString, defaultFontSize, nil)
self.fontSize = defaultFontSize
self.shadowOpacity = 0.75
self.alignmentMode = .right
self.frame = frame
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented. Use init(frame:) instead.")
}
override func draw(in ctx: CGContext) {
let height = self.bounds.size.height
let fontSize = self.fontSize
let yDiff = (height-fontSize) - fontSize/10 - bottomMargin // Bottom (minus margin)
ctx.saveGState()
ctx.translateBy(x: -rightMargin, y: yDiff)
super.draw(in: ctx)
ctx.restoreGState()
}
}
Run Code Online (Sandbox Code Playgroud)
任何想法可能会发生什么?
也许我的代码做错了什么,由于某些 Apple 错误已修复或实现“漏洞”已被堵塞,所以在以前的 SDK 中不知何故“获得通过”?
更新:我下载了Ray Wenderlich 的视频编辑示例项目,并尝试为视频添加“字幕”(我不得不调整太旧的项目,以便它可以在 Xcode 11 下编译)。
瞧,它以完全相同的方式崩溃。
更新 2:我现在在设备上进行了尝试(运行最新的 iOS 13.5 的 iPhone 8)并且它工作正常,没有崩溃。但是,iOS 13.5 模拟器确实会崩溃。当我最初发布问题(iOS 13.4?)时,我确定它在设备和模拟器上都崩溃了。
我正在下载 iOS 12.0 模拟器以进行检查,但它仍然有几 GB 的空间......
我有同样的问题。在 iOS 13.4 之后开始,仅在模拟器上显示(设备工作正常)。如果我注释掉,parentLayer.addSublayer(videoLayer)那么应用程序不会崩溃,但导出的视频不是所需的输出。
这在 iOS 14.5 中为我解决了这个问题:
public static var isSimulator: Bool {
#if targetEnvironment(simulator)
true
#else
false
#endif
}
// ...
let export = AVAssetExportSession(
asset: composition,
presetName: isSimulator ? AVAssetExportPresetPassthrough : AVAssetExportPresetHighestQuality
)
Run Code Online (Sandbox Code Playgroud)
编辑:实际上并没有像在真实设备上那样渲染。编辑完全被忽略...
遇到相同的问题,但仅限模拟器(Xcode 12.4 (12D4e))。
经过一番研究,我发现这次崩溃是由 引起AVVideoCompositionCoreAnimationTool的
+videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:inLayer:
Run Code Online (Sandbox Code Playgroud)
我通过将其替换为下面的一个来修复它(但我们需要instruction.layerInstructions以这种方式处理):
+videoCompositionCoreAnimationToolWithAdditionalLayer:asTrackID:
Run Code Online (Sandbox Code Playgroud)
下面是一个适用于真实设备和模拟器的示例代码(由于 OP 没有明确标记 Swift,我将在此处复制我的 Objective-C 示例):
...
// Prepare watermark layer
CALayer *watermarkLayer = ...;
CMPersistentTrackID watermarkLayerTrackID = [asset unusedTrackID];
// !!! NOTE#01: Use as additional layer here instead of animation layer.
videoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithAdditionalLayer:watermarkLayer asTrackID:watermarkLayerTrackID];
// Create video composition instruction
AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
// - Watermark layer instruction
// !!! NOTE#02: Make this instruction track watermark layer by the `trackID`.
AVMutableVideoCompositionLayerInstruction *watermarkLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstruction];
watermarkLayerInstruction.trackID = watermarkLayerTrackID;
// - Video track layer instruction
AVAssetTrack *videoTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
AVMutableVideoCompositionLayerInstruction *videoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
// Watermark layer above video layer here.
instruction.layerInstructions = @[
watermarkLayerInstruction,
videoLayerInstruction,
];
videoComposition.instructions = @[instruction];
// Export the video w/ watermark.
AVAssetExportSession *exportSession = ...;
...
exportSession.videoComposition = videoComposition;
...
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果您只需要添加图像作为水印,则可以使用 的另一种解决AVVideoComposition方案
-videoCompositionWithAsset:applyingCIFiltersWithHandler:
Run Code Online (Sandbox Code Playgroud)
在真实设备和模拟器上也运行良好,但我测试了它并发现它更慢。看来这种方式更适合视频混合器/过滤器。
| 归档时间: |
|
| 查看次数: |
623 次 |
| 最近记录: |