.ts
的 h264 ffmpeg
段,并将新的 .ts
段自动添加到 HLS 中流(例如 .m3u8
文件)。.m3u8
更新时,我希望正在观看的视频流只是“继续”,从添加新的 .ts
片段的点开始。为了生成流的“前”几个片段,我正在尝试以下(现在只是命令行以使 ffmpeg 正常工作,但最终将通过 Python 脚本自动化):
仅供参考,我使用的是 ffmpeg 版本 3.4.6-0ubuntu0.18.04.1.
ffmpeg -y -framerate 2 -i /frames/batch1/frame_%d.jpg \
-c:v libx264 -crf 21 -preset veryfast -g 2 \
-f hls -hls_time 4 -hls_list_size 4 -segment_wrap 4 -segment_list_flags +live video/stream.m3u8
其中
/frames/batch1/
文件夹包含一系列帧(例如,frame_01.jpg、frame_02.jpg 等)。这似乎已经无法正常工作,因为它不断将 #EXT-X-ENDLIST
添加到 .m3u8
文件的末尾,据我所知,这对于实时 HLS 流来说是不正确的 - 这是生成的内容:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.000000,
stream0.ts
#EXTINF:4.000000,
stream1.ts
#EXTINF:2.000000,
stream2.ts
#EXT-X-ENDLIST
我不知道如何在这里抑制
#EXT-X-ENDLIST
- 这是问题#1。
然后,为了生成后续片段(例如,当新帧可用时),我正在尝试这样做:
ffmpeg -y -framerate 2 -start_number 20 -i /frames/batch2/frame_%d.jpg \
-c:v libx264 -crf 21 -preset veryfast -g 2 \
-f hls -hls_time 4 -hls_list_size 4 -segment_wrap 4 -segment_list_flags +live video/stream.m3u8
不幸的是,这并没有按照我想要的方式工作。它只是覆盖
stream.m3u8
,前进和不前进 #EXT-X-MEDIA-SEQUENCE
,它没有正确索引新的 .ts
文件,并且还包含不需要的 #EXT-X-ENDLIST
- 这是该命令的输出:
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.000000,
stream0.ts
#EXTINF:4.000000,
stream1.ts
#EXTINF:3.000000,
stream2.ts
#EXT-X-ENDLIST
从根本上讲,我不知道如何以对 HLS 直播有意义的方式“附加”到现有的
.m3u8
。这本质上是问题#2。
为了托管流,我使用了一个简单的 Flask 应用程序 - 它似乎按照我想要的方式工作 - 以下是我正在做的事情以供参考:
@app.route('/video/<string:file_name>')
def stream(file_name):
video_dir = './video'
return send_from_directory(directory=video_dir, filename=file_name)
我正在 Chrome 中尝试 HLS.js - 基本上可以归结为:
<video id="video1"></video>
...
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
var video = document.getElementById('video1');
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource('/video/stream.m3u8');
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.play();
});
}
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = '/video/stream.m3u8';
video.addEventListener('loadedmetadata', function() {
video.play();
});
}
</script>
我想我正在尝试做的事情并不需要比我上面尝试的更复杂的方法,但由于我正在尝试的方法肯定行不通,所以我开始我认为我需要从不同的角度来看待这个问题。对我缺少的东西有什么想法吗?
我也尝试过使用
video.js
进行相同的操作(再次在 Chrome 中),并且看到类似的行为 - 特别是当我手动更新支持 stream.m3u8
(没有 #EXT-X-ENDLIST
标签)时,videojs
永远不会选择将新的更改添加到实时流中,并且只是无限期地缓冲/挂起。
<video id="video1" class="video-js vjs-default-skin" muted="muted" controls>
<source type="application/x-mpegURL" src="/video/stream.m3u8">
</video>
...
<script>
var player = videojs('video1');
player.play();
</script>
例如,如果我从
stream.m3u8
的初始版本开始:
#EXTM3U
#EXT-X-PLAYLIST-TYPE:EVENT
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.000000,
stream0.ts
#EXTINF:4.000000,
stream1.ts
#EXTINF:2.000000,
stream2.ts
然后在服务器端手动将其更新为:
#EXTM3U
#EXT-X-PLAYLIST-TYPE:EVENT
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:3
#EXTINF:4.000000,
stream3.ts
#EXTINF:4.000000,
stream4.ts
#EXTINF:3.000000,
stream5.ts
video.js 控件仅在播放前 3 个片段(stream*.ts 0-2)后无限期缓冲,这不是我期望发生的情况(我希望它继续播放stream*.ts) 3-5 一旦
stream.m3u8
更新并且 video.js
请求最新版本的播放列表)。
关于你的问题#1:你在hls_flags中使用omit_endlist,这样它就不会将end_list放在列表的末尾,从而使传输连续。但是,我建议将它与 independent_segments 一起使用,因为从你所说的来看,框架似乎是断开连接的。如果没有,您可以直接不使用它。
关于你的问题#2你在hls_flags中使用append_list,它总是将帧添加到列表中,而不会丢失前一个列表中的信息。
另外:如果你想使用删除不属于列表的段的问题,可以将hls_flags中的delete_segments与hls_delete_threshold一起使用。
如何表达的一个例子是这样的:
-segment_list_type hls \
-segment_list_flags +live \
-hls_flags delete_segments+append_list+omit_endlist \
-hls_delete_threshold 5 \
-hls_list_size 10 \
-hls_time 6 \
有关更多信息,请参阅更详细地解释 ffmpge 中 hls 实现的部分:https://ffmpeg.org/ffmpeg-formats.html#hls-2