錄像原理
- 創建一個畫布,video標籤本身不具備記錄畫面功能,所以我們需要通過Canvas來達成這個功能
- 創建一個錄製器,與CanvasStream綁定,這樣畫布繪製什麼,錄製器都能觸發回調
- 創建一個定時器,不停通過CanvasContext來捕捉video標籤畫面,然後繪製到Canvas上面
- 創建一個字節數組,只要Canvas捕捉到新畫面,就會觸發MediaRecorder數據回調,將拿到的幀數據寫入數組
- 每個幀數據本身就是一個Blob[]數據結構,將所有的Blob數組合並,得到一個最終的Blob,就是錄製的視頻數據
截圖原理
- 同錄像一樣,創建一個Canvas來捕捉video標籤畫面
- 通過Canvas2Image庫將Canvas當前畫面數據以base64格式寫到一個img標籤裏面
下載原理
- 錄製出來的Blob字節塊,可以通過window.URL.createObjectURL轉化爲一個BlobURL,將BlobURL作爲a標籤的鏈接地址,即可在點擊時下載該字節塊數據
- base64格式的圖像數據,可以直接作爲a標籤的鏈接地址,點擊時,瀏覽器會自動將其解碼爲圖片文件對應的字節塊,供用戶下載
- 通過a標籤的download屬性,可以設置下載的文件名
注意事項
- 截圖功能不但可以截取video標籤,還可以對其它任意元素/整個網頁進行抓取,但必須先通過Html2Canvas庫將對應的元素繪製到canvas上面,此處不多說,有興趣自己嘗試
- 這些功能均只支持相同域名下的文件/媒體流錄製,不支持跨域錄製,即無法錄製來自其它網站的圖片視頻等資源
MP4文件錄像代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>File Record</title>
</head>
<body>
<canvas id="canvas" width="640" height="360"></canvas>
<video id="video" autoplay loop muted>
<source src="../media/video.mp4"/>
</video>
<button id="button-start">開始錄製</button>
<button id="button-stop">結束錄製</button>
</body>
<style>
* {
margin: 20px;
font-size: 0px;
box-sizing: border-box;
}
#canvas {
display: none;
}
#video {
width: 640px;
height: 360px;
background: deepskyblue;
display: block;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
}
#button-start {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#FF000011, #FF000044) orange;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-start:hover {
background: linear-gradient(#FF000033, #FF000077) orange;
}
#button-start:active {
background: linear-gradient(#FF000055, #FF000099) orange;
}
#button-start[disabled] {
background: gray;
}
#button-stop {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#9933FF00, #9933FF33) dodgerblue;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-stop:hover {
background: linear-gradient(#9933FF22, #9933FF66) dodgerblue;
}
#button-stop:active {
background: linear-gradient(#9933FF44, #9933FF88) dodgerblue;
}
#button-stop[disabled] {
background: gray;
}
</style>
<script>
let canvasElement = document.querySelector("#canvas");
let videoElement = document.querySelector("#video");
let startButton = document.querySelector("#button-start");
let stopButton = document.querySelector("#button-stop");
const videoWidth = 640;
const videoHeight = 360;
const frameRate = 60;
const encodeType = "video/webm;codecs=vp8";
let chunks = [];
let frameId = null;
//設置畫布背景
const canvasContext = canvasElement.getContext("2d");
canvasContext.fillStyle = "deepskyblue";
canvasContext.fillRect(0, 0, canvasElement.width, canvasElement.height);
//創建MediaRecorder,設置媒體參數
const stream = canvasElement.captureStream(frameRate);
const recorder = new MediaRecorder(stream, {
mimeType: encodeType
});
//收集錄制數據
recorder.ondataavailable = e => {
chunks.push(e.data);
};
//按鈕事件
startButton.disabled = false;
stopButton.disabled = true;
startButton.onclick = e => {
startButton.disabled = true;
stopButton.disabled = false;
recorder.start(10);
drawFrame();
};
stopButton.onclick = e => {
startButton.disabled = false;
stopButton.disabled = true;
recorder.stop();
cancelAnimationFrame(frameId);
download();
};
//播放視頻
function drawFrame() {
canvasContext.drawImage(videoElement, 0, 0, videoWidth, videoHeight);
frameId = requestAnimationFrame(drawFrame);
}
//下載錄製內容
function download() {
let blob = new Blob(chunks);
let url = window.URL.createObjectURL(blob);
let link = document.createElement("a");
link.href = url;
link.download = new Date().getTime() + ".mp4";
link.style.display = "none";
document.body.appendChild(link);
link.click();
link.remove();
}
</script>
</html>
攝像頭錄像代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Camera Record</title>
</head>
<body>
<canvas id="canvas" width="640" height="360"></canvas>
<video id="video" autoplay loop muted></video>
<button id="button-start">開始錄製</button>
<button id="button-stop">結束錄製</button>
</body>
<style>
* {
margin: 20px;
font-size: 0px;
box-sizing: border-box;
}
#canvas {
display: none;
}
#video {
width: 640px;
height: 360px;
background: deepskyblue;
display: block;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
}
#button-start {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#FF000011, #FF000044) orange;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-start:hover {
background: linear-gradient(#FF000033, #FF000077) orange;
}
#button-start:active {
background: linear-gradient(#FF000055, #FF000099) orange;
}
#button-start[disabled] {
background: gray;
}
#button-stop {
width: 300px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#9933FF00, #9933FF33) dodgerblue;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-stop:hover {
background: linear-gradient(#9933FF22, #9933FF66) dodgerblue;
}
#button-stop:active {
background: linear-gradient(#9933FF44, #9933FF88) dodgerblue;
}
#button-stop[disabled] {
background: gray;
}
</style>
<script>
let canvasElement = document.querySelector("#canvas");
let videoElement = document.querySelector("#video");
let startButton = document.querySelector("#button-start");
let stopButton = document.querySelector("#button-stop");
const videoWidth = 640;
const videoHeight = 360;
const frameRate = 60;
const encodeType = "video/webm;codecs=vp8";
let chunks = [];
let frameId = null;
//設置畫布背景
const canvasContext = canvasElement.getContext("2d");
canvasContext.fillStyle = "deepskyblue";
canvasContext.fillRect(0, 0, canvasElement.width, canvasElement.height);
//創建MediaRecorder,設置媒體參數
const stream = canvasElement.captureStream(frameRate);
const recorder = new MediaRecorder(stream, {
mimeType: encodeType
});
//收集錄制數據
recorder.ondataavailable = e => {
chunks.push(e.data);
};
//按鈕事件
startButton.disabled = true;
stopButton.disabled = true;
startButton.onclick = e => {
startButton.disabled = true;
stopButton.disabled = false;
recorder.start(10);
drawFrame();
};
stopButton.onclick = e => {
startButton.disabled = false;
stopButton.disabled = true;
recorder.stop();
cancelAnimationFrame(frameId);
download();
};
//打開攝像頭,並將數據顯示到video標籤上
navigator.mediaDevices.getUserMedia({
audio: false,
video: true
}).then(mediaStream => {
videoElement.srcObject = mediaStream;
videoElement.play();
startButton.disabled = false;
}).catch(error => {
alert("打開攝像頭失敗");
});
//播放視頻
function drawFrame() {
canvasContext.drawImage(videoElement, 0, 0, videoWidth, videoHeight);
frameId = requestAnimationFrame(drawFrame);
}
//下載錄製內容
function download() {
let blob = new Blob(chunks);
let url = window.URL.createObjectURL(blob);
let link = document.createElement("a");
link.href = url;
link.download = new Date().getTime() + ".mp4";
link.style.display = "none";
document.body.appendChild(link);
link.click();
link.remove();
}
</script>
</html>
MP4文件截圖代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Canvas to Image</title>
</head>
<body>
<canvas id="canvas" width="640" height="360"></canvas>
<video id="video" autoplay loop muted>
<source src="../media/video.mp4"/>
</video>
<button id="button-start">屏幕截圖</button>
</body>
<script src="../js/canvas2image.js"></script>
<style>
* {
margin: 5px;
font-size: 0px;
box-sizing: border-box;
}
#video {
width: 640px;
height: 360px;
background: deepskyblue;
display: block;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
}
#button-start {
width: 640px;
height: 75px;
box-sizing: border-box;
background: linear-gradient(#FF000011, #FF000044) orange;
color: white;
font-size: 16px;
font-family: "monospace";
border: none;
outline: none;
border-radius: 2px;
box-shadow: 0 1px 1.5px 1px rgba(0, 0, 0, 0.12);
user-select: none;
}
#button-start:hover {
background: linear-gradient(#FF000033, #FF000077) orange;
}
#button-start:active {
background: linear-gradient(#FF000055, #FF000099) orange;
}
</style>
<script>
let canvasElement = document.querySelector("#canvas");
let videoElement = document.querySelector("#video");
let startButton = document.querySelector("#button-start");
const videoWidth = 640;
const videoHeight = 360;
let frameId = null;
//設置畫布背景
const canvasContext = canvasElement.getContext("2d");
canvasContext.fillStyle = "deepskyblue";
canvasContext.fillRect(0, 0, canvasElement.width, canvasElement.height);
//按鈕事件
startButton.onclick = e => {
download();
};
//將video標籤繪製到canvas上面
drawFrame();
//播放視頻
function drawFrame() {
canvasContext.drawImage(videoElement, 0, 0, videoWidth, videoHeight);
frameId = requestAnimationFrame(drawFrame);
}
//下載錄製內容
function download() {
let link = document.createElement("a");
link.href = Canvas2Image.saveAsPNG(canvasElement, true).src;
link.download = new Date().getTime() + ".png";
link.style.display = "none";
document.body.appendChild(link);
link.click();
link.remove();
}
</script>
</html>
源碼下載
下載地址:video_record.7z