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

## まえがき

WebRTC APIのアップデートの概要は、@yusuke84さんの記事「[WebRTC Update 2016 Summer](http://qiita.com/yusuke84/items/7c0d7556228caff6cf71)」が参考になります。

## 本題

WebRTCのブラウザ実装としては最後発となるSafariですが、[Safari Technology Preview](https://developer.apple.com/safari/technology-preview/release-notes/)(以下、Safari TP) 34のリリースノートを読むと、

> * Added support for receive-only SDP offers through `addTransceiver`

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

## RTCRtpTransceiver

現在の[WebRTC](https://w3c.github.io/webrtc-pc/)のAPI仕様では、ストリームの入出力をより自由に扱えるよう、

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

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

### Before `RTCRtpTransceiver`

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

```js
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](http://w3c.github.io/webrtc-pc/#legacy-interface-extensions)という扱いになっています。

### After `RTCRtpTransceiver`

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

```js:RTCRtpTransceiverの作り方(その1)
navigator.mediaDevices.getUserMedia({video: true}).then(stream => {
  const pc = new RTCPeerConnection({iceServers: [/* 中略 */]});
  const transceiver = pc.addTransceiver(stream.getVideoTracks()[0]);
})
```

```js:RTCRtpTransceiverの作り方(その2)
const pc = new RTCPeerConnection({iceServers: [/* 中略 */]});
const transceiver = pc.addTransceiver('video'); // 引数は'video'もしくは'audio'
```

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

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

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

```js:受信のみの例
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
```