2024.10.01
自社の社内情報を未来の“ゴミ”にしないための備え 「情報量が多すぎる」時代がもたらす課題とは?
カメラの映像をリアルタイムで送信するための部品をZig言語で書く(Part2)(全1記事)
リンクをコピー
記事をブックマーク
小林哲之氏:tetsu_kobaと申します。今日は、「カメラの映像をリアルタイムで送信するための部品をZig言語で書く(Part2)」というタイトルで、発表したいと思います。
概要です。Zig言語はOSのシステムコールの呼び出しや、C言語で書かれたライブラリを利用するのがとても容易です。Video for Linux 2でカメラの映像をキャプチャし、それをエンコード、デコードして、画面に表示するまでの一連のプログラムをパーツとしてZig言語で書きました。これは5月22日に別の勉強会で話した内容の続きなのでPart2です。
なぜこのようなことをしたかというと、映像の配信に必要な各プロセスをきちんと自分で理解するためです。実験のために再利用しやすいパーツを自分で作ります。将来的には新しいコーデックや通信方式を試したいので、そのための土台を作っておくということになります。要するに車輪の再発明ですが、とにかくZig言語でなにかを作ってみたかったということになります。
「とにかくシンプルに」ということで、今回はいろいろな制約を付けています。まず、映像と音声の同期を今回は行いません。さらに今回は映像だけを扱っています。プロセス間のデータの受け渡しにはパイプを使いました。非常にシンプルなプロセス間通信です。パイプの中には画像のデータのみが流れます。映像のサイズは決め打ちで、コマンドラインから指定するようにしました。
ネットワーク間のデータ送信には、TCP socketを使用しました。LAN環境のみなので、ネットワークがひどく混雑している状況は想定していません。今回はとにかく正常系を優先して実装しています。異常が起きたら基本的にはそこで終了するということにしています。必要に応じてエラーリカバリを追加しています。
前回発表時までに作ったものは(スライドを示して)以下のものです。主に送信側を中心に作りました。
今回は以下のようなものを追加しています。jpegのデコーダをエンハンスして、Motion JPEGに対応しました。受信側に使うものはVP8のデコーダ、それからTCPのソケットをlistenして (待ち受けて)、受けた内容をstdoutに出力する。今回はこれをGo言語で書きました。それからI420のローデータのビューアを、libsdl2を使って作りました。
V4L2 captureは、前回作ったものと同じです。
jpegのデコーダです。USBカメラでは、高解像度・高フレームレートの画像はmjpegのフォーマットになるので、今回このjpegのデコーダでmjpegを扱えるようにしました。これによって1,280×720の画像サイズを扱えるようになっています。
mjpegからjpegを切り出すために、jpegの先頭と最後に付いているマーカーを検出しているわけですが、このコードを書く時にいくつかTipsを見つけたので、(スライドを示して)このブログに書いています。jpegのデコードそのものには、libturbojpegのライブラリを使っています。
Convert to I420。これはさまざまな基本的な映像フォーマットをI420に変換するものです。
VP8用のエンコーダとデコーダを作りました。libvpxを使用しています。なぜVP8を利用したかというと、このエンコード済みのフォーマットがIVFになるからです。IVFはH.264やH.265で使われているNALフォーマットよりもずっとシンプルで扱いやすいです。
AV1もraw video formatとしてIVFを採用しているので、それの足掛かりとして、まずはIVFを使っているVP8でやってみました。エンコーダはVP8専用に作りましたが、デコーダはVP8とVP9の両対応にしています。
次にI420 Viewerです。これはlibsdl2を利用しています。フレームレートの制御はしていないので、データが来たタイミングで映像の更新をしています。画面に表示するためにはRGBへ変換する必要がありますが、変換はlibsdl2の内部に任せてあります。
作ったこれらのパーツを組み合わせて使用します。送信側はLinuxで動かすわけですが、V4L2 captureからmjpegのデータを取り込んでそれをデコードして、その結果をさらにI420の形式に変換します。それをVP8でエンコードしてTCP送信。それぞれのパーツをパイプでつなげています。
同様に受信側はLinux、あるいはmacで動かすようになっています。TCPで受信したものをVP8でデコードして画面に表示する。この3つがつながっています。
具体的に使用したシェルスクリプトです。(スライドを示して)このようにパラメーターをシェル変数に入れて呼んでいて、それぞれの入力・出力はファイルで指定するようになっていますが、/dev/stdin、/dev/stdoutを指定することによって、標準入力から得て標準出力に出すということにして、パイプでつなげていくことができるようになっています。
(スライドを示して)同様に、これは受信側です。前回は受信側を作っていなかったので「ffplay」にやらせていましたが、今回は受信側も自分で作りました。
パイプの効率化ですが、パイプのバッファサイズはLinuxの場合、デフォルトで64KBです。これはビデオデータを送るにはけっこう小さいので、何回もシステムコールを呼ばなければいけません。なのでパイプのバッファサイズの変更をしたり、パイプへの書き込みに対してwriteをシステムコールじゃなくvmspliceを使うことによってコピーの回数を1回減らしています。そういった工夫に関してはブログに書いたので、そちらをご覧ください。
Future workです。まだこれは土台なので、これからいろいろなことをやっていきたいと思っています。まずは各ライブラリをZigのパッケージにしたいと思います。それによってパイプでつなげるのではなく、もっとメモリで受け渡すとか、もっと効率の良いやり方で実用化に近いものに変更していけばいいと思います。最終的にはハードウェアアクセラレータによる動画のエンコードやデコードまでやってみたいと思っています。
まとめです。このように単純ではありますが、最低限の映像の送信と受信を行うことはできました。このレイヤーのソフトウェアを実装するのにZig言語はとても有用だと思います。特にC言語で書かれているCのABIのライブラリを使う上では、非常にスマートに使えるので、いいなと思いました。今日紹介したプログラムのソースコードはGitHubに公開しているので、ご覧ください。ブログにもいろいろなことを書いています。
このあと簡単にですが、デモをしたいと思います。2つの画面がありますが、上のほうが送信側。これはArmの評価ボードのJetson AGX OrinにSSHしているところです。下のほうは、M1 Macのターミナルです。動かすシェルスクリプトは(スライドを示して)このとおりです。
まず、下のレシーブ側から動かします。続いて送信側。こんな感じです。音に比べてそれほど遅延がないことがわかります。
以上です。終わります。ありがとうございました。
2024.10.29
5〜10万円の低単価案件の受注をやめたら労働生産性が劇的に向上 相見積もり案件には提案書を出さないことで見えた“意外な効果”
2024.10.24
パワポ資料の「手戻り」が多すぎる問題の解消法 資料作成のプロが語る、修正の無限ループから抜け出す4つのコツ
2024.10.28
スキル重視の採用を続けた結果、早期離職が増え社員が1人に… 下半期の退職者ゼロを達成した「関係の質」向上の取り組み
2024.10.22
気づかぬうちに評価を下げる「ダメな口癖」3選 デキる人はやっている、上司の指摘に対する上手な返し方
2024.10.24
リスクを取らない人が多い日本は、むしろ稼ぐチャンス? 日本のGDP4位転落の今、個人に必要なマインドとは
2024.10.23
「初任給40万円時代」が、比較的早いうちにやってくる? これから淘汰される会社・生き残る会社の分かれ目
2024.10.23
「どうしてもあなたから買いたい」と言われる営業になるには 『無敗営業』著者が教える、納得感を高める商談の進め方
2024.10.28
“力を抜くこと”がリーダーにとって重要な理由 「人間の達人」タモリさんから学んだ自然体の大切さ
2024.10.29
「テスラの何がすごいのか」がわからない学生たち 起業率2年連続日本一の大学で「Appleのフレームワーク」を教えるわけ
2024.10.30
職場にいる「困った部下」への対処法 上司・部下間で生まれる“常識のズレ”を解消するには