web-dev-qa-db-fra.com

Comment libérer correctement une avcapture

J'utilise les classes de fondation AV pour capturer le flux vidéo en direct de la caméra et traiter les échantillons vidéo. Cela fonctionne bien. Cependant, j'ai des problèmes de libération correctement les instances de fondation AV (session de capture, couche d'aperçu, entrée et sortie) une fois que j'ai terminé.

Lorsque je n'ai plus besoin de la session et de tous les objets associés, j'arrête la session de capture et la libérez-la. Cela fonctionne la plupart du temps. Cependant, l'application se bloque avec un signal EXEC_BAD_Access soulevé dans un deuxième fil créé par la file d'attente d'expédition (et où les échantillons vidéo sont traités). L'accident est principalement dû à ma propre instance de classe, qui sert de délégué tampon d'échantillon et est libérée après avoir arrêté la session de capture.

Le Apple Documentation mentionne le problème: arrêt de la session de capture est une opération asynchrone. C'est-à-dire que le deuxième fil continue de traiter des échantillons vidéo et d'accéder à des instances différentes comme la session de capture ou les périphériques d'entrée et de sortie.

Alors, comment puis-je libérer correctement l'avcapture et toutes les instances associées? Y a-t-il une notification qui me dit de manière fiable que l'avcapture a terminé?

Voici mon code:

Déclarations:

AVCaptureSession* session;
AVCaptureVideoPreviewLayer* previewLayer;
UIView* view;

Configuration des instances:

AVCaptureDevice* camera = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
session = [[AVCaptureSession alloc] init];

AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice: camera error: &error];
[session addInput: input];
AVCaptureVideoDataOutput* output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
[session addOutput: output];

dispatch_queue_t queue = dispatch_queue_create("augm_reality", NULL);
[output setSampleBufferDelegate: self queue: queue];
dispatch_release(queue);

previewLayer = [[AVCaptureVideoPreviewLayer layerWithSession: session] retain];
previewLayer.frame = view.bounds;
[view.layer addSublayer: previewLayer];

[session startRunning];

Nettoyer:

[previewLayer removeFromSuperlayer];
[previewLayer release];
[session stopRunning];
[session release];
31
Codo

Voici la meilleure solution que j'ai trouvée jusqu'à présent. L'idée de base est d'utiliser le finaliseur de la file d'attente d'expédition. Lorsque la file d'attente d'expédition quitte, nous pouvons être sûrs qu'il n'y aura plus d'action dans le deuxième fil où les tampons d'échantillonnage sont traités.

static void capture_cleanup(void* p)
{
    AugmReality* ar = (AugmReality *)p; // cast to original context instance
    [ar release];  // releases capture session if dealloc is called
}

...

dispatch_queue_t queue = dispatch_queue_create("augm_reality", NULL);
dispatch_set_context(queue, self);
dispatch_set_finalizer_f(queue, capture_cleanup);
[output setSampleBufferDelegate: self queue: queue];
dispatch_release(queue);
[self retain];

...

Malheureusement, je dois maintenant arrêter explicitement la capture. Sinon, la libération de mon instance ne le libérera pas, car le deuxième thread s'incrémente et décrit également le comptoir.

Un autre problème est que ma classe est maintenant libérée de deux threads différents. Est-ce fiable ou est-ce le prochain problème provoquant des accidents?

18
Codo

J'ai posté une question très similaire dans le fichier Apple Developer Forum et obtenu une réponse d'un Apple Employee. Il dit que c'est un problème connu:

Ceci est un problème avec l'avCaptureSession/VideodataAututPut dans iOS 4.0-4.1 qui a été corrigé et apparaîtra dans une mise à jour future. Pour le moment, vous pouvez le contourner en attendant une courte période après avoir arrêté l'avcapture, par exemple. une demi-seconde avant de disposer de la session et de la production de données.

Il/elle propose le code suivant:

dispatch_after(
    dispatch_time(0, 500000000),
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), // or main queue, or your own
    ^{
        // Do your work here.
        [session release];
        // etc.
    }
);

J'aime toujours l'approche avec le finaliste de la file d'attente d'expédition mieux car ce code devine simplement lorsque le deuxième fil aurait pu être terminé.

4
Codo

Selon le courant Apple docs ( 1 ) [AVCaptureSession stopRunning] est une opération synchrone qui bloque jusqu'à ce que le récepteur soit complètement arrêté en marche. Donc, toutes ces questions ne devraient plus arriver.

3
Kiran
 -(void)deallocSession
{
[captureVideoPreviewLayer removeFromSuperlayer];
for(AVCaptureInput *input1 in session.inputs) {
    [session removeInput:input1];
}

for(AVCaptureOutput *output1 in session.outputs) {
    [session removeOutput:output1];
}
[session stopRunning];
session=nil;
outputSettings=nil;
device=nil;
input=nil;
captureVideoPreviewLayer=nil;
stillImageOutput=nil;
self.vImagePreview=nil;

}

j'ai appelé cette fonction avant de faire apparaître et de pousser toute autre vue. Cela a résolu mon problème d'avertissement de mémoire faible.

2
souvickcse

Après une allocation d'avcapture, vous pouvez utiliser:

NSNotificationCenter *notify =
[NSNotificationCenter defaultCenter];
[notify addObserver: self
            selector: @selector(onVideoError:)
            name: AVCaptureSessionRuntimeErrorNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStart:)
            name: AVCaptureSessionDidStartRunningNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStop:)
            name: AVCaptureSessionDidStopRunningNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStop:)
            name: AVCaptureSessionWasInterruptedNotification
            object: session];
[notify addObserver: self
            selector: @selector(onVideoStart:)
            name: AVCaptureSessionInterruptionEndedNotification
            object: session];

Celles-ci rappellent les méthodes pertinentes lors de la session.Staprunning, session.startrunning, etc.

Vous devez également implémenter un bloc de nettoyage non documenté:

AVCaptureInput* input = [session.inputs objectAtIndex:0];
[session removeInput:input];
AVCaptureVideoDataOutput* output = (AVCaptureVideoDataOutput*)[session.outputs objectAtIndex:0];
[session removeOutput:output];  

Ce que j'ai trouvé confus, c'est que lors de l'appel de Seesion.Soprunning, Onvideostop: s'appelle synchroneusement! Malgré l'hypothèse asynchrone d'Apple sur l'affaire.

Son travail mais s'il vous plaît laissez-moi savoir au cas où vous voyez un tour. Je préférerais travailler avec elle asynchrone.

Merci

1
VLegakis