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
してコピーして、オーディオデータのコピーを含むブロックバッファを確実に取得できます。そうしないと、最初に渡したメモリを参照し、混乱してしまいます。