Swift 3: How to add watermark on video ? AVVideoCompositionCoreAnimationTool iOS 10 issue -
this code used work on ios9 add watermark , text video since ios10 it's not working anymore. there ios 10 bug has been filed no answer apple. have not been able implement workaround add watermark , text on video. code times video exported of time won't exported.
how should use avvideocompositioncoreanimationtool
works 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 applied set video in portrait otherwise rotated 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) { 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) { try filemanager.default.removeitem(at: filepath) } catch let error nserror { nslog("\(error), \(error.localizeddescription)") } var exporturl: url = filepath self.videourl = filepath 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() } } })
please note: if remove avvideocompositioncoreanimationtool
video exported ,hans no watermark , text on video. how make work avvideocompositioncoreanimationtool
not conflict avassetexportsession
?
some have implemented workaround customvideocompositorclass
, avvideocompositing
protocol seems heavy workaround compared how used work.
i have got answer here , working me. see if working you.
import uikit import assetslibrary import avfoundation enum quwatermarkposition { case topleft case topright case bottomleft case bottomright case default } class quwatermarkmanager: nsobject { func watermark(video videoasset:avasset, watermarktext text : string, savetolibrary flag : bool, watermarkposition position : quwatermarkposition, completion : ((status : avassetexportsessionstatus!, session: avassetexportsession!, outputurl : nsurl!) -> ())?) { self.watermark(video: videoasset, watermarktext: text, imagename: nil, savetolibrary: flag, watermarkposition: position) { (status, session, outputurl) -> () in completion!(status: status, session: session, outputurl: outputurl) } } func watermark(video videoasset:avasset, imagename name : string, savetolibrary flag : bool, watermarkposition position : quwatermarkposition, completion : ((status : avassetexportsessionstatus!, session: avassetexportsession!, outputurl : nsurl!) -> ())?) { self.watermark(video: videoasset, watermarktext: nil, imagename: name, savetolibrary: flag, watermarkposition: position) { (status, session, outputurl) -> () in completion!(status: status, session: session, outputurl: outputurl) } } private func watermark(video videoasset:avasset, watermarktext text : string!, imagename name : string!, savetolibrary flag : bool, watermarkposition position : quwatermarkposition, completion : ((status : avassetexportsessionstatus!, session: avassetexportsession!, outputurl : nsurl!) -> ())?) { dispatch_async(dispatch_get_global_queue(dispatch_queue_priority_default, 0), { () -> void in var mixcomposition = avmutablecomposition() var compositionvideotrack = mixcomposition.addmutabletrackwithmediatype(avmediatypevideo, preferredtrackid: int32(kcmpersistenttrackid_invalid)) var clipvideotrack = videoasset.trackswithmediatype(avmediatypevideo)[0] as! avassettrack compositionvideotrack.inserttimerange(cmtimerangemake(kcmtimezero, videoasset.duration), oftrack: clipvideotrack, attime: kcmtimezero, error: nil) clipvideotrack.preferredtransform let videosize = clipvideotrack.naturalsize var parentlayer = calayer() var videolayer = calayer() parentlayer.frame = cgrectmake(0, 0, videosize.width, videosize.height) videolayer.frame = cgrectmake(0, 0, videosize.width, videosize.height) parentlayer.addsublayer(videolayer) if text != nil { var titlelayer = catextlayer() titlelayer.backgroundcolor = uicolor.redcolor().cgcolor titlelayer.string = text titlelayer.font = "helvetica" titlelayer.fontsize = 15 titlelayer.alignmentmode = kcaalignmentcenter titlelayer.bounds = cgrectmake(0, 0, videosize.width, videosize.height) parentlayer.addsublayer(titlelayer) } else if name != nil { var watermarkimage = uiimage(named: name) var imagelayer = calayer() imagelayer.contents = watermarkimage?.cgimage var xposition : cgfloat = 0.0 var yposition : cgfloat = 0.0 let imagesize : cgfloat = 57.0 switch (position) { case .topleft: xposition = 0 yposition = 0 break case .topright: xposition = videosize.width - imagesize yposition = 0 break case .bottomleft: xposition = 0 yposition = videosize.height - imagesize break case .bottomright, .default: xposition = videosize.width - imagesize yposition = videosize.height - imagesize break default: break } imagelayer.frame = cgrectmake(xposition, yposition, imagesize, imagesize) imagelayer.opacity = 0.65 parentlayer.addsublayer(imagelayer) } var videocomp = avmutablevideocomposition() videocomp.rendersize = videosize videocomp.frameduration = cmtimemake(1, 30) videocomp.animationtool = avvideocompositioncoreanimationtool(postprocessingasvideolayer: videolayer, inlayer: parentlayer) var instruction = avmutablevideocompositioninstruction() instruction.timerange = cmtimerangemake(kcmtimezero, mixcomposition.duration) var videotrack = mixcomposition.trackswithmediatype(avmediatypevideo)[0] as! avassettrack let layerinstruction = self.videocompositioninstructionfortrack(compositionvideotrack, asset: videoasset) instruction.layerinstructions = [layerinstruction] videocomp.instructions = [instruction] let documentdirectory = nssearchpathfordirectoriesindomains(.documentdirectory, .userdomainmask, true)[0] as! string var dateformatter = nsdateformatter() dateformatter.datestyle = .longstyle dateformatter.timestyle = .shortstyle let date = dateformatter.stringfromdate(nsdate()) let savepath = documentdirectory.stringbyappendingpathcomponent("watermarkvideo-\(date).mov") let url = nsurl(fileurlwithpath: savepath) let exporter = avassetexportsession(asset: mixcomposition, presetname: avassetexportpresethighestquality) exporter.outputurl = url exporter.outputfiletype = avfiletypequicktimemovie exporter.shouldoptimizefornetworkuse = true exporter.videocomposition = videocomp exporter.exportasynchronouslywithcompletionhandler() { dispatch_async(dispatch_get_main_queue(), { () -> void in if exporter.status == avassetexportsessionstatus.completed { let outputurl = exporter.outputurl if flag { // save library let library = alassetslibrary() if library.videoatpathiscompatiblewithsavedphotosalbum(outputurl) { library.writevideoatpathtosavedphotosalbum(outputurl, completionblock: { (asseturl:nsurl!, error:nserror!) -> void in completion!(status: avassetexportsessionstatus.completed, session: exporter, outputurl: outputurl) }) } } else { completion!(status: avassetexportsessionstatus.completed, session: exporter, outputurl: outputurl) } } else { // error completion!(status: exporter.status, session: exporter, outputurl: nil) } }) } }) } private func orientationfromtransform(transform: cgaffinetransform) -> (orientation: uiimageorientation, isportrait: bool) { var assetorientation = uiimageorientation.up var isportrait = false if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 { assetorientation = .right isportrait = true } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 { assetorientation = .left isportrait = true } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 { assetorientation = .up } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 { assetorientation = .down } return (assetorientation, isportrait) } private func videocompositioninstructionfortrack(track: avcompositiontrack, asset: avasset) -> avmutablevideocompositionlayerinstruction { let instruction = avmutablevideocompositionlayerinstruction(assettrack: track) let assettrack = asset.trackswithmediatype(avmediatypevideo)[0] as! avassettrack var transform = assettrack.preferredtransform let assetinfo = orientationfromtransform(transform) var scaletofitratio = uiscreen.mainscreen().bounds.width / assettrack.naturalsize.width if assetinfo.isportrait { scaletofitratio = uiscreen.mainscreen().bounds.width / assettrack.naturalsize.height let scalefactor = cgaffinetransformmakescale(scaletofitratio, scaletofitratio) instruction.settransform(cgaffinetransformconcat(assettrack.preferredtransform, scalefactor), attime: kcmtimezero) } else { let scalefactor = cgaffinetransformmakescale(scaletofitratio, scaletofitratio) var concat = cgaffinetransformconcat(cgaffinetransformconcat(assettrack.preferredtransform, scalefactor), cgaffinetransformmaketranslation(0, uiscreen.mainscreen().bounds.width / 2)) if assetinfo.orientation == .down { let fixupsidedown = cgaffinetransformmakerotation(cgfloat(m_pi)) let windowbounds = uiscreen.mainscreen().bounds let yfix = assettrack.naturalsize.height + windowbounds.height let centerfix = cgaffinetransformmaketranslation(assettrack.naturalsize.width, yfix) concat = cgaffinetransformconcat(cgaffinetransformconcat(fixupsidedown, centerfix), scalefactor) } instruction.settransform(concat, attime: kcmtimezero) } return instruction } }
Comments
Post a Comment