3

此代码导致内存泄漏和应用程序崩溃:

    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()Create Rule 造成的

反过来,我们不能在 Swift 中使用 CFRelease()。我无法理解仅指向 Objective-C 规则的链接的原因。

有没有办法在 Swift 中手动释放 CMSampleBuffer?

4

2 回答 2

4

我最近通过使用自动释放池解决了类似的问题

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 就会被释放

于 2020-06-01T20:16:16.980 回答
1

这并不是一个真正的解决方案,因为似乎手动释放内存是不可能的,并且将 while 循环与assetReader 结合使用会导致在读取不安全的可变字节时不会释放内存。

该问题已通过一种解决方法解决:将音频文件转换为 CAF 格式,然后再将其暴露给 while 循环。

缺点:需要一秒钟,音频文件越长 - 花费的时间就越多。

好处:它只使用了极少的内存,这首先是问题所在。

灵感来自:https ://stackoverflow.com/users/2907715/carpsen90从音频文件中提取仪表级别中的答案

于 2019-03-05T10:11:34.220 回答