0
votes

Right now I get a url from a AVMixComposition using AVExportSession. HighestQuality looks great but the file size is too big, a 15 sec video is 27 mb. MediumQuality looks horrible but the file size is only 1 mb. I know I have to use AVAssetWriter and play with the bit rate to find a middle ground.

The problem is I can't figure out a way to get a url from an AVMixComposition without using AVExportSession to pass to the AVAssetWriter.

What I have to do is save the video using exportSession, get the url, pass it to assetWriter, set the bit rate, and then compress it from there. The resulting .mp4 (has to .mp4) assetWriter video doesn't compress the original exportSession videoURL. It's actually bigger which defeats the purpose.

let mixComposition = AVMutableComposition()
// ...

guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }

exporter.outputURL = outputFileURL
exporter.outputFileType = AVFileType.mp4
exporter.shouldOptimizeForNetworkUse = true

exporter.exportAsynchronously {
    // ...
    let data = try! Data(contentsOf: outputFileURL)
    print("compression size: \(Double(data.count / 1048576)) mb") // 27 mb

    DispatchQueue.main.async { ...
        guard let videoURL = exporter.outputURL else { return }

        self.passUrlToAssetWriter(urlToCompress: videoURL)
}

func passUrlToAssetWriter(urlToCompress: URL) {

    let asset = AVAsset(url: urlToCompress)
    guard let videoTrack = asset.tracks(withMediaType: AVMediaType.video).first else { return }
    guard let audioTrack = asset.tracks(withMediaType: AVMediaType.audio).first else return }

    // dropping the bit rate made no difference
    let videoSettings:[String:Any] = [AVVideoCompressionPropertiesKey: [AVVideoAverageBitRateKey: 250000], AVVideoCodecKey: AVVideoCodecType.h264, AVVideoHeightKey: videoTrack.naturalSize.height, AVVideoWidthKey: videoTrack.naturalSize.width]
    let audioSettings: [String:Any] = [AVFormatIDKey : kAudioFormatMPEG4AAC, AVNumberOfChannelsKey : 2, AVSampleRateKey : 44100.0, AVEncoderBitRateKey: 128000]

    assetWriter = try! AVAssetWriter(outputURL: myOutputURL, fileType: AVFileType.mp4)
    // everything else to configure assetWriter ...

    assetWriter?.finishWriting(completionHandler: {

        let data = try! Data(contentsOf: self.assetWriter!.outputURL)!)
        print("compression size: \(Double(data.count / 1048576)) mb") // 27.5 mb, it's actually bigger than the exporter file size         
    })
}
1

1 Answers

0
votes

There is no need to use a url, you can pass the mixComposition to an AVPlayerItem > AVPlayer > player.currentItem.asset:

let mixComposition = AVMutableComposition()
// ...
passMixToAssetWriter(mixComposition)

func passMixToAssetWriter(_ mixComposition: AVMutableComposition) {

    let item = AVPlayerItem(asset: composition)
    let player = AVPlayer()
    player.replaceCurrentItem(with: item)
    guard let currentItem = player.currentItem else { return }

    let asset = currentItem.asset

    guard let videoTrack = asset.tracks(withMediaType: AVMediaType.video).first else { return }
    guard let audioTrack = asset.tracks(withMediaType: AVMediaType.audio).first else return }

    // raise the bit rate to 11000000
    let videoSettings:[String:Any] = [AVVideoCompressionPropertiesKey: [AVVideoAverageBitRateKey: 1100000], AVVideoCodecKey: AVVideoCodecType.h264, AVVideoHeightKey: videoTrack.naturalSize.height, AVVideoWidthKey: videoTrack.naturalSize.width]
    let audioSettings: [String:Any] = [AVFormatIDKey : kAudioFormatMPEG4AAC, AVNumberOfChannelsKey : 2, AVSampleRateKey : 44100.0, AVEncoderBitRateKey: 128000]

    assetWriter = try! AVAssetWriter(outputURL: myOutputURL, fileType: AVFileType.mp4)
    // everything else to configure assetWriter ...

    assetWriter?.finishWriting(completionHandler: {

        let data = try! Data(contentsOf: self.assetWriter!.outputURL)!)
        print("compression size: \(Double(data.count / 1048576)) mb") // 27.5 mb, it's actually bigger than the exporter file size         
    })
}