前面我們能開啟本地攝像頭,並且在網頁上看到攝像頭的預覽影像。
本文我們使用MediaRecorder來錄製視訊。在網頁上播放錄製好的視訊,並能提供下載功能。
html
首先建立一個html介面,放上一些元素
<video id="v1" playsinline autoplay muted></video>
<video id="v2" playsinline loop></video>
<div>
<button id="startCamera">開啟攝像頭</button>
<button id="stopCamera">停止攝像頭</button>
<button id="record" disabled>錄製</button>
<button id="play" disabled>播放</button>
<button id="download" disabled>下載視訊</button>
</div>
<div> 錄製使用的視訊格式: <select id="codecSelect" disabled></select> </div>
<div>
<h4>視訊設定</h4>
<p>回聲消除: <input type="checkbox" id="echoCancellation"></p>
</div>
<div> <span id="msg" style="font-size:smaller"></span> </div>
<!-- 使用本地的介面卡 -->
<script src="../js/adapter-latest.js" async></script>
video
- v1 用來預覽
- v2 用來播放錄製好的視訊
button
控制攝像頭開啟、錄製,下載等等select
選擇錄製用的視訊格式input
選擇回聲消除
js
準備
先把介面上的元素拿到
'use strict';
let mediaRecorder;
let recordedBlobs; // 錄製下來的內容
let isRecording = false;
// 先把頁面元素拿到
const startCameraBtn = document.querySelector('button#startCamera'); // 啟動攝像頭按鈕
const stopCameraBtn = document.querySelector('button#stopCamera');
const recordBtn = document.querySelector('button#record'); // 開始錄製按鈕
const playBtn = document.querySelector('button#play'); // 播放按鈕
const downloadBtn = document.querySelector('button#download'); // 下載視訊按鈕
const codecSelector = document.querySelector('#codecSelect'); // 選擇格式
const msgEle = document.querySelector('span#msg'); // 顯示訊息
const previewV1 = document.querySelector('video#v1'); // 預覽用的
const recordedV2 = document.querySelector('video#v2'); // 用來播放錄製好的視訊
視訊支援的格式
先預定幾個可能的格式,然後一個個來判斷是否支援。找到支援的格式。
function getSupportedMimeTypes() {
const possibleTypes = [
'video/webm;codecs=vp9,opus',
'video/webm;codecs=vp8,opus',
'video/webm;codecs=h264,opus',
'video/mp4;codecs=h264,aac',
];
return possibleTypes.filter(mimeType => {
return MediaRecorder.isTypeSupported(mimeType);
});
}
開啟攝像頭
同樣要使用getUserMedia
方法。這裡給視訊指定了寬高。回聲消除是可選項。
// 啟動攝像頭
startCameraBtn.addEventListener('click', async () => {
startCameraBtn.disabled = true;
const isEchoCancellation = document.querySelector('#echoCancellation').checked;
const constraints = {
audio: {
echoCancellation: { exact: isEchoCancellation }
},
video: {
width: 1280, height: 720
}
};
await init(constraints);
});
async function init(constraints) {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
gotStream(stream);
} catch (e) {
showMsg(`navigator.getUserMedia error:${e.toString()}`);
}
}
function gotStream(stream) {
recordBtn.disabled = false;
showMsg('拿到了 stream:', stream);
window.stream = stream;
previewV1.srcObject = stream;
// 重置
var codecOption = codecSelector.lastChild;
while (codecOption != null) {
codecSelector.removeChild(codecOption);
codecOption = codecSelector.lastChild;
}
getSupportedMimeTypes().forEach(mimeType => {
const option = document.createElement('option');
option.value = mimeType;
option.innerText = option.value;
codecSelector.appendChild(option);
});
codecSelector.disabled = false; // 可以進行選擇了
}
下面是停止攝像頭的方法
stopCameraBtn.addEventListener('click', () => {
var stream = previewV1.srcObject;
if (stream == null) {
return;
}
const tracks = stream.getTracks();
tracks.forEach(function (track) {
track.stop();
});
previewV1.srcObject = null;
window.stream = null;
codecSelector.disabled = true;
startCameraBtn.disabled = false;
});
開始錄製
開始錄製視訊
function startRecording() {
recordedBlobs = [];
const mimeType = codecSelector.options[codecSelector.selectedIndex].value;
const options = { mimeType };
try {
mediaRecorder = new MediaRecorder(window.stream, options);
} catch (e) {
showMsg(`建立MediaRecorder出錯: ${JSON.stringify(e)}`);
return;
}
showMsg('建立MediaRecorder', mediaRecorder, ' -> options', options);
recordBtn.textContent = '停止錄製';
isRecording = true;
playBtn.disabled = true;
downloadBtn.disabled = true;
codecSelector.disabled = true;
mediaRecorder.onstop = (event) => {
showMsg('錄製停止了: ' + event);
showMsg('錄製的資料Blobs: ' + recordedBlobs);
};
mediaRecorder.ondataavailable = handleDataAvailable;
mediaRecorder.start();
showMsg('錄製開始 mediaRecorder: ' + mediaRecorder);
}
function handleDataAvailable(event) {
if (event.data && event.data.size > 0) {
recordedBlobs.push(event.data);
}
}
recordBtn.addEventListener('click', () => {
if (isRecording == false) {
startRecording();
} else {
stopRecording();
recordBtn.textContent = '開始錄製';
playBtn.disabled = false;
downloadBtn.disabled = false;
codecSelector.disabled = false;
}
});
- 重置錄製內容
recordedBlobs = []
- 拿到選定的視訊格式
mimeType
- 新建MediaRecorder物件,傳入前面獲取到的流
- 處理各個按鈕(ui)的狀態
- mediaRecorder
- 設定停止監聽器
onstop
- 監聽錄製資料
ondataavailable
,有資料來的時候存放在recordedBlobs
- 啟動錄製
mediaRecorder.start()
- 設定停止監聽器
停止錄製
function stopRecording() {
mediaRecorder.stop();
}
播放錄製好的視訊
錄製好的視訊內容存放在recordedBlobs
。新建Blob,交給video(recordedV2)來播放
playBtn.addEventListener('click', () => {
const mimeType = codecSelector.options[codecSelector.selectedIndex].value.split(';', 1)[0];
const superBuffer = new Blob(recordedBlobs, { type: mimeType });
recordedV2.src = null;
recordedV2.srcObject = null;
recordedV2.src = window.URL.createObjectURL(superBuffer);
recordedV2.controls = true;
recordedV2.play();
});
下載視訊
錄製好的視訊內容存放在recordedBlobs
。
downloadBtn.addEventListener('click', () => {
const blob = new Blob(recordedBlobs, { type: 'video/webm' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = '視訊_' + new Date().getTime() + '.webm';
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
});
新建Blob和一個a
元素。根據blob建立ObjectURL,並傳給a
元素的href。
修改下載檔案的預設名字a.download
。
觸發a
元素的click()
,即能讓瀏覽器下載這個檔案。
延遲把這個a
移除掉。
小結
getUserMedia()
開啟視訊拿到視訊流。MediaRecorder錄製視訊。用Blob來播放和下載。
實現一個小的錄製視訊效果。視訊資料快取在物件裡。
完整的效果請參考 視訊錄製