webrtc multistream
TRANSCRIPT
WebRTC Multi-Stream 挙動から見たブラウザの現状
2015.07.03
インフォコム株式会社
がねこまさし
@massie_g
JavaScript APIから見た PeerConnectionとMedia階層
RTCPeerConnection
MediaStream
MediaStream
MediaStream
MediaStreamTrack
MediaStreamTrack
MediaStreamTrack
MediaStreamTrack
videoTracks[] audioTracks[]
DataChannel
N個
N個
DataChannel
N個
複数のMediaを使う手段
• 1 アプリに、複数 PeerConnection
• 1 PeerConnection に、複数 MediaStream
• 1 MediaStream に、複数MediaStreamTrack
複数のMediaを使う手段
• 1 アプリに、複数 PeerConnection
– 複数人のケースと同じ
• 1 PeerConnection に、複数 MediaStream
– 今回検証した範囲
• 1 MediaStream に、複数MediaStreamTrack
– Chromeでは MediaStream.addTrack()は呼び出し可能 • ※特にイベントは発生しない
– 受け側で 特定のTrackを指定して再生することはできないはず • URL.createObjectURL(stream) で、複数Trackをハンドリングできない
関連する要素
• メソッド – RTCPeerConnection.addStream(stream)
• ローカルのMediaStreamを追加
– RTCPeerConnection.removeStream(stream) • ローカルのMediaStreamを除去
• イベント – RTCPeerConnection.onnegotiationneeded()
• SDPの再送が必要なときに発火
addStream() / removeStream()
• Chrome 43.0.2357.124 (64-bit) – RTCPeerConnection.addStream()
• 2つのMediaStream(video入り)を追加可能
– RTCPeerConnection.removeStream()も可能
• Firefox 38.0.5 – RTCPeerConnection.addStream()
• 2つのMediaStream(video入り)を追加可能
– RTCPeerConnection.removeStream()は不可 • NotSupportedError:
onnegotiationneeded()
• 発火のタイミング – ○ RTCPeerConnection.addStream() / .removeStream() – × RTCPeerConnection.createOffer() / .createAnswer() – × RTCPeerConnection.setLocalDescription() – × RTCPeerConnection.setRemoteDescription() – × RTCPeerConnection.onaddstream() 発火時
• リモートの MediaStream が検出されたときのイベント
• Chorme – 接続確立前の addStream() でも発火 – 接続確立後の addStream() / removeStream() でも発火
• Firefox – 接続確立前の addStream() では発火しない (※Nightlyでは発火) – 接続確立後の addStream() で発火 – 接続確立後の removeStream() は実行できない (※Nightlyでも不可)
参考:vanilla ICE シグナリング Peer B Peer A
createOffer()
Peer-to-Peer
offer SDP (with ICE candidate)
setLocalDescription()
setLocalDescription()
answer SDP (with ICE candidate)
setRemoteDescription()
createAnswer()
setRemoteDescription()
onicecandidate()
onicecandidate()
onicecandidate()
onicecandidate()
addStream(localStream) addStream(localStream)
onaddstream(remote)
onaddstream(remote)
最初からmultistream : Chrome
• ※ onnegotiationneeded()イベントは無視
• offer:1, answer:1→ 当然OK
• offer:2, answer:2 → OK
• offer:2, answer:1 → OK
• offer:1, answer:2 → OK
最初からmultistream : Firefox
• ※ onnegotiationneeded()イベントは無視
• offer:1, answer:1→ 当然OK
• offer:2, answer:2 → OK
• offer:2, answer:1 → OK
• offer:1, answer:2 → △
– offer側に1つしか表示されない
–※ answer側でsetOffer後、 onnegotiationneeded()が発火している
• そのイベントを適切に処理してあげる必要がある??
最初からmultistream Chrome(offer) → Firefox(answer)
• ※ onnegotiationneeded()イベントは無視
• offer:1, answer:1 → OK • offer:2, answer:2 → NG
– answer側でエラー – setRemoteDescription(offer) ERROR: " DOMException
[InvalidSessionDescriptionError: "Found multiple different webrtc msids in m-section 1
• offer:2, answer:1 → NG – answer側 – setRemoteDescription(offer) ERROR: " DOMException
[InvalidSessionDescriptionError: "Found multiple different webrtc msids in m-section 1
• offer:1, answer:2 → △。offer側に1つしか表示されない
最初からmultistream Firefox(offer) → Chrome(answer)
• ※ onnegotiationneeded()イベントは無視
• offer:1, answer:1 → OK • offer:2, answer:2 → NG
– answer側でエラー – Create Answer failed. err: Failed to initialize the answer.
• offer:2, answer:1answer:2 → NG – answer側でエラー – Create Answer failed. err: Failed to initialize the answer.
• offer:1, answer:2 → NG – answer側 OK – offer側でエラー – setRemoteDescription(offer) ERROR: " DOMException
[InvalidSessionDescriptionError: "Found multiple different webrtc msids in m-section 1
後からmultistream : offer側で+1 Peer B Peer A
createOffer()
Peer-to-Peer
offer SDP (with ICE candidate)
setLocalDescription()
setLocalDescription()
answer SDP (with ICE candidate)
setRemoteDescription()
createAnswer()
setRemoteDescription()
addStream(localStream)
onaddstream(remote)
onnegotiationneeded()
後からmultistream : offer側で変化 • onnegotiationneeded()イベントを使う
• offer:1 answer:1後、 offer +1 – offer側
• peer.addStream(stream2)
• peer.onnegotiationneeded()発火
• peer.createOffer()
• peer.setLocalDescription(sdp)
• sendSDP(sdp) ※何らかの手段で送る
– answer側 • receiveSDP(sdp) ※何らかの手段で受けてとる
• peer.setRemoteDescription(sdp)
• peer.onaddstream(stream2) 発火
• ※ peer.onnegotiationneeded()発火しない
• peer.createAnswer()
• peer.setLocalDescription(sdp)
• sendSDP(sdp) ※何らかの手段で送る
– offer側 • receiveSDP(sdp) ※何らかの手段で受けてとる
• peer.setRemoteDescription(sdp)
後からmultistream : answer側で+1 Peer B Peer A
createOffer()
Peer-to-Peer
offer SDP
setLocalDescription()
setLocalDescription()
answer SDP (with ICE candidate)
setRemoteDescription()
createAnswer()
setRemoteDescription()
addStream(localStream)
onnegotiationneeded()
onaddstream(remote)
REQUEST for re-createOffer
後からmultistream : answer側で変化 • onnegotiationneeded()イベントを使う
• offer:1 answer:1後、 answer +1 – answer側
• peer.addStream(stream2)
• peer.onnegotiationneeded()発火
• ※何らかの手段で、offer側にSDP再送を依頼する
– offer側 • peer.createOffer()
• peer.setLocalDescription(sdp)
• sendSDP(sdp) ※何らかの手段で送る
– answer側 • receiveSDP(sdp) ※何らかの手段で受けてとる
• peer.setRemoteDescription(sdp)
• peer.createAnswer()
• peer.setLocalDescription(sdp)
• sendSDP(sdp) ※何らかの手段で送る
– offer側 • receiveSDP(sdp) ※何らかの手段で受けてとる
• peer.setRemoteDescription(sdp)
• peer.onaddstream(stream2) 発火
後からmultistream : Chrome • onnegotiationneeded()イベントを使う
• offer:1 answer:1後、 offer +1 → OK (※ answer を再生成) • offer:2 answer:1後、 offer -1→ OK (※ answer を再生成) • offer:1 answer:2後、 offer +1→ OK (※ answer を再生成) • offer:2 answer:2後、 offer -1→ OK (※ answer を再生成)
• offer:1 answer:1後、 answer +1 → OK
– answer側のonnegotiationneeded → answer作らずスルー – offerを強制生成、answer側にoffer再セット(setRemote) – answer側で、ようやくanswer生成 – offer側に、asnwer再セット(setRemote)
• offer:1 answer:2後、 answer -1 → OK (※同上) • offer:2 answer:1後、 answer +1 → OK (※同上) • offer:2 answer:2後、 answer -1 → OK (※同上)
後からmultistream : Firefox • onnegotiationneeded()イベントを使う • offer:1 answer:1後、 offer +1 → OK.
– ※+1時には、offer側では onnegotiationneededが呼ばれる。 – ※answerは強制生成
• offer:2 answer:1後、 offer -1→ NG. removeStream() できない – NotSupportedError: removeStream not yet implemented
• offer:1 answer:2後、 offer +1→ その状況が作れない • offer:2 answer:2後、 offer -1→ NG. removeStream()できない
• offer:1, answer:1後、 answer +1 → NG. Answer側でエラー
– "Create Answer failed. err:" DOMException [InvalidStateError: "Cannot create answer in state stable"
• offer:1, answer:2後、 answer -1 → その状況が作れない • offer:2, answer:1後、 answer +1 → NG?? Javascriptミス? • offer:2, answer:2後、 answer -1 → NG. removeStream()できない
オマケ1
• RTCPeerConnection.addTrack() …
– onnegotiationneeded()発火しない
• MedaiStreamTrack.enabled = true / false
– メディア送信の on/offできる
オマケ2: 複数のMediaを使う手段
• 1 アプリに、複数 PeerConnection – 複数人のケースと同じ
• 1 PeerConnection に、複数 MediaStream – 今回検証した範囲
• 1 MediaStream に、複数MediaStreamTrack – Chromeでは MediaStream.addTrack()は呼び出し可能
• ※特にイベントは発生しない
– 受け側で 特定のTrackを指定して再生することはできないはず • URL.createObjectURL(stream) で、複数Trackをハンドリングできない
– ※無理やり再生はできた • MediaStream 生成 → addTrack() → URL.createObjectURL()
オマケ2: 複数のMediaを使う手段
※複数MediaStreamTrackから無理やり再生手順
var videoTracks = stream1.getVideoTracks();
var stream2 = new webkitMediaStream();
stream2.addTarck(videoTracks[1]);
video.src = URL.createObjectURL(stream2);
Thank you!