Как создать anAudioSampleBuffer для CMSampleBufferGetFormatDescription в iOS Swift

Я работаю над сжатием видео в iOS Swift и следую этому ответу SO. Он работает нормально, пока я не изменю формат файла этого фрагмента кода на .mp4.

    let videoWriter = try! AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileType.mov)

Есть причины, по которым мне нужен вывод в формате файла .mp4. Поэтому, когда я это делаю, приложение вылетает. И выдает мне эту ошибку,

2020-04-27 18:20:52.573614+0500 BrightCaster[7847:1513728] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVAssetWriter addInput:] In order to perform passthrough to file type public.mpeg-4, please provide a format hint in the AVAssetWriterInput initializer'
*** First throw call stack:
(0x1b331d5f0 0x1b303fbcc 0x1bd53b2b0 0x102383c0c 0x102382164 0x1021897cc 0x1b6ca73bc 0x1b6caba7c 0x1b6daec94 0x1b7835080 0x1b7834d30 0x1e9d077b4 0x1b786a764 0x1b783eb68 0x1b783f070 0x1e9d468f4 0x1b783f1c0 0x1e9d468f4 0x1b9e21d9c 0x105173730 0x105181710 0x1b329b748 0x1b329661c 0x1b3295c34 0x1bd3df38c 0x1b73c822c 0x10230f8a0 0x1b311d800)
libc++abi.dylib: terminating with uncaught exception of type NSException

Поэтому я искал на SO и нашел этот вопрос, относящийся к моей проблеме. но теперь проблема в том, что когда я пытаюсь добавить его ответ в свою функцию, она выдает ошибку anAudioSampleBuffer notdefined< /эм>. Поскольку я совершенно новичок в области аудио/видео, я не могу понять, почему он дает мне это. И как это решить. Фрагмент кода из ответа, который я добавляю с помощью своей функции, приведен ниже.

    //setup audio writer
    //let formatDesc = CMSampleBufferGetFormatDescription(anAudioSampleBuffer)
    //let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil, sourceFormatHint: formatDesc)
    let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil)
    audioWriterInput.expectsMediaDataInRealTime = false
    videoWriter.add(audioWriterInput)

Комментируемая часть не работает. Будем признательны за любую помощь. Спасибо.

Вся функция преобразования следующая

func convertVideoToLowQuailtyWithInputURL(inputURL: URL, outputURL: URL, completion: @escaping (Bool , _ url: String) -> Void) {

    let videoAsset = AVURLAsset(url: inputURL as URL, options: nil)
    let videoTrack = videoAsset.tracks(withMediaType: AVMediaType.video)[0]
    let videoSize = videoTrack.naturalSize
    let videoWriterCompressionSettings = [
        AVVideoAverageBitRateKey : Int(125000)
    ]

    let videoWriterSettings:[String : AnyObject] = [
        AVVideoCodecKey : AVVideoCodecH264 as AnyObject,
        AVVideoCompressionPropertiesKey : videoWriterCompressionSettings as AnyObject,
        AVVideoWidthKey : Int(videoSize.width) as AnyObject,
        AVVideoHeightKey : Int(videoSize.height) as AnyObject
    ]

    let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaType.video, outputSettings: videoWriterSettings)
    videoWriterInput.expectsMediaDataInRealTime = true
    videoWriterInput.transform = videoTrack.preferredTransform
    let videoWriter = try! AVAssetWriter(outputURL: outputURL as URL, fileType: AVFileType.mov) // for now its converting in .mov I THINK SO.
    videoWriter.add(videoWriterInput)



    //setup video reader
    let videoReaderSettings:[String : AnyObject] = [
        kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) as AnyObject
    ]

    let videoReaderOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: videoReaderSettings)
    var videoReader: AVAssetReader!

    do{

        videoReader = try AVAssetReader(asset: videoAsset)
    }
    catch {

        print("video reader error: \(error)")
        completion(false, "")
    }
    videoReader.add(videoReaderOutput)


    //setup audio writer
    //let formatDesc = CMSampleBufferGetFormatDescription(anAudioSampleBuffer) // this is giving me error here of un initilize, which I didn't I know.
    //let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil, sourceFormatHint: formatDesc)
    let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil)
    audioWriterInput.expectsMediaDataInRealTime = false
    videoWriter.add(audioWriterInput)
    //setup audio reader
    let audioTrack = videoAsset.tracks(withMediaType: AVMediaType.audio)[0]
    let audioReaderOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: nil)
    let audioReader = try! AVAssetReader(asset: videoAsset)
    audioReader.add(audioReaderOutput)
    videoWriter.startWriting()



    //start writing from video reader
    videoReader.startReading()
    videoWriter.startSession(atSourceTime: CMTime.zero)
    let processingQueue = DispatchQueue(label: "processingQueue1")
    videoWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
        while videoWriterInput.isReadyForMoreMediaData {
            let sampleBuffer:CMSampleBuffer? = videoReaderOutput.copyNextSampleBuffer();
            if videoReader.status == .reading && sampleBuffer != nil {
                videoWriterInput.append(sampleBuffer!)
            }
            else {
                videoWriterInput.markAsFinished()
                if videoReader.status == .completed {
                    //start writing from audio reader
                    audioReader.startReading()
                    videoWriter.startSession(atSourceTime: CMTime.zero)
                    let processingQueue = DispatchQueue(label: "processingQueue2")
                    audioWriterInput.requestMediaDataWhenReady(on: processingQueue, using: {() -> Void in
                        while audioWriterInput.isReadyForMoreMediaData {
                            let sampleBuffer:CMSampleBuffer? = audioReaderOutput.copyNextSampleBuffer()
                            if audioReader.status == .reading && sampleBuffer != nil {
                                audioWriterInput.append(sampleBuffer!)
                            }
                            else {
                                audioWriterInput.markAsFinished()
                                if audioReader.status == .completed {
                                    videoWriter.finishWriting(completionHandler: {() -> Void in
                                        completion(true, "\(videoWriter.outputURL)")
                                    })
                                }
                            }
                        }
                    })
                }
            }
        }
    })
}

person Ahsan    schedule 25.04.2020    source источник
comment
Можете ли вы дать больше информации о крушении? Это происходит при создании AVAssetWriter или позже? Приводит ли сбой к регистрации каких-либо сообщений об ошибках?   -  person Rhythmic Fistman    schedule 27.04.2020
comment
Привет, я отредактировал вопрос с ошибкой сбоя.   -  person Ahsan    schedule 27.04.2020
comment
Вы настроили свой AVAssetWriterInput для приема уже сжатого ввода. Это немного необычно. Это то, что вы хотели? Если нет, вам нужно установить AVAssetWriterInput outputSettings (как в ответе, которому вы следуете). Можете ли вы показать весь код установки AVAssetWriter?   -  person Rhythmic Fistman    schedule 27.04.2020
comment
да, я хочу сжать его. вот почему я использую этот код. И поскольку я думаю, что .mp4 весит меньше, чем файл .mov, поправьте меня, если я ошибаюсь.   -  person Ahsan    schedule 27.04.2020
comment
Вам нужно показать, как вы настраиваете AVAssetWriter и AVAssetWriterInput. Я не думаю, что вы увидите существенные различия между размерами mp4 и mov.   -  person Rhythmic Fistman    schedule 27.04.2020
comment
Я добавил целый блок для кода функции преобразования.   -  person Ahsan    schedule 27.04.2020


Ответы (1)


Вы можете выводить как mp4, пропуская звук (без перекодирования), предоставляя эту подсказку формата следующим образом:

let audioTrack = videoAsset.tracks(withMediaType: AVMediaType.audio)[0]
let audioWriterInput = AVAssetWriterInput(mediaType: AVMediaType.audio, outputSettings: nil, sourceFormatHint: audioTrack.formatDescriptions[0] as! CMFormatDescription)

Обратите внимание на новую позицию определения audioTrack.

Я предполагаю, что обе реализации Apple .mov и .mp4 должны знать сжатый аудиоформат для записи файла, но я думаю, что .mov нормально выводит эту информацию после инициализации, где .mp4 нет. Может быть, это еще один AVFoundation Surprise!.

В вашем случае я видел, что было бы утомительно переделывать код для получения формата аудио из буфера первого семпла, но потом вспомнил, что формат доступен из входной аудиодорожки.

person Rhythmic Fistman    schedule 27.04.2020
comment
большое спасибо, я заменил ваш код, и он больше не падает, не могли бы вы немного пояснить, что здесь произошло. Я пытаюсь понять это. Спасибо еще раз миллион. - person Ahsan; 28.04.2020
comment
хорошо, я понял. Большое спасибо, вы очень помогли, спасибо большое. - person Ahsan; 30.04.2020