rePlayer 内部構造: 字幕
Notice
この記事は機械翻訳されています。間違いがあるかもしれませんので、遠慮なく報告してください。
This article is machine translated. The English original is available here.
rePlayerは、Resonite用の私の動画プレーヤーで、その主な特徴はYouTubeから字幕を読み込む機能です。 現在のrePlayerのバージョンでは、特に音楽動画に役立つ高度な字幕にも対応しています。 この投稿では、この機能の歴史と各バージョンの動作について少し説明したいと思います。
バージョン1: 始まり
開発中に作成した字幕機能の最初のバージョンでは、字幕のサポートは主にシンプルな字幕に限定されていました。 開発を開始した際、DynamicSubtitleProviderという便利なコンポーネントが存在し、通常の字幕ファイルを読み込むことができたことを思い出しました。
要約すると、次のように機能していました:
- ユーザーがリストから字幕を選択します
- 動画IDとリクエストされた言語を含むURLがコンポーネントの
AssetUrl
に設定されます - サーバーはYouTubeからSRT形式の字幕ファイルをダウンロードし、太字、斜体、色のテキストフォーマットタグをResoniteで解析可能な形式に変換します
- サーバーは結果のファイルをゲームに送信します
- Resoniteは内部でSRTファイルを解析し、1つの「string」トラックを持つアニメーションに変換します
- アニメーターコンポーネントが字幕スロットのテキストを駆動し、ユーザーに表示します
しかし、このアプローチにはすぐに問題が発生しました:Resoniteでは何も神聖なものはなく、単に字幕をパースできないことがありました。 これは、YouTubeのSRTファイルの形式が不一致で、時には完全に破損していたためさらに悪化し、破損していなくてもResoniteがパースできないことがありました。
別の解決策を探し始めました… また、まだコードはほとんど書かれておらず、TypeScriptが本当にイライラさせるようになったので、この機会にRustで書き直すことにしました。 Rustで書き直すのは一種のネタみたいなものだと分かっていますが、本当にTypeScriptが全く好きではないのです。 :(
バージョン2: 謎の領域
この一連の出来事中、私はVlamsさんと話していました。 話している最中、突然アイデアが浮かびました:DynamicSubtitleProviderは字幕を「アニメーション」に変換するのですが、そのステップを省略して直接「アニメーション」に変換できないでしょうか?
AnimXという、Resoniteがアニメーション用に採用しているカスタムバイナリ形式を思い出しました。
結局、StaticAnimationProviderもURLからアセットを読み込むだけですから。
もし私のサーバーがURL経由でAnimXを直接配信できたらどうでしょうか?
このアイデアをVlamsさんに伝え、彼らはAnimXの書き込み(およびパース!)用のライブラリを作成してくれると申し出てくれました。
その時点では、VlamsさんはResoniteに一度も触れたことがなかったにもかかわらず、仕様書からAnimXライターを作成した最初の試作はすでにほぼ正しいものでした。
私がさらにテストを行った後、Vlamsさんはそれを機能させ、GitHubに公開しました:https://github.com/vlams1/resonite-core
VlamsさんがAnimXの書き込みを迅速に解決してくれたため、私は残りの必要な機能を実装しました。 全体の手順は前回のものとほぼ同様でしたが、SRTではなくWebVTTを使用し、ゲームにAnimXを送信する代わりに通常の字幕ファイルを送信しました。
その新しい方法では、次のように機能しました:
- ユーザーがリストから字幕を選択します
- 動画IDとリクエストされた言語を含むURLがStaticAnimationProviderの
AssetUrl
に設定されます - サーバーはYouTubeからWebVTT形式の字幕ファイルをダウンロードし、太字、斜体、色などのテキストフォーマットタグをResoniteで解析可能な形式に変換します
- (新機能!) サーバーは字幕のキューをAnimXキーフレームに変換し、テキストのないタイムスタンプには空のキューを挿入する場合もあります
- サーバーは結果のAnimXファイルをゲームに送信します
- Animatorコンポーネントが字幕スロットのテキストを駆動し、ユーザーに表示します
この方法には独自の課題がありましたが、今回は問題の原因はYouTube自体に起因するものでした。 SRTと同様に、YouTubeが提供するWebVTTファイルは時々破損しており、可能な限り対応する必要がありました。ほとんどのケースでは機能しましたが、満足のいく結果ではありませんでした。 また、画面に複数の字幕を同時に表示する動画では正常に動作しませんでした。 しかし、最も気になったのは、YouTubeの特殊機能(テキストの位置、フォントの変更など)への対応が不足していた点です…
そのため、再び別の方法を考案しました。
バージョン3: 秘密の要素は犯罪
YouTubeが字幕を従来の形式に変換する試みは常に問題を引き起こしていたため、YouTubeの独自の字幕形式を直接処理することにしました。 YouTubeには2つの内部字幕形式があり、そのどちらかを選択する必要がありました。SRV3とJSON3(この形式が何を基にしているか想像してみてください)です。 これらの両方は同じデータを格納していますが、名前が異なります(JSON3は適切な名前を使用しており、SRV3はデータを2文字の文字列で名前付けしています)。私はJSON3を選択しました。 これは私の人生で最も簡単な選択でした。なぜならSRV3はXMLベースであり、私は「選択肢がない場合を除いて」XMLには二度と触れないと誓ったからです。[https://github.com/AudiosurfResearch/Wavebreaker]
JSON3ファイルのパースは極めて簡単でした。なぜなら、単にJSONだからです。興味深い部分は、それをResoniteで使える形式に変換することでした。
その方法はすぐに分かりました。再びAnimXを使用しましたが、今回は複数のアニメーショントラックを使用しました。
私の悪巧みは次のような仕組みでした: テキスト用、垂直位置用、水平位置用、影の色用、輪郭の色用、装飾モード(なし、輪郭、影)用、背景の色用…など、いくつかのトラックを割り当てました。 さらに、メタデータとして機能するトラックが1つありました。このアニメーショントラックには、同時に表示される字幕トラックの最大数が保存されていました。パズルの最後のピースは、すべてを機能させるためのProtoFluxでした。
AnimXは、アニメーショントラックに一部のメタデータを設定できます。これらのメタデータはNodeに属し、Propertyを記述します。
ProtoFluxは、アニメーションノードの名前とプロパティの名前を使用して、FindAnimationTrackIndex関数でアニメーショントラックのインデックスを検索できます。
インデックスを使用することで、アニメーション トラックから値を取得できます。
まず、この機能を使用して、ノード「Metadata」のプロパティ「trackCount」を読み取ります。その値を使用して、サブタイトル トラック テンプレート Slot を複製します。すべてのサブタイトル トラック Slot は NestedCanvas の子要素であり、それぞれがすべての情報を表示する単一のサブタイトル トラックを表示します。 各字幕トラックには、生成されたアニメーションから必要な情報を読み取るProtoFluxが設定されています。 各字幕トラックはAnimXファイル内に独自のノードを持ち、各ノードには字幕を表示するために必要なすべての情報が含まれるプロパティがあります。 例えば、最初のトラックのノードはSubtitle0と呼ばれます。ProtoFluxは字幕トラックスロットのインデックスを使用して、どのノードから字幕データを取得するかを決定します。したがって、3番目のスロットの場合、Subtitle2をノードとして使用します。 プロパティ名は常に同じため、ノード名を取得すれば、アニメーションから残りのデータを容易に取得できます。このデータは、YouTubeの字幕の見た目をより忠実に再現するため、ProtoFluxで少し処理されることがあります。
各アニメーションノードで使用されるプロパティの完全なリストは以下の通りです:
- text
- hPos
- vPos
- bgColor
- anchorPoint
- textJustify
- printDir(テキストの回転用)
- edgeColor(影/輪郭の色用)
- edgeType(テキストに影または輪郭を付けるかどうか)
- shadowText(Resoniteの制限のため、テキストの影は別オブジェクトであり、メインテキストの色変更に影響されない別のテキスト値が必要です) その他の多くの機能も、Resoniteのテキストフォーマットタグを使用して処理されます。例えば、太字/斜体/下線やフォントの変更(これらはFontCollectionが必要になります)などです。
残りの部分は比較的簡単です。字幕トラックのSlotは、動画の現在の再生時間に応じてこれらのプロパティをすべてサンプリングします。
- ユーザーがリストから字幕を選択します
- 動画IDとリクエストされた言語を含むURLがStaticAnimationProviderの
AssetUrl
に設定されます - (新機能!) サーバーはYouTubeからJSON3形式の字幕ファイルをダウンロードし、太字、斜体、色のテキストフォーマットタグをResoniteで解析可能な形式に変換します
- (新機能!) サーバーは字幕を正しく表示するために必要な同時再生可能な字幕トラックの数をチェックします
- (新機能!) サーバーは字幕のキューをAnimXキーフレームに変換し、テキストのないタイムスタンプには空のキューを挿入する場合があり、その他のプロパティ用に追加の字幕トラックを作成します
- サーバーは結果のAnimXファイルをゲームに送信します
- (新機能!) ProtoFluxは必要な字幕トラックの数を読み取り(同時に複数の字幕を表示する場合のみ複数のトラックが必要)、必要に応じて字幕トラックテンプレートスロットを複製します
- (新機能!) 各字幕トラックスロットに接続されたProtoFluxは、表示内容を制御するテキストやその他の値を駆動します
この方法により、YouTubeの字幕を、高度な機能を使用している場合でも、画面に複数の字幕が同時に表示されている場合でも表示できるようになりました!他の方法よりも複雑ですが、より正確で、はるかに信頼性が高いです。 これは多くの時間と労力を要しましたが、確実に価値があったと思います。
終わり
読んでいただきありがとうございました!rePlayerの内部についてこの小さな記事を楽しんでいただけたなら幸いです。他に興味深いトピックがあれば、ぜひ教えてください! そして、rePlayerをぜひ試してみてください! :)