AVCaptureSession nur immer einen Rahmen für iPhone 3gs
-
25-09-2019 - |
Frage
Ich habe ein Stück Code, dass Sätze auf eine Aufnahmesitzung von der Kamera, den Rahmen mit OpenCV zu verarbeiten und dann stellen Sie die Bildeigenschaft eines UIImageView mit einem generierten UIImage aus dem Rahmen. Wenn die App gestartet wird, das Bild der Bildansicht ist gleich Null und keine Frames anzeigen, bis ich eine andere Ansicht-Controller auf den Stapel schieben und es dann aus Pop. Dann bleibt das Bild unverändert, bis ich es wieder tun. NSLog Aussagen zeigen, dass der Rückruf bei etwa der korrekten Frame-Rate genannt wird. Irgendwelche Ideen, warum es wird nicht angezeigt? Ich reduzierte die Fps den ganzen Weg bis zu 2 Bildern pro Sekunde. Ist es nicht schnell genug verarbeiten?
Hier ist der Code:
- (void)setupCaptureSession {
NSError *error = nil;
// Create the session
AVCaptureSession *session = [[AVCaptureSession alloc] init];
// Configure the session to produce lower resolution video frames, if your
// processing algorithm can cope. We'll specify medium quality for the
// chosen device.
session.sessionPreset = AVCaptureSessionPresetLow;
// Find a suitable AVCaptureDevice
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// Create a device input with the device and add it to the session.
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device
error:&error];
if (!input) {
// Handling the error appropriately.
}
[session addInput:input];
// Create a VideoDataOutput and add it to the session
AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
output.alwaysDiscardsLateVideoFrames = YES;
[session addOutput:output];
// Configure your output.
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);
// Specify the pixel format
output.videoSettings =
[NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:kCVPixelFormatType_32BGRA]
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
// If you wish to cap the frame rate to a known value, such as 15 fps, set
// minFrameDuration.
output.minFrameDuration = CMTimeMake(1, 1);
// Start the session running to start the flow of data
[session startRunning];
// Assign session to an ivar.
[self setSession:session];
}
// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
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
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// Get the pixel buffer width and height
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// Create a device-dependent RGB color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
if (!colorSpace)
{
NSLog(@"CGColorSpaceCreateDeviceRGB failure");
return nil;
}
// Get the base address of the pixel buffer
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// Get the data size for contiguous planes of the pixel buffer.
size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer);
// Create a Quartz direct-access data provider that uses data we supply
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, baseAddress, bufferSize,
NULL);
// Create a bitmap image from data supplied by our data provider
CGImageRef cgImage =
CGImageCreate(width,
height,
8,
32,
bytesPerRow,
colorSpace,
kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
provider,
NULL,
true,
kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
// Create and return an image object representing the specified Quartz image
UIImage *image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
return image;
}
// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection {
// Create a UIImage from the sample buffer data
UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
[self.delegate cameraCaptureGotFrame:image];
}
Lösung
Dies zu Threading-Versuche in Verbindung stehen könnte:
[self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:) withObject:image waitUntilDone:NO];
Andere Tipps
Das sieht wie ein Threading-Problem. Sie können nicht Ihre Ansichten in einem anderen Thread als im Hauptthread aktualisieren. In Ihrem Setup, was gut ist, die Delegatfunktion captureOutput: didOutputSampleBuffer: wird in einem sekundären Thread genannt. So können Sie die Bildansicht von dort nicht einstellen. Art Gillespies Antwort ist eine Möglichkeit, es zu lösen, wenn Sie loswerden der schlechten Zugriffsfehler erhalten.
Eine andere Möglichkeit ist es, die Probenpuffer in captureOutput zu ändern: didOutputSampleBuffer: und haben durch das Hinzufügen einer gezeigt AVCaptureVideoPreviewLayer Instanz auf Ihre Capture-Sitzung. Das ist sicherlich die bevorzugte Art und Weise, wenn Sie nur einen kleinen Teil des Bildes ändern, wie Hervorhebung etwas.
BTW:. Ihre schlechten Zugriffsfehler auftreten könnten, weil Sie das erstellte Bild im sekundären Thread nicht behalten und deshalb vor dem befreit werden cameraCaptureGotFrame ist auf dem Haupt-Thread genannt
Aktualisieren : Um richtig das Bild zu behalten, erhöhen den Referenzzähler in captureOutput: didOutputSampleBuffer: (im Sekundärgewinde) oder vermindern, in cameraCaptureGotFrame:. (im Hauptthread)
// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
// Create a UIImage from the sample buffer data
UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
// increment ref count
[image retain];
[self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:)
withObject:image waitUntilDone:NO];
}
- (void) cameraCaptureGotFrame:(UIImage*)image
{
// whatever this function does, e.g.:
imageView.image = image;
// decrement ref count
[image release];
}
Wenn Sie den Referenzzähler nicht erhöhen, wird das Bild durch den Auto-Release-Pool des zweiten Thread vor dem cameraCaptureGotFrame befreit wird: ist im Hauptthread genannt. Wenn Sie es nicht im Hauptthread verringern, werden die Bilder nie befreit und Sie laufen innerhalb weniger Sekunden von Speicher aus.
Machst du eine setNeedsDisplay auf der UIImageView nach jeder neuen Bildeigenschaft Update?
Edit:
Wo und wann aktualisieren Sie das Hintergrundbild Eigenschaft in Ihrem Bild Ansicht?