見出し画像

Sony製カメラから無線で映像を取り出す - Containerに詰めたこの想いを

こんにちは、音楽技術部技術班のDDliaです。

 現在サークルではカメラを所有しており、カメラ映像をHDMI経由でキャプチャーボードでPCに取り込んで、それをNDIで各種PCに送信しています。これは最小限の遅延(600ミリ秒程度)で伝送できるのですが、いちいちPC起動してケーブルをいろいろ挿して… とやるのは面倒なのでどうにかカメラから直接無線で映像を飛ばせるようにします。

カメラについて

使っているカメラはSony製のHDR-CX680というものです。部室に2台あります。Wi-Fi接続をサポートしており、ライブストリーミングも可能ですが……

Ustream(現 IBM Video Streaming)への配信しか対応していない!!
で、そのIBM Video Streamingはいくらかかるかというと…

IBM Video Streamingの価格

一番安いプランで ¥21900/月 ですね。富豪じゃないのでさすがに出せない。

探してみる

私は富豪ではないので、どうにかならないか探してみるとGitHubでissueが開かれていました。

Sony製カメラでuStream以外へストリーミングするにはどうすればよいのかが議論されています。目を通してみると、

  • DNSスプーフィングで api.ustream.tv をLAN内のサーバに向ける

  •  カメラがhttp://api.ustream.tv/users/self/channels.json から接続先情報を読み取るので、そこに配信先アドレスを記述する

  • 配信サーバからカメラに対して定期的にpingを送る(AMFメッセージを偽装)

  • カメラに配信用資格情報を書き込む

あたりをやればよさそう。

テスト

RTMPサーバを建てる

まずは配信用のRTMPサーバを建てます。
docker-composeで簡単に立ち上げれるようにしているので、適当なLinuxサーバで立ち上げます。

$ sudo dnf update -y && sudo dnf install -y git podman podman-compose podman-docker
$ git clone https://github.com/TechnoTUT/rtmp-live-server.git
$ cd rtmp-live-server
$ podman-compose up -d

DNSスプーフィングで api.ustream.tv をLAN内のサーバに向ける

テスト環境ではNEC IX3110というルータを使っていて、DNSレコードを登録できる機能があるのでそれを使います。

dns host api.ustream.tv <カメラから映像を受け取るサーバのIPアドレス>

カメラから映像を受け取るRTMPサーバを建てる

先ほどのRTMPサーバとは別にカメラから映像を受け取るRTMPサーバを建てます。その辺に転がっているLinuxマシンを拾ってきましょう。配信サーバからカメラに対して定期的にpingを送る(AMFメッセージを偽装)必要があるので、nginxを自分でビルドします。
nginx-rtmp-moduleは以下を使います。
(https://github.com/transelement/nginx-rtmp-module だと自分の環境下ではビルドエラーが出たので修正してあります。)

$ sudo apt update -y && sudo apt install -y nginx git wget tar build-essential libperl-dev libgeoip-dev libgd-dev libpcre3 libpcre3-dev libxml2 libxslt1-dev libxslt1.1 libxslt1-dev
$ wget https://nginx.org/download/nginx-1.22.1.tar.gz && tar xzf nginx-1.22.1.tar.gz && rm -f nginx-1.22.1.tar.gz
$ wget https://www.openssl.org/source/openssl-3.3.1.tar.gz && tar xzf openssl-3.3.1.tar.gz && rm -f openssl-3.3.1.tar.gz
$ git clone https://github.com/TechnoTUT/nginx-rtmp-module.git
$ cd nginx-1.22.1
$ ./configure --with-cc-opt='-g -O2 -ffile-prefix-map=/build/nginx-AoTv4W/nginx-1.22.1=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' \
     --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=stderr \
     --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
     --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module \
     --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads \
     --with-http_addition_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_secure_link_module \
     --with-http_sub_module --with-mail_ssl_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_realip_module --with-http_geoip_module=dynamic --with-http_image_filter_module=dynamic \
     --with-http_perl_module=dynamic --with-http_xslt_module=dynamic --with-mail=dynamic --with-stream=dynamic --with-stream_geoip_module=dynamic \
     --add-dynamic-module=~/nginx-rtmp-module --with-openssl=~/openssl-3.3.1
$ make modules
$ mkdir -p /usr/lib/nginx/modules && cp ~/nginx-1.22.1/objs/*.so /usr/lib/nginx/modules
$ mkdir -p /var/www/ustream/users/self && chown -R www-data:www-data /var/www/ustream

ビルドしたら、/var/www/ustream/users/selfに以下の内容でchannels.jsonを配置します。

{
    "channels": {
        "12345678": {
            "broadcast_urls": [
                "rtmp://api.ustream.tv/mystream"
            ]
        }
    }
}

/etc/nginx/nginx.conf を以下に書き換えます。

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;

load_module /usr/lib/nginx/modules/ngx_rtmp_module.so;

events {
	worker_connections 768;
	# multi_accept on;
}

rtmp {
        access_log /var/log/nginx/ustream-rtmp-access.log;

        server {
                listen 1935;
                chunk_size 4096;
                max_message 5M;
                ping 20s;
                ping_timeout 36000s;
                force_ping on;

                application mystream {
                        live on;
                        meta off;

                        push <配信用RTMPサーバのURL>;

						# Reduce buffer size
            			wait_key on;
            			sync 10ms;
            			drop_idle_publisher 5s;
                }
        }
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	types_hash_max_size 2048;
	# server_tokens off;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# SSL Settings
	##

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;

	##
	# Gzip Settings
	##

	gzip on;

	server {
        	listen 80;
        	server_name api.ustream.tv;

        	error_log /var/log/nginx/ustream-error.log;
        	access_log /var/log/nginx/ustream-access.log;

        	location / {
                	root /var/www/ustream/;
        	}

            location /stat {
                rtmp_stat all;
            }
	}
}

各種設定が済んだら、nginxを起動して配信サーバを起動します。

$ systemctl enable --now nginx

カメラに配信用資格情報を書き込む

以下のSony-PMCA-REを使用して、書き込みを行います。

Releaseから pmca-console.exe をダウンロードします。
pmca-console.exeと同じディレクトリに以下の内容でstream.cfgを作成します。

[
    [
        "twitterEnabled",
        0
    ],
    [
        "twitterConsumerKey",
        ""
    ],
    [
        "twitterConsumerSecret",
        ""
    ],
    [
        "twitterAccessToken1",
        ""
    ],
    [
        "twitterAccessTokenSecret",
        ""
    ],
    [
        "twitterMessage",
        "Live Streaming from Handycam by Sony"
    ],
    [
        "facebookEnabled",
        0
    ],
    [
        "facebookAccessToken",
        ""
    ],
    [
        "facebookMessage",
        "Live Streaming from Handycam by Sony"
    ],
    [
        "service",
        0
    ],
    [
        "enabled",
        1
    ],
    [
        "macId",
        "1234567890123456789012345678901234567890"
    ],
    [
        "macSecret",
        "123456789012345678901234567890123467890"
    ],
    [
        "macIssueTime",
		"a9315a5f00000000"
    ],
    [
        "unknown",
        1
    ],
    [
        "channels",
        [
            12345678
        ]
    ],
    [
        "shortURL",
        "http://ustre.am/1AAAA"
    ],
    [
        "videoFormat",
        3
    ],
    [
        "supportedFormats",
        [
            1,
            3
        ]
    ],
    [
        "enableRecordMode",
        0
    ],
    [
        "videoTitle",
        "Test"
    ],
    [
        "videoDescription",
        "Test"
    ],
    [
        "videoTag",
        "Test"
    ]
]

カメラをMTPモードで接続し、PCで書き込みます。

> pmca-console-v0.18-win.exe stream -w stream.cfg

動作確認

カメラをライブストリーミングモードに変更して、ストリームを開始します。

うまく動いたっぽい。

部室にデプロイ

テストはうまく行ったので、これを部室やコモンズイベントでも使えるようにしておきます。テスト環境と同じ手順で構築すれば動くわけですが、実際にはカメラが2台分あり、台数分のサーバ構築をするのは面倒です。
コンテナイメージを錬成して、部室のKubernetes環境に環境変数だけ変えてデプロイします。コンテナは神。

コンテナに想いを詰める

Dockerfileを書いて、それをビルドして ghcr.io で公開します。

https://ghcr.io/TechnoTUT/sonycam-nginx からpullできます。

マニフェストを書く

Kubernetesに載せるために、まずはマニフェストを書きます。

カメラから映像を受け取るnginxサーバは、先程錬成した ghcr.io/technotut/sonycam-nginx を使用し、api.ustream.tvをローカルIPで返すDNSサーバはCoreDNSを使っています。マニフェストは以下のように書いています。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cam1-nginx-deployment
  labels:
    app: cam1-sonycam-nginx
  namespace: cam1
spec:
  selector:
    matchLabels:
      app: cam1-sonycam-nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: cam1-sonycam-nginx
    spec:
      containers:
      - name: cam1-sonycam-nginx
        image: ghcr.io/technotut/sonycam-nginx:main
        env:
        - name: SERVER_URL
          value: rtmp://rtmp-service.live.svc.cluster.local:1935/live/cam1
        ports:
        - containerPort: 1935
        - containerPort: 80
        resources:
          limits:
            cpu: 1
            memory: 256Mi
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: cam1-nginx-service
  labels:
    app: cam1-sonycam-nginx
  namespace: cam1
  annotations:
    external-dns.alpha.kubernetes.io/hostname: cam1.kube.technotut.net
spec:
  selector:
    app: cam1-sonycam-nginx
  type: LoadBalancer
  loadBalancerIP: 10.33.71.1
  ports:
    - name: rtmp
      protocol: TCP
      port: 1935
      targetPort: 1935
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80

envで配信サーバのURLを指定し、metadata.annotationsでホスト名を外部のDNSサーバに登録して cam1.kube.technotut.net でアクセスできるようにしています。

ついでに、配信用RTMPサーバ用のマニフェストも作っておきます。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rtmp-deployment
  labels:
    app: rtmp-live-server
  namespace: live
spec:
  selector:
    matchLabels:
      app: rtmp-live-server
  replicas: 1
  template:
    metadata:
      labels:
        app: rtmp-live-server
    spec:
      containers:
      - name: rtmp-live-server
        image: ghcr.io/technotut/rtmp-live-server:main
        ports:
        - containerPort: 1935
        - containerPort: 8080
        resources:
          limits:
            cpu: 1
            memory: 256Mi
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: rtmp-service
  labels:
    app: rtmp-live-server
  namespace: live
  annotations:
    external-dns.alpha.kubernetes.io/hostname: live.kube.technotut.net
spec:
  selector:
    app: rtmp-live-server
  type: LoadBalancer
  loadBalancerIP: 10.33.0.1
  ports:
    - name: rtmp
      protocol: TCP
      port: 1935
      targetPort: 1935
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8080

ArgoCDに登録

ArgoCDでマニフェストを適用します。

ここで紹介した手順で操作します。

argocd login kube.intra.technotut.net:30001
kubectl create namespace cam1
argocd app create cam1 --repo https://github.com/TechnoTUT/k3s.git --path manifest/cam1 --dest-server https://kubernetes.default.svc --dest-namespace cam1
argocd app sync cam1
argocd app set cam1 --sync-policy automated

Wwb UIで確認してみると、ちゃんと動いていることが確認できますね。

動作確認

使い方

OBSのメディアストリームなり、VLCなりで
rtmp://live.kube.technotut.net/live/cam1
rtmp://live.kube.technotut.net/live/cam2
を開くと映像を受信できます!

まとめ

というわけで、部室のカメラから直接PCに映像を飛ばせるようにしました。今後のイベント等で活用してもらえると嬉しいです。
また、部室にKubernetesの環境を用意してあるので、ぜひみなさんも部内に提供したいサービスを気軽にコンテナに詰めて動かしに来てください。

この記事が気に入ったらサポートをしてみませんか?