🔩

Fifo(named pipe)書き込み専用のgstreamerのエレメントを作った

2022/07/15に公開

Fifo(named pipe)に書き込む専用のgstreamerのエレメント(fifosink)を作ってみました。
https://github.com/tetsu-koba/gst-fifosink

Fifo専用

fifoに対して書き込むことは普通にfilesinkでできます。
あえてfifo専用のsinkを作ったのは、ひとつには自分の勉強のためですが、fifoに特化したので以下のようにしました。

pipeのバッファサイズを最大にセットする

pipeのバッファサイズは64KBですが、fcntl(2)のF_SETPIPE_SZでそれを変更できます。
一般ユーザーのセットできる最大値は /proc/sys/fs/pipe-max-size でわかります。
このあたりの情報は man 7 pipe で調べられます。

fifosinkでは/proc/sys/fs/pipe-max-sizeで得られた最大値をセットしています。

書き込みにwritev(2)でなくvmspice(2)を使用する

pipeのパフォーマンスについて以下の記事が興味深いです。
https://mazzo.li/posts/fast-pipes.html
これによると、pipeにwriteするときにカーネル空間にメモリを割り当ててユーザー空間のメモリからそこへのコピーを行います。プロファイルをとるとカーネル空間でのメモリに割り当てに時間がかかっているそうです。
writeのかわりにvmspliceのシステムコールを使用すると渡したメモリをそのままカーネル空間にマッピングするので、カーネル空間でのメモリの割り当てとコピーを省略できて高速化できたそうです。

それを参考にしてfifosinkでは書き込むときにwritevの代わりにvmspliceを使用しています。

余談ですが、vmspliceは対称性のためにパイプからの読み込みにも使えるようになっていますが、そのときにはカーネル空間のメモリからユーザ空間のメモリにコピーが発生するので、readと本質的な差がないようです。このことはman 2 vmsplice に書かれています。

fifo(named pipe)に特化しpipe(標準出力)をサポートしない

ここまで書いた内容はpipeに共通のことなのでfifo(named pipe)だけでなくpipe(標準出力)にも適用できます。しかしfifosinkではあえてfifoのみのサポートにしました。
その理由は2つあります。
(1) gstreamerのエレメントに行儀悪く標準出力にログメッセージを出すのもがあり、それと混じってしまう。
gstreamerのログは標準エラー出力に出すのがルールで、そのためのマクロが用意されているのですが、それを守られていないものがありました。そういうものがあるため標準出力に書き込むのは実用的でないと判断しました。
(2) fifoに特化することで、start時に指定されたファイル名でオープンし、stop時にクローズすると単純化できました。標準エラー出力をサポートするとstop時にクローズせずにフラッシュすることを考えなくてはなりません。

使用例

先日作ったpipe_video_srcfifosinkを使ってI420の映像データを流し込むとWebRTCで送信することができます。
https://zenn.dev/tetsu_koba/articles/6d4d9b47b808c8

#!/bin/sh -eux

SIGNALING_URL=wss://207-148-110-182.canary.sora.sora-labo.shiguredo.app/signaling
CHANNEL_ID=tetsu-koba@sora-devtools
SIGNALING_KEY=your_key_xxxyyy
WIDTH=1280
HEIGHT=720
FRAMERATE=20

AUDIO_OPT='--no-audio-device'

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

./pipe_video_src --log-level 1 $AUDIO_OPT --video-fifo $FIFO --resolution ${WIDTH}x${HEIGHT} --framerate $FRAMERATE \
    --signaling-url $SIGNALING_URL --channel-id $CHANNEL_ID \
    --multistream true --video-codec-type H264 \
    --metadata "{\"signaling_key\": \"$SIGNALING_KEY\"}" > log1 2>&1 &

GST_DEBUG_NO_COLOR=1 GST_DEBUG=fifosink:6 gst-launch-1.0 -v v4l2src device="/dev/video0" ! \
    "image/jpeg, width=$WIDTH, height=$HEIGHT, framerate=$FRAMERATE/1" ! \
    nvv4l2decoder mjpeg=1 ! 'video/x-raw(memory:NVMM)' ! \
    nvvidconv ! 'video/x-raw, format=(string)I420' ! \
    fifosink location=$FIFO >log2 2>&1

関連

https://zenn.dev/tetsu_koba/articles/ec38bff54814f0
https://zenn.dev/tetsu_koba/articles/6d4d9b47b808c8
https://zenn.dev/tetsu_koba/articles/2637c4efa4ec72

Discussion