1
votes

I am trying to apply AVMutableVideoCompositionLayerInstruction on an AVMutableComposition for a video. The problem is it does not honour the instruction when the video is saved using AVAssetExportSession. The weird part is, the same composition works with AVPlayer (AVPlayer honours the instruction).

Here's the code:

        let path = Bundle.main.path(forResource: "flame", ofType: "mp4")
        let url = NSURL(fileURLWithPath: path!)
        let asset = AVAsset(url: url as URL)

        let mutableComposition = AVMutableComposition()

        let type = AVMediaTypeVideo
        let prefTrackID = kCMPersistentTrackID_Invalid

        let sourceVideoAssetTrack: AVAssetTrack = asset.tracks(withMediaType: type).first!
        let sourceAudioAssetTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaTypeAudio).first!

        let videoCompositionTrack1 = mutableComposition.addMutableTrack(withMediaType: type, preferredTrackID: prefTrackID)


        do {
            let range = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60,600))
            try videoCompositionTrack1.insertTimeRange(range, of: sourceVideoAssetTrack, at: kCMTimeZero)
        }catch { print(error) }

        let firstTransform = videoCompositionTrack1.preferredTransform;

        let fromLayer = AVMutableVideoCompositionLayerInstruction(assetTrack: videoCompositionTrack1)
        fromLayer.setTransform(firstTransform, at: kCMTimeZero)
        fromLayer.setCropRectangle(CGRect.init(x: 5, y: 5, width: 200, height: 200), at: kCMTimeZero)

        let instruction = AVMutableVideoCompositionInstruction()
        instruction.layerInstructions = [fromLayer]
        instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60,600))

        videoComposition = AVMutableVideoComposition()
        videoComposition!.instructions = [instruction]
        videoComposition!.renderSize = CGSize.init(width: 300, height: 300)
        videoComposition!.frameDuration = CMTimeMake(1, 30)


        if(true){ // just to switch between the saving and playing modes
            var exportPath: NSString = NSTemporaryDirectory().appendingFormat("/video.mov")
            var exportUrl: NSURL = NSURL.fileURL(withPath: exportPath as String) as NSURL

            var exporter = AVAssetExportSession(asset: mutableComposition, presetName: AVAssetExportPresetMediumQuality)!
            exporter.outputURL = exportUrl as URL
            exporter.videoComposition = videoComposition!

            exporter.outputFileType = AVFileTypeMPEG4
            exporter.shouldOptimizeForNetworkUse = true
            exporter.canPerformMultiplePassesOverSourceMediaData = true

            exporter.exportAsynchronously(completionHandler: {
                PHPhotoLibrary.shared().performChanges({
                    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: exportUrl as URL)
                }) { completed, error in
                    if completed {
                        print("Video is saved!")
                    }
                }
            })
        }
        else{
            let playerItem = AVPlayerItem(asset: mutableComposition)
            playerItem.videoComposition = videoComposition!
            player = AVPlayer(playerItem: playerItem)
            playerLayer = AVPlayerLayer(player: player)
            playerLayer.frame = self.view.frame
            self.view.layer.addSublayer(playerLayer)
            player.play()
        }

AVPlayer honours cropRectangle instruction

AVPlayer honours cropRectangle instruction as seen above

Saved video is same as the original video

Saved video is same as the original video.

I am building this on iOS 9. What am I doing wrong?

1
could this be that AVPlayer will rotate the media based on the meta data, then apply your instructions, the exporter does not, you may need to assess its transform and adjust accordinglySean Lintern
Thanks for your comment Sean. I am not sure what metadata are you referring to here ? I am sorry I am a noob in AVFoundation. Are you referring to the AVAsset metadata ?karyboy
In the AVAsset there are transforms that tell the player to rotate it, when you use exporter you need to get and perform these transforms yourselfSean Lintern
But isn't AVMutableVideoCompositionLayerInstruction supposed to do the transforms, avplayer or exporter? And that is applied in both cases. Also I am not rotating the asset, just cropping.karyboy
Yes but you have to tell it if you want it to transform somethingSean Lintern

1 Answers

0
votes

When applying a transform on a AVMutableVideoCompositionLayerInstruction you can get the desired transform needed from the AVAsset's preferredTransform

EDIT: Turns out it was an export error for existing file, either use a unique name when trying to write e.g.

String(Date) + ".mov"

Or delete before trying to write