45本のビデオテープをデジタル化する8年がかりの挑戦(後編)
原文は Michael Lynch により に公開されました。 このブログを購読する
前編では、古いホームビデオをデジタル形式で取り込み、シーンごとに分割するまでの苦労の多い道のりについて書いた。すべてのクリップの処理を終えたあと、私が目指したのは、YouTubeで動画を探すのと同じくらい手軽に視聴できる体験だった。ただ、これらの映像は家族のプライベートな思い出であり、本物のYouTubeでは公開範囲が広すぎる。使いやすく、かつ安全に共有できる方法が必要だった。
ステップ3:共有
ClipBucket――インストールできないオープンソース版YouTubeクローン
最初に試したのはClipBucketだった。セルフホスト可能なオープンソースのYouTubeクローンを謳っている。

ClipBucketは、ユーザーが(理論上は)セルフホストできるオープンソースのYouTubeクローンだ。
不可解なことに、ClipBucketにはインストール手順が用意されていなかった。そこでサードパーティのガイドを頼りに、サーバー構成管理ツールであるAnsibleを使ってインストール作業を自動化した。
難航した理由の一つは、ClipBucketのインストールスクリプトが完全に壊れていたことだ。当時私はGoogleの社員だったため、YouTubeクローンにパッチを送ることはできなかったが、見れば修正方法がわかるはずのバグレポートを提出した。数ヶ月が経っても彼らからは何の反応もなく、むしろリリースのたびにさらに致命的なエラーが増えていった。
ClipBucketはコンサルティングモデルで運営されていた。コード自体は無料で公開し、導入支援が必要な顧客から料金を取るという形だ。やがて、有料のインストールサポートで収益を得ている会社が、セルフサービスでの導入を積極的に支援するはずがないことに気づいた。
MediaGoblin――より現代的な代替案
ClipBucketに数ヶ月悩まされたあと、改めて選択肢を見直し、MediaGoblinを見つけた。

MediaGoblinはセルフホスト型のメディア共有プラットフォームだ。
MediaGoblinには気に入る点が多かった。見た目にも美しくないClipBucketのPHPとは違い、MediaGoblinは私が経験豊富なPythonで書かれていた。コマンドラインインターフェースも備えており、動画のアップロードを簡単に自動化できた。何より素晴らしかったのは、MediaGoblinがDockerイメージを提供していたことで、インストール時のあれこれで悩む必要がなくなるはずだったことだ。
MediaGoblinを再Docker化する際の意外な苦労
MediaGoblinのDockerイメージがあればデプロイは簡単だと思っていた。ところが、そうはいかなかった。
事前に用意されたイメージには、私が必要とする2つの機能が欠けていた。
- 認証
- MediaGoblinはデフォルトで公開設定のため、部外者がサイトにアクセスできないようにする必要があった。
- トランスコード
- 動画をアップロードするたびに、MediaGoblinは最適なストリーミングのために再エンコードを試みる。すでにストリーミングに適した形式の動画では、この処理は画質を劣化させ、計算リソースを無駄にするだけだ。
- MediaGoblinにはトランスコードをスキップする設定オプションがあるが、既存のDockerイメージでは設定を変更できなかった。
問題ない。Dockerイメージはオープンソースだったので、自分でビルドし直せばよかった。
ところが、Dockerイメージはすでに最新のMediaGoblinリポジトリではビルドできなくなっていた。最後にビルドが成功したバージョンに合わせてみても、やはり失敗した。まったく同じコードでビルドしているにもかかわらず、MediaGoblinの外部依存関係が知らないうちに変わってしまい、ビルドが壊れていたのだ。10分以上かかるMediaGoblinのビルドを何度も繰り返し、数十時間を費やした末、ようやく動くようになった。
数ヶ月後、同じことが再び起きた。ここ2年ほどの間にMediaGoblinの依存関係の入れ替わりでビルドが壊れたのは数回に及び、この記事を書いている最中にもまた一度壊れた。そこで私は最終的にMediaGoblinを独自にフォークし、すべての依存関係を明示的なバージョンに固定した。つまり、MediaGoblinがceleryのバージョン >= 3.0 ならどれでも動くといった曖昧な指定をやめ、実際に動作確認が取れているceleryの4.2.1リリースに固定したのだ。MediaGoblinには再現可能なビルドの仕組みが必要だと思うが、まだそこまでは手が回っていない。
いずれにせよ、何時間もの格闘の末、MediaGoblinをDocker内でビルドし、調整できる状態にまで持っていくことができた。そこからは、不要な動画のトランスコードをスキップしたり、認証のためにNginxを追加したりするのは簡単だった。
ステップ4:ホスティング
MediaGoblinがローカルマシン上のDockerで動くようになったので、次は家族が動画にアクセスできるよう、構成をクラウドサーバーにデプロイすることだった。
MediaGoblinと動画ストレージの問題
アプリケーションのDockerイメージを受け取り、公開URLでホストしてくれるプラットフォームは数多くある。厄介だったのは、MediaGoblin本体に加えて、共有すべき33GBの動画ファイルがあったことだ。これらをDockerイメージに直接埋め込むことも可能だったが、煩雑でスマートではない。設定ファイルを1行変更するだけで、33GBのデータを再デプロイしなければならなくなるからだ。
ClipBucketを使っていた頃は、gcsfuseというユーティリティでこの問題を解決していた。これはGoogle Cloud Storage上のディレクトリを通常のファイルシステムのパスとしてマウントできるツールだ。動画ファイルをGoogle Cloud Storageに置き、gcsfuseを使ってClipBucketからはローカルファイルのように見えるようにしていた。
違いは、ClipBucketがフル仮想マシンで動いていたのに対し、MediaGoblinはDockerコンテナで動いていたことだった。Docker環境でクラウドストレージをマウントすることは、はるかに複雑だとわかった。私は細かな落とし穴の解決に数十時間を費やし、その過程を丸ごと一つのブログ記事にまとめた。

Google Cloud StorageとMediaGoblinを統合するための初期アーキテクチャ。2018年のブログ記事で解説したもの
数週間かけてすべてのコンポーネントをうまく連携させるようにした結果、なんとか動くようになった。MediaGoblinのコードを一切変更することなく、メディアファイルをGoogle Cloud Storageに読み書きさせるようにごまかすことができたのだ。
唯一の問題は、これでMediaGoblinが使い物にならないほど遅くなったことだった。ホームページの動画サムネイルを読み込むのに丸20秒かかった。動画再生中にシークすると、再生が再開するまで10秒もの永遠とも思える時間がかかった。
根本的な原因は、動画や画像ファイルがユーザーに届くまでに長く回りくどい経路をたどっていたことだった。Google Cloud Storageからgcsfuse、MediaGoblin、Nginxを経由して、ようやくユーザーのブラウザに届くという流れだ。gcsfuseは速度が最適化されておらず、大きなボトルネックになっていた。プロジェクトのホームページでも、レイテンシの悪さについて次のようにはっきりと警告している。

gcsfuseのドキュメントにあるパフォーマンス低下に関する警告
理想的には、ブラウザがすべての中間層を迂回してGoogle Cloud Storageから直接ファイルを取得することだ。MediaGoblinのコードベースに深く手を入れ、Google Cloud Storageとの複雑な統合ロジックを追加することなく、どうすれば実現できるだろうか。
Nginxのsub_filterトリック
幸い、やや強引ではあるもののシンプルな解決策が見つかった。Nginxのdefault.confファイルに次のフィルターを追加したのだ。
sub_filter "/mgoblin_media/media_entries/" "https://storage.googleapis.com/MY-GCS-BUCKET/media_entries/";
sub_filter_once off;私の構成では、NginxがエンドユーザーとMediaGoblinの間のプロキシとして機能していた。上記のディレクティブは、MediaGoblinからのHTMLレスポンスをエンドユーザーに渡す前に、Nginxに検索と置換を実行させるものだ。NginxはMediaGoblin上のメディアファイルへの相対パスをすべて、Google Cloud StorageのURLに置き換える。
例えば、MediaGoblinは次のようなHTMLを生成する。
<video width="720" height="480" controls autoplay>
<source
src="/mgoblin_media/media_entries/16/Michael-riding-a-bike.mp4"
type="video/mp4"
/>
</video>Nginxはレスポンスを次のように書き換える。
<video width="720" height="480" controls autoplay>
<source
src="https://storage.googleapis.com/MY-GCS-BUCKET/media_entries/16/Michael-riding-a-bike.mp4"
type="video/mp4"
/>
</video>全体の構成は次のようになる。

NginxがMediaGoblinからのレスポンスを書き換えることで、クライアントはメディアファイルをGoogle Cloud Storageから直接取得できるようになる。
この解決策の優れた点は、MediaGoblinのコードを一切変更する必要がなかったことだ。たった2行のNginxディレクティブで、互いをまったく認識していないMediaGoblinとGoogle Cloud Storageをシームレスに統合できたのだ。
注記:この方法ではGoogle Cloud Storage上のファイルを誰でも読み取り可能にする必要がある。不正アクセスのリスクを軽減するため、私は長くランダムなバケット名(例:mediagoblin-39dpduhfz1wstbprmyk5ak29)を使用し、バケットのアクセス制御ポリシーで権限のないユーザーがディレクトリの内容を一覧できないようにしている。
完成した成果物
ここで、完全に動作するソリューションが完成した。MediaGoblinはGoogle Cloud Platform上の独自のコンテナで問題なく動作し、頻繁にパッチを当てたりアップグレードしたりする必要がなくなった。すべての工程が自動化され再現可能になったため、変更の反映や以前のバージョンへのロールバックも簡単だった。
家族は動画を簡単に閲覧できることをとても気に入ってくれた。Nginxによるパフォーマンス改善のおかげで、YouTubeを閲覧するのと同じくらいキビキビと動作した。
閲覧画面はこんな感じだった。

家族のホームビデオ共有サーバーの閲覧画面
サムネイルをクリックすると、次のような画面が表示された。

メディアサーバーで個別のクリップを視聴している様子
何年もの作業を経て、当初思い描いていたようなYouTubeのような体験を家族に提供できたことは、この上ない喜びだった。
おまけ:月額コストを1ドル未満に抑える
ホームビデオは数ヶ月に一度しか見ないものだ。家族全員合わせてもサイトへのアクセスは年間約20時間だったのに、サーバーは24時間365日稼働していた。99.7%の時間は何もせず待機しているだけのサーバーに、毎月15ドルを支払っていたことになる。
2018年末、GoogleはCloud Runをリリースした。最大の特徴は、HTTPリクエストに応答できるほど高速にDockerコンテナを起動できることだった。これにより、サーバーをスタンバイ状態で待機させ、誰かがURLにアクセスしたときだけ実行することが可能になった。私のアプリのようにアクセス頻度が低い場合、コストは月15ドルから年間数セントまで下がった。
今となっては理由を覚えていないが、Cloud Runは私のMediaGoblinイメージではうまく動かなかった。しかしCloud Runの存在が、Herokuが同様のサービスを無料で提供しており、そのツールがGoogleのものよりはるかに使いやすいことを思い出させてくれた。
無料のアプリケーションサーバーを使えば、唯一のコストはデータの保存料だけになる。Googleの標準的なリージョナルストレージは1GBあたり2.3セントで、動画コレクションは33GBなので、支払いは月額わずか0.77ドルで済む。

このソリューション全体のコストは月額わずか0.77ドルだ。
これから挑戦する人へのヒント
この作業には明らかに長い時間がかかったが、この記事が他の人がホームビデオをデジタル化して共有する際の手間の80〜90%を省く助けになれば幸いだ。次のセクションでは私のソリューションの詳細な手順を解説するが、まずはホームビデオをデジタル化して共有する際の一般的なヒントをいくつか紹介したい。
- 取り込みや編集の段階で、できるだけ多くのメタデータを記録する。
- テープのラベルには貴重な情報が書かれていることが多い。
- どのクリップがどのテープのどの順番から来たのかを記録しておく。
- クリップ内の撮影日に関する手がかりをメモしておく。
- 取り込み作業はプロに外注することも検討する。
- 個人で映像デジタル化業者の品質に匹敵するのは極めて困難で、コストもかかる。
- ただしEverPresentという会社は避けたほうがいい(詳細が知りたい場合はメールで連絡してほしい)。
- 自分で取り込む場合は、十分なディスク容量を用意する。
- 非圧縮のキャプチャでは、標準画質の動画で1分あたり約100〜200MBになる。
- 私はすべてを10TBのSynology DS412+に保存した。
- メタデータは特定のアプリケーションに依存しない形式で記録する。
- クリップの説明、タイムコード、日付など。
- アプリケーション固有の形式で保存したり(あるいは捨ててしまったり)すると、別のソリューションに切り替えた際に作業を再現できなくなる。
- 編集中に動画を見ていると、多くの有用なメタデータに気づく。それを記録しなければ失われてしまう。
- 動画では何が起きているか?
- 誰が映っているか?
- いつ収録されたものか?
- お気に入りに印をつける。
- 正直なところ、ホームビデオの映像のほとんどはかなり退屈だ。
- 私はお気に入りのクリップに「best of」タグを付け、楽しい動画が見たいときはそこから探すようにしている。
- エンドツーエンドのソリューションはできるだけ早く構築する。
- 私はまずすべてのテープを取り込み、次にすべてを編集する、といった進め方をした。
- 1本のテープから始めて、共有までに必要な作業を一通りやっておけばよかった。そうすれば、初期段階での判断が最終的な結果にどう影響するかがわかったはずだ。
- トランスコードは最小限に抑える。
- クリップを編集したり再エンコードしたりするたびに、画質は劣化する。
- 生素材は可能な限り最高の品質で取り込み、その後ブラウザがネイティブに再生できる形式へ、各クリップを一度だけトランスコードする。
- 動画クリップの共有には、可能な限りシンプルなソリューションを使う。
- モンタージュを作る。
- 動画のモンタージュは、複数のホームビデオから最高の瞬間を集める楽しい方法だ。
- モンタージュで重要なのは音楽だ。The Nationalの「Slow Show」はモンタージュにぴったりだが、まだ誰も気づいていないようだ。
私の作業工程のエンドツーエンドwalkthrough
この作業をどのように行ったのか、その詳細を知りたい方のために、最初から最後までのワークフロー全体を示したチュートリアルを用意した。私の工程を再現するためのすべてのソースコードとコマンドが含まれている。
イラスト:Loraine Yow
これらのクリップや静止画の一部を共有することを快諾し、そもそもすべてを記録してくれ、そしてこの長い過程を通じてずっと支えてくれた家族に心から感謝する。
記事をランダムに読む
コメント
ログインしてコメントする