目次
- はじめに
- 本記事のゴールと対象読者
- SRT とは
- 構築する動画配信環境
- 環境構築
- Linux 環境の準備
- Nginx をインストールしライブ動画コンテンツの置き場を作成
- Let's Encrypt で HTTPS 通信の有効化
- 動画編集用 FFmpeg 準備
- Linode VM で SRT 通信を受け取るための準備
- Linux 再起動時の SRT Listener 自動動作開始
- HLS & DASH コンテンツを生成する FFmpeg スクリプトの準備
- HLS 用スクリプト作成
- DASH 用スクリプト作成
- 両方同時出力用スクリプト作成
- スクリプトを実行可能にする
- SRT Caller 用 OBS の準備
- 動作の確認
- SRT 送信確認
- FFmpeg スクリプト実行
- プレイヤー再生確認
- 古いコンテンツの自動削除
- まとめ
はじめに
IP 及び HTTP の利用を前提とした動画配信サービスの人気上昇とクラウドサービスの普及により、大規模な OTT サービス事業者や放送局から中小規模の企業に至るまで、インターネット及びクラウドサービスを活用した動画配信のユースケースが増えています。一方、動画配信サービスは高性能なコンピューティングリソース及び高帯域かつ安定的なネットワーク環境を求める傾向があります。各企業は動画配信サービスにおける費用削減を試みると同時に高い品質や低遅延など、優れたユーザーエクスペリエンスを維持しようとしています。
そんな中、最近注目を集めている技術として SRT (Secure Reliable Transport) があります。従来放送局などで利用していた伝送方式 (RTMP) が持つ課題を乗り越え、現代のインターネット環境を踏まえて、動画データ伝送における厳しい要求事項を満たすことができる技術として注目されています。似たものとして Zixi などがあります。その中で、SRT は開発元の Haivision 社がオープンソースで公開し、基本無償で利用可能といった背景により、世界中の専門ベンダーが SRT 関連製品やソリューションを次々と出しています。
その SRT を含め、無償で利用可能なソフトウェアのみを利用して、少し本格的なライブ動画配信環境を構築する方法を紹介します。
RTMP と SRT の違いについて解説した Haivision 社の記事
Today’s Alternatives to RTMP for Getting Your Streams to the Internet
本記事のゴールと対象読者
本記事では SRT を利用したライブ動画配信サーバーの構築をゴールとします。また、なるべく費用をかけずにライブ動画配信サーバーを素早く構築したい方を対象にしています。
本記事の手順をスムーズに進めるにあたり、下記の条件を満たしていることが望ましいです。
- OS (Windows 10, Linux) やソフトウェア (OBS, FFmpeg, Nginx) における基礎的な知識や経験があること
- HTTP を利用した動画配信技術における基礎的な理解があること
- アカマイの Cloud Computing Services アカウントを保有していること
- HTTPS 通信ができるよう Let's Encrypt からの証明書発行してもらうため、自分が利用するドメインに対する DNS レコードの変更権限があること
上記の条件を満たしていれば、約2時間ほどでライブ動画配信ができるウェブサーバーを構築できます。もしアカマイの Cloud Computing Services 自体に馴染みがない、又はアカウントを保有していない方は、下記の記事を参考にしてアカウントを作成してみてください。
Linodeとは何かをわかりやすく解説
SRTについて
2017年に Haivision 社が発表した SRT は、その名 (Secure Reliable Transport) が示すように、セキュアかつ安定的に動画データを伝送するための技術として誕生しました。不安定でセキュアではないインターネット環境での利用を想定し、その伝送環境の不安定さによるライブ配信へのインパクトやユーザーエクスペリエンスの低下を最低限に抑制することを目的としています。
SRT Open Source Protocol (SRT とは)
これまでの RTMP のような伝送方式とは異なり TCP ではなく UDP で動作しますが、パケットの伝達保証やエラー補正機能を持たない UDP レイヤーにおいて、 SRT がメディア伝送の特性を踏まえた伝送制御ソフトウェアとして動作します。SRT を利用したデータ送受信を行う場合はその両方のデバイスが SRT に対応している必要があります。また、SRT は最大256ビットの AES 暗号化に対応し、セキュアに動画データを伝送することができます。
また、コンテンツ内容を問わない (Content Agnostic) という特性もあり、X.264 だけでなく VP9, AV1, HEVC を含め様々なコーデックにも対応しています。既存 RTMP が AV1 や HEVC などに対応してないこととは対照的です。
本記事のスコープではありませんが、2023年7月 VSO (Veovera Software Organization) が発表した Enhanced RTMP は新しいコーデックにも対応しています。
Enhanced RTML の概要
Haivision 社は SRT を利用するためのコードを GitHub に無償公開しているため、誰でも自由に利用できます。
SRT ソースコードのリポジトリ
また、Haivision 社を中心とした SRT Alliance という団体があり、様々な企業と動画配信技術の研究開発及び普及に力を入れています。
SRT Alliance ホームページ
エンコーダー及びデコーダーはソースである大容量の動画データを限られたネットワーク帯域の回線で伝送するための圧縮と伸張を行います。インターネット環境を通じて動画データを伝送する場合、どうしても遅延やパケットロスなどのリスクが存在します。 TCP を伝送方式に用いる場合、その特徴であるパケット再伝送 (Retransmission) などデータをきちんと送るための補正機能が存在します。しかし、動画配信サービスの特徴を考慮すれば TCP が最適な伝送補正処理を行えているとは言えません。
一方、UDP を用いる SRT で動画データを送受信する場合、送受信側両方にバッファーの役割を持つコンポーネントが付きます。SRT 伝送ではこのバッファーを利用し動画配信サービスの特徴を踏まえたデータ伝送補正を行います。その伝送補正に関わるパラメーターは様々であり、各々のユースケースにおける要件に柔軟に対応できるようパラメーター値の調整が利用者に委ねられています。
非常に大まかな説明ですが、このような特性を持つ SRT の利用により、インターネット環境で低遅延かつ安定的な動画配信サービスを実現しやすくなります。
構築する動画配信環境
本記事で構築する動画配信環境と重要コンポーネントを表現したワークフロー図です。各手順を進めていく中、自分がどの部分を触っているかを認識しておくと理解しやすいです。
本記事で利用する主なソフトウェアの Version 一覧
- Ubuntu 22.04 + SRT 1.5.3 (SRT Listener) & FFmpeg 5.1.3 (Transcoder & Packager) & Nginx 1.18.0 (HTTP Web Server)
- Windows 10 (22H2) + OBS 29.1.3 (SRT Caller)
環境を説明する前に SRT の動作モードについて解説します。SRT として動作するコンポーネントはその役割によって3つのモードに分けられます。本記事では Caller と Listener モードのみ登場します。
SRT 動作モード
- Caller → 動画データを伝送する宛先アドレス情報 (IP アドレス 及び Port) を知り、SRT 通信を開始する (≒電話で例えると、相手に電話をかける側)
- Listener → Caller から SRT シグナルを受信する側で、Caller のアドレス情報を知る必要はない (≒電話で例えると、相手からの電話信号を待ち受け、かけられてきた電話を取る側)
- Rendezvous → 送受信側におけるロールを分けず、同じ UDP Port を利用して SRT セッションを結ぶ。特殊なネットワーク環境で利用するモード
Caller は宛先アドレスを利用して SRT 通信を開始する役割を持ちます。Listener となるデバイスの IP アドレス 及び Port を事前に知り、通信を開始するという意味で Caller と言います。 Caller としての SRT 機能を搭載した汎用ソフトウェア (FFmpeg, OBS など) が存在します。本記事では、OBS を SRT Caller として使います。
Listener は Caller のアドレスを事前に知る必要がなく、自分の IP アドレス と SRT Listener 用に開いた Port 番号で受信した SRT 通信を処理します。SRT Listener として一度開いた Port は明示的に閉じない限り、開いたままの状態になります。例えば電話機が電話を切った後、いつでも受信可能な状態にあることと同じです。また Caller からの通信が一時的に途切れた場合でも継続して受信処理ができ、同じ Port を利用して別の Caller からの SRT 通信を受信することも可能です。Listener として利用する Port 番号は1024~65535の中で任意で選択できるので、サービス構成の自由度が高く、企業向けファイアウォールの構成とも相性が良いです。本記事では、Haivision 社が公開している SRT リポジトリからソースコードを取得して SRT Listener として利用します。
環境構築
では、これよりライブ動画配信サーバー環境の構築を開始します。
Linux 環境準備
Linode VM を生成します。アカマイの Cloud Computing Services ポータルでログイン後「Create - Linode」を選択します。
生成する VM の各種属性を選択していきます。利用する Linux Distribuition Image としては 「Ubuntu 22.04 LTS」 を選択し、VM が生成される Region は任意で選択します。エンコーダーとなる OBS と FFmpeg が動くウェブサーバー、そして視聴者となるクライアントの物理的な場所を考慮して Region を選んでください。本記事では「Osaka (jp-osa)」Region を選択しています。
Linode Resource プランを選びます。検証目的としてできる限り安価なプランを選びたいので、本記事の例では共有型リソースを提供する「Shared CPU - Linode 8GB」を選択します。
本番環境での運用を検討される場合は占有型リソースを提供する Dedicated CPU プランの利用を推奨します。
本記事の構成では Linode 8GB で十分 (FFmpeg 処理による全体 CPU の利用率が50%を超えない) ですが、ソースとなる動画データの Bitrate や FFmpeg で行う動画処理の内容次第でより高い性能の VM を求められることがあります。必要に応じて適切なプランを選択ください。自分の動画処理がどれほどのリソースを利用しているかは Longview を利用すると分かりやすいです。Longview 利用に関しては下記記事を参照ください。
Linode Longviewを利用してサーバーの負荷状況を可視化しよう
また、動画配信のアクセスが多くなる場合は VM の性能だけを高くしても効率が悪く限界があるため、 CDN の利用を検討してください。
動画配信 CDN 製品の例 - Akamai AMD
Linode VM のラベルと Root パスワードを入力 (任意) します。
必要な情報の入力や選択を完了した後、一番下の「Create Linode」ボタンをクリックすることで Linode VM が生成されます。
自動で画面が Linode VM 詳細に遷移します。「PROVISIONING」表示で Linode VM が準備中であることが分かります。
Linode VM は数秒で立ち上がり「RUNNING」表示に変わります。その後「Network」タブに移動します。
「Network」タブでは Linode VM に関連するネットワーク関連情報を確認できます。Reverse DNS アドレスを確認します。IPv4 形式の各オクテットと固定ドメイン<.ip.linodeusercontent.com>の組み合わせにより構成された Reverse DNS アドレスを利用して、該当 Linode VM に SSH アクセス可能です。(IPv4 アドレスを利用して直接アクセスも可能)
では、SSH を利用して Linode VM にアクセスします。root アカウントと、事前定義した root パスワードでログイン可能です。ログインに成功したら現在のパッケージをアップグレードします。
apt update -y
apt upgrade -y
アップグレードが完了したら、一度 Linode VM を再起動し新 Kernel やパッケージをロードします。再起動は数分ほどで完了するので、その後再度 SSH でアクセスします。
reboot
Nginx をインストールしライブ動画コンテンツの置き場を作成
動画配信用ウェブサーバーとして利用する Nginx をインストールします。
apt install nginx -y
インストール完了後、Linux 再起動時に自動で起動するようにします。
systemctl enable nginx
最後に、FFmpeg で処理した動画コンテンツファイルの置き場を準備します。本ディレクトリーに関連する詳細については後ほど解説します。
mkdir /var/www/html/live
ここまで設定が完了したら、Linode VM 詳細画面の「Network」タブで確認した Reverse DNS アドレスを利用して HTTP サーバーの動作を確認します。ウェブブラウザーで下記のようなページが確認できれば成功です。
Let's Encrypt で HTTPS 通信の有効化
(ドメインのレコード変更権限がなく、HTTPS 通信確認が不要であれば本章はスキップ可能)
Let's Encrypt の証明書をセットして HTTPS 通信を有効にする前提条件として、下記条件を満たしてドメイン認証を行う必要があります。条件が満たされないと Let's Encrypt からのドメイン認証が失敗したものと見なされ、証明書が発行されません。
Let's Encrypt に証明書を申請するための条件 (いずれか一つで OK)
- Linode VM の Reverse DNS 名を、証明書発行するサービスの公開ホスト名 の CNAME レコードとして登録する
- Linode VM の IPv4 アドレスを、証明書発行するサーバー Hostname の A レコードとして登録する
Let's Encrypt から証明書を発行するツールとして certbot をインストールします。certbot を利用することで自動更新も可能になります。
apt install certbot -y
certbot のインストール完了後、証明書発行のリクエストを行います。
certbot certonly --webroot -w /var/www/html -d {証明書申請を行う Hostname}
最初の証明書リクエスト時はメールアドレスの入力、及びサービス条件の確認を求められます。
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): {メールアドレスを入力}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: {'Y'を入力}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: {'Y'又は'N'を入力}
Account registered.
Requesting a certificate for {証明書申請を行った Hostname}
必要な情報を入力した後 Let's Encrypt によるドメイン認証が正しく行われた場合、下記の結果ログ表示と共に証明書が発行されます。
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/{証明書申請を行った Hostname}/fullchain.pem
Key is saved at: /etc/letsencrypt/live/{証明書申請を行った Hostname}/privkey.pem
This certificate expires on 2023-12-30.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
逆に Let's Encrypt によるドメイン認証が失敗した場合、証明書は発行されず、下記ログが表示されます。
Certbot failed to authenticate some domains (authenticator: webroot). The Certificate Authority reported these problems:
Domain: {証明書申請を行った Hostname}
Type: dns
Detail: DNS problem: NXDOMAIN looking up A for {証明書申請を行った Hostname} - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for {証明書申請を行った Hostname} - check that a DNS record exists for this domain
Hint: The Certificate Authority failed to download the temporary challenge files created by Certbot. Ensure that the listed domains serve their content from the provided --webroot-path/-w and that files created there can be downloaded from the internet.
Some challenges have failed.
証明書発行に成功した結果ログの中に、下記のように証明書と鍵の保管場所が表示されます。これらを利用し Nginx の HTTPS 通信を有効にします。
Certificate is saved at: /etc/letsencrypt/live/{証明書申請を行った Hostname}/fullchain.pem
Key is saved at: /etc/letsencrypt/live/{証明書申請を行った Hostname}/privkey.pem
Nginx で SSL 通信を有効にするため、Nginx 設定ファイルをアップデートします。HTTPS 通信を有効にするための 443ポートのオープン、証明書と鍵の指定に加え、ここでは CORS ヘッダーの設定を追加しています。
vim /etc/nginx/sites-available/default
(以下内容を追加、更新し保存)
# SSL configuration
#
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate "/etc/letsencrypt/live/{証明書申請を行った Hostname}/fullchain.pem";
ssl_certificate_key "/etc/letsencrypt/live/{証明書申請を行った Hostname}/privkey.pem";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
add_header Access-Control-Allow-Origin *;
(省略)
設定修正後、Nginx を再起動します。
systemctl restart nginx
Nginx 再起動後、動画視聴クライアントから、構築したサービスの公開ホスト名 を利用し HTTPS 通信可否を確認します。下記は Chrome での接続結果ですが、URL の部分に鍵マークが現れていれば成功です。
動画編集用 FFmpeg 準備
次は Linode VM にインジェストされた動画データを編集し HLS 及び DASH コンテンツ化するための FFmpeg を準備します。
cd ~
wget https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2023-03-31-12-50/ffmpeg-n5.1.3-linux64-gpl-5.1.tar.xz
tar -xf ffmpeg-n5.1.3-linux64-gpl-5.1.tar.xz
FFmpeg が正常動作するか下記コマンドで確認します。問題なく実行できれば Version やライブラリなどを確認できます。
/root/ffmpeg-n5.1.3-linux64-gpl-5.1/bin/ffmpeg -version
(以下内容が表示される)
ffmpeg version n5.1.3-20230331 Copyright (c) 2000-2022 the FFmpeg developers
built with gcc 12.2.0 (crosstool-NG 1.25.0.152_89671bf)
configuration: --prefix=/ffbuild/prefix --pkg-config-flags=--static --pkg-config=pkg-config --cross-prefix=x86_64-ffbuild-linux-gnu- --arch=x86_64 --target-os=linux --enable-gpl --enable-version3 --disable-debug --enable-iconv --enable-libxml2 --enable-zlib --enable-libfreetype --enable-libfribidi --enable-gmp --enable-lzma --enable-fontconfig --enable-libvorbis --enable-opencl --enable-libpulse --enable-libvmaf --enable-libxcb --enable-xlib --enable-amf --enable-libaom --enable-libaribb24 --enable-avisynth --disable-chromaprint --enable-libdav1d --enable-libdavs2 --disable-libfdk-aac --enable-ffnvcodec --enable-cuda-llvm --enable-frei0r --enable-libgme --enable-libkvazaar --enable-libass --enable-libbluray --enable-libjxl --enable-libmp3lame --enable-libopus --enable-mbedtls --enable-librist --enable-libssh --enable-libtheora --enable-libvpx --enable-libwebp --enable-lv2 --enable-libmfx --disable-openal --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopenmpt --enable-librav1e --enable-librubberband --disable-schannel --enable-sdl2 --enable-libsoxr --enable-libsrt --enable-libsvtav1 --enable-libtwolame --enable-libuavs3d --enable-libdrm --enable-vaapi --enable-libvidstab --enable-vulkan --enable-libshaderc --enable-libplacebo --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxvid --enable-libzimg --enable-libzvbi --extra-cflags=-DLIBTWOLAME_STATIC --extra-cxxflags= --extra-ldflags=-pthread --extra-ldexeflags=-pie --extra-libs='-ldl -lgomp' --extra-version=20230331
libavutil 57. 28.100 / 57. 28.100
libavcodec 59. 37.100 / 59. 37.100
libavformat 59. 27.100 / 59. 27.100
libavdevice 59. 7.100 / 59. 7.100
libavfilter 8. 44.100 / 8. 44.100
libswscale 6. 7.100 / 6. 7.100
libswresample 4. 7.100 / 4. 7.100
libpostproc 56. 6.100 / 56. 6.100
Linode VM で SRT 通信を受け取るための準備
Linode VM 側で SRT インジェストを受信するため、SRT をビルドします。まずは依存性問題を解決するためのパッケージをインストールします。
apt-get install tclsh pkg-config cmake libssl-dev build-essential -y
SRT バイナリーをダウンロードし、ビルドします。
wget https://github.com/Haivision/srt/archive/refs/tags/v1.5.3.tar.gz
tar -xf v1.5.3.tar.gz
cd srt-1.5.3 && mkdir _build && cd _build
cmake ..
make
SRT ビルドが完了したら、下記コマンドでビルドした SRT の動作を確認します。Listener モードでの動作が確認できれば、Ctrl+C で強制終了します。
/root/srt-1.5.3/_build/srt-live-transmit srt://:2000 udp://127.0.0.1:5000 -v
(以下内容が表示される)
Media path: 'srt://:2000' --> 'udp://127.0.0.1:5000'
Opening SRT source listener on :2000
Binding a server on :2000 ...
listen...
^C (Ctrl+C 入力)
-------- REQUESTED INTERRUPT!
SrtCommon: DESTROYING CONNECTION, closing sockets (rt%-1 ls%313240613)...
SrtCommon: ... done.
Linux 再起動時の SRT Listener 自動動作開始
rc-local サービスを利用して Linode VM 再起動時にも SRT Listener モードが自動で起動するようにします。まず rc-local.service ファイルを生成します。
(以下内容を入力し保存)
[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99
[Install]
WantedBy=multi-user.target
rc-local サービスで実行するコマンドを記入します。
(以下内容を入力し保存)
#!/bin/bash
/root/srt-1.5.3/_build/srt-live-transmit srt://:2000 udp://127.0.0.1:5000 -v
rc-local サービス登録準備を完了したら、下記コマンドでサービスを実行可能状態にし Linode VM をリブートします。
chmod +x /etc/rc.local
systemctl enable rc-local
reboot
Linode VM リブート完了後、下記コマンドで rc-local サービスの状態を確認します。
systemctl status rc-local
(以下内容が表示される)
● rc-local.service - /etc/rc.local Compatibility
Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled)
Drop-In: /usr/lib/systemd/system/rc-local.service.d
└─debian.conf
Active: activating (start) since Tue 2023-09-26 12:07:01 UTC; 7s ago
Cntrl PID: 614 (rc.local)
Tasks: 5 (limit: 4558)
Memory: 4.5M
CPU: 36ms
CGroup: /system.slice/rc-local.service
├─614 /bin/bash /etc/rc.local start
└─618 /root/srt-1.5.3/_build/srt-live-transmit srt://:2000 udp://127.0.0.1:5000 -v
Sep 26 12:07:01 localhost systemd[1]: Starting /etc/rc.local Compatibility...
Sep 26 12:07:01 localhost rc.local[618]: Media path: 'srt://:2000' --> 'udp://127.0.0.1:5000'
Sep 26 12:07:01 localhost rc.local[618]: Opening SRT source listener on :2000
Sep 26 12:07:01 localhost rc.local[618]: Binding a server on :2000 ...
Sep 26 12:07:01 localhost rc.local[618]: listen...
この手順により、もし Linode VM がリブートした場合でも SRT Listener がバックグランドで自動開始されることを確認できました。
HLS & DASH コンテンツを生成する FFmpeg スクリプトの準備
Linode VM で SRT として受信した動画データを FFmpeg を利用し HLS 及び DASH にパッケージング処理するためのスクリプトを作成します。スクリプトは3つ (HLS のみ, DASH のみ, 両方同時出力) 作成します。
各スクリプトは下記の属性を持ちます。
- 1080p/720p/480p の Adaptive Bitrate 出力に対応
- 再生中 Bitrate 情報を分かりやすくするため画面の左上にテキストボックスを追加
- HLS のみの場合、セグメントファイル形式は mpegts (.ts)
- DASH のみの場合、セグメントファイル形式は fMP4 (.mp4)
- 両方同時出力 (Both) の場合、HLS 及び DASH のセグメントファイル形式は fMP4 (.mp4)
動画コンテンツファイルの保存先情報
(古いコンテンツの自動削除のため、Date 情報をディレクトリー名として利用)
- HLS のみ, DASH のみの場合 : /var/www/html/live/{動画を出力する日の YYYYMMDD}/{都度任意入力する Program 名}/{hls 又は dash}/
- 両方同時出力 (Both) の場合 : /var/www/html/live/{動画を出力する日の YYYYMMDD}/{都度任意入力する Program 名}/both/
- HLS のマスタープレイリストファイル名 : master.m3u8
- DASH のマニフェストファイル名 : manifest.mpd
HLS 用スクリプト作成
#!/bin/bash
TODAY=$(date '+%Y%m%d')
echo "Input your live program name:"
read PROGRAMNAME
mkdir /var/www/html/live/$TODAY
mkdir /var/www/html/live/$TODAY/$PROGRAMNAME
mkdir /var/www/html/live/$TODAY/$PROGRAMNAME/hls
/root/ffmpeg-n5.1.3-linux64-gpl-5.1/bin/ffmpeg \
-i udp://:5000 -map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 \
-preset ultrafast -tune zerolatency \
-c:v libx264 -profile:v baseline -crf 23 -sc_threshold 0 -g 25 -r 25 -c:a aac -ar 48000 \
-filter:v:0 scale=w=842:h=480:force_original_aspect_ratio=decrease,drawtext=:"text=(480p):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:0 1500k -b:a:0 96k \
-filter:v:1 scale=w=1280:h=720:force_original_aspect_ratio=decrease,drawtext=:"text=(720p):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:1 3000k -b:a:1 128k \
-filter:v:2 scale=w=1920:h=1080:force_original_aspect_ratio=decrease,drawtext=:"text=(1080p):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:2 6000k -b:a:2 128k \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
-hls_flags independent_segments -hls_flags +program_date_time -hls_segment_type mpegts -hls_time 1 -hls_list_size 5 \
-format_options "movflags=+cmaf" \
-master_pl_name master.m3u8 \
-hls_segment_filename "/var/www/html/media/$TODAY/$PROGRAMNAME/hls/segment-%v_%08d.ts" "/var/www/html/live/$TODAY/$PROGRAMNAME/hls/child-%v.m3u8"
DASH 用スクリプト作成
#!/bin/bash
TODAY=$(date '+%Y%m%d')
echo "Input your live program name:"
read PROGRAMNAME
mkdir /var/www/html/live/$TODAY
mkdir /var/www/html/live/$TODAY/$PROGRAMNAME
mkdir /var/www/html/live/$TODAY/$PROGRAMNAME/dash
/root/ffmpeg-n5.1.3-linux64-gpl-5.1/bin/ffmpeg \
-i udp://:5000 \
-preset ultrafast -tune zerolatency -flags +global_header \
-c:v libx264 -pix_fmt yuv420p -c:a aac -b:a 128k -ac 1 -ar 44100 \
-use_timeline 0 -use_template 1 -utc_timing_url "https://time.akamai.com/?iso" -index_correction 1 \
-live 1 -streaming 1 -f dash -seg_duration 1 -dash_segment_type mp4 \
-ldash 1 -target_latency '0.5' -write_prft 1 -window_size 3 \
-force_key_frames "expr:gte(t,n_forced*1)" \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
-map v:0 -filter:v:0 "scale=-1:1080,drawtext=:text=(6000k):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:0 6000k -s:v:0 1920x1080 -profile:v:0 baseline \
-map v:0 -filter:v:1 "scale=-1:720,drawtext=:text=(3000k):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:1 3000k -s:v:1 720x480 -profile:v:1 baseline \
-map v:0 -filter:v:2 "scale=-1:360,drawtext=:text=(1500k):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:2 1500k -s:v:2 640x360 -profile:v:2 baseline \
-map 0:a \
-init_seg_name init-stream_r\$RepresentationID\$.mp4 -media_seg_name media-stream_r\$RepresentationID\$-\$Number%08d\$.mp4 \
-format_options "movflags=+cmaf" \
/var/www/html/live/$TODAY/$PROGRAMNAME/dash/manifest.mpd
両方同時出力用スクリプト作成
#!/bin/bash
TODAY=$(date '+%Y%m%d')
echo "Input your live program name:"
read PROGRAMNAME
mkdir /var/www/html/live/$TODAY
mkdir /var/www/html/live/$TODAY/$PROGRAMNAME
mkdir /var/www/html/live/$TODAY/$PROGRAMNAME/both
/root/ffmpeg-n5.1.3-linux64-gpl-5.1/bin/ffmpeg \
-i udp://:5000 \
-preset ultrafast -tune zerolatency -flags +global_header \
-c:v libx264 -pix_fmt yuv420p -c:a aac -b:a 128k -ac 1 -ar 44100 \
-use_timeline 0 -use_template 1 -utc_timing_url "https://time.akamai.com/?iso" -index_correction 1 \
-live 1 -streaming 1 -f dash -seg_duration 1 -dash_segment_type mp4 \
-ldash 1 -target_latency '0.5' -write_prft 1 -window_size 3 \
-force_key_frames "expr:gte(t,n_forced*1)" \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
-map v:0 -filter:v:0 "scale=-1:1080,drawtext=:text=(6000k):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:0 6000k -s:v:0 1920x1080 -profile:v:0 baseline \
-map v:0 -filter:v:1 "scale=-1:720,drawtext=:text=(3000k):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:1 3000k -s:v:1 720x480 -profile:v:1 baseline \
-map v:0 -filter:v:2 "scale=-1:360,drawtext=:text=(1500k):fontsize=32:x=32:y=32:box=1:boxcolor=white:fontcolor=black" \
-b:v:2 1500k -s:v:2 640x360 -profile:v:2 baseline \
-map 0:a \
-init_seg_name init-stream_r\$RepresentationID\$.mp4 -media_seg_name media-stream_r\$RepresentationID\$-\$Number%08d\$.mp4 \
-format_options "movflags=+cmaf" \
-hls_playlist 1 -hls_master_name master.m3u8 -hls_flags independent_segments -hls_flags program_date_time \
/var/www/html/live/$TODAY/$PROGRAMNAME/both/manifest.mpd
各コンテンツ生成用シェルスクリプトを実行可能にします。
chmod +x hls-publish.sh dash-publish.sh both-publish.sh
これで SRT Listener 及び FFmpeg パッケージング処理の準備が完了しました。
SRT Caller 用 OBS の準備
動画のインジェスト元となるローカル PC に、SRT Caller として利用する OBS をインストールします。OBS は OBS ホームページでダウンロードできます。
OBS ホームページ
OBS をインストール後、実行すると下記の画面が現れますが、いくつか事前にセッティングをしておく必要があります。
右下の「Settings」から「Stream」を選択し、SRT Listener になる宛先アドレス情報を入力します。Protocol は SRT、Listener となる Linode VM の IP と SRT としてオープンした Port を入力します。
主な設定変更内容は下記の通りです。
- Output Mode : Advanced 選択
- Rescale Output : チェックして1920x1080を入力
- Bitrate : 6000 Kbps
- Keyframe Interval : 2 s
- CPU Usage Preset : ultrafast
- Tune : zerolatency
「Video」に移動しデフォルト画像度を1920x1080に変更します。
「Advanced」に移動し Stream Delay を無効 (Enable 解除) にします。
ここまでの設定が完了したら「Settings」メニューの「Apply → OK」で変更内容を適用します。
次は、動画データのソースを準備します。本記事ではウェブブラウザーで「https://time.is」を開き、そのウィンドウの表示内容をソースデータとして利用します。まず、ウェブページを開きます。
OBS メイン画面の「Sources → +」ボタンをクリックし「Window Capture」を選択します。
「Create new - Window Capture」のまま「OK」を選択します。
ソースとして利用する Window を選択します。ここでは Chrome を利用しているため「Window」の部分で該当しているものを選択します。正しく選択できれば、上のプレビューの部分にブラウザーの画面が映りますので、その状態で「OK」をクリックします。
これで SRT Listener としての OBS セッティングを完了しました。
動作の確認
SRT データ送受信確認
では OBS メイン画面の右下メニュー「Start Streaming」ボタンをクリックし SRT データ送信を行います。OBS 最下段のテキスト情報で Streaming 状態が表示されます。
OBS Streaming を止める場合は「Stop Streaming」をクリックします。
SRT のデータ送信を確認できたら、Linode VM 側で その動画コンテンツを受信できているかを確認します。
systemctl status rc-local
(以下内容が表示される)
● rc-local.service - /etc/rc.local Compatibility
Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled)
Drop-In: /usr/lib/systemd/system/rc-local.service.d
└─debian.conf
Active: activating (start) since Tue 2023-09-26 12:07:01 UTC; 17min ago
Cntrl PID: 614 (rc.local)
Tasks: 6 (limit: 4558)
Memory: 5.7M
CPU: 5.551s
CGroup: /system.slice/rc-local.service
├─614 /bin/bash /etc/rc.local start
└─618 /root/srt-1.5.3/_build/srt-live-transmit srt://:2000 udp://127.0.0.1:5000 -v
Sep 26 12:07:01 localhost rc.local[618]: Binding a server on :2000 ...
Sep 26 12:07:01 localhost rc.local[618]: listen...
Sep 26 12:20:10 localhost rc.local[618]: accept...
Sep 26 12:20:10 localhost rc.local[618]: connected.
Sep 26 12:20:10 localhost rc.local[618]: 12:20:10.917589/srt-live-transm*E:SRT.ea: remove_usock: @617192981 not found as either socket or group. Removing only from epoll system.
Sep 26 12:20:10 localhost rc.local[618]: Accepted SRT source connection
Sep 26 12:20:11 localhost rc.local[618]: 12:20:11.917685/SRT:GC*E:SRT.ei: epoll/update: IPE: update struck E1 which is NOT SUBSCRIBED to @617192981
Sep 26 12:20:11 localhost rc.local[618]: 12:20:11.917738/SRT:GC*E:SRT.ei: epoll/update: IPE: update struck E1 which is NOT SUBSCRIBED to @617192981
Sep 26 12:20:21 localhost rc.local[618]: 12:20:21.837574/SRT:TsbPd!W:SRT.br: @617192980: RCV-DROPPED 4 packet(s). Packet seqno %659043764 delayed for 0.429 ms
Sep 26 12:20:23 localhost rc.local[618]: 12:20:23.231547/SRT:TsbPd!W:SRT.br: @617192980: RCV-DROPPED 11 packet(s). Packet seqno %659043949 delayed for 0.331 ms
次は、事前に作成したシェルスクリプトを利用して HLS と DASH 両方のパッケージング処理を行います。
ソース動画データのインジェスト (Linode VM への伝送) を FFmpeg パッケージング処理より先に始めた場合、一時的に下記のようなエラーが発生します。本エラーは受信中のソース動画データからキーフレームが見つからないためにパッケージング処理を行えないというメッセージです。しばらく時間が過ぎるとソース動画データのキーフレームを FFmpeg が見つけ、その部分からパッケージング処理が始まります。
[h264 @ 0x564d36c9b9c0] decode_slice_header error
[h264 @ 0x564d36c9b9c0] no frame!
[h264 @ 0x564d36c9b9c0] non-existing PPS 0 referenced
FFmpeg スクリプト実行
事前に用意したスクリプト (同時出力用) を利用し FFmpeg で HLS 及び DASH としてコンテンツ生成を開始します。HLS のみ及び DASH のみのスクリプトは本記事では解説しませんが、必要に応じて利用してください。
./both-publish.sh
Input your live program name: {任意の Program Name を入力}
無事にスクリプトが実行されれば、下記のように HLS 及び DASH としてのコンテンツファイルが出力されることを確認できます。
Input #0, mpegts, from 'udp://:5000':
Duration: N/A, start: 0.000000, bitrate: N/A
Program 1
Metadata:
service_name : Service01
service_provider: FFmpeg
Stream #0:0[0x100]: Video: h264 (Constrained Baseline) ([27][0][0][0] / 0x001B), yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 29.97 fps, 29.97 tbr, 90k tbn
Stream #0:1[0x101]: Audio: aac (LC) ([15][0][0][0] / 0x000F), 48000 Hz, stereo, fltp, 162 kb/s
Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stream #0:0 -> #0:1 (h264 (native) -> h264 (libx264))
Stream #0:0 -> #0:2 (h264 (native) -> h264 (libx264))
Stream #0:1 -> #0:3 (aac (native) -> aac (native))
Press [q] to stop, [?] for help
[Parsed_drawtext_1 @ 0x55a7ee5c4fc0] Using "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
[libx264 @ 0x55a7ee4fa100] using SAR=1/1
[libx264 @ 0x55a7ee4fa100] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x55a7ee4fa100] profile Constrained Baseline, level 4.0, 4:2:0, 8-bit
[libx264 @ 0x55a7ee4fa100] 264 - core 164 - H.264/MPEG-4 AVC codec - Copyleft 2003-2023 - http://www.videolan.org/x264.html - options: cabac=0 ref=1 deblock=0:0:0 analyse=0:0 me=dia subme=0 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=2 lookahead_threads=2 sliced_threads=1 slices=2 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0 keyint=250 keyint_min=25 scenecut=0 intra_refresh=0 rc=abr mbtree=0 bitrate=6000 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=0
[Parsed_drawtext_1 @ 0x55a7ef58a340] Using "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
[libx264 @ 0x55a7ee4f68c0] using SAR=32/27
[libx264 @ 0x55a7ee4f68c0] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x55a7ee4f68c0] profile Constrained Baseline, level 3.0, 4:2:0, 8-bit
[libx264 @ 0x55a7ee4f68c0] 264 - core 164 - H.264/MPEG-4 AVC codec - Copyleft 2003-2023 - http://www.videolan.org/x264.html - options: cabac=0 ref=1 deblock=0:0:0 analyse=0:0 me=dia subme=0 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=2 lookahead_threads=2 sliced_threads=1 slices=2 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0 keyint=250 keyint_min=25 scenecut=0 intra_refresh=0 rc=abr mbtree=0 bitrate=3000 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=0
[Parsed_drawtext_1 @ 0x55a7efec5100] Using "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
[libx264 @ 0x55a7ee4f2680] using SAR=1/1
[libx264 @ 0x55a7ee4f2680] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x55a7ee4f2680] profile Constrained Baseline, level 3.0, 4:2:0, 8-bit
[libx264 @ 0x55a7ee4f2680] 264 - core 164 - H.264/MPEG-4 AVC codec - Copyleft 2003-2023 - http://www.videolan.org/x264.html - options: cabac=0 ref=1 deblock=0:0:0 analyse=0:0 me=dia subme=0 psy=1 psy_rd=1.00:0.00 mixed_ref=0 me_range=16 chroma_me=1 trellis=0 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=0 threads=2 lookahead_threads=2 sliced_threads=1 slices=2 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=0 weightp=0 keyint=250 keyint_min=25 scenecut=0 intra_refresh=0 rc=abr mbtree=0 bitrate=1500 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=0
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/init-stream_r0.mp4' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/init-stream_r1.mp4' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/init-stream_r2.mp4' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/init-stream_r3.mp4' for writing
Output #0, dash, to '/var/www/html/live/20230930/test001/both/manifest.mpd':
Metadata:
encoder : Lavf59.27.100
Stream #0:0: Video: h264, yuv420p(tv, bt709, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 6000 kb/s, 29.97 fps, 30k tbn (default)
Metadata:
encoder : Lavc59.37.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/6000000 buffer size: 0 vbv_delay: N/A
Stream #0:1: Video: h264, yuv420p(tv, bt709, progressive), 720x480 [SAR 32:27 DAR 16:9], q=2-31, 3000 kb/s, 29.97 fps, 30k tbn
Metadata:
encoder : Lavc59.37.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/3000000 buffer size: 0 vbv_delay: N/A
Stream #0:2: Video: h264, yuv420p(tv, bt709, progressive), 640x360 [SAR 1:1 DAR 16:9], q=2-31, 1500 kb/s, 29.97 fps, 30k tbn
Metadata:
encoder : Lavc59.37.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/1500000 buffer size: 0 vbv_delay: N/A
Stream #0:3: Audio: aac (LC), 44100 Hz, mono, fltp, 128 kb/s
Metadata:
encoder : Lavc59.37.100 aac
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/media-stream_r3-00000001.mp4.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/manifest.mpd.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/master.m3u8.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/media-stream_r0-00000001.mp4.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/manifest.mpd.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/media-stream_r1-00000001.mp4.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/manifest.mpd.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/media-stream_r2-00000001.mp4.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/manifest.mpd.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/media-stream_r3-00000002.mp4.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/manifest.mpd.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/media_3.m3u8.tmp' for writing
[dash @ 0x55a7ee4e7b40] Opening '/var/www/html/live/20230930/test001/both/media-stream_r0-00000002.mp4.tmp' for writing
(以下省略)
FFmpeg により払い出される動画コンテンツファイルの置き場のネーミングルールとして、Linux のシステム時刻データ (date) から年月日 (YYYYMMDD) 部分を抜き出したものをディレクトリー名として利用する形にしています。また、その時刻データは UTC (+00:00) を基準にしています。
同じ日 (UTC) で複数のライブ配信を行う場合、一度利用した Program Name の再利用はできません。Program Name が重複すると新旧のライブ動画コンテンツデータが混ざってしまうため、正しいライブ動画の配信ができなくなります。
プレイヤー再生確認
最後、プレイヤーを利用してライブ動画の再生確認を行います。ここではウェブブラウザーで利用可能な標準プレイヤーを利用します。
■ HLS プレイヤーで再生確認
標準 HLS プレイヤー (hls.js)
再生用プレイバック URL - https://{Hostname}/live/{Date}/{Program Name}/both/master.m3u8
■ DASH プレイヤーで再生確認
標準 DASH プレイヤー (dash.js)
再生用プレイバック URL - https://{Hostname}/live/{Date}/{Program Name}/both/manifest.mpd
下記はプレイヤー再生画面の例です。上の大きな画面がソースとなる部分で、左下が HLS、右下が DASH プレイヤーでの再生となります。
プレイヤーでの再生確認が完了したら、FFmpeg の処理や OBS でのデータ送信を止めます。
FFmpeg 処理を止める方法
- キーボードボタン「Q」入力でパッケージング処理終了可能
- 「Q」で終了できない場合、Ctrl+C で強制終了
古いコンテンツの自動削除
これで SRT でインジェストしたソースデータで HLS 及び DASH としてのライブ動画配信ができることを確認しました。ただ、この構成ではライブ配信用の動画コンテンツを Linode VM に紐づいた Block Storage に保存し続けてしまいます。Block Storage の空き容量がなくなると正常な動作ができなくなります。その問題を防止するため、指定した期限を過ぎた古い動画コンテンツファイルを自動削除するシェルスクリプトを作成します。
下記は出力後30日を経過した動画コンテンツが保存されているディレクトリーを自動削除するスクリプトの例です。前述で説明したディレクトリーの構造を利用したものです。
#!/bin/bash
# Define the directory where the old directories are located
dir_to_clean="/var/www/html/live"
# Calculate the cutoff date (30 days ago)
cutoff_date=$(date -d "30 days ago" +%Y%m%d)
# Remove directories older than the cutoff date
find "$dir_to_clean" -type d -name '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' -maxdepth 1 | \
while read -r dir; do
dir_name=$(basename "$dir")
if [ "$dir_name" -le "$cutoff_date" ]; then
rm -r "$dir"
echo "Removed directory: $dir"
fi
done
30日以上前のデータ削除ではなく、7日以上前のデータを削除したい場合は下記の「"30 days ago"」を「"7 days ago"」に変更します。
cutoff_date=$(date -d "7 days ago" +%Y%m%d)
このスクリプトを Linux に毎日0時0分に自動実行させるようにするため、cron を利用します。
chmod +x cleanup.sh
crontab -e
(以下が表示される)
no crontab for root - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.basic
3. /usr/bin/vim.tiny
4. /bin/ed
Choose 1-4 [1]: {このまま Enter}
(nano 編集機で cron を修正できる。下記コードを入力し保存する)
0 0 * * * /root/cleanup.sh
これで古いコンテンツを自動削除できるようになったため、Linode VM に紐づいた Block Storage の容量を管理しやすくなります。
高画質かつ複数の ABR (Adaptive Bitrate) スタックを持つ動画を長時間出力し続ける場合 (≒Linear 配信サービス)、30日より短い間隔でのコンテンツ削除や、Linode VM に紐づく Block Storage 容量の拡張が必要です。
まとめ
ここまでの手順を実行することにより、下記の結果を得ることができました。
- 不安定なインターネット区間でのソース動画データを安定的に伝送 → SRT (OBS + SRT Binary)
- 受信したソースの動画データをウェブ配信に適したフォーマット (HLS 及び DASH) としてリアルタイム変換 → FFmpeg
- ウェブサーバーを構築しライブ動画配信サーバーを構築 → Nginx
- HTTPS 通信用の証明書を発行 → Let's Encrypt
- 古い動画コンテンツを自動削除し、費用がかかるストレージの利用量を削減 → rc-local
- これらすべてをクラウド上の VM 一つの中で安価に実現 → Linode VM
構築に利用したソフトウェアはすべて無償利用が可能なものであり、ほぼ費用をかけず Linode VM 上で動作させることができました。今後、本記事の事例以外にも様々なユースケースが発掘されることを期待します。