背景#
2019 年、一代致迅影眸图传が発表されました。自媒体が急成長する時代に、カメラのさまざまなアクセサリーも次々と登場し、三軸スタビライザー、映像伝送、マイク… 大小さまざまなメーカーが参入し、多くの粗雑な製品も生まれました。影眸図伝は致迅の第一世代の映像伝送製品であり、小規模な工房の雰囲気が見受けられますが、良い点も多く、デザインは際立っていませんが非常に実用的で、使用体験も非常に良好です:遅延が低く、映像伝送を見ながらフォーカスを合わせることができ、外部電源なしで 3〜4 時間使用できます。発売時の 799 元の価格と、後の 300 元未満の中古価格を考慮すると、今でも実用性を失っていません。
映像伝送の方式として、影眸は Wi-Fi 映像伝送に属し、専用の受信機は装備されていません。カメラなどの HDMI 信号源を映像伝送送信機に接続し、受信にはモバイルデバイスの Wi-Fi で映像伝送に接続し、専用アプリを通じて画像をリモートで確認します。Wi-Fi を採用することは、低価格の映像伝送に適した選択肢であり、一般的な Wi-Fi ネットワークカードは専用の無線方式に比べて開発の難易度とコストが大幅に低くなります。Wi-Fi 以外では、1080P のビデオストリーム伝送を満たす 5Mbps 以上の速度を安価に実現する方法はほとんどありません。低価格の映像伝送にとって、専用の受信機を備えないことは、ほぼ半分のハードウェアコストを節約でき、スマートフォンやタブレットの既存の画面を利用できるため、追加の表示デバイスを購入する必要がなく、購入と使用のハードルが下がり、影眸は個人や小規模チームに非常に適しています。ただし、大型モニターや放送台との接続の機会を失うという代償があります。
影眸を研究する目的は、どのようにして良好な体験を持つ Wi-Fi 映像伝送を実現しているのかを理解することでした。影眸が送信するデータストリームを正しく解析できるようになった後、受信側を「カスタマイズ」し、受信機がないために大型画面やライブ配信に接続できない問題を解決し、全志 D1 に基づく受信機のプロトタイプと OBS Studio プラグイン受信の 2 つの方案を実現しました。研究の過程で、影眸を選ぶことはソフトな柿を選ぶことでもあると気づきました:海思のハードウェアソリューション、Wi-Fi の伝送方式、簡素な初版受信アプリなど、利用できる手段は非常に豊富で、ちょうど学びや練習に適していました。以下はその全過程の記録です。
ハードウェア#
正面のネジを外すと影眸の外装を開けることができ、ハードウェアの構成が見えます。以下は主要なチップと基本情報です:
- 主チップ:海思 Hi3516ARBCV100 SoC
- インターフェースチップ:聯陽 IT6801FN HDMI - BT.1120
- RAM:海力士 H5TC2G63GFR DDR3L
- Flash:旺宏 MX25L12835F 16MB SPI Flash
- ネットワークカード:欧飛信 8121N-UH モジュール、高通 Atheros AR1021X チップ、2x2 802.11 a/n 5G、USB 2.0 インターフェース
- MCU:意法 STM32F030C8T6
主チップの海思 Hi3516A は、プロセッサが単核 Cortex-A7 で、公式には Linux 3.4 カーネルに基づく SDK を提供しており、H.264/H.265 ビデオハードウェアコーデックを備えています。公式には「新世代 ISP を統合したプロフェッショナル HD IP カメラ SoC」として位置付けられ、監視分野で広く使用されています。また、低コストの映像伝送の本質を明らかにしました —— 監視ソリューションの基盤の上に、画像センサーの入力を HDMI 入力に置き換えることで映像伝送が実現されました。監視ソリューションの出荷量は非常に多く、この SoC は監視に不要な表示などの周辺機器を取り除くことができ、コストを非常に低く抑えることができます。HDMI 入力を SoC 上で広く利用されている MIPI CSI、BT.1120 などのインターフェースに変換する方法はやや特殊ですが、少なくとも既存の IC を選択することができます。類似の考え方の製品にはこのようなHDMI エンコーダがあり、同様に海思のソリューションを利用しています。外部 Wi-Fi ネットワークカードを必要とする Wi-Fi 映像伝送に比べて、これらは SoC 内蔵の GMAC を利用でき、外部にイーサネット PHY チップを接続するだけで有線ネットワーク接続を実現でき、HDMI をキャプチャし、安定してライブストリーミングを行うことができます。
影眸のハードウェアソリューションで私が最も驚いたのは、わずか 16MB の Flash です:Linux を実行するデバイスがわずか 16MB のスペースを必要とするとは。しかし冷静に分析すると、多くの OpenWRT を実行するルーターも 16MB、さらには 4MB の Flash しか必要としません。ビデオ処理のスペースに対する要求は主に RAM に依存します。Debian などのディストリビューションの豊富なソフトウェアパッケージを捨て、特定のタスクに特化した Linux を使用すれば、非常に小さなスペースを占有することができます。
ボード環境#
ブート#
海思チップを使用しているため、基本的には公式 SDK に基づいて開発されます。ボード上のシリアルポート R、T、G の 3 つのはんだ付けポイントから線を引き出し、電源を入れると U-Boot と HiLinux のブートログが表示され、やはり純正の海思です。
U-Boot でprintenv
を実行すると、起動カーネルのコマンドとカーネルに渡されるパラメータを取得できます。出力から SPI Flash のレイアウトは 1M(boot)、3M(kernel)、12M(rootfs)で、rootfs は 12MB の jffs2 ファイルシステムであることがわかります。ブートプロセスは、最初に SPI Flash(sf)デバイスを探査(probe)して Flash の情報を取得し、次に Flash からオフセット 0x100000(1MB)で 0x300000(3MB)サイズのカーネルをメモリアドレス 0x82000000 に読み込み、最後にbootm
コマンドを使用してメモリからカーネルを起動します。
bootfile="uImage"
bootcmd=sf probe 0;sf read 0x82000000 0x100000 0x300000;bootm 0x82000000
bootargs=mem=128M console=ttyAMA0,115200 root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(boot),3M(kernel),12M(rootfs)
システム起動後#
システムに入った後、ボード上で実行されている映像伝送プログラムをどのように見つけるのでしょうか?海思の開発環境ユーザーガイド文書によると、システム起動後に自動的に実行されるプログラムは/etc/init.d/rcS
に追加できます。したがって、/etc/init.d/rcS
を開いて確認します。
rcS
の主な内容は以下の通りです:
まず、カーネルのネットワークバッファを変更し、書き込みバッファを 0x200000(2MB)、読み取りバッファを 0x80000(512KB)に設定します:
#sys conf
sysctl -w net.core.wmem_max=2097152
sysctl -w net.core.wmem_default=2097152
sysctl -w net.core.rmem_max=524288
sysctl -w net.core.rmem_default=524288
無線ネットワークカードドライバをロードします。
insmod /ko/wifi/ath6kl/compat.ko
insmod /ko/wifi/ath6kl/cfg80211.ko
insmod /ko/wifi/ath6kl/ath6kl_usb.ko reg_domain=0x8349
ip/dhcp の設定
ifconfig wlan0 10.0.0.1 netmask 255.255.255.0 up
echo udhcpd
udhcpd /etc/wifi/udhcpd.conf &
#echo hostapd
#hostapd /etc/wifi/hostap.conf &
MPP ドライバをロードし、SDK 文書と一致します。
cd /ko
./load3516a -i -sensor bt1120 -osmem 128 -online
MPP をロードする際には主に初期化を行い、多くのカーネルモジュールをロードし、ログは以下のように出力されます。
Hisilicon Media Memory Zone Manager
Module himedia: init ok
hi3516a_base: module license 'Proprietary' taints kernel.
Disabling lock debugging due to kernel taint
load sys.ko for Hi3516A...OK!
load tde.ko ...OK!
load region.ko ....OK!
load vgs.ko for Hi3516A...OK!
ISP Mod init!
load viu.ko for Hi3516A...OK!
load vpss.ko ....OK!
load vou.ko ....OK!
load hifb.ko OK!
load rc.ko for Hi3516A...OK!
load venc.ko for Hi3516A...OK!
load chnl.ko for Hi3516A...OK!
load h264e.ko for Hi3516A...OK!
load h265e.ko for Hi3516A...OK!
load jpege.ko for Hi3516A...OK!
load vda.ko ....OK!
load ive.ko for Hi3516A...OK!
==== Your input Sensor type is bt1120 ====
acodec inited!
insert audio
==== Your input Sensor type is bt1120 ====
mipi_init
init phy power successful!
load hi_mipi driver successful!
その後、RtMonitor
という名前のプログラムが実行され、すべての映像伝送ビジネスロジックがその中で実現されます。
ボード環境の探索は非常にスムーズで、何の障害もなく、スクリプトにはデバッグ時に残されたコメント情報もありました。実際、海思の SDK 文書には各段階での暗号化手段が提供されており、シリアルポートの無効化や root アカウントのパスワード設定など、いずれかを適用すればかなりのトラブルを引き起こすことになります。
伝送#
パケットキャプチャ#
まず、Wi-Fi を通じてパケットをキャプチャして、どのようなデータが伝送されているのかを見てみましょう。ARM アーキテクチャの Mac 上に iOS のアプリをインストールできるため、Accsoon アプリを実行した後、Wireshark を開くとキャプチャを開始できます。3 種類のパケットが見つかります:
- 映像伝送→受信端 UDP:データ量が大きく、映像伝送データストリームであると推測されます;
- 受信端→映像伝送 UDP:非常に短く、データ応答パケットであると推測されます;
- 受信端→映像伝送 TCP:映像伝送インターフェースを開くと送信され、上記の UDP 伝送をトリガーし、その後約 0.5〜1 秒ごとに 1 パケットが送信され、ハートビートの保活であり、内容には "ACCSOON" という文字列があります。
ついでにリスニングモードでパケットをキャプチャします。複数のデバイスが接続されていると、Wi-Fi には高速なマルチキャスト / ブロードキャストメカニズムがないため、データを各デバイスに個別に送信する必要があり、信道の圧力が倍増します。
Wi-Fi 映像伝送のもう一つの欠点は、802.11 プロトコルを遵守し、フレーム間隔(interframe space)やバックオフ(backoff)ランダム数を変更せずに信道競争で不正な優位性を得ることがない場合、この映像伝送は他の Wi-Fi デバイスよりも高い伝送優先度を持たないことです。信道上で他の Wi-Fi デバイスが大量にアクティブな場合、映像伝送のカクつきを避けることはできません。しかし、5GHz 信道の混雑度は一般的に 2.4GHz よりも良好です。
Android APK の逆コンパイル#
パケットキャプチャだけではデータパケットの具体的な内容、特にヘッダーの各フィールドの意味を明確にするのは難しいため、致迅の Android アプリ内のロジックを分析しようとしました。更新されたバージョンは他のデバイスをサポートするためにコードが増えたため、古いバージョンの方が分析に適しています。apkpure から影眸映像伝送をサポートする古いバージョン(Accsoon 1.2.5 Android 版 APK)をダウンロードしました。Jadx を使用して apk を逆コンパイルし、主に以下の内容を探しました:
- UDP ビデオストリームデータパケットの構成、ビデオストリームを正しく解析するため;
- TCP 制御命令の内容と送信ロジック、デバイスが送信機能を開始するための正しいトリガーを得るため。
Java コードの重要なロジックの分析:
- MediaCodecUtil クラス
-
Android のネイティブコーデックインターフェース MediaCodec の操作をラップしています。
-
コンストラクタ内で MediaCodec を初期化し、初期化時のパラメータから、使用されるデコーダは "video/avc" であり、伝送されるビデオストリームは H.264 エンコードであることがわかります。
-
MediaCodec を初期化する際、
MediaCodec.configure
メソッドにSurface
を渡すと、MediaCodec
はデコードされたビデオフレームをそのSurface
のBufferQueue
に直接出力し、onFrameAvailable()
をコールバックします。 -
putDataToInputBuffer
メソッドは、MediaCodec の入力バッファに対応します。空のバッファを要求し、デコードが必要なデータをコピーしてから、入力バッファキューに入れます。 -
renderOutputBuffer
メソッドは、MediaCodec の出力バッファに対応します。出力バッファキューからデコードされたデータを取得し、そのバッファを解放します。
-
- MediaServerClass クラス
Start()
メソッドは、MediaRtms.Start()
とTcpLinkClass.StartMediaStream()
を呼び出し、それぞれ UDP と TCP を起動します。H264FrameReceiveHandle
はコールバック関数としてMediaRtms
のインスタンス化時に渡されます。H264FrameReceiveHandle
が呼び出されると、最終的にMediaCodecUtil
内のputDataToInputBuffer
とrenderOutputBuffer
が呼び出されます。
- MediaRtms クラス
rtmsBase
クラスの簡単なラップです。Start()
メソッドは、DatagramSocket
を作成し、udpRxThread
スレッドを起動します。このスレッド内で、データを受信し続け、一定の長さのデータを受信した後、パケットヘッダーを解析し、ビデオであればH264FrameReceiveHandle
コールバックを呼び出します。
- TcpLinkClass クラス
StartMediaStream()
を呼び出すと、KeepAliveThread
スレッドが起動します。このスレッド内で、1 秒ごとにTcpLinkClass
クラス内のStaOp
というメソッドを呼び出し、TCP 接続、ハートビートパケットの送信、接続の切断のプロセスを実装します。
- SurfaceRender クラス
- ビデオは
GLSurfaceView
コントロール上に表示されます。VideoMainActivity
内で、setRenderer
メソッドを呼び出し、SurfaceRender
をGLSurfaceView
のレンダラーとして設定します。 onSurfaceCreated
メソッドでは、OpenGL テクスチャ(mTextureId
)にバインドされたSurfaceTexture
(mSurfaceTexture
)を作成し、MediaCodec
がデコードしたビデオフレームを受信します。そして、オフスクリーンレンダリングに必要なフレームバッファオブジェクト(FrameBuffer)とテクスチャ(Texture)を作成し、効果処理の準備をします。onDrawFrame
メソッドでは、現在のフレームを描画します。updateTexImage
メソッドを呼び出して、SurfaceTexture
内の最新の画像フレームをバインドされた OpenGL テクスチャに更新します。この時、オフスクリーンレンダリングに切り替え、シェーダープログラムを利用してビデオフレームテクスチャと LUT テクスチャを重ね合わせて 3D LUT を適用し、通常のレンダリングに戻り、オフスクリーンレンダリングから得られたテクスチャを基に、シェーダープログラムを利用してゼブラライン、白黒などの効果を実現し、表示します。中心線、比率フレームなどの重ね合わせ要素は最後に個別に描画されます。
- ビデオは
具体的にパケットヘッダーの長さと情報を確認します。
TCP フレーム:
UDP フレーム:
各メッセージは 1 フレームのコーディングストリームを含み、各メッセージの前にはメッセージヘッダーがあります:
各メッセージは複数のフレームに分割されて送信され、各フレームの前にはフレームヘッダーがあります:
H.264 コーディングストリームの抽出#
データパケットの構造がわかったので、解析を開始できます。フレームを分割してメッセージを再構成した後、メッセージの内容は0x000001
の固定プレフィックスで始まり、NALU(Network Abstraction Layer Unit)の特徴を持ち、1 バイトの NALU ヘッダーを含み、重要なのはnal_unit_type
で、ペイロードの内容タイプを判断するために使用されます。理論的にはここまで来れば、メッセージの内容を一つずつデコーダに送信すればビデオストリームをデコードできます。しかし、デコーダは SPS と PPS に保存されたプロファイル、レベル、幅、高さ、デブロックフィルターなどのパラメータに依存して正しくデコードする必要があるため、I フレームの前にデコーダに知らせる必要があります。したがって、コード内ではnal_unit_type
に基づいて判断し、SPS と PPS を待つことが最善です。
NAL ヘッダー:
NALU タイプ:
受信端設計#
受信方案 1 - コンピュータ受信#
データパケットの構造が明らかになった後、正しくデータパケットを受信し、H.264 コーディングストリームをデコーダに送るだけです。効率的に開発とデバッグを行うために、まずコンピュータ上で行います。FFmpeg(libav)や GStreamer(libgst)などのマルチメディアフレームワークを利用すれば、デコードを簡単に実現できます。まず FFmpeg を使用して、主に以下のプロセスを経る必要があります:
- デコーダの初期化:
avcodec_find_decoder()
を使用して H.264 デコーダを検索し、avcodec_alloc_context3()
を使用してコンテキストを作成し、avcodec_open2()
を使用してデコーダを開きます。 - データのデコード:
av_packet_from_data()
を使用してデータをAVPacket
に格納し、avcodec_send_packet()
を使用してデコーダに送信し、avcodec_receive_frame()
を使用してデコードされたデータをAVFrame
から取得します。
全体のプログラムの大まかなロジックは次の通りです:
- メインスレッド:FFmpeg デコーダと SDL 表示を初期化し、UDP と TCP スレッドを起動します。その後、利用可能なデータの信号を待つループを実行し、データをデコードして表示します。
- UDP スレッド:パケットを受信し、各
msg_id
に対応するすべてのセグメントを収集し、完全な内容を共有メモリに組み合わせ、信号をメインスレッドに通知します。 - TCP スレッド:定期的にハートビートパケットを送信します。
映像伝送の Wi-Fi に接続し、ソフトウェアを実行します。映像伝送は RX0 小型カメラに接続し、カメラでスマートフォンのストップウォッチを撮影し、大まかな遅延テストを行います。左側には画面が表示され、スマートフォンの画面表示→RX0 が画面を撮影し HDMI 出力→HDMI 入力映像伝送→コンピュータが無線受信し表示します。エンドツーエンドの遅延は基本的に 200ms 程度です。
受信方案 2 - 開発ボード#
コンピュータ上で動作するプログラムができたので、これを組み込みハードウェアに移植する希望が出てきました。私は以前、全志 D1 の Mango Pi MQ-Pro D1 を手に入れており、HDMI 出力があり、H.264 ハードウェアデコーダがあり、完全な SDK と文書が開放されており、受信端の制作に必要なほとんどの要件を満たしています。残念ながら、Wi-Fi ネットワークカードは 2.4GHz しかサポートしておらず、5GHz 帯域をサポートする RTL8821CS ネットワークカードに交換し、対応するドライバをコンパイルする必要があります。
全志は D1 に Tina Linux SDK を提供しています。Tina の特徴は Linux カーネル + OpenWRT 構築システムに基づいており、AIoT 製品をより軽量にすることができます。OpenWRT のより広く知られた用途は、メモリとストレージが非常に限られたルーターです。宣伝によると、元々1GB DDR + 8GB eMMC が必要だったシステムが、Tina Linux システムを使用することで 64MB DDR + 128MB NAND Flash で済むようになります。
D1 チップには H.264 のハードウェアデコーダがあり、Tina システムは libcedar の OpenMAX インターフェースをサポートしているため、GStreamer はomxh264dec
プラグインを使用して libcedar を呼び出してビデオハードデコードを行うことができ、さらに Tina はsunxifbsink
プラグインを提供しており、DE を呼び出して YV12→RGB を実現できます。したがって、GStreamer を使用してデコードと表示を行うことが最良の選択肢となりました。この記事に従って SDK を設定し、さまざまなコンパイルの問題を排除した後、上記のプラグインを備えた GStreamer を得て、アプリケーション開発を行うことができました。
方案一を行っている際に方案二を考えず FFmpeg を使用しましたが、TCP 制御命令と UDP データ取得部分は再利用できます。GStreamer の核心は、要素(element)が順次パイプライン(pipeline)を構成することです。UDP から取得したフレームデータをパイプラインに送るために、GStreamer のappsrc
を使用できます。appsrc
は GStreamer パイプラインにデータを送る API を提供します。appsrc
には 2 つのモードがあります:プッシュモードとプルモードです。プルモードでは、appsrc
はデータが必要なときに指定されたインターフェースを通じてアプリケーションから対応するデータを取得します。プッシュモードでは、アプリケーションがデータをパイプラインにプッシュします。プッシュ方式を採用すれば、UDP 受信スレッド内でデータをappsrc
に「送信」することができます。したがって、以下のプロセスでパイプラインを作成します:
-
要素の作成
appsrc = gst_element_factory_make("appsrc", "source"); parse = gst_element_factory_make("h264parse", "parse"); decoder = gst_element_factory_make("omxh264dec", "decoder"); sink = gst_element_factory_make("sunxifbsink", "videosink");
各要素は
g_object_set()
で属性を設定し、その中でcaps
はデータストリームのフォーマットと属性を定義し、要素が正しく処理できるようにし、要素間の交渉を行います。このアプリケーションでは、appsrc
のcaps
が最も重要です。そうでなければ、後続の要素は受け取った内容がどのフォーマットであるかを知ることができません。appsrc
のcaps
は以下のように設定します:GstCaps *caps = gst_caps_new_simple("video/x-h264", "width", G_TYPE_INT, 1920, "height", G_TYPE_INT, 1080, "framerate", GST_TYPE_FRACTION, 30, 1, "alignment", G_TYPE_STRING, "nal", "stream-format", G_TYPE_STRING, "byte-stream", NULL); g_object_set(appsrc, "caps", caps, NULL);
-
パイプラインの作成と要素の追加およびリンク
pipeline = gst_pipeline_new("test-pipeline"); gst_bin_add_many(GST_BIN(pipeline), appsrc, parse, decoder, sink, NULL); gst_element_link_many(appsrc, parse, decoder, sink, NULL);
これにより、
appsrc→h264parse→omxh264dec→sunxifbsink
のパイプラインが形成されます。
UDP スレッド内では、引き続きパケットを受信し、各msg_id
に対応するすべてのセグメントを収集し、完全な内容をgst_buffer
に組み合わせ、g_signal_emit_by_name(appsrc, "push-buffer", gst_buffer, &ret)
を通じてバッファgst_buffer
をappsrc
にプッシュします。gst_buffer
内には、フレームデータ自体に加えて、dts
、pts
、duration
は重要な時間パラメータとして渡す必要があります。appsrc
のdo-timestamp
属性をTRUE
に設定すると、appsrc
はバッファを受信した際に自動的にタイムスタンプを設定しますが、duration
(持続時間)はフレームレートに基づいて設定する必要があります。設定しない場合、実測で「カクつき感」が生じることがあり、その原因はduration
の設定が欠如しているため、再生速度が不安定になる可能性があります。追加の遅延を引き起こす可能性がありますが、視覚的な体験を保証するためには設定する方が良いです。
完成したコードをコンパイルするために、Makefile を作成し、私たちのコードを OpenWRT のソフトウェアパッケージとして構築 rootfs 時に一緒にコンパイルされるようにします。
ボード上でソフトウェアを実行し、映像伝送は RX0 小型カメラに接続し、画面のストップウォッチを撮影して粗い遅延テストを行います。具体的なプロセスは左側の画面表示→RX0 が画面を撮影し HDMI 出力→HDMI 入力映像伝送→開発ボードが無線受信し HDMI 出力→HDMI 入力右側のディスプレイ表示です。エンドツーエンドの遅延は基本的に 200〜300ms の間で、低くはありません。良い点は、映像伝送の画面を通じて画面上で再生されるビデオを視覚的に見ることができ、体験は比較的スムーズです。
カメラを直接ディスプレイに接続してテストし、プロセスは左側の画面表示→RX0 が画面を撮影し HDMI 出力→HDMI 入力右側のディスプレイ表示で、遅延は基本的に 70ms 程度です。したがって、映像伝送自体の遅延は基本的に 130ms〜230ms の間です。
この受信端を利用することで、HDMI を介してさまざまなモニターに接続でき、影眸はスマートフォンやタブレットに限定されることなく使用できます。
受信方案 3 - OBS Studio プラグイン#
前の 2 つの受信方案では、影眸を使用する際にコンピュータや HDMI 表示デバイスで監視できますが、低遅延のライブストリーミングのニーズには応えられません。方案一の基礎の上に受信プログラムを少し変更し、localhost の UDP を介して H.264 コーディングストリームを OBS Studio に送信すれば、バッファを有効にするとスムーズですが遅延が大きく、バッファを無効にすると遅延が低いですが、監視時に見られないカクつきが頻繁に発生します。方案二はキャプチャカードを接続して HDMI 出力をキャプチャできますが、開発ボード上でのデコード、出力、キャプチャカードの遅延が増加します。遅延を減らすためには、OBS プラグインを直接開発することがほぼ最良の選択肢です。
OBS Studio はプラグインを通じて機能を拡張することをサポートしていますPlugins — OBS Studio 30.0.0 documentation (obsproject.com)。紹介によると、ソースタイプのプラグインを開発すれば、ビデオソースを OBS に接続できます。OBS Studio のソースクラスプラグイン開発には、同期ビデオソース(Synchronous Video Source)と非同期ビデオソース(Asynchronous Video Source)があります。同期ビデオソースは Image Source のように OBS のレンダリングループと同期し、OBS がビデオソースのレンダリング関数を呼び出してフレームデータを取得します。グラフィック描画やエフェクト処理に適しています。非同期ビデオソースは独立した作業スレッドで実行され、OBS のレンダリングループと非同期で、ビデオソースがフレームデータを OBS にプッシュします。ネットワークストリームやカメラ入力には非同期の方が適しています。
提供されたプラグインテンプレートobs-plugintemplateを基にプロジェクトを立ち上げ、環境を準備し、OBS の既存の image_source プラグインのソースコードを参考にしてロジックを完成させます。行うべき変更は非常に少なく、大部分のコードは方案二のコードを再利用できます。異なる点は、デコードされたフレーム内容を直接表示要素に送ることができず、appsink
を介してデコード後の内容を取得し、obs_source_output_video()
を呼び出して OBS Studio に渡す必要があります。
コンパイルが成功した後、build
ディレクトリ下の.so
ファイルを OBS Studio のプラグインディレクトリ(例:/usr/local/lib/obs-plugins/
)にコピーし、OBS Studio を起動すればテストを開始できます。同様に遅延テストを行い、具体的なプロセスは左側の画面表示→RX0 が画面を撮影し HDMI 出力→HDMI 入力映像伝送→コンピュータ OBS プラグインが無線受信し表示します。エンドツーエンドの遅延は基本的に 200ms 程度で、映像伝送の画面を通じて画面上で再生されるビデオを視覚的に見ることができ、体験も連続的でスムーズです。
obs-studio プラグインの遅延テスト:左側の画面表示→RX0 が画面を撮影し HDMI 出力→HDMI 入力映像伝送→コンピュータ OBS プラグインが無線受信し表示
同時に 3 つの方案を接続すると、カクつきが増加することが感じられますが、すべてまだ受信可能な遅延を維持しています。
まとめ#
ある意味で、強力なコーデックと成熟した無線技術の支えにより、リアルタイムビデオ伝送を実現することはそれほど難しくなく、ソフトウェアロジックは非常にシンプルで直接的です。また、海思のハードウェアコーディングチップは監視に広く使用され、5GHz Wi-Fi の普及により、Wi-Fi 無線映像伝送のような製品が非常に低コストで良好なリアルタイムビデオ伝送を実現できるようになりました。さらに、大型ディスプレイデバイスの発展により、ハードルがさらに下がりました。残念ながら、これらの製品の上限は非常に制限されています:Wi-Fi のエコシステムを享受することで、Wi-Fi の混雑を受け入れなければならず、成熟したハードウェアコーデックを享受することで、基本的に変更の自由度を失います。当然、影眸自体は非常に「機能が充実した」製品であり、既定のタスクをうまく完了し、深刻な短所はなく、多くの新製品が登場しても、基本的な映像伝送のニーズを効果的に満たすことができます。致迅はあるライブ配信で影眸の製造過程を紹介し、彼らが早くからユーザーの痛点を捉え、心を込めて完成度の高い製品を作り上げたことは、今でも称賛に値します。