I'm reading video data with an AVAssetReader
and "looping" the content by reading it again and modifying the timestamps. The relevant code is:
private func readFrame() -> CMSampleBuffer? {
let buffer = output?.copyNextSampleBuffer()
guard buffer != nil else {
do {
reader?.cancelReading()
try loop()
} catch let e {
return nil
}
return readFrame()
}
// if we've looped, add previous loop times
// MEMORY LEAK IS IN HERE
if CMTimeCompare(offsetTime, .zero) > 0 {
var newTiming = [CMSampleTimingInfo(
duration: CMSampleBufferGetDuration(buffer!),
presentationTimeStamp: CMTimeAdd(CMSampleBufferGetOutputPresentationTimeStamp(buffer!), offsetTime),
decodeTimeStamp: CMTimeAdd(CMSampleBufferGetOutputDecodeTimeStamp(buffer!), offsetTime)
)]
CMSampleBufferCreateCopyWithNewTiming(allocator: kCFAllocatorDefault, sampleBuffer: buffer!, sampleTimingEntryCount: 1, sampleTimingArray: &newTiming, sampleBufferOut: &loopedBuffer)
return loopedBuffer
}
return buffer
}
private func loop() throws {
guard (reader?.status == .some(.completed)) else {
throw FileError.cannotLoop(reader?.error)
}
offsetTime = CMTimeAdd(offsetTime, assetDuration)
try createReader() // creates a new reader and calls reader.startReading()
}
Once the video finishes its first loop (that is, once offsetTime
is greater than zero), memory begins to blow up. Commenting out the code that creates a new buffer with updated timing fixes the memory leak (but that's not viable because I need the updated timing).
Presumably CMSampleBufferCreateCopyWithNewTiming
is allocating new space for each buffer, and that buffer is never cleaned up. I've read about autoreleasepool
as a potential option, but this buffer gets passed around a lot (it goes through a custom Publisher). Is there a way to remove the memory leak? I would be fine with allocating space for 1 buffer and just overwriting it again and again on each frame - is that possible?