添加文本字幕到video轨道(在Swift中)失败,错误代码-11841

我一直在努力为video添加文字字幕。 我已经添加了一些我详细提到的链接,但他们并没有帮助。

在下面的代码中,我正在试图添加一个字幕到video。 输出文件path如下:

file:///var/mobile/Applications/03E49B29-1070-4541-B7CB-B1366732C179/Documents/output_movie.mov 

另外,在下面的临时path中,input文件是通过调用UIPickerView在同一应用程序中logging的:

 file:///private/var/mobile/Applications/03E49B29-1070-4541-B7CB-B1366732C179/tmp/capture/capturedvideo.MOV 

我得到的错误如下,

 Error:- Domain=AVFoundationErrorDomain Code=-11841 "Operation Stopped" UserInfo=0x15ebcfb0 {NSLocalizedDescription=Operation Stopped, NSLocalizedFailureReason=The video could not be composed.} Description:- <AVAssetExportSession: 0x15d97c80, asset = <AVMutableComposition: 0x15d788d0 tracks = ("<AVMutableCompositionTrack: 0x15d86910 trackID = 1, mediaType = vide, editCount = 1>")>, presetName = AVAssetExportPresetHighestQuality, outputFileType = com.apple.quicktime-movie Completed merging the video with status code 4 

我使用的代码如下。 我在iPhone 4s上的iOS 7.1.2上运行它。

 class func mergeVideoWithTheme(outputUrl: NSURL, inputVideoUrl videoUrl: NSURL!, onComplete completionHandler: ((Int) -> ())!) -> Void { // 1. mergeComposition adds all the AVAssets var mergeComposition : AVMutableComposition = AVMutableComposition() var trackVideo : AVMutableCompositionTrack = mergeComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID()) //var trackAudio : AVMutableCompositionTrack = mergeComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID()) // 2. Add a bank for theme insertion later //trackVideo.insertTimeRange(range, ofTrack: VideoHelper.Static.blankTrack, atTime: kCMTimeZero, error: nil) // 3. Source tracks let sourceAsset = AVURLAsset(URL: videoUrl, options: nil) let sourceDuration = CMTimeRangeMake(kCMTimeZero, sourceAsset.duration) let vtrack: AVAssetTrack? = sourceAsset.tracksWithMediaType(AVMediaTypeVideo)[0] as? AVAssetTrack let atrack: AVAssetTrack? = sourceAsset.tracksWithMediaType(AVMediaTypeAudio)[0] as? AVAssetTrack if (vtrack == nil) { return } let renderWidth = vtrack?.naturalSize.width let renderHeight = vtrack?.naturalSize.height let insertTime = kCMTimeZero let endTime = sourceAsset.duration let range = sourceDuration // append tracks trackVideo.insertTimeRange(sourceDuration, ofTrack: vtrack, atTime: insertTime, error: nil) //if(atrack > 0){ // trackAudio.insertTimeRange(sourceDuration, ofTrack: atracks[0] as AVAssetTrack, atTime: insertTime, error: nil) //} // 4. Add subtitles (we call it theme) var themeVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition(propertiesOfAsset: sourceAsset) // 4.1 - Create AVMutableVideoCompositionInstruction let mainInstruction: AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction() mainInstruction.timeRange = range // 4.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation. let videolayerInstruction : AVMutableVideoCompositionLayerInstruction = AVMutableVideoCompositionLayerInstruction() videolayerInstruction.setTransform(trackVideo.preferredTransform, atTime: insertTime) videolayerInstruction.setOpacity(0.0, atTime: endTime) // 4.3 - Add instructions mainInstruction.layerInstructions = NSArray(array: [videolayerInstruction]) themeVideoComposition.renderScale = 1.0 themeVideoComposition.renderSize = CGSizeMake(renderWidth!, renderHeight!) themeVideoComposition.frameDuration = CMTimeMake(1, 30) themeVideoComposition.instructions = NSArray(array: [mainInstruction]) // add the theme // setup variables // add text let title = String("Testing this subtitle") var titleLayer = CATextLayer() titleLayer.string = title titleLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) let fontName: CFStringRef = "Helvetica-Bold" let fontSize = CGFloat(36) titleLayer.font = CTFontCreateWithName(fontName, fontSize, nil) titleLayer.alignmentMode = kCAAlignmentCenter titleLayer.foregroundColor = UIColor.whiteColor().CGColor var backgroundLayer = CALayer() backgroundLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) backgroundLayer.masksToBounds = true backgroundLayer.addSublayer(titleLayer) // 2. set parent layer and video layer var parentLayer = CALayer() var videoLayer = CALayer() parentLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) videoLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) parentLayer.addSublayer(backgroundLayer) parentLayer.addSublayer(videoLayer) // 3. make animation themeVideoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer) // Remove the file if it already exists (merger does not overwrite) let fileManager = NSFileManager.defaultManager() fileManager.removeItemAtURL(outputUrl, error: nil) // export to output url var exporter = AVAssetExportSession(asset: mergeComposition, presetName: AVAssetExportPresetHighestQuality) exporter.outputURL = outputUrl exporter.videoComposition = themeVideoComposition exporter.outputFileType = AVFileTypeQuickTimeMovie exporter.shouldOptimizeForNetworkUse = true exporter.exportAsynchronouslyWithCompletionHandler({ if (exporter.error != nil) { println("Error") println(exporter.error) println("Description") println(exporter.description) } completionHandler(exporter.status.rawValue) }) } 

我将不胜感激您的帮助。 我没有find任何快速的animation添加到video的例子。 想知道如果它快速为任何人工作。

参考文献:(1) https://gist.github.com/SheffieldKevin/c01789ccff2b2a87f5ea (2) http://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos

创build图层指令时未能插入trackID。 试试:

 let videolayerInstruction : AVMutableVideoCompositionLayerInstruction = AVMutableVideoCompositionLayerInstruction(vtrack) 

当我尝试这个字幕没有出现。 我通过交换子层的顺序来修复它。

parentLayer.addSublayer(videoLayer)

parentLayer.addSubLayer(backgroundLayer)

我更新了它与Swift 3 +一起工作,添加了由Deepakbuild议的修复。

 func mergeVideoWithTheme(outputUrl: NSURL, inputVideoUrl videoUrl: NSURL!, onComplete completionHandler: ((Int) -> ())!) -> Void { do { // 1. mergeComposition adds all the AVAssets var mergeComposition : AVMutableComposition = AVMutableComposition() var trackVideo : AVMutableCompositionTrack = mergeComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID()) //var trackAudio : AVMutableCompositionTrack = mergeComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID()) // 2. Add a bank for theme insertion later //trackVideo.insertTimeRange(range, ofTrack: VideoHelper.Static.blankTrack, atTime: kCMTimeZero, error: nil) // 3. Source tracks let sourceAsset = AVURLAsset(url: videoUrl as URL, options: nil) let sourceDuration = CMTimeRangeMake(kCMTimeZero, sourceAsset.duration) let vtrack: AVAssetTrack? = sourceAsset.tracks(withMediaType: AVMediaTypeVideo)[0] as? AVAssetTrack let atrack: AVAssetTrack? = sourceAsset.tracks(withMediaType: AVMediaTypeAudio)[0] as? AVAssetTrack if (vtrack == nil) { return } let renderWidth = vtrack?.naturalSize.width let renderHeight = vtrack?.naturalSize.height let insertTime = kCMTimeZero let endTime = sourceAsset.duration let range = sourceDuration // append tracks try trackVideo.insertTimeRange(sourceDuration, of: vtrack!, at: insertTime) //if(atrack > 0){ // trackAudio.insertTimeRange(sourceDuration, ofTrack: atracks[0] as AVAssetTrack, atTime: insertTime, error: nil) //} // 4. Add subtitles (we call it theme) var themeVideoComposition : AVMutableVideoComposition = AVMutableVideoComposition(propertiesOf: sourceAsset) // 4.1 - Create AVMutableVideoCompositionInstruction let mainInstruction: AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction() mainInstruction.timeRange = range // 4.2 - Create an AVMutableVideoCompositionLayerInstruction for the video track and fix the orientation. let videolayerInstruction : AVMutableVideoCompositionLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: vtrack!) videolayerInstruction.setTransform(trackVideo.preferredTransform, at: insertTime) videolayerInstruction.setOpacity(0.0, at: endTime) // 4.3 - Add instructions mainInstruction.layerInstructions = NSArray(array: [videolayerInstruction]) as! [AVVideoCompositionLayerInstruction] themeVideoComposition.renderScale = 1.0 themeVideoComposition.renderSize = CGSize(width: renderWidth!, height: renderHeight!) themeVideoComposition.frameDuration = CMTimeMake(1, 30) themeVideoComposition.instructions = NSArray(array: [mainInstruction]) as! [AVVideoCompositionInstructionProtocol] // add the theme // setup variables // add text let title = String("Testing this subtitle") var titleLayer = CATextLayer() titleLayer.string = title titleLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) let fontName: CFString = "Helvetica-Bold" as CFString let fontSize = CGFloat(36) titleLayer.font = CTFontCreateWithName(fontName, fontSize, nil) titleLayer.alignmentMode = kCAAlignmentCenter titleLayer.foregroundColor = UIColor.white.cgColor var backgroundLayer = CALayer() backgroundLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) backgroundLayer.masksToBounds = true backgroundLayer.addSublayer(titleLayer) // 2. set parent layer and video layer var parentLayer = CALayer() var videoLayer = CALayer() parentLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) videoLayer.frame = CGRect(x: 0, y: 0, width: renderWidth!, height: renderHeight!) parentLayer.addSublayer(backgroundLayer) parentLayer.addSublayer(videoLayer) // 3. make animation themeVideoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer) // Remove the file if it already exists (merger does not overwrite) let fileManager = FileManager.default try fileManager.removeItem(at: outputUrl as URL) // export to output url var exporter = AVAssetExportSession(asset: mergeComposition, presetName: AVAssetExportPresetHighestQuality) exporter?.outputURL = outputUrl as URL exporter?.videoComposition = themeVideoComposition exporter?.outputFileType = AVFileTypeQuickTimeMovie exporter?.shouldOptimizeForNetworkUse = true exporter?.exportAsynchronously(completionHandler: { if (exporter?.error != nil) { print("Error") print(exporter?.error) print("Description") print(exporter?.description) } completionHandler((exporter?.status.rawValue)!) }) } catch{ } }