使用FFMPEG将可寻址的AAC音频流写入MP4文件

6
我正在尝试使用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似乎能够估计总时间。然而,仍然无法正常工作。

这个**链接** 可能会帮助您理解有关MOV标志的问题(搜索词:-movflags empty_moov并阅读其余部分)。所以您描述了问题,但应用程序的上下文是什么?正在接收音频并且想要构建一个MP4直到X数量然后保存到文件吗?为什么不先缓冲所有所需的AAC,然后只编码为最终的MP4?无论如何,请考虑以下几点:#所有音频帧都是关键帧,#如果没有元数据,MPEG解码器无法查找,#尝试使用M4a输出,然后重命名为MP4? - VC.One
PS:我习惯将FFmpeg作为一个进程(而不是直接导入库),但是作为一个运行中的进程,我会使用ffmpeg -i - -c:a libfdk_aac -b:a 128k output.mp4,这里我将使用-i -告诉它没有输入文件,而是只需重复写入每个AAC帧的字节到process.writeBytes(myAACframe);,直到AAC结束为止,然后退出进程并生成可播放的文件。简而言之,通过追加可用的AAC帧来动态构建MP4文件。也许这种逻辑能对您有所帮助? - VC.One
为什么不回复而是浪费100个声望点呢?你可以添加评论来帮助其他人帮助你。如果您想要一个可搜索的MP4文件,请确保它具有元数据,因为这对于寻找MPEG解码器来说是必需的。(这意味着放弃-movflags empty_moov & frag_keyframe,因为它们会加重问题)。我建议使用“process”方法,因为这是一个简单的测试,无需担心您的完整代码是否正确(例如:nb_frames等)。它可以在C++、Java或C#中完成,具体取决于您的语言... - VC.One
我们在谈论什么时AAC的持续时间?我仍然认为你最好的选择是将输入字节收集到缓冲区中,当准备好(5分钟?10分钟?60分钟?)时,将其混合成MP4并将其发送到套接字。请记住,FFmpeg仅在文件创建完成后添加元数据(除非它已经通过内存或磁盘保留以便在发送之前更新元数据条目,否则它无法编辑字节)。 - VC.One
@VC.One抱歉没有回复。我过去两天都没来办公室,也没有处理这个问题。如果我早些看到你说的所有音频帧都是关键帧的提示,可能会对我有所帮助。我只是尝试了一下标志,并且当我删除empty_moov时它可以正常工作。然后,当我将frag_keyframe与其他分段选项(字节或毫秒)交换时,它也会创建较小的片段而不是一个巨大的片段。 - lex82
显示剩余2条评论
1个回答

3
如果你想要一个可定位的MP4文件,请确保它有元数据,因为这是MPEG解码器处理寻址的必要条件(MP4元数据列出了每个AAC帧字节的起始位置)。因此,请避免使用“-movflags empty_moov&frag_keyframe”选项,因为它们会加重问题。
请考虑以下内容:
- 由于所有音频帧都被归类为关键帧,因此您不需要使用“frag_keyframe”选项。 - 不要强制使用“empty_moov”选项,因为如果没有元数据,MP4解码器无法进行寻址。(在分段模式下,FFmpeg会处理元数据)。
以下是来自流媒体这篇优秀指南的引用:
“写入分段文件的好处是,即使写入过程中出现中断(而普通MOV/MP4文件未能正确完成的情况下),该文件也可以解码,并且在写入非常长的文件时需要更少的内存(因为写入普通的MOV/MP4文件会将每个单独的数据包的信息存储在内存中,直到文件关闭)。缺点是它与其他应用程序兼容性较差。”
您可以尝试的选项包括:
- “frag_duration [num]”选项:创建持续num毫秒的片段。 - “frag_size [num]”选项:创建包含最多num字节大小负载的片段。

这些方法有没有真正起作用?我面临类似的问题,因为我的AAC文件在通过HTTP进行流式传输时,在Android MediaPlayer的5.0.1和5.1版本之后是无法被搜索的。音频可以播放,但如果我尝试使用seekTo()函数,音频会重新从0开始播放。该问题在旧版本的Android 5.0.1、4.4.2等中可以解决。我已经在文件的开头设置了moov元素并且确实进行了流式传输。只是不能被搜寻。我应该查找哪些mp4原子以知道流中是否存在元数据? - chubbsondubs
我认为如果你想在浏览器中使用fMP4,这个答案并不是很有用。至少在Chrome下,你需要empty_moovfrag_keyframe(从我所知道的来看)。 - Stefan Falk
@chubbsondubs 你最终得到了一个可行的解决方案吗? :/ - Stefan Falk
1
@StefanFalk 好的,我试着回忆一下,是的我们修复了它。AAC流最初被编码为视频流,并且关键帧未在其中编码。修复方法是使用FFmpeg AAC重新编码所有材料作为音频流。由于每个音频帧都是关键帧,这是一件大事。这很重要,因为我们正在重新编码类似iTunes的东西。在我的情况下,我们将元数据放置在流的前面,但在重新编码之前,它不允许寻找。 - chubbsondubs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接