4

iOSのUnityアプリからビデオを生成しています。私はこれを行うためにAVFoundationを使用するiVidCapを使用しています。その側はすべて正常に機能しています。基本的に、ビデオはテクスチャレンダリングターゲットを使用してレンダリングされ、フレームをObj-Cプラグインに渡します。

次に、ビデオにオーディオを追加する必要があります。オーディオは、特定の時間に発生する効果音と、場合によってはバックグラウンドサウンドになります。使用されているファイルは、実際にはUnityアプリの内部のアセットです。これらを電話ストレージに書き込んでからAVCompositionを生成することもできますが、これを回避し、オーディオを浮動小数点形式のバッファーで合成することを計画していました(オーディオクリップからオーディオを取得するのは浮動小数点形式です)。後で、オンザフライのオーディオエフェクトを実行する可能性があります。

数時間後、オーディオを録音してビデオで再生することができました...しかし、途切れます。

現在、ビデオの各フレームの持続時間に方形波を生成し、それをAVAssetWriterInputに書き込んでいます。後で、実際に必要なオーディオを生成します。

大量のサンプルを1つ生成しても、吃音は発生しません。ブロックで書き込むと(大規模な配列を割り当てるよりもはるかに望ましい)、オーディオのブロックは互いにクリップしているように見えます。

グリッチ

これがなぜなのか理解できないようです。オーディオバッファのタイムスタンプが正しいことは確かですが、この部分全体が正しく行われていない可能性があります。または、ビデオをオーディオに同期させるためにいくつかのフラグが必要ですか?オーディオデータをwavに抽出した後、ウェーブエディタで問題を確認できるため、これが問題であることがわかりません。

オーディオを書くための関連コード:

- (id)init {
    self = [super init];
    
    if (self) {
        // [snip]
        
        rateDenominator = 44100;
        rateMultiplier = rateDenominator / frameRate;
        
        sample_position_ = 0;
        audio_fmt_desc_ = nil;
        int nchannels = 2;
        AudioStreamBasicDescription audioFormat;
        bzero(&audioFormat, sizeof(audioFormat));
        audioFormat.mSampleRate = 44100;
        audioFormat.mFormatID   = kAudioFormatLinearPCM;
        audioFormat.mFramesPerPacket = 1;
        audioFormat.mChannelsPerFrame = nchannels;
        int bytes_per_sample = sizeof(float);
        audioFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsAlignedHigh;
        audioFormat.mBitsPerChannel = bytes_per_sample * 8;
        audioFormat.mBytesPerPacket = bytes_per_sample * nchannels;
        audioFormat.mBytesPerFrame = bytes_per_sample * nchannels;
        
        CMAudioFormatDescriptionCreate(kCFAllocatorDefault,
                                       &audioFormat,
                                       0,
                                       NULL,
                                       0,
                                       NULL,
                                       NULL,
                                       &audio_fmt_desc_
        );
    }
    
    return self;
}

- (BOOL)beginRecordingSession {
    NSError* error = nil;
    
    isAborted = false;
    abortCode = No_Abort;
    
    // Allocate the video writer object.
    videoWriter = [[AVAssetWriter alloc] initWithURL:[self getVideoFileURLAndRemoveExisting:
                   recordingPath] fileType:AVFileTypeMPEG4 error:&error];
    
    if (error) {
        NSLog(@"Start recording error: %@", error);
    }
    
    // Configure video compression settings.
    NSDictionary* videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
                                           [NSNumber numberWithDouble:1024.0 * 1024.0], AVVideoAverageBitRateKey,
                                           [NSNumber numberWithInt:10],AVVideoMaxKeyFrameIntervalKey,
                                           nil];
    
    // Configure video settings.
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
    AVVideoCodecH264, AVVideoCodecKey,
    [NSNumber numberWithInt:frameSize.width], AVVideoWidthKey,
    [NSNumber numberWithInt:frameSize.height], AVVideoHeightKey,
    videoCompressionProps, AVVideoCompressionPropertiesKey,
    nil];
    
    // Create the video writer that is used to append video frames to the output video
    // stream being written by videoWriter.
    videoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings] retain];
    //NSParameterAssert(videoWriterInput);
    videoWriterInput.expectsMediaDataInRealTime = YES;
    
    // Configure settings for the pixel buffer adaptor.
    NSDictionary* bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];
    
    // Create the pixel buffer adaptor, used to convert the incoming video frames and
    // append them to videoWriterInput.
    avAdaptor = [[AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput sourcePixelBufferAttributes:bufferAttributes] retain];
    
    [videoWriter addInput:videoWriterInput];
    
    // <pb> Added audio input.
    sample_position_ = 0;
    AudioChannelLayout acl;
    bzero( &acl, sizeof(acl));
    acl.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
    
    NSDictionary* audioOutputSettings = nil;
    
    audioOutputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
    [ NSNumber numberWithInt: kAudioFormatMPEG4AAC ], AVFormatIDKey,
    [ NSNumber numberWithInt: 2 ], AVNumberOfChannelsKey,
    [ NSNumber numberWithFloat: 44100.0 ], AVSampleRateKey,
    [ NSNumber numberWithInt: 64000 ], AVEncoderBitRateKey,
    [ NSData dataWithBytes: &acl length: sizeof( acl ) ], AVChannelLayoutKey,
    nil];
    
    audioWriterInput = [[AVAssetWriterInput
    assetWriterInputWithMediaType: AVMediaTypeAudio
    outputSettings: audioOutputSettings ] retain];
    
    //audioWriterInput.expectsMediaDataInRealTime = YES;
    audioWriterInput.expectsMediaDataInRealTime = NO; // seems to work slightly better
    
    [videoWriter addInput:audioWriterInput];
    
    rateDenominator = 44100;
    rateMultiplier = rateDenominator / frameRate;
    
    // Add our video input stream source to the video writer and start it.
    [videoWriter startWriting];
    [videoWriter startSessionAtSourceTime:CMTimeMake(0, rateDenominator)];
    
    isRecording = true;
    return YES;
}

- (int) writeAudioBuffer:(float *)samples sampleCount:(size_t)n channelCount:(size_t)nchans {
    if (![self waitForAudioWriterReadiness]) {
        NSLog(@"WARNING: writeAudioBuffer dropped frame after wait limit reached.");
        return 0;
    }
    
    //NSLog(@"writeAudioBuffer");
    OSStatus status;
    CMBlockBufferRef bbuf = NULL;
    CMSampleBufferRef sbuf = NULL;
    
    size_t buflen = n * nchans * sizeof(float);
    // Create sample buffer for adding to the audio input.
    status = CMBlockBufferCreateWithMemoryBlock(
        kCFAllocatorDefault,
        samples,
        buflen,
        kCFAllocatorNull,
        NULL,
        0,
        buflen,
        0,
        &bbuf);
    
    if (status != noErr) {
        NSLog(@"CMBlockBufferCreateWithMemoryBlock error");
        return -1;
    }
    
    CMTime timestamp = CMTimeMake(sample_position_, 44100);
    sample_position_ += n;
    
    status = CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, bbuf, TRUE, 0, NULL, audio_fmt_desc_, 1, timestamp, NULL, &sbuf);
    if (status != noErr) {
        NSLog(@"CMSampleBufferCreate error");
        return -1;
    }
    BOOL r = [audioWriterInput appendSampleBuffer:sbuf];
    if (!r) {
        NSLog(@"appendSampleBuffer error");
    }
    CFRelease(bbuf);
    CFRelease(sbuf);
    
    return 0;
}

何が起こっているのかについて何かアイデアはありますか?

別の方法でサンプルを作成/追加する必要がありますか?

それはAAC圧縮と関係がありますか?非圧縮オーディオを使用しようとすると機能しません(スローされます)。

私の知る限り、PTSを正しく計算しています。なぜこれがオーディオチャンネルにも必要なのですか?ビデオをオーディオクロックに同期させるべきではありませんか?


アップデート

これはAACコンプレッサーで使用されるDCTのサイズであるため、1024サンプルの固定ブロックでオーディオを提供しようとしました。違いはありません。

ビデオを書く前に、一度にすべてのブロックをプッシュしてみました。動作しません。

残りのブロックにはCMSampleBufferCreateを使用し、最初のブロックにはCMAudioSampleBufferCreateWithPacketDescriptionsを使用してみました。変化なし。

そして、私はこれらの組み合わせを試しました。まだ正しくありません。


解決

次のように見えます。

audioWriterInput.expectsMediaDataInRealTime = YES;

そうでなければそれはその心を台無しにすることが不可欠です。おそらくこれは、ビデオがこのフラグで設定されたためです。また、CMBlockBufferCreateWithMemoryBlockフラグを渡してもサンプルデータはコピーしませんkCMBlockBufferAlwaysCopyDataFlag

したがって、これを使用してバッファを作成し、を使用CMBlockBufferCreateContiguousしてコピーして、オーディオデータのコピーを含むブロックバッファを確実に取得できます。そうしないと、最初に渡したメモリを参照し、混乱してしまいます。

4

2 に答える 2

2

CMBlockBufferCreateWithMemoryBlockサンプルをコピーするので使用しますが、問題ないように見えます。あなたのコードは、audioWriterInputがいつ終了したかわからなくても大丈夫ですか?

すべきではkAudioFormatFlagIsAlignedHighないkAudioFormatFlagIsPacked

于 2012-08-31T08:51:17.670 に答える
1
CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, bbuf, TRUE, 0, NULL, audio_fmt_desc_, 1, timestamp, NULL, &sbuf);

する必要があります

CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, bbuf, TRUE, 0, NULL, audio_fmt_desc_, n, timestamp, NULL, &sbuf);i made it.
于 2013-06-25T03:29:12.103 に答える