第 5 章 STUN/TURN サーバーの構築 Ubuntu の場合の起動方法 ▽リスト 5.3 /etc/default/coturn トアウトを解除します。 Ubuntu では、まず /etc/default/coturn を編集し、 TURNSERVER ENABLED のコメン # Uncomment it if you want tO have the turnserver running as # an automatic system service daemon $ sudO service coturn restart ( 再起動 ) $ sudO service COturn StOP ( 停止 ) $ sudO service coturn start ( 起動 ) TURNSERVER_ENABLED=I それから、 service コマンドでサービスの起動・停止・再起動を行います。 自動起動の設定についてはインストールと同時に行われているので特に何かする必要は アプリ側でこの STUN サーバーを利用するように設定を変更してみましよう。 アプリで STUN サーバを使うように指定する ないですが、変更する場合は sysv-rc-conf コマンドなどを利用します。 RTCPeerConnection のコンストラクター引数 iceservers に今回作ったサーバーを iceservers : [ let pc = new RTCPeerConnection({ ▽リスト 5.4 STUN サーバーを℃ E サーバーリストに設定する 指定します ( リスト 5.4 ) 。 ' stun : 198.51.100.135 : 3478 ' } {urls : / / ↓複数の場合は配列で指定する //{urls: [ ' stu Ⅱ : 198 . 51.100.135 : 3478 ' iceTransportP01icy: ' a11 ' ' stun : stu Ⅱ .1 ・ google.com : 19302 ' ] } STUN サーバーを利用して P2P 接続できるか確認するときは、通信する 2 台の端末が 58
第 2 章メディアストリームを取得する・・・ Media Capture API リスト 2.3 width/height の指定 v 表 2.3 条件指定で利用するオブジェクトの要素 指定できます。 値は数値をそのまま指定するほかに、表 2.3 のような要素をもっオプジェクトの形でも height : 720 width: 1280 , video: { 1et mediaStream = await navigator. mediaDevices . getUserMedia( 要素 max ideal exact 概要 最小値 最大値 理想値 完全一致 このうち、 min 、 max および ideal の 3 つはそれぞれ同時に組み合わせて指定できます が、 exact は他の項目と同時には指定できません。なお、オプジェクトの形とせず数値を そのまま指定した場合は、 ideal のみ指定した場合と同じ意味になります。これ以降の項 目も、特に注記のない限りすべてこの形式での指定が可能です。 リスト 2.4 width/height の指定 ( オブジェクトの形で指定 ) await navigator. mediaDevices. getUserMedia ( 1et mediaStream video: { width: {min: 1280 } , height : {min: 720 } リスト 2.4 のように指定すると、「最低でも 1 , 280 以上 x 720 以上サイズの映像を取得 したい」という意味になります。 Web カメラの性能が低く 640 x 480 までしか対応して いない場合、条件に合うストリームを取得できないため工ラーになります。あまり厳しす ぎる条件を指定するのはやめておいたほうがよいでしよう。 •ideal の挙動リスト 2.5 は min と ideal を同時に組み合わせ、「最低でも 1 , 280 x 720 以上で、なるべく 1 , 500 x 1 , 000 に近いサイズの映像を取得したい」と指定しています。 26
2.2 メディアストリームの条件を指定する マリスト 2.5 デバイスが対応していない値を ide で指定 1et mediaStream = await navigator. mediaDevices. getUserMedia( video : { width: {min: 1280 , ideal : 1500 } , height : {min: 720 , ideal : 1000 } たとえばスペックの低い環境のためにフレームレートを最大川 . ()fps までに制限するに 映像のフレームレートをフレーム毎秒 (fps) で指定します。 frameRate aspectRatio: {exact : 1 .7777777778 } / / アスペクト比 16 : 9 video : { 1et mediaStream = await navigator. mediaDevices . getUserMedia( v リスト 2.6 アスペクト比の指定 在、 Firefox はこの項目に対応していません。 4 : 3 なら 1 .3333333333 、 16 : 9 なら 1 .7777777778 となります * 7 ( リスト 2.6 ) 。執筆時現 ある " 4 : 3 " や " 16 : 9 " といった表記ではなく、縦を 1 としたときの横の長さです。つまり 映像の縦横比 ( アスペクト比 ) を指定します。ただしその指定方法は一般的に馴染みの aspectRatio るようにクロップしているようです。 けは挙動が異なり、指定サイズよりも大きな映像を取得した上で指定サイズびったりにな が勝手に選択するか、あるいは選択できなければ工ラーとなります。ただし、 Chrome だ ideal で指定した場合、その値に近いもの ( この場合は 1 , 280 x 720 など ) をプラウザー いため、ほとんどの Web カメラでは対応していません。デバイスが対応していない値を しかし、 1 , 500 x 1 , 000 というサイズは画面や映像のサイズとして一般的なものではな め、この表記となります。 は、リスト 2.7 のように指定します。 27 * 7 実際には永遠に 3 や 7 が続く循環小数ですが、小数は小数点第 10 位で四捨五人すると規定されているた
5.3 TURN サーバーを構築する turnadmin コマンドでユーザーを登録するには次のように指定します。オプションの一 るので、直接 SQL 文を利用する必要はありません。 min コマンドを使います。スキーマの管理も含めてすべてこのコマンドが面倒を見てくれ DB でユーザー情報を管理するときは、先ほどパスワードの変換にも利用した turnad 能です。 Redis にも対応しています。ューザー情報の管理に利用する DB は設定ファイルで変更可 デフォルトでは SQLite を利用しますが、それ以外に MySQL 、 PostgreSQL 、 MongoDB 、 DB に登録されているユーザーを確認するには一 1 オプションを使います。 $ sud0 turnadmin —a —u username —r example ・ jp —p pa5Sw0rd k が一 a に変わっただけですね。 ューザを削除するときは -d オプションを使い、削除したいユーザのユーザ名と realm username [example ・ jp] $ turnadmin ー 1 を指定します。 $ sudo turnadmin —d —u username —r example ・ jp アプリで TURN サーバーを使うように指定する 63 'turn: 198.51.100.135:3478?transport=tcp' 'turn: 198.51.100.135:3478?transport=udp' urls : [ ' stun : 198.51.100.135 : 3478 ' } , {urls : iceservers: [ let pc = new RTCPeerConnection({ マリスト 5.12 TURN サーバーをリストに追加 ワードも指定する必要があります ( リスト 5.12 ) 。 していましたが、 TURN サーバーではそれに加えて user Ⅱ e と credential つまりパス サーバーのリストに TURN サーバーを追加します。 STUN サーバーでは urls だけ指定 STUN サーバーのときと同様、 RTCPeerConnection のコンストラクターに渡す ICE
第 5 章 STUN/TURN サーバーの構築 方法です。設定ファイルにリスト 5.10 のような形式でユーザー情報を指定します。 まずひとつめは、すでに編集している設定ファイルに直接ューザー情報を書いてしまう 設定ファイルで管理する 理する方法の 2 つがあります。 ューザー情報の管理は大きく分けて、設定ファイルで静的に管理する方法と、 DB で管 が必要となるようにしました。今度はその認証に必要なユーザー情報を登録しましよう。 TURN サーバー用の設定時に lt-cred-mech のコメントアウトを外し、ユーザー認証 TURN サーバー用のユーザー情報を管理する ようにしたほうがよいでしよう。 りません。 tur Ⅱ a 面 i Ⅱコマンドを使ってパスワードを " キー " に変換し、これを書き込む ただこれではパスワードを平文で設定ファイルに書き込むことになり、セキュアではあ user=username : pa5SwOrd # user=[ ユーザ名 ] : [ パスワード ] ▽リスト 5.10 パスワードを平文で書く場合 0X3199a25ddd6fe8ffba284e771Cfa30f2 $ turnadmin —k —u username —r example ・ jp -p pa5Sw0rd ー u にはユーザ名、一 r には設定ファイルで指定した realm の値、一 p にはパスワードを指 62 すので、実際に運用するときは DB でユーザー情報を管理することをお勧めします。 ザー情報の変更を反映させるのにサーバーの再起動が必要になるなどデメリットもありま 設定ファイルに直接ューザー情報を書き込む方法はお手軽ではあります。しかし、ユー DB で管理する user=username : 0X3199a25ddd6fe8ffba284e771Cfa30f2 # use て = [ ユーザ名 ] : [turnadmin コマンドで生成したキー ] マリスト 5.11 バスワードのかわりにキーを書く場合 できます。 定します。リスト 5.11 のように、パスワードを平文で書くのとほとんど同じように指定
第 2 章メディアストリームを取得する・・・ Media Capture API ▽リスト 27 フレームレートの指定 environment facingMode : ' video: { 1et mediaStream = await navigator. mediaDevices . getUserMedia( リスト 2.8 インカメラ・アウトカメラの指定 らで撮影するかを指定する場合に使います。 モバイル端末にはたいていイン・アウト 2 つのカメラが搭載されていますが、そのどち facingMode frameRate : {max: 10 . 0 } , video: { Iet mediaStream = await navigator. mediaDevices . getUserMedia( つで、 left と right は対応している端末が少ないので気にしなくも大丈夫です。 指定できる値は表 2.4 のとおりです。このうちよく使うのは user と environment の 2 値 environment left r i ght マ表 2.4 facingM0de に指定できる値 意味 インカメラ ( フロントカメラ、画面側 ) アウトカメラ ( バックカメラ、背面側 ) 左側カメラ 右側カメラ デフォルト 〇 デフォルト値は user 、つまりインカメラとなりますが、 environment を指定すればア ウトカメラで撮影できます。このとき、 exact で指定すると、アウトカメラが存在しない 端末では取得に失敗します。 また、デスクトップ環境ではプラウザーと OS 、 Web カメラの内蔵・外付けの組み合わ せにより挙動が異なります。筆者の環境では、 user 指定で内蔵インカメラの映像が取得 され、その他の値を指定すると取得できないという想定どおりの挙動をしたのは Edge と Mac 版 Firefox だけです。 Chrome や Mac 以外の Firefox では何を指定しても取得に失 28
2.2 メディアストリームの条件を指定する ▽表 2.5 デスクトップ環境で facingMode を指定した場合の挙動 ( 筆者調べ ) た。また、 Safari 以外のプラウザーでは外付けカメラの映像は取得できませんでした。 敗し、逆に Safari ではカメラの内蔵・外付けや指定内容にかかわらず取得に成功しまし カメラ Firefox 56 Chrome 61 Edge 41 Safari 11 内蔵 外付け Mac のみ user で取得成功取得不可 取得不可 user で取得成功 常に取得成功 取得不可 取得不可 常に取得成功 該当するカメラのない端末やデスクトップ環境での取得失敗を防ぐには ideal で指定す るか、または faci Ⅱ gM 。 de は使用せずに後述のデバイスを直接指定する方法を使用するほ うがよいでしよう。 echoCanceIlation 音声にエコーキャンセラーをかけるかどうかを指定します。 この機能はデフォルトで有効になっています。ポイスチャットをする場合は有効のまま で問題ありませんが、音声処理により音質が低下するので音質を重視したい場合は無効化 するとよいでしよう。 v リスト 2.9 工コーキャンセラーを無効化 1et mediaStream = await navigator. mediaDevices ・ getUserMedia( audio : { echoCance11ation: false channeICount これはステレオマイクやライン人力などステレオソースの音声を、ステレオのまま取得 するかモノラルとして取得するかを指定するもの・・・ではなく、ソースが対応しているチャ ンネル数を指定するものです * 8 。リスト 2.10 のように { exact : 2 } を指定すると、人力 ソースをステレオ対応のものに限定できます。 なお、ステレオ音声を人力する場合は echocancellation に false を指定してエコー キャンセラーを切っておくことを推奨します。 Chrome ではエコーキャンセラーが有効の ステレオ音声のモノラル化は Web Audio API (https : //www.w3.org/TR/webaudio/) で可能です。 * 8 29
3.3 シグナリングのシーケンス / / ※ remoteVideoE1em は映像の再生を行う video 要素 'undefined ' ) { if (typeof pc . ontrack ! = / / 新仕様 pc . ontrack = (event) = > { if (event . track . kind = = 'video'){ remoteVideoE1em. srcObj ect = event. streams [ 0 ] ; }else{ / / 旧仕様 (event) = > { pc . onaddstream remoteVideoE1em. srcObject = event . stream; 先述のとおり、シグナリングサーバーの仕様は開発者が自由に実装できます。 リスト リスト 3.5 SDP の例 その理由を説明するため、とりあえず SDP の中身を見てみましよう。 このような一見冗長にみえる手順を踏む必要があるのでしようか。 点で LocaIDescription の設定まで自動的に済ませてくれてもいいように思えます。なぜ 定しています。わざわざこのような手順を踏まなくても、最初のメソッドを呼び出した時 生成した SDP を、再び setLoca1Description メソッドに渡して LocalDescription に設 ところでセッション情報の交換の手順では、 createOffer/createAnswer メソッドで SDP についてちょっと詳しく WebSocket を使うかも含め、このとおりに実装する必要はありません。 3.4 では WebSocket を利用していますが、これはあくまでもひとつの例であり、そもそも v=0 0 = mozilla . t=0 0 . THIS_IS_SDPARTA-52.0.1 6105063152592429256 0 IN IP4 0 . 0 . 0 . 0 a=fingerprint : sha-256 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 : 00 43 a=ice—pwd:4f996da2dd42eeb2e2b6370cba016f34 a=fmtp: 101 0 ー 15 a=fmtp : 109 maxp1aybackrate=48000 ; stereo=l ; useinbandfec=l a=extmap: l/sendonly urn: ietf :params:rtp-hdrext : ssrc-audio—level a=sendrecv c=IN IP4 0 . 0 . 0 . 0 m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101 a=msid—semantic : WMS * a=ice—options :trickle a=group : BUNDLE sdparta_O sdparta-l
その他のデバイスでも同様に 結果となります。 順番を入れ替えた場合 Y 表 2.6 マ表 27 第 1 条件 1 , 920 >< 1 , 080 第 2 条件 1 , 280 >< 720 1 , 92 ( ) >< 1 , 080 1 , 280 x 720 640 X 480 320 X 240 1 , 280 X 720 2.2 メディアストリームの条件を指定する デノヾイス D ~ 15.0fps ~ 15.0fps デバイスごとの対応画面サイズとフレームレート デバイス A デバイス B ~ 30.0fps ~ 30.0fps ~ 60. Ofps ~ 60. ()fps ~ 15.0fps ~ 30.0fps ~ 30.0fps ~ 30.0fps デバイス C ~ 15.0fps ~ 30.0fps ~ 30.0fps 各デバイス接続時の条件クリア状況と取得結果 第 3 条件 60.0fps 第 4 条件 30.0fps 第 5 条件 15.0fps 最低条件 640 ~ x 480 ~ 結果画面サイズ フレームレート デバイス A 30.0fps 1 , 920 X 1 , 080 〇 〇 〇 〇 〇 〇 デバイス B 〇 〇 15. Ofps 1 , 920 x 1 , 080 〇 〇 〇 デバイス C 〇 15.0fps 1 , 280 X 720 〇 〇 〇 デノヾイス D 〇 〇 640 X 480 15.0fps クリアした条件のうち優先順位が高いものが優先される さて、先ほどの条件を各要素の内容はそのままに えてみるとどうなるでしようか。 リスト 2.14 条件の順番を入れ替え リスト 2.14 のように順番を人れ替 1et mediaStream = await navigator. mediaDevices . getUserMedia( 33 {width: 1280 , height : 720 } / / 第 5 条件 {width: 1920 , height : 1080 } , / / 第 4 条件 {frameRate : 15 . 0 } , / / 第 3 条件フレームレート 15. {frameRate : 30.0 } , / / 第 2 条件フレームレート 30. {frameRate : 60.0 } , / / 第 1 条件フレームレート 60 advanced : [ height : {min: 480 } , width: {min: 640 } , / / 最低条件 640 以上 x 480 以上 video : { 1 , 920 X 1 , 080 Ofps Ofps . Ofps
6.6 大きなデータを送るには 75 として信頼性のあるモードを利用すべきです。 タを復元できなくなってしまいます。このようにデータの欠落が許されない場合は、原則 チャンクの並び順が正しくなかったり、抜けているチャンクがあったりすると元のデー データを復元する処理を行っています。 を確認し、 0 であれば受信を継続、 1 であればこれまで受け取ったチャンクをまとめ、元の でデータの終了を示すようにしています。受信側は受け取ったチャンクの先頭 1 バイト 6.6 の例では先頭 1 バイトを終了フラグとして利用し、最後のチャンクのみ 1 とすること を示しますが、データチャネルでは 0 バイトのデータは送信できません。そこで、リスト コーディングの場合は、最後に長さ 0 バイトの空データを転送することでデータの終わり データがあるのかを受信側に知らせる必要があります。 HTTP/ 1.1 のチャンク転送工ン データをチャンクに分割して送信した場合、データの転送が完了したのかまだ続きの