iphone - AVAssetWriter 不适用于音频

标签 iphone objective-c ios avfoundation avassetwriter

我正在尝试让音频与 iOS 应用程序的视频一起工作。视频很好。文件中未录制任何音频(我的 iPhone 扬声器正常工作。)

这是初始化设置:

session = [[AVCaptureSession alloc] init];
    menu->session = session;
    menu_open = NO;
    session.sessionPreset = AVCaptureSessionPresetMedium;
    camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    microphone = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
    menu->camera = camera;
    [session beginConfiguration];
    [camera lockForConfiguration:nil];
    if([camera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){
        camera.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
    }
    if([camera isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]){
        camera.focusMode = AVCaptureFocusModeContinuousAutoFocus;
    }
    if([camera isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]){
        camera.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
    }
    if ([camera hasTorch]) {
        if([camera isTorchModeSupported:AVCaptureTorchModeOn]){
            [camera setTorchMode:AVCaptureTorchModeOn];
        }
    }
    [camera unlockForConfiguration];
    [session commitConfiguration];
    AVCaptureDeviceInput * camera_input = [AVCaptureDeviceInput deviceInputWithDevice:camera error:nil];
    [session addInput:camera_input];
    microphone_input = [[AVCaptureDeviceInput deviceInputWithDevice:microphone error:nil] retain];
    AVCaptureVideoDataOutput * output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
    output.videoSettings = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
    [session addOutput:output];
    output.minFrameDuration = CMTimeMake(1,30);
    dispatch_queue_t queue = dispatch_queue_create("MY QUEUE", NULL);
    [output setSampleBufferDelegate:self queue:queue];
    dispatch_release(queue);
    audio_output = [[[AVCaptureAudioDataOutput alloc] init] retain];
    queue = dispatch_queue_create("MY QUEUE", NULL);
    AudioOutputBufferDelegate * special_delegate = [[[AudioOutputBufferDelegate alloc] init] autorelease];
    special_delegate->normal_delegate = self;
    [special_delegate retain];
    [audio_output setSampleBufferDelegate:special_delegate queue:queue];
    dispatch_release(queue);
    [session startRunning];

这是录音的开始和结束:

if (recording) { //Hence stop recording
    [video_button setTitle:@"Video" forState: UIControlStateNormal];
    recording = NO;
    [writer_input markAsFinished];
    [audio_writer_input markAsFinished];
    [video_writer endSessionAtSourceTime: CMTimeMakeWithSeconds([[NSDate date] timeIntervalSinceDate: start_time],30)];
    [video_writer finishWriting];
    UISaveVideoAtPathToSavedPhotosAlbum(temp_url,self,@selector(video:didFinishSavingWithError:contextInfo:),nil);
    [start_time release];
    [temp_url release];
    [av_adaptor release];
    [microphone lockForConfiguration:nil];
    [session beginConfiguration];
    [session removeInput:microphone_input];
    [session removeOutput:audio_output];
    [session commitConfiguration];
    [microphone unlockForConfiguration];
    [menu restateConfigiration];
    [vid_off play];
}else{ //Start recording
    [vid_on play];
    [microphone lockForConfiguration:nil];
    [session beginConfiguration];
    [session addInput:microphone_input];
    [session addOutput:audio_output];
    [session commitConfiguration];
    [microphone unlockForConfiguration];
    [menu restateConfigiration];
    [video_button setTitle:@"Stop" forState: UIControlStateNormal];
    recording = YES;
    NSError *error = nil;
    NSFileManager * file_manager = [[NSFileManager alloc] init];
    temp_url = [[NSString alloc] initWithFormat:@"%@/%@", NSTemporaryDirectory(), @"temp.mp4"];
    [file_manager removeItemAtPath: temp_url error:NULL];
    [file_manager release];
    video_writer = [[AVAssetWriter alloc] initWithURL: [NSURL fileURLWithPath:temp_url] fileType: AVFileTypeMPEG4 error: &error];
    NSDictionary *video_settings = [NSDictionary dictionaryWithObjectsAndKeys: AVVideoCodecH264, AVVideoCodecKey,[NSNumber numberWithInt:360], AVVideoWidthKey,[NSNumber numberWithInt:480], AVVideoHeightKey,nil];
    writer_input = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:video_settings] retain];
    AudioChannelLayout acl;
    bzero( &acl, sizeof(acl));
    acl.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
    audio_writer_input = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings: [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]] retain];
    audio_writer_input.expectsMediaDataInRealTime = YES;
    av_adaptor = [[AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput: writer_input sourcePixelBufferAttributes:NULL] retain];
    [video_writer addInput:writer_input];
    [video_writer addInput: audio_writer_input];
    [video_writer startWriting];
    [video_writer startSessionAtSourceTime: CMTimeMake(0,1)];
    start_time = [[NSDate alloc] init];
}

这是音频的委托(delegate):

@implementation AudioOutputBufferDelegate
    -(void)captureOutput: (AVCaptureOutput *) captureOutput didOutputSampleBuffer: (CMSampleBufferRef) sampleBuffer fromConnection: (AVCaptureConnection *) conenction{
        if (normal_delegate->recording) {
            CMSampleBufferSetOutputPresentationTimeStamp(sampleBuffer,CMTimeMakeWithSeconds([[NSDate date] timeIntervalSinceDate: normal_delegate->start_time],30));
            [normal_delegate->audio_writer_input appendSampleBuffer: sampleBuffer];
        }
    }
@end

视频方法无关紧要,因为它有效。 “restateConfiguration”只是整理 session 配置,否则 torch 熄灭等:

[session beginConfiguration];
    switch (quality) {
        case Low:
            session.sessionPreset = AVCaptureSessionPresetLow;
            break;
        case Medium:
            session.sessionPreset = AVCaptureSessionPreset640x480;
            break;
    }
    [session commitConfiguration];
    [camera lockForConfiguration:nil];
    if([camera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]){
        camera.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
    }
    if([camera isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]){
        camera.focusMode = AVCaptureFocusModeContinuousAutoFocus;
    }
    if([camera isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]){
        camera.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
    }
    if ([camera hasTorch]) {
        if (torch) {
            if([camera isTorchModeSupported:AVCaptureTorchModeOn]){
                [camera setTorchMode:AVCaptureTorchModeOn];
            }
        }else{
            if([camera isTorchModeSupported:AVCaptureTorchModeOff]){
                [camera setTorchMode:AVCaptureTorchModeOff];
            }
        }
    }
    [camera unlockForConfiguration];

感谢您的帮助。

最佳答案

AVAssetWriter and Audio

这可能与链接帖子中提到的问题相同。尝试注释掉这些行

[writer_input markAsFinished];
[audio_writer_input markAsFinished];
[video_writer endSessionAtSourceTime: CMTimeMakeWithSeconds([[NSDate date] timeIntervalSinceDate: start_time],30)];

编辑

我不知道你设置演示时间戳的方式是否一定是错误的。我处理这个问题的方法是使用一个在开始时设置为 0 的局部变量。然后,当我的代表收到第一个数据包时,我会执行以下操作:

if (_startTime.value == 0) {
    _startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
}

然后

[bufferWriter->writer startWriting];
[bufferWriter->writer startSessionAtSourceTime:_startTime];

您的代码看起来有效,因为您正在计算每个接收到的数据包的时间差。但是,AVFoundation 会为您计算这个,并且还会优化放置在交错容器中的时间戳。我不确定的另一件事是每个音频的 CMSampleBufferRef 都包含一个以上的数据缓冲区,其中每个数据缓冲区都有自己的 PTS。我不确定设置 PTS 是否会自动调整所有其他数据缓冲区。

我的代码与您的代码不同的地方是我对音频和视频使用了一个调度队列。在我使用的回调中(删除了一些代码)。

switch (bufferWriter->writer.status) {
    case AVAssetWriterStatusUnknown:

        if (_startTime.value == 0) {
            _startTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
        }

        [bufferWriter->writer startWriting];
        [bufferWriter->writer startSessionAtSourceTime:_startTime];

        //Break if not ready, otherwise fall through.
        if (bufferWriter->writer.status != AVAssetWriterStatusWriting) {
            break ;
        }

    case AVAssetWriterStatusWriting:
        if( captureOutput == self.captureManager.audioOutput) {
                if( !bufferWriter->audioIn.readyForMoreMediaData) { 
                    break;
                }

                @try {
                    if( ![bufferWriter->audioIn appendSampleBuffer:sampleBuffer] ) {
                        [self delegateMessage:@"Audio Writing Error" withType:ERROR];
                    }
                }
                @catch (NSException *e) {
                    NSLog(@"Audio Exception: %@", [e reason]);
                }
        }
        else if( captureOutput == self.captureManager.videoOutput ) {

            if( !bufferWriter->videoIn.readyForMoreMediaData) { 
                break;; 
            }

            @try {
                if (!frontCamera) {
                    if( ![bufferWriter->videoIn appendSampleBuffer:sampleBuffer] ) {
                        [self delegateMessage:@"Video Writing Error" withType:ERROR];
                    }
                }
                else {
                    CMTime pt = CMSampleBufferGetPresentationTimeStamp(sampleBuffer);

                    flipBuffer(sampleBuffer, pixelBuffer);

                    if( ![bufferWriter->adaptor appendPixelBuffer:pixelBuffer withPresentationTime:pt] ) {
                        [self delegateMessage:@"Video Writing Error" withType:ERROR];
                    }
                }

            }
            @catch (NSException *e) {
                NSLog(@"Video Exception Exception: %@", [e reason]);
            }
        }

        break;
    case AVAssetWriterStatusCompleted:
        return;
    case AVAssetWriterStatusFailed: 
        [self delegateMessage:@"Critical Error Writing Queues" withType:ERROR];
        bufferWriter->writer_failed = YES ;
        _broadcastError = YES;
        [self stopCapture] ;
        return;
    case AVAssetWriterStatusCancelled:
        break;
    default:
        break;
}

关于iphone - AVAssetWriter 不适用于音频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5200077/

相关文章:

iphone - 在 UITableView 上方增加 UIView 的高度

objective-c - 调整 NSView 的大小使其小于其 subview ?

ios - 如何避免崩溃 "OpenGL rendering while app in background state"

ios - 以安全格式存储游戏偏好和保存的游戏

ios - 元数据拒绝询问应用程序登录详细信息

ios - UICollectionView 中的单元格重叠

iphone - 调用宏

iphone - 如何将 2 个对象添加到 tableview 单元格中

ios - 如何在 Swift 4 中使用#keyPath()?

ios - Swift 中的 for 循环有问题