如何附加到录制的 MPEG4 AAC 文件?

Posted

技术标签:

【中文标题】如何附加到录制的 MPEG4 AAC 文件?【英文标题】:How can I append to a recorded MPEG4 AAC file? 【发布时间】:2011-03-01 11:50:52 【问题描述】:

我正在 iPhone 上录制音频,使用 AVAudioRecorder 并进行以下设置:

NSMutableDictionary *recordSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
       [NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
       [NSNumber numberWithFloat:44100.0], AVSampleRateKey,
       [NSNumber numberWithInt:1], AVNumberOfChannelsKey,
       [NSNumber numberWithInt:12800], AVEncoderBitRateKey,
       [NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
       [NSNumber numberWithInt: AVAudioQualityHigh],  AVEncoderAudioQualityKey,
       nil];

(我可以灵活使用这些设置中的大多数,但我必须使用 MPEG4 AAC。)

我将音频保存到一个文件中。

用户需要能够在以后返回并继续录制到同一文件。似乎没有直接使用AVAudioRecorder 执行此操作的选项,因此我正在录制到一个新文件并将它们连接起来。

目前我正在使用AVMutableCompositionAVMutableCompositionTrack 作为here 附加文件,但是对于较长时间的录制来说真的很慢,所以这并不可行。

我想如果我可以从第二个文件中剥离标题,将音频数据附加到第一个文件,然后更改组合文件的标题以反映新的持续时间,那会更快。我知道这两个文件都是使用完全相同的设置创建的,我认为标题中的其他细节应该是相同的。

很遗憾,我找不到任何有关标题格式的信息,或者是否可以以这种方式组合文件。

所以我的问题是:

在 iPhone 上创建的 MPEG-4 AAC 文件头的格式是什么? 我可以通过像这样弄乱标题来合并两个音频文件吗? 有没有更好的方法可以几乎瞬间附加两个 MPEG-4 AAC 音频文件?

【问题讨论】:

您绝对需要使用 MPEG4 吗? WAV呢?还是在两者之间转换? 我不知道 teedyay,但我使用 MPEG4 是因为文件压缩。 WAV 太大,两者之间的转换与导出 AVMutableComposition 一样会占用过多的时间。 是的,尺寸肯定是其中的一部分。我们还需要通过网络将生成的音频文件发送到我们无法控制的服务:他们需要接收 AAC。 【参考方案1】:

虽然我们要求AVAudioRecorder 以 MPEG4-AAC 格式录制,但它始终会生成一个 .caf(核心音频格式)文件。然而,这只是一种包装格式,它包含的实际音频数据是 AAC 格式。

最后,附加文件归结为逐字节操作 .caf 文件。核心音频格式文件的规范是here。消化这份文档并相应地处理文件一开始有点令人反感,但事实证明规范非常清晰和完整,因此并不太繁重。

正如规范所解释的,.caf 文件由开头有四字节名称的块组成。对于 AAC 文件,总是有一个 desc 块和一个 kuki 块。我们知道我们的两个原始文件格式相同,我们可以将这些块原封不动地复制到输出文件中。

还有一个pakt 块和一个data 块。我们无法保证它们在输入文件中的顺序。可能有也可能没有free 块 - 但这仅包含填充 0x00,因此我们无需将其复制到输出文件。

要合并pakt 块,我们需要检查块头并生成一个新的pakt 块,其mNumberPacketsmNumberValidFrames 字段是输入文件中的总和。 mPrimingFramesmRemainderFrames 始终为零 - 这些仅与流媒体相关。大部分pakt 块(即实际的数据包表数据)可以连接起来。

data 块也是如此:mChunkSize 字段需要求和,然后可以连接大部分数据。

从这些文件中的所有二进制数字字段读取数据时要小心:这些文件是大端的,但 iPhone 是小端的。

为了获得额外的功劳,您可能还想考虑从文件中删除音频片段,或者将一个音频文件插入另一个文件的中间。这有点棘手,因为您必须解析 pakt 块的内容。再次是遵循规范的情况:有一个很好的描述数据包大小如何存储在可变长度整数中,因此您必须解析这些以找出每个数据包在 data 块中占用的字节数,并据此计算他们的位置。

总而言之,这比我希望的要麻烦得多。也许有一个开源库可以为您完成所有这些工作,但我找不到。

但是,与在原始问题中使用 AVMutableCompositionAVMutableCompositionTrack 相比,处理这样的原始文件速度快得令人眼花缭乱 - 将长达一小时的录音插入另一个相同长度的录音大约需要两秒钟。

祝你好运!

【讨论】:

听起来你实现了这个?我现在正在自己处理它,但是如果您在我完成之前有任何可共享的代码,我将不胜感激。 如果可以的话。我的雇主同意我分享代码 sn-ps,但不太热衷于我赠送他们付钱让我花几天时间编写的东西。 :-(【参考方案2】:

我找到了一种实现起来更快的方法:

    使用 AVAudioRecorder 并使用扩展名“m4a”作为临时文件,但是如果需要,您也可以使用“caf”,但这不是必需的。

    修改代码 here 以使用 AVAssetExportPresetPassthrough 和 exportSession.outputFileType = AVFileTypeQuickTimeMovie 和文件名“audioJoined.mov”。使用您新录制的临时 m4a 和现有的 m4a 文件。这为您提供了即时加入(无需重新压缩)并产生“mov”。

注意。不幸的是,AVAudioPlayer 无法播放“mov”,因此下一步是将其转换为可播放的内容。但是,如果您只是要在某处共享文件,则可能会跳过下一步,因为 mov 在 Mac 上的 Quicktime 中可以完美播放。它也可以在 iTunes 中播放并同步回 iPhone 并在 iPod 应用程序中播放。

    使用 [[AVAssetExportSession alloc] initWithAsset:movFileAsset presetName:AVAssetExportPresetAppleM4A], @"audioJoined.m4a" 作为文件名和 exportSession.outputFileType = AVFileTypeAppleM4A 将 mov 转换回 m4a。同样,这是即时的。我猜在这种情况下,当导出器从 mov 资产而不是 AVMutableComposition 资产开始时,它会更聪明。

我在一个应用程序中使用了这种技术,它能够在停止录制并播放文件后恢复录制,或者即使应用程序重新启动,也很酷。

【讨论】:

感谢您提供此信息。我尝试使用它,它的工作原理与您所说的完全一样。一个缺点是您似乎没有办法控制输出采样率/文件大小。对于我的应用程序,文件大小是原始文件或生成 .mov 文件中的 m4a 的 6 倍。如果您对如何解决此问题有任何想法,我将不胜感激。

以上是关于如何附加到录制的 MPEG4 AAC 文件?的主要内容,如果未能解决你的问题,请参考以下文章

AVAudioRecorder 录制 AAC/m4a

使用 AVAssetWriter 和 AVAssetReader 进行音频录制和播放

将一个 AAC 文件附加到另一个文件时输出音频设置

如何使用附加模式将音频录制到现有的音频文件中?

通过 HTML5 API(RecordJS 或替代方法)将音频录制到 AAC

iOS录制时如何将PCM缓冲区实时转换为AAC数据?