🦧

jetson nanoのgstreamerでNVIDIAのハードウェアアクセラレーションと新しいlibsrtをdockerを使って共生させる

2022/05/23に公開

前回の記事でサーバ側ではdockerを使って新しいlibsrtを動かすことに成功しました。次にこの記事の送信側のjetson nanoでもやってみました。

https://zenn.dev/tetsu_koba/articles/8c22466ce57498

2つのプロセスに分離させて、間をfifoでつなぐ

NVIDIAのハードウェアアクセラレーションを行うgstreamerのエレメントは現状のバージョンのgstreamerでしか提供されていません。これはこのまま使用します。
SRTで送信するところを別のプロセスに分離し、そちらをubuntu 22.04のdocker環境で動かすようにします。
両者の間はfifo(名前付きパイプ)でつなぐことにしました。

とりあえず録画はやめて送信するだけのスクリプト。

send_srt.sh
#!/bin/sh -xue

HOST=xx.yy.zz.ww
PORT=7001

gst-launch-1.0 -eq v4l2src device="/dev/video0" ! \
    'image/jpeg, width=1280, height=720, framerate=30/1' ! \
    nvv4l2decoder mjpeg=1 ! 'video/x-raw(memory:NVMM)' ! \
    nvvidconv ! 'video/x-raw(memory:NVMM), format=(string)I420' ! \
    nvv4l2h265enc insert-aud=1 insert-sps-pps=1 iframeinterval=60 bitrate=2000000 ! \
    queue ! \
    h265parse ! mpegtsmux alignment=7 ! srtclientsink uri="srt://$HOST:$PORT"

これをfifoをはさんで2つのプロセスに分離します。fifoを読み出す側のプロセスを先に起動します。

send_fifo.sh
#!/bin/sh -xue

HOST=xx.yy.zz.ww
PORT=7001

FIFO=$PWD/.fifo
rm -f $FIFO
mkfifo $FIFO

gst-launch-1.0 -eq filesrc location=$FIFO ! \
    h265parse ! mpegtsmux alignment=7 ! \
    srtclientsink uri="srt://$HOST:$PORT" &

gst-launch-1.0 -eq v4l2src device="/dev/video0" ! \
    'image/jpeg, width=1280, height=720, framerate=30/1' ! \
    nvv4l2decoder mjpeg=1 ! 'video/x-raw(memory:NVMM)' ! \
    nvvidconv ! 'video/x-raw(memory:NVMM), format=(string)I420' ! \
    nvv4l2h265enc insert-aud=1 insert-sps-pps=1 iframeinterval=60 bitrate=2000000 ! \
    filesink location=$FIFO

これで動くことを確認しました。

dockerでubuntu 22.04の環境を準備

Dockerfile
FROM ubuntu:22.04
RUN apt-get update 
RUN apt-get install -y gstreamer1.0-tools gstreamer1.0-plugins-bad
RUN gst-inspect-1.0 --version
EXPOSE 7001/udp 7002/udp
CMD /bin/bash

docker イメージをビルド。

$ docker build -t koba/srt .

できたdockerイメージの中でコマンドを動かす。

$ docker run --rm -v $PWD:$PWD -p 7001:7001/udp koba/srt /usr/bin/gst-inspect-1.0 --version
gst-inspect-1.0 version 1.20.1
GStreamer 1.20.1
https://launchpad.net/distros/ubuntu/+source/gstreamer1.0

いい感じです。
docker run --rm -v $PWD:$PWD -p 7001:7001/udp koba/srt を頭につけてフルパスで実行すればubuntu 22.04環境の中でコマンドを実行できます。

後半部分をdocker環境の中で動かす

send_with_docker.sh
#!/bin/sh -xue

HOST=xx.yy.zz.ww
PORT=7001

FIFO=$PWD/.fifo
rm -f $FIFO
mkfifo $FIFO

docker run --rm -v $PWD:$PWD -p $PORT:$PORT/udp koba/srt \
    /usr/bin/gst-launch-1.0 -eq filesrc location=$FIFO ! \
        h265parse ! mpegtsmux alignment=7 ! \
        srtclientsink uri="srt://$HOST:$PORT" &

gst-launch-1.0 -eq v4l2src device="/dev/video0" ! \
    'image/jpeg, width=1280, height=720, framerate=30/1' ! \
    nvv4l2decoder mjpeg=1 ! 'video/x-raw(memory:NVMM)' ! \
    nvvidconv ! 'video/x-raw(memory:NVMM), format=(string)I420' ! \
    nvv4l2h265enc insert-aud=1 insert-sps-pps=1 iframeinterval=60 bitrate=2000000 ! \
    filesink location=$FIFO

みごと成功!
計算通り ----- !!

と思ったけど、やっぱりコマ落ちが発生してる。映像をfifo(名前付きパイプ)で渡すのは厳しいか。
** 2022/05/28 追記
fifoはボトルネックではありませんでした。この記事の最後の関連のところを見てください。
** 追記ここまで

録画機能を追加したバージョン

録画のほうはfifoを通さずに処理するように修正しました。

rec_send_with_docker.sh
#!/bin/sh -xue

HOST_IP=xx.yy.zz.ww
PORT=7001
RECDIR=$PWD/rec
MAX_FILE_SIZE=$((100 * 1024 * 1024))
MAX_FILES=20

filename="$RECDIR/d%04d.m2ts"
index_wrap=10000
last_index=`ls -t rec/d*.m2ts |head -1 |sed -n -e 's/\.m2ts$//' -e 's/[^0-9]//gp'`
if [ -z $last_index ] ; then
    mkdir -p $RECDIR
    next_index=0
else
    next_index=`echo "($last_index + 1) % $index_wrap" |bc`
fi    

FIFO=$PWD/.fifo
rm -f $FIFO
mkfifo $FIFO

touch $RECDIR/start_${next_index}_$(date +%Y%m%d%H%M%S)
docker run --rm -v $PWD:$PWD -p $PORT:$PORT/udp koba/srt \
    /usr/bin/gst-launch-1.0 -eq filesrc location=$FIFO ! queue leaky=2 ! \
        h265parse ! mpegtsmux alignment=7 ! srtclientsink uri="srt://$HOST_IP:$PORT" &

gst-launch-1.0 -eq v4l2src device="/dev/video0" ! \
    'image/jpeg, width=1280, height=720, framerate=30/1' ! \
    nvv4l2decoder mjpeg=1 ! 'video/x-raw(memory:NVMM)' ! \
    nvvidconv ! 'video/x-raw(memory:NVMM), format=(string)I420' ! \
    nvv4l2h265enc insert-aud=1 insert-sps-pps=1 iframeinterval=60 bitrate=2000000 ! \
    tee name = t \
    t. ! queue leaky=2 ! filesink location=$FIFO \
    t. ! queue ! h265parse ! mpegtsmux ! multifilesink location=$filename index=$next_index \
        next-file=4 max-file-size=$MAX_FILE_SIZE max-files=$MAX_FILES

関連

ffmpegを使ってコマ落ちを回避するのに成功しました。
https://zenn.dev/tetsu_koba/articles/3a36fdd43ca0ca

gstreamerでもコマ落ちを解消できました。
https://zenn.dev/tetsu_koba/articles/2637c4efa4ec72

Discussion