HLS中的AAC如何合并

Date:

在研究Radiko的时候,想实现在浏览器插件里把M3U8中所有的AAC保存成一个音频文件。

如果不限制在浏览器插件中,最简单的方法就是 ffmpeg -c copy 让万能的ffmpeg替你解决一切问题。然而一个插件带一个ffmpeg是不是有点大炮打蚊子?也考虑过替代方案,例如使用基于LLVM的Emscripten将ffmpeg转成Javascript,详见ffmpeg.js。但也似乎过于沉重。此外经过测试,用ffmpeg合并的结果似乎并不正确。

第二种方法就是简单的拼接,但是拼接带来一个问题:部分播放器只能识别到第一个片段。

因此,在阅读了相关文档之后终于找到了正确的解决方法。

首先,这个aac里面包含了什么? 根据http-live-streaming的文档中关于Packed Audio部分的说明:

A Packed Audio Segment contains encoded audio samples and ID3 tags that are simply packed together with minimal framing and no per- sample timestamps. Supported Packed Audio formats are AAC with ADTS framing [ISO_13818_7]; MP3 [ISO_13818_3]; AC-3 [AC_3]; and Enhanced AC-3 [AC_3].

A Packed Audio Segment has no Media Initialization Section.

Each Packed Audio Segment MUST signal the timestamp of its first sample with an ID3 PRIV tag [ID3] at the beginning of the segment. The ID3 PRIV owner identifier MUST be "com.apple.streaming.transportStreamTimestamp". The ID3 payload MUST be a 33-bit MPEG-2 Program Elementary Stream timestamp expressed as a big-endian eight-octet number, with the upper 31 bits set to zero. Clients SHOULD NOT play Packed Audio Segments without this ID3 tag.

这个AAC是由ID3标签和ADTS组成的格式。

ADTS是AAC的一种编码方式,与另一种编码方式ADIF不同,ADTS可以在任意帧解码。因此我们只需要把AAC中的ID3标签去掉,然后在拼接起来就能得到正常的AAC文件了。

回到之前第一种方法,ffmpeg似乎只把第一个AAC的ID3标签去掉,后面的依然保留,不知道是不是参数设置错误还是什么问题。因此只能手动处理掉ID3标签。

那么ID3标签的格式又是啥呢?详见ID3官方文档ID3标签 或者Abobe给出的图解ID3标签头

标签头如下:

I D 3 Version Revision Flags Size
1Byte 1Byte 1Byte 1Byte 1Byte 1Byte 4Bytes

The bitorder in ID3v2 is most significant bit first (MSB).Most significant bit first (MSB) also known as big endian and network byte order.

只需要去掉ID3标签头的10个字节加上Size的值,剩下的就是ADTS,简单合并就可以了。

如果想进一步了解ID3标签里的PRIV是啥的话,可以自行阅读文档ID3 Private frame。关于 com.apple.streaming.transportStreamTimestamp 的数值的意义。个人理解这个数值的绝对值是没有意义的,只有两个Timestamp的差值是有意义的。 (stamp2 - stamp1) / (90*1000.0)的结果就是第一段的秒数。参考资料:MPEG-2 Program Elementary Stream timestamp 的作用

以上

Leave a comment

Note : Your comment will not be displayed until a Github PullRequest have been merged.