From 0d596a4afc1694ec64031eb567eb67457c1f7a95 Mon Sep 17 00:00:00 2001 From: Balazs Jantek Date: Wed, 15 Jul 2026 15:32:15 +0200 Subject: [PATCH 1/9] Add WebRTC audio track selector --- embedded-player/embedded-player.js | 64 +++++++++++++++++++++++++++++- src/main/webapp/css/player.css | 26 +++++++++++- src/main/webapp/play.html | 4 ++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/embedded-player/embedded-player.js b/embedded-player/embedded-player.js index 5073ed3c..6b1328b1 100644 --- a/embedded-player/embedded-player.js +++ b/embedded-player/embedded-player.js @@ -4,8 +4,70 @@ var webPlayer = new WebPlayer(window, document.getElementById("video_container") webPlayer.initialize().then(() => { webPlayer.play(); + initializeMultiAudioSelector(); }); +function initializeMultiAudioSelector() { + const streamId = getUrlParameter("id", window.location.search); + const token = getUrlParameter("token", window.location.search); + const container = document.getElementById("audioTrackSelectorContainer"); + const selector = document.getElementById("audioTrackSelector"); + let attempts = 0; + + const findWebRTCHandler = window.setInterval(() => { + attempts++; + const player = webPlayer.videojsPlayer; + const tech = player && typeof player.tech === "function" ? player.tech() : null; + const handler = tech && tech.webrtc; + if (!handler || !handler.webRTCAdaptor) { + if (attempts >= 100) { + window.clearInterval(findWebRTCHandler); + console.warn("[MultiAudio] WebRTC handler was not available"); + } + return; + } + + window.clearInterval(findWebRTCHandler); + const adaptor = handler.webRTCAdaptor; + const updateSelector = trackList => { + console.log("[MultiAudio] Tracks returned by server:", trackList); + const audioTrackIds = [streamId].concat(trackList.filter(trackId => + trackId.startsWith(`${streamId}_audio_`) + )); + console.log("[MultiAudio] Audio tracks available to selector:", audioTrackIds); + + selector.innerHTML = ""; + audioTrackIds.forEach((trackId, index) => { + const option = document.createElement("option"); + option.value = trackId; + option.textContent = index === 0 ? "Audio 1 (default)" : `Audio ${index + 1}`; + selector.appendChild(option); + }); + + const selectAudioTrack = selectedTrackId => { + audioTrackIds.forEach(trackId => { + adaptor.toggleAudio(streamId, trackId, trackId === selectedTrackId); + }); + }; + selector.onchange = () => selectAudioTrack(selector.value); + container.style.display = audioTrackIds.length > 1 ? "block" : "none"; + console.log("[MultiAudio] Selector visible:", audioTrackIds.length > 1); + selectAudioTrack(audioTrackIds[0]); + }; + + player.on("webrtc-info", (event, data) => { + if (data && data.info === "trackList") { + console.log("[MultiAudio] Raw trackList callback:", data.obj); + updateSelector(data.obj.trackList || []); + } + else if (data && data.info === "play_started") { + adaptor.getTracks(streamId, token); + } + }); + adaptor.getTracks(streamId, token); + }, 100); +} + webPlayer.addWebRTCDataListener((data) => { @@ -324,4 +386,4 @@ webPlayer.addPlayerListener((status) => { }); } //console.debug("player event: ", status); -}); \ No newline at end of file +}); diff --git a/src/main/webapp/css/player.css b/src/main/webapp/css/player.css index 19534b23..2bf2b916 100644 --- a/src/main/webapp/css/player.css +++ b/src/main/webapp/css/player.css @@ -8,6 +8,30 @@ body { display: none; } +#audioTrackSelectorContainer { + display: none; + position: fixed; + top: 16px; + right: 16px; + z-index: 10000000; + width: 210px; + padding: 9px 11px; + border-radius: 6px; + background: rgba(0, 0, 0, .72); + color: white; + font-size: 13px; +} + +#audioTrackSelectorContainer label { + display: block; + margin-bottom: 5px; +} + +#audioTrackSelector { + width: 100%; + padding: 5px; +} + #video_info { color: white !important; position: absolute; @@ -90,4 +114,4 @@ body { } #video-overlay img{ width: 25%; -} \ No newline at end of file +} diff --git a/src/main/webapp/play.html b/src/main/webapp/play.html index 23f7c204..41b983f9 100644 --- a/src/main/webapp/play.html +++ b/src/main/webapp/play.html @@ -21,6 +21,10 @@
+
+ + +
Your connection isn't fast enough to play this stream!
Date: Sun, 16 Aug 2026 22:47:57 +0300 Subject: [PATCH 2/9] fix audio selection --- embedded-player/embedded-player.js | 76 +++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/embedded-player/embedded-player.js b/embedded-player/embedded-player.js index 6b1328b1..88588564 100644 --- a/embedded-player/embedded-player.js +++ b/embedded-player/embedded-player.js @@ -9,10 +9,11 @@ webPlayer.initialize().then(() => { function initializeMultiAudioSelector() { const streamId = getUrlParameter("id", window.location.search); - const token = getUrlParameter("token", window.location.search); const container = document.getElementById("audioTrackSelectorContainer"); const selector = document.getElementById("audioTrackSelector"); let attempts = 0; + let lastAudioTrackIds = []; + let trackListRefreshInterval = null; const findWebRTCHandler = window.setInterval(() => { attempts++; @@ -29,13 +30,25 @@ function initializeMultiAudioSelector() { window.clearInterval(findWebRTCHandler); const adaptor = handler.webRTCAdaptor; + window.webRTCAdaptor = adaptor; + console.log("[MultiAudio] Exposed window.webRTCAdaptor"); + + const requestTrackList = () => { + if (typeof adaptor.getTracks === "function") { + adaptor.getTracks(streamId, getUrlParameter("token", window.location.search)); + } + }; + const updateSelector = trackList => { - console.log("[MultiAudio] Tracks returned by server:", trackList); - const audioTrackIds = [streamId].concat(trackList.filter(trackId => - trackId.startsWith(`${streamId}_audio_`) - )); - console.log("[MultiAudio] Audio tracks available to selector:", audioTrackIds); + const audioTrackIds = [streamId].concat(trackList || []) + .filter((trackId, index, trackIds) => trackId && trackIds.indexOf(trackId) === index); + console.log("[MultiAudio] Audio tracks from getTrackList:", audioTrackIds); + container.style.display = audioTrackIds.length > 1 ? "block" : "none"; + if (audioTrackIds.join("|") === lastAudioTrackIds.join("|")) { + return audioTrackIds.length; + } + const selectedTrackId = selector.value; selector.innerHTML = ""; audioTrackIds.forEach((trackId, index) => { const option = document.createElement("option"); @@ -45,26 +58,57 @@ function initializeMultiAudioSelector() { }); const selectAudioTrack = selectedTrackId => { + adaptor.enableTrack(streamId, selectedTrackId, true); audioTrackIds.forEach(trackId => { - adaptor.toggleAudio(streamId, trackId, trackId === selectedTrackId); + if (trackId !== streamId && trackId !== selectedTrackId) { + adaptor.enableTrack(streamId, trackId, false); + } }); }; selector.onchange = () => selectAudioTrack(selector.value); - container.style.display = audioTrackIds.length > 1 ? "block" : "none"; console.log("[MultiAudio] Selector visible:", audioTrackIds.length > 1); - selectAudioTrack(audioTrackIds[0]); + lastAudioTrackIds = audioTrackIds; + if (audioTrackIds.length > 0) { + selector.value = audioTrackIds.includes(selectedTrackId) ? selectedTrackId : audioTrackIds[0]; + selectAudioTrack(selector.value); + } + return audioTrackIds.length; }; - player.on("webrtc-info", (event, data) => { - if (data && data.info === "trackList") { - console.log("[MultiAudio] Raw trackList callback:", data.obj); - updateSelector(data.obj.trackList || []); + const refreshTrackListUntilAudioTracksAreReady = () => { + let refreshAttempts = 0; + if (trackListRefreshInterval) { + window.clearInterval(trackListRefreshInterval); } - else if (data && data.info === "play_started") { - adaptor.getTracks(streamId, token); + trackListRefreshInterval = window.setInterval(() => { + refreshAttempts++; + requestTrackList(); + if (lastAudioTrackIds.length > 1 || refreshAttempts >= 20) { + window.clearInterval(trackListRefreshInterval); + trackListRefreshInterval = null; + } + }, 500); + }; + + const handleWebRTCInfo = (info, obj) => { + console.log("[MultiAudio] WebRTC info:", info, obj); + if (info === "play_started") { + refreshTrackListUntilAudioTracksAreReady(); } + else if (info === "trackList" && obj && obj.streamId === streamId) { + updateSelector(obj.trackList || []); + } + }; + + if (typeof adaptor.addEventListener === "function") { + adaptor.addEventListener(handleWebRTCInfo); + } + + player.on("webrtc-info", (event, data) => { + const payload = data || event || {}; + handleWebRTCInfo(payload.info, payload.obj); }); - adaptor.getTracks(streamId, token); + requestTrackList(); }, 100); } From 76b916cb0f03ba70e75170ea2afcb75de348ed87 Mon Sep 17 00:00:00 2001 From: burak Date: Sat, 22 Aug 2026 18:47:47 +0300 Subject: [PATCH 3/9] fix webrtc multi-audio naming --- embedded-player/embedded-player.js | 71 +++++++++++++++++++++++++++--- src/main/webapp/play.html | 2 +- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/embedded-player/embedded-player.js b/embedded-player/embedded-player.js index 88588564..96ebc758 100644 --- a/embedded-player/embedded-player.js +++ b/embedded-player/embedded-player.js @@ -5,6 +5,7 @@ var webPlayer = new WebPlayer(window, document.getElementById("video_container") webPlayer.initialize().then(() => { webPlayer.play(); initializeMultiAudioSelector(); + initializeHlsAudioTrackLabels(); }); function initializeMultiAudioSelector() { @@ -40,7 +41,13 @@ function initializeMultiAudioSelector() { }; const updateSelector = trackList => { - const audioTrackIds = [streamId].concat(trackList || []) + const trackIdsFromServer = (trackList || []) + .filter((trackId, index, trackIds) => trackId && trackIds.indexOf(trackId) === index); + const hasLanguageTrackIds = trackIdsFromServer.some(trackId => { + const languagePart = getAudioLanguageFromTrackId(trackId); + return languagePart && !/^audio_\d+$/.test(languagePart); + }); + const audioTrackIds = (hasLanguageTrackIds ? trackIdsFromServer : [streamId].concat(trackIdsFromServer)) .filter((trackId, index, trackIds) => trackId && trackIds.indexOf(trackId) === index); console.log("[MultiAudio] Audio tracks from getTrackList:", audioTrackIds); container.style.display = audioTrackIds.length > 1 ? "block" : "none"; @@ -53,17 +60,20 @@ function initializeMultiAudioSelector() { audioTrackIds.forEach((trackId, index) => { const option = document.createElement("option"); option.value = trackId; - option.textContent = index === 0 ? "Audio 1 (default)" : `Audio ${index + 1}`; + option.textContent = getAudioTrackLabel(trackId, index); selector.appendChild(option); }); const selectAudioTrack = selectedTrackId => { + const primaryAudioTrackId = audioTrackIds[0]; adaptor.enableTrack(streamId, selectedTrackId, true); - audioTrackIds.forEach(trackId => { - if (trackId !== streamId && trackId !== selectedTrackId) { - adaptor.enableTrack(streamId, trackId, false); - } - }); + [streamId].concat(audioTrackIds) + .filter((trackId, index, trackIds) => trackId && trackIds.indexOf(trackId) === index) + .forEach(trackId => { + if (trackId !== selectedTrackId && !(trackId === streamId && selectedTrackId === primaryAudioTrackId)) { + adaptor.enableTrack(streamId, trackId, false); + } + }); }; selector.onchange = () => selectAudioTrack(selector.value); console.log("[MultiAudio] Selector visible:", audioTrackIds.length > 1); @@ -75,6 +85,19 @@ function initializeMultiAudioSelector() { return audioTrackIds.length; }; + const getAudioLanguageFromTrackId = trackId => { + const prefix = `${streamId}_`; + return trackId && trackId.startsWith(prefix) ? trackId.substring(prefix.length) : ""; + }; + + const getAudioTrackLabel = (trackId, index) => { + const language = getAudioLanguageFromTrackId(trackId); + if (language && !/^audio_\d+$/.test(language)) { + return language.replace(/_/g, " "); + } + return index === 0 ? "Audio 1 (default)" : `Audio ${index + 1}`; + }; + const refreshTrackListUntilAudioTracksAreReady = () => { let refreshAttempts = 0; if (trackListRefreshInterval) { @@ -112,6 +135,40 @@ function initializeMultiAudioSelector() { }, 100); } +function initializeHlsAudioTrackLabels() { + let attempts = 0; + const findVideoJSPlayer = window.setInterval(() => { + attempts++; + const player = webPlayer.videojsPlayer; + if (!player || typeof player.audioTracks !== "function") { + if (attempts >= 100) { + window.clearInterval(findVideoJSPlayer); + } + return; + } + + window.clearInterval(findVideoJSPlayer); + const audioTracks = player.audioTracks(); + const updateDefaultAudioTrackLabels = () => { + for (let i = 0; i < audioTracks.length; i++) { + const track = audioTracks[i]; + if (/^audio_\d+$/.test(track.label) && track.language && track.language.toLowerCase() !== "und") { + track.label = track.language; + } + } + }; + + audioTracks.addEventListener("addtrack", updateDefaultAudioTrackLabels); + audioTracks.addEventListener("change", updateDefaultAudioTrackLabels); + player.on("loadedmetadata", updateDefaultAudioTrackLabels); + player.on("dispose", () => { + audioTracks.removeEventListener("addtrack", updateDefaultAudioTrackLabels); + audioTracks.removeEventListener("change", updateDefaultAudioTrackLabels); + }); + updateDefaultAudioTrackLabels(); + }, 100); +} + webPlayer.addWebRTCDataListener((data) => { diff --git a/src/main/webapp/play.html b/src/main/webapp/play.html index 41b983f9..896cb664 100644 --- a/src/main/webapp/play.html +++ b/src/main/webapp/play.html @@ -35,7 +35,7 @@ -