ここでの目標は、AVCaptureDataOutput を介してビデオを介して mp4 ファイルを作成し、CoreAudio に記録されたオーディオを作成することです。次に、両方の CMSampleBuffers を、付随する AVAssetWriterInput(AVMediaTypeVideo) と AVAssetWriterInput(AVMediaTypeAudio) を持つ AVAssetWriter に送信します。
私のオーディオ エンコーダーは、AudioBuffer を新しい CMSampleBuffer にコピーし、それを AVAssetWriterInput(AVMediaTypeAudio) に渡します。この例は、AudioBuffer から CMSampleBuffer への変換がどのように行われるかを示しています。CMSampleBuffer への変換
要するに、それは機能しません。ビデオは表示されますが、音声は表示されません。
しかし、ビデオ エンコーディングをコメント アウトすると、オーディオがファイルに書き込まれ、聞こえるようになります。
これは、経験から、タイミングの問題であることがわかります。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
}