使用准确的 CMTime 将 AudioBuffer 转换为 CMSampleBuffer
Posted
技术标签:
【中文标题】使用准确的 CMTime 将 AudioBuffer 转换为 CMSampleBuffer【英文标题】:Converting AudioBuffer to CMSampleBuffer with accurate CMTime 【发布时间】:2015-04-24 20:50:28 【问题描述】:这里的目标是通过视频通过 AVCaptureDataOutput 创建一个 mp4 文件,并将音频录制到一个 CoreAudio。然后将两者的 CMSampleBuffers 发送到具有随附 AVAssetWriterInput(AVMediaTypeVideo) 和 AVAssetWriterInput(AVMediaTypeAudio) 的 AVAssetWriter
我的音频编码器将 AudioBuffer 复制到一个新的 CMSampleBuffer 并将其传递给 AVAssetWriterInput(AVMediaTypeAudio)。这个例子是如何完成 AudioBuffer 到 CMSampleBuffer 的转换。 Converstion to CMSampleBuffer
长话短说,它不起作用。视频显示,但没有音频。
但是,如果我注释掉视频编码,那么音频将被写入文件并且可以听到。
根据经验告诉我这是一个时间问题。 Converstion to CMSampleBuffer 确实显示
CMSampleTimingInfo timing = CMTimeMake(1, 44100.0), kCMTimeZero, kCMTimeInvalid ;
它产生0/1 = 0.000
的时间CMTimeCopyDescription,这对我来说似乎完全错误。我尝试跟踪渲染的帧,并像这样传递时间值的帧数和时间尺度的采样率
CMSampleTimingInfo timing = CMTimeMake(1, 44100.0), CMTimeMake(self.frameCount, 44100.0), kCMTimeInvalid ;
但没有骰子。更好看的 CMSampleTimingInfo 107520/44100 = 2.438
,但文件中仍然没有音频。
视频 CMSampleBuffer 会产生类似 65792640630624/1000000000 = 65792.641, rounded
的内容。这告诉我 AVCaptureVideoOutput 的时间尺度为 10 亿,可能是纳秒。我来宾时间值是设备时间之类的东西。我找不到任何关于 AVCaptureVideoOutput 使用的信息。
谁有任何有用的指导?我是否走在正确的轨道上?
这就是转换
CMSampleBufferRef buff = malloc(sizeof(CMSampleBufferRef));
CMFormatDescriptionRef format = NULL;
self.frameCount += inNumberFrames;
CMTime presentationTime = CMTimeMake(self.frameCount, self.pcmASBD.mSampleRate);
AudiostreamBasicDescription audioFormat = self.pcmASBD;
CheckError(CMAudioFormatDescriptionCreate(kCFAllocatorDefault,
&audioFormat,
0,
NULL,
0,
NULL,
NULL,
&format),
"Could not create format from AudioStreamBasicDescription");
CMSampleTimingInfo timing = CMTimeMake(1, self.pcmASBD.mSampleRate), presentationTime, kCMTimeInvalid ;
CheckError(CMSampleBufferCreate(kCFAllocatorDefault,
NULL,
false,
NULL,
NULL,
format,
(CMItemCount)inNumberFrames,
1,
&timing,
0,
NULL,
&buff),
"Could not create CMSampleBufferRef");
CheckError(CMSampleBufferSetDataBufferFromAudioBufferList(buff,
kCFAllocatorDefault,
kCFAllocatorDefault,
0,
audioBufferList),
"Could not set data in CMSampleBufferRef");
[self.delegate didRenderAudioSampleBuffer:buff];
CFRelease(buff);
还有我创建的assetWriters
func createVideoInputWriter()->AVAssetWriterInput?
let numPixels = Int(self.size.width * self.size.height)
let bitsPerPixel:Int = 11
let bitRate = Int64(numPixels * bitsPerPixel)
let fps:Int = 30
let settings:[NSObject : AnyObject] = [
AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : self.size.width,
AVVideoHeightKey : self.size.height,
AVVideoCompressionPropertiesKey : [
AVVideoAverageBitRateKey : NSNumber(longLong: bitRate),
AVVideoMaxKeyFrameIntervalKey : NSNumber(integer: fps)
]
]
var assetWriter:AVAssetWriterInput!
if self.mainAssetWriter.canApplyOutputSettings(settings, forMediaType:AVMediaTypeVideo)
assetWriter = AVAssetWriterInput(mediaType:AVMediaTypeVideo, outputSettings:settings)
assetWriter.expectsMediaDataInRealTime = true
if self.mainAssetWriter.canAddInput(assetWriter)
self.mainAssetWriter.addInput(assetWriter)
return assetWriter;
func createAudioInputWriter()->AVAssetWriterInput?
let settings:[NSObject : AnyObject] = [
AVFormatIDKey : kAudioFormatMPEG4AAC,
AVNumberOfChannelsKey : 2,
AVSampleRateKey : 44100,
AVEncoderBitRateKey : 64000
]
var assetWriter:AVAssetWriterInput!
if self.mainAssetWriter.canApplyOutputSettings(settings, forMediaType:AVMediaTypeAudio)
assetWriter = AVAssetWriterInput(mediaType:AVMediaTypeAudio, outputSettings:settings)
assetWriter.expectsMediaDataInRealTime = true
if self.mainAssetWriter.canAddInput(assetWriter)
self.mainAssetWriter.addInput(assetWriter)
else
let error = NSError(domain:CMHDFileEncoder.Domain, code:CMHDFileEncoderErrorCode.CantAddInput.rawValue, userInfo:nil)
self.errorDelegate.hdFileEncoderError(error)
else
let error = NSError(domain:CMHDFileEncoder.Domain, code:CMHDFileEncoderErrorCode.CantApplyOutputSettings.rawValue, userInfo:nil)
self.errorDelegate.hdFileEncoderError(error)
return assetWriter
【问题讨论】:
【参考方案1】:当然,问题持续了 2 周,周五晚上发布了问题,周一早上找到了解决方案。
我遇到的研究让我走上了正确的轨道......
1000000000 时间刻度是纳秒。 但是时间值必须是设备绝对时间的纳秒。
这篇文章比我解释得更好 - mach time
我最终使用此代码来修复它
CMSampleBufferRef buff = malloc(sizeof(CMSampleBufferRef));
CMFormatDescriptionRef format = NULL;
AudioStreamBasicDescription audioFormat = self.pcmASBD;
CheckError(CMAudioFormatDescriptionCreate(kCFAllocatorDefault,
&audioFormat,
0,
NULL,
0,
NULL,
NULL,
&format),
"Could not create format from AudioStreamBasicDescription");
uint64_t time = inTimeStamp->mHostTime;
/* Convert to nanoseconds */
time *= info.numer;
time /= info.denom;
CMTime presentationTime = CMTimeMake(time, kDeviceTimeScale);
CMSampleTimingInfo timing = CMTimeMake(1, self.pcmASBD.mSampleRate), presentationTime, kCMTimeInvalid ;
CheckError(CMSampleBufferCreate(kCFAllocatorDefault,
NULL,
false,
NULL,
NULL,
format,
(CMItemCount)inNumberFrames,
1,
&timing,
0,
NULL,
&buff),
"Could not create CMSampleBufferRef");
CheckError(CMSampleBufferSetDataBufferFromAudioBufferList(buff,
kCFAllocatorDefault,
kCFAllocatorDefault,
0,
audioBufferList),
"Could not set data in CMSampleBufferRef");
【讨论】:
你好,感谢这个解决方案,你能解释一下什么是 inTimeStamp 吗?和信息? 嘿 Pablo,inTimeStamp
是与音频单元上设置的回调函数中的采样缓冲区相关联的时间戳。它通过AURenderCallbackStruct
分配。如果您想了解更多信息,我强烈推荐Learning Core Audio。
谢谢!问题是,我从流媒体服务中获取了 AudioBufferList。你知道我该怎么做吗?
副手我不确定,AURenderCallback 给我发了AudioBufferList
和AudioTimeStamp
。但是我会考虑自己填充该时间戳,然后如果可以的话将其传递。那会改变时间,但如果改变总是一致的,它就可以工作。请记住,我现在是在推测。
嗨,我知道这是一个旧线程,但我正在做和你一样的事情,但是我的你 CMSampleBuffer
的演示时间戳是根据音频和 CMSampleBuffer
的时间戳创建的来自AVCaptureSession
的视频真的不一样,你也有这个问题吗?以上是关于使用准确的 CMTime 将 AudioBuffer 转换为 CMSampleBuffer的主要内容,如果未能解决你的问题,请参考以下文章
尝试将 CMTime 存储为 Core Data 上的可转换属性
Xcode 6- SWIFT- 将 CMTime 转换为浮点数
iOS AVFoundation 如何使用 CMTime 转换每秒相机帧数以创建延时摄影?