2010-11-10 4 views
57

Ich habe versucht, ein Video + Audio mit AVAssetWriter und AVAssetWriterInputs zu schreiben.Dieser Code zum Schreiben von Video und Audio über AVAssetWriter und AVAssetWriterInputs funktioniert nicht. Warum?

Ich las mehrere Beiträge in diesem Forum von Leuten, die sagten, dass sie das schaffen konnten, aber es funktioniert nicht für mich. Wenn ich nur Video schreibe, macht der Code seine Arbeit sehr gut. Wenn ich Audio hinzufüge, ist die Ausgabedatei beschädigt und kann nicht reproduziert werden.

Hier ist ein Teil meines Code:

Einrichten AVCaptureVideoDataOutput und 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); 

AVAssetWriter Einrichten und Zuordnen sowohl Audio- als auch Video AVAssetWriterInputs es:

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

hier Funktionen zum Starten/Stoppen der Videoaufnahme

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

Und schließlich der CaptureOutput Code

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

würde ich mich sehr freuen, wenn jemand die in diesem Code ist das Problem gefunden.

+0

Ich habe Probleme mit meiner Audio-Einstellung mit Code sehr ähnlich wie Ihres. Meine App wird Video aufzeichnen, aber sobald ich den AVAssetWritterInput sage, habe ich für Audio appendSampleBuffer gemacht: es sagt mir 'Input-Puffer muss in einem unkomprimierten Format sein, wenn outputSettings nicht Null' ist.Sind Sie jemals auf dieses Problem gestoßen? Es fährt mich leicht nussig! – Baza207

+1

hallo kalos, ist der Audio-Eingang in Ihrem Beispiel vom Mikrofon oder der Anwendung selbst? – justicepenny

+0

@Kalos Bruder können Sie mir sagen, wie können wir videoURL verwenden. – morroko

Antwort

23

In startVideoRecording Ich nenne (ich nehme an, Sie dies irgendwann anrufen)

[_capSession startRunning] ; 

In stopVideoRecording nenne ich nicht

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

Die markAsFinished ist für die Verwendung mit dem Block Schlüpfen Methode. Siehe requestMediaDataWhenReadyOnQueue: usingBlock in AVAssetWriterInput für eine Erklärung. Die Bibliothek sollte das richtige Timing zum Verschachteln der Puffer berechnen.

Sie müssen endSessionAtSrouceTime nicht aufrufen. Der letzte Zeitstempel in den Beispieldaten wird nach dem Aufruf von

[_videoWriter finishWriting]; 

verwendet werden, ich ausdrücklich auch für die Art der Erfassung Ausgabe überprüfen.

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

Hier ist was ich habe. Das Audio und Video kommen für mich durch. Es ist möglich, dass ich etwas verändert habe. Wenn das für dich nicht funktioniert, poste ich alles, was ich habe.

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

Vielen Dank Steve, deine Hinweise waren sehr, sehr hilfreich. Jetzt Videoaufnahme hat auch Audio! – kalos

+2

@Steve und kalos können Sie korrekten Arbeitscode geben, wo es möglich ist, Audio und Video aufzunehmen? – sach

+0

Sie können AVCamDemo aus dem WWDC 2010-Beispielcode betrachten. –

6

Verwenden Sie zunächst nicht [NSNummer numberWithInt: kCVPixelFormatType_32BGRA], da dies nicht das native Format der Kamera ist. Verwenden Sie [NSNummer numberWithInt: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]

Außerdem sollten Sie immer überprüfen, bevor Sie startWriting aufrufen, dass es nicht bereits ausgeführt wird. Sie müssen die Endzeit der Sitzung nicht festlegen, da stopWriting dies erledigt.

+1

Was ist los mit kCVPixelFormatType_32BGRA? Wenn du das native Format verwendest, wirst du am ehesten mit Shadern in BGRA konvertieren, was Apple wahrscheinlich für dich tut, wenn du BGRA spezifizierst ... – jjxtra