iOS: captureOutput: didOutputSampleBuffer: fromConnection NO se llama

Quiero sacar frameworks de la transmisión en vivo de AVCaptureSession y estoy usando el AVCam de Apple como un caso de testing. Aquí está el enlace a AVCam:

https://developer.apple.com/library/ios/samplecode/AVCam/Introduction/Intro.html

Encontré que captureOutput:didOutputSampleBuffer:fromConnection NO se llama y me gustaría saber por qué o qué estoy haciendo mal.

Esto es lo que he hecho:

(1) hago de AVCamViewController un delegado

 @interface AVCamViewController () <AVCaptureFileOutputRecordingDelegate, AVCaptureVideoDataOutputSampleBufferDelegate> 

(2) AVCaptureVideoDataOutput un object AVCaptureVideoDataOutput y lo AVCaptureVideoDataOutput a la session

 AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; if ([session canAddOutput:videoDataOutput]) { [session addOutput:videoDataOutput]; } 

(3) Agregué el método y la testing del delegado al registrar una cadena aleatoria para probar

 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { NSLog(@"I am called"); } 

La aplicación de testing funciona pero captureOutput: didOutputSampleBuffer: fromConnection no se llama.

(4) Leí en SO que la variable de session en AVCaptureSession *session = [[AVCaptureSession alloc] init]; siendo local en viewDidLoad es una posible razón por la que no se llama al delegado y lo convertí en una variable de instancia de la class AVCamViewController, pero no se llama.

Aquí está el método viewDidLoad con el que estoy probando (tomado de AVCam), agregué AVCaptureDataOutput hacia el final del método:

 - (void)viewDidLoad { [super viewDidLoad]; // Create the AVCaptureSession session = [[AVCaptureSession alloc] init]; [self setSession:session]; // Setup the preview view [[self previewView] setSession:session]; // Check for device authorization [self checkDeviceAuthorizationStatus]; // In general it is not safe to mutate an AVCaptureSession or any of its inputs, outputs, or connections from multiple threads at the same time. // Why not do all of this on the main queue? // -[AVCaptureSession startRunning] is a blocking call which can take a long time. We dispatch session setup to the sessionQueue so that the main queue isn't blocked (which keeps the UI responsive). dispatch_queue_t sessionQueue = dispatch_queue_create("session queue", DISPATCH_QUEUE_SERIAL); [self setSessionQueue:sessionQueue]; dispatch_async(sessionQueue, ^{ [self setBackgroundRecordingID:UIBackgroundTaskInvalid]; NSError *error = nil; AVCaptureDevice *videoDevice = [AVCamViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack]; AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error]; if (error) { NSLog(@"%@", error); } if ([session canAddInput:videoDeviceInput]) { [session addInput:videoDeviceInput]; [self setVideoDeviceInput:videoDeviceInput]; dispatch_async(dispatch_get_main_queue(), ^{ // Why are we dispatching this to the main queue? // Because AVCaptureVideoPreviewLayer is the backing layer for AVCamPreviewView and UIView can only be manipulated on main thread. // Note: As an exception to the above rule, it is not necessary to serialize video orientation changes on the AVCaptureVideoPreviewLayer's connection with other session manipulation. [[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] setVideoOrientation:(AVCaptureVideoOrientation)[self interfaceOrientation]]; }); } AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject]; AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error]; if (error) { NSLog(@"%@", error); } if ([session canAddInput:audioDeviceInput]) { [session addInput:audioDeviceInput]; } AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; if ([session canAddOutput:movieFileOutput]) { [session addOutput:movieFileOutput]; AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo]; if ([connection isVideoStabilizationSupported]) [connection setEnablesVideoStabilizationWhenAvailable:YES]; [self setMovieFileOutput:movieFileOutput]; } AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; if ([session canAddOutput:stillImageOutput]) { [stillImageOutput setOutputSettings:@{AVVideoCodecKey : AVVideoCodecJPEG}]; [session addOutput:stillImageOutput]; [self setStillImageOutput:stillImageOutput]; } AVCaptureVideoDataOutput *videoDataOutput = [[AVCaptureVideoDataOutput alloc] init]; [videoDataOutput setSampleBufferDelegate:self queue:sessionQueue]; if ([session canAddOutput:videoDataOutput]) { NSLog(@"Yes I can add it"); [session addOutput:videoDataOutput]; } }); } - (void)viewWillAppear:(BOOL)animated { dispatch_async([self sessionQueue], ^{ [self addObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:SessionRunningAndDeviceAuthorizedContext]; [self addObserver:self forKeyPath:@"stillImageOutput.capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext]; [self addObserver:self forKeyPath:@"movieFileOutput.recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:[[self videoDeviceInput] device]]; __weak AVCamViewController *weakSelf = self; [self setRuntimeErrorHandlingObserver:[[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureSessionRuntimeErrorNotification object:[self session] queue:nil usingBlock:^(NSNotification *note) { AVCamViewController *strongSelf = weakSelf; dispatch_async([strongSelf sessionQueue], ^{ // Manually restarting the session since it must have been stopped due to an error. [[strongSelf session] startRunning]; [[strongSelf recordButton] setTitle:NSLocalizedString(@"Record", @"Recording button record title") forState:UIControlStateNormal]; }); }]]; [[self session] startRunning]; }); } 

¿Puede alguien decirme por qué y sugerencias sobre cómo solucionarlo?

He hecho muchos experimentos con esto y creo que probablemente tenga la respuesta. Tengo un código similar pero diferente que está escrito desde cero en lugar de copyrse de las muestras de Apple (que son un poco viejas ahora).

Creo que es la seccion …

 AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init]; if ([session canAddOutput:movieFileOutput]) { [session addOutput:movieFileOutput]; AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo]; if ([connection isVideoStabilizationSupported]) [connection setEnablesVideoStabilizationWhenAvailable:YES]; [self setMovieFileOutput:movieFileOutput]; } 

De mis experimentos, esto es lo que causa tu problema. En mi código, cuando esto está allí captureOutput:didOutputSampleBuffer:fromConnection no se llama. Creo que el sistema de video O BIEN le ofrece una serie de búferes de muestra O que registra un file de película comprimido y optimizado para el disco, no ambos. (Al less en iOS.) Supongo que esto tiene sentido / no es sorprendente, ¡pero no lo he visto documentado en ningún lado!

Además, en un momento, parecía estar recibiendo errores y / o la callback del búfer no se producía cuando tenía el micrófono encendido. Nuevamente indocumentados, estos fueron error -11800 (error desconocido). Pero no siempre puedo reproducir eso.

Tu código se ve bien para mí y pude pensar en 10 cosas que puedes probar y probar, así que tomaré un enfoque diferente que, con suerte, solucionará indirectamente el problema. Además del hecho de que creo que AVCam está mal escrito, creo que sería mejor para usted ver un ejemplo que solo se enfoca en video en vivo en lugar de grabar video y tomar imágenes fijas. He proporcionado un ejemplo que hace eso y nada más.

 -(void)startSession { self.session = [AVCaptureSession new]; self.session.sessionPreset = AVCaptureSessionPresetMedium; AVCaptureDevice *backCamera; for (AVCaptureDevice *device in [AVCaptureDevice devices]) { if ([device hasMediaType:AVMediaTypeVideo] && device.position == AVCaptureDevicePositionBack) { backCamera = device; break; } } NSError *error = nil; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error]; if (error) { // handle error } if ([self.session canAddInput:input]) { [self.session addInput:input]; } AVCaptureVideoDataOutput *output = [AVCaptureVideoDataOutput new]; [output setSampleBufferDelegate:self queue:self.queue]; output.videoSettings = @{(id)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32BGRA)}; if ([self.session canAddOutput:output]) { [self.session addOutput:output]; } dispatch_async(self.queue, ^{ [self.session startRunning]; }); }