續上回Chrome Extension 開發與實作 24-打造螢幕錄影功能 chrome.desktopCapture (上)今天來講講螢幕截錄的功能實作。
今天的範例實作大概為了四到五個小時,大至上照著上一個章節的規畫一步一步實作完成,但影片輸出的方案我作了一些微調,後面會談到。細節還不太夠,只能算是雛型。
完整的原始碼都放在:GitHub
{
"manifest_version": 2,
"name": "影片截圖範例",
"description": "影片截圖範例",
"version": "2.0",
"browser_action": {
"default_title": "影片截圖範例",
"default_icon": "play_icon.png"
},
"background": {
"scripts": ["event.js"],
"persistent": false
},
"permissions": [
"desktopCapture",
"tabs"
]
}
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.create({
url: "preview.html"
}, function(tab) {
console.log('window open');
});
});
點擊瀏覽器按鈕,會用新頁籤打開一個已經準備好的view。
<body>
<div class="content">
<p class="ctrls">
<a id="recodBtn" class="btn btn-primary btn-large btn-block" href="#"><span class="fui-video-24"></span>開始錄制</a>
<a id="stopBtn" class="btn btn-large btn-block btn-danger" href="#">停止錄制</a>
<a id="downloadBtn" class="btn btn-large btn-block btn-primary " href="#">影片下載</a>
</p>
<div class="row clearfix">
<div class="live col">
<h3>即時的串流畫面</h3>
<video id="video" class="box" autoplay></video>
</div>
<div class="capture col">
<h3>Canvas的截錄畫面</h3>
<canvas id="canvas" class="box"></canvas>
</div>
<div class="result col">
<h3>輸出影片</h3>
<div id="resultWrap" class="box">
</div>
</div>
</div>
</div>
<script src="jquery-3.1.1.min.js"></script>
<script src="preview.js"></script>
</body>
畫面上會有三個影象輸出區:
畫面的腳本
(function(exports) {
//API兼容處理
exports.URL = exports.URL || exports.webkitURL;
exports.requestAnimationFrame = exports.requestAnimationFrame ||
exports.webkitRequestAnimationFrame || exports.mozRequestAnimationFrame ||
exports.msRequestAnimationFrame || exports.oRequestAnimationFrame;
exports.cancelAnimationFrame = exports.cancelAnimationFrame ||
exports.webkitCancelAnimationFrame || exports.mozCancelAnimationFrame ||
exports.msCancelAnimationFrame || exports.oCancelAnimationFrame;
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
var isRecoding = false;
//預覽影片,使用原始的dom物件操作
var video = document.getElementById('video');
var videoWidth = 600;
var videoHeight = 400;
video.autoplay = true;
video.height = videoHeight;
video.width = videoWidth;
//取得原始的dom物件,以便使用download api
var downloadlink = document.getElementById('downloadBtn');
//畫面展示需要,使用jquery dom
var dRecordBtn = $('#recodBtn');
var dStopBtn = $('#stopBtn').hide();
var dDownloadBtn = $('#downloadBtn').hide();
//錄制畫面用的canvas
var canvas = document.getElementById('canvas');
canvas.height = videoHeight;
canvas.width = videoWidth;
//準備用來存放 requestAnimationFrame 的id 以便在停止時取消Canvas的截錄繪制
var rafId = null;
//準備畢來存放,cnavas的錄制相關的物件
var cStream = null;
var recorder = null;
var chunks = [];
//串流的來源
var sourceTrack = null;
//開始錄制的邏輯
function record(stream) {
//將live區塊的影片來源跟串流接上。
video.src = URL.createObjectURL(stream);
shareStream = stream;
var ctx = canvas.getContext('2d');
sourceTrack = stream.getTracks()[0];
function drawVideoFrame(time) {
rafId = requestAnimationFrame(drawVideoFrame);
ctx.drawImage(video, 0, 0, videoWidth, videoHeight);
};
//開始截取畫面並把requestAnimationFrame的id儲存起來以便控制
cStream = canvas.captureStream(30);
recorder = new MediaRecorder(cStream);
rafId = requestAnimationFrame(drawVideoFrame);
recorder.start();
recorder.ondataavailable = function(e) {
//saveChunks
chunks.push(e.data);
};
};
//處理停止的邏輯
function stopRecord() {
//停止影片串流的來源
sourceTrack.stop();
//停止Canvas錄制畫面
recorder.onstop = exportPreview;
recorder.stop();
//停止請求requestAnimationFrame
cancelAnimationFrame(rafId);
};
function exportPreview() {
//顯示下載按鈕
dDownloadBtn.show();
//影片輸出
var blob = new Blob(chunks);
var vidURL = URL.createObjectURL(blob);
var vid = document.createElement('video');
vid.controls = true;
vid.src = vidURL;
vid.onend = function() {
//釋放URL訪問object
URL.revokeObjectURL(vidURL);
};
$('#resultWrap').append(vid);
//指定下載網址跟下載檔的副檔名
downloadlink.download = 'capture.mp4';
downloadlink.href = vidURL;
};
//取得串流失敗的錯誤處理
function getUserMediaError(error) {
console.log('navigator.webkitGetUserMedia() errot: ', error);
};
//按下開始錄制鈕
dRecordBtn.click(function() {
if (!isRecoding) {
isRecoding = true;
dRecordBtn.hide();
dStopBtn.show();
//設定可以選擇媒體來源,以便開始處理串流
captureRequestID = chrome.desktopCapture.chooseDesktopMedia(["screen", "window", "tab"], function(streamId) {
var audioConstraint = {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: streamId
}
};
//使用 Navigator.getUserMedia拿到串流並開始處理
navigator.getUserMedia({
audio: audioConstraint,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: streamId,
maxWidth: screen.width,
maxHeight: screen.height
}
}
}, record, getUserMediaError);
});
}
});
//按下停止錄制鈕
dStopBtn.click(function() {
if (isRecoding) {
//處理停止事件
stopRecord();
//處理UI
isRecoding = false;
dRecordBtn.show();
dStopBtn.hide();
}
});
})(window);
截錄畫面時,利用HTMLCanvasElement.captureStream()錄制畫面的相關程式碼片段。
本來在上一篇的規畫,我們應該把canvas輸出成webp再處理成webm,但我在實作上遇到了一些無法解決的問題,導致無法輸出成果,後來發現canvas直接有API提供串流,所以我就換了個作法。
cStream = canvas.captureStream(30);
recorder = new MediaRecorder(cStream);
rafId = requestAnimationFrame(drawVideoFrame);
recorder.start();
recorder.ondataavailable = function(e) {
//saveChunks
chunks.push(e.data);
};
呃…不支援gifv檔,那我直接付上連結,晚點看要不要傳到youtube。
Demo 動圖
完整程式碼在GitHub,大家可以參考Chrome Extension 開發與實作 02-官網導讀:快速打造一個chrome extension下載到地端載入到擴充功能裡玩看看。
關於canvas可以輸出成影片這件事,再加上可以取得影片畫畫面來繪圖,我們應該就能利用它作到影片跟動畫(2d canvas or webgl)的合成功能。我覺得蠻值得深入研究,找時間玩起XD。(題外的題外:這是之前玩webgl的一些實例)。