LoginSignup
6
6

More than 3 years have passed since last update.

FFmpegで動画を作成してHLSのストリーミング

Last updated at Posted at 2020-02-26

先の記事で

  • 動画自体のタイムコードを書き込んだものを作った
  • Shaka Streamer, Shaka Packagerを使ってHLS Liveのストリーミングをローカル環境で構築できた

なので、次は「現在時刻(FFmpegで動画を作っている時の時刻)を書き込んだ動画を作成しながらローカル環境でHLS Liveのストリーミング」をしてみる。

使用する環境

  • macOS 10.14.6(18G3020)
  • FFmpeg version 4.1.4
  • Shaka Packager v2.4.1-c731217-release

Shaka Streamerは使わなくてもよかった。FFmpegからのUDPの入力をするためのShaka Streamerのconfigがわからなかったので、Shaka Streamerを使うのは断念した。

現在時刻を書き込んだ動画を作成

先に書いた記事のコマンドを一部変更して、現在時刻を書き込んだ動画をローカルに保存する。

ffmpeg -re -i 03_caminandes_llamigos_1080p.mp4 -vcodec h264 \
-profile:v main -b:v 1000k -minrate:v 1000k -maxrate:v 1000k \
-bufsize:v 2000k -crf 23 -sc_threshold 0 -keyint_min 24 -r 24 -g 48 \
-vf "scale=640:-1, \
drawtext=fontfile=/Library/Fonts/Courier\ New\ Bold.ttf: \
text='%{localtime\:%X}': r=24: fontsize=60: fontcolor=white: \
x=(w-tw)/2: y=h-(2*lh): box=1: boxcolor=0x00000000@1" \
-acodec aac -ac 2 -ab 64k -ar 44.1k -y output2.mp4

変更した点

  • ffmpegのオプションで-reを加える。
  • drawtextのオプションのtimecode='00\;00\:00\:00'text='%{localtime\:%X}'に変更する。

localtimeの説明については https://ffmpeg.org/ffmpeg-filters.html#Text-expansion を参考に。
ffmpeg drawtime localtimeでググるといろんな人のページも出てくるので参考になると思います。

HLSのストリーミングをしてみる

httpdを起動し、Shaka Packagerを実行し、UDPで入力を待ち受け、UDPにFFmpegで入力をして、HLSを出力するという風なことをしてみます。

httpd起動

sudo brew services start httpd

Shaka Packagerの実行

以下のスクリプトではこんなことをしてます。

  • udp://127.0.0.1:40000 で入力を待つ
  • ./www/packager/hls.m3u8 にHLSを出力する
#!/bin/bash

INPUT="udp://127.0.0.1:40000"
OUT_DIR="./www/packager"

OUT_AUDIO_SEGMENT="${OUT_DIR}/audio_\$Number\$.ts"
OUT_AUDIO_PLAYLIST="${OUT_DIR}/audio.m3u8"

OUT_VIDEO_SEGMENT="${OUT_DIR}/video_\$Number\$.ts"
OUT_VIDEO_PLAYLIST="${OUT_DIR}/video.m3u8"

OUT_MASTER_PLAYLIST="${OUT_DIR}/hls.m3u8"

rm -rf $OUT_DIR
mkdir -p $OUT_DIR

packager \
  "in=${INPUT},stream=audio,segment_template=${OUT_AUDIO_SEGMENT},playlist_name=${OUT_AUDIO_PLAYLIST},hls_group_id=audio" \
  "in=${INPUT},stream=video,segment_template=${OUT_VIDEO_SEGMENT},playlist_name=${OUT_VIDEO_PLAYLIST}" \
  --hls_master_playlist_output ${OUT_MASTER_PLAYLIST} \
  --hls_playlist_type LIVE

FFmpegでの入力

udp://127.0.0.1:40000 でFFmpegから入力をする。
これまでのffmpegのコマンドだとストリーミングをするにはCPUを使用しすぎるので、オプションを変えて試す。

ffmpeg -re -stream_loop -1 -i 03_caminandes_llamigos_1080p.mp4 \
-vcodec h264 -profile:v main -preset ultrafast -tune zerolatency \
-b 500k -sc_threshold 0 -keyint_min 24 -r 24 -g 48 \
-vf "scale=640:-1, \
drawtext=fontfile=/Library/Fonts/Courier\ New\ Bold.ttf: \
text='%{localtime\:%X}': r=24: fontsize=60: fontcolor=white: \
x=(w-tw)/2: y=h-(2*lh): box=1: boxcolor=0x00000000@1" \
-acodec aac -ac 2 -ab 64k -ar 44.1k -f mpegts udp://127.0.0.1:40000

結果

遅延

ffplayで再生をして確認してみたところ、遅延が23秒前後〜28秒程度となった。

スクリーンショット 2020-02-27 0.32.25.png

出来上がったHLSの確認

出来上がったm3u8の中身を見ると以下の通りだった。

  • m3u8を見る限り、1つのtsファイルの長さが6秒だった
  • m3u8に含まれるtsファイルの数が特定の数ではなかった(5、6個を想定していたが、生成されたtsが全部含まれていた)
video_47.ts
#EXTINF:6.000,
video_48.ts
#EXTINF:6.000,
video_49.ts
#EXTINF:6.000,
video_50.ts
#EXTINF:6.000,
video_51.ts
#EXTINF:6.000,
video_52.ts

また、videoのtsファイルをffprobeで確認し、以下のことは確認できたので、FFmpegでの入力したストリームにはおよそ問題がなさそうだった。

  • 長さが6秒であるのは確かだった
  • 2秒ごとにI-frameが含まれていた

次の調査

調子のいい時は遅延が20秒未満になるのだけれども、どうも安定しない。
FFmpegのコマンドのチューニング、Shaka Packagerのコマンドのオプションの理解が必要そうだ。

(追記)チューニング後

Shaka Packagerのオプションを見直したら安定した。

  • チャンクファイルの長さが2秒になるように。
  • m3u8のチャンクファイルの数が3つになるように。

遅延は8〜9秒ほど。
前のスクリプトからの変更点のみ記述。

# 以下は追加
OPTIONS="${OPTIONS} --segment_duration 2"
OPTIONS="${OPTIONS} --time_shift_buffer_depth 4"
OPTIONS="${OPTIONS} --hls_master_playlist_output ${OUT_MASTER_PLAYLIST}"
OPTIONS="${OPTIONS} --hls_playlist_type LIVE"

# 以下packagerのコマンドに上記のOPTIONSを追加
packager \
  "in=${INPUT},stream=audio,segment_template=${OUT_AUDIO_SEGMENT},playlist_name=${OUT_AUDIO_PLAYLIST},hls_group_id=audio" \
  "in=${INPUT},stream=video,segment_template=${OUT_VIDEO_SEGMENT},playlist_name=${OUT_VIDEO_PLAYLIST}" \
  ${OPTIONS}

スクリーンショット 2020-02-27 8.05.54.png

CPUのStats等調査はしていなかったけども、Shaka Packagerの動作が安定したのか、HLSの再生も止まることがなくなった。
単純に6秒のチャンクを作るとしたら6秒分のリソースを最低確保するわけだから当たり前だよな。。。
FFmpegで入力するVideoを 720p/1.5Mbps/MP にしても問題なく再生ができるようになった。

(追記2)MP4のHLSにして試してみる

先のShaka Packagerのスクリプトを変更し、

  • 出力をTSからMP4(m4s)に変更
  • Fragmentも指定(1秒)

のHLSに変更してみる。

# SegmentとFragmentを指定
OPTIONS="${OPTIONS} --segment_duration 2"
OPTIONS="${OPTIONS} --fragment_duration 1"
OPTIONS="${OPTIONS} --time_shift_buffer_depth 4"
OPTIONS="${OPTIONS} --hls_master_playlist_output ${OUT_MASTER_PLAYLIST}"
OPTIONS="${OPTIONS} --hls_playlist_type LIVE"

packager \
  "in=${INPUT},stream=audio,init_segment=${OUT_AUDIO_INIT},segment_template=${OUT_AUDIO_SEGMENT},playlist_name=${OUT_AUDIO_PLAYLIST},hls_group_id=audio" \
  "in=${INPUT},stream=video,init_segment=${OUT_VIDEO_INIT},segment_template=${OUT_VIDEO_SEGMENT},playlist_name=${OUT_VIDEO_PLAYLIST}" \
  ${OPTIONS}

結果は、最短で6秒以上〜7秒未満となった。

スクリーンショット 2020-02-27 22.09.26.png

理由は調べていないが、Fragment化していないとTSで出力した時と同じ程度の遅延であったので・・・
『Fragment化されているので、Shaka Playerはその分m3u8を早く作り、プレイヤー側はそれを読み込んでm4sをダウンロードしながら再生しているんじゃなかろうか』
と思った。適当な見解なので間違っていると思ってください。

本記事はこれで終わり。

実際にサーバーに配置して試してみたくなった。
けどサーバー知識皆無で面倒だしお金かかるの怖い。

(追記3)AndroidとiOSで再生してみた

追記2で作ったストリームをAndroidとiOSで再生してみた。興味深い結果だった。
環境は以下の通り。

  • Android
    • SOV33 (Android 8.0.0)
    • ExoPlayer r2.11.1
  • iOS
    • iOS Simulator (iOS 13.2.2)
    • AVPlayer

結果としては以下の通り。

  • Androidは実機にも関わらず、ffplayの遅延+2秒程度(=8〜10秒)
  • iOSはffplayの遅延+1秒程度(=7〜9秒)、Simulatorなのに頑張っている

Androidで使用しているExoPlayerは以前のバージョンだと再生位置をチューニングすることができた。最新のバージョンでもチューニングができれば遅延を小さくできるかもしれない。
実際、ExoPlayerのデモアプリではシークバーが表示され、シークバーを操作するとffplayと同程度の遅延になることもあったので、可能性は十分にありそう。

画像は、左上がiOSのSimulator, 左下がAndroid, 右がffplay, という感じです。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6