jetson nanoのgstreamerでNVIDIAのハードウェアアクセラレーションと新しいlibsrtをdockerを使って共生(2)
この記事の続きです。
これまでの経緯
「jetson nanoでWebcamの映像をH.265で録画しつつSRTで小規模配信する」でSRTをつかって配信することができたのですが、libsrtに古いものを使用していました。そのためかサーバ側の状態が不安定になることがありました。
「dockerを使ってgstreamerで新しいlibsrtを利用する」でサーバ側で使用するlibsrtを新しいものにすることができました。
送信側については「jetson nanoのgstreamerでNVIDIAのハードウェアアクセラレーションと新しいlibsrtをdockerを使って共生させる」で、ハードウェアアクセラレーションをつかう部分とSRTで送信する部分を分離してfifoでつなぎ、後半部分をdockerを使って新しいlibsrtを使うようにしました。しかし、コマ落ちが発生してしまって配信品質に問題がありました。
「jetson nanoでgstreamerとffmpegの合わせ技でwebcamの映像をH.265でSRTで送信する」でSRTで送信する部分をffmpegを使うことで新しいlibsrtを使うことができました。しかもこの方法ではコマ落ちは発生しませんでした。そうするとコマ落ちが発生する原因はfifoではないので、gstreamerでもコマ落ちせずに配信できる方法があるのではないかということで調べたのが今回の記事です。
まず結論
jetson nanoで動かすスクリプトを以下のようにすることで、コマ落ちがない状態で送信することができました。
つまり、NVIDIAのハードウェアアクセラレーションをするgstreamerのエレメントと新しいlibsrtを使うubuntu 22.04のgstreamerを共存して動かすことに成功しました。
#!/bin/sh -xue
HOST_IP=xx.yy.zzz.www
PORT=7001
FIFO=$PWD/.fifo
rm -f $FIFO
mkfifo $FIFO
docker run --rm -v $PWD:$PWD -p $PORT:$PORT/udp koba/srt \
/bin/sh -c "gst-launch-1.0 -eq fdsrc fd=0 blocksize=65536 ! \
h265parse ! mpegtsmux alignment=7 ! \
srtclientsink uri=\"srt://$HOST_IP:$PORT\" < $FIFO" &
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-sps-pps=1 iframeinterval=60 bitrate=1000000 ! \
filesink location=$FIFO
結論に至る過程
ffmpegを実行するときにstrace -f -o st.log
を頭につけることで、straceのログをファイルに書き出します。そして、その結果からまず.fifo
をオープンしているところを探します。
$ grep '/\.fifo' st_ffmpeg.log
12619 execve("/usr/local/bin/ffmpeg-small-srt", ["ffmpeg-small-srt", "-probesize", "1024", "-f", "hevc", "-r", "30", "-i", "/home/koba/work/srt/.fifo", "-c", "copy", "-f", "mpegts", "srt://140.83.51.23:7001"], 0x7ff2ea51f0 /* 34 vars */) = 0
12619 openat(AT_FDCWD, "/home/koba/work/srt/.fifo", O_RDONLY) = 4
fifoをオープンしたのは 12619
のスレッドで、ファイルディスクリプタは4
だとわかりました。
12619 read(4, "\0\0\0\1@\1\f\1\377\377\1@\0\0\3\0\0\3\0\0\3\0\0\3\0]\254\t\0\0\0\1"..., 65536) = 64403
readのときのバッファサイズは64KBにしていることがわかりました。
同じようにgstreamerの方もstraceのログをとり、fifoをオープンしたファイルディスクリプタに注目します。
readのときのバッファサイズは4KBでした。
filesrcにblocksize=65536
をつけて実行しなおすと、straceのログではバッファサイズは64KBに変わっていました。しかし、映像のコマ落ちはさらにひどいものになりました。
さらに見ていくと、このようにread
だけを繰り返し、用意した64KBのバッファがいっぱいになるまでループしているように見える箇所がありました。
14 read(9, <unfinished ...>
14 <... read resumed>"L\200p\372q\f?]R}\244\334`5\0277\311\332m\335\261\345\216\316\t\261\357\303\250\17H\272"..., 65536) = 3251
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\225\t`\351\7\355O\353\252\263V\206\307p?\302\374\206\237I\177\233\22WO"..., 62285) = 3560
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\235\10`9\rM\326\207T%9\216\256\2224\267\312`=\"\374\35\21}\270"..., 58725) = 6716
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\245\10`\37\306\332)N/\322\244\3555KH\205\tP\316\244\10\\\17\2457"..., 52009) = 4582
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\255\10`\347Z\300\3600\t}\216\33W\374\33\02165\335\2403\202\10\f'"..., 47427) = 3869
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\265\t`\24\26H\356\331!k\31\330]\312xM\316\226\200Z~3\334\211R"..., 43558) = 1952
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\275\10`\37\306\347\276\345y\2\204\230>[z|\362'\356/>\267D}-"..., 41606) = 3602
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\305\10`\37\306\347\276\352\251\20O\22\23\366\351\237\215\356\362\0276\210\376\4\326"..., 38004) = 3211
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\315\10`\347\26\255\345\355\357\33\304\224e\330=\224\260\364z\272Ocy\215|"..., 34793) = 3059
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\325\10`\37\306\360,E\261J\252\35\314)Z\242\3\304\356\327\230`\v\2!"..., 31734) = 2906
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\335\35\200:\177]\6\262\253\255\234\te\1Y\336l\0013]\341\16\307ri"..., 28828) = 6766
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\345\35\200\360\22\265\7\314dm\325\255cH\2669lgF\26\"\206A?\362"..., 22062) = 5374
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\355\35\200\347.\262\3666\6\3\240o\257\202\342_(l'$\260\204\277\367\342"..., 16688) = 4896
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\365\35\200\352\324\20\2222\301\232=A\325\276\247\316>\254l\320\347}G~p"..., 11792) = 4505
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\320\375\10`\37\306\363\236\372z\347\321\334j\251\265\3512\2\316NB\36\371\320M"..., 7287) = 2506
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\321\5\10`_\305\201\300h\32\252\33\21\373UE`\7\325!)\270B4S\235"..., 4781) = 2535
14 read(9, <unfinished ...>
14 <... read resumed>"\0\0\0\1\2\1\321\r\10`:\265M\344J\372\324\23\325|\335\362\304\334\275l\253\v|\326\353I"..., 2246) = 2246
該当するソースコードはここです。
readできるものがある限り、なるべくバッファを満たそうとループしています。ブロックデバイス上に置かれた普通のファイルならばこれでよいのですが、fifoの場合ではこれは流れをせきとめることになってしまいます。バッファサイズを64KBに増やしたらコマ落ちが悪化したのはこれが原因です。一方、fdsrc
のエレメントのソースコードを見てみると、こちらはパイプを意識しているので、filesrc
のようにまとめて読もうとせずに1回のread
で読めた分だけ次に渡すようになっていました。filesrc
の代わりにfdsrc
を使うとコマ落ちが解消しそうです。
cat $FIFO | gst-launch-1.0 -eq fdsrc fd=0 blocksize=65536 ! \
h265parse ! mpegtsmux alignment=7 ! \
srtclientsink uri="srt://$HOST:$PORT" &
このようにすることでfilesrc
の代わりにfdsrc
を使うことができ、コマ落ちは解消しました!
しかし、cat
のプロセスがひとつ増えてしまっています。リダイレクトを使って以下のようにしました。
gst-launch-1.0 -eq fdsrc fd=0 blocksize=65536 ! \
h265parse ! mpegtsmux alignment=7 ! \
srtclientsink uri="srt://$HOST:$PORT" < $FIFO &
これでプロセスも増やさずに済みました。
さらにこれをdockerを使って新しいlibsrtを使うようにしたのが、結論のところのスクリプトです。
Discussion