Моя общая цель:
На стороне сервера:
- У меня есть партии последовательных кодированных в JPEG кадров (8-16), поступающих со времени время, генерируемое со скоростью примерно 2 FPS.
- Я хотел бы разместить живой поток HLS, где, когда приходит новый пакет кадров, я кодирую эти новые кадры как сегменты h264
.ts
с 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, et c ... ). Похоже, что это уже не работает правильно, потому что он продолжает добавлять #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
По сути, я не могу понять, как "добавить" к существующему .m3u8
способ, который имеет смысл для прямой трансляции HLS. По сути, это проблема # 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)
На стороне клиента:
Я пытаюсь HLS. js в Chrome - в основном сводится к этому:
<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>
Я хотел бы думать, что то, что я пытаюсь сделать, не требует более сложного подхода, чем то, что я пытаюсь описать выше, но так как то, что я пытаюсь, безусловно, не работает, я начинаю думать, что мне нужно прийти к этому под другим углом. Любые идеи о том, что мне не хватает?
Редактировать:
Я также пытался сделать то же самое (снова в Chrome) с video.js
, и я Видя подобное поведение - в частности, когда я вручную обновляю резервную копию 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
видео. js элемент управления просто буферизует бесконечно после воспроизведения только первых 3-х сегментов (поток * .ts 0-2), а это не то, чего я ожидал (я ожидал, что он продолжит играть поток * .ts 3-5 один раз stream.m3u8
обновляется и video.js
делает запрос на последнюю версию списка воспроизведения).