mirror of
https://github.com/eatfishfish/openpilot-server.git
synced 2026-08-22 04:42:32 +00:00
增加远程、本地查看摄像头功能
This commit is contained in:
+193
-14
@@ -174,15 +174,183 @@
|
||||
window.location = url;
|
||||
}
|
||||
|
||||
function showSentry() {
|
||||
did = localStorage.getItem("dongleId");
|
||||
url = "FRP_SVR/"+String(did)
|
||||
console.log(url)
|
||||
window.location = url;
|
||||
}
|
||||
|
||||
|
||||
function recordOperationTime(){
|
||||
function showSentry() {
|
||||
did = localStorage.getItem("dongleId");
|
||||
url = "http://192.168.1.113:10201/"+String(did)
|
||||
console.log(url)
|
||||
window.location = url;
|
||||
}
|
||||
|
||||
let h264Ws = null;
|
||||
let h264Decoder = null;
|
||||
let h264Configured = false;
|
||||
let h264Timestamp = 0;
|
||||
let h264Frames = 0;
|
||||
|
||||
function findH264NalUnits(data) {
|
||||
const units = [];
|
||||
let start = -1;
|
||||
let index = 0;
|
||||
while (index + 3 < data.length) {
|
||||
let prefixLength = 0;
|
||||
if (data[index] === 0 && data[index + 1] === 0 && data[index + 2] === 1) {
|
||||
prefixLength = 3;
|
||||
} else if (index + 4 < data.length &&
|
||||
data[index] === 0 && data[index + 1] === 0 &&
|
||||
data[index + 2] === 0 && data[index + 3] === 1) {
|
||||
prefixLength = 4;
|
||||
}
|
||||
if (!prefixLength) {
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (start >= 0) {
|
||||
units.push(data.subarray(start, index));
|
||||
}
|
||||
start = index + prefixLength;
|
||||
index = start;
|
||||
}
|
||||
if (start >= 0 && start < data.length) {
|
||||
units.push(data.subarray(start));
|
||||
}
|
||||
return units;
|
||||
}
|
||||
|
||||
function inspectH264(data) {
|
||||
let key = false;
|
||||
let codec = null;
|
||||
for (const nal of findH264NalUnits(data)) {
|
||||
if (!nal.length) {
|
||||
continue;
|
||||
}
|
||||
const type = nal[0] & 0x1f;
|
||||
key = key || type === 5;
|
||||
if (type === 7 && nal.length >= 4) {
|
||||
codec = "avc1." + [nal[1], nal[2], nal[3]]
|
||||
.map(function(value) { return value.toString(16).padStart(2, "0"); })
|
||||
.join("").toUpperCase();
|
||||
}
|
||||
}
|
||||
return {key: key, codec: codec};
|
||||
}
|
||||
|
||||
function closeH264Decoder() {
|
||||
if (h264Decoder) {
|
||||
try {
|
||||
h264Decoder.close();
|
||||
} catch (_) {
|
||||
}
|
||||
}
|
||||
h264Decoder = null;
|
||||
h264Configured = false;
|
||||
h264Timestamp = 0;
|
||||
}
|
||||
|
||||
function createH264Decoder(camera) {
|
||||
if (!("VideoDecoder" in window)) {
|
||||
document.getElementById("h264Status").innerText = "WebCodecs H.264 is not supported";
|
||||
return false;
|
||||
}
|
||||
const canvas = document.getElementById("h264Frame");
|
||||
const context = canvas.getContext("2d");
|
||||
h264Decoder = new VideoDecoder({
|
||||
output: function(frame) {
|
||||
if (canvas.width !== frame.displayWidth || canvas.height !== frame.displayHeight) {
|
||||
canvas.width = frame.displayWidth;
|
||||
canvas.height = frame.displayHeight;
|
||||
}
|
||||
context.drawImage(frame, 0, 0, canvas.width, canvas.height);
|
||||
frame.close();
|
||||
h264Frames += 1;
|
||||
document.getElementById("h264Status").innerText = "Live " + camera + " / " + h264Frames + " frames";
|
||||
},
|
||||
error: function(error) {
|
||||
console.error("H.264 decoder error", error);
|
||||
document.getElementById("h264Status").innerText = "H.264 decode error";
|
||||
closeH264Decoder();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function playH264(camera) {
|
||||
const dongleId = localStorage.getItem("dongleId");
|
||||
if (!dongleId) {
|
||||
alert("Please input dongle ID first.");
|
||||
return;
|
||||
}
|
||||
stopH264();
|
||||
document.getElementById("h264Panel").style.display = "";
|
||||
document.getElementById("h264Status").innerText = "Connecting " + camera;
|
||||
h264Frames = 0;
|
||||
|
||||
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
||||
h264Ws = new WebSocket(protocol + "//" + location.host + "/h264/stream?dongle_id=" + encodeURIComponent(dongleId) + "&camera=" + encodeURIComponent(camera));
|
||||
h264Ws.binaryType = "arraybuffer";
|
||||
h264Ws.onopen = function() {
|
||||
document.getElementById("h264Status").innerText = "Waiting for H.264 keyframe " + camera;
|
||||
};
|
||||
h264Ws.onmessage = function(event) {
|
||||
const data = new Uint8Array(event.data);
|
||||
const info = inspectH264(data);
|
||||
if (!h264Configured) {
|
||||
if (!info.key || !info.codec || (!h264Decoder && !createH264Decoder(camera))) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
h264Decoder.configure({
|
||||
codec: info.codec,
|
||||
optimizeForLatency: true,
|
||||
hardwareAcceleration: "prefer-hardware"
|
||||
});
|
||||
h264Configured = true;
|
||||
} catch (error) {
|
||||
console.error("H.264 decoder configure error", error);
|
||||
document.getElementById("h264Status").innerText = "Unsupported H.264 stream";
|
||||
closeH264Decoder();
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
h264Decoder.decode(new EncodedVideoChunk({
|
||||
type: info.key ? "key" : "delta",
|
||||
timestamp: h264Timestamp,
|
||||
data: data
|
||||
}));
|
||||
h264Timestamp += 50000;
|
||||
} catch (error) {
|
||||
console.error("H.264 decode submission error", error);
|
||||
closeH264Decoder();
|
||||
}
|
||||
};
|
||||
h264Ws.onclose = function() {
|
||||
document.getElementById("h264Status").innerText = "Stopped";
|
||||
};
|
||||
h264Ws.onerror = function() {
|
||||
h264Ws.close();
|
||||
};
|
||||
recordOperationTime();
|
||||
}
|
||||
|
||||
function stopH264() {
|
||||
if (h264Ws) {
|
||||
h264Ws.onclose = null;
|
||||
h264Ws.close();
|
||||
h264Ws = null;
|
||||
}
|
||||
closeH264Decoder();
|
||||
const canvas = document.getElementById("h264Frame");
|
||||
if (canvas) {
|
||||
canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
const status = document.getElementById("h264Status");
|
||||
if (status) {
|
||||
status.innerText = "Stopped";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function recordOperationTime(){
|
||||
strSec = Math.floor(Date.now() / 1000)
|
||||
localStorage.setItem("operationTime", strSec)
|
||||
}
|
||||
@@ -237,11 +405,22 @@
|
||||
|
||||
<input type="button" value="AMap" class="button button1" onclick = "showAmap();" />
|
||||
|
||||
<input type="button" value="Nav" class="button button1" onclick = "showNavigation();" />
|
||||
</div>
|
||||
|
||||
</br></br></br></br>
|
||||
<div style="text-align:center"><label style="display: none;" id="lastActive" > </label ></div>
|
||||
<input type="button" value="Nav" class="button button1" onclick = "showNavigation();" />
|
||||
</br></br>
|
||||
<input type="button" value="Road Cam" class="button button1" onclick = "playH264('roadCameraState');" />
|
||||
|
||||
<input type="button" value="Wide Cam" class="button button1" onclick = "playH264('wideRoadCameraState');" />
|
||||
|
||||
<input type="button" value="Stop Cam" class="button button1" onclick = "stopH264();" />
|
||||
</div>
|
||||
|
||||
</br></br></br></br>
|
||||
<div id="h264Panel" style="display:none;text-align:center;margin:0 auto;max-width:960px;">
|
||||
<div id="h264Status" style="margin-bottom:8px;">Stopped</div>
|
||||
<canvas id="h264Frame" aria-label="H.264 preview" style="width:100%;background:#000;min-height:240px;object-fit:contain;"></canvas>
|
||||
</div>
|
||||
|
||||
<div style="text-align:center"><label style="display: none;" id="lastActive" > </label ></div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user