代表的な面接トピック

フロントエンド面接:WebCodecs を用いたブラウザ動画パイプラインをどのように設計しますか?

フロントエンド難しい
Offer.cc 編集チーム公開日 更新日

質問

ブラウザベースのリアルタイム動画プレビュー、フィルター処理、アップロードを構築する場合、バックプレッシャー、タイムスタンプ、ブラウザの機能差に対処しながら、キャプチャ、デコード、処理、エンコード、多重化(muxing)に WebCodecs をどのように活用しますか?

質問と背景

ブラウザベースのリアルタイム動画プレビュー、フィルター処理、アップロードを構築する場合、バックプレッシャー、タイムスタンプ、ブラウザの機能差に対処しながら、キャプチャ、デコード、処理、エンコード、多重化(muxing)に WebCodecs をどのように活用しますか?

この質問は、フロントエンド、ブラウザ、リアルタイムメディア、マルチメディア関連の職種に適しています。目的は API 名の暗記ではなく、データフローとリソース境界の理解度を問うことです。WebCodecs は未加工フレーム、エンコード済みチャンク、エンコーダー/デコーダーインターフェースを公開しますが、エンコードされたデータを自動的に再生可能ファイルへとパッケージングしたり、すべてのブラウザですべてのコーデックを保証したりするわけではありません。

面接官が見ているポイント

  • VideoFrameEncodedVideoChunk、コンテナパッケージングの違いを区別できているか?
  • キャプチャ、処理、エンコード、転送を有界キュー(上限のあるキュー)に分割しているか?
  • configureencodeflushresetclose のライフサイクルを理解しているか?
  • Worker、VideoFrame.close()、キューの深さを用いて、メインスレッドの処理とメモリを制御しているか?
  • タイムスタンプ、キーフレーム、ドロップフレーム、音声と映像の同期を適切に処理しているか?
  • ブラウザ間の差異に対して isConfigSupported()、機能マトリクス、フォールバックを使用しているか?

30秒で答える要約

「キャプチャ、デコード、フレーム処理、エンコード、多重化(mux)、アップロードにパイプラインを分割します。未加工フレームとエンコード済みチャンクはメディアタイムスタンプを保持し、すべてのキューに上限を設定します。生成速度がエンコードやネットワークの消費速度を上回る場合は、キーフレームとメトリクスを維持しつつ、再構築可能な中間フレームをドロップします。フレームごとの重い処理は Dedicated Worker で実行し、消費された VideoFrame オブジェクトは速やかにクローズします。構成を選択する前に機能を検証し、必要に応じて MediaRecorder やサーバー処理へとフォールバックします。アーカイブのアップロード前にチャンクを多重化し、レイテンシ、キュー深度、ドロップ数、エンコーダーエラーを監視します。」

ステップごとの詳細な回答

ステップ 1:データ型と境界を定義する

キャプチャ段階では MediaStreamTrack からフレームを読み取ることができ、デコード処理はエンコード済みチャンクを VideoFrame に変換します。フィルターやスケーリングはフレームを消費して新しいフレームを生成し、エンコード処理はフレームを EncodedVideoChunk に変換します。これらは異なるデータ型であり、エンコード済みチャンク単体では再生可能なファイルにはなりません。

多重化(Muxing)は、チャンク、タイムスタンプ、トラックメタデータを MP4 や WebM などの形式に書き込みます。ライブ転送では、設定メタデータとともにチャンクを送信するだけで十分な場合があります。ダウンロードや再生用には、マルチプレクサ(muxer)を追加して生成されたタイムラインを検証します。

ステップ 2:処理を可観測なパイプラインに配置する

各ステージでキューの長さ、待機時間、ドロップ数を記録します。制御、プレビュー、ユーザー操作はメインスレッドに残し、フレームごとの処理は Dedicated Worker に移行します。グラフィックスメモリが無期限に保持されないよう、フレームオブジェクトを意図的に転送またはクローズします。

フィルター処理で無制限にフレームをコピーしてはなりません。処理後の所有権を明確に定義します。フレームを受け取ったエンコーダーまたはレンダラーがそれをクローズし、例外パスでも確実に解放します。キューが満杯になった場合、メモリ肥大化によってリアルタイム処理の破綻を隠すのではなく、未エンコードの非キーフレームから優先的にドロップします。

ステップ 3:エンコーダーとそのライフサイクルを設定する

VideoEncoder を作成する前に、コーデック、解像度、フレームレート、ビットレート、ハードウェアオプションのサポート状況を検証します。encode() の後に configure() を順序通りに呼び出します。入力が終了したら flush() を呼び出して投入済み処理の完了を待ち、再構成や復旧には reset() を、パイプライン終了時には close() を使用します。

出力コールバックでは、無制限の同期処理を行うのではなく、次のステージへチャンクを渡すだけに留めるべきです。設定エラーやエンコードエラーが発生した場合、あるいは出力キューが深すぎる場合は、障害を拡大させないようフレームの投入を停止し、フォールバックや再起動に移行します。

ステップ 4:バックプレッシャー、ドロップ、レイテンシを設計する

ライブプレビューは通常「リアルタイム性(鮮度)」を重視し、アーカイブのアップロードは「完全性」を重視します。それぞれに異なるポリシーを適用します。プレビュー用には直近のウィンドウのみを保持し、アーカイブ用にはキューが高水位標(high watermark)に達した際にキャプチャを一時停止するかフレームレートを下げます。タイマーによる負荷予測に頼るのではなく、低水位標(low watermark)で処理を再開します。

フレームをドロップする際は、タイムスタンプの連続性とキーフレームの境界を維持します。デコードの復旧にキーフレームが必要な場合は、次のリカバリポイントでキーフレームを要求します。転送側では、エンコードキューの深さ、送信キューの深さ、ACK 遅延を記録し、エンコード、ネットワーク、レンダリングの各ボトルネックを分離して把握します。

ステップ 5:タイムスタンプと同期を処理する

フレームとチャンクには単一のメディア時間基準(time base)を使用します。到着時刻でメディアタイムスタンプを上書きしてはなりません。フィルターによって処理コストが変動しても、意図された表示時刻(presentation time)は変わりません。リサンプリング、ドロップ、速度変更を行う場合は、明示的にタイムラインを更新する必要があります。

音声に対しても同一のルールを適用し、マスタークロックに対するドリフトを補正します。プレーヤーやアップローダーがタイムスタンプの逆行、異常なギャップ、トラック終了時刻の不一致を検出した場合は、無言で結合するのではなく、そのセグメントを無効としてマークします。

ステップ 6:機能の検証、グレースフルデグラデーション、可観測性の確保

機能の検証は、実装が特定の構成をサポートしているかどうかを示すだけであり、継続的なエンコードが目標フレームレートを満たせることを証明するものではありません。エンコード時間、出力間隔、エラーの種類、キュー深度、デバイス情報を継続的に記録し、短時間のワークロードを実行して実際のハードウェアアクセラレーションを確認します。

ブラウザに対象のコーデックや Worker パスがない場合は、MediaRecorder へのフォールバック、解像度やフレームレートの引き下げ、またはサーバー処理用に復元可能な未加工セグメントのアップロードを行います。ユーザーにとって状態がわかりやすいように保ち、処理が中断されても復元できるように元のメディアを保持します。

情報の価値と境界

重要なポイントは、「ブラウザで動画エンコードができる」という事実を、制限が効いており復元可能なストリームへと昇華させることです。WebCodecs はフレームとチャンクのインターフェースを提供し、Worker はメインスレッドの競合を減らし、バックプレッシャーとタイムスタンプはリアルタイム性を保ち、多重化と機能チェックによって出力を再生可能かつデグレード可能なものにします。

これは、ブラウザが自動的に多重化を実行したり、ブラウザ間でコーデックを統一したり、無制限のスループットを提供したりすることを意味するわけではありません。プロダクション設計では、プライバシー、カメラのパーミッション、端末の発熱、メモリ制限、アップロード中断、サーバー互換性などを引き続き評価する必要があります。

質の高い模範解答

「まず、ドロップと完全性のトレードオフが異なるため、目的がライブプレビュー、ライブ転送、ダウンロード用アーカイブのいずれであるかを明確にします。キャプチャされたメディアはデコード、フレーム処理、エンコード、多重化、アップロードの順に流れ、VideoFrameEncodedVideoChunk、コンテナファイルは別個のモデルとして維持します。

フレームごとの処理は Dedicated Worker で実行します。各キューには高低の水位標を設定し、キュー深度、待機時間、ドロップメトリクスを監視します。プレビューでは最新のフレームを保持し、アーカイブでは高水位標でキャプチャを一時停止するかフレームレートを落とし、再開時はキーフレームから開始します。共有メディアクロックにより音声と映像の同期を保ちます。

開始前にコーデック、解像度、フレームレート、ビットレート、ハードウェアオプションを検証します。実行時はエンコード時間、出力間隔、エラー、端末リソースを監視します。flush で投入済み処理を待機し、reset で再構成し、close でリソースを解放します。出力チャンクは対象コンテナ用のマルチプレクサを経由させ、そのまま直接再生可能であるとは決して見なしません。

機能やパフォーマンスが不足している場合は、解像度やフレームレートを落とすか、MediaRecorder に切り替えるか、あるいは復元可能なソースセグメントを保持しつつサーバー側処理へ移行します。これにより、正確性、リアルタイム性、リソースの安全性、ブラウザのフォールバックが網羅されます。」

よくある間違い

  • EncodedVideoChunk をファイルとして扱う → コンテナトラックやパッケージングメタデータが欠落している → マルチプレクサを追加し、アーカイブ用のタイムラインを検証する。
  • isConfigSupported() の成功だけでサポートを断定する → 持続的な負荷によってフレーム落ちや失敗が発生する可能性がある → 短時間の負荷テストとランタイムメトリクスで確認する。
  • 無制限のキューを使用する → エンコード遅延によって無制限のメモリ増大が発生する → 高低の水位標と明示的なドロップポリシーを適用する。
  • VideoFrame をクローズしない → グラフィックスメモリの解放が遅れる → 成功パス・失敗パスの両方で所有権に基づいてクローズする。
  • 到着時刻でタイムスタンプを書き換える → ジッターが音声と映像のズレ(ドリフト)になる → メディアクロックを維持し、補正内容を記録する。
  • 単一のブラウザのみでテストする → コーデック、ハードウェア、Worker の挙動が異なる → 機能マトリクスとフォールバックチェーンを維持する。

関連する質問と回答

エンコード済みチャンクを MP4 として直接書き込めないのはなぜですか?

チャンクにはコーデックデータとタイミング情報しか含まれておらず、MP4 にはトラック、サンプルテーブル、コンテナメタデータも必要となるためです。互換性のあるマルチプレクサを使用し、タイムスタンプ順にチャンクを書き込んで再生をテストしてください。

キューが満杯になったときに非キーフレームから優先してドロップするのはなぜですか?

キーフレームは後続のデコードにおける復旧ポイントとなるためです。通常のフレームをドロップすることでレイテンシを低減しつつ、キーフレームを保持することでデコーダーがコンテキストを再構築できます。アーカイブモードでは、必要なデータを暗黙的に失うのではなく、一時停止や品質低下で対処すべきです。

ハードウェアアクセラレーションが有効であることをどのように証明しますか?

同一構成下でエンコード時間、CPU 使用率、出力フレーム間隔、端末温度を比較し、経時的なエラーやドロップを監視します。設定フィールドはリクエストや希望値に過ぎず、ランタイムの実測値ではありません。

どのような場合に処理をサーバーへ移行すべきですか?

端末の処理能力が不足している場合、ブラウザに対象コーデックがない場合、一貫したトランスコードが求められる場合、あるいはソースメディアを端末上に残せない場合に移行します。復元可能なセグメントをアップロードし、処理境界とリトライ境界を明確に定義してください。

公開情報ソース

関連する質問