🐟

VCUを使った動画ストリーミング

2022/02/05に公開

1.はじめに

今回はZynq UltraScale+ MPSoC ZCU104 評価キットでMPSoCに統合されたVideoCodecUnit(VCU)を使ってGstreamerで動画ストリーミングの動作を確認して行きたいと思います。

2.ハードウェアプラットフォームの確認

PetalinuxをBSPからインストールすると、<path-to-installed-PetaLinux>/hardware/xilinx-zcu104-2021.1ディレクトリにハードウエアプラットフォームがインストールされます。
まずはその中身について確認していこうと思います。

Vivadoを使うので、セットアップファイルを読み込みます。

source <path-to-installed-Vitis>/Vitis/2021.1/settings64.sh

ハードウエアプラットフォームのフォルダに移動し、Vivadoを起動します。

cd <path-to-installed-PetaLinux>/hardware/xilinx-zcu104-2021.1
vivado &

Vivadoが起動したらFile -> Project -> Openを選択します。

ファイルダイアログが開くので、Vivadoのプロジェクトファイルxilinx-zcu104-2021.1.xprを選択してOKボタンをクリックします。

そうすると、以下の図のようにVivadoプロジェクトが立ち上がります。

次に回路の構成図を参照していきます。
Vivado のFlow NavigatorからOpen Block Designをクリックします。
以下のようなブロック図が表示されます。

ブロック図内から、今回調べたいVCUのIPを探します。
Block Design画面の検索ボタンをクリックして、検索画面にvcuというワードを入力するとvcu_0が表示されるので、これをクリックします。

Block Design画面の中央にVCUが拡大表示されます。
このハードウェアプラットフォームにはVCUが組み込まれている事がわかります。

Block Design画面上のVCUをダブルクリックするとIPのパラメータ画面が表示されます。

VCUエンコーダのパラメータには以下が指定されています。

  • コーデック種別はHEVC(H.265)
  • Intra/Interフレーム圧縮
  • 解像度1920x1080、30fps
  • カラーフォーマット4:2:2
  • 画素ビット10bit
  • 最大8本のストリームエンコード

ここでVCUパラメータの内容がわかったので一旦Vivadoは終了し、
次にZCU104でVCUを使ったGstreamerのストリーミングを試していこうと思います。

3.Gstreamerの送信側設定

ZCU104のスイッチSW1をSDカードブートモードに設定します。
Petalinuxイメージを書き込んだSDカードをZCU104のSDカードスロットに差し込みます。
USBカメラをZCU104に接続し、シリアル通信用のUSBケーブルでZCU104と受信用PCを接続します。
受信用PCでTerminalソフトを起動します。通信のBAUDレートは115200を設定します。

ZCU104の電源を投入します。起動に成功するとコンソールがTerminal画面に表示されます。

起動に成功したら、最初にIPアドレスを変更しておきます。
ZCU104に接続したTerminalから以下のコマンドを実行します。

ifconfig eth0 192.168.0.20

次に、ZCU104に接続したカメラの解像度パラメータを確認します。
ZCU104に接続したTerminalから以下のコマンドを実行します。

gst-device-monitor-1.0

コマンド実行結果が表示されるので、USBカメラの情報を確認する事ができます。

Device found:

        name  : See3CAM_CU30
        class : Video/Source
        caps  : video/x-raw, format=(string)UYVY, width=(int)2304, height=(int)1536, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 24/1, 12/1 };
                video/x-raw, format=(string)UYVY, width=(int)2048, height=(int)1536, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 42/1, 21/1 };
                video/x-raw, format=(string)UYVY, width=(int)2304, height=(int)1296, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 30/1, 15/1 };
                video/x-raw, format=(string)UYVY, width=(int)1920, height=(int)1280, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 50/1, 25/1 };
                video/x-raw, format=(string)UYVY, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 60/1, 30/1, 15/1 };
                video/x-raw, format=(string)UYVY, width=(int)1280, height=(int)960, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 58/1, 30/1 };
                video/x-raw, format=(string)UYVY, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 60/1, 30/1 };
                video/x-raw, format=(string)UYVY, width=(int)1152, height=(int)768, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 60/1, 30/1 };
                video/x-raw, format=(string)UYVY, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, interlace-mode=(string)progressive, framerate=(fraction){ 60/1, 30/1 };
                image/jpeg, width=(int)2304, height=(int)1536, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)48/1;
                image/jpeg, width=(int)2048, height=(int)1536, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)50/1;
                image/jpeg, width=(int)2304, height=(int)1296, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)60/1;
                image/jpeg, width=(int)1920, height=(int)1280, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)50/1;
                image/jpeg, width=(int)1920, height=(int)1080, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)60/1;
                image/jpeg, width=(int)1280, height=(int)960, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)58/1;
                image/jpeg, width=(int)1280, height=(int)720, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)60/1;
                image/jpeg, width=(int)1152, height=(int)768, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)60/1;
                image/jpeg, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)60/1;

ZCU104でGstreamerコマンドを実行して動画配信します。
gst-device-monitor-1.0コマンドで調べたUSBカメラパラメータを選択しますが、RAWデータからHEVCに圧縮して配信したいので、まずはサイズが小さいvideo/x-raw Width=640, Height=480, FrameRate=30のパラメータを選択して試してみます。
送信側のGstreamerは以下を実行します。

gst-launch-1.0 v4l2src io-mode=4 device=/dev/video0  ! videoconvert ! video/x-raw, format=NV12, framerate=30/1, width=640, height=480 ! omxh265enc ! h265parse ! queue ! rtph265pay ! udpsink host=192.168.0.15 port=9000 buffer-size=20000000

ZCU104のVCUを使ったGstreamerコマンドの設定方法については、以下のマニュアルが参考になります。
https://japan.xilinx.com/support/documentation/ip_documentation/vcu/v1_0/j_pg252-vcu.pdf

4.Gstreamerの受信側設定

受信側PCでH.265のハードウェアデコードを実行したいので、ここではIntel UHD Graphic 630のGPUでコーデックができるようにIntelMediaSDKをインストールします。
以下のページにアクセスしてダウンロードします。
https://www.intel.com/content/www/us/en/developer/tools/media-sdk/overview.html

Windows版を選択してRegister and Downloadをクリックします。

サインインが求められるので、必要な情報を入力してSUBMITボタンをクリックします。

IntelMediaSDKのバージョンを指定してIntel Media SDK 2021のボタンをクリックしてダウンロードを開始します。ここでは最新版の2021 R1を選択しました。

ダウンロードされたファイルMSDK2021R1.exeをダブルクリックしてインストーラを起動します。
インストーラが起動すると以下の画面が表示さるのでHEVC Decoder & Encoderを選択してNextボタンをクリックします。

しばらくするとインストールが完了して以下の画面が表示されます。
Finishボタンをクリックして終了します。

今度は受信側PCのGstreamerコマンドを実行します。
受信PCのCMDプロンプトから以下を実行します。

gst-launch-1.0.exe --gst-debug-level=0 udpsrc name=video_src port=9000 caps="application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H265" ! rtph265depay ! queue ! h265parse ! queue ! d3d11h265dec ! videoconvert ! d3d11videosink

WindowsPC上に画面が立ち上がり、そこに動画が表示されますが、何もパラメータを指定していないので画質はかなり悪く、ブロックノイズが目立つことが見てわかると思います。

タスクマネージャーからイーサーネットのデータ受信量をチェックしてみると、100kbps程度の圧縮率でストリーミングされているようです。

5.HEVCの画質調整

GstreamerのHEVC(omxh265enc)では符号化パラメータが公開されています。
以下のコマンドを実行しomxh265encの仕様が確認できます。

gst-inspect-1.0 omxh265enc

デフォルトではブロックノイズが目立っていたので、量子化パラメータを変更してみたいと思います。上記コマンドでプラグイン仕様を確認するとmax-qpというパラメータがあります。max-qpのデフォルト値は51と低画質向けの設定になっているので、これをmax-qp=20くらいに変更して試してみます。送信側のGstreamerを以下に変更してストリーミングを実行してみます。

gst-launch-1.0 v4l2src io-mode=4 device=/dev/video0  ! videoconvert ! video/x-raw, format=NV12, framerate=30/1, width=640, height=480 ! omxh265enc max-qp=20 ! h265parse ! queue ! rtph265pay ! udpsink host=192.168.0.15 port=9000 buffer-size=20000000

パラメータ変更の結果、以下の動画が表示されます。
ブロックノイズはだいぶ改善されている事が分かります。

タスクマネージャーからイーサーネットのデータ受信量をチェックしてみると、約1Mbpsまで圧縮率が上がっている事が確認できると思います。

次回、高解像度のストリーミングを試していこうと思います。

Discussion