DJI Osmo Action 4 のライブ機能でツイキャスできない話
【理由】Osmo Action 4 側が H.264 で雑な(適切でない) Level を付けるからツイキャスが拒否る
判明した原因から先にお伝えしますと… Osmo Action 4 側のコーデックが「H.264 High 5.2」でストリームするのに対し、ツイキャスは「H.264 High 4.2」までしか受けられないらしく、サーバ側から接続を切られます。
ツイキャス側のバリデーションが厳しい? と思う半面、よくよく考えるとライブ配信機能では FullHD までしか対応していないのに 4K 相当を意味する Level を付与している時点でおかしくね? という話になります。
DJI さん、なんでそんな適当なことするの…。
改めまして
というわけで dyrmahiiragi こと柊☆でぃるまと申します。
私は普段「ニコ生」ことニコニコ生放送で休日にドライブの様子を垂れ流す車載配信の生主です。
…が、皆様ご承知の通り 2024 年 6 月 8 日未明からランサムウェアを含む大規模サイバー攻撃を受け長期間のサービス停止を余儀なくされ、現在復旧に向けた対応が続けられています。
一方、生主界隈ではニコニコ復旧を待ちつつ、その間は避難所や新天地的な感じで他社ライブ配信サービスを使って配信を行っているのが現状です。いわば「ニコ生流浪者(エグザイル)の民」とでも言えばいいんですかね?
そんな流浪の生主達の多くが流れ着いた地が「ツイキャス」です。私も今回そこで車載配信を行おうと考えました。
さて、ツイキャスでライブ配信をしようと思ったら通常は以下の手段を取るのが最速最短で無難な線だと思います。
「ツイキャス ライブ」アプリでスマホ配信
PC+WebCam+OBS Studio 構成で配信する「ツール配信」
自分もそれに倣えば何も苦労はない…はずなのですが、これまでの配信環境を維持したままどうにかしたい…と思うのが人の常でありましょう。
私の配信手段はこれです。
DJI Osmo Action 4 のライブ配信機能を使う
ある意味ド変態構成ですが、車内には必要最小限の機材を持ち込むだけにしたい、という思想からこの構成にしています。
まあ普通こう考えますよね。
「ニコ生が配信できるんだから、ツイキャスだって行けるっしょ!?」
地獄の始まりでした。
DJI Osmo Action 4 からツイキャス配信するために
では、実際問題 Osmo Action 4 を使ってツイキャス配信をするにはどうすれば良いのか?
いろいろ試しましたので書き連ねます。
【ケース#1】PCとUSB接続して「ウェブカメラ」モードを使う
手っ取り早い方法がこちらです。
OsmoAction4 の USB-C ポートを使って PC と USB 接続する
認識すると Osmo Action 4 の画面が接続モード選択になる
「ウェブカメラ」をタップする
ツイキャスをブラウザで開き、配信設定を行う
ブラウザ配信画面で「カメラ」に切り替える
「デバイス設定」でカメラとマイクのデバイスを選択する
配信する
上記は「ブラウザ配信」を使っていますが、OBS Studio 等のPC配信ツールに慣れている方ならそちらを活用して「ツール配信」するのが正攻法かと。普通に映像キャプチャデバイスとして認識するはずです。
一方デメリットとしては、車内に持ち込めるそこそこスペックのノートPCが必要なこと。固定スタンドと電源のやり繰りも考慮する必要があるので、それらの装備を持っていない方は結構痛い出費になるかと。
(私は初代 NANOTE で試しましたが、正直スペック不足でした…)
あと普段 Osmo Action 4 に純正 3.5mm オーディオアダプタを装着している方は外してください。PCとUSB接続した際にカメラ側の接続認識がうまく行かず詰みます。よって有線マイクが使えません。PCのマイク端子がヘッドホン端子と分離しているPCならばそちらに繋げば問題ないかと思います。
ぶっちゃけ DJI Mic 2 が欲しくなりましたが、予算がなく断念しました。泣ける。
【ケース#2】ノートPCを簡易RTMPプロキシサーバにする
これも車内に持ち込めるそこそこスペックのPCが必要になりますが、ウェブカメラモードではなく Osmo Action 4 のライブ配信モードでストリームをPCに中継させて、極力ツイキャスが望む永遠方式で配信する方法です。
PC に FFmpeg をインストールする
Powershell とかで「winget install Gyan.FFmpeg」あたりがお手軽
公式は こちら
FFmpeg を Listen モードで実行する
コマンドラインは下記「FFmpeg コマンドライン例」を参照
<RTMP URL> 部分はツイキャス指定のRTMP URL をもとに以下の通り "live" と "?" の間に "/" + "ストリームキー" を挿入したもの
rtmp://rtmp401.twitcasting.tv/live/ストリームキー?key=hogehogeho
スマホ等の DJI MIMO アプリから Osmo Action 4 に接続し、ライブ配信設定を行う
RTMP配信を選択
URLは以下を入力
rtmp://PCのIPアドレス/live/ストリームキー
配信開始する
#「FFmpeg コマンドライン例」
# FFmpeg を Listen モードで起動し、ツイキャスへ中継させる
ffmpeg.exe -f flv -listen 1 -i rtmp://0.0.0.0:1935/live -c:v libx264 -profile:v high -level:v 4.2 -preset veryfast -bf 0 -s 1280x720 -crf 24 -c:a copy -force_key_frames "expr:gte(t,n_forced*2)" -f flv <RTMP URL>
先述の通り、FFmpeg では Osmo Action 4 からのストリーム待ち受けとツイキャス向けの変換処理を以下の通り行っています。
ストリームを H264 High 4.2 に付け替え
Bフレームを 0 に
動画サイズを 1280 x 720 (HD) に
CRF(動画出力の品質レベル)を 24 に
キーフレームを 2 秒おきに挿入
なおこの方法だとワンライナーでお手軽ではあるものの、RTMP接続が終了すると FFmpeg 自体も終了してしまいます。切れる度に都度コマンドを実行して…というのはさすがに手間ですよね?
そこで、以下のような Powershell スクリプトを作成し、Ctrl+C を押すまで無限に FFmpeg が何度でも蘇るようにしました。ブラックだ。狂ってる。
# tinyrtmpproxy.ps1
$remoteUrl = "<ツイキャスのRTMP URL>"
$streamKey = "<ストリームキー>"
$parse = $remoteUrl.split("?")
$url = $parse[0] + "/" + $streamKey + "?" + $parse[1]
Write-Host "[接続先] $url"
while ($true) {
ffmpeg.exe -f flv -listen 1 -i rtmp://0.0.0.0:1935/live -c:v libx264 -profile:v high -level:v 4.2 -preset veryfast -bf 0 -s 1280x720 -crf 24 -c:a copy -force_key_frames "expr:gte(t,n_forced*2)" -f flv $url
}
あと注意点としては、回線にPocketWifi 等のモバイルルーターを利用している場合、機種によっては接続端末同士のLAN接続ができないものがあり Osmo Action 4 - PC 間で繋げられないケースがあります。手持ち端末では Galaxy 5G Mobile Wi-Fi (SCR01) がそれでした。泣ける。
【ケース#3】クラウドに RTMP Proxy サーバ立てた
むしろ真っ先に手を付けたのはこっちで、冒頭で述べた原因もこのサーバ構築過程で判明に至っています。
詳細な構築過程は省きますが、サーバの構成は大体以下の通りです。
さくらのクラウド プラン/3core-2GB/SSD 20GB x 1台
通信制限はクラウド内のパケットフィルタ機能を活用
24 時間あたり 253 円、月額 5,060 円
Ubuntu 22.04.4 LTS 64bit
ffmpeg 4.4.2 (apt)
nginx 1.26.1 + nginx-rtmp-module (.tar.gz)
nginx 一式は /usr/local/nginx/ に入ります。
色々先人のお知恵を拝借しながら調整した conf/nginx.conf がこちら。
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
server_name localhost;
# rtmp stat
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /usr/local/nginx/html;
}
# rtmp control
location /control {
rtmp_control all;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
rtmp {
log_format main '$remote_addr [$time_local] $command "$app" "$name" "$args" - '
'$bytes_received $bytes_sent "$pageurl" "$flashver" ($session_readable_time) '
'"$swfurl" "$tcurl"';
access_log logs/access.log main;
server {
listen 1935;
# incoming from ActionCam
application osmo4 {
live on;
record off;
allow publish all;
exec_push /usr/bin/ffmpeg -i rtmp://127.0.0.1/osmo4/$name -c:v libx264 -profile:v high -level:v 4.2 -preset veryfast -bf 0 -s 1280x720 -crf 24 -c:a copy -force_key_frames expr:gte(t,n_forced*2) -f flv rtmp://127.0.0.1/twitcastlive/$name 2>&1 1>/usr/local/nginx/logs/ffmpeg.log;
}
# outgoing to twitcastlive server
application twitcastlive {
live on;
record off;
allow publish 127.0.0.1;
deny publish all;
push <ツイキャスの RTMP URL>;
}
}
}
application osmo4 では前述と同様に Osmo Action 4 からのストリーム待ち受けとツイキャス向けの変換処理を行い、一旦内部の application twitcastlive へ中継しています。
application twitcastlive はツイキャスサーバへの接続のみ担当しています。
次に Osmo Action 4 側の配信設定を行います。ストリームキーは Osmo Action 4 側に設定する配信 URL に入れ込む形です。
スマホ等の DJI MIMO アプリから Osmo Action 4 に接続し、ライブ配信設定を行う
RTMP配信を選択
URLは以下を入力
rtmp://サーバのIPアドレス/osmo4/ストリームキー
配信開始する
これで Osmo Action 4 からツイキャスに配信できるようになりました。
デメリットとしては、それなりにサーバ費用が発生すること。多少面倒ですが、配信する日だけ起動する運用にすると経済的です。
一方この形であればニコ生の時と大体同じ運用で車載配信できる環境となります。(「100%」と言えないのは、コメビュ(読み上げ)問題@iOS があるんで…)
ツイキャスは原則 OsmoAction4 非対応です
今回の試行錯誤の過程にて、ツイキャスのサポートにも問い合わせました。大抵は門前払いになるところ、サポートからの回答は上記の旨を言及しつつも RTMP URL の基本的な仕様についてシンプル丁寧にご教示いただき、重要な手がかりとなりました。この場をお借りして御礼申し上げます。ありがとうございました。
この流れでついでに理想を言ってしまえば Osmo Action 4 から配信する際に H.264 High 4.2 でストリームを流せるようになれば万事解決なのですが、それをメーカーの DJI に要求するのも酷かなと思います。費用対効果無さそうですし、あくまでライブ配信はおまけ機能な側面もあると思いますし。
(実際、他社 Insta360 の最近のモデルはライブ配信機能が無いです)
同様にして H.264 High 5.2 に対応するようツイキャスへ求めるのも酷かなと思います。仮にツイキャスが 4K 配信に対応すれば可能性無きにしもあらずですが、過度な期待は禁物でしょう。
というわけでタイトルでは「できない」とか言っておきながら、何とか「できるようにした」お話でした。ありがとうございました。
あとは…コメビュ(読み上げ)問題@iOS をどうするかだ…無理だわ(絶望)。