やんごとなき事情により、光学ディスクから動画を取り込む必要が出てきたので、調べながらやってみた。そもそもPCに光学ドライブがついていないので、使っていないマシンからかっぱらってSATA接続するところから始めた。
ちゃんと知らなかったけど、 コピーガード機能がついたディスクからデータをリッピング(取り込み)するのは、私的であっても違法となる。
今回は、コピーガードがついていないディスクからの取り込みを行う。ディスクから動画データを取り込んで、PCやiPhoneで見られるようにエンコードし、家庭内LANで視聴可能にするのが目的だ。
リッピングは、Windowsマシンで行う。手元にSATA接続のドライブしか無かったのと、 NVIDIA GPU が刺さっているからである。 GPUを使うことで、エンコード・デコードを効率化できる。 あらかじめ GeForceドライバを最新にしておく。(2020年の古いドライバで作業していたら色々とエラーが出た)
リッピングのためのソフトは、探せばいくつも見つけることができるが・・・見るからに危険なサイトが多いので注意が必要となる。今回は HandBrakeを使う。HandBrakeは、単純に、暗号化されていない動画をほかの形式にコンバートするためのツール。
chocoでもでインストールできる。 https://community.chocolatey.org/packages/handbrake
choco install handbrake
続いて、データを取り込む。 取り込む際にフォーマットを考えることになる。 動画ファイルは、単なる映像で構成されているわけではなく、コンテナ、ビデオ、オーディオ、あと字幕で構成されている。 HandBrakeでは以下のコンテナが選べる。
- MP4
- 昔からあるので対応環境が多い
- 字幕を使えない
- MKV
- いろいろなコーデックを格納できる
- MP4よりも対応環境が少ない
- 字幕が使える
- WebM
- パテントフリーな動画・音声コーデックを使う
- iOS標準では再生できない。
今回は、字幕つきBlu-Rayから取り込むため、MKVを使うことにした。 動画エンコードのフォーマットを、「H.264 (x264)」の代わりに 「H.264 (Nvidia NVEnc)」にする ことでGPUエンコードが有効になる。わたしの環境では、ふつうのエンコードよりも3倍速くらいになった。
こうしてできたMKVファイルをてきとうなNASに配置する。 iPhoneやiPadからは「VLC Playerアプリ」 を使って再生する。このアプリを使うと、デバイスが対応していないフォーマットであってもだいたい再生できる。また、データを端末にフルダウンロードせずとも再生できる。
ここまでで、動画の取り込みとネットワーク越しの再生は担保できたが、一歩進んで Jellyfin を試してみることにする。
Jellyfinは、オープンソースのメディアライブラリ。 例えるなら、AppleTVやAndroidTVや、PrimeVideoっぽい感じのライブラリを構築できる。完全にLANで完結させることが可能で、外部への依存はほとんど無い。望むなら、外部のデーターベースサービスで音楽や映画などの情報を検索して紐づけることができる。
サーバはC#で書かれているが、C# = Windowsというのはもはや古い常識で、クロスプラットフォームに対応している。Windows、Mac、Linuxで動かすことができるし、Dockerイメージも配布されている。
フロントエンドは、Web UI がある。ネイティブ・アプリは試していない。 わたしは基本的に Web UI での再生を考えている。 iOS向けにもアプリがあるが、これは Expo で作られたガワ・ネイティブである。Swift版は開発中とのことで待ち遠しく思う。
データソースも柔軟で、ローカルディスクでも、NASでも大丈夫。また、データソースとデータベースが分離されている点も好感がもてる。フォルダ構造などは考えずとにかくファイルを置いておけば、あとはスキャンして自前のデータベースが構築される。 一応、フォルダ構造やファイル名の推奨ルールがあり、外部データベースをスクレイピングしたり、グルーピングするのに役立てられる。 データベースには内部的にsqliteを使っている。 それから、アートワークなどを格納するために余裕をもったディスクスペースを必要とする。
まずは、いつも使っている Raspberry Pi に docker-compose で立ててみる。
Docker hub の linuxserver/jellyfin には、arm64イメージがあるのでこれを使う。
難しいことは一切なく、すんなり立ち上がる。 しかし、この方法では、DLNAサーバが使えないことに気づく。Dockerコンテナは独自のネットワークに接続していて、ポートマッピングやIPマスカレードによって外部と通信しているからだ。家庭内LANにいるDLNAクライアントからは見えない。
そこで、ほとんど同じだが、macvlanを使ってコンテナを家庭内LANに参加させる。 目論見通り、DLNAサーバとして機能させることができた。
しかし、Web UI での動画の再生が遅いことに気づく。詰まる。 とくに、「字幕」をONにすると、視聴できないレベルになる。 回線の問題とは思えないが・・・
そこで、トランスパイルに気づく。
Jellyfinでは、ブラウザで動画を配信するさいに、クライアントが対応するフォーマットに自動的にトランスパイル(変換)しながら配信する。
どんなときにトランスパイルが発生するのかは、公式ドキュメントに書かれている。
ビデオコーデックのトランスパイルがとくに高負荷なので、避けるべき。 逆に、コンテナのトランスパイルは、小負荷である。
今回は「PGS」という画像形式による字幕を使っている。 なお、字幕にも色々あって、テキスト形式のものもあるようだ。 どうやら、再生時にPGS字幕をONにすると、 メイン動画と字幕を別々に配信するわけではなく、 1つの動画として結合して配信してくれているのだ。 これには、デコード・合成・再度エンコードという処理が必要になる。
このトランスパイルによって、VLC Playerを使わずとも、ブラウザだけで、あらゆる環境で視聴できるってわけ。ブラウザごとに対応コーデックがバラバラなのにもかかわらず。
そもそもMKVコンテナを使わずにMP4に字幕を焼き付けてしまえば、こんな苦労は無いのだが、今回は、MKVとPGSSUB字幕を使うことにこだわりたい。
そこで、HWA(ハードウェア・アクセラレーション)を使う。
Hardware Acceleration | Jellyfin
一般に、Docker越しだとHWAが難しかったり、そもそも特別なDockerデーモンが必要になる場合もあるので、面倒を避ける。Dockerをやめて、aptでインストールしなおす。
Raspberry Pi 上の Ubuntuで直に動かし、HWA(ドキュメントに従い「V4L2」を使う)を有効にしてみる。 しかし、今度は字幕ONでの再生が全くできなくなってしまった。
これについては、GitHub issue も上がっているようで、読んでみる。
Hardware acceleration with PGS subtitles hangs indefinitely - GitHub
どうやら、Raspberry Pi の GPUや、その上のffmpegでは、字幕を乗せるさいに使うオーバーレイ系のAPIが動かないらしい。
なるほど、うーん・・・ Raspberry Pi 上で Jellyfin を動かす試みは、中断する。
ではどうするか。 先ほどリッピングに利用したWindowsマシンで Jellyfin を動かすことにする。 Windows向けには、msiインストーラーが配布されているので、かんたんにインストールできる。 まずはHWA無しで動画再生・字幕動画再生を試すが、やはり字幕のほうは厳しい。たまに再生が詰まる。 次に、HWAを有効化する。 すると、かなり余裕を持ってトランスパイルできているようだった。
このスキームを使い続けるかどうかはわからないが、これで、 iOS Safari からでも字幕動画が再生できるようになったわけだ。
めでたしめでたし!
今回の探求では、動画フォーマットや動画配信について知ることができて収穫だった。わたしは以前から(音声や写真ほどは)動画に興味が無かったので、まったく触ってこなかった分野だ。
アーキテクチャとしての大きなポイントは、変換をいつ・だれが行うか、だ。
動画配信には、変換処理が(ほとんど)不可欠だ。 変換には大きなマシンパワーを使う。 たとえば、変換処理を、アップロード前に配信者が済ませておく。すると、再生端末はコーデックに依存してしまうが、全体で使う計算パワーとしてはもっとも少なくて済む。変換処理を、アップロード時にシステムで自動的に行うようにすれば、少し人間に優しくなる。小さいMP4を配信するならこれで十分なのだろう。
変換をせずに配信すると、閲覧者側で、VLC Player などの特別なアプリを用意する必要があるし、過剰な配信(不要なトラック、過剰な画質など)による通信帯域も気になってくる。
それから、Jellyfinのような、オンデマンドに変換しながら配信するタイプが考えられる。これは、最適なかたちで配信できるが、変換のための計算コストを配信者が支払うことになる。大規模になればなるほど、キャッシュが肝心となる。動画のキャッシュを保存するためのストレージ・コストも配信者側が支払うことになる。
JellyfinはLANで動かすことが前提でスケールアウトはできないが、(LinuxならSSHを通じて別マシンのffmpegを使えると書いてあった気がするのでhaproxyなどでさばけるのだろうか?)、公開サービスにおいては変換部分のスケーリングが重要になる。「AWS Elemental MediaConvert」などといったマネージドサービスを使うと楽にできる。
計算コストだけではなく、それぞれUI/UX特性も異なる。
それでは