はじめに
何の用途に使用するか決めないまま取り合えず面白そうだからだいぶ昔に購入したRaspberry Pi 2 Model B。この度、地上デジタル放送を録画するサーバとしてこのRaspberry Piを活用してみたので、そのセットアップの記録をここにメモする。
ハードウェアを準備する
録画サーバの構築に必要な主なハードウェアは以下の通り。
- サーバ: Raspberry Pi 2 Model B
- 地上デジタル放送チューナー: PLEX PX-S1UD V2.0
- ICカードリーダ/ライタ: SCR3310
ソフトウェアをセットアップする
必要なハードウェアが準備できたら次にソフトウェアをセットアップする。
microSDカードにRaspberry Pi OSをインストールする
下記の動画にあるようにRaspberry Pi Imagerを使用してmicroSDカードにRaspberry Pi OSを書き込む。
そのためにRaspberry Pi Imagerをインストールする。
PCにmicroSDカードを挿入後、Raspberry Pi Imagerを起動すると以下の画面が表示される。
OSを選択する。
歯車ボタンから以下の設定をする。
- SSHを有効化する
- ユーザ名とパスワードを設定する
- ロケール(タイムゾーン/キーボードレイアウト)設定をする
上記の終了画面が表示される際、Windowsからドライブをフォーマットするかどうかを尋ねるポップアップが表示されるので、誤ってはいを選択してmicroSDカードを初期化しないよう注意する。
OSのインストール後、Raspberry PiにmicroSDカード、HDMI端子、有線LAN、キーボード、マウスをセットして最後に電源を投入する。電源を投入すると直ちに自動的に初期化作業が始まっていることを示す画面が表示されるので、デスクトップ画面が表示されるまで数分間待機する。
Raspberry Piはサーバとして使用するため、グラフィカルな画面は必ずしも必要としない。このため、以後の作業はssh経由とする。以前とは異なり、現在のWindows10は標準でsshクライアントがインストールされているため、コマンドプロンプト画面から以下の書式でPCからRaspberry Piにssh経由で接続できる。
$ ssh ユーザ名@IPアドレス
チューナのドライバをインストールする
今回使用する地上デジタル放送のチューナは、Ver3.15以降のLinux Kerneでサポートされているため、別途、新しくドライバを追加する必要はない。ただ、チューナを正常動作させるためには、メーカが提供するファームウェアを所定のディレクトリに配置しなければならないようなので、以下のコマンドを実行する。
$ wget http://plex-net.co.jp/plex/px-s1ud/PX-S1UD_driver_Ver.1.0.1.zip $ unzip PX-S1UD_driver_Ver.1.0.1.zip $ sudo cp PX-S1UD_driver_Ver.1.0.1/x86/i386/isdbt_rio.inp /lib/firmware/
ファームウェアをインストール後、チューナをRaspberry Piに接続し、/dev/dvbフォルダにadapter0が見えることを確認する。
ICカードリーダ/ライタのドライバをインストールする
$ sudo apt install pcscd libpcsclite-dev libccid pcsc-tools
ドライバをインストール後、B-CASカードが挿入されたICカードリーダ/ライタをRaspberry Piに接続し、以下のコマンドでスキャンする。
$ pcsc_scan
スキャンすると「Japanese Chijou Digital B-CAS Card (pay TV)」が表示されることを確認する。確認後、「Ctrl + C」でスキャンを終了する。
デコーダライブラリをインストールする
チューナで受信したTS(トランスポートストリーム)を復号化するライブラリlibarib25を以下のコマンドでインストールする。
$ sudo apt install cmake $ git clone https://github.com/stz2012/libarib25 $ cd libarib25 $ cmake . $ make $ sudo make install
録画プログラムをインストールする
$ sudo apt install autoconf $ git clone https://github.com/kaikoma-soft/recdvb $ cd recdvb $ ./autogen.sh $ ./configure --enable-b25 $ make $ sudo make install
インストール後、チューナ、ICカードリーダ/ライタ、及びそれらに関連するソフトウェアが正常に動作することを確認するため、試しに以下のコマンドで録画してみる。
$ recdvb --b25 --strip 物理チャンネル番号 30 example.m2ts
物理チャンネル番号はリモコンの番号と異なることに注意。以下のサイトから居住している地域のチャンネル番号を調べる。
https://www.maspro.co.jp/contact/channel.pdf
Windows10ではMPEG2のコーデックがデフォルトでインストールされていないため、録画したファイルを再生できないことがある。この場合、Microsoft Storeから「MPEG-2 ビデオ拡張機能」をインストールすることでWindows10の標準アプリでm2tsファイルを再生できるようになる。
電子番組表取得プログラムをインストールする
後述する予約録画システムをインストールするために必要となる電子番組表取得プログラムepgdumpを以下のコマンドでインストールする。
$ git clone https://github.com/Piro77/epgdump $ cd epgdump $ ./autogen.sh $ make $ sudo make install
インストール後、前述の録画したexample.m2tsファイルを使用して以下のコマンドを実行すると電子番組表example.xmlが出力されることを確認する。
$ epgdump example.m2ts example.xml
予約録画システムをインストールする
ブラウザに表示される番組表から録画を予約するシステムraspirecをインストールする。本システムはrubyとsqlite3で構築されているので、始めに以下のパッケージをaptでインストールする。
- sqlite3
- ruby
- ruby-sqlite3
- ruby-sys-filesystem
- ruby-net-ssh
- ruby-sinatra
- ruby-slim
- ruby-sass
$ sudo apt install sqlite3 $ sudo apt install ruby ruby-sqlite3 ruby-sys-filesystem ruby-net-ssh ruby-sinatra ruby-slim ruby-sass
これらのパッケージをインストール後、raspirecのプロジェクトをダウンロードする。
$ git clone https://github.com/kaikoma-soft/raspirec.git
設定ファイルの雛形を所定の場所にコピーする。
$ mkdir ~/.config/raspirec $ cp raspirec/config.rb.sample ~/.config/raspirec/config.rb
自分の環境に合わせてconfig.rbファイルを編集する。自身の場合、以下の項目を変更した。
Recpt1_cmd = "/usr/local/bin/recdvb" Recpt1_opt = %w( --b25 --strip ) BaseDir = ENV["HOME"] + "/Downloads/raspirec" DataDir = ENV["HOME"] + "/Videos" GR_tuner_num = 1 # 地デジ チューナー数 BSCS_tuner_num = 0 # BS/CS チューナー数 GR_EPG_channel = %w( 20 13 14 17 15 18 19 ) # 地デジ EPG受信局(スカイツリー) BS_EPG_channel = %w( ) # BS EPG 受信局 CS_EPG_channel = %w( ) # CS EPG 受信局 EpgBanTime = [ ] # EPG禁止時間帯(24H制)
設定ファイルを編集したら以下のコマンドでraspirecをフォアグラウンドで実行し、エラーが出力されないことを確認する。
$ ruby raspirec.rb -f
なお、当環境では上記コマンドを実行すると以下のエラーメッセージが出力された。
Traceback (most recent call last): 11: from raspirec.rb:16:in `<main>' 10: from /usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_require.rb:85:in `require' 9: from /usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_require.rb:85:in `require' 8: from /home/pi/Downloads/raspirec/src/require.rb:31:in `<top (required)>' 7: from /usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_require.rb:85:in `require' 6: from /usr/lib/ruby/vendor_ruby/rubygems/core_ext/kernel_require.rb:85:in `require' 5: from /home/pi/Downloads/raspirec/src/lib/rewriteConst.rb:43:in `<top (required)>' 4: from /home/pi/Downloads/raspirec/src/lib/rewriteConst.rb:12:in `constRewrite' 3: from /home/pi/Downloads/raspirec/src/db/Log.rb:56:in `sto' 2: from /home/pi/Downloads/raspirec/src/db/Log.rb:47:in `write' 1: from /home/pi/Downloads/raspirec/src/db/Log.rb:47:in `open' /home/pi/Downloads/raspirec/src/db/Log.rb:47:in `initialize': No such file or directory @ rb_sysopen - /home/pi/Videos/log/raspirec.log (Errno::ENOENT)
エラーのメッセージを読むとどうやら/home/pi/Videos/logディレクトリにログファイルをオープンできないことが原因のようである。そこで確認してみるとlogディレクトリが無かったため、以下のコマンドでディレクトリを作成する。
$ cd Videos $ mkdir log
そうすると上記のコマンドを実行してもエラーメッセージが表示されなくなった。そこで「Ctrl + C」でraspirecを終了し、次は以下のコマンドでraspirecをデーモンとして起動する。
$ ruby raspirec.rb
ブラウザから4567ポートにアクセスし、番組表などが表示されることを確認する。
なお、当環境だと予約録画をするとraspirecのログページに「録画開始失敗: <番組名> retry 1」が何故か出力される。ソースコードを調べるとどうやらsrc/model/Timer.rbにあるTimer#recStartメソッドにおいて、例外の発生により処理がリトライされている模様だということが分かった。以下に例外が投げられている箇所をマーカーで示す。
begin duration = durationCalc( data ) finish = Time.now + duration DBlog::sto("終了予定 = #{finish} : #{data[:title]}") fname = makeTSfname2( data, duration, retryC ) bs = File.basename( fname ).bytesize if bs > 255 DBaccess.new().open do |db| DBlog::warn(db, "ファイル名長(#{bs}) > 255") end end duration2 = AutoRecExt == true ? duration * 2 : duration arg = [ ] arg += Recpt1_opt if Recpt1_opt != nil arg += ch + [ duration2.to_s, fname ] waitT = retryC + 2 if data[:svid] == 101 or data[:svid] == 102 # NHK BS は遅い waitT = retryC + 5 end recpt1 = Recpt1.new pid = recpt1.recTS( arg, fname, waitT, finish.to_i ) DBlog::stoD( "pid=#{pid}") DBlog::info( nil,"録画開始: #{data[:title]} : pid=#{pid}") rescue ExecError retryC += 1 if retryC < 10 DBlog::warn( nil,"録画開始失敗: #{data[:title]} retry #{retryC}") sleep( 1 + sleeptime ) retry else DBaccess.new().open do |db| text = "録画開始失敗:" DBreserve.new.updateStat(db,data[:id],stat: RsvConst::AbNormalEnd, comment: text ) text += " #{data[:title]}" DBlog::warn(db, text) end return end rescue puts $! puts $@ end
このExecErrorを生成している箇所のソースコードであるsrc/model/Recpt1.rbを見ると、録画を開始してから一定時間内に出力ファイルが作成されないか出力ファイルが作成されてもそれが1024バイト以下であれば、録画を終了して例外を生成しているようであった。
# # 録画の実行 # def recTS( args, outfname, waitT, endTime = nil ) @endTime = endTime if endTime != nil raise ExecError if test(?f, outfname ) # 出力ファイルが既に有る pid = fork do now = Time.now txt = sprintf("%s: %s\n",now.strftime("%H:%M:%S"),args.join(" ")) STDOUT.puts( txt ) STDOUT.flush exec( Recpt1_cmd, *args, :err=>:out ) end $rec_pid[ pid ] = true (waitT * 5 ).times do |n| if test(?f, outfname ) size = File.size( outfname ) break if size > 1024 end sleep(0.2) end if ! test(?f, outfname ) or File.size( outfname ) < 1024 DBlog::sto("kill for retry #{pid}") begin Process.kill(:HUP, pid); rescue end raise ExecError end return pid end
そこで、録画開始から出力ファイルのサイズを確認するためのタイムアウト時間を延ばすこととした。以下に示すTimer.rbのTimer#recStartメソッド内にある録画開始処理を呼び出す際に渡すパラメータを変更する。
waitT = retryC + 2 + 6 if data[:svid] == 101 or data[:svid] == 102 # NHK BS は遅い waitT = retryC + 5 + 6 end recpt1 = Recpt1.new pid = recpt1.recTS( arg, fname, waitT, finish.to_i )
これでひとまず様子を見ることとする。
また、デフォルトだと録画したファイル名に:(コロン)が自動で含まれるのだが、Windowsだとコロンはファイル名として扱えないため、Raspberry PiとPCの間で録画ファイルをやり取りするとき難がある。このため、設定ファイル(~/.config/raspirec/config.rb)を以下のように設定する。
TSnameFormat = "%YEAR%-%MONTH%-%DAY%_%HOUR%%MIN%_%DURATION%_%TITLE%_%CHNAME%" # TS ファイル名の書式
おわりに
本セットアップにあたり、下記のサイトを参考にした。