Как вручную выпустить CMSampleBuffer

Этот код приводит к утечке памяти и падению приложения:

    var outputSamples = [Float]()

    assetReader.startReading()
    while assetReader.status == .reading {
        let trackOutput = assetReader.outputs.first!

        if let sampleBuffer = trackOutput.copyNextSampleBuffer(),
            let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) {
            let blockBufferLength = CMBlockBufferGetDataLength(blockBuffer)
            let sampleLength = CMSampleBufferGetNumSamples(sampleBuffer) * channelCount(from: assetReader)
            var data = Data(capacity: blockBufferLength)
            data.withUnsafeMutableBytes { (blockSamples: UnsafeMutablePointer<Int16>) in
                CMBlockBufferCopyDataBytes(blockBuffer, atOffset: 0, dataLength: blockBufferLength, destination: blockSamples)
                CMSampleBufferInvalidate(sampleBuffer)

                let processedSamples = process(blockSamples,
                                               ofLength: sampleLength,
                                               from: assetReader,
                                               downsampledTo: targetSampleCount)
                outputSamples += processedSamples
            }
        }
    }
    var paddedSamples = [Float](repeating: silenceDbThreshold, count: targetSampleCount)
    paddedSamples.replaceSubrange(0..<min(targetSampleCount, outputSamples.count), with: outputSamples)

Это связано с copyNextSampleBuffer() и правилом создания.

В свою очередь, мы не можем использовать CFRelease() в Swift. Причина, по которой ссылка на правило Objective-C заключается в том, что я не понимаю.

Есть ли способ освободить CMSampleBuffer вручную в Swift?

0 ответов

Недавно я решил аналогичную проблему с помощью автозапуска

Попробуйте обернуть область, где sampleBufferиспользуется в автовыпускном пуле. Что-то вроде этого:

var outputSamples = [Float]()

assetReader.startReading()
while assetReader.status == .reading {
    let trackOutput = assetReader.outputs.first!

    autoreleasepool {
        if let sampleBuffer = trackOutput.copyNextSampleBuffer(),
            let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) {
            let blockBufferLength = CMBlockBufferGetDataLength(blockBuffer)
            let sampleLength = CMSampleBufferGetNumSamples(sampleBuffer) * channelCount(from: assetReader)
            var data = Data(capacity: blockBufferLength)
            data.withUnsafeMutableBytes { (blockSamples: UnsafeMutablePointer<Int16>) in
                CMBlockBufferCopyDataBytes(blockBuffer, atOffset: 0, dataLength: blockBufferLength, destination: blockSamples)
                CMSampleBufferInvalidate(sampleBuffer)

                let processedSamples = process(blockSamples,
                                               ofLength: sampleLength,
                                               from: assetReader,
                                               downsampledTo: targetSampleCount)
                outputSamples += processedSamples
            }
        }
    }
}
var paddedSamples = [Float](repeating: silenceDbThreshold, count: targetSampleCount)
paddedSamples.replaceSubrange(0..<min(targetSampleCount, outputSamples.count), with: outputSamples)

Если я правильно понимаю, как только он выходит за рамки autoreleasepool, sampleBuffer будет выпущен

Это не совсем решение, потому что кажется, что освобождение памяти вручную невозможно, и использование цикла while в сочетании с assetReader приводит к тому, что память не освобождается при чтении небезопасных изменяемых байтов.

Проблема была решена с помощью обходного пути: преобразование аудиофайла в формат CAF перед тем, как подвергнуть его циклу while.

Недостаток: требуется горячая секунда, чем длиннее аудиофайл - тем больше времени.

Перевернутый: он использовал только незначительное количество памяти, что было проблемой в первую очередь.

Вдохновлен: Carpsen90 ответ в Извлечь уровни счетчика из аудиофайла

Другие вопросы по тегам