最近公司項目涉及到一個需求娩鹉,調(diào)用電腦攝像頭功能,經(jīng)過在網(wǎng)上查找資料有以下三種方案實現(xiàn)元媚。
- 通過瀏覽器API來getUserMedia來實現(xiàn)調(diào)用用戶的攝像頭轧叽,但有兩點限制:部署到生產(chǎn)服務(wù)的時候要使用htts協(xié)議訪問該項目,第二點限制就是第一次獲取攝像頭時要點擊允許彈窗刊棕。
- 通過web端發(fā)送websocket請求給客戶端炭晒,然后客戶端調(diào)用攝像頭應(yīng)用程序,但每次初始打開攝像頭應(yīng)用時都有個初始化2~3秒左右甥角,用戶體驗不免友好网严。
- 通過客戶端獲取攝像頭的每一幀數(shù)據(jù),并通過websocket返回給web端嗤无,具體步驟下面有代碼講解震束。
getUserMedia方式實現(xiàn)代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>調(diào)用攝像頭錄像</title>
</head>
<body>
<video id="video" width="640" height="480" autoplay></video>
<button id="startRecord">開始錄制</button>
<button id="stopRecord">停止錄制</button>
<script>
var video = document.querySelector('#video');
var startRecord = document.querySelector('#startRecord');
var stopRecord = document.querySelector('#stopRecord');
var mediaRecorder;
var chunks = [];
navigator.mediaDevices.getUserMedia({ audio: false, video: true }).then(function (stream) {
video.srcObject = stream;
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = function (e) {
chunks.push(e.data);
};
mediaRecorder.onstop = function (e) {
var blob = new Blob(chunks, { type: 'video/mp4' });
chunks = [];
var videoURL = window.URL.createObjectURL(blob);
video.src = videoURL;
};
});
startRecord.onclick = function () {
mediaRecorder.start();
};
stopRecord.onclick = function () {
mediaRecorder.stop();
};
</script>
</body>
</html>
客戶端返回視頻流方式實現(xiàn)代碼
vue前端代碼
前端接收到H264視頻流,需要用JMuxer來轉(zhuǎn)碼才能在video標(biāo)簽里播放当犯,需要執(zhí)行npm install JMuxer來安裝這個庫垢村。詳細(xì)代碼如下:
<template>
<video id="player" :controls="false" :muted="false" :autoplay="true"></video>
</template>
<script setup>
import JMuxer from 'jmuxer';
import { onMounted, onBeforeUnmount} from 'vue';
var ws = null;
var jmuxer = null;
const list = reactive([]);
const isDisable = ref(false);
onMounted(() => {
jmuxer = new JMuxer({
node: 'player',
// 可用值是:video、audio
mode: 'video',
// 最大延遲時間(毫秒), 默認(rèn)為值是500毫秒
maxDelay: 100,
// 緩沖區(qū)刷新時間,默認(rèn)為值是500毫秒
flushingTime: 0,
// 是否會自動清除播放的媒體緩沖區(qū)嚎卫。默認(rèn)為true
clearBuffer: true,
// 可選值嘉栓。視頻的幀率,如果它是已知的或固定值拓诸。如果所提供的媒體數(shù)據(jù)中沒有塊持續(xù)時間侵佃,它將用于查找?guī)掷m(xù)時間。
fps: 30,
// 將從MP4軌道數(shù)據(jù)讀取FPS奠支,而不是使用(以上)FPS值馋辈。默認(rèn)為false。
readFpsFromTrack: false,
// 將在瀏覽器控制臺打印調(diào)試日志胚宦。默認(rèn)為false
debug: false,
// 遇到任何丟失的視頻幀將會被觸發(fā)
onMissingVideoFrames: function (data) {
console.log('丟失的視頻幀');
},
onError: function (data) {
if (/Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor)) {
jmuxer.reset();
}
}
});
ws = new WebSocket('ws://127.0.0.1:12797');
ws.binaryType = 'arraybuffer';
ws.onmessage = function (event) {
jmuxer.feed({ video: new Uint8Array(event.data) });
};
ws.onopen = function () {
console.log('已連接');
ws.send('{"interfaceId":"100002","command":"1"}');
};
ws.onerror = function (err) {
console.log('出錯--Socket Error', err);
};
ws.onclose = function () {
console.log('斷開');
};
});
// 實現(xiàn)拍照功能
const toScan = () => {
const videoEl = document.getElementById('player');
const canvas = document.createElement('canvas');
canvas.width = videoEl.videoWidth;
canvas.height = videoEl.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(videoEl, 0, 0, videoEl.videoWidth, videoEl.videoHeight);
console.log(canvas.toDataURL('image/png'));
};
onBeforeUnmount(() => {
console.info('離開頁面,關(guān)閉高拍儀');
ws.close();
jmuxer.destroy();
});
</script>
C++客戶端
https://github.com/qiyingcong/vue-c-websocket點擊下載客戶端包
雙擊打開這個應(yīng)用程序就可以訪問本地的websocket的視頻流了