はじめに

何の用途に使用するか決めないまま取り合えず面白そうだからだいぶ昔に購入した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を有効化する
  • ユーザ名とパスワードを設定する
  • ロケール(タイムゾーン/キーボードレイアウト)設定をする
その後、PCにセットされているmicroSDカードに対応するストレージを選択する。
 
 
そして書き込むボタンを選択する。データを削除する確認画面が表示されるのではいを選択する。
 
 
インストールが完了すると以下の画面が表示されるので続けるボタンを選択する。
 
 

上記の終了画面が表示される際、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カードリーダ/ライタのドライバをインストールする

B-CASカードを読み取る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

録画プログラムをインストールする

録画プログラムrecdvbを以下のコマンドでインストールする。
$ 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 ファイル名の書式

おわりに

本セットアップにあたり、下記のサイトを参考にした。