Edited at

FirefoxがWebRTCとgetUserMediaのPromise化に対応したので早速試してみる

More than 3 years have passed since last update.

動向そのものは、 @Y-NAKA さんのスライドWebRTC/ORTCの最新動向まるわかり!あたりをご覧頂くこととして。

W3CのWebRTC 1.0Media Capture and Streamsの仕様が更新されていて、メソッドの引数でsuccessとfailureのコールバックを渡していたものが、全てPromise形式に変更されているわけですが、Firefoxの最近のバージョンでは随時実装されてきているようなので、早速試してみました。

※ Chromeの状況、どなたか教えて下さい^^


まずは、いわゆるgetUserMedia()

まずは、カメラ映像のキャプチャやマイク音声の入力に使う、毎度おなじみnavigator.mozGetUserMedia()のPromise形式への移行です。

Firefox 36から、navigator.mozGetUserMedia()の置き換えとして、navigator.mediaDevices.getUserMedia()が実装されています。前者の引数はこれまで通りですが、後者はPromise形式で定義されています。具体的には、次のような感じです。


これまでのgetUserMedia()の場合

navigator.mozGetUserMedia(

{ video: true, audio: true }, successCallback, failureCallback);

 ↓


Promise形式になったgetUserMedia()の場合

navigator.mediaDevices.getUserMedia(

{ video: true, audio: true }).then(successCallback, failureCallback);


ビデオキャプチャの解像度が指定可能に

Firefox 38 (執筆時点ではBeta)では、新旧両方のAPIで、ようやくカメラ映像のキャプチャで入力解像度の指定が可能になりました。但し、Chrome等に現在実装されているものとは指定方法が異なりますので、注意が必要です。

まずは、入力解像度として1280×720を指定する方法を例として示します。


解像度指定・例(1)

navigator.mediaDevices.getUserMedia({

video: { width: 1280, height: 720 },
audio: true
}).then(successCallback, failureCallback);

さらに、対応していれば1280×720かそれ以上、対応していなければ640✕480かそれ以上を入力解像度として指定、といったような複数の条件で指定することも可能となっています。


解像度指定・例(2)

navigator.mediaDevices.getUserMedia({

video: {
width: { min: 640 }, height: { min: 480 },
advanced: [
{ width: { min: 1280 }, height: { min: 720 } }
]
},
audio: true
}).then(successCallback, failureCallback);

なお、他にも、フレームレート(frameRate)等も指定できるようですが、こちらでは今のところ動作確認が取れていません。


補足: mozSrcObjectについて

上記において、successCallbackには引数としてMediaStreamオブジェクトが渡されて、その後の実装として、一般的には<video>タグや<audio>タグのsrc属性にURL.createObjectURL(MediaStream)を指定するわけですが、最近のバージョンのFirefoxでは、mozSrcObject属性を用いて次のように書くことが出来ます。


mozSrcObjectの例

navigator.mediaDevices.getUserMedia({

video: true, audio: true
}).then(function(stream) {
var v = document.createElement('video');
v.mozSrcObject = stream;
});


入力デバイスの一覧を取得可能に

さらに、Firefox 39 (執筆時点ではDeveloper Edition (aurora))では、入力デバイスの一覧を取得するnavigator.mediaDevices.enumerateDevices()も実装されています。


入力デバイスの一覧を取得する例

navigator.mediaDevices.enumerateDevices().then(successCallback, failureCallback);


取得に成功すると、successCallbackには引数としてMediaDeviceInfoの配列(正確にはsequence)が渡されます。これによって、ビデオ入力(カメラ)と音声入力のデバイスがそれぞれいくつ搭載されているかが確認可能となります。MediaDeviceInfoの具体的な内容は次の通りです。なお、現状のFirefox Developer Editionの実装では、labelは空文字列となっています。


MediaDeviceInfoの内容

{

deviceId: (デバイスID),
groupId: (グループID),
kind: ("videoinput', "audiooutput", "audioinput"のいずれか),
label: (デバイスの具体的な名前)
}

W3Cの仕様上は、getUserMedia()に指定する制約条件(width, heightなど)に、deviceId等を指定することも可能であるため、スクリプト側から入力デバイスの指定が可能になるはずですが、Firefoxでは、依然としてgetUserMedia()実行時にユーザにデバイスを選択させる吹き出しが表示されるため、有効に機能しないように見受けられます。

※ 正しい使い方をご存じの方がいらっしゃいましたら、ご指摘下さると非常に助かります!


WebRTCのPromise対応

Firefox 38 (執筆時点ではBeta)では、WebRTCのmozRTCPeerConnectionでコールバックを引数とする各メソッドが、Promise形式にも対応するようになっています。互換性維持のため、旧来のコールバック引数形式でも使えます。


従来形式(createOffer()の例)

peerConnection.createOffer(successCallback, failureCallback, options);


 ↓


Promise形式(createOffer()の例)

peerConnection.createOffer(options).then(successCallback, failureCallback);


新たにPromise形式に対応したメソッドは、createOffer(), createAnswer(), setLocalDescription(), setRemoteDescription(), addIceCandidate()の5つとなります。


補足: createOffer()のオプションの仕様変更

W3CのWebRTC 1.0の仕様において、createOffer()で指定するオプションのフォーマットが変更されており、ブラウザ側の実装としては、Firefox 33以降が対応しています。


createOffer()旧オプションの例

peerConnection.createOffer(

successCallback,
failureCallback,
{
mandatory: {
OfferToReceiveVideo: true,
OfferToReceiveAudio: true
}
}
);

 ↓


createOffer()新オプションの例

peerConnection.createOffer({

offerToReceiveVideo: true,
offerToReceiveAudio: true
}).then(successCallback, failureCallback);

なお、現状では、新オプションをChrome等で試すと、エラーとなってしまうので、注意が必要です。旧オプションをFirefox 33以降で試すと、エラーにはならないものの、旧オプションが廃止される予定とコンソールに警告が出ます。


ところで、

ふと思うのですが、ブラウザがPromise形式のメソッドに対応しているかどうかは、どのようにしてチェックするのだろうか、という気がします。例えば、

try {

peerConnection.createOffer({ offerToReceiveAudio: true }).then(success, failure);
} catch(e) {
peerConnection.createOffer(success, failure, { offerToReceiveAudio: true });
}

とでもするしかないのでしょうか... (もっとエレガントな方法があれば、教えてください!)