Ich habe eine kleine Augmented-Reality-App, die ich entwickle, und möchte wissen, wie ich einen Screenshot dessen speichern kann, was der Benutzer sieht, indem er auf eine Schaltfläche oder einen Timer tippt.
Die App funktioniert, indem sie den Live-Kamerastrom über einer anderen UIView überlagert. Ich kann Screenshots speichern, indem ich die Ein-/Aus-Taste + die Home-Taste verwende, diese werden in der Kamerarolle gespeichert. Allerdings wird Apple die AVCaptureVideoPreviewLayer nicht rendern, selbst wenn ich das Fenster bitte, sich selbst zu speichern. Es wird ein transparentes Stück Leinwand erstellen, wo die Vorschichtschicht ist.
Wie kann eine Augmented-Reality-App ordnungsgemäß Screenshots speichern, einschließlich Transparenz und Unterviews?
// Anzeigen einer Live-Vorschau auf einer der Ansichten
-(void)startCapture
{
captureSession = [[AVCaptureSession alloc] init];
AVCaptureDevice *audioCaptureDevice = nil;
// AVCaptureDevice *audioCaptureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSArray *videoDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in videoDevices) {
if(useFrontCamera){
if (device.position == AVCaptureDevicePositionFront) {
// FRONT-KAMERA EXISTIERT
audioCaptureDevice = device;
break;
}
}else{
if (device.position == AVCaptureDevicePositionBack) {
// RÜCKSEITIGE KAMERA EXISTIERT
audioCaptureDevice = device;
break;
}
}
}
NSError *error = nil;
AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioCaptureDevice error:&error];
if (audioInput) {
[captureSession addInput:audioInput];
}
else {
// Fehler behandeln.
}
if([captureSession canAddOutput:captureOutput]){
captureOutput = [[AVCaptureVideoDataOutput alloc] init];
[captureOutput setAlwaysDiscardsLateVideoFrames:YES];
[captureOutput setSampleBufferDelegate:self queue:queue];
[captureOutput setVideoSettings:videoSettings];
dispatch_release(queue);
}else{
// Fehler behandeln
}
previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
UIView *aView = arOverlayView;
previewLayer.frame =CGRectMake(0,0, arOverlayView.frame.size.width,arOverlayView.frame.size.height); // Nehmen Sie an, dass Sie möchten, dass die Vorschichtschicht die Ansicht ausfüllt.
[aView.layer addSublayer:previewLayer];
[captureSession startRunning];
}
// Fordern Sie das gesamte Fenster auf, sich selbst in einem Grafikkontext zu zeichnen. Dieser Aufruf wird nicht rendern
// die AVCaptureVideoPreviewLayer . Sie muss durch eine UIImageView oder eine GL-basierte Ansicht ersetzt werden. // siehe folgenden Code zur Erstellung einer dynamisch aktualisierenden UIImageView -(void)saveScreenshot {
UIGraphicsBeginImageContext(appDelegate.window.bounds.size);
[appDelegate.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(screenshot, self,
@selector(image:didFinishSavingWithError:contextInfo:), nil);
}
// Callback für das Speichern des Bildes in der Kamerarolle
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error
contextInfo:(void *)contextInfo
{
// Gab es einen Fehler?
if (error != NULL)
{
// Fehlermeldung anzeigen...
NSLog(@"Speichern fehlgeschlagen");
}
else // Keine Fehler
{
NSLog(@"Speichern erfolgreich");
// Meldung anzeigen, dass das Bild erfolgreich gespeichert wurde
}
}
Hier ist der Code zur Erstellung des Bildes:
// Sie müssen Ihren ViewController als Delegate für den Kamera-Output hinzufügen, um über gepufferte Daten informiert zu werden
-(void)activateCameraFeed
{
// Dies ist der Code, der für die Erfassung des Feeds für die Bildverarbeitung zuständig ist
dispatch_queue_t queue = dispatch_queue_create("com.AugmentedRealityGlamour.ImageCaptureQueue", NULL);
captureOutput = [[AVCaptureVideoDataOutput alloc] init];
[captureOutput setAlwaysDiscardsLateVideoFrames:YES];
[captureOutput setSampleBufferDelegate:self queue:queue];
[captureOutput setVideoSettings:videoSettings];
dispatch_release(queue);
//......Audio-Feed konfigurieren, Eingänge und Ausgänge hinzufügen
}
// Callback für den Puffer
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if ( ignoreImageStream )
return;
[self performImageCaptureFrom:sampleBuffer];
}
Erstellen Sie ein UIImage:
- (void) performImageCaptureFrom:(CMSampleBufferRef)sampleBuffer
{
CVImageBufferRef imageBuffer;
if ( CMSampleBufferGetNumSamples(sampleBuffer) != 1 )
return;
if ( !CMSampleBufferIsValid(sampleBuffer) )
return;
if ( !CMSampleBufferDataIsReady(sampleBuffer) )
return;
imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
if ( CVPixelBufferGetPixelFormatType(imageBuffer) != kCVPixelFormatType_32BGRA )
return;
CVPixelBufferLockBaseAddress(imageBuffer,0);
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer);
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
CGImageRef newImage = nil;
if ( cameraDeviceSetting == CameraDeviceSetting640x480 )
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
newImage = CGBitmapContextCreateImage(newContext);
CGColorSpaceRelease( colorSpace );
CGContextRelease(newContext);
}
else
{
uint8_t *tempAddress = malloc( 640 * 4 * 480 );
memcpy( tempAddress, baseAddress, bytesPerRow * height );
baseAddress = tempAddress;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
newImage = CGBitmapContextCreateImage(newContext);
CGContextRelease(newContext);
newContext = CGBitmapContextCreate(baseAddress, 640, 480, 8, 640*4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGContextScaleCTM( newContext, (CGFloat)640/(CGFloat)width, (CGFloat)480/(CGFloat)height );
CGContextDrawImage(newContext, CGRectMake(0,0,640,480), newImage);
CGImageRelease(newImage);
newImage = CGBitmapContextCreateImage(newContext);
CGColorSpaceRelease( colorSpace );
CGContextRelease(newContext);
free( tempAddress );
}
if ( newImage != nil )
{
//für iOS5.0 mit ARC geändert
tempImage = [[UIImage alloc] initWithCGImage:newImage scale:(CGFloat)1.0 orientation:cameraImageOrientation];
CGImageRelease(newImage);
//Dieser Aufruf erzeugt die Illusion einer Vorschichtschicht, während wir aktiv Bilder wechseln, die mit dieser Methode erstellt wurden
[self performSelectorOnMainThread:@selector(newCameraImageNotification:) withObject:tempImage waitUntilDone:YES];
}
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
}
Aktualisieren Sie die Benutzeroberfläche mit einer UIView, die tatsächlich in einem Grafikkontext gerendert werden kann:
- (void) newCameraImageNotification:(UIImage*)newImage
{
if ( newImage == nil )
return;
[arOverlayView setImage:newImage];
//oder führen Sie eine fortgeschrittenere Verarbeitung des Bildes durch
}