Как HLS-live-stream принимать входящие пакеты отдельных кадров, «добавляя» их в плейлист m3u8 в режиме реального времени, с помощью ffmpeg? - PullRequest
2 голосов
/ 06 марта 2020

Моя общая цель:

На стороне сервера:

  • У меня есть партии последовательных кодированных в 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 делает запрос на последнюю версию списка воспроизведения).

...