2022-04-04 字幕システムの振り返り
This page is available in English: English
このページは AI(Claude)によって翻訳されています。翻訳に誤りが含まれる可能性があります。
私のワールド「Offline」用に字幕システムを作りました。もともとは、YouTube の英語字幕を使って日本語の映画を観るために作ったものです。
字幕システムはすでにいくつか存在しますが、私が知っているものは映画のリストを前提に設計されています。このシステムは、任意の動画に対して、ユーザーが字幕を貼り付けられるようにすることに特化しています。
SubtitleManager.cs - Shared to twitter
Instantly share code, notes, and snippets. SubtitleManager.cs - Shared to twitter You can't perform that action at this time. You signed in with another tab or window. You signed out in another tab or window. Reload to refresh your session. Reload to refresh your session.
別のユーザーが、この実装をもとにさらに発展させてくれました。
https://twitter.com/loljacklul/status/1510610024925741065
ここでは、この字幕プレハブを作る中での経験を振り返ります。
字幕のフォーマット
幅広くサポートされているように見えたので、字幕フォーマットには SRT(SubRip)を採用しました。簡単にパースでき、レイアウトに関する懸念もほとんどありません。
字幕は通常、表示される時刻順に並んでいます。これは私の Udon 実装における前提にもなっていて、パースした順序が表示順であると仮定しています。
このフォーマットの唯一の変わった点は、2 つのタイムスタンプの範囲が重なった場合にどうすべきかが、きちんと規定されていないことです( https://www.youtube.com/watch?v=FqJ21-GaYew )。
私が採った方法は、2 つの字幕が重なった場合に、複数の字幕テキストを改行で区切って表示するというものです。表示順は常にタイムスタンプ順で、「最後に表示された順」ではありません。
さらに、字幕 A と重なっている字幕 B が、A より後に始まって A より先に終わる場合、A が字幕 C と再び重なることはないと仮定しています。
コピー&ペースト
この記事は 2022 年 4 月、つまりネイティブの String Loader が導入される半年前に書かれました。
今日では String Loader を使うほうが、おそらくより良い解決策ですが、このセクションには他の用途にも関係する情報が含まれているかもしれません。
私が作っているのは動画プレイヤーのワールドであり、映画のワールドではないので、あらかじめ決められた映画のリストはありません。字幕を事前に読み込むことはできず、ユーザーは好きな SRT 字幕を自由に使えます。
映画やドキュメンタリーの中には、字幕が非常に大きく、ファイルサイズが 300KB を超えるものがあります。しかし、Unity の Text フィールドは約 16K 文字までしかサポートしておらず、コピー&ペーストが失敗してしまいます。
これを回避するために、この制限のない TextMeshPro の Text フィールドを使い、ユーザーが途方もない量のデータを貼り付けられるようにしています。これにはいくつかの問題があります。
Udon からアクセスできない
TextMeshPro のテキストには Udon からアクセスできません。テキストをコピーするには、Unity イベントを配線して、Unity UI の Text オブジェクトの Text 要素に設定する必要がありました。そうすれば、Udon から UI Text にアクセスできます。
テキストを表示すると、貼り付け中も貼り付け後もラグが発生する
大量のテキストを貼り付けると、2 つの問題が起こります。
- 貼り付けた直後に大きなヒッチが発生し、数秒待つしかありません。
- 字幕テキストがフィールドに表示されている間は、パフォーマンスが大幅に低下します。日本語の 300KB の字幕ファイルでテストしたとき、この低下は顕著でした。
パフォーマンスを取り戻すには、送信した直後にテキストフィールドをクリアする必要があります。しかし、私自身はフィールドを簡単にクリアする方法を見つけられませんでした。もしかすると方法はあるのかもしれませんが、私にはわかりません。私が採った方法は、非表示のテンプレートとして置いてある Text フィールドの階層を複製(VRC Instantiate)し、それまで使っていた Text フィールドの階層を破棄することです。
同期
この記事は 2022 年 4 月、つまりネイティブの String Loader が導入される半年前に書かれました。
今日では String Loader を使うほうが、おそらくより良い解決策ですが、このセクションには他の用途にも関係する情報が含まれているかもしれません。
ローカルユーザーが、同じ言語設定を持つインスタンス内の別のユーザーが貼り付けた字幕の恩恵を受けたい場合は、同期が必要です。
得られたアドバイスによると、およそ 60KB を超えるものをシリアライズしようとすると失敗することがあります。
これを回避するために、文字列を任意の文字数(8192 文字)で分割し、1 つずつ送信しています。文字列同期にかかるネットワーク上の制約を詳しく知らないため、文字数でのサイズは、バイト数の上限よりも意図的にかなり低く設定しています。
問題の規模を考えると、この数値を少し増やしたり減らしたりしても、同期の伝送にはそれほど大きな差は出ません。これは長い映画を観るためのものであり、字幕が毎分変わることはなく、プレイヤーもインスタンスに長く滞在することが想定されるので、同期速度は問題になりません。同じ理由で、文字列の圧縮も行っていません。この文脈では、それは作り込みすぎになるからです。
追跡
表示するテキストを送る前に、動画プレイヤーの現在位置を追跡しています。
通常の動作中、字幕マネージャーは最後に表示した字幕のインデックスを記憶しています。その字幕と、(重なっているものがあれば)これから表示される他の字幕を表示し、完了したら次へ進みます。これにより、毎フレーム配列全体を走査せずに済みます。
動画プレイヤーが任意の地点まで巻き戻されたときは、字幕マネージャーが、配列のインデックス 0 から、現在時刻に一致する最初のタイムスタンプまで、すべての字幕のタイムスタンプを読み取ります。
表示
字幕を表示するために、TextMeshPro の Text の 2 つのテクニックを利用しています。
- 背景としての Mark( http://digitalnativestudios.com/textmeshpro/docs/rich-text/#mark )
Mark を使うと、テキストに重ねて表示できます。通常は縁取りに使われますが、ここでは字幕の背景として使いたいのです。テキストをただ縁取るだけでは、テキストが暗くなってしまいます。
これを実現するために、通常のテキスト用と、背景の表示専用の 2 つの Text オブジェクトを用意しています。
背景用の Text には$"<mark=#000000>{buildingText}</mark>"を渡しています。
- 黒い縁取りのための Outline( http://digitalnativestudios.com/textmeshpro/docs/shaders/ )
テキストの視認性を高めるために、既存の背景の上に、字幕へ黒い縁取りを追加しました。これは、マテリアルの Outline 設定を変更することで行っています。
ディスレクシア(読字障害)
上記の設定は、ディスレクシアのある方にもうまく合っているようです。
https://twitter.com/exe_elf/status/1496023742048313344
https://twitter.com/exe_elf/status/1496060055992639489
thank you! also thank you for making this yellow, that always makes it a bit easier to read subtitles with dyslexia for me
For me personally, rounder open fonts, and bright colors (like yellow!) work wonders, tysm 4 the consideration
ディスレクシアのユーザーにとってより識別しやすいとされるカスタムフォントを選べるオプションも追加しました。現在のフォントでも問題ないようではありますが。
攻撃のベクトル
この字幕システムは、信頼できる環境で友人たちとくつろぐために使うことを想定しているため、攻撃への耐性を考慮した設計にはなっていません。
公開された環境では、悪意のある攻撃者が、悪意ある字幕ファイルを使って、大量のテキストを表示させてパフォーマンスを低下させようとしたり、問題のあるリッチテキストを字幕に使ったり、大量のテキスト切り替えを発生させたり、何らかの方法でネットワークを詰まらせてオブジェクトの所有権の問題を引き起こしたりする可能性があります。
このシステムが攻撃のベクトルとして使われるのを減らすために、さまざまな健全性チェックを導入する必要があるかもしれません。Udon のチャットボックスなど、ユーザー入力を受け付けるあらゆるシステムについても同じことが言えます。