具有不同长度的音频文件的 HTTP 实时流式传输

Posted

技术标签:

【中文标题】具有不同长度的音频文件的 HTTP 实时流式传输【英文标题】:HTTP Live Streaming with audio files of varying lengths 【发布时间】:2011-08-13 02:33:07 【问题描述】:

我正在尝试使用 Apple 的 HTTP Live Streaming 协议将音频流式传输到 ios 和 Safari 客户端。与 HTTP Live Streaming 的许多常见实现不同,我的目标是使用本质上具有不同长度的短音频剪辑,主要在 10-30 秒范围内。除了从这些片段流式传输音频之外,我还想访问每个片段的元数据,以便更新显示和/或为用户提供额外选项以获取有关特定音频片段的更多信息。

目前我已经设置了一些测试用例,将我的源音频 (MP3) 转换为各种格式并创建流式 M3U 文件以在 iOS 设备上进行测试,但我的方法都没有正常工作(正确流式传输并将元数据传递到客户端)。我正在使用AVPlayer 来加载和播放创建的 M3U 文件:

_playerItem = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:@"http://localhost/sample.m3u8"]]
_player = [[AVPlayer alloc] initWithPlayerItem:_playerItem];
[_playerItem addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:NULL];

// ... wait for user input

[_player play];

方法 1:原始 MP3 文件

我使用 id3v2 (v2.3.0) 元数据获取原始源 MP3 文件并将它们添加到 M3U 播放列表中。

#EXTM3U
#EXT-X-TARGETDURATION:23
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:14
http://localhost/trk_01.mp3
#EXTINF:22
http://localhost/trk_02.mp3
#EXTINF:16
http://localhost/trk_03.mp3
#EXT-X-ENDLIST

结果:timedMetadata 属性会在播放开始后立即更新,并使用第一首曲目的正确 ID3 信息。第一首曲目播放,但在接近尾声时中断。显示第二首曲目的 ID3 数据,但第二首曲目未开始播放。片刻之后,控制台出现错误:

2011-04-26 07:04:52.668 TestClient[49756:601b] Prime: Exiting because mConverterError is '!buf' (0x800 req, 0x0 primed)
2011-04-26 07:04:52.668 TestClient[49756:601b] Prime failed ('!buf'); will stop (2048/0 frames)

方法 2:使用 Apple 的 mediafilesegmenter 创建单独的 MP3 文件

在这种方法中,我使用mediafilesegmenter 为每个片段创建一个新的 MP3 文件。 Apple 的分段工具通常用于分段,但是因为我的音频剪辑都很短且长度不一,这并不适合我的应用程序。我将 999 秒的目标持续时间传递给实用程序,以便它为我给它的每个输入文件创建一个输出文件。这是我用来创建每个单独轨道的命令:

mediafilesegmenter -t 999 -f "$OUTPUT_DIR" "$INPUT_FILE" && cp $OUTPUT_DIR/fileSequence0.mp3 $OUTPUT_FILE

生成的 MP3 文件似乎有一些时间戳数据,因为 vbindiff 向我显示了文件头的变化,并且字符串“com.apple.streaming.transportStreamTimestamp”出现在新文件的前几个字节中。研究该字符串会发现HTTP Live Streaming draft specification 中的一段:

基本音频流文件必须 发出第一个时间戳的信号 通过在文件中添加一个示例 带有所有者的 ID3 PRIV 标签 [ID3] 的标识符 “com.apple.streaming.transportStreamTimestamp”。 二进制数据必须是 33 位 MPEG-2 节目基本流 时间戳表示为大端 八位字节数,高位 31 位设置为零。

然后我创建一个 M3U 文件,就像在方法 1 中一样。(请注意,使用 mediafilesegmenter 我还可以使用预先创建的 ID3 标记文件和描述 ID3 时间偏移的元文件传递 ID3 信息。我在这里跳过它,因为我什至无法正确播放这些文件。)

结果:第一首曲目与方法 1 一样流式传输。曲目在接近尾声时再次中断,第二首曲目不播放。不存在元数据,但可以使用 mediafilesegmenter 的 -M 选项轻松添加。

方法三:使用 ffmpeg 创建 MPEG 传输流文件

使用最后一种方法,我通过 ffmpeg 传递我的源 MP3 文件以创建 MPEG 传输流数据:

ffmpeg -i "$INPUT_FILE" -f mpegts -acodec copy "$OUTPUT_FILE"

然后我像前两种方法一样创建一个 M3U。

结果:这种方法确实有效;所有文件都在客户端流畅地传输。但是,我无法将任何元数据传递给客户端。我尝试将 -metadata title="My Title" 之类的参数传递给 ffmpeg,但没有成功。

【问题讨论】:

当使用 ffmpeg 解决方案并将元数据传递到该流中时,您生成的 TS 文件实际上是否包含文本元数据?如果是这样,这些格式到底使用了哪种格式? 不,它们似乎不包含任何元数据。至少没有一个我可以使用 id3 工具、exiftoolfile 或 Apple 的 AVPlayer 客户端提取出来。 我不确定 TS 文件如何以及是否可以实际包含元数据。我查看的 TS 规范在这个问题上似乎含糊不清。您能否将您的任何 TS 结果放到网上,以便我仔细查看?目前我正在围绕 TS / MPMoviePlayerController 进行大量研究,因此它适合我的工作,也许您也可以从中受益。 【参考方案1】:

只是一个建议..你试过这个项目吗?https://github.com/DigitalDJ/AudioStreamer我在我的项目中使用它,它很好

更新 1 -

您可以使用 FFMPEG 的参数“-map_meta_data”将元数据信息传输到另一个文件

这是一个例子 -

ffmpeg -i /root/Desktop/new_tracks/02-drug-raps.mp3 -ab 24k /root/Desktop/new_tracks/converted/2.mp3 -map_meta_data /root/Desktop/new_tracks/02-drug-raps.mp3:/root/Desktop/new_tracks/converted/2.mp3;

【讨论】:

感谢您的建议。我看过 AudioStreamer,但我认为它不适合这个项目,因为我正在播放由许多不同长度的片段组成的相当长的音频流。 AudioStreamer 不支持 HTTP Live Streaming。关于您在 ffmpeg 上的注释,将元数据输入或输出 MP3 文件没有问题;我的问题是生成的 MP3 文件在添加到 HTTP Live Streaming 播放列表时无法正常播放。我只能可靠地播放 MPEG TS(传输流)文件,但这些文件没有元数据。 是的,我的一个项目也面临同样的问题,我在该项目中使用了 AutioStreamer。 你好 pix hw r u?我也对音频流做同样的事情,但我真的找不到任何合适的文档或源代码。请帮助我摆脱困境【参考方案2】:

除非将片段准备为单个流,否则解码器不一定能够在它们之间无缝切换。

您应该在解码器需要重置时引入#EXT-X-DISCONTINUITY 行。除非您正在分割单个长 mp3 文件,否则您需要在每个新 mp3 之前标记不连续性。

【讨论】:

以上是关于具有不同长度的音频文件的 HTTP 实时流式传输的主要内容,如果未能解决你的问题,请参考以下文章

流式传输实时音频

如何在具有单独位置 url 和资源 url 的 swift 中从服务器实时流式传输音频?

使用分块传输通过 HTTP POST 流式传输麦克风输出

将音频从浏览器流式传输到具有特定 MIME 类型的 node.js 服务器

将通过 HTTP 流式传输的 wav 实时转换为 mp3

从 URL 实时流式传输音频的 RadioKit 替代方案