我正在从iOS上的Unity应用程序生成视频。我使用的是iVidCap,它使用AVFoundation来完成这项工作。这一端工作得很好。本质上,视频是通过使用纹理渲染目标并将帧传递给Obj-C插件来渲染的。
现在我需要将音频添加到视频中。音频将是在特定时间发生的声音效果,也可能是一些背景声音。正在使用的文件实际上是Unity应用程序内部的资产。我可能会将它们写入手机存储器,然后生成一个AVComposition,但我的计划是避免这种情况,并将音频合成到浮点格式的缓冲区中(从音频片段中获取音频是浮点格式)。稍后我可能会做一些动态的音频效果。
几个小时后,我设法将音频录制下来,并与视频一起回放……但是它卡顿了。
目前,我只是在每一帧视频的持续时间内生成一个方波,并将其写入AVAssetWriterInput。稍后,我将生成我真正想要的音频。
如果我生成一个巨大的样本,我就不会卡顿。如果我以块的形式编写它(我更喜欢这样做,而不是分配一个巨大的数组),那么音频块似乎是相互剪切的:

我似乎弄不明白为什么会这样。我非常确定我得到的音频缓冲区的时间戳是正确的,但可能我整个部分都做错了。或者我需要一些标志来使视频与音频同步?我看不到这就是问题所在,因为我可以在将音频数据提取到wav之后在wave编辑器中看到这个问题。
写音频的相关代码:
- (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是正确的。为什么这是音频通道所必需的?视频不应该与音频时钟同步吗?
更新
我已经尝试在1024个样本的固定块中提供音频,因为这是AAC压缩器使用的DCT的大小。没什么不同。
在写任何视频之前,我已经尝试过一次推完所有的模块。不起作用。
我已经尝试对其余的块使用CMSampleBufferCreate,只对第一个块使用。没有变化。
我尝试过这些的组合。还是不太对。
解决方案
看起来是:
audioWriterInput.expectsMediaDataInRealTime = YES;是必不可少的,否则它就会搞乱自己的思维。也许这是因为视频是用这个标志设置的。此外,即使您将标志kCMBlockBufferAlwaysCopyDataFlag传递给CMBlockBufferCreateWithMemoryBlock,它也不会复制样本数据。
因此,可以使用它创建一个缓冲区,然后使用CMBlockBufferCreateContiguous进行复制,以确保您可以使用音频数据的副本获得一个块缓冲区。否则它将引用你最初传入的内存,事情会变得一团糟。
发布于 2012-08-31 16:51:18
它看起来没问题,尽管我会使用CMBlockBufferCreateWithMemoryBlock,因为它复制了示例。你的代码可以不知道audioWriterInput什么时候用完它们吗?
kAudioFormatFlagIsAlignedHigh不应该是kAudioFormatFlagIsPacked吗?
发布于 2013-06-25 11:29:12
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.https://stackoverflow.com/questions/12187124
复制相似问题