Help us understand the problem. What is going on with this article?

Safari TP 34では新しい片方向WebRTCの手順が使えるようになったので、試してみた

More than 3 years have passed since last update.

まえがき

WebRTC APIのアップデートの概要は、@yusuke84さんの記事「WebRTC Update 2016 Summer」が参考になります。

本題

WebRTCのブラウザ実装としては最後発となるSafariですが、Safari Technology Preview(以下、Safari TP) 34のリリースノートを読むと、

  • Added support for receive-only SDP offers through addTransceiver

と書かれています。意外なことに、ChromeやFirefox、Edgeを出し抜いて、です。

RTCRtpTransceiver

現在のWebRTCのAPI仕様では、ストリームの入出力をより自由に扱えるよう、

  • ストリームの入力とコーデックのパラメータ設定等を扱うRTCRtpSender
  • 受信ストリームを扱うRTCRtpReceiver

が設けられています。Safari TP 34では、これら2つをまとめて送受信を管理するRTCRtpTransceiverを備えています。

Before RTCRtpTransceiver

現在のChromeやFirefox等では、片方向に音声・映像を送受信する場合は、RTCPeerConnectionにマイクやカメラのMediaStreamTrackaddTrackで追加して、createOffercreateAnswerofferToReceiveVideoofferToReceiveAnswerといったオプションを使う必要がありました。例えば、Offerer側がビデオを送信するのみで受信しないようにしたい場合は、次のような感じのコードになります。

const pc = new RTCPeerConnection({iceServers: [/* 中略 */]});
navigator.mediaDevices.getUserMedia({video: true}).then(stream => {
  pc.addTrack(stream.getVideoTracks()[0]);
  return pc.createOffer({offerToReceiveVideo: false});
}).then(sdp => {
  return pc.setLocalDescription(sdp);
}).then(() => {
  // ここで、Answerer側にsdpをwebサーバ経由で送信
});

この方法の場合、全オーディオトラック、全ビデオトラックまとめて受信の有無を設定することになります。なお、現在のWebRTCの仕様では、これらのオプションはLegacy Interface Extensionsという扱いになっています。

After RTCRtpTransceiver

RTCRtpTransceiverを使うと、片方向通信の扱い方が次のように変わります。まず、RTCRtpTransceiverは次のような手順で作成します。

RTCRtpTransceiverの作り方(その1)
navigator.mediaDevices.getUserMedia({video: true}).then(stream => {
  const pc = new RTCPeerConnection({iceServers: [/* 中略 */]});
  const transceiver = pc.addTransceiver(stream.getVideoTracks()[0]);
})
RTCRtpTransceiverの作り方(その2)
const pc = new RTCPeerConnection({iceServers: [/* 中略 */]});
const transceiver = pc.addTransceiver('video'); // 引数は'video'もしくは'audio'

作成されたRTCRtpTransceiverには、senderreceiverの2つの属性が設定され、送信(入力)・受信それぞれのMediaStreamTrackをコントロールできるようになっています。

ここで、次のようにすることで、トラック単位で、送信のみ、受信のみを設定することが可能です。

送信のみの例
const transceiver = pc.addTransceiver(stream.getVideoTracks()[0]);
transceiver.receiver.track.enabled = false;
transceiver.setDirection('sendonly');
受信のみの例
const transceiver = pc.addTransceiver('video');
transceiver.setDirection('recvonly');

この後で、pc.createOffer()pc.createAnswer()を実行した時に生成されるSDPに、受信する内容が反映されます。次のSDPの例では、確かにa=recvonlyとなっているのが確認できます。

v=0
o=- 4056205042783684755 3 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE video
a=msid-semantic: WMS
m=video 9 UDP/TLS/RTP/SAVPF 96 98 99 97 100
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:x4Xi
a=ice-pwd:pqIcVaMdsF/jXsud1MpqZRKz
a=fingerprint:sha-256 B8:0E:77:89:95:23:2B:0D:A9:75:60:8A:CA:A7:21:CA:3B:F9:11:0F:CB:C8:ED:A5:B5:46:B0:71:16:BF:F8:58
a=setup:actpass
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 urn:3gpp:video-orientation
a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=recvonly
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 red/90000
a=rtpmap:98 ulpfec/90000
a=rtpmap:99 H264/90000
a=rtcp-fb:99 ccm fir
a=rtcp-fb:99 nack
a=rtcp-fb:99 nack pli
a=rtcp-fb:99 goog-remb
a=rtcp-fb:99 transport-cc
a=fmtp:99 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:100 rtx/90000
a=fmtp:100 apt=99

ちなみに、sendonlyで全く受信しないRTCPeerConnectionでSDPを生成すると、次のような結果になります。全く受信を行わないので、当然ながら受信に関するIPアドレスやポートの情報が全く追加されていないのが確認できます。

v=0
o=- 3443265369116888093 2 IN IP4 127.0.0.1
s=-
t=0 0
a=msid-semantic: WMS
tomoyukilabs
Qiitaでは今のところ、主にWeb標準関連の記事を書いております。
https://github.com/tomoyukilabs
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away