タイムシフト録画(全録)

Mirakurunには無い機能として,mirakcはタイムシフト録画(いわゆる全録)を実装しています.機能の細かい説明は後回しにして,動かしてみましょう.

まず,タイムシフト録画用のフォルダーとファイルを作成します.

$ mkdir timeshift
$ fallocate -l 1540096000 timeshift/bs1.timeshift.m2ts
$ ls -lh timeshift
total 3.1G
-rw-r--r-- 1 pi pi 1.5G Mar 18 07:21 bs1.timeshift.m2ts

ディスク容量が足りない場合,ファイル作成に失敗します.十分な空き容量を作ってから再チャレンジしてください.

フォルダーとファイルの作成に成功したら,docker-compose.ymlを修正し,これをmirakcコンテナーにマウントします.

# 追加部分のみ抜粋
services:
  mirakc:
    volumes:
      - ./timeshift:/var/lib/mirakc/timeshift

BS1をタイムシフト録画するようにconfig.ymlを修正してみます.

# 追加部分のみ抜粋
jobs:
  # 通常,タイムシフト録画では以下のジョブは不要
  # 無効化すると`/api/programs`などが機能しなくなる
  sync-clocks:
    disabled: true
  update-schdules:
    disabled: true

timeshift:
  recorders:
    bs1:
      service-id: 400101
      ts-file: /var/lib/mirakc/timeshift/bs1.timeshift.m2ts
      data-file: /var/lib/mirakc/timeshift/bs1.timeshift.json
      num-chunks: 10

準備は整いました.mirakcを起動してください.

$ sudo docker compose up -d

タイムシフト録画が動いていれば,以下のように表示されます.

$ curl -sG http://localhost:40772/api/timeshift | jq .[].name
"bs1"

起動直後は何も録画されていないので何も視聴できませんが,1,2分程度待つと視聴可能な状態になります.

# 直後は0で視聴できないが,そのうち0以外が表示されるようになる
$ curl -sG http://localhost:40772/api/timeshift/bs1 | jq .duration
75420

ffplayがインストールされているデスクトップ環境上で以下のコマンドを実行すると,最初の番組からタイムシフト再生されます.

# raspberrypi上ではなく,別のデスクトップ環境上で実行すること
curl -sG http://raspberrypi.local:40772/api/timeshift/bs1/stream | ffplay -

上記のWeb APIはタイムシフト録画をライブ視聴相当でストリーミングします.そのため,シークはできませんが,視聴中の番組が終了したら次の番組が自動で再生されます.

特定の番組のみを視聴したい場合は,以下のWeb APIを使います.

ID=$(curl -sG http://raspberrypi.local:40772/api/timeshift/bs1/records | jq .[0].id)
curl -sG http://raspberrypi.local:40772/api/timeshift/bs1/records/$ID/stream | ffplay -

こちらはWeb API呼び出し時に録画されているところまでが再生可能範囲になります.シーク可能ですが,録画中の番組を再生した場合,途中までしか再生されません.普通は録画完了した番組の再生に使います.

タイムシフト録画とは?

冒頭でも書きましたが,mirakcのタイムシフト録画は,いわるゆ全録機能です.以下のような特徴を持ちます.

  • 固定サイズのファイルをリングバッファーとして,指定サービスのTSストリームを録画します
    • サイズはconfig.ymlで指定します
  • サイズ上限に達すると,古い録画から順番に消えていきます
    • ファイルをある固定サイズのチャンクに分割し,この単位で消していきます
  • mirakcを停止しても録画は消えません
    • 再起動しても前の録画を残し,続きから録画を再開します

固定サイズのファイルを使って録画を行うため,タイムシフト録画を行ない続けても,ディスクを際限なく消費してしまうことはありません.

タイムシフト録画が設定されると,mirakcは指定されたサービスの番組をひたすら録画し続けます.このような性質の機能であることから,タイムシフト録画を有効にしたmirakcを起動する場合は,以下の条件を満たしておく必要があります.

  • タイムシフト録画の数以上のチューナーを用意する
    • mirakcを起動することは可能ですが,タイムシフト録画数はチューナー数を超えません
  • 十分なディスク容量を用意する
    • 容量が足りないと書き込みエラーが発生して機能しないため,予めfallocateなどでts-fileを最大サイズで確保しておくことが望ましいです
    • ts-fileの最大ファイルサイズは,チャンクサイズ(既定値は154009600で約154MB)にnum-chunks を掛けた数
    • data-fileの最大ファイルサイズは計算不可能ですが,多くても数MB程度
  • 常時起動
    • NASに書き込むのはお勧めしません(NASのファームアップデート時に停止する必要があるため)

このような条件を満たす必要があるため,タイムシフト録画用のmirakcは専用の独立サーバーとして運用することをお勧めします.その上で,タイムシフト録画の対象となるチャンネルのみをconfig.ymlに定義します.こうしておけば,EPG関連データの取得に失敗することはなくなります.なぜなら,常時チューナーが開いているためです.一方,タイムシフト録画の対象になっていないチャンネルをconfig.ymlに定義していると,そのチャンネルのEPG関連データの取得のためチューナーを開こうとするため,チューナーの数がタイムシフト録画の数以上存在しないとエラーが発生します.

タイムシフト録画に必要なチャンク数の計算方法

先に説明したように,タイムシフト録画用のts-fileは,チャンクサイズ(chunk-size)とチャンク数( num-chunks)で決まります.

ts-file-max-size = chunk-size * num-chunks

タイムシフト録画に使用するディスクサイズが既に決まっている場合には,それを超えないようにチャンク数を決めることになります.例えば,既定値のチャンクサイズで1チャンネルを最大1TBだけタイムシフト録画したい場合は,

num-chunks = ts-file-max-size / chunk-size = 1000000000000 / 154009600 = 6493

となります.

録画期間からチャンク数を計算する場合は,少し面倒な計算が必要です.例えば,BS1を一週間分タイムシフト録画しようと考えた場合,

  • BS1のTSストリームのビットレートは約20Mbps
  • 一分間の録画に必要なサイズは20M bits * 60 / 8で約150MB(既定値の1チャンクで大体1分間録画できる)
    • タイムシフト録画対象のサービスに依存しますが,既定値の1チャンクは約1〜3分に相当するサイズです
  • 150 * 60 * 24 * 7 = 1512000で約1.5TB

となります.これらを計算式に当てはめると,

num-chunks = 1512000000000 / 154009600 = 9817

となるため,num-chunks10000を指定しておけば,余裕を持って一週間分タイムシフト録画できると期待できます.この場合のts-fileの最大ファイルサイズは,1540096000000(約1.5TB)です.

TSストリームのビットレートはサービスごとに異なるため,タイムシフト録画の期間を決めてからチャンク数を求めたい場合には,サービスごとにビットレートを計測する必要があります.また,ビットレートは固定ではなく時間変動するため,上記計算は目安程度と考えてください.

以下は実績値です.参考にしてください.

サービス chunk-size num-chunks 録画可能期間
NHK総合1・東京 154009600(既定値) 14000 二週間程度
NHKEテレ1東京 154009600(既定値) 14000 二週間程度
NHKBS1 154009600(既定値) 18000 二週間程度
NHKBSプレミアム 154009600(既定値) 18000 二週間程度

上記各タイムシフト録画のdata-fileはどれも2MBを超えません.そのため,上記設定で4チャンネルのタイムシフト録画を10TBのHDDで行うことが可能です.

config.ymlの設定値に基いて必要なサイズのファイルを確保するスクリプトを用意してあります.

deno run -A \
  https://raw.githubusercontent.com/mirakc/contrib/main/timeshift/allocate-ts-file.js \
  -c /path/to/config.yml

詳細については-hで表示されるヘルプを見るか,スクリプトファイル の内容を直接確認してください.

データ管理方法

mirakcのタイムシフト録画では,録画データ(TSパケット)ファイル(ts-file)をチャンクという塊で管理します.そのため,番組の録画が進んでもチャンクがいっぱいになるまで録画済みとは扱われません.

ts-file
+-------------------------------------------------
| Chunk#0 | Chunk#1 | Chunk#2 | Chunk#3 | ...
|xxxxxxxxx|xxx______|_________|xxxxxxxxx|x...
+-------------------------------------------------
|         |   |               |
+-番組#100-+   +-番組#10だがもう-+-番組#10(途中から)---
                見ることはできない

上記ではChunk#0はデータで埋まっているため録画済みとして扱われます.一方,Chunk#1は現在書き込みを行っているチャンクで,データで埋まるまで録画画済みとは扱われません.Chunk#1には過去の録画データが記録されている場合がありますが,データは上書きされていきます.

Chunk#2にも録画データが書き込まれているかもしれませんが,このデータは古くなったものとして扱われます.その結果,古い番組のデータが消えていきます.

TSパケットの他に,録画済み番組情報などを記録しておく必要があり,data-fileはそのために使用されます.data-fileを定期的に更新する必要がありますが,これがチャンク単位で行われるようになっています. data-fileを削除すると,ts-fileがあっても,どこにどの番組が録画されているのかわからなくなり,録画された番組はないものとして扱われます.

タイムシフト・ファイルシステム

mirakcはバックエンド機能しか提供しません.そのため,タイムシフト録画した番組を再生する場合,curljqなどを使って,必要な情報を取得する必要があります.Web UIなどのフロントエンドを実装すればよいのでしょうが,今の所はその計画はありません.

タイムシフト録画した番組を再生できるとはいえ,流石にこのままでは不便なので(作った本人である私も)使いたくはありません.そこで,FUSEを使ってタイムシフト録画した番組を普通のファイルとして扱えるファイルシステムを用意しました.

以下をdocker-compose.ymlに追記してください.

services:
  ...
  mirakc-timeshift-fs:
    container_name: mirakc-timeshift-fs
    image: mirakc/timeshift-fs
    init: true
    restart: unless-stopped
    cap_add:
      - SYS_ADMIN
    devices:
      - /dev/fuse
    volumes:
      # タイムシフト録画しているmirakcと同じ設定を使うこと
      - ./config.yml:/etc/mirakc/config.yml:ro
      # タイムシフト録画ファイルをコンテナーにマップ
      - ./timeshift:/var/lib/mirakc/timeshift
      # mirakc/timeshift-fsはコンテナー内の/mntにタイムシフト・ファイルシステムをマウントする
      # 下記はこれをDockerホストの./timeshift-fsに反映させる
      - type: bind
        source: ./timeshift-fs
        target: /mnt
        bind:
          propagation: rshared
    environment:
      TZ: Asia/Tokyo
      RUST_LOG: info
      # タイムシフト・ファイルシステムの所有者の設定
      # ログイン・ユーザーのUID/GIDを指定
      MIRAKC_TIMESHIFT_UID: 1000
      MIRAKC_TIMESHIFT_GID: 1000
      MIRAKC_TIMESHIFT_MOUNT_OPTIONS: ALLOW_OTHER

timeshift-fsフォルダーを作成し,追加したコンテナーを起動します.

mkdir timeshift-fs
sudo docker compose up -d mirakc-timeshift-fs

起動に成功すれば,timeshift-fsにタイムシフト・ファイルシステムがマウントされます.

$ ls timeshift-fs
bs1

$ ls timeshift-fs/bs1
6052B1C8.BSニュース.m2ts

タイムシフト・ファイルシステムは読み取り専用なので書き込みはできません.録画が進むとファイルが増えたり減ったりします.

普通のファイルとして扱われるので,あとは煮るなり焼くなり好きにできます.試しにSambaでファイル共有してみましょう.

docker-compose.ymlを書き換え

services:
  ...
  samba:
    depends_on:
      # ./timeshift-fsへのタイムシフト・ファイルシステムのマウント完了後に起動する
      - mirakc-timeshift-fs
    container_name: samba
    image: dperson/samba
    command:
      # LAN(192.168.0.0/16)からのアクセスのみ許可
      - '-g'
      - 'hosts allow = 192.168.'
      # timeshiftフォルターとして公開(ゲスト・アクセス可)
      - '-s'
      - 'timeshift;/mnt'
    init: true
    restart: unless-stopped
    ports:
      - '139:139'
      - '445:445'
    volumes:
      # タイムシフト・ファイルシステムをコンテナー内にマップ
      - ./timeshift-fs:/mnt:ro
    environment:
      TZ: Asia/Tokyo

sambaコンテナーを起動します.

sudo docker compose up -d samba

エクスプローラーやFinderでSambaに接続し,MPEG2-TSを再生可能なメディアプレーヤーでファイルを開けば,番組が再生されるはずです.

macOSユーザーには,Finderのプレビューを無効にすることをお勧めします.これを有効にしていると,動画のサムネイル画像の生成処理が開始され,Finderが操作不能になる場合があります.また,アプリケーションのメニューからファイルを開くと,Finderのプレビューを無効にしているのに,なぜかサムネイル画像を生成しようとすることがあるようなので,m2tsファイルをメディアプレーヤーに関連付けて,openコマンドで開くことをお勧めします.

どうもタイムシフト・ファイルシステムのアンマウントがうまくできないようで,それが原因で mirakc-timeshift-fsコンテナーの再起動に失敗するようです.そのような場合は,以下のようにアンマウント後にmirakc-timeshift-fsコンテナーを起動してください.

sudo umount ./timeshift-fs

多重音声(デュアルモノラル)について

タイムシフト録画は,単にTSパケットをファイルに書き込むだけなので,多重音声(デュアルモノラル)な録画を再生する場合,メディアプレーヤーがこれをサポートしていないと2つの音声(例えば,日本語と英語)が同時に再生されます.

多重音声(デュアルモノラル)をサポートしているメディアプレーヤーは数が限られています.例えば,日本向けのTVなどでは音声切り替え可能ですが,Kodiなどはサポートしていません.

非サポートのメディアプレーヤーで正しく再生するためには,再生前にトランスコードする必要があります. フィルター設定で説明しましたが,mirakcのフィルターを使えば,わざわざトランスコード済みのファイルを作成する必要はありません.

以下のsplit-dual-monoフィルターは,ステレオ音声を2つの音声に分離します.

# ffmpegがインストール済みのカスタムイメージを使うこと
filters:
  post-filters:
    # See https://trac.ffmpeg.org/wiki/AudioChannelManipulation
    split-dual-mono:
      command: >-
        ffmpeg -i - -hide_banner -vcodec copy
        -filter_complex "[0:a]channelsplit=channel_layout=stereo"
        -f mpegts pipe:1

音声トラックを切り替え可能なメディアプレーヤーで再生すれば,目的の言語の音声のみを再生できます.

# ステレオ音声が2つのモノラル音声に分離されることを確認
# 実際は多重音声(デュアルモノラル)の録画にフィルターを適用する
STREAM=http://raspberrypi.local:40772/api/timeshift/bs1/stream
curl -sG "$STREAM?post-filters[]=split-dual-mono" | ffprobe

コンソール上のログから,ステレオ音声が分離されていることを確認できます.

Input #0, mpegts, from 'pipe:':
  Duration: N/A, start: 1.400000, bitrate: N/A
  Program 1
    Metadata:
      service_name    : Service01
      service_provider: FFmpeg
    Stream #0:0[0x100]: Audio: mp2 ([3][0][0][0] / 0x0003), 48000 Hz, mono, fltp, 384 kb/s
    Stream #0:1[0x101]: Audio: mp2 ([3][0][0][0] / 0x0003), 48000 Hz, mono, fltp, 384 kb/s
    Stream #0:2[0x102]: Video: mpeg2video (Main) ([2][0][0][0] / 0x0002), yuv420p(tv, bt709, top first), 1440x1080 [SAR 4:3 DAR 16:9], 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc
    Side data:
      cpb: bitrate max/min/avg: 20000000/0/0 buffer size: 9781248 vbv_delay: N/A

欠点として,post-filterでストリームを変換した場合,シークできなくなります.変換によりストリームが動的に生成されるため,HTTPレスポンスはContent-Lengthヘッダーを含まず,HTTPレスポンス・ボディにはチャンク・エンコーディングが適用されるためです.

タイムシフト録画の再実行

タイムシフト録画が何らかの理由で停止した場合,自動でタイムシフト録画を再実行します.

INFO mirakc_core::timeshift: Recording stopped recorder.name="bs1"
...
INFO mirakc_core::timeshift: Recording started recorder.name="bs1"

タイムシフト録画が停止している間も,録画データにアクセス可能です.

放送休止中にタイムシフト録画が停止することをユーザーが報告しています.

results matching ""

    No results matching ""