这里的目标是通过AVCaptureDataOutput捕获视频和CoreAudio录制音频来创建mp4文件。然后将两者的CMSampleBuffer发送到具有相应AVAssetWriterInput(AVMediaTypeVideo)和AVAssetWriterInput(AVMediaTypeAudio)的AVAssetWriter中。
我的音频编码器会将AudioBuffer复制到新的CMSampleBuffer,然后将其传递给AVAssetWriterInput(AVMediaTypeAudio)。以下示例演示了如何完成从AudioBuffer转换为CMSampleBuffer:转换为CMSampleBuffer 长话短说,它不起作用。视频显示出来,但没有音频。
但是,如果我注释掉视频编码,那么音频就会写入文件并且可以听到。
这告诉我有一个时间问题。 CMSampleBuffer的转换可能存在问题。
我的音频编码器会将AudioBuffer复制到新的CMSampleBuffer,然后将其传递给AVAssetWriterInput(AVMediaTypeAudio)。以下示例演示了如何完成从AudioBuffer转换为CMSampleBuffer:转换为CMSampleBuffer 长话短说,它不起作用。视频显示出来,但没有音频。
但是,如果我注释掉视频编码,那么音频就会写入文件并且可以听到。
这告诉我有一个时间问题。 CMSampleBuffer的转换可能存在问题。
CMSampleTimingInfo timing = { CMTimeMake(1, 44100.0), kCMTimeZero, kCMTimeInvalid };
它生成一个时间 CMTimeCopyDescription 的{0/1 = 0.000}
,我认为这完全是错误的。我尝试跟踪呈现的帧并将帧计数作为时间值以及采样率作为时间刻度传递,如下所示
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);
我创建的资产编写器
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
}
inTimeStamp
是与从音频单元设置的回调函数关联的采样缓冲区相关联的时间戳。它通过AURenderCallbackStruct
进行分配。我强烈推荐学习核心音频,如果你想了解更多信息。 - mylegfeelsfunnyAudioBufferList
和AudioTimeStamp
发送给我。但是如果可以的话,我会尝试自己填充时间戳并向前传递它。这样会改变时间,但如果改变始终保持一致,那么可能会起作用。请记住,我现在只是在推测。 - mylegfeelsfunnyCMSampleBuffer
的演示时间戳和从AVCaptureSession
中传入的视频的CMSampleBuffer
的时间戳确实不同,你也遇到了这个问题吗? - YYfim