mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 01:00:29 +08:00
web: reconfigure webcodec if needed
This commit is contained in:
parent
2bdd349fbf
commit
8391b7a467
@ -52,6 +52,9 @@ class _Source:
|
||||
clients: dict[WsSession, "_Client"] = dataclasses.field(default_factory=dict)
|
||||
key_required: bool = dataclasses.field(default=False)
|
||||
|
||||
def is_diff(self) -> bool:
|
||||
return StreamerFormats.is_diff(self.streamer.get_format())
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class _Client:
|
||||
@ -98,6 +101,14 @@ class MediaServer(HttpServer):
|
||||
async def __ws_bin_ping_handler(self, ws: WsSession, _: bytes) -> None:
|
||||
await ws.send_bin(255, b"") # Ping-pong
|
||||
|
||||
@exposed_ws(1)
|
||||
async def __ws_bin_key_handler(self, ws: WsSession, _: bytes) -> None:
|
||||
for src in self.__srcs:
|
||||
if ws in src.clients:
|
||||
if src.is_diff():
|
||||
src.key_required = True
|
||||
break
|
||||
|
||||
@exposed_ws("start")
|
||||
async def __ws_start_handler(self, ws: WsSession, event: dict) -> None:
|
||||
try:
|
||||
@ -145,7 +156,7 @@ class MediaServer(HttpServer):
|
||||
# =====
|
||||
|
||||
async def __sender(self, client: _Client) -> None:
|
||||
need_key = StreamerFormats.is_diff(client.src.streamer.get_format())
|
||||
need_key = client.src.is_diff()
|
||||
if need_key:
|
||||
client.src.key_required = True
|
||||
has_key = False
|
||||
|
||||
@ -37,6 +37,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
||||
var __ws = null;
|
||||
var __ping_timer = null;
|
||||
var __missed_heartbeats = 0;
|
||||
|
||||
var __decoder = null;
|
||||
var __codec = "";
|
||||
var __canvas = $("stream-canvas");
|
||||
@ -86,11 +87,15 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
||||
__ws.onerror = __wsErrorHandler;
|
||||
__ws.onclose = __wsCloseHandler;
|
||||
__ws.onmessage = async (event) => {
|
||||
if (typeof event.data === "string") {
|
||||
event = JSON.parse(event.data);
|
||||
__wsJsonHandler(event.event_type, event.event);
|
||||
} else { // Binary
|
||||
await __wsBinHandler(event.data);
|
||||
try {
|
||||
if (typeof event.data === "string") {
|
||||
event = JSON.parse(event.data);
|
||||
__wsJsonHandler(event.event_type, event.event);
|
||||
} else { // Binary
|
||||
await __wsBinHandler(event.data);
|
||||
}
|
||||
} catch (ex) {
|
||||
__wsErrorHandler(ex);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -142,10 +147,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
||||
clearInterval(__ping_timer);
|
||||
__ping_timer = null;
|
||||
}
|
||||
if (__decoder) {
|
||||
__decoder.close();
|
||||
__decoder = null;
|
||||
}
|
||||
__closeDecoder();
|
||||
__missed_heartbeats = 0;
|
||||
__fps_accum = 0;
|
||||
__ws = null;
|
||||
@ -156,38 +158,12 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
||||
|
||||
var __wsJsonHandler = function(event_type, event) {
|
||||
if (event_type === "media") {
|
||||
__decoderCreate(event.video);
|
||||
__setupCodec(event.video);
|
||||
}
|
||||
};
|
||||
|
||||
var __wsBinHandler = async (data) => {
|
||||
let header = new Uint8Array(data.slice(0, 2));
|
||||
|
||||
if (header[0] === 255) { // Pong
|
||||
__missed_heartbeats = 0;
|
||||
|
||||
} else if (header[0] === 1 && __decoder !== null) { // Video frame
|
||||
let key = !!header[1];
|
||||
if (__decoder.state !== "configured") {
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
await __decoder.configure({"codec": __codec, "optimizeForLatency": true});
|
||||
__setActive();
|
||||
}
|
||||
|
||||
let chunk = new EncodedVideoChunk({ // eslint-disable-line no-undef
|
||||
"timestamp": (performance.now() + performance.timeOrigin) * 1000,
|
||||
"type": (key ? "key" : "delta"),
|
||||
"data": data.slice(2),
|
||||
});
|
||||
await __decoder.decode(chunk);
|
||||
}
|
||||
};
|
||||
|
||||
var __decoderCreate = function(formats) {
|
||||
__decoderDestroy();
|
||||
|
||||
var __setupCodec = function(formats) {
|
||||
__closeDecoder();
|
||||
if (formats.h264 === undefined) {
|
||||
let msg = "No H.264 stream available on PiKVM";
|
||||
__setInfo(false, false, msg);
|
||||
@ -203,19 +179,76 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
||||
__logInfo(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
__decoder = new VideoDecoder({ // eslint-disable-line no-undef
|
||||
"output": __drawFrame,
|
||||
"error": (err) => __logInfo(err.message),
|
||||
});
|
||||
__codec = `avc1.${formats.h264.profile_level_id}`;
|
||||
|
||||
__ws.send(JSON.stringify({
|
||||
"event_type": "start",
|
||||
"event": {"type": "video", "format": "h264"},
|
||||
}));
|
||||
};
|
||||
|
||||
var __wsBinHandler = async (data) => {
|
||||
let header = new Uint8Array(data.slice(0, 2));
|
||||
if (header[0] === 255) { // Pong
|
||||
__missed_heartbeats = 0;
|
||||
} else if (header[0] === 1) { // Video frame
|
||||
let key = !!header[1];
|
||||
if (await __ensureDecoder(key)) {
|
||||
await __processFrame(key, data.slice(2));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var __ensureDecoder = async (key) => {
|
||||
if (__codec === "") {
|
||||
return false;
|
||||
}
|
||||
if (__decoder === null || __decoder.state === "closed") {
|
||||
let started = (__codec !== "");
|
||||
let codec = __codec;
|
||||
__closeDecoder();
|
||||
__codec = codec;
|
||||
__decoder = new VideoDecoder({ // eslint-disable-line no-undef
|
||||
"output": __drawFrame,
|
||||
"error": (err) => __logInfo(err.message),
|
||||
});
|
||||
if (started) {
|
||||
__ws.send(new Uint8Array([0]));
|
||||
}
|
||||
}
|
||||
if (__decoder.state !== "configured") {
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
await __decoder.configure({"codec": __codec, "optimizeForLatency": true});
|
||||
}
|
||||
if (__decoder.state === "configured") {
|
||||
__setActive();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var __processFrame = async (key, raw) => {
|
||||
let chunk = new EncodedVideoChunk({ // eslint-disable-line no-undef
|
||||
"timestamp": (performance.now() + performance.timeOrigin) * 1000,
|
||||
"type": (key ? "key" : "delta"),
|
||||
"data": raw,
|
||||
});
|
||||
await __decoder.decode(chunk);
|
||||
};
|
||||
|
||||
var __closeDecoder = function() {
|
||||
if (__decoder !== null) {
|
||||
try {
|
||||
__decoder.close();
|
||||
} catch { // eslint-disable-line no-empty
|
||||
} finally {
|
||||
__decoder = null;
|
||||
__codec = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var __drawFrame = function(frame) {
|
||||
try {
|
||||
let width = frame.displayWidth;
|
||||
@ -254,14 +287,6 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
||||
}
|
||||
};
|
||||
|
||||
var __decoderDestroy = function() {
|
||||
if (__decoder !== null) {
|
||||
__decoder.close();
|
||||
__decoder = null;
|
||||
__codec = "";
|
||||
}
|
||||
};
|
||||
|
||||
var __logInfo = (...args) => tools.info("Stream [Media]:", ...args);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user