WPP
ラズパイカメラ + nginx で hls(RTMP)サーバ

ここのところカメラでストリーミングの実験をいろいろしているのですが、今回は hls サーバの実験です。RTMPサーバと呼ぶべきなのかもしれませんが何となくあやふやです。

内容的には IoT PLUS さんの内容のほぼまんまですが、ffmpegのパラメータとsystemd での起動に変更したのでさらします。
まあ、誰かの役に立つかもしれないがわからない。

それにしても ffmpeg のコマンドラインは私にとって鬼門です。ドキュメント読む気にイマイチならないのよ。誰か分かりやすい解説ページつくってくれないでしょうか ?

手順

必要な物は、ffmpeg と nginx です。apache は追ってないですが nginx に rtmp モジュールがあってお手軽なのでその例があるのだと思います。

自分の例では http basic auth も設定していないので apache2-utils は不要です。php-fpm も入れません。

インストール

$ sudo apt update
$ sudo apt install ffmpeg nginx libnginx-mod-rtmp

出力フォルダの準備

こちらも丸パクリですが、/dev/shm のリンクを作成します。

$ sudo mkdir -p /var/www/html/live
$ cd /var/www/html/live
$ sudo ln -s /dev/shm hls

index.html の準備

/var/www/html/index.html を作成します。中身はとりあえずパクリです。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8"/>
  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
</head>

<body>
  <video id="video" controls width="100%"></video>
  <script>
    if(Hls.isSupported()) {
      var video = document.getElementById('video');
      var hls = new Hls();
      hls.loadSource('live/hls/stream.m3u8');
      hls.attachMedia(video);
      hls.on(Hls.Events.MANIFEST_PARSED,function() {
      video.play();
    });
   }
  </script>
<p>
  iPhoneなどで再生されない場合は、<a href="live/hls/stream.m3u8">こちら</a>をクリック。
</p>
</body>
</html>

nginx の設定

/etc/nginx/rtmp.conf ファイルを作成し、そいつを /etc/nginx/nginx.conf でインクルードしてやります。

もともと nginx.conf のなかで. conf.d の中にある *.conf ファイルは読み込まれるのですが、設定ファイルの位置的にまずいらしくエラーになるのでこの構成にしてます。

更に、/var/www/html のロケーションのハンドラを /etc/nginx/conf.d/default.conf に書きます。

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        allow play all;
        access_log /var/log/nginx/rtmp_access.log;

        application live {
            live on;
            hls on;
            record off;
            hls_path /var/www/html/live/hls;
            hls_fragment 1s;
            hls_type live;
        }
    }
}

これを下の nginx.conf でインクルードしてやります。

events {
----
}
http {
----
}

# http ブロックの外になるように以下の行を追加
include /etc/nginx/rtmp.conf;

/etc/nginx/conf.d/default.conf はこうなります。リッスンポートは 8080 に変更してます。

server {
    listen 8080;
    proxy_set_header   X-Forwarded-For     $proxy_add_x_forwarded_for;
    access_log /var/log/nginx/access.log combined;
    error_log /var/log/nginx/error.log warn;

    location = /favicon.ico {
        access_log off;
        empty_gif;
        expires 30d;
    }

    location / {
        #auth_basic "Web Cam Streaming";
        #auth_basic_user_file /var/www/.htpasswd;

        root /var/www/html;
        index index.html;
        set_real_ip_from    127.0.0.1;
        real_ip_header      X-Forwarded-For;
    }
}

nginx を起動しておきます

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

ffmpeg の起動テスト

一旦、ffmpeg で動画を見れるかテストします。今回の環境ではマイクがないので、音声関連のパラメータはざっくり削除しています。

$ ffmpeg -f v4l2 -thread_queue_size 8192 -s 720x480 -i /dev/video0 \
    -r 15 -c:v h264_omx -b:v 512k \
    -vf "rotate=180*PI/180" \
    -f flv rtmp://localhost/live/stream

パラメータの説明

-s 720×480: 解像度
-r 15: フレームレート(15 フレーム / sec)
-vf “rotate=180*PI/180”: 画像を 180度 ローテーションする。(ラジアンに変換)

すべてがうまくいっていれば、こんなふうにブラウザに動画が表示されます。

systemd の設定

参考にしたページでは起動スクリプトで終わっていましたが、systemd に登録することにします。まずは先ほどの ffmpeg 起動コマンドをスクリプト化します。

さっきは、直打ちしていたパラメータを環境変数に逃がしてあげます。更にちょっと乱暴ですが起動時に ffmpeg のプロセスを一旦全部殺します。ロックファイルで制御するのが丁寧だとおもいますが、面倒なのでよしとします。

#!/bin/bash 

ROTATE=180
BITRATE=512k
FRATE=15
RES=720x480
# RES=1920x1080


# kill other instances of ffmpeg
pkill ffmpeg

# start camera server
ffmpeg -f v4l2 -thread_queue_size 8192 -s $RES -i /dev/video0 -r $FRATE \
    -c:v h264_omx -b:v $BITRATE \
    -vf "rotate=$ROTATE*PI/180" \
    -f flv rtmp://localhost/live/stream > /dev/null 2>&1 </dev/null&

これを呼び出す systemd ユニットを作成します。名前はrtmp-camera.service としましたが好きなものに変えて構いません。

ExecStart に 上のシェルスクリプトのフルパスを指定します。systemd unit ファイルでは フルパスを指定します。そうでないとサービス起動時にエラーになります。

パスはシェルスクリプトを作成した場所に適宜変更してください。

[Unit]
Description=RTMP camera server with ffmpeg.

[Service]
ExecStart=/opt/rtmp/start.sh
Restart=always
#Type=simple
Type=oneshot
RemainAfterExit=yes
User=pi

[Install]
WantedBy=multi-user.target

2024-4-12 Type=oneshot に修正し、RemainAfterExit=yes を追加。

systemctl で有効にしていきます。

$ sudo systemctl enable /opt/rtmp/rtmp-camera.service
$ sudo systemctl daemon-reload
$ sudo systemctl start rtmp-camera.service

daemon-reload はもしかしたら不要かもしれません。とにかく enable した後、ちゃんと起動しているか確かめてください。上手く動いていればブラウザでカメラで撮影している動画が流れているはずです。

参考

超簡単! ffmpeg + rtmp + nginxを使ってraspberry pi + webカメラのストリーミング環境を作る(raspbian buster版) | IoT PLUS

子供の笑顔と笑い声を聞く為に ffmpeg + Nginx + RTMP on RaspberryPI – 長生村本郷Engineers’Blog

【ffmpeg】簡単ネットワークカメラの作り方
ここの例は、hls ではなく jpg 画像を更新し続けているのでクライアントからすると motionJPEG 形式になるのだと思う。

VS Code の設定がおかしくなった時

気に入って数年使っている VS Code ですが、たまに1つのプラグインのロードに失敗したっきり他のプラグインもアクティブにならないという困った状態になることがあります。

『設定』の Sync 機能で最新の状態を各 PC で同期できるようにしているのですが、最新状態にしても復帰できないことがありました。

アプリを再インストールしても状況が改善しません。

『設定』を同期している前提ですが、一度PC の設定データを削除すると正しいデータが降ってきて機能が復帰する場合がありあます。

設定の場所

Macの場合:
/Users/ログインID/Library/Application\ Support/Code/User

Winの場合:
%appdata%\code\User
または
C:\Users\ログインID\AppData\Roaming\Code\User

回復方法

通常は、上記フォルダの settings.json を消すと正しい settings.json がダウンロードされて正しく動きますが、user フォルダのいずれかのデータがおかしくなっている時はこの方法では解決しないので、User フォルダごと削除します。

前提にも書いていますが、同期していない場合はそもそも設定のダウンロードができないのでご注意下さい。

簡単にいうとクリーンインストールの方法に近いことをすることになります。

android端末をRTSPカメラサーバにするアプリ

仕事の関係でRTSPサーバをいくつか調べたので、リストアップしておく。

監視カメラ、NVR に関連するシステム周りを調べる必要があったのですが、RTSP は監視カメラと NVR間のストリーミングを流すプロトコルとして使われているようです。

つまり、Android 端末が RTSP を吐ければ監視カメラとして使用できるわけです。

世の中には 2 台のスマホに同じアプリを入れて一台はカメラ、一台はレコーダとするものが沢山ありますが、これらも内部では RTSP を実装しているものが多いと思います。

今回は、agentDVR とか frigate を NVR として使いたいのでアプリにロックインされるタイプのものは不可です。世間ではその手のものの紹介は沢山あり自分が調査に手間取ったのでさらします。

  • IP Webcam
    黒いレンズの円形アイコン
  • NT RTSP Onvif
    水色のガン型監視カメラアイコン
  • IP Camera
    オレンジの監視カメラアイコン、結構いい感じ
  • RTSP Security Camera
    犬のアイコン試した限りでは速度が出ないかも
  • RTSP Camera Server Pro
    試用モードだと数分のみと制限がきついからあまり試していない

あくまで個人的な感覚ですが、単純に上から順に好印象な順です。

アイコン貼るの面倒なので、説明で茶を濁していますがどうしても似たような名前が多いのでアイコンの説明を入れています。

これらは全部 agentDVR に接続できました。長時間稼働とかスタビリティとかはご自身でご判断してください。

Alfred は UI も素敵だし使いやすいが同アプリ間に閉じてるんだよね。

拡張版ls: exa から eza へ

python 環境がおかしくなって、brew doctor してみるとexa がメンテナンスされてないからリプレースしろとのお達しがあり、eza をインストールした話。

exa はざっくりいうと、いい感じの ls ってとこでしょうか。

実際の brew のメッセージはこんな感じです。

$ brew doctor
Please note that these warnings are just used to help the Homebrew maintainers
with debugging if you file an issue. If everything you use Homebrew for is
working fine: please don't worry or file an issue; just ignore this. Thanks!

Warning: Some installed formulae are deprecated or disabled.
You should find replacements for the following formulae:
  exa
  ..... その他もろもも

“exa replacement” とか “exa alternative” とかで検索すると見つかったのが eza。

$ brew install eza でインストールしてみましたが、exa とあんまり変わらないっぽい。

exa は “A modern replacement for ls.” を標榜しているのですが
他方、新しい eza は “A modern, maintained replacement for ls.” とメンテナンスされていることを協調しています。修正を待てなかったユーザーたちの渇望感が伝わってきます。

eXa の2つ先だから eZa なんだろうけど、ちょっと覚えにくい。

参考

eza-community/eza: A modern, maintained replacement for ls

Netgear スイッチを見つけるツール

自宅では netgear の PoE スイッチを使っているのですが、これは IPアドレスが振られて VLAN やらいろいろ機能があるらしいです。まあほとんど使いこなしていないのですが。。。

で、ずぼらに管理しているのでその IP アドレスを忘れてしまった時などに見つけてくれるツールが公式から出ています。

Switch Discovery Protocol | Product | Support | NETGEAR

これ使うと簡単に見つけ出すことができます。ホントをいうとルータで固定アドレス割り当てているのでそこから見つければいいのですがちょっと面倒なんですよね。

参考

Switch Discovery Protocol | Product | Support | NETGEAR

Xiaomi Mi Pad 4 に PixelExperience をインストール

何処かで中古 + カスタム ROM (LinegeOS) インストール済みで買った Mi Pad 4 がイマイチ調子悪いので、OS を入れ替えてみた。

XDA に行くと PixelExperience がオフィシャル版であるっぽいのでそれにした。オフィシャルだと多分 OTA が効くのでアップデートが楽なのでそうした。こだわりがあるならアンオフィシャルの他の ROM でももちろんいいと思う。

手順

公式通りにやればいいので簡単。

  1. Recovery ダウンロード & fastboot flash recovery
  2. リカバリーに入る
  3. Apply update から sideload を選択し
  4. PC から adb sideload を実行する

注意しなければならないのは、bootloader アンロックしなきゃいけないが、一ヶ月くらいかかる。やり方は調べて下さい。

あと、TWRP とかのサードパーティ製リカバリーだとインストールできないので注意が必要。

まあ、説明みてキーワードを拾い読みすれば十分間違えないくらい簡潔な説明になっているから大丈夫でしょう。

参考

xapk ファイルを adb でインストール

さて、相変わらずアンドロイド端末をいじる日が続いていますが、APKPure などからダウンロードした .xapk ファイルを ADB コマンドでインストールする話です。端的にいうと install-multiple っていうアクションがあるよってことです。

今扱っているアンドロイド風端末は、Google Play が入っていません少し特殊なデバイスなので余計なものが一切入っていないです。あるのはいくつかのボタンと画面くらいです。

Google Play が入っていないということはストアからアプリをインストールできないということですが、それはちょっと困るので APKPure から落としてきました。

APKPure アプリをインストールすればいいのですが、今回はパスしてインストールしたい。

方法

参考にある通り、.xapk は zip ファイルなんだそうです。(file コマンド? とかでわかりそうですね。)なので展開すればインストールできるようです。

$ unzip -d tmp original.xapk
$ cd tmp

# 展開の結果、aa.apk bb.apk cc.apk ができたとするとこんな感じになる
$ adb install-multiple aa.apk bb.apk cc.apk

余談ですが、.xapk になっているタイプのアプリを Apk Extractor でapk を抽出しても上手くきませんでした。あまり追求してないので、もしかしたらやり方があるのかもしれないですがアプリが複数の apk 構成されているという情報がとれないのかもしれません。

参考

How to install xapk, apks, or multiple-apks via adb? – Android Enthusiasts Stack Exchange
ここにずばり書いてある通りの内容です。

Android 端末と rsync する

android 端末からファイルをコピーするには adb pull でファイルを抜き取れますが、毎回全コピーするのはちょっといやです。

linux 系なら rsync コマンドが使えますが android 端末には入っていないようです。そもそも rsync はローカルファイルファイルシステムや sshfs 位しか対応していないはずで adb 経由では使えなそう。

github に Better ADB Sync というのがあり試してみる

インストール

個人的に pipenv を使っているので pipenv ベースで。readme では pip でグローバルインストールしている

$ pipenv shell
$ pipenv install BetterADBSync

実行

ほぼ adb push / pull と同じ感じなのですぐにイメージはつかめると思う。

# アンドロイド から PC へ同期
$ pipenv run adbsync pull /sdcard ./sd

# PC から アンドロイド へ同期
$ pippenv run adbsync push ./sd /sdcard

グローバルインストールするか、パスを通せば pipenv run の部分は不要になります。

参考

jb2170/better-adb-sync: Completely rewritten adbsync with –exclude