許多瀏覽器現在都能存取使用者的影片與音訊輸入。不過,視瀏覽器而定,這可能是完整的動態和內嵌體驗,也可以委派給使用者裝置上的其他應用程式。
循序漸進地開始
最簡單的做法是要求使用者提供預錄檔案。方法是建立簡單的檔案輸入元素,並新增 accept
篩選器,表示我們只接受影片檔案,以及指出要從相機中直接取得的 capture
屬性。
<input type="file" accept="video/*" capture />
這個方法適用於所有平台。在電腦上,系統會提示使用者從檔案系統上傳檔案 (忽略 capture
屬性)。在 iOS 版 Safari 中,系統會開啟「相機」應用程式,讓您錄製影片並傳回網頁;在 Android 上,則可在影片傳回網頁前,讓使用者選擇要使用哪個應用程式錄影。
許多行動裝置都配備多部攝影機。如果您有偏好,可以將 capture
屬性設為 user
(如果您希望鏡頭面向使用者);或者,如果想讓相機鏡頭朝外,則可將 environment
屬性設為 environment
。
<input type="file" accept="video/*" capture="user" />
<input type="file" accept="video/*" capture="environment" />
請注意,這只是提示;如果瀏覽器不支援該選項,或您要求的攝影機類型無法使用,瀏覽器可能會選擇其他攝影機。
使用者完成錄製並回到網站後,您必須透過某種方式保留檔案資料。如要快速存取,您可以將 onchange
事件附加至輸入元素,然後讀取事件物件的 files
屬性。
<input type="file" accept="video/*" capture="camera" id="recorder" />
<video id="player" controls></video>
<script>
var recorder = document.getElementById('recorder');
var player = document.getElementById('player');
recorder.addEventListener('change', function (e) {
var file = e.target.files[0];
// Do something with the video file.
player.src = URL.createObjectURL(file);
});
</script>
一旦取得檔案的存取權,就能對檔案執行任何操作。舉例來說,您可以執行下列操作:
- 將其直接附加至
<video>
元素,即可播放 - 下載到使用者的裝置
- 附加至
XMLHttpRequest
,即可上傳至伺服器 - 將影格繪製成畫布並套用濾鏡
雖然使用輸入元素方法取得影片資料的存取方法相當普遍,但這是最不具吸引力的選項。我們真的希望能使用相機 直接在頁面中提供良好的體驗
透過互動方式存取相機
新式瀏覽器可直接與攝影機相互整合,讓我們可以打造與網頁完全整合的使用體驗,使用者也絕對不會離開瀏覽器。
取得相機存取權
我們可以在 WebRTC 規格中,使用名為 getUserMedia()
的 API 直接存取相機。getUserMedia()
會提示使用者授予已連線麥克風和攝影機的存取權。
如果 API 成功,則會傳回 Stream
,其中包含相機或麥克風的資料,我們就可以將其附加至 <video>
元素、附加至 WebRTC 串流,或使用 MediaRecorder
API 儲存。
如要從相機取得資料,只需在傳遞至 getUserMedia()
API 的限制物件中設定 video: true
<video id="player" controls></video>
<script>
var player = document.getElementById('player');
var handleSuccess = function (stream) {
player.srcObject = stream;
};
navigator.mediaDevices
.getUserMedia({audio: true, video: true})
.then(handleSuccess);
</script>
如要選擇特定鏡頭,可以先列舉可用的相機。
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices = devices.filter((d) => d.kind === 'videoinput');
});
您接著可以傳遞呼叫 getUserMedia
時要使用的 deviceId。
navigator.mediaDevices.getUserMedia({
audio: true,
video: {
deviceId: devices[0].deviceId,
},
});
而這本身並不實用。我們只需擷取影片資料並播放即可。
存取相機的原始資料
如要從相機存取原始影片資料,您可以將每個影格繪製至 <canvas>
並直接操作像素。
如果是 2D 畫布,您可以使用結構定義的 drawImage
方法,將 <video>
元素目前的影格繪製到畫布中。
context.drawImage(myVideoElement, 0, 0);
您可以透過 WebGL 畫布使用 <video>
元素做為紋理的來源。
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
gl.RGBA,
gl.UNSIGNED_BYTE,
myVideoElement,
);
請注意,無論是哪一種情況,都會使用播放影片的目前畫面。如要處理多個影格,您每次需要將影片重新繪製在畫布上。
如需更多資訊,請參閱為圖片和影片套用即時效果一文。
儲存相機資料
如要儲存相機中的資料,最簡單的方法是使用 MediaRecorder
API。
MediaRecorder
API 會擷取 getUserMedia
建立的串流,然後逐步將串流中的資料儲存至您偏好的目的地。
<a id="download">Download</a>
<button id="stop">Stop</button>
<script>
let shouldStop = false;
let stopped = false;
const downloadLink = document.getElementById('download');
const stopButton = document.getElementById('stop');
stopButton.addEventListener('click', function() {
shouldStop = true;
})
var handleSuccess = function(stream) {
const options = {mimeType: 'video/webm'};
const recordedChunks = [];
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorder.addEventListener('dataavailable', function(e) {
if (e.data.size > 0) {
recordedChunks.push(e.data);
}
if(shouldStop === true && stopped === false) {
mediaRecorder.stop();
stopped = true;
}
});
mediaRecorder.addEventListener('stop', function() {
downloadLink.href = URL.createObjectURL(new Blob(recordedChunks));
downloadLink.download = 'acetest.webm';
});
mediaRecorder.start();
};
navigator.mediaDevices.getUserMedia({ audio: true, video: true })
.then(handleSuccess);
</script>
在本範例中,我們會將資料直接儲存至陣列,再轉換為 Blob
,然後將其儲存至網路伺服器,或直接儲存在使用者裝置的儲存空間中。
以負責任的方式要求使用相機
如果使用者之前從未授予您的網站相機存取權,則您呼叫 getUserMedia
時,瀏覽器會即時提示使用者將相機授予您的網站權限。
使用者因收到提示,要求存取電腦上的強大裝置,而經常封鎖要求;或者,如果使用者不瞭解建立提示的情境,就會忽略要求。最佳做法是只在必要時只要求存取相機。使用者先前已授予存取權後,系統就不會再要求他們提供存取權,但是如果他們拒絕存取權,您將無法再次要求使用者授予權限。
使用權限 API 檢查您是否具備存取權
如果已經擁有相機存取權,getUserMedia
API 就不會向您透露。這會產生一個問題,為了提供良好的 UI 來讓使用者授予相機存取權,您必須要求存取相機。
在某些瀏覽器中使用 Permission API 即可解決這個問題。您可以利用 navigator.permission
API 查詢特定 API 的存取權狀態,而不必再次提示。
如要查詢您是否有使用者的相機存取權,請將 {name: 'camera'}
傳入查詢方法,然後傳回以下其中一項:
granted
:使用者先前已授予您相機存取權;prompt
:使用者尚未授予您存取權,且會在您呼叫getUserMedia
時收到提示;denied
:系統或使用者已明確封鎖存取權,您將無法存取相機。
您現在可以快速查看是否需要修改使用者介面,以符合使用者需要的動作。
navigator.permissions.query({name: 'camera'}).then(function (result) {
if (result.state == 'granted') {
} else if (result.state == 'prompt') {
} else if (result.state == 'denied') {
}
result.onchange = function () {};
});