console.lealog();

@leader22のWeb系に関する勉強めもブログですのだ

ChromeのMediaStreamTrackとvideo要素の組合せには罠がある話

WebRTC界隈で、

  • Chromeの`video.srcObject = stream`はバグってるから使っちゃダメ
  • 代わりに`video.src = URL.createObjectURL(stream)`を使え

という噂があって。

とはいっても、

などなど事情もあるのでそろそろ`srcObject`だけ使いたい!
そのためにもどういうケースでバグってるとか、この噂の真意を確かめたいなーと思って調べてたのが事の発端。

よくわからなかった

実際に`srcObject`を使ってるコードでストリームが止まる謎の挙動を示すケースが実際にあった。
ただ代わりに`createObjectURL()`だと大丈夫かというと、だいたい大丈夫やけど大丈夫じゃない時もあるという不安定な感じで。

そして午前中は問題ないのに午後になるとダメだったり・・(この時点で何か別のものを疑うべきな気もするけどw)。
↑の例でいうと、結局は関係ないコードをリファクタしたらストリームが止まることがなくなったりと本当に謎。

でも実際バグってた

あれこれ調べてて偶然見つけた。

javascript - Stopping 2nd webrtc stream in Chrome breaks all webrtc streams - Stack Overflow

ここに上がってる再現デモ -> https://jsfiddle.net/kqjmt7a0/1/

どういうデモかというと、

  • `video`要素を2つ用意する(AとBとする)
  • `getUserMedia()`してストリームをAの`video`にいれておく
    • Aはその後ほったらかし
  • Bの`video`要素にも`getUserMedia()`してストリームを追加
    • この時、既にストリームがあればそれを止めてから再度追加
  • このBに対する停止・追加を2回やると、関係ないはずのAが止まる
    • そしてBにも追加されない
    • そしてこれ以降はストリームを使って何もできなくなる

というもの。

Firefoxでは再現しなくて、Chromeでだけ再現する。
現時点の最新のCanary 62.0.3179.0(Official Build)でもダメ。

674965 - MediaStreamTrack::stop() can break unrelated WebRTC streams - chromium - Monorail

この人はChromeにもバグ報告してくれてて、中の人も認知はしてるっぽい。
ただChrome的には直したことになってるぽいけど、ステータスはWontFixのままやし、実際直ってないし、たぶんずっと直らん気がする・・。

このデモは`createObjectURL()`で書かれててダメなので、`srcObject`がマズいというよか、やっぱり組合せに罠がある気がする。
`createObjectURL()`使っててもダメな時はダメ。

何がダメなのか

Workaroundがないわけではない。
あれこれ触ってみると微妙に傾向が見えてきて、どうやら特定のコードの組合せで動かなくなるっぽい。

function stop(videl, stream) {
  videl.src = '';
	
  var tracks = stream.getTracks();
  for(var i = 0; i < tracks.length; ++i) {
    tracks[i].stop();
  }
}

このデモのコードにおいては、`src = ''`が悪い。
たとえばこの行を、

すると問題は起きない。
`video.onload`で`revokeObjectURL()`しても関係なくダメ。

ちなみに本来の目的どおり、`srcObject`を使ってみても解決しない。

  • `track.stop()` -> `srcObject = null`だと大丈夫
  • `srcObject = null` -> `track.stop()`だと止まる

という感じ。

なので、「`video`要素に代入されてる`MediaStreamTrack`を、`video`からの参照を切ってから`stop()`しようとするとダメになる」っぽい。

`MediaStreamTrack`は`stop()`してから、`video`から外すが正しい手順。挙動がおかしくなるのはChromeだけやけど・・・。

もちろん律儀に`video`を解放しない場合 = `src`も`srcObject`も放置する場合は、問題にならない。
なのでそんなにハマってる人がいないのかなーとも思ったり・・。

何を学ぶべきか

少なくともこういう挙動を示すことがあるという知識を持っておく。

あとはこのメディア周りは本当にブラウザの挙動が不定で色々苦労するので、その覚悟をもつ。
なんかこういうのは勘と運で切り抜けるしかないなーという感じ。