UIImageWriteToSavedPhotosAlbum вызывает EXC плохой доступ

Я разрабатываю CGImage и он отлично работает, когда программа отображает его на экране с помощью этого:

[output_view.layer performSelectorOnMainThread:@selector(setContents:) withObject: (id) image waitUntilDone:YES];

теперь, делая это, сбой приложения:

UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage: image)],nil,nil,nil);

Я вообще ничего не понимаю. Вот стек во время аварии:

#0  0x33b5db6e in memmove (Line 65)
#1  0x341ddee2 in CGAccessSessionGetBytes
#2  0x31ab4488 in alphaProviderGetBytes
#3  0x341ddf52 in CGAccessSessionGetBytes
#4  0x31abbc80 in writeOne
#5  0x31abbdae in _CGImagePluginWriteJPEG
#6  0x31ab2ddc in CGImageDestinationFinalize
#7  0x3037eda2 in imageDataFromImageWithFormatAndProperties
#8  0x3037effc in imageDataFromImageRef
#9  0x3038ea3c in __-[PLAssetsSaver _saveImage:imageData:properties:completionBlock:]_block_invoke_1
#10 0x33c32680 in _dispatch_call_block_and_release
#11 0x33c32ba0 in _dispatch_worker_thread2
#12 0x33bd7250 in _pthread_wqthread

вот метод с проблемой:

-(void)captureOutput: (AVCaptureOutput *) captureOutput didOutputSampleBuffer: (CMSampleBufferRef) sampleBuffer fromConnection: (AVCaptureConnection *) conenction{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    // Get a CMSampleBuffer's Core Video image buffer for the media data
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    // Lock the base address of the pixel buffer
    CVPixelBufferLockBaseAddress(imageBuffer, 0); 
    // Get the number of bytes per row for the pixel buffer
    UInt8 * image_data = CVPixelBufferGetBaseAddress(imageBuffer); 
    // Get the number of bytes per row for the pixel buffer
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer);
    //Removed part here which modifies the CVPixelBuffer pixels with a particular algorithm.
    [bottom_view setNeedsDisplay];
    [bottom_view performSelectorOnMainThread:@selector(setBackgroundColor:) withObject: [UIColor colorWithRed:0 green:av/255.0 blue:0 alpha:1] waitUntilDone: YES];
    // Create a device-dependent RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    // Create a Quartz image from the pixel data
    CGDataProviderDirectCallbacks providerCallbacks = { 0, GetBytePointer, ReleaseBytePointer, GetBytesAtPosition, 0 };
    CGDataProviderRef d_provider = CGDataProviderCreateDirect(image_data,pixels*4,&providerCallbacks);
    CGImageRef image = CGImageCreate (width,height,8,32,bytesPerRow,colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst,d_provider,NULL,true,kCGRenderingIntentDefault);
    //Draw image
    if (needs_photo) {
        UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage: image],nil,nil,nil);
        needs_photo = NO;
    }
    if (recording) {
        [writer_input appendSampleBuffer:sampleBuffer];
    }
    [output_view.layer performSelectorOnMainThread:@selector(setContents:) withObject: (id) image waitUntilDone:YES];
    // Unlock the pixel buffer
    CVPixelBufferUnlockBaseAddress(imageBuffer,0);
    // Free up the context and color space
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(image);
    CGDataProviderRelease(d_provider);
    [pool drain];
 }

Спасибо за любую помощь с этой проблемой.

10 ответов


вы можете попробовать этот код вместо вашего и посмотреть, работает ли он, возможно, ваш входной поток неисправен, а не операция сохранения.

#import <OpenGLES/ES1/gl.h>

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

float width = 64;
float height = 64;

GLubyte *buffer = (GLubyte *) malloc(width * height * 4);
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, (width * height * 4), NULL);

// set up for CGImage creation
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, 0);

UIImageWriteToSavedPhotosAlbum([UIImage imageWithCGImage:imageRef],nil,nil,nil);

CGColorSpaceRelease(colorSpaceRef);
CGImageRelease(imageRef);
CGDataProviderRelease(provider);

[pool drain];

так

если это работает, добавьте части исходного кода понемногу, чтобы увидеть, когда он аварийно завершает работу.


пожалуйста, попробуйте это:

UIImageWriteToSavedPhotosAlbum (image, self, @selector (imageSavedToPhotosAlbum: didFinishSavingWithError: contextInfo:), context);

Это решит вашу проблему. Если вы все еще получаете ту же проблему, пожалуйста, дайте мне знать. Я хотел бы решить вашу проблему.


Я думаю, вам нужно сохранить сохранение UIImage, пока он не будет успешно сохранен в библиотеке фотографий. UIImageWriteToSavedPhotoAlbum имеет метод делегата для уведомления о завершении сохранения изображения.


ваши данные изображения (или, что более вероятно, объект UIImage, из которого он получен) были выпущены или autoreleased слишком часто, или не удалось сохранить один или несколько раз. Никакое другое объяснение.


можете ли вы показать нам, как вы получаете оригинальный CGImageRef? Так как он врезается в memmove(), похоже, вы указываете блок данных изображения, который не полностью выделен или преждевременно освобожден. Это означает, что он может успешно сохранить около половины вашего изображения, но затем сбой на другой половине, которая может быть вне пределов.


Я думаю, это вызвано тем, что вы вызываете UIImageWriteToSavedPhotosAlbum из другого потока, чем основной поток (я думаю, вы делаете из-за "output_view.слой performSelectorOnMainThread " некоторые строки вниз.

имейте в виду, что эти функции UI-graphics не являются полностью потокобезопасными.

возможно, попробуйте переместить вызов" сохранить в альбом "в функцию" setContent " основного потока для тестирования. Если он тоже падает, я действительно не знаю, почему это происходит.


все ответы до сих пор предполагают, что авария находится в UIImageWriteToSavedPhotosAlbum(), но строка в стеке trace imageDataFromImageRef предлагает мне, что он врезается в [UIImage imageWithCGImage: image]. Возможно, вы могли бы переместить этот вызов в промежуточную переменную, чтобы узнать наверняка.


как вы вызываете следующий метод

-(void)captureOutput: (AVCaptureOutput *) captureOutput didOutputSampleBuffer: (CMSampleBufferRef) sampleBuffer fromConnection: (AVCaptureConnection *) conenction{

если вы вызвали этот метод, используя другой поток (кроме основного потока), то только вам нужно использовать NSAutoReleasePool другой мудрый он выпустит некоторые переменные, из-за этого он может произойти сбой.

и еще одно,

вы используете эту,

[output_view.layer performSelectorOnMainThread:@selector(setContents:) withObject: (id) image waitUntilDone:YES];

почему вы используете performSelector, вы можете напрямую вызвать метод, как

[output_view.layer setContents:image];

попробовать выше. вы можете решить проблема.

С уважением,

Сатья


для меня EXC_BAD_ACCESS означает, что вы используете объект, который больше не существует (я знаю, что это неправильно, но это самая причина, если я его получу). Отлаживайте все свои объекты / переменные и посмотрите, есть ли в них значения.

попробовать UIImageWriteToSavedPhotosAlbum([[UIImage imageWithCGImage: image]retain],nil,nil,nil);

включить NSZombieEnabled и запустите снова (исполняемый файл - > Аргументы - > NSZombieEnabled = YES)


в случае, если у кого-то еще возникли проблемы с этим, я обнаружил, что в моем конкретном случае это было вызвано одной из этих функций, не совсем уверен, но как только я прокомментировал их все по всему моему приложению, он начал работать

CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpaceRef);