前回に引き続き今回は”Query media devices”です。サンプルは2つ。
(1) Choose camera, microphone and speaker
このHTMLを実行するとPCの、”Audio input”、”Audio output”、”Video source”を見つけてプルダウンリストに登録し映像の再生を行います。別になっているjava script fileをサンプルのHTMLに取り込んで見ました。
sample01.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Select audio and video sources</title>
<style>
div.select {
display: inline-block;
margin: 0 0 1em 0;
}
p.small {
font-size: 0.7em;
}
label {
width: 12em;
display: inline-block;
}
video {
background: #222;
margin: 0 0 20px 0;
max-width: 60em;
--width: 100%;
width: var(--width);
height: calc(var(--width) * 0.75);
}
</style>
</head>
<body>
<div id="container">
<h1>WebRTC samples Select sources & outputs</h1>
<div class="select">
<label for="audioSource">Audio input source: </label><select id="audioSource"></select>
</div>
<div class="select">
<label for="audioOutput">Audio output destination: </label><select id="audioOutput"></select>
</div>
<div class="select">
<label for="videoSource">Video source: </label><select id="videoSource"></select>
</div>
<video id="video" playsinline autoplay></video>
</div>
<script>
'use strict';
const videoElement = document.querySelector('video');
const audioInputSelect = document.querySelector('select#audioSource');
const audioOutputSelect = document.querySelector('select#audioOutput');
const videoSelect = document.querySelector('select#videoSource');
const selectors = [audioInputSelect, audioOutputSelect, videoSelect];
audioOutputSelect.disabled = !('sinkId' in HTMLMediaElement.prototype);
function gotDevices(deviceInfos)
{
// Handles being called several times to update labels. Preserve values.
const values = selectors.map(select => select.value);
selectors.forEach(select =>
{
while (select.firstChild) select.removeChild(select.firstChild);
});
for (let i = 0; i !== deviceInfos.length; ++i)
{
const deviceInfo = deviceInfos[i];
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'audioinput')
{
option.text = deviceInfo.label || `microphone ${audioInputSelect.length + 1}`;
audioInputSelect.appendChild(option);
}
else if (deviceInfo.kind === 'audiooutput')
{
option.text = deviceInfo.label || `speaker ${audioOutputSelect.length + 1}`;
audioOutputSelect.appendChild(option);
}
else if (deviceInfo.kind === 'videoinput')
{
option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
videoSelect.appendChild(option);
}
else console.log('Some other kind of source/device: ', deviceInfo);
}
selectors.forEach((select, selectorIndex) =>
{
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex]))
select.value = values[selectorIndex];
});
}
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
// Attach audio output device to video element using device/sink ID.
function attachSinkId(element, sinkId)
{
if (typeof element.sinkId !== 'undefined')
{
element.setSinkId(sinkId)
.then(() =>
{
console.log(`Success, audio output device attached: ${sinkId}`);
})
.catch(error =>
{
let errorMessage = error;
if (error.name === 'SecurityError')
errorMessage = `You need to use HTTPS for selecting audio output device: ${error}`;
console.error(errorMessage);
// Jump back to first output device in the list as it's the default.
audioOutputSelect.selectedIndex = 0;
});
}
else console.warn('Browser does not support output device selection.');
}
function changeAudioDestination()
{
const audioDestination = audioOutputSelect.value;
attachSinkId(videoElement, audioDestination);
}
function gotStream(stream)
{
window.stream = stream; // make stream available to console
videoElement.srcObject = stream;
// Refresh button list in case labels have become available
return navigator.mediaDevices.enumerateDevices();
}
function handleError(error)
{
console.log('navigator.MediaDevices.getUserMedia error: ', error.message, error.name);
}
function start()
{
if (window.stream)
{
window.stream.getTracks().forEach(track =>
{
track.stop();
});
}
const audioSource = audioInputSelect.value;
const videoSource = videoSelect.value;
const constraints =
{
audio: {deviceId: audioSource ? {exact: audioSource} : undefined},
video: {deviceId: videoSource ? {exact: videoSource} : undefined}
};
navigator.mediaDevices.getUserMedia(constraints).then(gotStream).then(gotDevices).catch(handleError);
}
audioInputSelect.onchange = start;
audioOutputSelect.onchange = changeAudioDestination;
videoSelect.onchange = start;
start();
</script>
</body>
</html>
このコードでキーになるのは、101行の navigator.mediaDevices.enumerateDevices()関数です。この関数は使用可能なオーディオ入力デバイス(マイク)、 ビデオ入力デバイス(カメラ)、 オーディオ出力デバイス(スピーカー)を検出する関数です。
- 101行: navigator.mediaDevices.enumerateDevices()
- まずはこの関数が実行されます
- デバイスが検出出来れば ”gotDevices”。出来なければ ”handleError” を実行
- 62行: gotDevices(deviceInfos)
- 検出されたデバイスをここでHTMLに登録します。”deviceInfos”がデバイスの情報アレイです。
- 71−92行: ”deviceInfos”の情報を”audioSource”、”audioOutput”、”videoSource”
- 各プルダウンリストに登録。
- 94−98行: リストの一番最初の項目を選択。
- 検出されたデバイスをここでHTMLに登録します。”deviceInfos”がデバイスの情報アレイです。
- 171行: start();
- デバイス検出後、ここが実行されます。
- 163行: プルダウンリストを元に引数を決定して、navigator.mediaDevices.getUserMedia()を実行
- 正常終了すれば、”gotStream”。”gotStream”が正常終了すれば、”gotDevices”を実行。
- エラーがでると、”handleError”を実行
- 後はこれの繰り返し。
(2) Choose media source and audio output
注記に、”This demo must be run from localhost or over HTTPS Chrome 49 or later, Firefox is not supported yet.”と有りました。今回はブラウザがFirefoxなのでスキップします。