62 votes

Ce code pour écrire de la vidéo et de l'audio via AVAssetWriter et AVAssetWriterInputs ne fonctionne pas. Pourquoi?

J'ai essayé d'écrire une vidéo+audio à l'aide de AVAssetWriter et AVAssetWriterInputs.

J'ai lu plusieurs messages dans ce forum des gens en disant qu'ils étaient en mesure d'accomplir cela, mais il ne fonctionne pas pour moi. Si je viens écrire de la vidéo, puis le code est fait son travail très bien. Quand j'ai ajouter de l'audio du fichier de sortie est corrompu et ne peut être reproduit.

Voici la partie de mon code:

La configuration de AVCaptureVideoDataOutput et AVCaptureAudioDataOutput:

 NSError *error = nil;

 // Setup the video input
 AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeVideo];
 // Create a device input with the device and add it to the session.
 AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
 // Setup the video output
 _videoOutput = [[AVCaptureVideoDataOutput alloc] init];
 _videoOutput.alwaysDiscardsLateVideoFrames = NO;
 _videoOutput.videoSettings =
 [NSDictionary dictionaryWithObject:
  [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];     

 // Setup the audio input
 AVCaptureDevice *audioDevice     = [AVCaptureDevice defaultDeviceWithMediaType: AVMediaTypeAudio];
 AVCaptureDeviceInput *audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error ];     
 // Setup the audio output
 _audioOutput = [[AVCaptureAudioDataOutput alloc] init];

    // Create the session
 _capSession = [[AVCaptureSession alloc] init];
 [_capSession addInput:videoInput];
 [_capSession addInput:audioInput];
 [_capSession addOutput:_videoOutput];
 [_capSession addOutput:_audioOutput];

 _capSession.sessionPreset = AVCaptureSessionPresetLow;     

 // Setup the queue
 dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
    [_videoOutput setSampleBufferDelegate:self queue:queue];
 [_audioOutput setSampleBufferDelegate:self queue:queue];
     dispatch_release(queue);

La configuration de AVAssetWriter et en les associant à la fois l'audio et la vidéo AVAssetWriterInputs:

-(BOOL) setupWriter{
 NSError *error = nil;
 _videoWriter = [[AVAssetWriter alloc] initWithURL:videoURL fileType:AVFileTypeQuickTimeMovie
                                                        error:&error];
 NSParameterAssert(_videoWriter);


 // Add video input
 NSDictionary *videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
                                               [NSNumber numberWithDouble:128.0*1024.0], AVVideoAverageBitRateKey,
                                                      nil ];

 NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                            AVVideoCodecH264, AVVideoCodecKey,
                                            [NSNumber numberWithInt:192], AVVideoWidthKey,
                                            [NSNumber numberWithInt:144], AVVideoHeightKey,
                                            videoCompressionProps, AVVideoCompressionPropertiesKey,
                                            nil];

 _videoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
                                 outputSettings:videoSettings] retain];


 NSParameterAssert(_videoWriterInput);
 _videoWriterInput.expectsMediaDataInRealTime = YES;


 // Add the audio input
 AudioChannelLayout acl;
 bzero( &acl, sizeof(acl));
 acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;


 NSDictionary* audioOutputSettings = nil;          
 // Both type of audio inputs causes output video file to be corrupted.
 if( NO ) {
      // should work from iphone 3GS on and from ipod 3rd generation
      audioOutputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                            [ NSNumber numberWithInt: kAudioFormatMPEG4AAC ], AVFormatIDKey,
                                   [ NSNumber numberWithInt: 1 ], AVNumberOfChannelsKey,
                            [ NSNumber numberWithFloat: 44100.0 ], AVSampleRateKey,
                            [ NSNumber numberWithInt: 64000 ], AVEncoderBitRateKey,
                            [ NSData dataWithBytes: &acl length: sizeof( acl ) ], AVChannelLayoutKey,
                            nil];
 } else {
      // should work on any device requires more space
      audioOutputSettings = [ NSDictionary dictionaryWithObjectsAndKeys:                       
                            [ NSNumber numberWithInt: kAudioFormatAppleLossless ], AVFormatIDKey,
                                  [ NSNumber numberWithInt: 16 ], AVEncoderBitDepthHintKey,
                            [ NSNumber numberWithFloat: 44100.0 ], AVSampleRateKey,
                            [ NSNumber numberWithInt: 1 ], AVNumberOfChannelsKey,                                      
                            [ NSData dataWithBytes: &acl length: sizeof( acl ) ], AVChannelLayoutKey,
                               nil ];
 } 

 _audioWriterInput = [[AVAssetWriterInput 
                          assetWriterInputWithMediaType: AVMediaTypeAudio 
                outputSettings: audioOutputSettings ] retain];

      _audioWriterInput.expectsMediaDataInRealTime = YES;



 // add input
 [_videoWriter addInput:_videoWriterInput];
 [_videoWriter addInput:_audioWriterInput];

 return YES;}

voici les fonctions de démarrage/arrêt de l'enregistrement vidéo

-(void) startVideoRecording
{
         if( !_isRecording )
         {
              NSLog(@"start video recording...");
              if( ![self setupWriter] )
                   return;

              _isRecording = YES;
         }
    }

-(void) stopVideoRecording
{
         if( _isRecording )
         {
              _isRecording = NO;

              [_videoWriterInput markAsFinished];
              [_videoWriter endSessionAtSourceTime:lastSampleTime];

              [_videoWriter finishWriting];

              NSLog(@"video recording stopped");
         }
    }

Et enfin le code CaptureOutput

    - (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
        fromConnection:(AVCaptureConnection *)connection
{
     if( !CMSampleBufferDataIsReady(sampleBuffer) )
     {
          NSLog( @"sample buffer is not ready. Skipping sample" );
          return;
     }


     if( _isRecording == YES )
     {
          lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
                if( _videoWriter.status != AVAssetWriterStatusWriting  )
          {
               [_videoWriter startWriting];
               [_videoWriter startSessionAtSourceTime:lastSampleTime];
          }

          if( captureOutput == _videoOutput )
               [self newVideoSample:sampleBuffer];

                /*
                // If I add audio to the video, then the output file gets corrupted and it cannot be reproduced
          else
               [self newAudioSample:sampleBuffer];
                */
     }
}

-(void) newVideoSample:(CMSampleBufferRef)sampleBuffer
{     
     if( _isRecording )
     {
          if( _videoWriter.status > AVAssetWriterStatusWriting )
          {
               NSLog(@"Warning: writer status is %d", _videoWriter.status);
               if( _videoWriter.status == AVAssetWriterStatusFailed )
                    NSLog(@"Error: %@", _videoWriter.error);
               return;
          }

          if( ![_videoWriterInput appendSampleBuffer:sampleBuffer] )
               NSLog(@"Unable to write to video input");

     }

}



-(void) newAudioSample:(CMSampleBufferRef)sampleBuffer

{     
     if( _isRecording )
     {
          if( _videoWriter.status > AVAssetWriterStatusWriting )
          {
               NSLog(@"Warning: writer status is %d", _videoWriter.status);
               if( _videoWriter.status == AVAssetWriterStatusFailed )
                    NSLog(@"Error: %@", _videoWriter.error);
               return;
          }

          if( ![_audioWriterInput appendSampleBuffer:sampleBuffer] )
               NSLog(@"Unable to write to audio input");

     }

Je serais très heureux si quelqu'un pouvait trouver qui est le problème dans ce code.

24voto

Steve McFarlin Points 2303

Dans startVideoRecording j'appelle (je suppose que vous êtes à l'appel de cette à un certain point)

[_capSession startRunning] ;

Dans stopVideoRecording je n'appelle pas

[_videoWriterInput markAsFinished];
[_videoWriter endSessionAtSourceTime:lastSampleTime];

Le markAsFinished est plus pour une utilisation avec le bloc de style de mode de traction. Voir requestMediaDataWhenReadyOnQueue:usingBlock dans AVAssetWriterInput pour une explication. La bibliothèque doit calculer le moment approprié pour l'entrelacement de la mémoire.

Vous n'avez pas besoin d'appeler endSessionAtSrouceTime. Le dernier horodatage dans l'échantillon de données sera utilisé après l'appel à

[_videoWriter finishWriting];

J'ai aussi explicitement vérifier le type de sortie de capture.

else if( captureOutput == _audioOutput) {
    [self newAudioSample:sampleBuffer]; 
}

Voici ce que j'ai. L'audio et la vidéo pour moi. Il est possible que j'ai changé quelque chose. Si cela ne fonctionne pas pour vous, alors je vais poster tout ce que j'ai.

-(void) startVideoRecording
    {
        if( !_isRecording )
         {
            NSLog(@"start video recording...");
            if( ![self setupWriter] ) {
                NSLog(@"Setup Writer Failed") ;

                return;
            }

            [_capSession startRunning] ;
            _isRecording = YES;
         }
    }

    -(void) stopVideoRecording
    {
        if( _isRecording )
         {
            _isRecording = NO;

            [_capSession stopRunning] ;

            if(![_videoWriter finishWriting]) { 
                NSLog(@"finishWriting returned NO") ;
            }
            //[_videoWriter endSessionAtSourceTime:lastSampleTime];
            //[_videoWriterInput markAsFinished];
            //[_audioWriterInput markAsFinished];

            NSLog(@"video recording stopped");
         }
    }

6voto

JeffMc Points 51

Tout d'abord, n'utilisez pas [NSNumber numberWithInt: kCVPixelFormatType_32BGRA], car ce n'est pas le format natif de l'appareil photo. use [NSNumber numberWithInt: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]

En outre, vous devez toujours vérifier avant d'appeler startWriting qu'il n'est pas déjà en cours d'exécution. Vous n'avez pas besoin de définir l'heure de fin de session, car stopWriting le fera.

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: