【发布时间】:2014-11-14 14:02:44
【问题描述】:
我想要做什么:
录制长达指定持续时间的音频/视频,其中生成的输出文件将添加来自外部音频文件的预定义背景音乐 - 录制后无需进一步编码/导出。
就像您使用 iPhone 的相机应用程序录制视频一样,“相机胶卷”中所有录制的视频都有背景歌曲。结束录制后不导出或加载,也不在单独的 AudioTrack 中。
我是如何做到这一点的:
通过使用AVCaptureSession,在传递 (CMSampleBufferRef) 样本缓冲区的委托方法中,我将它们推送到 AVAssetWriter 以写入文件。由于我不希望输出文件中有多个音轨,因此我无法通过单独的 AVAssetWriterInput 传递背景音乐,这意味着我必须将背景音乐添加到录音中的每个样本缓冲区 在录制时以避免在录制后合并/导出。
背景音乐是一个特定的、预定义的音频文件(格式/编解码器:m4a aac),不需要时间编辑,只需在整个录音下方添加,从开始到结尾。录音永远不会超过背景音乐文件。
在开始写入文件之前,我还准备了一个AVAssetReader,读取指定的音频文件。
一些伪代码(不包括线程):
-(void)startRecording
{
/*
Initialize writer and reader here: [...]
*/
backgroundAudioTrackOutput = [AVAssetReaderTrackOutput
assetReaderTrackOutputWithTrack:
backgroundAudioTrack
outputSettings:nil];
if([backgroundAudioReader canAddOutput:backgroundAudioTrackOutput])
[backgroundAudioReader addOutput:backgroundAudioTrackOutput];
else
NSLog(@"This doesn't happen");
[backgroundAudioReader startReading];
/* Some more code */
recording = YES;
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
if(!recording)
return;
if(videoConnection)
[self writeVideoSampleBuffer:sampleBuffer];
else if(audioConnection)
[self writeAudioSampleBuffer:sampleBuffer];
}
AVCaptureSession 已经在传输摄像头视频和麦克风音频,正在等待将BOOL recording 设置为YES。这不完全是我这样做的方式,而是一个简短的、某种程度上等效的表示。当委托方法接收到音频类型的CMSampleBufferRef 时,我调用我自己的方法writeAudioSamplebuffer:sampleBuffer。如果这要正常完成,没有我想要做的背景轨道,我会简单地输入这样的内容:[assetWriterAudioInput appendSampleBuffer:sampleBuffer]; 而不是调用我的方法。不过,在我的情况下,我需要在写入之前重叠两个缓冲区:
-(void)writeAudioSamplebuffer:(CMSampleBufferRef)recordedSampleBuffer
{
CMSampleBufferRef backgroundSampleBuffer =
[backgroundAudioTrackOutput copyNextSampleBuffer];
/* DO MAGIC HERE */
CMSampleBufferRef resultSampleBuffer =
[self overlapBuffer:recordedSampleBuffer
withBackgroundBuffer:backgroundSampleBuffer];
/* END MAGIC HERE */
[assetWriterAudioInput appendSampleBuffer:resultSampleBuffer];
}
问题:
我必须将本地文件中的增量样本缓冲区添加到传入的实时缓冲区中。我创建的名为 overlapBuffer:withBackgroundBuffer: 的方法现在并没有做太多。 我知道如何从 CMSampleBufferRef 中提取 AudioBufferList、AudioBuffer 和 mData 等,但我不确定如何将它们实际加在一起 - 然而 strong> - 我无法测试不同的方法来做到这一点,因为真正的问题发生在此之前。在 Magic 发生之前,我拥有两个 CMSampleBufferRefs,一个从麦克风接收,一个从文件读取,这就是问题所在:
从背景音乐文件接收到的样本缓冲区与我从录制会话接收到的不同。似乎对[self.backgroundAudioTrackOutput copyNextSampleBuffer]; 的调用接收了大量样本。我意识到这对某些人来说可能显而易见,但我以前从未接触过这种媒体技术水平。我现在看到,每次我从会话中收到一个 sampleBuffer 时调用 copyNextSampleBuffer 是一厢情愿的想法,但我不知道何时/何处放置它。
据我所知,录制会话在每个样本缓冲区中提供 一个 音频样本,而文件阅读器在每个样本中提供 多个 样本-缓冲。我可以以某种方式创建一个 counter 来计算每个接收到的记录样本/缓冲区,然后使用第一个 file-sampleBuffer 来提取每个样本,直到当前 file-sampleBuffer 没有更多样本“要提供”,然后调用[..copyNext..],对那个缓冲区做同样的事情?
由于我可以完全控制录音和文件的编解码器、格式等,我希望这样的解决方案不会破坏音频的“对齐”/同步。鉴于两个样本具有相同的 sampleRate,这仍然是个问题吗?
注意
我什至不确定这是否可行,但我没有直接的理由不应该这样做。 另外值得一提的是,当我尝试使用视频文件而不是音频文件并尝试不断拉取视频样本缓冲区时,它们会完美对齐。
【问题讨论】:
标签: ios objective-c avfoundation