iphone video audio avassetwriter

iphone - Este código para escribir video+audio a través de AVAssetWriter y AVAssetWriterInputs no funciona. ¿Por qué?



(2)

En startVideoRecording, llamo (supongo que estás llamando esto en algún momento)

[_capSession startRunning] ;

En stopVideoRecording no llamo

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

El markAsFinished es más para usar con el método de extracción de estilo de bloque. Consulte requestMediaDataWhenReadyOnQueue: usingBlock en AVAssetWriterInput para obtener una explicación. La biblioteca debe calcular el momento adecuado para entrelazar los almacenamientos intermedios.

No necesita llamar a endSessionAtSrouceTime. La última marca de tiempo en los datos de muestra se usará después de la llamada a

[_videoWriter finishWriting];

También compruebo explícitamente el tipo de salida de captura.

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

Esto es lo que tengo. El audio y el video vienen por mí. Es posible que haya cambiado algo. Si esto no funciona para usted, entonces publicaré todo lo que tengo.

-(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"); } }

He intentado escribir un video + audio usando AVAssetWriter y AVAssetWriterInputs.

Leí varias publicaciones en este foro de personas que decían que podían lograr eso, pero que no funciona para mí. Si solo escribo video, entonces el código está haciendo su trabajo muy bien. Cuando agrego audio, el archivo de salida está dañado y no se puede reproducir.

Aquí está parte de mi código:

Configuración de AVCaptureVideoDataOutput y 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);

Configuración de AVAssetWriter y asociación de audio y video AVAssetWriterInputs a él:

- (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; }

Aquí hay funciones para iniciar / detener la grabación de video

- (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"); } }

Y finalmente el código de 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"); } } }

Estaría muy contento si alguien pudiera encontrar cuál es el problema en este código.


Primero, no use [NSNumber numberWithInt: kCVPixelFormatType_32BGRA], ya que no es el formato nativo de la cámara. use [NSNumber numberWithInt: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]

Además, siempre debe verificar antes de llamar a startWriting que aún no se está ejecutando. No necesita establecer la hora de finalización de la sesión, ya que stopWriting lo hará.