Streams を用いた MediaStreamTrack の挿入可能なメディア処理

W3C 作業草案,

この文書の詳細
このバージョン:
https://www.w3.org/TR/2026/WD-mediacapture-transform-20260416/
最新公開バージョン:
https://www.w3.org/TR/mediacapture-transform/
編集者草案:
https://w3c.github.io/mediacapture-transform/
以前のバージョン:
履歴:
https://www.w3.org/standards/history/mediacapture-transform/
フィードバック:
public-webrtc@w3.org 件名行 “[mediacapture-transform] … メッセージのトピック …” で送信 (アーカイブ)
GitHub
編集者:
(Google)
(Google)

概要

この API は、生データを運ぶ MediaStreamTrackの ビットを操作するための API サーフェスを定義する。

この文書のステータス

この節は、この文書の公開時点におけるステータスを説明する。 現行の W3C 公開物と、この技術報告の最新版の一覧は、 W3C 技術報告 インデックスで確認できる。

この文書は、 Web Real-Time Communications Working Group により、勧告 トラックを用いた作業草案として公開された。この文書は W3C 勧告になることを意図している。

この文書に関するコメントを送る場合は、 public-webrtc@w3.org まで送信すること (購読, アーカイブ)。 電子メールを送信する際は、 件名に “mediacapture-transform” というテキストを含めること。 できれば次のようにすること: “[mediacapture-transform] …コメントの要約…”。 すべてのコメントを歓迎する。

作業草案としての公開は、 W3C およびその会員による承認を意味しない。これは草案文書であり、 いつでも更新、置換、または 他の文書により廃止される可能性がある。進行中の作業以外としてこの 文書を引用するのは不適切である。

この文書は、 W3C 特許ポリシーの下で運営されるグループによって作成された。 W3C は、グループの成果物に関連して行われた 特許開示の公開一覧 を管理している。 そのページには、特許を開示するための手順も含まれている。 個人が、必須クレーム を含むと個人が考える特許について実際の知識を有する場合、その個人は W3C 特許ポリシーの第 6 節に従って情報を開示しなければならない。

この文書は、2025年8月18日の W3C Process Document によって管理される。

1. 序論

[WEBRTC-NV-USE-CASES] 文書は、 メディアへのアクセスによってのみ実現できるいくつかの機能 (要件 N20-N22) を記述しており、これには以下が含まれるが、それらに限定されない:

これらのユースケースではさらに、処理を worker スレッドで行えることも必要になる (要件 N23-N24)。

この仕様は、[WEBCODECS] および [STREAMS] に基づくインターフェイスを提供し、 そのような機能へのアクセスを可能にする。

この仕様は生メディアへのアクセスを提供する。 これはカメラ、マイク、画面キャプチャ、 またはコーデックのデコーダー部などのメディアソースの出力であり、 コーデックのデコーダー部への入力でもある。処理されたメディアは、 HTML <video> タグ、RTCPeerConnection、canvas、MediaRecorder を含む、 MediaStreamTrack を受け取れる任意の宛先で消費できる。

この仕様は、以下のユースケースを明示的にサポートすることを目的とする:

注記: 音声の ユースケースをサポートすべきかどうかについては、WG の合意はない。

注記: WG は、Streams 仕様が 現在の Streams 仕様に関するいくつかの問題を解決するために、 関連する explainer で概説されている解決策を採用することを期待している。

2. 仕様

この仕様は、[MEDIACAPTURE-STREAMS] の IDL 拡張を示す。 MediaStreamTrack インターフェイスを継承し、 MediaStreamTrack から構築できる いくつかの新しいオブジェクトを定義する。

この API は 2 つの要素から成る。1 つは、トラックからの符号化されていないメディアフレームを ReadableStream に公開できるトラックシンクである。 もう 1 つはその逆であり、メディアフレームを入力として受け取る トラックソースを提供する。

2.1. MediaStreamTrackProcessor

MediaStreamTrackProcessor は、 与えられた MediaStreamTrack を流れるメディアを公開できる ReadableStream の作成を可能にする。 MediaStreamTrack が動画トラックである場合、 ストリームによって公開されるチャンクは VideoFrame オブジェクトになる。

これにより MediaStreamTrackProcessor は、 MediaStream モデルにおける実質的なシンクとなる。

MediaStreamTrackProcessor は内部に循環キューを含み、 接続先のトラックによって配信される入力メディアフレームを バッファリングできる。このバッファリングにより、MediaStreamTrackProcessor は、 関連付けられた ReadableStream から読み取られるのを待つフレームを 一時的に保持できる。 アプリケーションは、MediaStreamTrackProcessor コンストラクターで提供されるパラメーターを通じて、キューの最大サイズに影響を与えることができる。 ただし、キューの最大サイズは UA によって決定され、動的に変更される可能性があるが、 アプリケーションが要求したサイズを超えることはない。 アプリケーションが最大サイズパラメーターを提供しない場合、UA は キューの最大サイズを自由に決定できる。

新しいフレームが MediaStreamTrackProcessor に到着したとき、 キューが最大サイズに達している場合、 最も古いフレームがキューから削除され、新しいフレームが キューに追加される。これは、最大サイズが 1 のキューという特定の場合に、 キュー済みのフレームがあるなら、それは常に 最新のフレームになることを意味する。

UA はまた、いつでも任意のフレームをキューから自由に削除できる。UA は、 リソースを節約するため、または特定の状況で性能を向上させるために、 フレームを削除してもよい。この場合、[[numDiscardedFrames]] は それに応じてインクリメントされなければならない。いずれの場合も、破棄されないフレームは、 ReadableStream に対し、 MediaStreamTrackProcessor に到着した順序で利用可能にされなければならない。

MediaStreamTrackProcessor は、 ストリームに対して読み取り要求が発行された場合にのみ、 関連付けられた ReadableStream にフレームを利用可能にする。その考え方は、ストリームの内部バッファリングを避けることであり、 その内部バッファリングでは UA がバッファリング方針を選択する十分な柔軟性を持てない。

作者には、出力 VideoFrames が不要になったら直ちに close() を呼び出すことが推奨される。 基盤となるメディアリソースは、MediaStreamTrack の ソースによって所有される。 それらを解放しないこと (またはガベージコレクションを待つこと) は、ソースが新しい VideoFrames の発行を停止する原因になり得る。

2.1.1. インターフェイス定義

[Exposed=(Window,DedicatedWorker), Transferable]
interface MediaStreamTrackHandle {
   constructor(MediaStreamTrack track);
};

[Exposed=DedicatedWorker]
interface MediaStreamTrackProcessor {
    constructor(MediaStreamTrackProcessorInit init);
    readonly attribute ReadableStream readable;
    readonly attribute unsigned long long discardedFrames;
    readonly attribute unsigned long long totalFrames;
};

typedef (MediaStreamTrack or MediaStreamTrackHandle) MediaStreamTrackOrHandle;
dictionary MediaStreamTrackProcessorInit {
  required MediaStreamTrackOrHandle track;
  [EnforceRange] unsigned short maxBufferSize;
};

注記: このインターフェイスを DedicatedWorker に公開すべきであるという WG の合意がある。 このインターフェイスを Window に公開すべきでないかどうかについては、WG の合意はない。

注記: kind が "video" である MediaStreamTrack から MediaStreamTrackProcessor を作成する機能は存在すべきであるという WG の合意がある。 kind が "audio" である MediaStreamTrack から MediaStreamTrackProcessor を作成する機能を サポートすべきかどうかについては、WG の合意はない。

2.1.2. 内部スロット

[[track]]
その生データが MediaStreamTrackProcessor によって公開されるトラック。
[[maxBufferSize]]
アプリケーションによって指定された、MediaStreamTrackProcessor がバッファリングするメディアフレームの最大数。 アプリケーションがこれを提供しない場合、値を持たないことがある。 最小の有効値は 1 である。
[[queue]]
アプリケーションによってまだ読み取られていないメディアフレームをバッファリングするために使用される キュー
[[numPendingReads]]
アプリケーションによって発行され、まだ処理されていない読み取り要求の数を表す値を持つ整数。
[[isClosed]]
MediaStreamTrackProcessor が閉じられているかどうかを示す値を持つ真偽値。
[[numDiscardedFrames]]
この MediaStreamTrackProcessor によって破棄されたフレーム数。
[[numTotalFrames]]
破棄されたフレームを含め、この MediaStreamTrackProcessor が受信したフレーム数。

2.1.3. コンストラクター

MediaStreamTrackProcessor(init)
  1. init.track が有効でない MediaStreamTrack である場合、 TypeError を投げる。

  2. init.trackMediaStreamTrackHandle である場合、 以下のサブステップを実行する:

    1. init.track.[[transferable]] が false の場合、TypeError を投げる。

    2. init.track.[[track]] が有効でない MediaStreamTrack を参照している場合、 TypeError を投げる。

  3. maxBufferSize を 1 とする。

  4. init.maxBufferSize が 1 より大きい整数値を持つ場合、以下のサブステップを実行する:

    1. maxBufferSizeinit.maxBufferSize に設定する。

    2. ユーザーエージェントは maxBufferSize をより低い値に丸めてもよいが、 1 より低くしてはならない。

      maxBufferSize を丸めることは、たとえばカメラのように、 任意の時点で限られた数の VideoFrames しか使用できない場合があるソースにとって有用である。

  5. processor を新しい MediaStreamTrackProcessor オブジェクトとする。

  6. processor.[[track]]init.track に設定する。

  7. processor.[[maxBufferSize]]maxBufferSize に設定する。

  8. processor.[[queue]] を空の キューに設定する。

  9. processor.[[numPendingReads]] を 0 に設定する。

  10. processor.[[isClosed]] を false に設定する。

  11. processor.[[numDiscardedFrames]] を 0 に設定する。

  12. processor.[[numTotalFrames]] を 0 に設定する。

  13. processor を返す。

2.1.4. 属性

readable, 型は ReadableStream, readonly
[[track]] 内部スロットに格納された MediaStreamTrack または MediaStreamTrackHandle によって配信されるフレームの読み取りを可能にする。 この属性は、以下のステップに従って、最初に呼び出されたときに作成される:
  1. this.readable を新しい ReadableStream になるよう初期化する。

  2. Set up this.readable with its pullAlgorithm set to processorPull with this as parameter, cancelAlgorithm set to processorCancel with this as parameter, and highWaterMark set to 0.

processorPull アルゴリズムは、processor を入力として与えられる。これは以下のステップで定義される:

  1. processor.[[numPendingReads]] の値を 1 増やす。

  2. processor をパラメーターとして maybeReadFrame アルゴリズムを実行する タスクをキューに入れる

  3. undefined で 解決済みの promise を返す。

maybeReadFrame アルゴリズムは、processor を入力として与えられる。 これは以下のステップで定義される:

  1. processor.[[queue]]である場合、 これらのステップを中止する。

  2. processor.[[numPendingReads]] が 0 に等しい場合、これらのステップを中止する。

  3. frame を、 processor.[[queue]] からフレームメディアデータを デキューした結果とする。

  4. frameprocessor.readableエンキューする。

  5. processor.[[numPendingReads]] を 1 減らす。

  6. ステップ 1 に戻る。

processorCancel アルゴリズムは、processor を入力として与えられる。 これは以下のステップを実行することで定義される:

  1. processor をパラメーターとして processorClose アルゴリズムを実行する。

  2. undefined で 解決済みの promise を返す。

processorClose アルゴリズムは、processor を入力として与えられる。 これは以下のステップを実行することで定義される:

  1. processor.[[isClosed]] が true の場合、これらのステップを中止する。

  2. processorprocessor.[[track]] から切断する。 これを行う仕組みは UA 固有であり、その結果として processorprocessor.[[track]]、またはそれによって参照される MediaStreamTrack のシンクではなくなる。

  3. processor.[[track]] を null に設定する。

  4. processor.readable.[[controller]]閉じる

  5. processor.[[queue]]空にする

  6. processor.[[isClosed]] を true に設定する。

discardedFrames, 型は unsigned long long, readonly
processor.[[numDiscardedFrames]] の値を返す。
totalFrames, 型は unsigned long long, readonly
processor.[[numTotalFrames]] の値を返す。

2.1.5. トラックとの相互作用の処理

MediaStreamTrackProcessor processor[[track]]processor にフレームを配信するとき、 UA は processor をパラメーターとして handleNewFrame アルゴリズムを実行しなければならない。

handleNewFrame アルゴリズムは、processor を入力として与えられる。 これは以下のステップを実行することで定義される:

  1. processor.[[queue]]processor.[[maxBufferSize]] 個の要素を持つ場合、以下のステップを実行する:

    1. droppedFrame を、processor.[[queue]]デキューした結果とする。

    2. droppedFrame について、 Close VideoFrame アルゴリズムを実行する。

    3. processor.[[numDiscardedFrames]] を 1 増やす。

  2. processor.[[numTotalFrames]] を 1 増やす。

  3. 新しいフレームメディアデータを processor.[[queue]]エンキューする。

  4. processor をパラメーターとして maybeReadFrame アルゴリズムを実行する タスクをキューに入れる

いつでも、UA は processor.[[queue]] から任意のフレームを 削除してもよい。 UA は、たとえばリソース枯渇を防止するため、または特定の状況で性能を向上させるために、 processor.[[queue]] からフレームを削除すると決定してもよい。 この場合、processor.[[numDiscardedFrames]] は それに応じてインクリメントされなければならない。

アプリケーションは、フレームのタイムスタンプに隙間があることに気付くこと、 または discardedFrames を見ることで、フレームがドロップされたことを検出できる。

MediaStreamTrackProcessor processor[[track]]終了するとき、 processorClose アルゴリズムは processor をパラメーターとして 実行されなければならない。

2.1.6. MediaStreamTrackHandle

MediaStreamTrackHandle インターフェイスは、MediaStreamTrackProcessor の DedicatedWorkerGlobalScope を、別のコンテキストに存在する MediaStreamTrack へのシンクにするのに有用な、MediaStreamTrack オブジェクトへのプロキシを表す。

MediaStreamTrackHandle は以下のスロットを持つ:

  1. ハンドルオブジェクトインスタンスが一度転送されたら、そのインスタンスを再度転送できないことを保証するために使用される [[transferable]] 内部スロット。

  2. プロキシされる MediaStreamTrack オブジェクトへの弱参照である [[track]] 内部スロット。

2.1.7. コンストラクター

MediaStreamTrackHandle(track)
  1. handle を新しい MediaStreamTrackHandle オブジェクトとする。

  2. handle.[[transferable]] を true に設定する。

  3. handle.[[track]]track への内部参照に設定する。

  4. handle を返す。

MediaStreamTrackHandle転送ステップは、valuedataHolder が与えられたとき、次のとおりである:

  1. value.[[transferable]] が false の場合、DataCloneError DOMException を投げる。

  2. value.[[transferable]] を false に設定する。

  3. dataHolder.[[track]]handle.[[track]] に設定する。

  4. value.[[track]] を null に設定する。

MediaStreamTrackHandle転送受信ステップは、dataHolderhandle が与えられたとき、次のとおりである:

  1. handle.[[track]]dataHolder.[[track]] に設定する。

MediaStreamTrackHandle は、そのプロキシ先である MediaStreamTrack の生存期間を延ばすことができる。 ガベージコレクションの観点からは、MediaStreamTrackHandle がその MediaStreamTrack への強参照を保持しているかのようである。 また、MediaStreamTrack は、転送中のデータホルダーから参照されている場合、ガベージコレクトされてはならない。

2.2. VideoTrackGenerator

VideoTrackGenerator は、MediaStreamTrack のために、そのフレームを VideoFrame オブジェクトの Stream から生成する動画ソースを MediaStream モデル内に作成することを可能にする。これは 2 つの readonly 属性を持つ: writable WritableStream と、 track MediaStreamTrack である。

VideoTrackGenerator は、その writable 属性の underlying sink] である。track 属性は 出力である。同じ VideoTrackGenerator に接続される追加のトラックは、 track 属性上の clone メソッドを使用して作成できる。

WritableStreamVideoFrame オブジェクトを受け付ける。 VideoFramewritable に書き込まれると、 そのフレームの close() メソッドが自動的に呼び出され、 その内部リソースは JavaScript からアクセスできなくなる。

注記: kind "video" の MediaStreamTrack を生成できる ソースは存在すべきであるという WG の合意がある。 kind "audio" の MediaStreamTrack を生成できるソースが存在すべきかどうかについては、 WG の合意はない。

2.2.1. インターフェイス定義

[Exposed=DedicatedWorker]
interface VideoTrackGenerator {
  constructor();
  readonly attribute WritableStream writable;
  attribute boolean muted;
  readonly attribute MediaStreamTrack track;
};

注記: このインターフェイスは DedicatedWorker に公開すべきであるという WG の合意がある。 それを Window に公開すべきかどうかについては、WG の合意はない。

2.2.2. 内部スロット

[[track]]
このソースの出力である MediaStreamTrack
[[isMuted]]
このソースおよびそれが供給元であるすべての MediaStreamTrack が、 現在 muted であるかどうかを示す値を持つ真偽値。

2.2.3. コンストラクター

VideoTrackGenerator()
  1. generator を新しい VideoTrackGenerator オブジェクトとする。

  2. track を、sourcegenerator に設定され、 tieSourceToContextfalse に設定された、新しく 作成された MediaStreamTrack とする。

  3. generator.tracktrack に初期化する。

  4. generator を返す。

2.2.4. 属性

writable, 型は WritableStream, readonly
VideoTrackGenerator への動画フレームの書き込みを可能にする。 この属性が 最初にアクセスされたとき、以下のステップで初期化されなければならない:
  1. this.writable を新しい WritableStream になるよう初期化する。

  2. Set up this.writable, with its writeAlgorithm set to writeFrame with this as parameter, with closeAlgorithm set to closeWritable with this as parameter and abortAlgorithm set to closeWritable with this as parameter.

writeFrame アルゴリズムは、generatorframe を入力として与えられる。これは以下のステップを実行することで定義される:

  1. frameVideoFrame オブジェクトでない場合、TypeError拒否された promise を返す。

  2. frame[[Detached]] 内部スロットの値が true である場合、TypeError拒否された promise を返す。

  3. generator.[[isMuted]] の値が false の場合、 generator をソースとする各ライブトラックを track として、 以下のステップを実行する:

    1. clone を、frame について Clone videoFrame アルゴリズムを実行した結果とする。

    2. clonetrack に送信する。

  4. frame について Close VideoFrame アルゴリズムを実行する。

  5. undefined で 解決済みの promise を返す。

closeWritable アルゴリズムは、generator を入力として与えられる。 これは以下のステップを実行することで定義される。

  1. generator をソースとする各トラック t について、 t終了する。

  2. undefined で 解決済みの promise を返す。

メディアデータがトラックに送信されるとき、UA は、そのトラックに送信されるメディアデータが トラックの制約を満たすことを保証するために、 処理 (例: クロッピングおよびダウンスケーリング) を適用してもよい。 各トラックは、その制約に応じて、メディアデータの 異なるバージョンを受け取ってもよい。

muted, 型は boolean
VideoTrackGenerator をミュートする。 getter ステップは this.[[isMuted]] を返すことである。値 newValue が与えられた setter ステップは、次のとおりである:
  1. newValuethis.[[isMuted]] に等しい場合、これらのステップを中止する。

  2. this.[[isMuted]]newValue に設定する。

  3. この event loop の実行中にすでにキューに入れられているものがない限り、以下のステップを実行する タスクをキューに入れる:

    1. settledValuethis.[[isMuted]] とする。

    2. this をソースとする各ライブトラックについて、 settledValueトラックの muted 状態を設定するための タスクをキューに入れる

track, 型は MediaStreamTrack, readonly
MediaStreamTrack 出力。getter ステップは this.[[track]] を返すことである。

2.2.5. MediaStreamTrack の挙動の特殊化

VideoTrackGenerator は、1 つ以上の MediaStreamTrack のソースとして機能する。 この節は、 VideoTrackGenerator をソースとする MediaStreamTrack がどのように振る舞うかについての明確化を追加する。
2.2.5.1. stop
stop メソッドはトラックを停止する。 VideoTrackGenerator をソースとする最後のトラックが終了すると、その VideoTrackGeneratorwritable閉じられる
2.2.5.2. 制約可能プロパティ

以下の制約可能プロパティは、 VideoTrackGenerator をソースとする 任意の MediaStreamTrack に対して定義される:

プロパティ名 注記
width ConstrainULong 設定として、これはトラックが受信した最新フレームの幅をピクセル単位で表す。 能力として、maxVideoFrame が持ち得る最大の幅を反映しなければならず、minVideoFrame が持ち得る最小の幅を反映しなければならない。
height ConstrainULong 設定として、これはトラックが受信した最新フレームの高さをピクセル単位で表す。 能力として、maxVideoFrame が持ち得る最大の高さを反映しなければならず、minVideoFrame が持ち得る最小の高さを反映しなければならない。
frameRate ConstrainDouble 設定として、これはトラックが最近受信したフレームに基づくフレームレートの推定値である。 能力として、min は 0 でなければならず、 max はシステムがサポートする最大フレームレートでなければならない。
aspectRatio ConstrainDouble 設定として、これはトラックによって配信された最新フレームの アスペクト比である。 これはピクセル単位の幅をピクセル単位の高さで割った値であり、 小数第 10 位に丸められた double である。能力として、 minVideoFrame がサポートする最小のアスペクト比でなければならず、 maxVideoFrame がサポートする最大のアスペクト比でなければならない。
resizeMode ConstrainDOMString 設定として、この文字列は VideoResizeModeEnum のメンバーの 1 つであるべきである。 値 "none" は、MediaStreamTrack が出力するフレームが、制約に関係なく、 トラックを支える writable に書き込まれたフレームの未変更版であることを意味する。 値 "crop-and-scale" は、 MediaStreamTrack が出力するフレームが、トラックの width、height、 aspectRatio 制約の値に基づいて、ソースフレームを クロップおよび/または ダウンスケールした版であってもよいことを意味する。 能力として、値 "none" および "crop-and-scale" はどちらも存在しなければならない。

applyConstraints メソッドを、 VideoTrackGenerator をソースとする動画 MediaStreamTrack に適用した場合、上で定義されたプロパティがサポートされる。 これは、たとえば、フレームのサイズを変更したり、トラックのフレームレートを調整したりするために使用できる。 これらの制約は、VideoTrackGeneratorwritable に書き込まれる VideoFrame オブジェクトには影響せず、 制約が適用されたトラックの出力だけに影響することに注意すること。 また、VideoTrackGenerator は原理的にサポートされる制約可能プロパティに対して任意の設定の メディアデータを生成できるため、 VideoTrackGenerator によって支えられるトラックに対する applyConstraints 呼び出しは、与えられた制約が getCapabilities によって報告されるシステム対応範囲外でない限り、通常 OverconstrainedError で失敗しないことにも注意すること。

2.2.5.3. イベントと属性
イベントと属性は、任意の MediaStreamTrack と同じように機能する。 関連して注目すべき点として、VideoTrackGeneratorwritable ストリームが閉じられた場合、それに接続されたすべてのライブ トラックは終了し、それらに対して ended イベントが発火される。

3.

3.1. 動画処理

顔位置 (何らかの形式) を返す顔認識関数 detectFace(videoFrame) と、 与えられた videoFrame に似ているが、顔以外の部分をぼかした 新しい VideoFrame を返す操作関数 blurBackground(videoFrame, facePosition) を考える。 この例は、効果の適用前後の動画を video 要素で示すことも行っている。
// main.js

const stream = await navigator.mediaDevices.getUserMedia({video:true});
const videoBefore = document.getElementById('video-before');
const videoAfter = document.getElementById('video-after');
videoBefore.srcObject = stream;

const trackHandle = new MediaStreamTrackHandle(stream.getVideoTracks()[0]);
const worker = new Worker('worker.js');
worker.postMessage({trackHandle}, [trackHandle]);

const {data} = await new Promise(r => worker.onmessage);
videoAfter.srcObject = new MediaStream([data.track]);

// worker.js

self.onmessage = async ({data: {trackHandle}}) => {
  const source = new VideoTrackGenerator();
  parent.postMessage({track: source.track}, [source.track]);

  const {readable} = new MediaStreamTrackProcessor({track: trackHandle});
  const transformer = new TransformStream({
    async transform(frame, controller) {
      const facePosition = await detectFace(frame);
      const newFrame = blurBackground(frame, facePosition);
      frame.close();
      controller.enqueue(newFrame);
    }
  });
  await readable.pipeThrough(transformer).pipeTo(source.writable);
};

3.2. 制約付き複数コンシューマー後処理

よくあるユースケースは、ビデオ会議に入力されるライブカメラ動画から背景を削除し、 ライブの自己表示でその結果を示すことである。実際の送信に使用されるフレームレートが、 帯域幅制約によるバックプレッシャーのために低下する場合でも、 自己表示は高いフレームレートを持つことが望ましい。これはトラックの clone に制約を適用することで実現でき、 2 回処理する必要を避けられる。
// main.js

const stream = await navigator.mediaDevices.getUserMedia({video:true});
const [track] = stream.getVideoTracks();
const worker = new Worker('worker.js');
worker.postMessage({track}, [track]);

const {data} = await new Promise(r => worker.onmessage);
const selfView = document.getElementById('video-self');
selfView.srcObject = new MediaStream([data.track.clone()]); // 60 fps

await data.track.applyConstraints({width: 320, height: 200, frameRate: 30});
const pc = new RTCPeerConnection(config);
pc.addTrack(data.track); // 30 fps

// worker.js

self.onmessage = async ({data: {track}}) => {
  const source = new VideoTrackGenerator();
  parent.postMessage({track: source.track}, [source.track]);

  const {readable} = new MediaStreamTrackProcessor({track});
  const transformer = new TransformStream({transform: myRemoveBackgroundFromVideo});
  await readable.pipeThrough(transformer).pipeTo(source.writable);
};

3.3. worker 内での制約付き複数コンシューマー後処理

高いフレームレートの自己表示を示せることは、worker 内で WebTransport 経由で 動画フレームを送信するときにも関連する。ここでは、制約が worker 内のトラック clone に適用される点を除いて、 上記と同じ手法を使用できる。
// main.js

const stream = await navigator.mediaDevices.getUserMedia({video:true});
const [track] = stream.getVideoTracks();
const worker = new Worker('worker.js');
worker.postMessage({track}, [track]);

const {data} = await new Promise(r => worker.onmessage);
const selfView = document.getElementById('video-self');
selfView.srcObject = new MediaStream([data.track]); // 60 fps

// worker.js

self.onmessage = async ({data: {track}}) => {
  const source = new VideoTrackGenerator();
  const sendTrack = source.track.clone();
  parent.postMessage({track: source.track}, [source.track]);

  await sendTrack.applyConstraints({width: 320, height: 200, frameRate: 30});

  const wt = new WebTransport("https://webtransport.org:8080/up");

  const {readable} = new MediaStreamTrackProcessor({track});
  const transformer = new TransformStream({transform: myRemoveBackgroundFromVideo});
  await readable.pipeThrough(transformer)
    .pipeThrough({writable: source.writable, readable: sendTrack.readable}),
    .pipeThrough(createMyEncodeVideoStream({
      codec: "vp8",
      width: 640,
      height: 480,
      bitrate: 1000000,
    }))
    .pipeThrough(new TransformStream({transform: mySerializer}));
    .pipeTo(wt.createUnidirectionalStream()); // 30 fps
};

上記の例は、リアルタイムストリームに関する問題のため、複数の コンシューマーに供給するために tee() 関数を使用することを避けている。

簡潔さのため、この例は、単一の WebTransport ストリーム上で動画フレームを エンコードして送信するために WebCodecs ラッパーを使用することも過度に単純化している (head-of-line blocking を招く)。

4. 実装上の助言

この節は参考情報である。

4.1. 複数コンシューマーでの使用

プログラマーが、単一のフレームストリームを複数のコンシューマーによって 消費させたいと望むユースケースがある。

例には、背景ぼかし関数の結果を自己表示に表示し、 VideoEncoder を使用してエンコードする場合が含まれる。

両方のコンシューマーが未処理フレームを消費し、同期が不要な場合は、 複数の MediaStreamTrackProcessor オブジェクトをインスタンス化することが堅牢な解決策である。

両方のコンシューマーが、処理ステップの結果を VideoTrackGenerator を使用して MediaStreamTrack に変換しようとする場合、 たとえば処理済みストリームを <video> タグと RTCPeerConnection の両方に供給する場合には、得られた MediaStreamTrack を複数のシンクに接続することが、最も適切な仕組みである可能性がある。

下流の処理がストリームではなくフレームを受け取る場合には、必要に応じてフレームを clone し、下流の処理に送ることができる。"clone" は低コストな操作である。

ストリームが何らかの処理の出力であり、両方の分岐がさらに処理を行うために Stream オブジェクトを 必要とする場合、1 つのストリームから 2 つのストリームを生成する関数が必要になる。

しかし、標準の tee() 操作は この文脈では問題がある:

したがって、メディアを含む Streams で tee() を使用することは、その影響を 十分に理解している場合にのみ行うべきである。代わりに、ユースケースにより適した ストリーム分割用のカスタム要素を使用すべきである。

注記: Streams 仕様には、 その解決がこの節に影響する可能性のある issue が提出されている: https://github.com/whatwg/streams/issues/1157, https://github.com/whatwg/streams/issues/1156, https://github.com/whatwg/streams/issues/401, https://github.com/whatwg/streams/issues/1186

5. セキュリティとプライバシーの考慮事項

この API は MediaStreamTrack ソースと MediaStreamTrack シンクを定義する。 ソース (VideoTrackGenerator) のセキュリティとプライバシーは、 同一オリジンポリシーに依存する。つまり、VideoTrackGeneratorMediaStreamTrack の形式で利用可能にできるデータは、 VideoFrame オブジェクトを構築して VideoTrackGenerator にプッシュできるようになる前に、 文書から可視でなければならない。 クロスオリジンデータを使用して VideoFrame オブジェクトを作成しようとする試みは失敗する。 したがって、VideoTrackGenerator は新しい fingerprinting surface を導入しない。

この API によって導入される MediaStreamTrack シンク (MediaStreamTrackProcessor) は、 WebRTC ピア接続やメディア要素など、他の MediaStreamTrack シンクによって公開されるものと同じデータを MediaStreamTrack から公開する。 MediaStreamTrackProcessor のセキュリティとプライバシーは、 MediaStreamTrackProcessor が接続されているトラックの MediaStreamTrack ソースのセキュリティとプライバシーに依存する。たとえば、カメラ、マイク、画面キャプチャトラックは、 permission dialog による明示的な使用許可に依存し ([MEDIACAPTURE-STREAMS] および [SCREEN-CAPTURE] を参照)、 一方で element capture と VideoTrackGenerator は同一オリジンポリシーに依存する。

MediaStreamTrackProcessor に関する潜在的な問題はリソース枯渇である。 たとえば、サイトが開いた VideoFrame オブジェクトを保持しすぎて、 GPU メモリーに支えられたフレームのシステム全体のプールを枯渇させる可能性がある。 UA は、サイトが保持できる pool-backed frame の数を制限することで このリスクを軽減できる。これは、バッファリングされるフレームの最大数を減らすこと、 および予算上限に達したら readable へのさらなるフレーム配信を拒否することによって達成できる。偶発的な枯渇は、 VideoFrame オブジェクトが VideoTrackGenerator に書き込まれると自動的に閉じられることによっても軽減される。

6. 以前の提案との後方互換性

この節は参考情報である。

このインターフェイスの以前の提案には、次のような API があった:

[Exposed=Window,DedicatedWorker]
interface MediaStreamTrackGenerator : MediaStreamTrack {
    constructor(MediaStreamTrackGeneratorInit init);
    attribute WritableStream writable;  // VideoFrame or AudioData
};

dictionary MediaStreamTrackGeneratorInit {
  required DOMString kind;
};
このインターフェイスでは、MediaStreamTrack の generator が、 MediaStreamTrack を含むものではなく、そのインスタンスであった。

VideoTrackGenerator は、次のように MediaStreamTrackGenerator の上に shim できる:

// Not tested, unlikely to work as written!
class VideoTrackGenerator {
  constructor() {
     this.innerGenerator = new MediaStreamTrackGenerator({kind: 'video'});
     this.writable = this.innerGenerator.writable;
     this.track = this.innerGenerator.clone();
  }
  // Missing: shim for setting of the "muted" attribute.
};

音声の処理に関する考慮事項を含む、以前の提案のさらなる説明は、 この文書の以前のバージョンにある。

注記: リポジトリの移動を終えたら、 chrome-96 branch を指すリンクがここに置かれる。

適合性

文書の 慣例

適合要件は、 記述的な表明と RFC 2119 の用語との組み合わせで表現される。 この文書の規範的な部分におけるキーワード “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, および “OPTIONAL” は、RFC 2119 で説明されるように解釈される。 ただし、読みやすさのため、 この仕様ではこれらの語がすべて大文字で現れるとは限らない。

この仕様のすべてのテキストは、 非規範的であることが明示された節、例、および注記を除き、規範的である。 [RFC2119]

この仕様における例は、“for example” という語で導入されるか、 または次のように class="example" によって 規範的なテキストから分離される:

これは参考例の一例である。

参考注記は “Note” という語で始まり、 次のように class="note" によって 規範的なテキストから分離される:

Note, これは参考注記である。

適合 アルゴリズム

アルゴリズムの一部として命令形で表現された要件 (たとえば "strip any leading space characters" や "return false and abort these steps") は、そのアルゴリズムの導入で使用される キーワード ("must", "should", "may" など) の意味で解釈される。

アルゴリズムまたは特定のステップとして表現された適合要件は、 最終結果が等価である限り、 任意の方法で実装できる。 特に、この仕様で定義されるアルゴリズムは 理解しやすいことを意図しており、 高性能であることを意図していない。 実装者には最適化が推奨される。

索引

この 仕様で定義される用語

参照により定義される 用語

参考文献

規範的参考文献

[CSS-TEXT-4]
Elika Etemad; et al. CSS Text Module Level 4. 29 May 2024. WD. URL: https://www.w3.org/TR/css-text-4/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[MEDIACAPTURE-STREAMS]
Cullen Jennings; et al. Media Capture and Streams. 9 October 2025. CRD. URL: https://www.w3.org/TR/mediacapture-streams/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[STREAMS]
Adam Rice; et al. Streams Standard. Living Standard. URL: https://streams.spec.whatwg.org/
[WEBCODECS]
Paul Adenot; Eugene Zemtsov. WebCodecs. 14 April 2026. WD. URL: https://www.w3.org/TR/webcodecs/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/
[WEBRTC]
Cullen Jennings; et al. WebRTC: Real-Time Communication in Browsers. 13 March 2025. REC. URL: https://www.w3.org/TR/webrtc/

参考情報の参考文献

[SCREEN-CAPTURE]
Jan-Ivar Bruaroey; Elad Alon. Screen Capture. 17 July 2025. WD. URL: https://www.w3.org/TR/screen-capture/
[WEBRTC-NV-USE-CASES]
Bernard Aboba. WebRTC Extended Use Cases. 14 December 2023. DNOTE. URL: https://www.w3.org/TR/webrtc-nv-use-cases/
[WEBTRANSPORT]
Nidhi Jaju; Victor Vasiliev; Jan-Ivar Bruaroey. WebTransport. 25 March 2026. WD. URL: https://www.w3.org/TR/webtransport/

IDL 索引

[Exposed=(Window,DedicatedWorker), Transferable]
interface MediaStreamTrackHandle {
   constructor(MediaStreamTrack track);
};

[Exposed=DedicatedWorker]
interface MediaStreamTrackProcessor {
    constructor(MediaStreamTrackProcessorInit init);
    readonly attribute ReadableStream readable;
    readonly attribute unsigned long long discardedFrames;
    readonly attribute unsigned long long totalFrames;
};

typedef (MediaStreamTrack or MediaStreamTrackHandle) MediaStreamTrackOrHandle;
dictionary MediaStreamTrackProcessorInit {
  required MediaStreamTrackOrHandle track;
  [EnforceRange] unsigned short maxBufferSize;
};

[Exposed=DedicatedWorker]
interface VideoTrackGenerator {
  constructor();
  readonly attribute WritableStream writable;
  attribute boolean muted;
  readonly attribute MediaStreamTrack track;
};