通過瀏覽器獲取麥克風或相機等媒體的使用許可權

G_Owen 發表於2019-08-13

概覽

mediaDevices 是 Navigator 物件的只讀屬性,一個單列物件,可以連線訪問相機和麥克風,螢幕共享等媒體輸入裝置

方法

enumerateDevices

請求一個可用的媒體輸入和輸出裝置列表,如麥克風、相機、耳機等。返回的 Promise完成狀態中是一個帶有 MediaDeviceInfo 的陣列

let mediaDevices = navigator.mediaDevices
if(!mediaDevices || !mediaDevices.enumerateDevices) return console.erorr('瀏覽器不支援enumerateDevices API')
navigator.mediaDevices.enumerateDevices()
.then((devices)=>{
    for (let device of devices){
        console.log(device.kind + ': ' +device.lable + ' id = '+ device.deviceId );
    }
})
.catch(err=>{
    console.error(err)
})

/*
audioinput: undefined id = default
audioinput: undefined id = communications
audioinput: undefined id = ac67d348685a08c75e5017f9a449b3d85f08dcb774c88ab95de82bbf2c0fc820
videoinput: undefined id = e41039bcfbc84d926a0b73cdc1d8b1daf3d67d36c62588202191d918fb076426
audiooutput: undefined id = default
audiooutput: undefined id = communications
audiooutput: undefined id = 015d73652e57bffb21679b937675d32c4d4a43862aba3774aaf0b5f1e983151f
*/

相容性

getSupportedConstraints

返回一個 MediaTrackSupportedConstraints 物件,其屬性都是客戶端所支援約束的屬性,值為 Boolean 型別

let supportedConstraints = navigator.mediaDevices.getSupportedConstraints()
for (let constraint of Object.keys(supportedConstraints)){
    console.log(constraint)
}
/*
aspectRatio
autoGainControl
brightness
channelCount
colorTemperature
contrast
deviceId
echoCancellation
exposureCompensation
exposureMode
exposureTime
facingMode
focusDistance
focusMode
frameRate
groupId
height
iso
latency
noiseSuppression
pointsOfInterest
resizeMode
sampleRate
sampleSize
saturation
sharpness
torch
volume
whiteBalanceMode
width
zoom
*/

相容性

getDisplayMedia

提示使用者選擇和授予許可權來捕獲顯示或部分的內容,(如分屏共享時分享哪一屏的內容)然後使用 medieaStream Recording API 記錄生成的 stream,或作為 webRTC 會話的一部分進行傳輸。

可以傳遞一個MediaStreamConstraints 物件指定返回要求的 mediaStream

async function startCapture(displayMediaOptions) {
  let captureStream = null;

  try {
    captureStream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
  } catch(err) {
    console.error("Error: " + err);
  }
  return captureStream;
}

相容性

getUserMedia

提示使用者給予使用媒體輸入的許可(如麥克風,攝像機),當媒體輸入時產生一個 mediaStream包含所請求的媒體型別的軌道。該流可以包括視訊軌道(攝像機,視訊記錄裝置,共屏等硬體或虛擬視訊流源)、音訊軌道(來自麥克風、A/D轉換器等硬體或虛擬音訊源),也可能是其它軌道型別

該方法返回一個Promise物件,成功時 resolve 回撥函式帶有mediaStream物件。如果使用者拒絕授予使用許可權,或是媒體源不可用,則返回 reject回撥

** Promise 可能既不會 resolve 也不會reject,因為使用者不必做出選擇,可能只是忽略請求**

// 想要獲取一個最接近 1280x720 的相機解析度
let constraints = { audio: true, video: { width: 1280, height: 720 } };

navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) {
  let video = document.querySelector('video');
  video.srcObject = mediaStream;
  video.onloadedmetadata = function(e) {
    video.play();
  };
})
.catch(function(err) { console.log(err.name + ": " + err.message); });

引數 constraints

一個mediaStreamConstraints物件指定請求的媒體型別和相對應引數,該物件包含 videoaudio 兩個屬性,必須一個或兩個同時被指定,如果無法找到指定的媒體型別或無法滿足對於的引數要求,Promise將返回 rejected

引數配置

1
 {audio:true,video:true}

屬性設定為 Truthy 則生成的stream 必須具有該型別的軌道,否則呼叫 getUserMedia會丟擲錯誤

2
{
  audio: true,
  video: { width: 1280, height: 720 }
}

表示video的解析度應為 1280x720 瀏覽器將試著滿足這個請求引數,如果無法滿足要求或選擇覆蓋,則可能返回其它的解析度

3
{
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}

此配置要求了最低解析度,如果達不到要求,promise 將返回 reject;還可配置 max、exact(min == max),而且使用者將不會得到要求授權的提示

4
{
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 776, ideal: 720, max: 1080 }
  }
}

如果使用ideal,瀏覽器將嘗試找到(如果相機有多個的話)最接近指定值的理想值的裝置或相機
意味著上方的第一個解析度例子可簡寫為:

{
  audio: true,
  video: {
    width: { ideal: 1280 },
    height: { ideal: 720 }
  }
}
5

並不是所有的 constraint 都說數字,如在移動裝置上優先使用前置相機

{ audio: true, video: { facingMode: "user" } }

強制使用後置相機

{ audio: true,
 video: {
  facingMode: { exact: "environment" }
   }
}

APP許可權配置

"permissions": {
  "audio-capture": {
    "description": "Required to capture audio using getUserMedia()"
  },
  "video-capture": {
    "description": "Required to capture video using getUserMedia()"
  }
}

作為可能涉及重大隱私問題的API,getUserMedia()規範規定了瀏覽器有義務滿足的各種隱私和安全要求。
getUserMedia()是一個強大的功能,只能在安全的環境中使用; 在不安全的情境中,navigator.mediaDevices 是undefined,阻止訪問getUserMedia()
簡而言之,安全上下文是使用HTTPSfile:///URL 方案載入的頁面,或者是從中載入的頁面localhost

在舊的瀏覽器中使用新的API

推薦使用處理了約束的 adapter.jspolyfill 來替代。



// 老的瀏覽器可能根本沒有實現 mediaDevices,所以我們可以先設定一個空的物件
let mediaDevices = navigator.mediaDevices
if (mediaDevices === undefined) {
  mediaDevices = {};
}
// 一些瀏覽器部分支援 mediaDevices。我們不能直接給物件設定 getUserMedia
// 因為這樣可能會覆蓋已有的屬性。這裡我們只會在沒有getUserMedia屬性的時候新增它。
if (mediaDevices.getUserMedia === undefined) {
  mediaDevices.getUserMedia = function(constraints) {

    // 首先,如果有getUserMedia的話,就獲得它
    var getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

    // 一些瀏覽器根本沒實現它 - 那麼就返回一個error到promise的reject來保持一個統一的介面
    if (!getUserMedia) {
      return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
    }

    // 否則,為老的navigator.getUserMedia方法包裹一個Promise
    return new Promise(function(resolve, reject) {
      getUserMedia.call(navigator, constraints, resolve, reject);
    });
  }
}
mediaDevices.getUserMedia({ audio: true, video: true })
.then(function(stream) {
  var video = document.querySelector('video');
  // 舊的瀏覽器可能沒有srcObject
  if ("srcObject" in video) {
    video.srcObject = stream;
  } else {
    // 防止在新的瀏覽器裡使用它,應為它已經不再支援了
    video.src = window.URL.createObjectURL(stream);
  }
  video.onloadedmetadata = function(e) {
    video.play();
  };
})
.catch(function(err) {
  console.log(err.name + ": " + err.message);
});

相容性