山田覚書

Mirakurun + EPGStation on Debian 10 (Buster)

Posted on Thu 18 June 2020 ( 2020-09-17 update ) in development

PCを使った録画環境

PCでテレビ番組を録画する際に必要なものはチューナーです。

私はずっとアースソフトのPT2を使っています。PC自体はNECのサーバであるExpress5800/S70タイプRB。いわゆる鼻毛鯖を2010年から使っていました。組み立ててしばらくしたら東日本大震災が発生してましたね。かれこれ10年経過していますがあっという間です。そしてPT2はまだまだ現役です。

2020年を前に、経年劣化しつつある各種部品の更新をかねて録画サーバーを更新しました。

当初はPCIスロットを装備したマザーボードで組んでみましたが、先を見越してPCI-ExpressスロットにPCI拡張カードを変換して利用することにしました。

パーツ 製品
マザーボード ASUS アスース H310M-E R2.0
CPU Intel インテル BX80684G4920 [CPU Celeron G4920]
CPUクーラー リテール品
メモリ ADATA エーデータ DDR4-2666MHz CL19 288Pin DIMM デスクトップPC用メモリ 8GB×1枚
電源 ANTEC アンテック NE550 GOLD
SSD A-DATA エーデータ ASU650SS-120GT-R [SSD 120GB]
HDD 手持ちの2TB
拡張ボード AREA エアリア SD-PECPCiRi2 [拡張ボードの旧世主 第二章]
拡張ボード アースソフト PT2
ケース Thermaltake サーマルテイク Versa H18

上記構成で相性問題などはまった発生しませんでした。

エアリアの「拡張ボードの旧世主第二章」はPCIボードへの電源供給や固定するブラケットなどよく考えられた仕様だったので購入。もちろんPT2も動作確認できました。もう少しシンプルなものならAliExpressで2000円付近で入手可能なのでそちらでも良いと思います。ひと月も待てば届くはず。

問題が発生したのは当初用意したAntecの電源。NeoEcoシリーズの電源はSeasonic(シーソニック)のOEM品だから非常に高品質である!……なんてネットのレビューがありますが不具合続出でした。購入してファンを下向きにしケースに設置するとファンとファンガードが干渉してカラカラと音がなりました。工作精度がどうもよくない模様です。初期不良交換。

交換後の製品は異音の発生はないので運用開始。しかし、半年も経過すると原因不明な電源断が発生するようになりました。どうも内臓ハードディスクへのアクセスなどで負荷がかかるとストンと電源が落ちてしまうようです。状況を切り分けるのに数ヶ月かかりました……。日に日に動作状況が悪くなり、最後はSSDから煙。どうもSATAの電力供給に異常があるようでした。

こりゃダメだということで7年保証をいかそうと販売元に連絡するも新型コロナウイルスによる感染症のため営業時間が不安定になり修理依頼後ひと月を経過しても進捗がなし。(二ヶ月後に新品を手配いただきました……。)市場に新品在庫もないのがわかっているので、保証があてにならないと判断。別の電源を手配することにします。

Antecの電源は評判ほどよくはない。似たような不具合がぼちぼち見つかる。感染症のおかげで国内代理店のリンクスインターナショナルの対応も期待できずでした。残念無念です。

debian Buster インストール

ずっと使っているDebian GNU/Linuxのインストールです。boとかpotatoなんかを使っていた頃から比べたら何も考えることはありませんね。非常に簡単になりました。

まず、Buster(Debian 10)のnetinstイメージを入手して、インストールUSBディスクを作成します。

macOS環境で作業する手順は次のとおり。

Debian GNU/LinuxのカレントCDイメージをダウンロードします。現行バージョンは10.4のようなので適宜実施。ブラウザが一番簡単じゃないだろうか。

$ cd ~/Downloads
$ curl -C - -L -O https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-10.4.0-amd64-netinst.iso
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   359  100   359    0     0    230      0  0:00:01  0:00:01 --:--:--   230
100  336M  100  336M    0     0  3064k      0  0:01:52  0:01:52 --:--:-- 3080k

isoファイルをimgファイルに変換します。

$ hdiutil convert -format UDRW -o ~/Downloads/debian-10.4.0-amd64-netinst.img ~/Downloads/debian-10.4.0-amd64-netinst.iso
Driver Descriptor MapDDM0 を読み込み中...
Debian 10.4.0 amd64 n           Apple_ISO1 を読み込み中...
AppleApple_partition_map2 を読み込み中...
Debian 10.4.0 amd64 n           Apple_ISO3 を読み込み中...
EFIApple_HFS4 を読み込み中...
.
Debian 10.4.0 amd64 n           Apple_ISO5 を読み込み中...
..............................................................................
経過時間: 3.992s
速度:84.1M バイト/秒
節約率:0.0%
created: /Users/YOUR_USER_NAME/Downloads/debian-10.4.0-amd64-netinst.img.dmg

差し込んでいるUSBメモリのデバイスを確認。(例は/dev/disk2の場合)

$ diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *128.0 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS macOS                   127.2 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
()
/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *4.0 GB     disk2
   1:             Windows_FAT_32 ESD-USB                 4.0 GB     disk2s1

USBメモリをアンマウント。(例は/dev/disk2の場合)

$ diskutil unMountDisk /dev/disk2
Unmount of all volumes on disk2 was successful

ddコマンドでimgファイルをUSBメモリに書き込み。(例は/dev/disk2の場合)

$ sudo dd if=~/Downloads/debian-10.4.0-amd64-netinst.img of=/dev/rdisk2 bs=1m
Password:
290+0 records in
290+0 records out
304087040 bytes transferred in 2.365862 secs (128531188 bytes/sec)

このとき、「/dev/disk2」ではなくて「/dev/rdisk2」の方が手早くコピーが終わる。シーケンシャルアクセスとランダムアクセスの関係ってやつと理解しています。

インストールUSBディスクが作成できたらLinuxマシンにUSBメモリを差し込んで起動する。お約束だけど内蔵しているSSDやHDDからブートしないためにも、BIOSの画面もたまには拝んで確認しておくのも良いことだと思います。

Debianインストーラーの操作については割愛します。

基本システムのみインストールすればいいので、デスクトップ環境やプリントサーバーなどのチェックを外してしまいます。スッキリ構成ならば5分くらいでインストールできてしまうことでしょう。やり直しだって気楽にできます。

Debianインストール後

インストールできたらsudoとsshが利用できるようにします。

$ su -
# apt-get update
# apt-get install sudo openssh-server
# gpasswd -a YOUR_USER_NAME sudo

YOUR_USER_NAMEは適宜環境に応じて置換します。

sudoとsshさえ導入してしまえば、他の端末から操作できます。メモを見ながら作業なんてのはsshで端末に入った方が楽チンですからね。

LOCALEは日本語にしないのが自身の慣例なので、追加の設定。

$ sudo vi /etc/environment
LANG=en_US.utf-8
LC_ALL=en_US.utf-8

sshは公開鍵でのログインにします。作成した公開鍵をもとにauthorized_keysを適宜編集します。

$ ssh-keygen -t rsa

sshサーバーの設定は好みだと思いますが必要最低限からはじめます。

$ sudo vi /etc/ssh/sshd_config
PermitEmptyPasswords no

続いて、ソースリストの編集。これも好みです。国内の特定のミラーサーバを指定するよりは公式からのリダイレクト任せでもいいかなと思います。

$ sudo vi /etc/apt/sources.list
deb http://ftp.jp.debian.org/debian/ buster main contrib non-free
deb http://ftp.jp.debian.org/debian/ buster-updates main contrib
deb http://ftp.jp.debian.org/debian/ buster-backports main contrib non-free
deb http://security.debian.org/debian-security buster/updates main
deb-src http://ftp.jp.debian.org/debian/ buster main contrib non-free
deb-src http://ftp.jp.debian.org/debian/ buster-updates main contrib
deb-src http://ftp.jp.debian.org/debian/ buster-backports main contrib non-free
deb-src http://security.debian.org/debian-security buster/updates main

aptソースを編集したらupdateとしてupgradeします。

$ sudo apt-get update
$ sudo apt-get upgrade

基本的なコマンドと設定

シェルのエイリアスを設定します。定番どころを中心に設定して、場合によってはコメントアウトしておきます。

$ vi ~/.bashrc
alias ll='ls -l'
alias la='ls -A'
alias rm='rm -i'
alias mv='mv -i'
alias cp='cp -i'
alias vi='vim'
alias ..='cd ..'

設定ファイルを編集するのにvimをインストール。vimの設定は奥が深すぎていつも迷うので適当なところからスタートします。

$ sudo apt-get install vim
$ vi ~/.vimrc
set nocompatible
set encoding=utf-8
set fileencodings=utf-8,iso-2022-jp,sjis,euc-jp
set fileformats=unix,dos
set number
set list
set ruler
set autoindent
syntax on
highlight Comment ctermfg=LightCyan
set showmatch
set showcmd
set noswapfile
set nobackup
set history=50
set ignorecase
set smartcase
set hlsearch
set incsearch
set binary noeol
set tabstop=4
set expandtab
set shiftwidth=4
$ select-editor

ネットワーク内の端末とファイルを共有するためにsambaをインストールします。smb.confも適宜設定する。クライアントがWindows 10の場合はsambaのプロトコルを指定する必要があるみたい。SMB2以上じゃないと接続できない。

$ sudo apt-get install samba
$ sudo vi /etc/samba/smb.conf
unix charset = UTF-8
dos charset = CP932
display charset = UTF-8
interfaces = 127.0.0.0/8 192.168.0.0/24 eth0
client max protocol SMB3
client min protocol SMB2
[sharedfiles]
comment = server name here
path = /path/sharedfiles
writable = yes
public = no
guest ok = no
guest only = no
printable = no
force create mode = 0666
force directory mode = 0777

sambaのユーザに自分を追加してリスタートします。

$ sudo pdbedit -a YOUR_USER_NAME
$ sudo systemctl restart smbd

基本的なコマンドと言いながら、bashとvimとsambaだけだし。それしか必要ないってのが現状だし……。

録画関連コマンドのインストール

pt2を動かすドライバとカードリーダーの設定をさくっと済ませます。

$ sudo apt-get install \
    g++ \
    build-essential \
    autoconf \
    automake \
    cmake \
    linux-headers-`uname -r` \
    git \
    pcscd \
    pcsc-tools \
    libpcsclite-dev

pcsc_scanで動作確認しておきます。確認できたらCtr+C

$ pcsc_scan

b25コマンドはB-CASカードがリーダーから抜けていてデコードできなかったファイルをあとからデコードしたい時などに便利ですね。

$ mkdir downloads
$ cd downloads
$ git clone https://github.com/stz2012/libarib25.git
$ cd libarib25/
$ cmake .
$ make
$ sudo make install

earth_pt1というドライバがロードされているのでrmmodした上で、ブラックリストに登録します。

$ lsmod | grep pt1
$ sudo rmmod earth_pt1
$ sudo vi /etc/modprobe.d/blacklist.conf
blacklist earth_pt1

PT2を動作させるドライバーであるpt1_drvをインストールします。

$ wget http://hg.honeyplanet.jp/pt1/archive/tip.tar.bz2
$ tar xvf tip.tar.bz2
$ cd pt1-xxxx/driver/
$ make
$ sudo make install
$ sudo modprobe pt1_drv
$ lsmod | grep pt1

pt1_drvがロードされたのを確認したら録画コマンドrecpt1をインストールします。

$ cd ../recpt1/
$ ./autogen.sh
$ ./configure --enable-b25
$ make
$ sudo make install
$ ls -la /dev/pt1*

デバイスが確認できたらシグナル受信状況を確認したり、実際に録画してみます。

$ checksignal --device /dev/pt1video0 --lnb 15 211
$ checksignal --device /dev/pt1video1 --lnb 15 211
$ checksignal --device /dev/pt1video2 20
$ checksignal --device /dev/pt1video3 20
$ recpt1 --b25 --strip 20 10 test.ts

MirakurunとEPGStationのインストール

はじめは全部まとめられたDockerで導入をしてみたのです。しかし、どういうプログラムなのかさっぱり理解せずに導入したので挙動がよくわからない。いろいろ不便。いまはDockerを利用せずに運用しています。ほかに何かを動かすわけでもないので見通しのよいようにします。

Mirakurunをひとまず導入します。

Mirakurunはnode.js上で動作するので最新のnode.jsを取り入れつつインストールします。インストールスクリプトを信用しているのであまり小難しいことは考えずに実行。

$ sudo apt-get install libssl-dev libtool pkg-config yasm
$ npm install pm2 -g
$ curl -sL https://deb.nodesource.com/setup_12.x -o nodesource_setup.sh
$ sudo bash nodesource_setup.sh 
$ sudo apt install nodejs

node.jsのバージョンを確認しておきます。

$ node -v

必要だと言われているものをインストール。コマンドを実行します。

$ sudo npm install pm2 -g
$ sudo npm install arib-b25-stream-test -g --unsafe
$ sudo npm install mirakurun -g --unsafe --production

チューナーの名前をPT3からPT2にするのは識別名でしかないのでそのままでもよいはずが気分的に変更。デバイス名は正しく編集します。それとisDisabledはfalseにすることが大事です。ちなみに2枚のPT2で稼働させる時のチューナーの設定は次の通り。デバイスの番号がどう割り当てられるかってとカード単位なんですね。

$ sudo mirakurun config tuners
- name: PT2-S1
  types:
    - BS
    - CS
  command: recpt1 --device /dev/pt1video0 --lnb 15 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT2-S2
  types:
    - BS
    - CS
  command: recpt1 --device /dev/pt1video1 --lnb 15 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT2-T1
  types:
    - GR
  command: recpt1 --device /dev/pt1video2 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT2-T2
  types:
    - GR
  command: recpt1 --device /dev/pt1video3 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT1-S3
  types:
    - BS
    - CS
  command: recpt1 --device /dev/pt1video4 --lnb 15 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT1-S4
  types:
    - BS
    - CS
  command: recpt1 --device /dev/pt1video5 --lnb 15 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT1-T3
  types:
    - GR
  command: recpt1 --device /dev/pt1video6 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

- name: PT1-T4
  types:
    - GR
  command: recpt1 --device /dev/pt1video7 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

チャンネルも適宜設定します。チャンネルスキャンなんてしなくてもいいですし。放送が終了したチャンネルを設定してしまうとmirakurunがepgを取得しようとチューナーを始終占有することになりますので必要なものだけに絞る方が良いと思います。チャンネルの名前は設定上の呼称です。適宜EPGの名称に置きかわりますので適当でも大丈夫。

$ sudo mirakurun config channels
- name: MX
  type: GR
  channel: '20'

- name: tvk
  type: GR
  channel: '18'

- name: chiba
  type: GR
  channel: '30'

- name: saitama
  type: GR
  channel: '32'

- name: CX
  type: GR
  channel: '21'

- name: TBS
  type: GR
  channel: '22'

- name: TX
  type: GR
  channel: '23'

- name: EX
  type: GR
  channel: '24'

- name: NTV
  type: GR
  channel: '25'

- name: NHK E
  type: GR
  channel: '26'

- name: NHK G
  type: GR
  channel: '27'

- name: NHK BS1
  type: BS
  channel: BS15_0
  serviceId: 101

- name: NHK BS1 (sub)
  type: BS
  channel: BS15_0
  serviceId: 102

- name: NHK BSプレミアム
  type: BS
  channel: BS03_1
  serviceId: 103

- name: NHK BSプレミアム (sub)
  type: BS
  channel: BS03_1
  serviceId: 104

- name: BS日テレ
  type: BS
  channel: BS13_0
  serviceId: 141

- name: BS日テレ (sub)
  type: BS
  channel: BS13_0
  serviceId: 142

- name: BS朝日
  type: BS
  channel: BS01_0
  serviceId: 151

- name: BS朝日 (sub)
  type: BS
  channel: BS01_0
  serviceId: 152

- name: BS-TBS
  type: BS
  channel: BS01_1
  serviceId: 161

- name: BSジャパン
  type: BS
  channel: BS01_2
  serviceId: 171

- name: BSフジ
  type: BS
  channel: BS13_1
  serviceId: 181

- name: WOWOWプライム
  type: BS
  channel: BS03_0
  serviceId: 191
  isDisabled: false

- name: WOWOWライブ
  type: BS
  channel: BS05_0
  serviceId: 192
  isDisabled: false

- name: WOWOWシネマ
  type: BS
  channel: BS05_1
  serviceId: 193
  isDisabled: false

- name: スターチャンネル1
  type: BS
  channel: BS09_1
  serviceId: 200
  isDisabled: false

- name: スターチャンネル2
  type: BS
  channel: BS15_1
  serviceId: 201
  isDisabled: false

- name: スターチャンネル3
  type: BS
  channel: BS15_1
  serviceId: 202
  isDisabled: false

- name: BS11
  type: BS
  channel: BS09_0
  serviceId: 211

- name: TwellV
  type: BS
  channel: BS09_2
  serviceId: 222

- name: 放送大学BS1
  type: BS
  channel: BS11_2
  serviceId: 231
  isDisabled: true

- name: 放送大学BS2
  type: BS
  channel: BS11_2
  serviceId: 232
  isDisabled: true

- name: 放送大学BS3
  type: BS
  channel: BS11_2
  serviceId: 233
  isDisabled: true

- name: グリーンチャンネル
  type: BS
  channel: BS19_0
  serviceId: 234
  isDisabled: true

- name: BSアニマックス
  type: BS
  channel: BS13_2
  serviceId: 236
  isDisabled: false

- name: FOXスポーツエンターテイメント
  type: BS
  channel: BS11_0
  serviceId: 238
  isDisabled: true

- name: BSスカパー!
  type: BS
  channel: BS11_1
  serviceId: 241
  isDisabled: true

- name: J SPORTS 1
  type: BS
  channel: BS19_1
  serviceId: 242
  isDisabled: false

- name: J SPORTS 2
  type: BS
  channel: BS19_2
  serviceId: 243
  isDisabled: false

- name: J SPORTS 3
  type: BS
  channel: BS21_1
  serviceId: 244
  isDisabled: false

- name: J SPORTS 4
  type: BS
  channel: BS21_2
  serviceId: 245
  isDisabled: false

- name: BS釣りビジョン
  type: BS
  channel: BS23_0
  serviceId: 251
  isDisabled: true

- name: イマジカBS映画
  type: BS
  channel: BS21_0
  serviceId: 252
  isDisabled: true

- name: 日本映画専門チャンネル
  type: BS
  channel: BS23_1
  serviceId: 255
  isDisabled: true

- name: ディズニーチャンネル
  type: BS
  channel: BS03_2
  serviceId: 256
  isDisabled: true

- name: CS2
  type: CS
  channel: CS2

- name: CS4
  type: CS
  channel: CS4

- name: CS6
  type: CS
  channel: CS6

- name: CS8
  type: CS
  channel: CS8

- name: CS10
  type: CS
  channel: CS10

- name: CS12
  type: CS
  channel: CS12

- name: CS14
  type: CS
  channel: CS14

- name: CS16
  type: CS
  channel: CS16

- name: CS18
  type: CS
  channel: CS18

- name: CS20
  type: CS
  channel: CS20

- name: CS22
  type: CS
  channel: CS22

- name: CS24
  type: CS
  channel: CS24

ログのローテーションの設定も行っておきます。

$ sudo pm2 startup
$ sudo pm2 install pm2-logrotate
$ sudo vim /etc/logrotate.d/mirakurun
/usr/local/var/log/mirakurun.stdout.log
/usr/local/var/log/mirakurun.stderr.log
/{
  compress
  rotate 53
  missingok
  notifempty
}

EPGStationはデータベースを必要とします。今回はMySQL/mariaDBで構築します。

mariaDBをインストールしたら専用のデータベースを作成します。適宜データベースの名前やユーザは変更します。

$ sudo apt-get install mariadb-server
$ sudo mysql_secure_installation
$ sudo mysql -u root -p
> CREATE DATABASE epgstation CHARACTER SET utf8;
> GRANT ALL PRIVILEGES ON epgstation.* TO epgstation@localhost IDENTIFIED BY 'epgstation';
> ALTER USER epgstation@localhost IDENTIFIED BY 'epgstation';
> exit

ffmpegはソースからビルドするのが定番のようですが、パッケージでも十分ニーズを満たしているようなので簡単に済ませます。

$ sudo apt-get -s install ffmpeg

EPGStationのインストールもgithub上で公開されているとおりに実施。

$ git clone https://github.com/l3tnun/EPGStation.git
$ cd EPGStation
$ npm install --no-save
$ npm run build

設定ファイルはいろいろとありますが、ひとつずつ確認しながら編集します。

$ cp config/config.sample.json config/config.json
$ cp config/operatorLogConfig.sample.json config/operatorLogConfig.json
$ cp config/serviceLogConfig.sample.json config/serviceLogConfig.json
$ mkdir config/sample
$ mv config/*sample.json config/sample/

config.jsonはこのあたりは要編集ですね。ほかはおこのみ。

   "dbType": "mysql",
   "mysql": {
       "host": "localhost",
       "port": 3306,
       "user": "epgstation",
       "password": "epgstation",
       "database": "epgstation",
       "connectTimeout": 20000,
       "connectionLimit": 10
   },
   "ffmpeg": "/usr/bin/ffmpeg",
   "ffprobe": "/usr/bin/ffprobe",

さらに自動起動の設定をしておきます。

$ sudo npm install pm2 -g
$ sudo pm2 startup systemd -u YOUR_USER_NAME --hp /home/YOUR_USER_NAME
$ pm2 start dist/server/index.js --name "epgstation"
$ pm2 save

ここまで進めればひと段落なはず。ブラウザでポート8888にアクセスすればRPGStationのUIが表示されます。

MirakurunとEPGStationの調整

基本的な調整は各種の設定ファイルに行っていきます。

まずMirakurunについて調整。tuners.ymlやchannels.ymlは追加の調整なし。server.ymlを調整します。

MirakurunとEPGStationの運用をはじめるとたびたび録画に失敗しました。どうやらEPGStationが録画を開始しようとすると空いているチューナーがない。つまりMirakurunがチューナーを占有してしまっている状態になっていると見受けられます。

programGCInterval: 86400000
epgGatheringInterval: 864000000

サーバーの設定のうちEPG取得のインターバルを変更します。EPG情報はEPGStaionが取得するのでMirakurunで取得する必要はありません。864000000ミリ秒=10日にしておきます。ファイル末に追記。場合によってはMirakurun自体を修正してしまうこともありだと思います。

highWaterMark: 268435456

それとバッファの枯渇対策をちょっとだけしておきます。枯渇する前からやってしまうと正しいのかどうかちょっとわからなくなるので、必要に応じてでいいと思います。

しばらく運用してみましたが、デバイスが足りていないみたいであまり改善しませんでした。なので、物理的に解消することにします。

PT2は2枚持っているので2枚挿し。地デジ4チャンネルとBS/CS4チャンネルで運用することにしました。

設定はこのように変更。

highWaterMark: 268435456
programGCInterval: 43200000
epgGatheringInterval: 43200000

Mirakurunのログレベルなどほかの項目は変更せず様子を見ています。

EPGStationの調整箇所はconfig.jsonの部分。

    "recPriority": 2,
    "timeSpecifiedStartMargin": 5,
    "timeSpecifiedEndMargin": 1,
    "reservesUpdateIntervalTime": 720,
    "allowEndLack": true,
    "gid": "username",
    "uid": "username",
    "reservationAddedCommand": "/bin/bash /path/epgcommand.sh added",
    "recordedPreStartCommand": "/bin/bash /path/epgcommand.sh prestart",
    "recordedPrepRecFailedCommand": "/bin/bash /path/epgcommand.sh preprecfailed",
    "recordedStartCommand": "/bin/bash /path/epgcommand.sh start",
    "recordedEndCommand": "/bin/bash /path/epgcommand.sh end",
    "recordedFailedCommand": "/bin/bash /path/epgcommand.sh failed",
    "recordedFormat": "%TITLE%_%MONTH%%DAY%%HOUR%%MIN%%CHID%",
    "recorded": "/path/tsfiles/"

録画の開始と終了のマージンを少し増やしています。録画端末の環境により秒数は微妙に調整してあげるのが良いかと思います。コマンド立ち上げに要する時間が注目ポイントです。

EPGStationの実行ユーザやグループも変更します。外部のスクリプトを実行したりする時に権限やらをあれこれ考えていたら面倒なので自分にしてしまうのが楽かもしれません。また外部スクリプトを指定。引数で動作を変えるなど編集の利便性を考えます。

また録画ファイルの保存ディレクトリも場所を変えておきました。接続した大容量ハードディスクなどをマウントしておくと便利でしょう。

外部スクリプトでは環境変数もいくつか引き継がれています。録画開始をツイートしたり、録画のエラーをメールしたりなど柔軟な対応の余地があります。TSファイルを保存するディレクトリに録画状況を切り出したログの出力をして、いちいち正規のログを見なくて済むようにしていますが、悪くない利便性です。

#!/usr/bin/env bash

set -eu

SCRIPT_DIR=/path
LOG_FILE=$SCRIPT_DIR/record_scripts.log

case $1 in
    "added")
        # reservationAddedCommand
        echo "[INFO] `date "+%Y-%m-%d %T"` [EPG] タイマー ${NAME} at ${CHANNELNAME}" >> $LOG_FILE
        ;;
    "prestart")
        # recordedPreStartCommand
        echo "[INFO] `date "+%Y-%m-%d %T"` [EPG] ○録画/一時停止 ${NAME} at ${CHANNELNAME}" >> $LOG_FILE
        ;;
    "preprecfailed")
        # recordedPrepRecFailedCommand
        echo "[ERROR] `date "+%Y-%m-%d %T"` [EPG] 録画できません ${NAME} at ${CHANNELNAME}" >> $LOG_FILE
        ;;
    "start")
        # recordedStartCommand
        echo "[INFO] `date "+%Y-%m-%d %T"` [EPG] ●録画 ${NAME} at ${CHANNELNAME}" >> $LOG_FILE
        ;;
    "failed")
        # recordedFailedCommand
        echo "[ERROR] `date "+%Y-%m-%d %T"` [EPG] 録・画・失・敗 ${NAME} at ${CHANNELNAME}" >> $LOG_FILE
        ;;
    "end")
        # recordedEndCommand
        echo "[INFO] `date "+%Y-%m-%d %T"` [EPG] ■停止 ${NAME} at ${CHANNELNAME}" >> $LOG_FILE
        ;;
esac

連動して映画のx265エンコード自動化なんてのが便利でいいですね。ここからいろいろ追加していこうと思います。

Docker上にtssplitter環境

TSファイルを編集するにはTsSplitter.exeはやっぱり便利です。wine32をDocker上で動作させます。DockerとDocker Composeをインストールしておきます。

$ sudo apt-get update
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/debian \
    $(lsb_release -cs) \
    stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
$ sudo docker run hello-world
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

以上でDockerの導入が完了。

TsSplitterはバイナリを入手した上でDockerfileを編集しビルドします。

FROM debian:buster

ENV WINEDEBUG -all
ENV LANGUAGE ja_JP.ja \
  LANG ja_JP.UTF-8 \
  LC_ALL ja_JP.UTF-8

RUN dpkg --add-architecture i386 \
  && apt-get update \
  && apt-get install -y --no-install-recommends \
  wine \
  wine32 \
  wine64 \
  wine32:i386 \
  libwine \
  libwine:i386 \
  fonts-wine \
  locales \
  && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \
  && echo "ja_JP.UTF-8 UTF-8" >> /etc/locale.gen \
  && locale-gen \
  && apt-get autoremove -y \
  && apt-get clean

RUN useradd -u 1000 -d /home/YOUR_USER_NAME -m -s /bin/bash YOUR_USER_NAME
ENV HOME /home/YOUR_USER_NAME
WORKDIR /home/YOUR_USER_NAME
USER YOUR_USER_NAME
RUN winecfg
COPY ./TsSplitter.exe /usr/local/bin/
VOLUME /home/YOUR_USER_NAME/videos/ex2TBHDD/recorded
ENTRYPOINT [ "/usr/bin/wine" ]

ビルドします。

$ sudo docker build -t wine32 ~/tssplitter/.

こんな記法で利用できます。

$ sudo docker run --rm --mount type=bind,src=$PWD,dst=$PWD wine32 /usr/local/bin/TsSplitter.exe -SD -1SEG -SEP -SEPA "$PWD/source.ts"

運用に便利なシェルスクリプトを用意する

適宜、スクリプトに連動させて動かします。wowowのような5.1chの映画なんかはTsSplitterを通すだけで切り抜きできますから重宝しています。

tsの編集に追加して、エンコードするコマンドを追加。

$ sudo apt-get install handbrake-cli

当初はDockerの中で動かしていましたが、あまり意味がないのでそのままインストールしているHandbrake-CLI。ffmpegよりもこちらの方が使い慣れているのでh265のファイル生成なんかに使っています。

ある程度環境が落ち着いてきたので、エンコードスクリプトの整備を進めました。

まずはconfig.jsonの編集

    "encode": [
        {
            "name": "wowow 5.1ch",
            "cmd": "/bin/bash %ROOT%/config/wowow.sh",
            "suffix": ".mp4"
        },
        {
            "name": "H265",
            "cmd": "/bin/bash %ROOT%/config/handbrake.sh h265-med-28",
            "suffix": "-265.mp4"
        },
        {
            "name": "H264",
            "cmd": "/bin/bash %ROOT%/config/handbrake.sh h264-med-23",
            "suffix": "-264.mp4"
        },
        {
            "name": "H264 veryfast",
            "cmd": "%NODE% %ROOT%/config/enc.js",
            "suffix": "-vf.mp4",
            "default": true
        }
    ],

エンコードのメニューとして「wowow 5.1ch」、「H265」、「H264」、「H264 veryfast」としました。「H264 veryfast」は手早くエンコードするときのため。初期のものをそのまま残しています。慣れの問題で基本的なエンコードはHandbrakeCLIで実施します。古いデバイスを考慮してのH264とここ数年のものなら問題ないH265でエンコードするためにひとつ。内容は次の通り。

#!/usr/bin/env bash

set -eux

SCRIPT_DIR=/path
LOG_FILE=$SCRIPT_DIR/record_scripts.log

case $1 in
    "h265-med-28")
        ENCSTRING="-e x265 --x265-preset=medium --h265-profile=main -q 28"
        ;;
    "qsv_h265-med-28")
        ENCSTRING="-e qsv_h265 --x265-preset=medium --h265-profile=main -q 28"
        # QSVはあまり効果がないかも
        ;;
    "h264-med-23")
        ENCSTRING="-e x264 --x264-preset=medium --h264-profile=main -q 23"
        ;;
    "qsv_h264-med-23")
        ENCSTRING="-e qsv_h264 --x264-preset=medium --h264-profile=main -q 23"
        # QSVはあまり効果がないかも
        ;;
    "h264-veryfast-23")
        ENCSTRING="-e x264 --x264-preset=veryfast --h264-profile=main -q 23"
        # QSVはあまり効果がないかも
        ;;
    "")
        ENCSTRING="-e h264 --x264-preset=veryfast --h264-profile=main -q 23"
        ;;
esac

echo "[INFO] `date "+%Y-%m-%d %T"` [handbrake] ●REC ${OUTPUT##*/}" >> $LOG_FILE

/usr/bin/HandBrakeCLI \
    -f mp4 $ENCSTRING --detelecine -l 720 --crop 0:0:0:0 \
    --aencoder copy:aac --mixdown auto \
    -i "$INPUT" \
    -o "$OUTPUT" 1> /dev/null

echo "[INFO] `date "+%Y-%m-%d %T"` [handbrake] ■STOP ${OUTPUT##*/}" >> $LOG_FILE

特にファイル名を変更したりとか考えず、入力されたファイルを指定したコーデックとプリセットでエンコードします。QSVの設定も書いてみましたが多少早くなったとしても画質の損失がそれに見合わないと感じたので使っていません。

次にwowowなどの5.1chソースの動画をエンコードするスクリプト。TsSplitterで音声チャンネルの切り替えで動画をカットすると本編部分だけの動画が切り出せるので、それをエンコードするようにシェルスクリプトを書いています。

#!/usr/bin/env bash

set -eux

SCRIPT_DIR=/path
LOG_FILE=$SCRIPT_DIR/recorder.log

WORK_DIR=${INPUT%/*}
INPUT_FILE=${INPUT##*/}
OUTPUT_FILE=${OUTPUT##*/}

TASK_NO=`date "+%H%M"`

sleep 1m

echo "[INFO] `date "+%Y-%m-%d %T"` [tsspliter] ●START $INPUT_FILE" >> $LOG_FILE
cd $WORK_DIR
splittemp=${TASK_NO}_split.ts
mv "$INPUT" "$WORK_DIR/$splittemp"
docker run --rm --mount type=bind,src=$WORK_DIR,dst=$WORK_DIR wine32 /usr/local/bin/TsSplitter.exe -SD -1SEG -SEP -SEPA "$WORK_DIR/$splittemp" 2>>$LOG_FILE
echo "[INFO] `date "+%Y-%m-%d %T"` [tsspliter] ■STOP $INPUT_FILE" >> $LOG_FILE        
i=0
for deletes in $( ls -S $WORK_DIR/${splittemp%.ts}_*.ts )
do
    if [ $i -eq 0 ]; then
        echo "[INFO] `date "+%Y-%m-%d %T"` [tsspliter] split $INPUT_FILE" >> $LOG_FILE
        encodetemp=${TASK_NO}_encode.ts
        mv $deletes $encodetemp
        i=1
    else
        echo "[INFO] `date "+%Y-%m-%d %T"` [tsspliter] remove $deletes" >> $LOG_FILE
        rm $deletes
    fi
done

echo "[INFO] `date "+%Y-%m-%d %T"` [handbrake] ●REC $INPUT_FILE" >> $LOG_FILE
ENCSTRING="-e x265 --x265-preset=medium --h265-profile=main -q 28"
#ENCSTRING="-e qsv_h264 --x264-preset=veryfast --h264-profile=main -q 23"
/usr/bin/HandBrakeCLI \
    -f mp4 $ENCSTRING --detelecine -l 720 --crop 0:0:0:0 \
    --aencoder copy:aac --mixdown auto \
    -i "$WORK_DIR/$encodetemp" \
    -o "$OUTPUT" 1> /dev/null
echo "[INFO] `date "+%Y-%m-%d %T"` [handbrake] ■STOP $OUTPUT_FILE" >> $LOG_FILE

mv "$WORK_DIR/$splittemp" "$INPUT"
mv "$WORK_DIR/$encodetemp" "${INPUT%.ts}-src.ts"

素人スクリプトなのでとっても冗長的です。上から簡単に解説します。

INPUTとOUTPUTはEPGStationから渡されるのでパスやファイル名を変数に格納。

作業に取り掛かる前に1分スリープしています。ファイル名を変更して作業するので録画直後に動作させてしまうとサムネイルの作成に失敗してしまいますので猶予をもたせています。

作業時のファイル名は時間を付与して重複するのを回避しています。

TsSplitterで分割したらファイルサイズでソートして大きなものをエンコード対象ファイルとして小さなものは削除します。

エンコードはH265で標準的なプリセットに設定。やっぱり標準がいちばんバランスいいと思います。

作業が終わったら元動画の名称を戻します。元動画、余分な部分を削除した動画、エンコードした動画の3つになります。頻繁にスクリプトを調整していますが、ここのところは安定傾向です。

それと過去に録画したTSファイルをH265に再度エンコードするスクリプトも用意しました。EPGStationのエンコードを圧迫しないようにプロセスを確認してから動作するようにしてます。H264のライブラリをH265にしてホームサーバのディスク容量を節約しているつもりです。

#!/usr/bin/env bash

set -eux

SCRIPT_DIR=/home/koichiroyamada/record_scripts
LOG_FILE=$SCRIPT_DIR/recorder.log

STOCK_DIR=ウルトラマン
SOURCE_DIR=/path/$STOCK_DIR
OUTPUT_DIR=/path/enc/$STOCK_DIR

TASK_NO=`date "+%H%M"`

PROCESS_NAME="265.sh"
count=`ps -ef | grep $PROCESS_NAME | grep -v grep | wc -l`
echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] script awake" >> $LOG_FILE
if [ $count -lt 4 ]; then
    echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] ... no processes exist ($count)" >> $LOG_FILE
else
    echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] ... another process still working ($count)" >> $LOG_FILE
    echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] script dismiss" >> $LOG_FILE
    exit 1
fi

if ls $SOURCE_DIR/*.ts >/dev/null 2>&1; then
    cd $SOURCE_DIR
    IFS_BACK="$IFS"
    IFS=$'\n'
    for target in $(ls -1 $SOURCE_DIR/*.ts)
    do
        target_file="${target##*/}"
        if [ -s "$OUTPUT_DIR/${target_file%.ts}.mp4" ]; then
            echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] skip ${target_file%.ts}.mp4" >> $LOG_FILE
        else
            PROCESS_NAME="TsSplitter.exe"
            count=`ps -ef | grep $PROCESS_NAME | grep -v grep | wc -l`
            if [ $count -lt 1 ]; then
                echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] split process clean ($count)" >> $LOG_FILE
            else
                echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] ... another split process found" >> $LOG_FILE
                echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] script dismiss" >> $LOG_FILE
                exit 1
            fi
            PROCESS_NAME="HandBrakeCLI"
            count=`ps -ef | grep $PROCESS_NAME | grep -v grep | wc -l`
            if [ $count -lt 1 ]; then
                echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] encode process clean ($count)" >> $LOG_FILE
            else
                echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] ... another encode process found" >> $LOG_FILE
                echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] script dismiss" >> $LOG_FILE
                exit 1
            fi
            echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] ●REC ${target_file%.ts}.mp4" >> $LOG_FILE
            /usr/bin/HandBrakeCLI -f mp4 -l 720 --crop 0:0:0:0 \
                -e x265 --x265-preset=medium --h265-profile=main -q 28 --detelecine \
                --aencoder copy:aac --mixdown auto \
                -i "$SOURCE_DIR/$target_file" \
                -o "$OUTPUT_DIR/${target_file%.ts}.mp4" 1> /dev/null
            echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] ■STOP ${target_file%.ts}.mp4" >> $LOG_FILE
        fi
    done
    IFS="$IFS_BACK"
else
    echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] ... no files to encode" >> $LOG_FILE
fi
echo "[INFO] `date "+%Y-%m-%d %T"` [x265][$TASK_NO] script dismiss" >> $LOG_FILE

crontabで定時で起動するスクリプトにしました。自身がすでに稼働していたら終了。また、先のwowow用スクリプトを念頭にTsSplitterやHandbrakeが起動していたら動作を終了します。何もしていなかったら指定したフォルダのTSファイルをH265に順次エンコードするというのがおおまかな動作内容です。

もっとスマートなやり方があると思いますが、動きゃいいんだよでそのままです。とても安定しておりますね。

いまのところ、こんな感じで動作していますが、環境によってはFirewallなんかもあわせて設定する必要があるでしょうね。