使用 FFMPEG 将可搜索的 AAC 音频流写入 MP4 文件

Posted

技术标签:

【中文标题】使用 FFMPEG 将可搜索的 AAC 音频流写入 MP4 文件【英文标题】:Write seekable AAC audio stream into MP4 file with FFMPEG 【发布时间】:2016-07-26 18:47:01 【问题描述】:

我正在尝试使用 FFMPEG 库将 AAC 音频流写入 mp4 文件。我正在使用直接写入套接字的自定义 IO 上下文,因此我必须设置 ioContext->seekable = 0。为了完成这项工作,我必须在编写标题时添加“movflags”empty_moovfrag_keyframe

将输出写入套接字另一端的文件后,我可以在 VLC 或 Windows Media Player 中播放该文件。但是,寻找文件中的特定位置在两个播放器中都无法正常工作。 WMP 也不会显示总时长,VLC 只会在音频结束时短暂闪烁。

有没有办法在复用时添加更多元数据,以便玩家能够将文件视为不是作为流写入的?通过套接字的传输不会突然中断,因此我可以在文件末尾写入元数据。我也提前知道总持续时间,所以如果可能的话,我可以将它添加到文件的标题中。我不能使用faststart 标志,因为这需要在写入套接字之前输出到可查找的文件。

更新: 我知道我可以在AVFormatContext 中设置持续时间,我可以在AVStream 中设置nb_framesavg_frame_rate。但是,它并不能解决我的问题。当我设置 codecContext 标志AV_CODEC_FLAG_QSCALE 时,VLC 似乎能够估计总时间。但是,搜索仍然不起作用。

【问题讨论】:

这个 link 可能会帮助您了解 MOV 标志的问题(单词搜索:-movflags empty_moov 并阅读其余部分)。所以你描述了问题,但应用程序的上下文是什么?正在接收音频并且您想建立一个 MP4 直到 X 数量然后保存到文件?为什么不先缓冲所有需要的 AAC,然后再编码成最终的 MP4?无论如何考虑:#所有音频帧都是关键帧,#如果没有元数据,MPEG解码器无法搜索,#尝试M4a输出然后重命名为MP4? PS:我习惯将 FFmpeg 作为一个进程(而不是直接导入的库),但作为一个正在运行的进程,我会使用 ffmpeg -i - -c:a libfdk_aac -b:a 128k output.mp4 在这里我会使用 -i - 让它知道没有输入文件,而是重复将每个 AAC 帧的字节写入process.writeBytes(myAACframe);,直到 AAC 结束,我退出进程并有一个可播放的文件。简而言之,通过附加 AAC 帧动态构建的 MP4 文件可用。也许该逻辑中的某些内容可以帮助您? 你为什么不直接回应而不是在没有回应的情况下将 100 个代表点烧掉?您可以添加可以帮助其他人帮助您的 cmets。如果您想要一个可搜索的 MP4 文件,请确保它具有元数据,因为这是 MPEG 解码器搜索的必要条件。 (这意味着失去-movflags empty_moov & frag_keyframe,因为它们增加了问题)。我建议使用“过程”方法,因为这是一个简单的测试,无需担心您的完整代码是否正确(例如:nb_frames 等)。它可以用 C++、Java 或 C# 完成,具体取决于您的语言... PS:我们在这里谈论的 AAC 的持续时间是多少?我仍然认为您最好的选择是将输入字节收集到缓冲区中,并在准备好时(得到 5?10?60 分钟?)只需复用到 MP4 并将其发送到套接字。请记住,FFmpeg 仅在文件创建完成后才添加元数据(除了这里它将从套接字中消失,因此它无法编辑字节,这与保存在内存或磁盘中以在发送前先更新元数据条目不同)。跨度> @VC.One 抱歉没有回复。过去 2 天我没有在办公室,也没有解决这个问题。如果我早点阅读,您暗示所有音频帧都是关键帧可能会对我有所帮助。我只是玩弄这些标志,当我放下 empty_moov 时它就可以工作了。然后,当我将 frag_keyframe 与另一个碎片选项(字节或毫秒)交换时,它也会创建更小的碎片,而不是一个巨大的碎片。 【参考方案1】:

如果您想要一个可搜索的 MP4 文件,请确保它肯定包含元数据,因为这是 MPEG 解码器处理搜索的必要条件(MP4 元数据列出了每个 AAC 帧字节的起始位置。 这意味着失去-movflags empty_moov & frag_keyframe,因为它们增加了问题。

考虑:

您不需要frag_keyframe,因为所有音频帧都被归类为关键帧。 不要强制empty_moov,因为如果没有元数据,MP4 解码器将无法搜索。 (FFmpeg 将在分片模式下处理元数据)。

this excellent guide 对流媒体的引用:

写入碎片文件的优点是文件可解码 即使写入被中断(而普通的 MOV/MP4 如果没有正确完成,则无法解码),并且它需要更少 写入非常长的文件时的内存(因为写入普通的 MOV/MP4 文件将有关每个数据包的信息存储在内存中,直到文件 已经关闭)。缺点是less compatible with other applications

您可以尝试的选项是:

frag_duration [num] 创建 num 毫秒长的片段。 frag_size [num] 创建包含高达 num 字节大小的有效负载的片段。

【讨论】:

这些真的有用吗?我面临着类似的问题,因为在 5.0.1 和 5.1 之后,我的 AAC 文件在通过 http 流式传输时无法在 android MediaPlayer 上搜索。音频播放但如果我尝试使用 seekTo() 音频从 0 开始。问题是这适用于旧版本的 Android 5.0.1、4.4.2 等。我已经在头部有 moov 元素文件,它正在流式传输。它只是不可寻找。我应该在我的流中查找哪些 mp4 原子以了解元数据是否存在? 如果您想在浏览器中使用 fMP4,我认为这个答案没有用。至少在 Chrome 下你需要empty_moovfrag_keyframe(至少据我所知)。 @chubbsondubs 您最终找到了可行的解决方案吗? :// @StefanFalk 好的,我正在努力记住这一切,是的,我们确实修复了它。 AAC 流最初被编码为视频流,关键帧没有在其中编码。修复方法是使用 ffmpeg AAC 将所有材料重新编码为音频流。对于音频帧,每一帧都是一个关键帧。这很重要,因为我们按照 iTunes 的顺序重新编码了一些东西。在我的例子中,我们在流的前面有元数据,但在我们重新编码之前它不允许搜索。

以上是关于使用 FFMPEG 将可搜索的 AAC 音频流写入 MP4 文件的主要内容,如果未能解决你的问题,请参考以下文章

从 AAC 转换后 FFmpeg 播放音频缓慢

ffmpeg 音频转码

iOS Swift 从网络流中播放音频(aac)

FFmpeg:检查 aac 比特流是不是格式错误

ffmpeg:如何将 AAC 音频数据包转换为 ADTS 格式

如何使用 Android MediaCodec 生成 AAC ADTS 基本流