Tuesday, June 12, 2018

AVMutableVideoComposition output video shrinked

Leave a Comment

I'm a newbie to Swift. I'm trying to add a watermark with reference to code from SO. My original video resolution is 1280 X 720, but the output video is a shrinked version.

Here are the before and after pictures

Before

After

Here is my function to create a watermark.

private func watermark(video videoAsset:AVAsset, watermarkText text : String!, image : CGImage!, saveToLibrary flag : Bool, completion : ((_ status : AVAssetExportSessionStatus?, _ session: AVAssetExportSession?, _ outputURL : URL?) -> ())?) {     DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async {          let mixComposition = AVMutableComposition()          let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))         let clipVideoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0]         do {             try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: clipVideoTrack, at: kCMTimeZero)         }         catch {             print(error.localizedDescription)         }          let videoSize = clipVideoTrack.naturalSize          print("Video size", videoSize.height) //720         print("Video size", videoSize.width) //1280          let parentLayer = CALayer()         let videoLayer = CALayer()         parentLayer.frame = CGRect(x: 0.0,                                    y: 0.0,                                    width: videoSize.width,                                    height: videoSize.height)         videoLayer.frame = CGRect(x: 0.0,                                   y: 0.0,                                   width: videoSize.width,                                   height: videoSize.height)         parentLayer.addSublayer(videoLayer)          if text != nil {             let titleLayer = CATextLayer()             titleLayer.backgroundColor = UIColor.red.cgColor             titleLayer.string = text             titleLayer.font = "Helvetica" as CFTypeRef             titleLayer.fontSize = 15             titleLayer.alignmentMode = kCAAlignmentCenter             titleLayer.bounds = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)             parentLayer.addSublayer(titleLayer)         } else if image != nil {             let imageLayer = CALayer()             imageLayer.contents = image              let width: CGFloat = (self.imageView.image?.size.width)!             let height: CGFloat = (self.imageView.image?.size.height)!              print("Video size", height) //720             print("Video size", width) //1280              imageLayer.frame = CGRect(x: 0.0, y: 0.0, width: width, height: height)             imageLayer.opacity = 0.65             parentLayer.addSublayer(imageLayer)         }          let videoComp = AVMutableVideoComposition()         videoComp.renderSize = videoSize         videoComp.frameDuration = CMTimeMake(1, Int32(clipVideoTrack.nominalFrameRate))         videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)          let instruction = AVMutableVideoCompositionInstruction()         instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration)         _ = mixComposition.tracks(withMediaType: AVMediaTypeVideo)[0] as AVAssetTrack          let layerInstruction = self.videoCompositionInstructionForTrack(track: compositionVideoTrack, asset: videoAsset)          instruction.layerInstructions = [layerInstruction]         videoComp.instructions = [instruction]          let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]         let dateFormatter = DateFormatter()         dateFormatter.dateStyle = .long         dateFormatter.timeStyle = .short         let date = dateFormatter.string(from: Date())         let url = URL(fileURLWithPath: documentDirectory).appendingPathComponent("watermarkVideo-\(date).mov")          let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)         exporter?.outputURL = url         exporter?.outputFileType = AVFileTypeQuickTimeMovie         exporter?.shouldOptimizeForNetworkUse = true         exporter?.videoComposition = videoComp          exporter?.exportAsynchronously() {             DispatchQueue.main.async {                  if exporter?.status == AVAssetExportSessionStatus.completed {                     let outputURL = exporter?.outputURL                     if flag {                          if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(outputURL!.path) {                             PHPhotoLibrary.shared().performChanges({                                 PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL!)                             }) { saved, error in                                 if saved {                                     completion!(AVAssetExportSessionStatus.completed, exporter, outputURL)                                 }                             }                         }                     } else {                         completion!(AVAssetExportSessionStatus.completed, exporter, outputURL)                     }                  } else {                     // Error                     completion!(exporter?.status, exporter, nil)                 }             }         }     } } 

While the size of watermark image is absolutely correct, the video is really shrinked.

2 Answers

Answers 1

can you try this function

private func watermark(video videoAsset: AVAsset, watermarkText text : String!, image : CGImage!, saveToLibrary flag : Bool, completion : ((_ status : AVAssetExportSessionStatus ?, _ session: AVAssetExportSession ?, _ outputURL : URL ?) -> ())?) {   DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async {      let mixComposition = AVMutableComposition()      let compositionVideoTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))     let clipVideoTrack = videoAsset.tracks(withMediaType: AVMediaTypeVideo)[0] as AVAssetTrack     do {       try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), of: clipVideoTrack, at: kCMTimeZero)     }       catch {       print(error.localizedDescription)     }      let videoSize = clipVideoTrack.naturalSize      let parentLayer = CALayer()     let videoLayer = CALayer()     parentLayer.frame = CGRect(x: 0.0,       y: 0.0,       width: videoSize.width,       height: videoSize.height)     videoLayer.frame = CGRect(x: 0.0,       y: 0.0,       width: videoSize.width,       height: videoSize.height)     parentLayer.addSublayer(videoLayer)      //            if text != nil {     //                let titleLayer = CATextLayer()     //                titleLayer.backgroundColor = UIColor.red.cgColor     //                titleLayer.string = text     //                titleLayer.font = "Helvetica" as CFTypeRef     //                titleLayer.fontSize = 15     //                titleLayer.alignmentMode = kCAAlignmentCenter     //                titleLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)     //                parentLayer.addSublayer(titleLayer)     //            } else     if image != nil {       let imageLayer = CALayer()       imageLayer.contents = image        let width: CGFloat = (self.imageView.image ?.size.width)!       let height: CGFloat = (self.imageView.image ?.size.height)!       //       print("Video size", height)       print("Video size", width)        imageLayer.frame = CGRect(x: 0, y: 0, width: width, height: height)        //                imageLayer.frame = CGRect(x: 0, y: 0, width: videoSize.width, height: videoSize.height)        imageLayer.opacity = 1       parentLayer.addSublayer(imageLayer)     }      let videoComp = AVMutableVideoComposition()     videoComp.renderSize = videoSize     videoComp.frameDuration = CMTimeMake(1, Int32(clipVideoTrack.nominalFrameRate))     videoComp.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, in: parentLayer)      let instruction = AVMutableVideoCompositionInstruction()     instruction.timeRange = CMTimeRangeMake(kCMTimeZero, mixComposition.duration)     let videotrack = mixComposition.tracks(withMediaType: AVMediaTypeVideo)[0] as AVAssetTrack     let layerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videotrack)      //            let layerInstruction = self.videoCompositionInstructionForTrack(track: compositionVideoTrack, asset: videoAsset)      instruction.layerInstructions = [layerInstruction]     videoComp.instructions = [instruction]      let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]     let dateFormatter = DateFormatter()     dateFormatter.dateStyle = .long     dateFormatter.timeStyle = .short     let date = dateFormatter.string(from: Date())     let url = URL(fileURLWithPath: documentDirectory).appendingPathComponent("watermarkVideo-\(date).mp4")      guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return}     exporter.videoComposition = videoComp     exporter.outputFileType = AVFileTypeMPEG4     exporter.outputURL = url      exporter.exportAsynchronously() {       DispatchQueue.main.async {          if exporter.status == AVAssetExportSessionStatus.completed {           let outputURL = exporter.outputURL           if flag {             // Save to library             //                            let library = ALAssetsLibrary()              if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(outputURL!.path) {               PHPhotoLibrary.shared().performChanges({                 PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputURL!)               }) {                 saved, error in                               if saved {                   completion!(AVAssetExportSessionStatus.completed, exporter, outputURL)                 }               }             }              //                            if library.videoAtPathIs(compatibleWithSavedPhotosAlbum: outputURL) {             //                                library.writeVideoAtPathToSavedPhotosAlbum(outputURL,             //                                                                           completionBlock: { (assetURL:NSURL!, error:NSError!) -> Void in             //             //                                                                            completion!(AVAssetExportSessionStatus.Completed, exporter, outputURL)             //                                })             //                            }           } else {             completion!(AVAssetExportSessionStatus.completed, exporter, outputURL)           }          } else {           // Error           completion!(exporter.status, exporter, nil)         }       }     }   } } 

Answers 2

The code above for creating the watermarked video seems not to be the reason for the smaller output resolution.

Problem

The resolution depends on what kind of AVAsset is put into the watermark method.

Example: Frequently an UIImagePickerController is used. There is the delegate method

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])  

There one often can see something like this:

let url = info[UIImagePickerControllerMediaURL] as? URL let videoAsset = AVAsset(url: url!) self.watermark(video: videoAsset, watermarkText: nil, image: self.imageView.image?.cgImage ... 

But with the lines above a downsized input image is used, e.g. instead of having a video with 1920x1080 one has a reduced video size of 1280x720.

Solution

A method for determining the AVAsset from the PHAsset could look like this:

private func videoAsset(for asset: PHAsset, completion: @escaping (AVAsset?) -> Void) {     let requestOptions = PHVideoRequestOptions()     requestOptions.version = .original     PHImageManager.default().requestAVAsset(forVideo: asset, options: requestOptions, resultHandler: {         (avAsset, avAudioMix, info) in         completion(avAsset)     }) } 

And where to get the PHAsset from? It can also be determined in the didFinishPickingMediaWithInfo method by using UIImagePickerControllerPHAsset:

let asset = info[UIImagePickerControllerPHAsset] as? PHAsset 

Quick Test

For a quick test one could use:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {     if let asset = info[UIImagePickerControllerPHAsset] as? PHAsset {         picker.dismiss(animated: true, completion: { [weak self] in             self?.videoAsset(for: asset, completion: { (avAsset) in                 if let videoAsset = avAsset {                     DispatchQueue.main.async {                         self?.watermark(video: videoAsset, watermarkText: nil, image: self?.imageView.image?.cgImage, saveToLibrary: true) { (exportStat: AVAssetExportSessionStatus? , session: AVAssetExportSession?, url: URL?) in                             print("url: \(String(describing: url?.debugDescription))")                         }                     }                 }             })         })     } } 

The result is a video in the original resolution with a watermark on the lower left, see screenshot of resulting video:

test of adding watermark

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment