【Javascript】【視頻錄製】通過video標籤和canvas實現視頻截圖錄制和下載

錄像原理

  • 創建一個畫布,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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章