5 votes

Fondation AV : AVCaptureVideoPreviewLayer et durée des images

J'utilise la Fondation AV pour traiter les images de la caméra vidéo (iPhone 4s, iOS 6.1.2). Je configure AVCaptureSession, AVCaptureDeviceInput, AVCaptureVideoDataOutput conformément au guide de programmation de la Fondation AV. Tout fonctionne comme prévu et je suis en mesure de recevoir des images dans le fichier captureOutput:didOutputSampleBuffer:fromConnection: délégué.

J'ai également un calque de prévisualisation défini comme suit :

AVCaptureVideoPreviewLayer *videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
[videoPreviewLayer setFrame:self.view.bounds];
videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer insertSublayer:videoPreviewLayer atIndex:0];

Le fait est que je n'ai pas besoin de 30 images par seconde dans mon traitement des images et que je ne suis de toute façon pas en mesure de les traiter aussi rapidement. J'utilise donc ce code pour limiter la durée des images :

// videoOutput is AVCaptureVideoDataOutput set earlier
AVCaptureConnection *conn = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
[conn setVideoMinFrameDuration:CMTimeMake(1, 10)];
[conn setVideoMaxFrameDuration:CMTimeMake(1, 2)];

Cela fonctionne bien et limite le nombre de trames reçues par l'application captureOutput délégué.

Cependant, cela limite également le nombre d'images par seconde sur le calque de prévisualisation et la vidéo de prévisualisation devient très peu réactive.

Je comprends d'après la documentation que la durée de l'image est définie indépendamment de la connexion et que la couche de prévisualisation a en effet une connexion AVCaptureConnection différente. En vérifiant les durées d'images mixtes/maximales sur [videoPreviewLayer connection] montre qu'elle est effectivement réglée sur les valeurs par défaut (1/30 et 1/24) et qu'elle est différente des durées définies dans la connexion de la sortie AVCaptureVideoDataOutput.

Est-il donc possible de limiter la durée de l'image uniquement sur la sortie de capture d'image et de continuer à voir une durée d'image de 1/24-1/30 sur la vidéo de prévisualisation ? Comment ?

Merci.

3voto

gWiz Points 1274

Pensez-y de cette façon : Vous demandez à l'appareil de capture de limiter la durée de l'image, afin d'obtenir une meilleure exposition. C'est parfait. Vous voulez prévisualiser à une fréquence d'images plus élevée. Si vous prévisualisez à une fréquence d'images plus élevée, le dispositif de capture (l'appareil photo) n'aura PAS assez de temps pour exposer l'image, de sorte que vous obtiendrez une meilleure exposition sur les images capturées. C'est comme si vous demandiez à voir dans la prévisualisation des images différentes de celles qui ont été capturées.

Je pense que, si c'était possible, ce serait aussi une expérience négative pour l'utilisateur.

1voto

Kaelin Colclasure Points 2746

J'ai eu le même problème avec mon application Cocoa (Mac OS X). Voici comment je l'ai résolu :

Tout d'abord, veillez à traiter les images capturées dans une file d'attente distincte. Assurez-vous également que toutes les images que vous n'êtes pas prêt à traiter sont jetées ; c'est le cas par défaut, mais j'ai tout de même mis le drapeau ci-dessous juste pour documenter le fait que j'en dépende.

    videoQueue = dispatch_queue_create("com.ohmware.LabCam.videoQueue", DISPATCH_QUEUE_SERIAL);
    videoOutput = [[AVCaptureVideoDataOutput alloc] init];
    [videoOutput setAlwaysDiscardsLateVideoFrames:YES];
    [videoOutput setSampleBufferDelegate:self
                                   queue:videoQueue];
    [session addOutput:videoOutput];

Ensuite, lors du traitement des images dans le délégué, vous pouvez simplement faire dormir le thread pendant l'intervalle de temps souhaité. Les images que le délégué n'est pas réveillé pour traiter sont discrètement rejetées. J'ai implémenté la méthode optionnelle de comptage des trames abandonnées ci-dessous à titre de contrôle d'hygiène ; mon application n'a jamais enregistré d'abandon de trames à l'aide de cette technique.

- (void)captureOutput:(AVCaptureOutput *)captureOutput
  didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection;
{
    OSAtomicAdd64(1, &videoSampleBufferDropCount);
}

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection;
{
    int64_t savedSampleBufferDropCount = videoSampleBufferDropCount;
    if (savedSampleBufferDropCount && OSAtomicCompareAndSwap64(savedSampleBufferDropCount, 0, &videoSampleBufferDropCount)) {
        NSLog(@"Dropped %lld video sample buffers!!!", savedSampleBufferDropCount);
    }
    // NSLog(@"%s", __func__);
    @autoreleasepool {
        CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
        CIImage * cameraImage = [CIImage imageWithCVImageBuffer:imageBuffer];
        CIImage * faceImage = [self faceImage:cameraImage];
        dispatch_sync(dispatch_get_main_queue(), ^ {
            [_imageView setCIImage:faceImage];
        });
    }
    [NSThread sleepForTimeInterval:0.5]; // Only want ~2 frames/sec.
}

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X