ドキュメント一覧
デバイス SDK(C)
デバイス SDK は、カメラのファームウェアを Rerinku View に接続します。デバイスは Rerinku View に登録し、MQTT でいつでもコマンドを受け取り、誰かが視聴しているときや録画中にはハードウェアエンコーダーの出力を WebRTC(WHIP)で送信します。SDK はキャプチャとエンコードを担当しません。ファームウェアはエンコード済みのフレームを SDK に渡すだけです。
- 純粋な C(C11)で、インクルードするヘッダーは
<rerinku/rerinku_device.h>ひとつだけです。 - 静的ライブラリ
librerinku_device.a(依存ライブラリをすべて含む)と動的ライブラリlibrerinku_device.soを提供します。どちらもエクスポートするのはrerinku_*シンボルだけで、C ライブラリ以外には何にも依存しません。 - CMake(
find_package(rerinku_device))と pkg-config(rerinku_device.pc)に対応しています。 - チップメーカーのツールチェーンでクロスコンパイルします。Rockchip RV1103(Luckfox Pico Plus:Cortex-A7、uClibc、GCC 8.3、メモリ 64 MB)で動作を確認しています。
- Apache-2.0 のオープンソースです。依存ライブラリはすべて C のライブラリで、バージョンを固定してソースからビルドします。
入手とビルド
CMake 3.21 以上と C11 コンパイラーが必要です。対応するのは Linux などの POSIX システム(pthreads、BSD ソケット)だけです。初回の configure 時に、バージョンを固定した依存ライブラリがダウンロードされます。
git clone https://github.com/akarineren/relink.git rerinku-device-sdk
cd rerinku-device-sdk
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
ctest --test-dir build --output-on-failure
cmake --install build --component sdk --strip --prefix "$PWD/sdk"
インストールされた sdk/ ディレクトリが成果物で、ディレクトリごとほかのマシンにコピーして使えます。
include/rerinku/rerinku_device.h API
include/rerinku/rerinku_version.h バージョンマクロ(ビルド時に生成)
lib/librerinku_device.a SDK とすべての依存ライブラリをまとめた 1 つの静的ライブラリ
lib/librerinku_device.so* 同上の動的ライブラリ(soname librerinku_device.so.0.5)
lib/cmake/rerinku_device/ CMake パッケージ:find_package(rerinku_device)
lib/pkgconfig/rerinku_device.pc pkg-config ファイル(場所を移動可能)
share/rerinku_device/examples/ quickstart/ と device_example.c
share/doc/rerinku_device/ README、CHANGELOG、LICENSE、NOTICE
librerinku_device.a は、SDK とすべての依存ライブラリを 1 つのオブジェクトファイルにまとめたもので、外部に見えるグローバルシンボルは rerinku_* だけです。ファームウェアはこのファイル 1 つをリンクするだけで済み、ファームウェアが独自に持っている mbedTLS や cJSON などが、SDK 内部のバージョンと衝突することもありません。
クロスコンパイル
ターゲットプラットフォームの CMake ツールチェーンファイルを渡します。リポジトリには Rockchip RV1103/RV1106 用のもの(メーカーのツールチェーン arm-rockchip830-linux-uclibcgnueabihf)が含まれています。
cmake -S . -B build-rv1106 -DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/rv1106-uclibc.cmake \
-DRV1106_TOOLCHAIN_DIR=<sdk>/tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf
cmake --build build-rv1106 -j
cmake --install build-rv1106 --component sdk --strip --prefix "$PWD/sdk-rv1106"
ほかのチップでは、このファイルをコピーして、コンパイラーのプレフィックスと CPU のパラメーターを書き換えてください。SDK は必ずファームウェアと同じツールチェーンでビルドし、C ライブラリと浮動小数点 ABI を一致させる必要があります。
クイックスタート
完全なプログラムは、リポジトリの examples/quickstart/quickstart.c にあります。エンコーダーの代わりに動画ファイルを使うので、PC 上でそのまま動かせます。カメラに載せるときは、その中の next_frame() をエンコーダーの出力に置き換えるだけです。中心となる部分は次のとおりです。
#include <rerinku/rerinku_device.h>
rerinku_device_config_t config;
rerinku_device_config_init(&config); /* 既定値 + struct_size */
config.endpoint = "http://192.168.1.10:8080"; /* Rerinku View のアドレス */
config.sn = "CAM-0001"; /* このデバイスのシリアル番号 */
rerinku_device_callbacks_t callbacks = {
.struct_size = sizeof(callbacks),
.on_status = on_status, /* 接続中 / オンライン / オフライン */
.on_stream_start = on_stream_start, /* 視聴者が来た:エンコーダーを起動 */
.on_stream_stop = on_stream_stop, /* 視聴者がいなくなった:エンコーダーを休止してよい */
.on_keyframe_request = on_keyframe_request, /* 例:RK_MPI_VENC_RequestIDR */
};
rerinku_device_t *device;
rerinku_err_t err = rerinku_device_create(&config, &callbacks, &device);
if (err != RERINKU_OK)
fprintf(stderr, "%s\n", rerinku_strerror(err));
rerinku_device_channel_t video = {
.struct_size = sizeof(video),
.channel_id = 0,
.kind = RERINKU_MEDIA_KIND_VIDEO,
.codec = RERINKU_CODEC_H264,
.stream_id = "main",
};
rerinku_device_add_channel(device, &video);
rerinku_device_start(device); /* すぐに戻る。登録と再接続はバックグラウンドで行う */
/* エンコードスレッド:アクセスユニットが 1 つ出るたびに 1 回呼ぶ */
rerinku_device_write(device, 0, data, len, pts_us);
/* シャットダウン時 */
rerinku_device_stop(device);
rerinku_device_destroy(device);
PC で試すには、まず ffmpeg でテスト動画(各フレームの前にアクセスユニットデリミタ付き)を生成し、クイックスタートのプログラムを実行します。
ffmpeg -f lavfi -i testsrc2=size=1280x720:rate=25 -t 10 -c:v libx264 \
-profile:v baseline -g 50 -bf 0 -x264-params aud=1 clip.h264
./quickstart http://192.168.1.10:8080 QUICKSTART-001 clip.h264
デバイスの登録に成功すると、Rerinku View のデバイス一覧に自動で表示され、開けば映像を見られます。より多くの機能を使ったサンプルは examples/device_example.c です。H.264/H.265 ファイルに G.711 音声を加え、コマンドとスナップショットの要求も処理します。
ファームウェアへのリンク
CMake:
find_package(rerinku_device 0.5 CONFIG REQUIRED) # -DCMAKE_PREFIX_PATH=<SDK ディレクトリ>
target_link_libraries(camera PRIVATE rerinku::device) # 静的ライブラリ
# または rerinku::device_shared # 動的ライブラリ
クロスコンパイル用のツールチェーンファイルは、通常 find_package の検索範囲を sysroot 内に限定しています。その場合は、SDK ディレクトリを明示的に指定します。
find_package(rerinku_device 0.5 CONFIG REQUIRED
PATHS "${RERINKU_SDK_DIR}" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
pkg-config(Makefile、Buildroot、チップメーカーの SDK):
export PKG_CONFIG_PATH=<SDK ディレクトリ>/lib/pkgconfig
# 静的リンク:静的ライブラリにすべての依存ライブラリが含まれているので、追加するのは pthread だけ
cc -std=c11 $(pkg-config --cflags rerinku_device) camera.c \
"$(pkg-config --variable=libdir rerinku_device)/librerinku_device.a" -pthread
# 動的リンク
cc -std=c11 $(pkg-config --cflags rerinku_device) camera.c $(pkg-config --libs rerinku_device)
examples/quickstart/ には、どちらの方法についても、そのまま使える CMakeLists.txt と Makefile があります。
CMake プロジェクトでソースを直接使う: add_subdirectory(rerinku-device-sdk) のあと、rerinku::device をリンクします。Android ではこの方法を使っており、NDK で SDK のソースを直接ビルドして、その外側を JNI でラップしています。
API の規約
API の書き方は、SDK が土台にしている C ライブラリ(mbedTLS、libjuice、libsrtp)に合わせています。
- 命名:関数と型は
rerinku_で、マクロと列挙値はRERINKU_で始まります。型は_tで、コールバック型は_cbで終わります。列挙値には型名が付きます(rerinku_stream_state_t→RERINKU_STREAM_STATE_*。唯一の例外はRERINKU_OK)。ライブラリ全体に共通の関数はrerinku_*、デバイスに対する関数はrerinku_device_*で、その第 1 引数はデバイスです。 - エラー:失敗する可能性のある関数は
rerinku_err_tを返します。値はRERINKU_OK(0)か負のRERINKU_ERR_*で、rerinku_strerror()で説明文を取得できます。rerinku_device_write()は例外で、rerinku_write_result_tを返します。その結果の大半(視聴者がいない、キーフレーム待ち)は正常な状態だからです。 - オブジェクト:
rerinku_device_tは不透明なハンドルで、create/destroyとstart/stopを対にして使います。 - バージョン付きの構造体:設定、チャンネル、コールバックの 3 つの構造体は、最初のフィールドが
struct_sizeです。古いヘッダーでビルドしたプログラムが渡す構造体は小さく、足りないフィールドは既定値のままになります。新しいバージョンで追加されたフィールドは、古いバージョンのライブラリでは無視されます。 - バージョン番号:
<rerinku/rerinku_version.h>はヘッダーのバージョン(RERINKU_VERSION_MAJOR、_MINOR、_PATCH、_STRING、および 16 進数のRERINKU_VERSION_NUMBER)を提供します。rerinku_version()とrerinku_version_number()は、実際にリンクされたライブラリのバージョンを返します。1.0 より前は、マイナーバージョンが変わると soname も変わります(librerinku_device.so.0.5)。 - エクスポート:外部向けの関数にはすべて
RERINKU_APIが付いており、それ以外のシンボルはすべて非公開です。
スレッド:すべての関数は、どのスレッドからでも呼び出せます。コールバックは SDK 自身のスレッドで実行されるので、すぐに戻ってください。コールバックの中で rerinku_device_stop() や rerinku_device_destroy() を呼ばないでください。
メモリ:渡された文字列はすべてコピーされます。呼び出し側が渡すデータ(フレームデータを含む)の所有権はすべて呼び出し側のままで、呼び出しの間だけ有効であれば十分です。
デバイスのライフサイクル
rerinku_device_start() はすぐに戻ります。登録、MQTT 接続、障害からの復旧はバックグラウンドで行われ、失敗するとバックオフしながら再試行を続けます(1 秒から始めて、倍々に 30 秒まで)。そのため、ネットワークや Rerinku View より先にデバイスが起動しても、あとから自動でオンラインになります。on_status は、接続中、オンライン、オフラインの 3 つの状態を通知します。
- ストリーム送信:誰かが視聴しているときや録画中には、プラットフォームからストリーム送信のコマンドが届きます。SDK は
on_stream_startを呼び、WHIP セッションを確立してキーフレームを 1 つ要求します。不要になるとon_stream_stopを呼びます。サーバー側から切断されたセッション(再起動、ネットワーク切断)は、SDK が自動で作り直します。 - トークン:登録時に、プラットフォームがデバイストークンを発行します(Rerinku View の既定では 24 時間有効)。SDK は有効期間の 4 分の 3 が過ぎた時点で登録し直します。時間の計測はサーバーの時計を基準にするので、デバイスの時計が 1970 年のままでも問題ありません。HTTP リクエストが拒否された(401)場合や、MQTT のログインが拒否された場合は、すぐに登録し直します。
- デバイスキー:Rerinku View で
DEVICE_KEYを設定している場合、正しいsecretを持つデバイスしか登録できません。secret = base64(HMAC-SHA256(デバイスキー, sn))です。量産時は、出荷時に各デバイスへそれぞれのsecretを書き込み、デバイスキーそのものはファームウェアに入れないでください。
フレームの送信
rerinku_device_write() には、1 回の呼び出しごとに完全なアクセスユニット(映像 1 フレーム分のすべての NAL ユニット)か音声 1 フレームを渡し、キャプチャ時刻を添えます(マイクロ秒、単調増加、起点は任意。たとえば Rockchip MPI の u64PTS をそのまま使えます)。
- H.264/H.265 のチャンネルは既定で
RERINKU_BITSTREAM_FORMAT_AUTOを使い、フレームごとに形式を判別します。対応する形式は、3 バイトまたは 4 バイトのスタートコードを使う Annex-B、1/2/4 バイトの長さプレフィックス、単一の NAL です。データはまず検証されてから、4 バイトのスタートコードの形式に整えられます。 - SPS/PPS(H.265 では VPS も)はキャッシュされ、キーフレームにそれらが欠けていれば自動で補われます。新しく加わった視聴者は、必ずデコードできる映像を受け取れます。
- SDK は現在の GOP をキャッシュする(既定では映像 1 本あたり最大 1 MiB)ので、新しい視聴者はすぐにキーフレームから再生を始められ、次の IDR を待つ必要がありません。
- 書き込みがネットワークによってブロックされることはありません。視聴者がいないときは
RERINKU_WRITE_RESULT_NOT_STREAMINGを返し、オーバーヘッドはほとんどないので、フレームをずっと送り続けても構いません。on_stream_stopでエンコーダーを休止させれば、電力を節約できます。 - ネットワークが追いつかないときは
RERINKU_WRITE_RESULT_DROPPEDを返してそのフレームを捨て、新しいキーフレームを要求します。映像はそこから再開します。
対応コーデック:映像は H.264、H.265(ブラウザーが H.265 に対応していないと再生できないため、Web ページでの視聴が前提なら H.264 を送ってください)、音声は Opus、G.711 µ-law/A-law です。
プラットフォームからの要求
| コールバック / 関数 | 用途 |
|---|---|
on_command → rerinku_device_reply_command() / rerinku_device_fail_command() |
SDK 自身が処理しないコマンド(再起動、PTZ……)。params_json はコマンドのパラメーターで、あとから任意のスレッドで応答できます |
on_snapshot → rerinku_device_reply_snapshot() |
プラットフォームがサムネイルを求めています。JPEG を 1 枚撮って SDK に渡します。ハードウェア JPEG エンコーダーの準備ができたときに応答すれば構いません |
on_runtime_info |
誰かがデバイス詳細を開くと、プラットフォームが数秒ごとに問い合わせます。温度、CPU、メモリなどを入れた JSON オブジェクトを返します |
rerinku_device_send_event() |
デバイスからイベント(動体検知、アラーム……)を通知します。オフライン時は RERINKU_ERR_NETWORK を返し、SDK はキューに入れて後から送り直すことはしません。重要なイベントは自分で保存しておき、オンラインに戻ってから送ってください |
rerinku_device_upload_recording() |
デバイスがネットワーク切断中に自分で録画した MP4 をプラットフォームに渡します。プラットフォームは録画時刻に従って録画に組み込みます。アップロードが終わるまでブロックするので、専用のスレッドで呼び出してください |
デバイスの静的な情報(SoC、CPU、メモリ、センサー……)は、設定の ext_json で接続のたびに通知され、プラットフォームが保存してデバイス情報に表示します。リアルタイムのデータ(温度、CPU 使用率……)は、on_runtime_info で必要に応じて返します。プラットフォームが知っているキーは対応する名前で表示され、それ以外のキーは元の名前のまま一覧されるので、デバイスはプラットフォームの更新を待たずに独自のフィールドを追加できます。
プロトコルとネットワーク
| ステップ | プロトコル | 説明 |
|---|---|---|
| 登録 | HTTP(S) POST <endpoint>/ |
sn とデバイス情報を送り、WHIP のアドレス、MQTT のアドレス、ICE サーバー、デバイストークンを受け取ります |
| コマンド | MQTT 3.1.1 | ユーザー名は SN、パスワードはデバイストークン、クライアント ID は rerinku-dev-<sn> です。Rerinku View 内蔵のブローカーは、デバイスに自分のトピックしか使わせません |
| 映像 | WebRTC(WHIP) | 1 回の POST で SDP を交換します。ICE は libjuice が担当し、DTLS-SRTP で暗号化します |
| スナップショット、録画のアップロード | HTTP(S) | デバイストークン付き |
デバイスは Rerinku View の HTTP ポートと MQTT ポートにアクセスでき、UDP の送信(WebRTC のメディア)が許可されている必要があります。https:// と mqtts:// ではサーバー証明書が検証され、証明書の検証には正しい時計(NTP)が必要です。ca_file で必要なルート証明書だけを含む PEM ファイルを指定すれば、メモリを節約できます。
リソース使用量
RV1103(Cortex-A7 1.2 GHz)で、MinSizeRel、640×360 の H.264 約 0.8 Mbit/s と G.711A を送信した場合の測定値です。
librerinku_device.so(strip 後) |
0.39 MB。依存するのは C ライブラリだけ |
| アイドル時のメモリ(登録済み、MQTT オンライン) | 約 0.8 MB |
| ストリーム送信中のメモリ | 約 1.8 MB |
| ストリーム送信中の CPU | 1 コアの約 6% |
| スレッド | アイドル時 3 本、ストリーム送信中 5 本 |
rerinku_device_config_t で調整できるメモリ:
gop_cache_max_bytes:映像 1 本あたりの GOP キャッシュ。既定は 1 MiB。メモリの少ないデバイスでは 0 にしてon_keyframe_requestを実装し、エンコーダーの IDR から映像を始めることもできます。nack_history_packets:再送のために保持しておく送信済みパケット(1 個あたり約 1.2 KB)。既定は 512。メモリ 64 MB のデバイスでは 128〜256 をおすすめします。
既知の制限
- ストリームの送信(WHIP)のみで、受信はしません。trickle ICE と DataChannel には対応していません。
- 対応するのは POSIX システムだけで、Windows 版はまだありません。
- イベントはキューに入りません。オフライン中のイベントは自分で保存して、後から送る必要があります。
変更履歴の全体と新旧の名前の対応表は、リポジトリの CHANGELOG.md を参照してください。