Этот код для записи видео+аудио через AVAssetWriter и AVAssetWriterInputs не работает. Зачем?



Я пытался написать видео + аудио с помощью 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");
}
}
}


Я был бы очень рад, если бы кто-то мог найти, в чем проблема в этом коде.

680   2  

2 ответов:

в startVideoRecording я звоню (я предполагаю, что вы называете это в какой-то момент)

[_capSession startRunning] ;

в stopVideoRecording я не звоню

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

markAsFinished больше для пользы с методом тяги типа блока. См.requestMediaDataWhenReadyOnQueue:usingBlock в AVAssetWriterInput для объяснения. Библиотека должна рассчитать правильное время для чередования буферов.

вам не нужно вызывать 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");
         }
    }

во-первых, не используйте [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], так как это не родной формат камеры. используйте [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarvideorange]

кроме того, вы всегда должны проверять перед вызовом startWriting, что он еще не запущен. Вам не нужно устанавливать время окончания сеанса, так как stopWriting сделает это.

Comments

    Ничего не найдено.