增加远程、本地查看摄像头功能

This commit is contained in:
iRankoo
2026-08-15 19:17:12 +08:00
parent b54ad411c0
commit f3a99f9467
11 changed files with 2346 additions and 289 deletions
+193 -14
View File
@@ -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 @@
&nbsp;&nbsp;
<input type="button" value="AMap" class="button button1" onclick = "showAmap();" />
&nbsp;&nbsp;
<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');" />
&nbsp;&nbsp;
<input type="button" value="Wide Cam" class="button button1" onclick = "playH264('wideRoadCameraState');" />
&nbsp;&nbsp;
<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>