2014-05-18 5 views
7

Talvolta ricevo un'eccezione NSInvalidArgumentException quando avvio la registrazione di video in un viewController ma solo dopo aver scattato le foto in un controller di visualizzazione precedente. Ho provato un paio di suggerimenti da Google e così, ma ottengo ancora questo errore alla chiamata startRecordingToOutputFileURL:fileURL.AVCaptureMovieFileOutput NSInvalidArgumentException nessuna connessione attiva/abilitata

Non riesco mai a visualizzare l'errore se non visito l'altro controller di visualizzazione che scatta le foto: si verifica solo quando scatta foto e quindi passa al nuovo controller di visualizzazione che esegue la registrazione video.

Penso che ci sia un po 'di cruft lasciato indietro dalle foto, ma quando inizializzo il mio controller di visualizzazione del videoregistratore non ottengo errori nell'impostare le sessioni e quant'altro. Qualche idea su cosa sta succedendo o su come recuperarlo? Perché è un'eccezione NSInvalidArgumentException? Grazie!

Ecco il mio codice:

dispatch_async(dispatch_get_main_queue(), ^{ 

      // Try to Fix bug: 
      // http://stackoverflow.com/questions/5979962/error-while-recording-video-on-iphone-using-avfoundation 

      [self.captureSession beginConfiguration]; 

      // Ensure session is running 
      if ([self.captureSession isRunning] == NO) { 
       NSLog(@"Capture session is NOT running... Starting it now!"); 
       [self.captureSession startRunning]; 
      } 
      else { 
       NSLog(@"Capture session is ALREADY running..."); 
      } 

      NSLog(@"File URL is: %@",fileURL); 
      NSLog(@"FileOutput is: %@",self.fileOutput); 

      [self.fileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:self]; 

      // Try to Fix bug: 
      // http://stackoverflow.com/questions/5979962/error-while-recording-video-on-iphone-using-avfoundation 
      [self.captureSession commitConfiguration]; 

     }); 

Ecco il traceback errore:

2014-05-18 16:01:38.818 app[1699:60b] *** Start recording 
2014-05-18 16:01:38.820 app[1699:60b] Capture session is ALREADY running... 
2014-05-18 16:01:38.827 app[1699:60b] Capture session is ALREADY running... 
2014-05-18 16:01:38.828 app[1699:60b] File URL is: file:////var/mobile/Applications/73FFC590-05A8-4D74-82D9-EBA122B00A20/Documents/2014-05-18-16-01-38-0.mp4 
2014-05-18 16:01:38.828 app[1699:60b] FileOutput is: <AVCaptureMovieFileOutput: 0x16513b10> 
2014-05-18 16:01:38.829 app[1699:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] - no active/enabled connections.' 
*** First throw call stack: 
(0x2fe5ff0b 0x3a5f6ce7 0x2ed5751d 0xfb4b5 0x3aadfd53 0x3aadfd3f 0x3aae26c3 0x2fe2a681 0x2fe28f4d 0x2fd93769 0x2fd9354b 0x34d006d3 0x326f2891 0xe40c9 0x3aaf4ab7) 
libc++abi.dylib: terminating with uncaught exception of type NSException 

Questo è come il captureSession viene inizializzato (dal progetto OpenSource qui: https://github.com/shu223/SlowMotionVideoRecorder):

- (id)initWithPreviewView:(UIView *)previewView { 

    self = [super init]; 

    if (self) { 

     NSError *error; 

     self.captureSession = [[AVCaptureSession alloc] init]; 
     self.captureSession.sessionPreset = AVCaptureSessionPresetInputPriority; 

     AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 
     AVCaptureDeviceInput *videoIn = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error]; 

     if (error) { 
      NSLog(@"Video input creation failed"); 
      return nil; 
     } 

     if (![self.captureSession canAddInput:videoIn]) { 
      NSLog(@"Video input add-to-session failed"); 
      return nil; 
     } 
     [self.captureSession addInput:videoIn]; 


     // save the default format 
     self.defaultFormat = videoDevice.activeFormat; 
     defaultVideoMaxFrameDuration = videoDevice.activeVideoMaxFrameDuration; 


     AVCaptureDevice *audioDevice= [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; 
     AVCaptureDeviceInput *audioIn = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error]; 
     [self.captureSession addInput:audioIn]; 

     self.fileOutput = [[AVCaptureMovieFileOutput alloc] init]; 
     [self.captureSession addOutput:self.fileOutput]; 


     self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; 
     self.previewLayer.frame = previewView.bounds; 
     self.previewLayer.contentsGravity = kCAGravityResizeAspectFill; 
     self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; 
     [previewView.layer insertSublayer:self.previewLayer atIndex:0]; 

     [self.captureSession startRunning]; 
    } 
    return self; 
} 

Il mio codice utilizza questo codice di inizializzazione come questo in viewDi DLOAD:

self.captureManager = [[AVCaptureManager alloc] initWithPreviewView:self.view]; 
self.captureManager.delegate = self; 

Il codice che in realtà avvia e arresta la registrazione viene eseguita da un metodo IBAction come questo:

- (IBAction)recButtonTapped:(id)sender { 

    // REC START 
    if (self.captureManager.isRecording == NO) { 

     NSLog(@"*** Start recording"); 

     // change UI 
     [self.recBtn setImage:self.recStopImage 
        forState:UIControlStateNormal]; 
     self.fpsControl.enabled = NO; 

     // timer start 
     startTime = [[NSDate date] timeIntervalSince1970]; 
     self.timer = [NSTimer scheduledTimerWithTimeInterval:0.01 
                 target:self 
                selector:@selector(timerHandler:) 
                userInfo:nil 
                repeats:YES]; 

     [self.captureManager startRecording]; 

    } 
    // REC STOP 
    else { 

     NSLog(@"*** Stop recording"); 

     isNeededToSave = YES; 
     [self.captureManager stopRecording]; 

     [self.timer invalidate]; 
     self.timer = nil; 

     // change UI 
     [self.recBtn setImage:self.recStartImage 
        forState:UIControlStateNormal]; 
     self.fpsControl.enabled = YES; 

    } 
} 

EDIT - Sono sicuramente la chiusura della sessione nella vista Foto, qui è che il codice . Ho verificato che venga chiamato quando esco dal controller di visualizzazione foto.

 NSLog(@"RELEASE PHOTO SESSION NOW!"); 

     for(AVCaptureInput *input1 in _mySesh.inputs) { 
      [_mySesh removeInput:input1]; 
     } 

     for(AVCaptureOutput *output1 in _mySesh.outputs) { 
      [_mySesh removeOutput:output1]; 
     } 


     [_mySesh stopRunning]; 

     // Fix closing of session 
     dispatch_after(
         dispatch_time(0,500000000), 
         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 
         ^{ 
          _mySesh = nil; 
         } 

     ); 
UPDATE #####

Secondo l'unica risposta di seguito, ho cercato di 'unlink' il file prima di iniziare la registrazione. Ancora non ha funzionato.

NSURL *fileURL = [NSURL URLWithString:[@"file://" stringByAppendingString:filePath]]; 

    //NSLog(@"Beginning to record to output file..."); 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 

     // Wait for session to start 
     //[NSThread sleepForTimeInterval:1.0]; 

     dispatch_async(dispatch_get_main_queue(), ^{ 

      // Ensure session is running 
      if ([self.captureSession isRunning] == NO) { 
       NSLog(@"Capture session is NOT running... Starting it now!"); 
       [self.captureSession startRunning]; 
      } 
      else { 
       NSLog(@"Capture session is ALREADY running..."); 
      } 

      NSLog(@"File URL is: %@",fileURL); 
      NSLog(@"FileOutput is: %@",self.fileOutput); 

      // Delete the file 
      unlink([[@"file://" stringByAppendingString:filePath] UTF8String]); 

      [self.fileOutput startRecordingToOutputFileURL:fileURL recordingDelegate:self]; 

     }); 

    }); 
UPDATE

Solo per i posteri, io chiamo il 'didFinishRecordingToOutputFileAtURL' metodo delegato:

- (void)     captureOutput:(AVCaptureFileOutput *)captureOutput 
    didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL 
         fromConnections:(NSArray *)connections error:(NSError *)error 
{ 

    // Print any errors 
    if (error) { 
     NSLog(@"Error Recording Video! %@",error.localizedDescription); 
    } 

    _isRecording = NO; 

    if ([self.delegate respondsToSelector:@selector(didFinishRecordingToOutputFileAtURL:error:)]) { 
     [self.delegate didFinishRecordingToOutputFileAtURL:outputFileURL error:error]; 
    } 

} 
+0

Il tuo 'beginConfiguration' e' commitConfiguration' sono inutili perché _non stai facendo nessuna configurazione_. In effetti, l'intera domanda, che non stai affrontando nel codice che hai mostrato, è il modo in cui 'self.captureSession' è configurato. Quando è stato fatto e cosa è stato fatto? – matt

+0

Grazie per il commento: ho aggiunto ulteriori informazioni alla domanda. – PhilBot

+0

Sei sicuro di distruggere correttamente le sessioni quando hai finito con loro? iOS non supporta più versioni di AVCaptureSession. Forse la sessione per l'acquisizione di foto non è stata rilasciata. Quando si crea un'altra sessione, non è possibile creare connessioni perché le porte di input sono trattenute da un'altra istanza di sessione. O controllalo tu stesso o mostraci il codice per l'acquisizione di foto e video con allocazione e deallocazione delle sessioni. – creker

risposta

2

Da quello che vedo questo è dovuto al file già esistente.

prova a rimuovere il file prima della chiamata a startRecordingToOutputFileURL: con: [[NSFileManager defaultManager] removeItemAtPath:fileURL]; è possibile fare doppio verificare con: [[NSFileManager defaultManager] fileExistsAtPath:fileURL];

If a file at the given URL already exists when capturing starts, recording to the new file will fail.

Un'altra cosa che potrebbe causare l'incidente è se non si ha la metodo delegato implementato.

Si è tenuto ad attuare: captureOutput:didFinishRecordingToOutputFileAtURL:fromConnections:error:

perché questo è l'unico posto affidabile per ottenere il risultato.

Un'altra cosa sospetta:

Quando si crea fileURL, lo fai

NSURL *fileURL = [NSURL URLWithString:[@"file://" stringByAppendingString:filePath]]; 

Puoi cambiare la situazione a

NSURL *fileURL = [NSURL fileURLWithPath:filePath]; 

E assicurarsi filePath è valido.

Dalla documentazione: This method throws an NSInvalidArgumentException if the URL is not a valid file URL.

+0

Ho provato questo ma non ha ancora risolto l'errore. Grazie per i suggerimenti, puoi pensare ad altro? Ho pubblicato il codice di cancellazione come modifica alla mia domanda. – PhilBot

+0

@PhilBot Puoi ricontrollare che sia stato eliminato correttamente? Dal tuo log di output, 'filePath' ha già' file: // ', ma lo hai anteposto nuovamente. Potresti controllare l'esistenza del file con 'BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath: filePath];'? Voglio solo essere sicuro! – Jack

+0

@PhilBot Se ciò non funziona, si dispone del file 'captureOutput: didFinishRecordingToOutputFileAtURL: fromConnections: error:' delegate? Credo che potrebbe bloccarsi se non si dispone di tale metodo delegato, è necessario. – Jack

4

Prima di tutto, avviare AVCaptureSession prima. Successivamente è possibile creare AVPreviewLayer e AVCaptureMovieFileOutput.

In secondo luogo, utilizzare -[AVCaptureSession canAddOutput:] e -[AVCaptureSession canAddInput:] prima di aggiungere qualsiasi elemento alla sessione di acquisizione, in questo modo si risparmia un sacco di tempo e frustrazione.

In terzo luogo, è necessario solo beginConfiguration e commitConfiguration quando si desidera modificare contemporaneamente molte cose in captureSession, ad es. rimuovi input, cambia preset. È utile per l'interruttore della fotocamera anteriore/posteriore, se hai intenzione di implementarlo. L'avvio o l'interruzione della sessione tra queste chiamate non è bueno.

0

Prova a commentare self.captureSession.sessionPreset = ... linea e vedere se aiuta. Ha fatto il trucco per me. Il motivo del problema era che ho cercato di catturare foto con la massima qualità e il FOV, ma con questa impostazione ottenevo l'eccezione "nessuna connessione attiva/abilitata" quando ho provato a catturare un video.

+0

Conosci un modo per registrare video senza impostare un preset? A volte è necessario impostare direttamente il activeFormat, come suggerisce Apple. –