1. 序論
[WEBRTC-NV-USE-CASES] 文書は、 メディアへのアクセスによってのみ実現できるいくつかの機能 (要件 N20-N22) を記述しており、これには以下が含まれるが、それらに限定されない:
-
おもしろい帽子
-
機械学習
-
仮想現実ゲーム
これらのユースケースではさらに、処理を worker スレッドで行えることも必要になる (要件 N23-N24)。
この仕様は、[WEBCODECS] および [STREAMS] に基づくインターフェイスを提供し、 そのような機能へのアクセスを可能にする。
この仕様は生メディアへのアクセスを提供する。 これはカメラ、マイク、画面キャプチャ、 またはコーデックのデコーダー部などのメディアソースの出力であり、 コーデックのデコーダー部への入力でもある。処理されたメディアは、 HTML <video> タグ、RTCPeerConnection、canvas、MediaRecorder を含む、 MediaStreamTrack を受け取れる任意の宛先で消費できる。
この仕様は、以下のユースケースを明示的にサポートすることを目的とする:
-
動画処理: これは "Funny Hats" ユースケースであり、入力は単一の動画トラックで、 出力は変換された動画トラックである。
-
カスタム動画シンク: このユースケースでは、目的は処理済みの
MediaStreamTrackを生成することではなく、 メディアを別の方法で消費することである。たとえば、アプリケーションは [WEBCODECS] と [WEBTRANSPORT] を使用して、 異なるコーデック設定とネットワークプロトコルを用いるRTCPeerConnectionのような シンクを作成できる。 -
複数ソース処理: このユースケースでは、2 つ以上のトラックを 1 つに結合する。 たとえば、ライブ天気図と話者のカメラトラックを含むプレゼンテーションを結合して、 天気予報アプリケーションを生成できる。
注記: 音声の ユースケースをサポートすべきかどうかについては、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)
-
init.
trackが有効でないMediaStreamTrackである場合、TypeErrorを投げる。 -
init.
trackがMediaStreamTrackHandleである場合、 以下のサブステップを実行する:-
init.
track.[[track]]が有効でないMediaStreamTrackを参照している場合、TypeErrorを投げる。
-
maxBufferSize を 1 とする。
-
init.
maxBufferSizeが 1 より大きい整数値を持つ場合、以下のサブステップを実行する:-
maxBufferSize を init.
maxBufferSizeに設定する。 -
ユーザーエージェントは maxBufferSize をより低い値に丸めてもよいが、 1 より低くしてはならない。
maxBufferSize を丸めることは、たとえばカメラのように、 任意の時点で限られた数の VideoFrames しか使用できない場合があるソースにとって有用である。
-
-
processor を新しい
MediaStreamTrackProcessorオブジェクトとする。 -
processor.
[[track]]を init.trackに設定する。 -
processor.
[[maxBufferSize]]を maxBufferSize に設定する。 -
processor.
[[queue]]を空の キューに設定する。 -
processor.
[[numPendingReads]]を 0 に設定する。 -
processor.
[[isClosed]]を false に設定する。 -
processor.
[[numDiscardedFrames]]を 0 に設定する。 -
processor.
[[numTotalFrames]]を 0 に設定する。 -
processor を返す。
2.1.4. 属性
readable, 型は ReadableStream, readonly-
[[track]]内部スロットに格納されたMediaStreamTrackまたはMediaStreamTrackHandleによって配信されるフレームの読み取りを可能にする。 この属性は、以下のステップに従って、最初に呼び出されたときに作成される:-
this.
readableを新しいReadableStreamになるよう初期化する。 -
Set up this.
readablewith its pullAlgorithm set to processorPull with this as parameter, cancelAlgorithm set to processorCancel with this as parameter, and highWaterMark set to 0.
processorPull アルゴリズムは、processor を入力として与えられる。これは以下のステップで定義される:
-
processor.
[[numPendingReads]]の値を 1 増やす。 -
processor をパラメーターとして maybeReadFrame アルゴリズムを実行する タスクをキューに入れる。
-
undefined で 解決済みの promise を返す。
maybeReadFrame アルゴリズムは、processor を入力として与えられる。 これは以下のステップで定義される:
-
processor.
[[queue]]が空である場合、 これらのステップを中止する。 -
processor.
[[numPendingReads]]が 0 に等しい場合、これらのステップを中止する。 -
frame を、 processor.
[[queue]]からフレームメディアデータを デキューした結果とする。 -
processor.
[[numPendingReads]]を 1 減らす。 -
ステップ 1 に戻る。
processorCancel アルゴリズムは、processor を入力として与えられる。 これは以下のステップを実行することで定義される:
-
processor をパラメーターとして processorClose アルゴリズムを実行する。
-
undefined で 解決済みの promise を返す。
processorClose アルゴリズムは、processor を入力として与えられる。 これは以下のステップを実行することで定義される:
-
processor.
[[isClosed]]が true の場合、これらのステップを中止する。 -
processor を processor.
[[track]]から切断する。 これを行う仕組みは UA 固有であり、その結果として processor は processor.[[track]]、またはそれによって参照されるMediaStreamTrackのシンクではなくなる。 -
processor.
[[track]]を null に設定する。 -
processor.
readable.[[controller]] を 閉じる。 -
processor.
[[queue]]を空にする。 -
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 を入力として与えられる。 これは以下のステップを実行することで定義される:
-
processor.
[[queue]]が processor.[[maxBufferSize]]個の要素を持つ場合、以下のステップを実行する:-
droppedFrame を、processor.
[[queue]]を デキューした結果とする。 -
droppedFrame について、 Close VideoFrame アルゴリズムを実行する。
-
processor.
[[numDiscardedFrames]]を 1 増やす。
-
-
processor.
[[numTotalFrames]]を 1 増やす。 -
新しいフレームメディアデータを processor.
[[queue]]に エンキューする。 -
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
は以下のスロットを持つ:
-
ハンドルオブジェクトインスタンスが一度転送されたら、そのインスタンスを再度転送できないことを保証するために使用される
[[transferable]]内部スロット。 -
プロキシされる
MediaStreamTrackオブジェクトへの弱参照である[[track]]内部スロット。
2.1.7. コンストラクター
MediaStreamTrackHandle(track)
-
handle を新しい
MediaStreamTrackHandleオブジェクトとする。 -
handle.
[[transferable]]を true に設定する。 -
handle.
[[track]]を track への内部参照に設定する。 -
handle を返す。
MediaStreamTrackHandle
の
転送ステップは、value と dataHolder が与えられたとき、次のとおりである:
-
value.
[[transferable]]が false の場合、DataCloneErrorDOMException を投げる。 -
value.
[[transferable]]を false に設定する。 -
dataHolder.
[[track]]を handle.[[track]]に設定する。 -
value.
[[track]]を null に設定する。
MediaStreamTrackHandle
の
転送受信ステップは、dataHolder と
handle が与えられたとき、次のとおりである:
-
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
メソッドを使用して作成できる。
WritableStream
は VideoFrame
オブジェクトを受け付ける。
VideoFrame
が writable
に書き込まれると、
そのフレームの 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()
-
generator を新しい
VideoTrackGeneratorオブジェクトとする。 -
track を、source が generator に設定され、 tieSourceToContext が
falseに設定された、新しく 作成されたMediaStreamTrackとする。 -
generator.
trackを track に初期化する。 -
generator を返す。
2.2.4. 属性
writable, 型は WritableStream, readonly-
VideoTrackGeneratorへの動画フレームの書き込みを可能にする。 この属性が 最初にアクセスされたとき、以下のステップで初期化されなければならない:-
this.
writableを新しいWritableStreamになるよう初期化する。 -
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 アルゴリズムは、generator と frame を入力として与えられる。これは以下のステップを実行することで定義される:
-
frame が
VideoFrameオブジェクトでない場合、TypeErrorで 拒否された promise を返す。 -
frame の
[[Detached]]内部スロットの値が true である場合、TypeErrorで 拒否された promise を返す。 -
generator.
[[isMuted]]の値が false の場合、 generator をソースとする各ライブトラックを track として、 以下のステップを実行する:-
clone を、frame について Clone videoFrame アルゴリズムを実行した結果とする。
-
clone を track に送信する。
-
-
frame について Close VideoFrame アルゴリズムを実行する。
-
undefined で 解決済みの promise を返す。
closeWritable アルゴリズムは、generator を入力として与えられる。 これは以下のステップを実行することで定義される。
-
generator をソースとする各トラック
tについて、tを 終了する。 -
undefined で 解決済みの promise を返す。
-
メディアデータがトラックに送信されるとき、UA は、そのトラックに送信されるメディアデータが トラックの制約を満たすことを保証するために、 処理 (例: クロッピングおよびダウンスケーリング) を適用してもよい。 各トラックは、その制約に応じて、メディアデータの 異なるバージョンを受け取ってもよい。
muted, 型は boolean-
VideoTrackGeneratorをミュートする。 getter ステップは this.[[isMuted]]を返すことである。値 newValue が与えられた setter ステップは、次のとおりである:-
newValue が this.
[[isMuted]]に等しい場合、これらのステップを中止する。 -
this.
[[isMuted]]を newValue に設定する。 -
この event loop の実行中にすでにキューに入れられているものがない限り、以下のステップを実行する タスクをキューに入れる:
-
settledValue を this.
[[isMuted]]とする。 -
this をソースとする各ライブトラックについて、 settledValue に トラックの muted 状態を設定するための タスクをキューに入れる。
-
-
track, 型は MediaStreamTrack, readonlyMediaStreamTrack出力。getter ステップは this.[[track]]を返すことである。
2.2.5. MediaStreamTrack の挙動の特殊化
VideoTrackGenerator
は、1 つ以上の MediaStreamTrack
のソースとして機能する。
この節は、
VideoTrackGenerator
をソースとする
MediaStreamTrack
がどのように振る舞うかについての明確化を追加する。
2.2.5.1. stop
stop
メソッドはトラックを停止する。
VideoTrackGenerator
をソースとする最後のトラックが終了すると、その VideoTrackGenerator
の
writable
は閉じられる。
2.2.5.2. 制約可能プロパティ
以下の制約可能プロパティは、
VideoTrackGenerator
をソースとする
任意の MediaStreamTrack
に対して定義される:
| プロパティ名 | 値 | 注記 |
|---|---|---|
| width |
ConstrainULong
|
設定として、これはトラックが受信した最新フレームの幅をピクセル単位で表す。
能力として、max は
VideoFrame
が持ち得る最大の幅を反映しなければならず、min
は VideoFrame
が持ち得る最小の幅を反映しなければならない。
|
| height |
ConstrainULong
|
設定として、これはトラックが受信した最新フレームの高さをピクセル単位で表す。
能力として、max は
VideoFrame
が持ち得る最大の高さを反映しなければならず、min
は VideoFrame
が持ち得る最小の高さを反映しなければならない。
|
| frameRate |
ConstrainDouble
|
設定として、これはトラックが最近受信したフレームに基づくフレームレートの推定値である。
能力として、min は 0 でなければならず、
max はシステムがサポートする最大フレームレートでなければならない。
|
| aspectRatio |
ConstrainDouble
|
設定として、これはトラックによって配信された最新フレームの
アスペクト比である。
これはピクセル単位の幅をピクセル単位の高さで割った値であり、
小数第 10 位に丸められた double である。能力として、
min は VideoFrame
がサポートする最小のアスペクト比でなければならず、
max は
VideoFrame
がサポートする最大のアスペクト比でなければならない。
|
| resizeMode |
ConstrainDOMString
|
設定として、この文字列は
VideoResizeModeEnum
のメンバーの 1 つであるべきである。
値 "none"
は、MediaStreamTrack が出力するフレームが、制約に関係なく、
トラックを支える
writable
に書き込まれたフレームの未変更版であることを意味する。
値 "crop-and-scale"
は、
MediaStreamTrack が出力するフレームが、トラックの width、height、
aspectRatio 制約の値に基づいて、ソースフレームを
クロップおよび/または
ダウンスケールした版であってもよいことを意味する。
能力として、値 "none"
および
"crop-and-scale"
はどちらも存在しなければならない。
|
applyConstraints
メソッドを、
VideoTrackGenerator
をソースとする動画 MediaStreamTrack
に適用した場合、上で定義されたプロパティがサポートされる。
これは、たとえば、フレームのサイズを変更したり、トラックのフレームレートを調整したりするために使用できる。
これらの制約は、VideoTrackGenerator
の
writable
に書き込まれる
VideoFrame
オブジェクトには影響せず、
制約が適用されたトラックの出力だけに影響することに注意すること。
また、VideoTrackGenerator
は原理的にサポートされる制約可能プロパティに対して任意の設定の
メディアデータを生成できるため、
VideoTrackGenerator
によって支えられるトラックに対する
applyConstraints
呼び出しは、与えられた制約が
getCapabilities
によって報告されるシステム対応範囲外でない限り、通常
OverconstrainedError
で失敗しないことにも注意すること。
2.2.5.3. イベントと属性
イベントと属性は、任意のMediaStreamTrack
と同じように機能する。
関連して注目すべき点として、VideoTrackGenerator
の writable
ストリームが閉じられた場合、それに接続されたすべてのライブ
トラックは終了し、それらに対して 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() 操作は この文脈では問題がある:
-
過剰なキューイングを防ぐバックプレッシャー機構を無効にしてしまう
-
同じバッファへの複数のリンクを作成するため、どのコンシューマーが バッファを destroy() するのかという問題への対処が難しくなる
したがって、メディアを含む Streams で tee() を使用することは、その影響を 十分に理解している場合にのみ行うべきである。代わりに、ユースケースにより適した ストリーム分割用のカスタム要素を使用すべきである。
-
両方の分岐がフレームを破棄できる能力を必要とする場合は、frame を clone() し、 それぞれのキューに別個のコピーを enqueue する。これは ReadableStreamTee(stream, cloneForBranch2=true) 関数に対応する。 その後、以下の選択肢のいずれかを選択する。
-
一方の分岐がすべてのフレームを必要とし、もう一方の分岐がドロップされたフレームを許容する場合は、 すべてのフレームを必要とするストリームにバッファを enqueue し、 そのストリームからのバックプレッシャー信号を使用してソースからの読み取りを停止する。 もう一方のストリームからのバックプレッシャー信号が空き容量を示している場合は、 同じフレームをそのキューにも enqueue する。
-
どちらのストリームもドロップされたフレームを許容しない場合は、組み合わせたバックプレッシャー信号を 使用してソースからの読み取りを停止する。この場合、バッファサイズがどちらも 1 であれば、 フレームはロックステップで処理される。
-
プロセスに割り当てられた基盤のバッファプールが枯渇したときにのみ入力ストリームが 停止されてもよい場合は、標準の 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)
のセキュリティとプライバシーは、
同一オリジンポリシーに依存する。つまり、VideoTrackGenerator
が MediaStreamTrack
の形式で利用可能にできるデータは、
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 ; };
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 を指すリンクがここに置かれる。