【WebRTC】Unified Plan(Plan A)のトリセツ
Unified PlanやRTCRtpTransceiverについて調べてみました。
この記事は アイソルート Advent Calendar 24日目の記事です。
通信(主にVoIP関連)が専門です。
#SIP #WebRTC #WebSocket通信
これからWebRTCを触ってみようという人や、Unified Planへの移行を考えている方は
ご一読いただき、参考にしていただければ幸いです。
もくじ
- Plan AとPlan B
- Unified Planを使用する準備
- Unified PlanのSDP
- RTCRtpTransceiverの説明書
- まとめ
Plan AとPlan B
Plan AとPlan Bは、SDPの記述方法に違いがあります。
WebRTCの発足当初はPlan AとPlan Bが混在しており、それらに互換性がなかったのですが、
Plan Aを標準規格(Unified Plan)とすることが決定し、今日に至ります。
Chrome/Safari/Edgeは従来Plan Bを採用してきましたが、
これらのブラウザもUnified Planを標準化し、Plan Bを廃止する方向で動いています。
まもなくChromiumベースのEdgeがリリースされ、EdgeもUnified Planの仲間入りですね。
Unified Planを使用する準備
Unified Planを使うための準備は簡単です。
→ ブラウザを最新にアップデートしましょう。
前述の通り、ブラウザがUnified Planを標準化していますので、特別な対応は不要です。
万が一、強制的にPlan Bで動作させていた場合は、その指定を解除してあげましょう。
// Plan Bで動作させる
new RTCPeerConnection({sdpSemantics: "plan-b"});
// 標準動作に従う
new RTCPeerConnection();
明示的にUnified Planを指定しておきたい場合は以下の通りに指定します。
// 明示的にUnified Planを使用する
new RTCPeerConnection({ sdpSemantics: "unified-plan" });
Unified PlanのSDP
ここからが本題です。
Unified PlanになることでSDPがどう変わるのか、まとめていきます。
[1] a=mid
Plan Bでは、メディア種別(audio/video)ごとにメディア記述セクション(“m=”)を用意し、
そのセクションごとに”a=mid:audio”/”a=mid:video”を設定していました。
複数トラックを使用する場合も、該当の”m=”内に情報を記述します。
a=group:BUNDLE audio video
…
m=audio xx UDP/TLS/RTP/SAVPF xxx xxx xxx xxx
a=mid:audio
…
m=video xx UDP/TLS/RTP/SAVPF xxx xxx xxx xxx
a=mid:video
…
一方、Unified Planの場合、トラックごとに”m=”を用意し、
a=midには(任意の)ユニークな文字列を設定します。(RFC5888上、文字列長の上限は規定なし)
複数トラックを使用する場合は、トラックの数だけ”m=”を記述します。
a=group:BUNDLE 0 1
…
m=audio x UDP/TLS/RTP/SAVPF xxx xxx xxx xxx
a=mid:0
…
m=video x UDP/TLS/RTP/SAVPF xxx xxx xxx xxx
a=mid:1
…
Unified Planでは、この”m=”単位にRTCRtpTransceiverが生成され、セッション管理を行います。
RTCRtpTransceiverでは、送信トラック(RTCRtpSender)と受信トラック(RTCRtpReceiver)とで
対で管理され、SDP上の”a=mid”の値が、RTCRtpTransceiver.midと一致します。
RTCPeerConnectionに対してaddTrack()すると、RTCRtpTransceiver(”m=”)が生成され、
同時にRTCRtpSenderに追加したトラックが登録されます。
相手側のトラック追加は、RTCPeerConnection.ontrackで検出できます。
送信トラックを変更したい場合は、RTCRtpSenderに対してreplaceTrack()をすると、
同じmid(”m=”)を使用したまま送信トラックを付け替えることができます。
// トラック(m=)を追加する
pc.addTrack( new_track );
// トラックを付け替える
var sender = pc.getSenders().find(function(sdr){
return sdr.track.kind == new_track.kind;
});
sender.replaceTrack( new_track );
不必要にaddTrack()をすると、その分SDPのサイズが膨れ上がっていきますので、
送信する映像を切り替えるような場合は、replaceTrack()を使用するのが良いでしょう。
[2] direction
RTCRtpTransceiver.directionを指定してネゴすることで、
通話相手に送信方向の要望を伝えることができます。
一時的に映像の送信を止めたい場合、また再開したい場合などに使用できます。
- sendrecv
- sendonly
- recvonly
- inactive
a=group:BUNDLE 0 1
…
m=audio x UDP/TLS/RTP/SAVPF xxx xxx xxx xxx
a=mid:0
a=sendrecv ←コレ
…
m=video x UDP/TLS/RTP/SAVPF xxx xxx xxx xxx
a=mid:1
a=recvonly ←コレ
…
ネゴシエーションの結果(相手の応答結果)はRTCRtpTransceiver.currentDirectionに格納されます。
[3] 不要になったメディア
Unified Planでは、セッションの途中で不要になったメディアがあっても、
(例: ビデオ通話から音声通話に切り替わった場合など)
途中で”m=”セクションを削除することは禁止されています。
前述のdirectionを変更して、inactiveなメディアとして記述を維持します。
そのためUnified Planでは、リモート側のMediaStreamTrack.onended は発火しません。
代わりにMediaStreamTrack.onmuteによって変更を検知することができます。
なお、使用していないメディアは”a=group:BUNDLE”行から削除することができますが、
Offerで指定された”a=group:BUNDLE”行をAnswerで変更してはいけません。
a=group:BUNDLE 0
…
m=audio x UDP/TLS/RTP/SAVPF xxx xxx xxx xxx
a=mid:0
a=sendrecv
…
m=video 0 UDP/TLS/RTP/SAVPF
a=mid:1
a=inactive
RTCRtpTransceiverの説明書
Unified Planを扱うには、RTCRtpTransceiverの理解が重要です。
[1] Transceiverの作り方
RTCRtpTransceiverはaddTransceiver()またはaddTrack()で作成されます。
// 音声を受信するためのtransceiverを用意する
pc.addTransceiver('audio', {direction: 'recvonly'});
// 音声を送信するためにtransceiverを作成する
pc.addTrack( new_audio_track );
[2] MediaStreamの取り出し方
MediaStreamを取り出すには、まずRTCRtpTransceiverを参照する必要があります。
// transceiverを配列で取得する
var transceivers = pc.getTransceivers();
音声/映像の通信であれば、transceivers[0]にaudioトラック、
transceivers[1]にvideoトラックのTransceiverが格納されていたりします。
Transceiverの種別は、senderまたはreceiverのtrack.kindから判断できます。
Transceiverからsenderのトラックを集めてくれば、自身が送信しているMediaStreamを生成できます。
Transceiverからreceiverのトラックを集めてくれば、相手から受信しているMediaStreamを生成できます。
// MediaStreamを作成する
var local = new MediaStream;
var remote = new MediaStream;
pc.getTransceivers().forEach(function(trs){
local.addTrack(trs.sender.track);
remote.addTrack(trs.receiver.track);
});
[3] MediaStreamの切り替え方
以下の操作の後、再ネゴを走らせれば、MediaStreamを切り替えることができます。
replaceTrack()に関しては再ネゴせずとも出力は切り替えられますが、
再ネゴした方が、相手側で明示的に検出できるので良いかもしれません。
● 付け替える
// トラックを付け替える
var sender = pc.getSenders().find(function(sdr){
return sdr.track.kind == new_track.kind;
});
sender.replaceTrack( new_track );
● 一時的にON/OFFする
// directionを変更して送信を止める
var transceiver = pc.getTransceivers().find(function(trs){
return trs.sender.track.kind == kind;
});
transceiver.direction = 'recvonly';
まとめ
Unified Planを扱うには、RTCRtpTransceiverの理解が重要です。
APIを使用する分には、SDPまで意識することはないですが、
裏でどのようなやり取りが行われているかを把握することは、
APIを正しく使うことにもつながります。バックボーンを理解することは大事ですね。
早くも明日はAdvent Calendar最終日、kikuchi.sさん、sugita.mさんの
【チャットボットで業務効率化】申請テンプレートを提示するボットを30分でつくるです!
お楽しみに。