VideoFlint / VIExportSession

A AVAssetExportSession drop-in replacement with customizable audio&video settings.
MIT License
45 stars 7 forks source link

Range of values for AVVideoAverageBitRateKey #1

Closed Tulakshana closed 5 years ago

Tulakshana commented 5 years ago

Hi,

Can I ask whether I could achieve video compression by changing the value passed for AVVideoAverageBitRateKey? If yes, how to determine the range based on the properties of the original video?

Thanks!

mountainKingG commented 5 years ago

Hi, Use this code compress video, project will crash, are you the same?

Tulakshana commented 5 years ago

Hi,

I'm sorry, I didn't use this code. So I'm unable to answer your question. I have the code below working though.

static func compressFile(video: AVAsset, bitRate: Float, outputURL: URL, completion:@escaping (_ errorMsg: String?)->Void){

        var audioFinished = false
        var videoFinished = false

        //create asset reader
        var assetReader: AVAssetReader?
        do{
            assetReader = try AVAssetReader(asset: video)
        } catch{
            completion(error.localizedDescription)
            return
        }

        guard let reader = assetReader else{
            completion("Could not read asset")
            return
        }

        let videoTrack = video.tracks(withMediaType: AVMediaType.video).first!
        let audioTrack = video.tracks(withMediaType: AVMediaType.audio).first!

        let videoReaderSettings: [String:Any] =  [kCVPixelBufferPixelFormatTypeKey as String:kCVPixelFormatType_32ARGB ]

        // ADJUST BIT RATE OF VIDEO HERE

        let videoSettings:[String:Any] = [
            AVVideoCompressionPropertiesKey: [AVVideoAverageBitRateKey: bitRate],
            AVVideoCodecKey: AVVideoCodecType.h264,
            AVVideoHeightKey: videoTrack.naturalSize.height,
            AVVideoWidthKey: videoTrack.naturalSize.width
        ]

        let assetReaderVideoOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
        let assetReaderAudioOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)

        if reader.canAdd(assetReaderVideoOutput){
            reader.add(assetReaderVideoOutput)
        }else{
            completion("Couldn't add video output reader")
            return
        }

        if reader.canAdd(assetReaderAudioOutput){
            reader.add(assetReaderAudioOutput)
        }else{
            completion("Couldn't add audio output reader")
            return
        }

        let audioInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil)
        let videoInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoSettings)
        videoInput.transform = videoTrack.preferredTransform
        //we need to add samples to the video input

        let videoInputQueue = DispatchQueue(label: "videoQueue")
        let audioInputQueue = DispatchQueue(label: "audioQueue")

        var assetWriter: AVAssetWriter?
        do{
            assetWriter = try AVAssetWriter(outputURL: outputURL, fileType: AVFileType.mov)
        }catch{
            print("Composer " + #function + ": " + error.localizedDescription)
            assetWriter = nil
        }
        guard let writer = assetWriter else{
            completion("Could not write asset")
            return
        }

        writer.shouldOptimizeForNetworkUse = true
        writer.add(videoInput)
        writer.add(audioInput)

        writer.startWriting()
        reader.startReading()
        writer.startSession(atSourceTime: CMTime.zero)

        let closeWriter:()->Void = {
            if (audioFinished && videoFinished){
                assetWriter?.finishWriting(completionHandler: {
                    completion(nil)
                })
                assetReader?.cancelReading()
            }
        }

        audioInput.requestMediaDataWhenReady(on: audioInputQueue) {
            while(audioInput.isReadyForMoreMediaData){
                let sample = assetReaderAudioOutput.copyNextSampleBuffer()
                if (sample != nil){
                    audioInput.append(sample!)
                }else{
                    audioInput.markAsFinished()
                    DispatchQueue.main.async {
                        audioFinished = true
                        closeWriter()
                    }
                    break;
                }
            }
        }

        videoInput.requestMediaDataWhenReady(on: videoInputQueue) {
            while(videoInput.isReadyForMoreMediaData){
                let sample = assetReaderVideoOutput.copyNextSampleBuffer()
                if (sample != nil){
                    videoInput.append(sample!)
                }else{
                    videoInput.markAsFinished()
                    DispatchQueue.main.async {
                        videoFinished = true
                        closeWriter()
                    }
                    break;
                }
            }
        }
    }

Bit rate is achieved as shown below,

//Write video size
let numPixels = kScreenWidth * kScreenHeight
//bits per pixel
let bitsPerPixel = pow(2.0, x) // 'x' is the value you want to change for the compression
let bitRate = numPixels * bitsPerPixel

estimatedDataRate property of the video track in the AVAsset will give you the current bit rate.