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)
|
clients: dict[WsSession, "_Client"] = dataclasses.field(default_factory=dict)
|
||||||
key_required: bool = dataclasses.field(default=False)
|
key_required: bool = dataclasses.field(default=False)
|
||||||
|
|
||||||
|
def is_diff(self) -> bool:
|
||||||
|
return StreamerFormats.is_diff(self.streamer.get_format())
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class _Client:
|
class _Client:
|
||||||
@ -98,6 +101,14 @@ class MediaServer(HttpServer):
|
|||||||
async def __ws_bin_ping_handler(self, ws: WsSession, _: bytes) -> None:
|
async def __ws_bin_ping_handler(self, ws: WsSession, _: bytes) -> None:
|
||||||
await ws.send_bin(255, b"") # Ping-pong
|
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")
|
@exposed_ws("start")
|
||||||
async def __ws_start_handler(self, ws: WsSession, event: dict) -> None:
|
async def __ws_start_handler(self, ws: WsSession, event: dict) -> None:
|
||||||
try:
|
try:
|
||||||
@ -145,7 +156,7 @@ class MediaServer(HttpServer):
|
|||||||
# =====
|
# =====
|
||||||
|
|
||||||
async def __sender(self, client: _Client) -> None:
|
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:
|
if need_key:
|
||||||
client.src.key_required = True
|
client.src.key_required = True
|
||||||
has_key = False
|
has_key = False
|
||||||
|
|||||||
@ -37,6 +37,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
|||||||
var __ws = null;
|
var __ws = null;
|
||||||
var __ping_timer = null;
|
var __ping_timer = null;
|
||||||
var __missed_heartbeats = 0;
|
var __missed_heartbeats = 0;
|
||||||
|
|
||||||
var __decoder = null;
|
var __decoder = null;
|
||||||
var __codec = "";
|
var __codec = "";
|
||||||
var __canvas = $("stream-canvas");
|
var __canvas = $("stream-canvas");
|
||||||
@ -86,12 +87,16 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
|||||||
__ws.onerror = __wsErrorHandler;
|
__ws.onerror = __wsErrorHandler;
|
||||||
__ws.onclose = __wsCloseHandler;
|
__ws.onclose = __wsCloseHandler;
|
||||||
__ws.onmessage = async (event) => {
|
__ws.onmessage = async (event) => {
|
||||||
|
try {
|
||||||
if (typeof event.data === "string") {
|
if (typeof event.data === "string") {
|
||||||
event = JSON.parse(event.data);
|
event = JSON.parse(event.data);
|
||||||
__wsJsonHandler(event.event_type, event.event);
|
__wsJsonHandler(event.event_type, event.event);
|
||||||
} else { // Binary
|
} else { // Binary
|
||||||
await __wsBinHandler(event.data);
|
await __wsBinHandler(event.data);
|
||||||
}
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
__wsErrorHandler(ex);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -142,10 +147,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
|||||||
clearInterval(__ping_timer);
|
clearInterval(__ping_timer);
|
||||||
__ping_timer = null;
|
__ping_timer = null;
|
||||||
}
|
}
|
||||||
if (__decoder) {
|
__closeDecoder();
|
||||||
__decoder.close();
|
|
||||||
__decoder = null;
|
|
||||||
}
|
|
||||||
__missed_heartbeats = 0;
|
__missed_heartbeats = 0;
|
||||||
__fps_accum = 0;
|
__fps_accum = 0;
|
||||||
__ws = null;
|
__ws = null;
|
||||||
@ -156,38 +158,12 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
|||||||
|
|
||||||
var __wsJsonHandler = function(event_type, event) {
|
var __wsJsonHandler = function(event_type, event) {
|
||||||
if (event_type === "media") {
|
if (event_type === "media") {
|
||||||
__decoderCreate(event.video);
|
__setupCodec(event.video);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var __wsBinHandler = async (data) => {
|
var __setupCodec = function(formats) {
|
||||||
let header = new Uint8Array(data.slice(0, 2));
|
__closeDecoder();
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
if (formats.h264 === undefined) {
|
if (formats.h264 === undefined) {
|
||||||
let msg = "No H.264 stream available on PiKVM";
|
let msg = "No H.264 stream available on PiKVM";
|
||||||
__setInfo(false, false, msg);
|
__setInfo(false, false, msg);
|
||||||
@ -203,19 +179,76 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) {
|
|||||||
__logInfo(msg);
|
__logInfo(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
__decoder = new VideoDecoder({ // eslint-disable-line no-undef
|
|
||||||
"output": __drawFrame,
|
|
||||||
"error": (err) => __logInfo(err.message),
|
|
||||||
});
|
|
||||||
__codec = `avc1.${formats.h264.profile_level_id}`;
|
__codec = `avc1.${formats.h264.profile_level_id}`;
|
||||||
|
|
||||||
__ws.send(JSON.stringify({
|
__ws.send(JSON.stringify({
|
||||||
"event_type": "start",
|
"event_type": "start",
|
||||||
"event": {"type": "video", "format": "h264"},
|
"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) {
|
var __drawFrame = function(frame) {
|
||||||
try {
|
try {
|
||||||
let width = frame.displayWidth;
|
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);
|
var __logInfo = (...args) => tools.info("Stream [Media]:", ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user