できる mirakc

masnagam
空気録学電子版【公式】
41 min readJun 13, 2020

--

最新版「できるmirakc」はこちら

はじめまして、masnagam です。mirakc の開発をしています。

これまで mirakc は、ソフトウェア開発の経験がある人が使用することを前提としてドキュメントを作成してきました。しかし、本記事では、対象読者をソフトウェア開発者に限定せず、すでに EPGStation や TVTest を使った録画及び視聴環境を持っている人が、mirakc を利用できるようになるところまで順を追って説明します。最終目標は、既に稼働している録画・視聴環境中の Mirakurun を、mirakc に置き換えて動かすことです。

本記事では、mirakc 0.9.0を使って説明しています。最新のmirakcに対する文書を探している人は、こちらを参照してください。

🧪 mirakc って何ですか?

始めて名前を目にした人もいると思うので、簡単に説明しておきます。

mirakc は、Raspberry Pi のような シングルボードコンピュータ (SBC) をターゲットデバイスとして、Rust 言語で記述された Mirakurun クローンです。

大きな改善点として、CPU 使用率及びメモリー使用量の改善が挙げられます。例えば、8チャンネル同時ストリーミング時のリソース使用効率が、Mirakurun と比べて以下のように向上しています。

  • CPU 使用率は 2/3
  • メモリー使用量は 1/60

また、GC を使用しない実装となっていることから(一部、参照カウント方式のメモリー管理を行っています)、CPU 使用率・メモリー使用量共に安定的に推移します。

mirakc は、Mirakurun の完全クローンには至っておらず、EPGStation を動作させるために必要な Web API のみを実装しています。そのため、残念ながら Chinachu では使えません(もちろん、不足している Web API を実装すれば動くとは思います)。

開発を始めるまでの経緯

一般的に、SBC は以下のような特徴を持っています。

  • 1GHz 程度の低クロック CPU
  • 1GB 程度のメモリー
  • 十数ワットの電力消費

一昔前のデスクトップ PC よりも貧弱な環境です。動画を ffmpeg でソフトウェアエンコードするといった使い方には向いていません。しかし、24時間稼働し続けるちょっとしたサービスの運用には最適です。

ちょっとしたサービス。そう、録画サーバーに向いていると思いませんか?私もそう考えて、棚の上で埃を被っていた Raspberry Pi 3B に手を伸ばしました。

早速メルカリで PX-S1UD とカードリーダーを手に入れました。マイクロ SD カードに Raspbian を書き込み、チューナーデバイスのファームウェアと Mirakurun をインストールしました。

Mirakurun は申し分ない速度で動作しました。 CPU 使用率も問題ありませんでした。心配していたメモリー使用量は 150MB 前後であり、予想よりも少ない消費量で動作しました。ちょっとした成功に高揚感を感じた私は、何を思ったのか ROCK64 と PX-Q3U4 を購入していました。

当時(2019年第1四半期)、Raspberry Pi 3B に搭載されているイーサネットの上限 が 100Mbps であるため、Mirakurun で4つの TS ストリームを同時に送信することは困難であることが知られていました(Qiita の記事だったと記憶していますが、その記事はすでに削除されてしまっているようです)。そこで、ROCK64 の登場です。今なら Raspberry Pi 4 を購入する場面ですが、その当時、1Gbps イーサネット搭載で扱いやすそうな SBC の1つが ROCK64 でした。価格も手頃だったので悩むことなく購入しました。

準備を整え、8つの TS ストリームの同時送信を試したところ、メモリーが枯渇しました。後で分かったことですが、Mirakurun でも 2GB あれば8つの TS ストリームを同時送信可能です。当時私が使用していた ROCK64 に搭載されていたメモリーは 1GB 。ROCK64 のメモリーのラインナップ 1/2/4GB 。数千円をケチって安いのを買ったことが mirakc 開発の始まりとなりました。

🧪 Docker を使ったインストール

本記事では、Dockerを使ったmirakcのインストール手順についてのみ説明します。

CPUパワーが貧弱なSBCでDocker使って大丈夫なのかと危惧する人もいるかも知れませんが、実際に動かしてみた経験から問題ないことが分かっています。Docker コンテナーとして実行する場合、ホストOS上で直接実行する場合に比べてオーバーヘッドが存在 します。しかし、このオーバーヘッドは問題になるほど大きくありません。さらに、この オーバーヘッドに対して、以下の費用対効果が十分にあると私は考えています。

  • インストールの容易さ
  • 環境依存度の低減
  • ホストOSアップデートの影響を受けにくい

もちろん、Docker を使わず、ホスト OS に mirakc を直接インストールすることも可能です。その手順は、Docker イメージのビルド手順を記述した Dockerfile にすべて記述されていま す。mirakc は少し特殊な事情があり、Dockerfile を dockerfile-gen スクリプトを使って 生成していますが、生成された Dockerfile に書かれている手順通りに実行すればインスト ールは完了します。

ただ、mirakc をホスト OS に直接インストールする場合は、必要なソフトウェアを個別にビ ルドする必要があるため、初心者向きとは言えません。というのも、一般的に、SBC の CPU は普段皆さんが使っているようなデスクトップ PC 向け CPU に比べて非常に非力であるため、必然的に必要なソフトウェアをデスクトップ PC 上などでクロスビルドすることになるからです。例えば、過去に ROCK64 上で mirakc をリリースビルドしたことがありますが、1時間では終わらなかったと記憶しています。

このような事情もあり、mirakc では Docker を使ったインストールを推奨しています。

Raspberry Pi のセットアップ

Raspberry Pi のセットアップ手順については、「Raspberry Pi 4で構築する録画マシン」 に記載されているため詳細な説明を行いません。

以降の説明は:

  • Raspberry Pi 3B
  • 2020–02–13-raspbian-buster-lite.img
  • PX-Q3U4 + nns779/px4_drv

で動作確認をしましたが、以下のような環境でも動作します:

  • Raspberry Pi 2B 以上 + Raspbian (arm32v7)
  • ZeroPi + Armbian (arm32v7)
  • ROCK64 + Armbian (arm64v8)

デバイスの準備

デバイスにログインしないことには何も始まりません。 Raspbian を書き込んだマイクロ SD カードに以下の修正を適用します。

# SSH の有効化
touch /Volumes/boot/ssh
# WiFi の無効化(使わない場合)
echo "dtoverlay=pi3-disable-wifi" >>/Volumes/boot/config.txt
# Bluetooth の無効化(使わない場合)
echo "dtoverlay=pi3-disable-bt" >>/Volumes/boot/config.txt

必要に応じて以下のカーネルパラメーターを設定してください.

# cmdline.txt
... coherent_pool=4M ...

マイクロ SD カードを Raspberry Pi に挿入し、電源をオン。有効化してある ssh を使ってログインします。

# 必要に応じてホストを追加
cat <<EOF >>~/.ssh/config
Host pi
HostName raspberrypi.local
User pi
EOF
# パスワード入力が面倒なので公開鍵をコピー
ssh-copy-id pi
# ログイン
ssh pi

raspberrypi.local に接続できない場合、何らかの理由で mDNS が機能していないことを意味します。

ログインしたら、安全のために以下を実行しておきます。

# パスワード変更
passwd
# root をロック
sudo passwd -l root

SDカードの寿命が気になる人は、以下の設定に追加して tmpfs の設定も行っておきましょう。

# swap の無効化
sudo systemctl stop dphys-swapfile
sudo systemctl disable dphys-swapfile
sudo rm -f /var/swap
free -h
# ntp の有効化
echo 'NTP=ntp.nict.jp' | sudo tee -a /etc/systemd/timesyncd.conf >/dev/null
sudo systemctl restart systemd-timesyncd
systemctl status systemd-timesyncd | grep nict

ソフトウェアを更新し、再起動します。

sudo apt-get update
sudo apt-get dist-upgrade
sudo reboot

Docker のインストール

今回利用した Raspbian の APT リポジトリーには、docker.io および docker-composeパッケージが既に含まれています。しかし、

  • docker: 18.09.1
  • docker-compose: 1.21.0

と一年以上前のバージョンです。そこで今回は、Raspbian APT リポジトリーを使わずに、これらのツールをインストールします。

まずは docker をインストールします。Docker 公式ドキュメント では get.docker.com スクリプトの使用は推奨されていませんが、Raspbian でのインストール手順が公式ドキュメントに記載されていないため、Raspbian でも動作する get.docker.com スクリプトを使用します。

# Docker のインストール
#
# 動作確認のため使用した `get.docker.com` スクリプトのコミットハッシュは
# `442e66405c304fa92af8aadaa1d9b31bf4b0ad94`
curl -sSL https://get.docker.com | sudo sh

# 念の為、動くことを確認
docker version

次に docker-compose をインストールします。残念ながら、公式リポジトリー docker/compose では amd64 バイナリーしか配布されていません。そこで、私が作成した masnagam/sbc-scriptsget-docker-compose スクリプトを使用します。このスクリプトを使用すると、Docker Hub にアップロード済みの masnagam/docker-compose イメージからバイナリーを抽出できます。

# Docker イメージから docker-compose コマンドを抽出
curl -fsSL https://raw.githubusercontent.com/masnagam/sbc-scripts/master/get-docker-compose | \
sh | sudo tar -x -C /usr/local/bin
# バージョンが表示されれば OK
docker-compose version

上記で利用した Docker イメージは Debian/Buster ベースですが、今回使用している Raspbian のバージョンも Debian/Buster をベースとして作成されているため、問題なく動作します。

上記で利用した masnagam/docker-compose イメージは、GitHub Actionsと公式リポジトリーに含まれている Dockerfile を使ってビルドしたマルチアーキイメージです。現時点では、以下のアーキテクチャーのみサポートしています。

amd64

  • 一般的なデスクトップ PC に搭載されている Intel 系や Ryzen 系

arm32v7

  • Raspberry Pi 2 以降の Raspbian や ZeroPi など

arm64v8

  • ROCK64 向け Armbian など
  • Raspberry Pi 2B v1.2 以降の 64bits OS

残念ながら、古い Raspberry Pi や Raspberry Pi Zero 系 (arm32v5) では利用できません。

mirakc のインストール

細かい設定は後回しにして、mirakc を起動してみましょう。

まず、以下のような docker-compose.yml を作成します。

version: '3.7'services:
mirakc:
image: masnagam/mirakc:alpine
container_name: mirakc
init: true
restart: unless-stopped
ports:
- 40772:40772
volumes:
- ./config.yml:/etc/mirakc/config.yml:ro
environment:
TZ: Asia/Tokyo
RUST_LOG: info

次に、同じフォルダー内に config.yml を作成します。

server:
addrs:
- http: 0.0.0.0:40772

最後に、mirakc を起動します。

# mirakc コンテナーをバックグラウンドで起動
$ sudo docker-compose up -d
Starting mirakc ... done
# バージョン文字列の取得
$ curl -s http://localhost:40772/api/version
"0.9.0"
# mirakc コンテナーのシャットダウン
$ sudo docker-compose down
Stopping mirakc ... done
Removing mirakc ... done
Removing network pi_default

mirakc の起動に成功していれば、"0.9.0" のようなバージョン文字列が表示されます。

どうでしょうか?思っていたより簡単ですよね。

masnagam/mirakc イメージ

Docker Hub で配布している masnagam/mirakc イメージには、mirakc と recpt1 などの主要なツールがすでに含まれています。多くの場合、各自でツールをソースからビルドしたりインストールする必要はありません。

mirakc/docs/docker.md に記載されているように、masnagam/mirakc イメージには複数のタグがあり、この中から自分の目的に合ったタグを選択します。以下に代表的なタグを列挙します。

  • latest … debian のエイリアス
  • debian … 最新リリースの Debian/Buster ベースのイメージ
  • alpine … 最新リリースの Alpine/3.11 ベースのイメージ
  • master … master-debian のエイリアス
  • master-debian … GitHub master ブランチの最新コミットから作成した Debian/Buster ベースのイメージ
  • master-alpine … GitHub master ブランチの最新コミットから作成した Alpine/3.11 ベースのイメージ

これらのイメージは全てマルチアーキイメージです。以下のアーキテクチャーをサポートしています。

  • amd64
  • arm32v6
  • arm32v7
  • arm64v8

Raspberry Pi などの SBC で実行する場合は、先の例で使用した masnagam/mirakc:alpine を選択することを推奨しています。詳しい理由については mirakc/docs/notes.md を見てください。

🧪 mirakc の設定

mirakc を起動できるようになったので、次はチャンネルやチューナーなどの設定を行います。

Mirakurun では、設定する項目ごとに YAML ファイルが存在します。例えば、チャンネル設定なら channels.yml という感じです。一方、mirakc では、config.yml という1つの YAML ファイルにすべての設定を記述します。

本記事では、最低限動かすために必要な設定項目についてのみ説明を行います。他の設定項目については、mirakc/docs/config.md を参照してください。

また、ログに関する設定は、config.yml ではなく、いくつかの環境変数を用いて設定します。詳細については、mirakc/docs/logging.md を参照してください。

チャンネル設定

mirakc には、利用可能なチャンネルをスキャンする機能は備わっていません。しかし、Mirakurun のチャンネル設定と一定の互換性があるため、Mirakurun の設定を流用可能です。

Mirakurun の channels.yml の例:

# NHK 総合
- name: NHK
type: GR
channel: '27'
# NHK BS1
- name: BS1
type: BS
channel: BS15_0
serviceId: 101

mirakc の config.yml の例:

# `channels` プロパティの要素としてチャンネルを登録
channels:
# NHK 総合
- name: NHK
type: GR
channel: '27'
# NHK BS1
- name: BS1
type: BS
channel: BS15_0
# プロパティ名が違う
# 配列で複数指定可能
services: [101]

基本的には、以下のような置き換えで Mirakurun の channels.yml から変換可能です。

| Mirakurun のプロパティ名  | mirakc のプロパティ名        |
|-------------------------|---------------------------|
| serviceId | services (Array) |
| satelite | (n/a) |
| space | (n/a) |
| (None) | excluded_services (Array) |

チューナーコマンドとして recpt1 を使用する場合、以下のような Mirakurun 用のチャンネルスキャンスクリプトを利用可能です。

どちらも epgdump を使用していますが、現時点では masnagam/mirakc イメージには含まれていません。 masnagam/mirakc イメージからカスタムイメージを作成する方法については後述します。

チューナー設定

チャンネル設定と同様に、チューナー設定も Mirakurun と一定の互換性があります。

Mirakurun の tuners.yml の例:

- name: gr1
types: [GR]
command: recpt1 --device /dev/px4video2 <channel> - -
decoder: /usr/local/bin/decoder

mirakc の config.yml の例:

tuners:
- name: gr1
types: [GR]
command: recpt1 --device /dev/px4video2 {{channel}} - -

ここで、tuners[].command にはテンプレート文字列が指定されており、{{}} で囲われた識別子(上記の例では channel)は、テンプレートパラメーターと呼ばれる特別な値です。 mirakc は、テンプレート言語として Mustache を採用しており、リスト型のテンプレートパラメーターの展開もサポートしています。tuners[].command 以外にもいくつかの設定項目でテンプレート文字列が指定可能で、設定項目毎に使用可能なテンプレートパラメーターは異なります。

mirakc のチューナー設定には decoder プロパティは存在しません。その代わりに、後述するフィルター設定を使用します。

Docker を使用している場合、以下のように docker-compose.yml に使用するデバイスファイルを記述する必要があります。

version: '3.7'services:
mirakc:
image: masnagam/mirakc:alpine
container_name: mirakc
init: true
restart: unless-stopped
# `devices` を追加
devices:
# チューナーコマンドで利用するデバイスファイルを列挙
- /dev/px4video2
ports:
- 40772:40772
volumes:
- ./config.yml:/etc/mirakc/config.yml:ro
environment:
TZ: Asia/Tokyo
RUST_LOG: info

ここまで設定すると、EPG データの取得が可能になります。動くか試してみましょう。

# mirakc コンテナーの起動(Ctrl+C で停止)
$ docker-compose up -d
...
... scan-services: performing...
...
... scan-services: Done successfully, 13s 714ms 157us 639ns elapsed
...
^CGracefully stopping... (press Ctrl+C again to force)
Stopping mirakc ... done

ここで、各行の始めの ... は実際に表示されるテキストではなく、ログの省略を意味します。

たくさんのログが表示されますが、正しく動作していれば、上記のようなログを見つけることができます。

💡 既に稼働している Mirakurun / mirakc をチューナーとして利用する

ちょっとした動作確認のために、わざわざチューナーのデバイスドライバーをインストールするのは面倒な作業です。このような場合には、以下のような設定を行い、既に稼働している Mirakurun または mirakc をチューナーとして利用することをお勧めします。 (同様の ことはMirakurunでも可能です)

tuners:
- name: upstream
types: [GR, BS]
command: >-
curl -s http://upstream:40772/api/channels/{{channel_type}}/{{channel}}/stream

上記の http://upstream:40772 の部分は接続先の Mirakurun または mirakc サーバーの URL に置き換えてください。

Mirakurun とは異なり、mirakc の /api/channels/{{channel_type}}/{{channel}}/stream API は、チューナーからの出力をそのまま送出します。 NULL パケットすらドロップしません。そのため、TS パケットを処理するツールのテストなどにも使えて便利です。

curl -s http://mirakc:40772/api/channels/GR/27/stream | \
MIRAKC_ARIB_LOG=debug mirakc-arib scan-services

EPG データキャッシュ

mirakc の先程のログの中に、以下のようなワーニングが見つかります。

... WARN ... No epg.cache-dir specified, skip to save services

これは、まだ EPG データキャッシュを指定していないため、せっかく取得した EPG データを保存することができないことを意味しています。この状態で mirakc を停止すると、すべての EPG データは消えてしまいます。

今の設定のままでは、mirakc 起動直後は EPG データが存在しません。その結果、EPG データの取得が完了するまで、番組表取得や番組録画はできません。そこで、EPG データキャッシュを設定します。

もちろん、EPG データキャッシュは必要ないという人は、以下の設定を行う必要はありません。

config.yml:

# EPG データの保存先のフォルダーを指定
epg:
cache-dir: /var/lib/mirakc/epg

docker-compose.yml:

version: '3.7'services:
mirakc:
image: masnagam/mirakc:alpine
container_name: mirakc
init: true
restart: unless-stopped
devices:
- /dev/px4video2
ports:
- 40772:40772
volumes:
# epg.cache-dir で指定したパスにボリュームをマウント
- mirakc-epg:/var/lib/mirakc/epg
- ./config.yml:/etc/mirakc/config.yml:ro
environment:
TZ: Asia/Tokyo
RUST_LOG: info
volumes:
# EPG データキャッシュ用ボリューム
mirakc-epg:
name: mirakc_epg
driver: local

データボリュームの代わりに Docker ホスト上のフォルダーをマウントすることも可能ですが、特別な理由がない限りデータボリュームを使用することをお勧めします。

例えば、現在の設定では、mirakc コンテナーではコマンドをすべて root 権限で実行するため、ボリューム内に作成されるファイルの所有者は root になります。 Docker ホスト上のフォルダーをマウントする場合には、この辺にも注意が必要です。

設定が機能しているか、確認してみます。

# mirakc コンテナーの起動(5分程度待ってから Ctrl+C で停止)
$ sudo docker-compose up
...
... ERROR ... Failed to load services: ...
... ERROR ... Failed to load clocks: ...
... ERROR ... Failed to load schedules: ...
...
... Saved schedules for 3 services

先程のワーニングが出ていなければ正しく機能しています。

設定したデータボリュームは mirakc コンテナーをシャットダウンしても残り続けます。

# mirakc コンテナーのシャットダウン
$ sudo docker-compose down
Removing mirakc ... done
Removing network pi_default
# データボリュームが存在することを確認
$ sudo docker volume ls
DRIVER VOLUME NAME
local mirakc_epg

では次に、キャッシュが機能しているか確認するため、再度 mirakc コンテナーを起動します。

# mirakc コンテナーの起動(Ctrl+C で停止)
$ sudo docker-compose up

今度は EPG データ読み込みエラーが表示されなくなっているはずです。キャッシュは正しく機能しています。

EPG データが正しく取得できているのか確認してみましょう。

# jqをインストール
$ sudo apt-get install -y jq
# mirakc コンテナーをバックグラウンドで起動
$ sudo docker-compose up -d
$ curl -s http://localhost:40772/api/services | jq .[0]
{
"id": 3273601024,
"serviceId": 1024,
"networkId": 32736,
"type": 1,
"logoId": 0,
"remoteControlKeyId": 1,
"name": "NHK総合1・東京",
"channel": {
"type": "GR",
"channel": "27"
},
"hasLogoData": false
}
# アクセスログを確認
$ sudo docker logs --tail=1 mirakc
... 172.19.0.1:59498 "GET /api/services HTTP/1.1" 200 564 ...
# mirakc コンテナーをシャットダウン
$ sudo docker-compose down

なお、以下のコマンドを実行すると、シャットダウン時にデータボリュームを削除します。

$ sudo docker-compose down -v
...
Removing volume mirakc_epg
$ sudo docker volume ls
DRIVER VOLUME NAME

データボリュームを削除すると、当然 EPG データキャッシュもなくなります。そのため、次回起動時に EPG データの取得が完了するまで、番組表取得や番組録画はできなくなります。

フィルター設定

EPG データの取得が完了すると、特定サービスの TS ストリーミングが可能となります。

# mirakc コンテナーをバックグラウンドで起動
$ sudo docker-compose up -d
Starting mirakc ... done
# 最初のサービス(NHK総合1)の ID を表示
$ curl -s http://localhost:40772/api/services | jq .[0].id
3273601024
# NHK総合1の TS ストリーミングを実行(Ctrl+C で停止)
$ curl http://localhost:40772/api/services/3273601024/stream >/dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 16.8M 0 16.8M 0 0 1578k 0 --:--:-- 0:00:10 --:--:-- 1725k

しかし、今の設定のままでは、TS ストリームを ffmpeg で変換したりすることはできません。なぜならば、フィルターが設定されていないためです。

フィルターは、TS ストリームを標準入力から受け取り、処理後の TS ストリームを標準出力に出力する外部プログラムです。 mirakc 自体にはこのような機能は実装されておらず、フィルター設定で指定された外部プログラムに TS ストリームの処理を委譲します。

フィルターの動作を確認するため、以下のような設定を行ってみます。

config.yml:

filters:
post-filter: >-
echo "{{channel_type}}/{{channel}} SID#{{sid}}"

設定反映のため mirakc コンテナーを再起動し、動作確認します。

# mirakc コンテナーを再起動
$ sudo docker-compose restart
# post-filter 付き TS ストリーミングを実行
$ curl -s http://localhost:40772/api/services/3273601024/stream?post-filter=true
GR/27 SID#1024

TSストリームをチャンネル情報に置き換えることに成功しました。

なお、filters.post-filter は、Mirakurun の tuners.yml での decoder に相当する設定です。 Mirakurun との互換性のため、クエリーパラメーター post-filter=truedecode=1 に置き換えても動作します。

$ curl -s http://localhost:40772/api/services/3273601024/stream?decode=1
GR/27 SID#1024

フィルターの動作確認も終了したので、実用的なフィルターを設定してみます。

filters:
# Chinachu/Mirakurun の設定例より
post-filter: arib-b25-stream-test

また、以下のように外部サーバーで TS ストリームを処理し、その結果をクライアントに返すことも可能です。

filters:
post-filter: >-
# 標準入力から入力される TS ストリームをリモートホスト tsd の TCP 40773 ポートに転
# 送し、tsd から返された TS ストリームを標準出力に出力
socat - tcp-connect:tsd:40773

socat は、配布している masnagam/mirakc イメージにも含まれているツールです。詳しい説明はリンク先を見るか Google 先生に教えてもらってください。

filters プロパティーはチューナーごとに設定するのではなく、すべてのチューナーに対して共通で利用される設定です。そのため、特定のチューナーに対して特別な処理を行いたい場合は、渡されたテンプレートパラメーターを利用して処理を分岐するようなスクリプトを作成し、これをフィルターとして設定する必要があります。

試しに、以下のような簡単な bs-not-supported スクリプトを作って、動作を確認してみます。

#!/bin/shCHANNEL_TYPE="$1"
CHANNEL="$2"
if [ "$CHANNEL_TYPE" = "GR" ]; then
echo "$CHANNEL"
else
echo "BS not supported"
fi

config.yml:

# 変更部分のみ記載
filters:
post-filter: bs-not-supported {{channel_type}} {{channel}}

docker-compose.yml:

# 変更部分のみ記載
...
volumes:
- mirakc-epg:/var/lib/mirakc/epg
- ./config.yml:/etc/mirakc/config.yml:ro
# 忘れずに `chmod +x bs-not-supported` しておくこと
- ./bs-not-supported:/usr/local/bin/bs-not-supported:ro
...

以下のように表示されれば、bs-not-supported スクリプトは機能しています。

$ curl http://localhost:40772/api/channels/GR/27/stream?post-filter=true
27
$ curl http://localhost:40772/api/channels/BS/BS15_0/stream?post-filter=true
BS not supported

🧪 フロントエンドアプリケーションとの連携

mirakc の準備が整ったので、フロントエンドアプリケーションから利用できるように設定を変更してみましょう。

EPGStation

EPGStation のセットアップ方法は、すでに色々なところに書かれているので省略します。以下、既にセットアップ済みの環境が存在すると仮定して説明を行います。

もし、実験目的で設定変更しようとしているのでしたら、以下を実行する前にデータベースのバックアップを作成しておいてください。設定を変更し、EPGStation を再起動すると、データベースに保存されている EPG データは上書きされます。 Mirakurun と mirakc では利用している文字コード変換処理が異なるため、録画ルールで使用している正規表現などが機能しなくなります。

それでは設定を変更します。変更箇所は以下の1箇所のみです。

{
// 変更不要な設定は省略
"mirakurunPath": "http://raspberrypi.local:40772/"
}

EPGStation を動かしているサーバーから raspberrypi.local を mDNS を使って名前解決できることを前提としています。 mDNS が使えない場合は、IP アドレスもしくは名前解決可能なホスト名に置き換えてください。

EPGStation を再起動すると置き換えは完了します。チャンネル設定で指定したチャンネルの番組表が表示されていれば、置き換え成功です。

TVTest

原因はよく分かっていませんが、Mirakurun 用の BonDriver_Mirakurun ではうまく動かないようです。代わりに、epgdatacapbon さんが作成した BonDriver_mirakc を使ってください。

SERVICE_SPLIT=1 とすることで mirakc と BonDriver_mirakc 間の通信量を減らすことが できます。

🧪 カスタムイメージの作成

多くの人は、ここまでの説明と masnagam/mirakc イメージを使って録画システムを構築できたと思います。しかし、配布している Docker イメージに含まれていないツール、例えば recpt1recdvb 以外のチューナーコマンドを使っている人は、この Docker イメージだけでは録画システムを構築できません。自力でカスタムイメージを作成する必要があります。

カスタムイメージの作成はそれほど難しい作業ではありません。試しに HEALTHCHECK を追加してみます。

まず、以下のような Dockerfile を作成します。

FROM masnagam/mirakc:alpine-arm32v7# 短時間でテストできるように、チェック間隔を10秒に設定しています
HEALTHCHECK --interval=10s --timeout=3s \
CMD curl -fsSL http://localhost:40772/api/status || exit 1

ここでポイントとなるのが、FROM に指定した Docker イメージのタグ alpine-arm32v7です。最終的に mirakc コンテナーを動作させる SBC 上でカスタムイメージをビルドする場合は、マルチアーキイメージである alpine タグを使用しても問題ありません。しかし、一般的に SBC 上でのビルドは大変時間がかかります。このビルド時間の問題は、より高速な PC上で SBC 用の Docker イメージをクロスビルドすることで解決できます。ただ、クロスビルドでマルチアーキイメージを使用すると、最終的にコンテナーを実行する SBC (Raspberry Pi 3 だと arm32v7) 用の Docker イメージではなく、クロスビルドを実行するマシン(多くの場合、amd64)用の Docker イメージを取得してしまいます。そのため、上記では alpine-arm32v7 を指定しています。

今回の例では不要ですが、異なるアーキテクチャ用の Docker イメージ上で何からのコマンドを実行する場合、QEMU ユーザーモードエミュレーションのための準備が必要です。具体的には、こちら を参照してください。

次にカスタムイメージのビルド。

sudo docker build -t custom/mirakc .

あとは起動して動作確認。

sudo docker run -d --rm --name custom-mirakc \
-v $(pwd)/config.yml:/etc/mirakc/config.yml custom/mirakc
# 10秒ごとにログが表示される
sudo docker events
sudo docker stop custom-mirakc

より実用的な例としては、以下の Dockerfile を参考にしてください。

Docker イメージのビルド時に、追加するソフトウェアをクロスビルドする場合などは、以下を参考にしてください。

🧪 トラブルシューティング

最後に、問題が発生したときの対処方法について説明を行い、本記事を終了したいと思います。

ログメッセージの確認

問題発生時にまず行うべき事柄の1つは、システムから出力されるログを確認することです。以下のコマンドは、mirakc コンテナーの最新 1000 行のログを表示します。

sudo docker logs --tail=1000 mirakc

特定のログレベルのメッセージのみ表示させたい場合は、grep を使います。

sudo docker logs --tail=1000 mirakc 2>&1 | grep -e WARN -e ERROR

例えば、受信できないチャンネル番号が指定されていた場合、以下のようなエラーが出力されます。

... scan-services: Failed: JSON error: EOF while parsing a value at line 1 column 0
... sync-clocks: Failed: JSON error: EOF while parsing a value at line 1 column 0

ログレベルを変更することで、より多くの情報を得られるようになります。

# docker-compose.yml からの抜粋
...
environment:
# mirakc のログレベルを debug に
RUST_LOG: info,mirakc=debug

フィルター等の外部プログラムが stderr に出力するログを確認したい場合は、環境変数 MIRAKC_DEBUG_CHILD_PROCESS を以下のように定義してください。

# docker-compose.yml からの抜粋
...
environment:
MIRAKC_DEBUG_CHILD_PROCESS: ''

フィルターの動作確認

フィルターで問題が発生している場合、フィルターに指定した外部プログラムを単独実行することで、問題の切り分けが可能です。

例えば、以下のコマンドで mirakc-arib filter-service の動作を確認できます。

sudo docker exec -it mirakc sh -c \
'curl -s http://localhost:40772/api/channels/GR/27/stream | \
MIRAKC_ARIB_LOG=debug mirakc-arib filter-service --sid=1024 >/dev/null'

本記事では説明しませんでしたが、ジョブ設定(jobs)に指定されている外部プログラムも上記と同様の方法で動作を確認できます。

GitHub Issues の検索

同様の問題が他の人の環境でも発生している場合、GitHub Issues に既に報告済みかもしれません。思いつくキーワードを指定して、報告済みの問題を検索することで、解決方法が見つかるかもしれません。

問題が既に解決済みである場合もあります。is:closed での検索してみてください。

もし同様の問題が見つからない場合は、問題を登録してください。既に問題の回避・解決方法が分かっている場合は、その方法も一緒に登録しましょう。同様の問題に悩んでいる人の手助けになります。

🧪 tsdサーバーの詳しい話

私が動作検証用に利用している TS ストリームを処理するためのサーバーです。サーバー化することで、TS ストリーム処理機能を開発環境 (macOS) と動作検証環境 (SBC) に個別に準備する手間が省けます。また、TS ストリームに対する処理は、SBC にとって軽い処理ではないため、複数の TS ストリームを同時に処理する場合、1台の SBC で全てを処理することが困難であるという事情もあります。

tsd サーバーでは、以下の2つの Docker コンテナーが動いています。

  • b25 コンテナー
  • bcas コンテナー

b25 コンテナーは、TCP 経由で入力される TS ストリームを、bcas コンテナーを利用して処理し、処理結果を TCP 接続元に返します。

bcas コンテナーは、TCP 経由でのスマートカードリーダーへのアクセスを提供します。このコンテナーを使うことで、複数のサーバーから1つのスマートカードリーダーを使用可能となります。

どちらのコンテナーも TCP 接続のために socat を使っています。以下を見ると分かります が、socat を使うと UNIX ソケットの共有が簡単にできます。

b25 コンテナーの Dockerfile:

FROM alpine:latestRUN set -eux \
&& apk add --no-cache \
ccid \
musl \
pcsc-lite-libs \
socat \
tzdata \
&& apk add --no-cache --virtual .build-deps \
gcc \
g++\
make \
musl-dev \
nodejs \
npm \
pcsc-lite-dev \
pkgconf \
# Use arib-b25-stream-test instead of stz2012/libarib25.
# Because stz2012/libarib25 doesn't support STDIN/STDOUT.
# stz2012/libarib25 supports NEON, but it doesn't improve the performance
# significantly.
&& (cd /tmp; npm i arib-b25-stream-test) \
&& cp /tmp/node_modules/.bin/arib-b25-stream-test /usr/local/bin/b25 \
# cleanup
&& apk del --purge .build-deps \
&& rm -rf /tmp/*
COPY b25-server /usr/local/bin/EXPOSE 40773
ENTRYPOINT ["b25-server"]

b25-server:

#!/bin/shset -euB25_BCAS_SERVER=${B25_BCAS_SERVER:-}if [ -z "$B25_BCAS_SERVER" ]; then
echo "B25_BCAS_SERVER must be defined" >&2
exit 1
fi
rm -rf /var/run/pcscd
mkdir -p /var/run/pcscd
echo "Create a UNIX-domain socket to communicate with a remote BCAS server listening on $B25_BCAS_SERVER"
socat unix-listen:/var/run/pcscd/pcscd.comm,fork \
tcp-connect:$B25_BCAS_SERVER &
echo "Start a descrambling server on tcp 40773"
socat tcp-listen:40773,fork,reuseaddr system:b25

bcas コンテナーの Dockerfile:

FROM alpine:latestRUN set -eux \
&& apk add --no-cache \
ccid \
musl \
pcsc-lite-libs \
socat \
tzdata
COPY pcsc-server /usr/local/bin/EXPOSE 40774
ENTRYPOINT ["bcas-server"]

bcas-server:

#!/bin/shset -euBCAS_DEBUG=${BCAS_DEBUG:-}
BCAS_DEVICE=${BCAS_DEVICE:-}
if [ -z "$BCAS_DEVICE" ]; then
echo "BCAS_DEVICE must be defined" >&2
exit 1
fi
rm -rf /var/run/pcscd
mkdir -p /var/run/pcscd
echo "Start pcscd on $BCAS_DEVICE"
if [ -n "$BCAS_DEBUG" ]; then
pcscd -f --debug &
else
pcscd -f &
fi
echo "Start a pcscd proxy on tcp 40774"
socat tcp-listen:40774,fork,reuseaddr \
unix-connect:/var/run/pcscd/pcscd.comm

あとはこれらを起動するだけ。

version: '3.7'services:
b25:
container_name: b25
ports:
- 40773:40773
environment:
B25_BCAS_SERVER: bcas:40774
TZ: Asia/Tokyo
depends_on:
- bcas
bcas:
container_name: bcas
devices:
- /dev/bus/usb/001/002
ports:
- 40774:40774
environment:
BCAS_DEVICE: /dev/bus/usb/001/002
#BCAS_DEBUG: 1
TZ: Asia/Tokyo

最後まで読んでいただきありがとうございました。

「うちにも使われていないラズパイがあるから、試しに動かしてみよう」そう感じた人が少しでもいてくれたなら、とても嬉しく思います。

最後に、本企画に声をかけてくださった録画研究会のみなさんに感謝致します。

--

--