2010-11-10 26 views
57

Tôi đã cố gắng để viết một video + âm thanh bằng cách sử dụng AVAssetWriter và AVAssetWriterInputs.Mã này để ghi video + âm thanh qua AVAssetWriter và AVAssetWriterInputs không hoạt động. Tại sao?

Tôi đọc nhiều bài viết trong diễn đàn này của những người nói rằng họ đã có thể thực hiện điều đó, nhưng nó không làm việc cho tôi. Nếu tôi chỉ viết video thì mã sẽ hoạt động rất tốt. Khi tôi thêm âm thanh, tập tin đầu ra bị hỏng và không thể sao chép được.

Dưới đây là một phần của mã của tôi:

Thiết lập AVCaptureVideoDataOutput và 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); 

Thiết lập AVAssetWriter và kết hợp cả hai AVAssetWriterInputs âm thanh và video với nó:

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

ở đây chức năng để bắt đầu/dừng quay 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"); 
    } 
} 

Và cuối cùng mã 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"); 
     } 
    } 
} 

tôi sẽ rất vui mừng nếu ai đó có thể tìm thấy đó là vấn đề trong mã này.

+0

Tôi gặp sự cố với cài đặt âm thanh của mình với mã rất giống với mã của bạn. Ứng dụng của tôi sẽ ghi lại video nhưng ngay sau khi tôi nói cho AVAssetWritterInput tôi đã tạo cho âm thanh để nối thêm SampleBuffer: nó cho tôi biết 'Bộ đệm đầu vào phải ở định dạng không nén khi outputSettings không phải là nil'.Bạn đã bao giờ gặp vấn đề này chưa? Đó là lái xe hơi hấp dẫn của tôi! – Baza207

+1

Xin chào kalos, đầu vào âm thanh trong ví dụ của bạn là từ micrô hoặc bản thân ứng dụng? – justicepenny

+0

@kalos brother, bạn có thể cho tôi biết cách chúng tôi có thể sử dụng videoURL hay không. – morroko

Trả lời

23

Trong startVideoRecording tôi gọi (tôi giả sử bạn đang gọi này tại một số điểm)

[_capSession startRunning] ; 

Trong stopVideoRecording Tôi không gọi

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

Các markAsFinished là hơn để sử dụng với khối phong cách pull phương pháp. Xem requestMediaDataWhenReadyOnQueue: usingBlock trong AVAssetWriterInput để có giải thích. Thư viện cần tính thời gian thích hợp để xen kẽ các bộ đệm.

Bạn không cần gọi endSessionAtSrouceTime. Dấu thời gian cuối cùng trong dữ liệu mẫu sẽ được sử dụng sau khi gọi tới

[_videoWriter finishWriting]; 

Tôi cũng kiểm tra rõ ràng loại đầu ra chụp.

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

Đây là những gì tôi có. Âm thanh và video đi qua cho tôi. Có thể tôi đã thay đổi một cái gì đó. Nếu điều này không làm việc cho bạn thì tôi sẽ đăng mọi thứ tôi có.

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

Cảm ơn bạn rất nhiều Steve, gợi ý của bạn rất hữu ích. Bây giờ quay video cũng có âm thanh! – kalos

+2

@Steve và kalos bạn có thể cung cấp mã làm việc chính xác để có thể ghi cả âm thanh và video không? – sach

+0

Bạn có thể muốn xem AVCamDemo từ mã mẫu WWDC 2010. –

6

Trước tiên, không sử dụng [số NSNumberWithInt: kCVPixelFormatType_32BGRA] vì nó không phải là định dạng gốc của máy ảnh. sử dụng [NSNumber numberWithInt: kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]

Ngoài ra, bạn nên luôn kiểm tra trước khi gọi bắt đầuCảm thấy rằng nó chưa chạy. Bạn không cần phải đặt thời gian kết thúc phiên, vì stopWriting sẽ làm điều đó.

+1

Có gì sai với kCVPixelFormatType_32BGRA? Nếu bạn sử dụng định dạng gốc, rất có thể bạn sẽ chuyển sang BGRA thông qua trình đổ bóng, đó có thể là những gì Apple làm cho bạn khi bạn chỉ định BGRA ... – jjxtra

Các vấn đề liên quan