Este código provoca una fuga de memoria y un bloqueo de la aplicación:
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)Esto se debe a copyNextSampleBuffer() y The Create Rule .
A su vez, no podemos usar CFRelease() en Swift. La razón por la cual hay un enlace a la regla de Objective-C only está más allá de mi comprensión.
¿Hay alguna forma de liberar CMSampleBuffer manualmente en Swift?
Recientemente resolví un problema similar usando un grupo de liberación automática
Intente envolver el área donde se usa sampleBuffer en un grupo de liberación automática. Algo como esto:
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) Si entiendo correctamente, una vez que se sale del alcance de autoreleasepool , se liberará el sampleBuffer
Esta no es realmente una solución, porque parece que liberar memoria manualmente es imposible y usar el ciclo while junto con assetReader da como resultado que la memoria no se libere cuando se leen bytes mutables inseguros.
El problema se resolvió con una solución alternativa: convertir el archivo de audio a formato CAF antes de exponerlo al ciclo while.
Desventaja: toma un segundo caliente, cuanto más largo es el archivo de audio, más tiempo toma.
Al revés: solo usaba una cantidad minúscula de memoria, que era el problema en primer lugar.
Inspirado en: https://stackoverflow.com/users/2907715/carpsen90 respuesta en Extraer niveles de medidor de archivo de audio