66

AVAssetWriterとAVAssetWriterInputsを使用してビデオとオーディオを書き込もうとしています。

私は人々のこのフォーラムで彼らがそれを達成することができたと言っている複数の投稿を読みました、しかしそれは私のために働いていません。私がビデオを書くだけなら、コードは非常にうまく機能しています。オーディオを追加すると、出力ファイルが破損し、再生できなくなります。

これが私のコードの一部です:

AVCaptureVideoDataOutputと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を設定し、オーディオとビデオの両方の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;
}

ビデオ録画を開始/停止する機能は次のとおりです

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

そして最後に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");
        }
    }
}

誰かがこのコードの問題を見つけることができれば、私は非常にうれしいです。

4

2 に答える 2

24

startVideoRecording で呼び出します(ある時点でこれを呼び出していると思います)

[_capSession startRunning] ;

stopVideoRecording では呼び出しません

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

markAsFinished は、ブロック スタイルのプル メソッドでの使用に適しています。説明については、AVAssetWriterInput の requestMediaDataWhenReadyOnQueue:usingBlock を参照してください。ライブラリは、バッファをインターリーブするための適切なタイミングを計算する必要があります。

endSessionAtSrouceTime を呼び出す必要はありません。サンプル データの最後のタイム スタンプは、への呼び出し後に使用されます。

[_videoWriter finishWriting];

また、キャプチャ出力のタイプを明示的にチェックします。

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

これが私が持っているものです。オーディオとビデオが伝わってきます。何かを変えた可能性があります。これがうまくいかない場合は、私が持っているすべてを投稿します。

-(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");
         }
    }
于 2010-12-14T08:03:25.363 に答える
7

まず、[NSNumber numberWithInt:kCVPixelFormatType_32BGRA]カメラのネイティブ形式ではないため、使用しないでください。使用する[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]

また、startWriting を呼び出す前に、まだ実行されていないことを常に確認する必要があります。stopWriting が行うため、セッション終了時間を設定する必要はありません。

于 2010-12-13T20:33:49.413 に答える