Fuite de memory dans CMSampleBufferGetImageBuffer

Je reçois un UIImage à partir d'un tampon video CMSampleBufferRef toutes les N trames video comme:

- (void)imageFromVideoBuffer:(void(^)(UIImage* image))completion { CMSampleBufferRef sampleBuffer = _myLastSampleBuffer; 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); if(completion) completion(image); return; } if(completion) completion(nil); } 

XCode et Instruments détectent une fuite de memory, mais je ne suis pas capable de m'en débarrasser. Je libère les CGImageRef et CMSampleBufferRef comme d'habitude:

 CGImageRelease(cgImage); CFRelease(sampleBuffer); 

[UPDATE] J'ai mis dans le callback de sortie AVCapture pour get le 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); } }); 

Le code qui fuit est le createCGImage:ciImage :

 CGImageRef processedCGImage = [_context createCGImage:ciImage fromRect:[ciImage extent]]; 

même avec autoreleasepool , CGImageRelease de la reference CGImage et une propriété CIContext comme instance.

Cela semble être le même problème abordé ici: Impossible de sauvegarder CIImage dans un file sur iOS sans memory leaks

[MISE À JOUR] La fuite semble due à un bug. Le problème est bien décrit dans la fuite de memory sur CIContext createCGImage à iOS 9?

Un exemple de projet montre comment reproduire cette fuite: http://www.osamu.co.jp/DataArea/VideoCameraTest.zip

Les derniers commentaires assurent que

On dirait qu'ils ont corrigé cela en 9.1b3. Si quelqu'un a besoin d'une solution de contournement qui fonctionne sur iOS 9.0.x, j'ai pu le faire fonctionner avec ceci:

dans un code de test (Swift dans ce cas):

 [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { if (error) return; __block NSSsortingng *filePath = [NSTemporaryDirectory() ssortingngByAppendingPathComponent:[NSSsortingng ssortingngWithFormat:@"ipdf_pic_%i.jpeg",(int)[NSDate date].timeIntervalSince1970]]; NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; dispatch_async(dispatch_get_main_queue(), ^ { @autoreleasepool { CIImage *enhancedImage = [CIImage imageWithData:imageData]; if (!enhancedImage) return; static CIContext *ctx = nil; if (!ctx) ctx = [CIContext contextWithOptions:nil]; CGImageRef imageRef = [ctx createCGImage:enhancedImage fromRect:enhancedImage.extent format:kCIFormatBGRA8 colorSpace:nil]; UIImage *image = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationRight]; [[NSFileManager defaultManager] createFileAtPath:filePath contents:UIImageJPEGRepresentation(image, 0.8) atsortingbutes:nil]; CGImageRelease(imageRef); } }); }]; 

et la solution de contournement pour iOS9.0 devrait être

 extension CIContext { func createCGImage_(image:CIImage, fromRect:CGRect) -> CGImage { let width = Int(fromRect.width) let height = Int(fromRect.height) let rawData = UnsafeMutablePointer<UInt8>.alloc(width * height * 4) render(image, toBitmap: rawData, rowBytes: width * 4, bounds: fromRect, format: kCIFormatRGBA8, colorSpace: CGColorSpaceCreateDeviceRGB()) let dataProvider = CGDataProviderCreateWithData(nil, rawData, height * width * 4) {info, data, size in UnsafeMutablePointer<UInt8>(data).dealloc(size)} return CGImageCreate(width, height, 8, 32, width * 4, CGColorSpaceCreateDeviceRGB(), CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue), dataProvider, nil, false, .RenderingIntentDefault)! } } 

Nous avons rencontré un problème similaire dans une application que nous avons créée, dans laquelle nous traitons chaque image pour les points-keys de fonction avec OpenCV et envoyons une image toutes les deux secondes. Après un moment de course, nous aurions un bon nombre de messages de pression de memory.

Nous avons réussi à rectifier cela en exécutant notre code de traitement dans son propre pool de publication automatique comme ça (jpegDataFromSampleBufferAndCrop fait quelque chose de similaire à ce que vous faites, avec un recadrage ajouté):

 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { @autoreleasepool { if ([self.lastFrameSentAt timeIntervalSinceNow] < -kContinuousRateInSeconds) { NSData *imageData = [self jpegDataFromSampleBufferAndCrop:sampleBuffer]; if (imageData) { [self processImageData:imageData]; } self.lastFrameSentAt = [NSDate date]; imageData = nil; } } } } 

Je peux confirmer que cette fuite de memory existe toujours sur iOS 9.2. (J'ai également posté sur le forum des développeurs Apple .)

Je reçois la même fuite de memory sur iOS 9.2. J'ai testé l'abandon de EAGLContext en utilisant MetalKit et MLKDevice. J'ai testé en utilisant différentes methods de CIContext comme drawImage, createCGImage et render mais rien ne semble fonctionner.

Il est très clair que c'est un bug depuis iOS 9. Essayez-le en téléchargeant l'exemple d'application d'Apple (voir ci-dessous), puis exécutez le même projet sur un appareil avec iOS 8.4, puis sur un appareil avec iOS 9.2 et faites attention à la jauge de memory dans Xcode.

Télécharger https://developer.apple.com/library/ios/samplecode/AVBasicVideoOutput/Introduction/Intro.html#//apple_ref/doc/uid/DTS40013109

Ajoutez ceci à l'APLEAGLView.h: 20

 @property (strong, nonatomic) CIContext* ciContext; 

Remplacer APLEAGLView.m: 118 avec ceci

 [EAGLContext setCurrentContext:_context]; _ciContext = [CIContext contextWithEAGLContext:_context]; 

Et enfin replace APLEAGLView.m: 341-343 avec ce

 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); @autoreleasepool { CIImage* sourceImage = [CIImage imageWithCVPixelBuffer:pixelBuffer]; CIFilter* filter = [CIFilter filterWithName:@"CIGaussianBlur" keysAndValues:kCIInputImageKey, sourceImage, nil]; CIImage* filteredImage = filter.outputImage; [_ciContext render:filteredImage toCVPixelBuffer:pixelBuffer]; } glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle);