Imrul Kayes
Imrul Kayes

Reputation: 648

Cannot load video with text/image overlay in swift 3

I am using AVFoundation for making text overlay and watermark on a video. I am facing some difficulties. I am using Image/Text overlay in video swift question's code. But I modified it. When I call function loadVideo, video does not load. I cannot figure it out. Here is my code

@IBAction func loadVideo(_ sender: Any) {

    let path = Bundle.main.path(forResource: "SampleVideo", ofType:"mp4")
    let fileURL = URL(fileURLWithPath: path!)

    let composition = AVMutableComposition()
    let vidAsset = AVURLAsset(url: fileURL as URL, options: nil)

    // get video track
    let vtrack =  vidAsset.tracks(withMediaType: AVMediaTypeVideo)
    let videoTrack:AVAssetTrack = vtrack[0] 
    _ = videoTrack.timeRange.duration
    let vid_timerange = CMTimeRangeMake(kCMTimeZero, vidAsset.duration)


    let compositionvideoTrack:AVMutableCompositionTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())

    do {
        _ = try compositionvideoTrack.insertTimeRange(vid_timerange, of: videoTrack, at: kCMTimeZero)
    } catch {
        print("error")
    }

    compositionvideoTrack.preferredTransform = videoTrack.preferredTransform

    // Watermark Effect
    let size = videoTrack.naturalSize

    let imglogo = UIImage(named: "iosIcon.png")
    let imglayer = CALayer()
    imglayer.contents = imglogo?.cgImage
    imglayer.frame = CGRect(x: 5, y: 5, width: 100, height: 100)
    imglayer.opacity = 0.6

    // create text Layer
    let titleLayer = CATextLayer()
    titleLayer.backgroundColor = UIColor.white.cgColor
    titleLayer.string = "Subtitle Overlay Text"
    titleLayer.font = UIFont(name: "Helvetica", size: 28)
    titleLayer.shadowOpacity = 0.5
    titleLayer.alignmentMode = kCAAlignmentCenter
    titleLayer.frame = CGRect(x: 0, y: 50, width: size.width, height: size.height / 6)

    let videolayer = CALayer()
    videolayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

    let parentlayer = CALayer()
    parentlayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    parentlayer.addSublayer(videolayer)
    parentlayer.addSublayer(imglayer)
    parentlayer.addSublayer(titleLayer)

    let layercomposition = AVMutableVideoComposition()
    layercomposition.frameDuration = CMTimeMake(1, 30)
    layercomposition.renderSize = size
    layercomposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videolayer, in: parentlayer)

    // instruction for watermark
    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, composition.duration)
    let videotrack = composition.tracks(withMediaType: AVMediaTypeVideo)[0] as AVAssetTrack
    let layerinstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videotrack)
    instruction.layerInstructions = NSArray(object: layerinstruction) as [AnyObject] as [AnyObject] as! [AVVideoCompositionLayerInstruction]
    layercomposition.instructions = NSArray(object: instruction) as [AnyObject] as [AnyObject] as! [AVVideoCompositionInstructionProtocol]



    //  create new file to receive data
    let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let docsDir = dirPaths[0] as String
    let movieFilePath = docsDir.appending("result.mov")

    let movieDestinationUrl = URL(fileURLWithPath: movieFilePath)
    //remove existing file
    _ = try? FileManager().removeItem(at: movieDestinationUrl)


    // use AVAssetExportSession to export video
    guard let assetExport = AVAssetExportSession(asset: composition, presetName:AVAssetExportPresetHighestQuality) else {return}
    assetExport.videoComposition = layercomposition
    assetExport.outputFileType = AVFileTypeQuickTimeMovie
    assetExport.outputURL = movieDestinationUrl
    assetExport.exportAsynchronously(completionHandler: {
        switch assetExport.status{
        case  AVAssetExportSessionStatus.failed:
            print("failed \(assetExport.error)")
        case AVAssetExportSessionStatus.cancelled:
            print("cancelled \(assetExport.error)")
        case AVAssetExportSessionStatus.completed:
            print("Completed")
        default:
            print("unknown")
        }
    })

}

What I am doing wrong? is something wrong with saving ExportAsset in file directory?

Upvotes: 3

Views: 934

Answers (2)

Markinson
Markinson

Reputation: 857

After creating the layerInstuctions try this code instead of whats above. for anyone else having trouble with this like I was. And you should be able to keep the preset at AVAssetExportPresetHighestQuality

instruction.layerInstructions = [layerinstruction]
layercomposition.instructions = [instruction]

    let movieFilePath = NSTemporaryDirectory() + "result.mp4"
    let movieDestinationUrl = URL(fileURLWithPath: movieFilePath)


// must delete existing temporary file from file directory, also use try catch
do {
    try FileManager.default.removeItem(at: movieDestinationUrl as URL)
} catch _ as NSError {
    print("Error")
}

Upvotes: 1

Imrul Kayes
Imrul Kayes

Reputation: 648

At last I decide to answer my own question. Above code is running without failing but in simulator it takes some time to export. So I changed the presetName:AVAssetExportPresetHighestQuality to AVAssetExportPresetMediumQuality. There is also a modification in exporting video file in documents directory. Above code does not remove existing video file in documents directory. So when I export another video it cannot override the existing video. So I remove the file. Below is the code:

let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let docsDir: AnyObject = dirPaths[0] as AnyObject
    let movieFilePath = docsDir.appending("result.mp4")
    let movieDestinationUrl = NSURL(fileURLWithPath: movieFilePath)

    // must delete existing temporary file from file directory, also use try catch
    do {
        try FileManager.default.removeItem(at: movieDestinationUrl as URL)
    } catch _ as NSError {
        print("Error")
    }

Upvotes: 1

Related Questions