From 1c179da857006852ec037109f3e7f9d51c17022d Mon Sep 17 00:00:00 2001 From: Maxim Devaev Date: Mon, 3 Feb 2025 09:51:25 +0200 Subject: [PATCH] web: orientation changing for media --- web/share/js/kvm/stream.js | 15 +++++--- web/share/js/kvm/stream_media.js | 63 +++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 24 deletions(-) diff --git a/web/share/js/kvm/stream.js b/web/share/js/kvm/stream.js index 34baaa92..14622c5b 100644 --- a/web/share/js/kvm/stream.js +++ b/web/share/js/kvm/stream.js @@ -72,10 +72,10 @@ export function Streamer() { tools.radio.setOnClick("stream-mode-radio", __clickModeRadio, false); // Not getInt() because of radio is a string container. - // Also don't reset Janus at class init. + // Also don't reset Streamer at class init. tools.radio.clickValue("stream-orient-radio", tools.storage.get("stream.orient", 0)); tools.radio.setOnClick("stream-orient-radio", function() { - if (__streamer.getMode() === "janus") { // Right now it's working only for H.264 + if (["janus", "media"].includes(__streamer.getMode())) { let orient = parseInt(tools.radio.getValue("stream-orient-radio")); tools.storage.setInt("stream.orient", orient); if (__streamer.getOrientation() !== orient) { @@ -299,19 +299,22 @@ export function Streamer() { mode = __streamer.getMode(); } __streamer.stopStream(); + let orient = tools.storage.getInt("stream.orient", 0); if (mode === "janus") { - __streamer = new JanusStreamer(__setActive, __setInactive, __setInfo, - tools.storage.getInt("stream.orient", 0), !$("stream-video").muted, $("stream-mic-switch").checked); + let allow_audio = !$("stream-video").muted; + let allow_mic = $("stream-mic-switch").checked; + __streamer = new JanusStreamer(__setActive, __setInactive, __setInfo, orient, allow_audio, allow_mic); // Firefox doesn't support RTP orientation: // - https://bugzilla.mozilla.org/show_bug.cgi?id=1316448 tools.feature.setEnabled($("stream-orient"), !tools.browser.is_firefox); } else { if (mode === "media") { - __streamer = new MediaStreamer(__setActive, __setInactive, __setInfo); + __streamer = new MediaStreamer(__setActive, __setInactive, __setInfo, orient); + tools.feature.setEnabled($("stream-orient"), true); } else { // mjpeg __streamer = new MjpegStreamer(__setActive, __setInactive, __setInfo); + tools.feature.setEnabled($("stream-orient"), false); } - tools.feature.setEnabled($("stream-orient"), false); tools.feature.setEnabled($("stream-audio"), false); // Enabling in stream_janus.js tools.feature.setEnabled($("stream-mic"), false); // Ditto } diff --git a/web/share/js/kvm/stream_media.js b/web/share/js/kvm/stream_media.js index ae751f9d..0e552677 100644 --- a/web/share/js/kvm/stream_media.js +++ b/web/share/js/kvm/stream_media.js @@ -26,7 +26,7 @@ import {tools, $} from "../tools.js"; -export function MediaStreamer(__setActive, __setInactive, __setInfo) { +export function MediaStreamer(__setActive, __setInactive, __setInfo, __orient) { var self = this; /************************************************************************/ @@ -43,11 +43,12 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo) { var __ctx = __canvas.getContext("2d"); var __state = null; - var __frames = 0; + var __fps_accum = 0; /************************************************************************/ - self.getName = () => "HTTP H.264"; + self.getOrientation = () => __orient; + self.getName = () => "Direct H.264"; self.getMode = () => "media"; self.getResolution = function() { @@ -110,8 +111,8 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo) { if (__decoder && __decoder.state === "configured") { let online = !!(__state && __state.source.online); - let info = `${__frames} fps dynamic`; - __frames = 0; + let info = `${__fps_accum} fps dynamic`; + __fps_accum = 0; __setInfo(true, online, info); } } catch (ex) { @@ -145,7 +146,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo) { __decoder = null; } __missed_heartbeats = 0; - __frames = 0; + __fps_accum = 0; __ws = null; if (!__stop) { setTimeout(() => __ensureMedia(true), 1000); @@ -203,18 +204,7 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo) { } __decoder = new VideoDecoder({ // eslint-disable-line no-undef - "output": (frame) => { - try { - if (__canvas.width !== frame.displayWidth || __canvas.height !== frame.displayHeight) { - __canvas.width = frame.displayWidth; - __canvas.height = frame.displayHeight; - } - __ctx.drawImage(frame, 0, 0); - __frames += 1; - } finally { - frame.close(); - } - }, + "output": __drawFrame, "error": (err) => __logInfo(err.message), }); __codec = `avc1.${formats.h264.profile_level_id}`; @@ -225,6 +215,43 @@ export function MediaStreamer(__setActive, __setInactive, __setInfo) { })); }; + var __drawFrame = function(frame) { + try { + let width = frame.displayWidth; + let height = frame.displayHeight; + switch (__orient) { + case 90: case 270: + width = frame.displayHeight; + height = frame.displayWidth; + } + + if (__canvas.width !== width || __canvas.height !== height) { + __canvas.width = width; + __canvas.height = height; + } + + if (__orient === 0) { + __ctx.drawImage(frame, 0, 0); + } else { + __ctx.save(); + try { + switch(__orient) { + case 90: __ctx.translate(0, height); __ctx.rotate(-Math.PI / 2); break; + case 180: __ctx.translate(width, height); __ctx.rotate(-Math.PI); break; + case 270: __ctx.translate(width, 0); __ctx.rotate(Math.PI / 2); break; + } + __ctx.drawImage(frame, 0, 0); + } finally { + __ctx.restore(); + } + } + + __fps_accum += 1; + } finally { + frame.close(); + } + }; + var __decoderDestroy = function() { if (__decoder !== null) { __decoder.close();