3

I'm currently exporting a video in the following way:

   let exporter = AVAssetExportSession.init(asset: mixComposition, presetName: AVAssetExportPreset1280x720)
   exporter?.outputURL = outputPath
   exporter?.outputFileType = AVFileType.mp4
   exporter?.shouldOptimizeForNetworkUse = true
   exporter?.videoComposition = mainCompositionInst

A 15s video consumes about 20MB in data. Comparing this to Snapchat's 2MB videos, this number seems totally unacceptable.

I already reduced the quality of the export- and capture session (1280x720).

The video is filmed on a custom camera. UIImagePickerController is not used.

AVAssetExportSession is used with default settings.

Is there any way I can reduce the size of my videos? Thanks a lot!

EDIT 1: I tried to use this library: https://cocoapods.org/pods/NextLevelSessionExporter

Unfortunately, this creates sizing problems and removes my audio:

// Creating exporter
    let exporter = NextLevelSessionExporter(withAsset: mixComposition)
    exporter.outputURL = outputPath
    exporter.outputFileType = AVFileType.mp4
    exporter.videoComposition = mainCompositionInst

    let compressionDict: [String: Any] = [
        AVVideoAverageBitRateKey: NSNumber(integerLiteral: 2500000),
        AVVideoProfileLevelKey: AVVideoProfileLevelH264BaselineAutoLevel as String,
        ]

        exporter.videoOutputConfiguration = [
            AVVideoCodecKey: AVVideoCodecType.h264,
            AVVideoWidthKey: NSNumber(integerLiteral: 1280),
            AVVideoHeightKey: NSNumber(integerLiteral: 720),
            AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
            AVVideoCompressionPropertiesKey: compressionDict
        ]

        exporter.audioOutputConfiguration = [
            AVFormatIDKey: kAudioFormatMPEG4AAC,
            AVEncoderBitRateKey: NSNumber(integerLiteral: 128000),
            AVNumberOfChannelsKey: NSNumber(integerLiteral: 2),
            AVSampleRateKey: NSNumber(value: Float(44100))
        ]

enter image description here

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
thelearner
  • 1,440
  • 3
  • 27
  • 58
  • Not a duplicate, the context is different. I am using AVAssetExportSession here. – thelearner May 28 '18 at 09:19
  • Have U tried another export preset options - e.g. `AVAssetExportPresetMediumQuality` ? – Evgeny Karkan May 31 '18 at 10:40
  • @EvgenyKarkan Yes, but the quality is unacceptable bad. – thelearner May 31 '18 at 11:00
  • 1
    @holex "AVAssetExportSession is used with default settings." - Thanks for your toxic comment. – thelearner Jun 02 '18 at 14:50
  • @dfi, the comment was not toxic at all, my friend, the default settings give you 20MB large video, are you expecting some kinda random miracle or what? change the specs and you can get different size of output. piece of cake... but you may need to define how much _loss_ of the video's quality you are willing to accept to get a smaller output, without that your post makes no sense at all, I'm afraid. – holex Jun 02 '18 at 14:56
  • @holex That's why I'm asking this question .. (???). I do not find any references on that and I do not know how I can efficiently tweak the settings. – thelearner Jun 02 '18 at 15:05
  • @dfi, as per you still need to define what kinda _loss_ of the video quality you can accept to get a still decent output, after that it is easy to find the _efficient_ settings to create such output... until you can define the _loss_, you cannot find _efficient_ setting either – neither could anyone else for you. I hope you understand the causality here...? _e.g.: is it 1 FPS of your video okay for you? no? what is the minimum FPS you seek? 60? or need 240 for slow-mo? well, you are doomed. could you accept 12 FPS, maybe? there might be something then, etc..._ if you see the flow here. – holex Jun 02 '18 at 15:16
  • 1
    @dfi This problem is correlated to this issue: https://stackoverflow.com/questions/50700076/avmutablecomposition-resizing-issue – thelearner Jun 16 '18 at 11:13
  • @thelearner hey man, I am in the same exact situation that you was in. Do you have GitHub link with the code that you used to get this working? I'm using AVExportSession and a 15 sec HighestQuality video is 27mb, MediumQuality is 1 mb but looks horrible. Any help would be appreciated – Lance Samaria Jul 08 '20 at 07:38

4 Answers4

8

To reduce file size try these properties for setting up HEVC codec (use cocoa pod NextLevelSessionExporter):

let compressionDict: [String: Any] = [
AVVideoAverageBitRateKey: NSNumber(integerLiteral: 2500000), //lower it if you wish
AVVideoProfileLevelKey: AVVideoProfileLevelH264BaselineAutoLevel as String,
]
exporter.videoOutputConfiguration = [
    AVVideoCodecKey : AVVideoCodecType.hevc,
    AVVideoWidthKey : NSNumber(integerLiteral: 1280),
    AVVideoHeightKey: NSNumber(integerLiteral: 720),
    AVVideoScalingModeKey: AVVideoScalingModeResizeAspectFill,
    AVVideoCompressionPropertiesKey: compressionDict
]

You need to upgrade to macOS High Sierra and iOS 11 in order to use HEVC video codec. But if you can't use HEVC for some reason, use regular H.264 with lower bitrate.

AVVideoCodecKey : AVVideoCodecType.h264:

enter image description here

Also, look at this SO post about video bitrate in iOS.

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • 2
    first of all, thanks a lot for helping me! Your answer reduced the size from 20MB to stunning 4MB. I love you, dude!. You don't know how long I researched to achieve this. - You are my hero. I wasn't able to use HEVC, so I used h264 as you suggested. THANK YOU SO MUCH! – thelearner Jun 02 '18 at 15:34
  • Сheck the type of data contained in samplebuffer. https://stackoverflow.com/questions/23216610/ios-avassetwriter-writes-video-but-does-not-record-audio – Andy Jazz Jun 02 '18 at 16:53
  • 1
    Hello andy! After days of hustling, I finally managed to fix my problem. There is an active bug in iOS 11, that causes sizing issues with mirrored videos. I mirrored the video manually and it is now working perfectly fine. Bless you for your help! – thelearner Jun 16 '18 at 11:12
  • I am getting NextLevelSessionExporter, writing failed, Error Domain=AVFoundationErrorDomain Code=-11800 \"The operation could not be completed\" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x282fb4570 {Error Domain=NSOSStatusErrorDomain Code=-12780 \"(null)\"}}" all the time..any one faced this issue – christijk Nov 21 '18 at 08:17
  • I don't know why. I tried it in Xcode 9, all worked fine. – Andy Jazz Nov 21 '18 at 08:23
  • I am picking video file using UIImagePickerController and creating AVURLAsset – christijk Nov 21 '18 at 08:35
  • I suppose you should ask dfi about it. Maybe he saw a similar error. – Andy Jazz Nov 21 '18 at 08:57
  • For HEVC, the profile should be set to `kVTProfileLevel_HEVC_Main_AutoLevel` from the `VideoToolbox` framework, found this comment in `AVVideoSettings.h`: `/* HEVC profiles/levels are defined in VideoToolbox/VTCompressionProperties.h, e.g. kVTProfileLevel_HEVC_Main_AutoLevel. The constants defined there can be used as the value for the key AVVideoProfileLevelKey. */` – Moshe Gottlieb Jan 07 '19 at 17:42
  • Thank you very much @MosheGottlieb, I'll fix it. – Andy Jazz Jan 07 '19 at 20:04
6

cracked this finally.

Use exportSession.fileLengthLimit = 1048576 * 10 //10 MB

10MB is hard coded number. Use according to your required bitrate.

fileLengthLimit /* Indicates the file length that the output of the session should not exceed. Depending on the content of the source asset, it is possible for the output to slightly exceed the file length limit. The length of the output file should be tested if you require that a strict limit be observed before making use of the output. See also maxDuration and timeRange. */

Kumar
  • 1,882
  • 2
  • 27
  • 44
2

Lalit Kumar's answer works perfectly. On top of his solution, I wrote an AVAsset extension so that I can easily set the bit rate, as opposed to the file size. The equation in the preferredBitrate method is my original. You could have a hard coded table instead.

extension AVAsset {
    private var preferredBitRate: Float {
        //          720p   1080p
        //  30fps:   5.0    11.3
        //  60fps:   7.5    16.9
        // 120fps:  11.3    25.3
        // 240fps:  16.9    38.0 (Mbps)
        guard let videoTrack = self.tracks(withMediaType: .video).first else {
            return .zero
        }
        let size = Float(min(videoTrack.naturalSize.width, videoTrack.naturalSize.height))
        let frameRate = videoTrack.nominalFrameRate
        return pow(1.5, log2(frameRate / 30)) * pow(size / 720, 2) * 5
    }

    private var preferredFileLength: Int64 {
        // 1 Mbit := 125000 bytes
        return Int64(self.duration.seconds * Double(self.preferredBitRate)) * 125000
    }
}
Kousuke Ariga
  • 691
  • 9
  • 10
1

If you set the fileLengthLimit value, the video bitrate value of the file generated by the exportSession changes. That's exactly what I wanted. If you want to set the video bitrate value of the AVExportSession, calculate and set the fileLengthLimit value of the AVExportSession.

Like this:

exportSession.fileLengthLimit = Int64(Double(preferredBitrate / 8) * video duration)

Thanks Lalit Kumar.