Open loretoparisi opened 9 years ago
I did further tests. What it seems to leak is actually something inside
- (UIImage *)_imageFromSampleBufferHolder:(SCSampleBufferHolder *)sampleBufferHolder {
__block CMSampleBufferRef sampleBuffer = nil;
dispatch_sync(_videoQueue, ^{
sampleBuffer = sampleBufferHolder.sampleBuffer;
if (sampleBuffer != nil) {
CFRetain(sampleBuffer);
}
});
if (sampleBuffer == nil) {
return nil;
}
CVPixelBufferRef buffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:buffer];
if (_context == nil) {
_context = [CIContext contextWithOptions:nil];
}
CGImageRef cgImage = [_context createCGImage:ciImage fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(buffer), CVPixelBufferGetHeight(buffer))];
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CFRelease(sampleBuffer);
return image;
}
called by
- (UIImage *)snapshotOfLastAppendedVideoBuffer {
return [self _imageFromSampleBufferHolder:_lastAppendedVideoBuffer];
}
that I'm calling in the
-(UIImage*)captureVideoBuffer:(id)sender {
UIImage *__lastImage = [_recorder snapshotOfLastAppendedVideoBuffer];
if(!__lastImage) {
return nil;
}
in the didCompleteRecordSession
delegate
I have added a auto release pool like:
- (void)imageFromVideoBuffer:(void(^)(UIImage* image))completion {
@autoreleasepool {
CMSampleBufferRef sampleBuffer = _lastAppendedVideoBuffer.sampleBuffer;
if (sampleBuffer != nil) {
CFRetain(sampleBuffer);
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:CMSampleBufferGetImageBuffer(sampleBuffer)];
_lastAppendedVideoBuffer.sampleBuffer = nil;
if (_context == nil) {
_context = [CIContext contextWithOptions:nil];
}
CVPixelBufferRef buffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CGImageRef cgImage = [_context createCGImage:ciImage fromRect:
CGRectMake(0, 0, CVPixelBufferGetWidth(buffer), CVPixelBufferGetHeight(buffer))];
__block UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CFRelease(sampleBuffer);
[self analyzeImageBuffer:image sampleBuffer:sampleBuffer completion:^(UIImage *image) {
if(completion) completion(image);
}];
return;
}
if(completion) completion(nil);
}
}
Here for another discussion about that: http://stackoverflow.com/questions/32685756/memory-leak-in-cmsamplebuffergetimagebuffer
I confirm that calling the _imageFromSampleBufferHolder
the memory leaks the
sampleBuffer
retained from the SCSampleBufferHolder
:
__block CMSampleBufferRef sampleBuffer = nil;
dispatch_sync(_videoQueue, ^{
sampleBuffer = sampleBufferHolder.sampleBuffer;
if (sampleBuffer != nil) {
CFRetain(sampleBuffer);
}
});
The leak constantly increases the memory allocation every X seconds.
This could be due to a wrong way to restart a recording session that is ended:
- (void)recorder:(SCRecorder *)recorder didCompleteRecordSession:(SCRecordSession *)recordSession {
NSLog(@"didCompleteRecordSession %f", CMTimeGetSeconds( recordSession.segmentsDuration ));
[self captureVideoBuffer]; // this will call the _imageFromSampleBufferHolder
_recordSession = recordSession;
if (_recordSession != nil) { // cancel record session
_recorder.recordSession = nil;
[recordSession cancelSession:nil];
}
[self prepareCamera];
[self updateTimeRecordedLabel];
[_recorder record];
}
I have set the max recording time to 5 seconds:
_recorder.maxRecordDuration = CMTimeMake(5, 1);
So I did another test!
I put in the AVCapture output callback to get the sampleBuffer
.
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
if (captureOutput == _videoOutput) {
_lastVideoBuffer.sampleBuffer = sampleBuffer;
id<CIImageRenderer> imageRenderer = _CIImageRenderer;
dispatch_async(dispatch_get_main_queue(), ^{
@autoreleasepool {
CIImage *ciImage = nil;
ciImage = [CIImage imageWithCVPixelBuffer:CMSampleBufferGetImageBuffer(sampleBuffer)];
if(_context==nil) {
_context = [CIContext contextWithOptions:nil];
}
CGImageRef processedCGImage = [_context createCGImage:ciImage
fromRect:[ciImage extent]];
//UIImage *image=[UIImage imageWithCGImage:processedCGImage];
CGImageRelease(processedCGImage);
NSLog(@"Captured image %@", ciImage);
}
});
The code that leaks is the createCGImage:ciImage
:
CGImageRef processedCGImage = [_context createCGImage:ciImage
fromRect:[ciImage extent]];
I have taken this from the generateImageFromSampleBufferHolder
@loretoparisi is it possible your issue is related to this? https://forums.developer.apple.com/message/50981#50981
If it is, it seems to have been resolved in iOS9.1 beta 3
@mjgaylord that's a super finding! I will check it with the latest beta. Back with some results.
I'm trying to restart automatically a new
SCRecordSession
as soon the previous session is completed (getting thedidCompleteRecordSession
delegate). It seems that this causes a memory leak:The code is like:
The leak seamed (see latest comment) to be caused by
prepareCamera
and so due to the init of the newSCRecordSession
:I need the prepareCamera, otherwise I'm not getting the last video buffer updated:
Looking at the last implementation, I have also tried to reference the passed
SCRecordSession
object and clean it up like:But it keeps leaking. Maybe I'm wrong on how to initialize a new
SCRecordSession
automatically.