Swift 3:如何在视频上添加水印?AVVideoCompositionCoreAnimationTool iOS 10 问题 [英] Swift 3: How to add watermark on video ? AVVideoCompositionCoreAnimationTool iOS 10 issue
问题描述
此代码曾经在 iOS9 上工作以向视频添加水印和文本,但自 iOS10 以来它不再工作了.有一个 iOS 10 错误 已提交但 Apple 没有答复.我无法实施任何解决方法来在视频上添加水印和文本.使用此代码有时会成功导出视频,但大多数情况下不会导出.
This code used to work on iOS9 to add a watermark and text to a video but since iOS10 it's not working anymore. There is a iOS 10 bug that has been filed but no answer from Apple. I have not been able to implement any workaround to add watermark and text on video. With this code some times the video will be exported successfully but most of time it won't be exported.
我应该如何使用 AVVideoCompositionCoreAnimationTool
以便它像在 iOS9 上一样工作.
How should I use AVVideoCompositionCoreAnimationTool
so that it works as it did on iOS9.
let videoComposition: AVMutableVideoComposition = AVMutableVideoComposition()
videoComposition.frameDuration = CMTimeMake(1, 60)
videoComposition.renderSize = CGSize(width: clipVideoTrack.naturalSize.height, height: clipVideoTrack.naturalSize.height)
let instruction: AVMutableVideoCompositionInstruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30))
// transformer is applied to set the video in portrait otherwise it is rotated by 90 degrees
let transformer: AVMutableVideoCompositionLayerInstruction =
AVMutableVideoCompositionLayerInstruction(assetTrack: clipVideoTrack)
let t1: CGAffineTransform = CGAffineTransform(translationX: clipVideoTrack.naturalSize.height, y: -(clipVideoTrack.naturalSize.width - clipVideoTrack.naturalSize.height)/2)
let t2: CGAffineTransform = t1.rotated(by: CGFloat(M_PI_2))
var finalTransform: CGAffineTransform = t2
transformer.setTransform(finalTransform, at: kCMTimeZero)
instruction.layerInstructions = NSArray(object: transformer) as! [AVVideoCompositionLayerInstruction]
videoComposition.instructions = NSArray(object: instruction) as! [AVVideoCompositionInstructionProtocol]
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)
do {
try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, asset.duration), of: clipVideoTrack, at: kCMTimeZero)
} catch {
print(error)
}
//Add watermark
let myImage = UIImage(named: "logo")
let aLayer = CALayer()
aLayer.contents = myImage!.cgImage
aLayer.frame = CGRect(x: (clipVideoTrack.naturalSize.height*(self.view.bounds.width-45))/self.view.bounds.width, y: (clipVideoTrack.naturalSize.height*(self.view.bounds.width-40))/self.view.bounds.width, width: (clipVideoTrack.naturalSize.height*40)/self.view.bounds.width, height: (clipVideoTrack.naturalSize.height*40)/self.view.bounds.width)
let titleLayer = CATextLayer()
titleLayer.string = "text"
titleLayer.font = UIFont(name: "helvetica", size: 0)
titleLayer.fontSize = clipVideoTrack.naturalSize.height/16
titleLayer.shadowOpacity = 0.5
titleLayer.alignmentMode = kCAAlignmentCenter
titleLayer.frame = CGRect(x: 0, y: 0, width: clipVideoTrack.naturalSize.height, height: clipVideoTrack.naturalSize.height/6)
titleLayer.display()
let videoSize = asset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize
let parentLayer = CALayer()
let videoLayer = CALayer()
parentLayer.frame = CGRect(x: 0, y: 0, width: videoSize.height, height: videoSize.height)
videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.height, height: videoSize.height)
parentLayer.addSublayer(videoLayer)
parentLayer.addSublayer(aLayer)
parentLayer.addSublayer(titleLayer)
videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)
do { try FileManager.default.removeItem(at: filePath) }
catch let error as NSError {
NSLog("(error), (error.localizedDescription)")
}
var exportUrl: URL = filePath
self.videoUrl = filePath as NSURL
var exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetMediumQuality)
exporter!.videoComposition = videoComposition
exporter!.outputFileType = AVFileTypeQuickTimeMovie
exporter!.outputURL = URL(fileURLWithPath: exportUrl.path)
exporter!.exportAsynchronously(completionHandler: {
DispatchQueue.main.async {
self.view.layer.addSublayer(self.avPlayerLayer)
let item = AVPlayerItem(url: exportUrl)
self.player.replaceCurrentItem(with: item)
if (self.player.currentItem != nil) {
print("Starting playback!")
self.player.play()
}
}
})
请注意:如果我删除 AVVideoCompositionCoreAnimationTool
,那么视频总是会被导出,但是视频上没有水印和文字.如何使它工作,以便 AVVideoCompositionCoreAnimationTool
不与 AVAssetExportSession
冲突?
PLEASE NOTE: If I remove AVVideoCompositionCoreAnimationTool
then the video is always exported but this ,hans no watermark and text on video. How to make it work so that AVVideoCompositionCoreAnimationTool
does not conflict with AVAssetExportSession
?
有些已经实施了 解决方法 使用 customVideoCompositorClass
和 AVVideoCompositing
协议,但与过去的工作方式相比,这似乎是一个繁重的解决方法.
Some have implemented a workaround with customVideoCompositorClass
and AVVideoCompositing
protocol but this seems to be a heavy workaround compared to how it used to work.
推荐答案
这个解决方案对我有用,超级简单,超级快
This solution work for me, super easy and super fast
func addWatermark(inputURL: URL, outputURL: URL, handler:@escaping (_ exportSession: AVAssetExportSession?)-> Void) {
let mixComposition = AVMutableComposition()
let asset = AVAsset(url: inputURL)
let videoTrack = asset.tracks(withMediaType: AVMediaType.video)[0]
let timerange = CMTimeRangeMake(kCMTimeZero, asset.duration)
let compositionVideoTrack:AVMutableCompositionTrack = mixComposition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))!
do {
try compositionVideoTrack.insertTimeRange(timerange, of: videoTrack, at: kCMTimeZero)
compositionVideoTrack.preferredTransform = videoTrack.preferredTransform
} catch {
print(error)
}
let watermarkFilter = CIFilter(name: "CISourceOverCompositing")!
let watermarkImage = CIImage(image: UIImage(named: "waterMark")!)
let videoComposition = AVVideoComposition(asset: asset) { (filteringRequest) in
let source = filteringRequest.sourceImage.clampedToExtent()
watermarkFilter.setValue(source, forKey: "inputBackgroundImage")
let transform = CGAffineTransform(translationX: filteringRequest.sourceImage.extent.width - (watermarkImage?.extent.width)! - 2, y: 0)
watermarkFilter.setValue(watermarkImage?.transformed(by: transform), forKey: "inputImage")
filteringRequest.finish(with: watermarkFilter.outputImage!, context: nil)
}
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) else {
handler(nil)
return
}
exportSession.outputURL = outputURL
exportSession.outputFileType = AVFileType.mp4
exportSession.shouldOptimizeForNetworkUse = true
exportSession.videoComposition = videoComposition
exportSession.exportAsynchronously { () -> Void in
handler(exportSession)
}
}
对不起,如果我没有发布如何调用这个函数.我已经有一段时间没有碰过那个代码了,所以我希望这已经足够了:
Sorry if i haven't posted how to call this function. It's been a while that i haven't touched that code, so i hope this will be enough:
let outputURL = NSURL.fileURL(withPath: "TempPath")
let inputURL = NSURL.fileURL(withPath: "VideoWithWatermarkPath")
addWatermark(inputURL: inputURL, outputURL: outputURL, handler: { (exportSession) in
guard let session = exportSession else {
// Error
return
}
switch session.status {
case .completed:
guard NSData(contentsOf: outputURL) != nil else {
// Error
return
}
// Now you can find the video with the watermark in the location outputURL
default:
// Error
}
})
这篇关于Swift 3:如何在视频上添加水印?AVVideoCompositionCoreAnimationTool iOS 10 问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!