From 0bd7a674e9c7834efca00da2a6c5c85726801f7c Mon Sep 17 00:00:00 2001 From: a-wing <1@233.email> Date: Sun, 7 Jan 2024 00:17:27 +0800 Subject: [PATCH] feat: add webui audio && video codec --- assets/index.html | 43 +++++++++++++++++++++++++++++++++------ assets/sdp.js | 52 +++++++++++++++++++++++++++++++++++++++++++++++ assets/whep.js | 2 +- assets/whip.js | 8 ++++++-- 4 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 assets/sdp.js diff --git a/assets/index.html b/assets/index.html index 91bcac38..69a52ada 100644 --- a/assets/index.html +++ b/assets/index.html @@ -27,6 +27,23 @@ <div style="display: flex;justify-content: space-evenly;flex-wrap: wrap;"> <fieldset> <legend>WHIP</legend> + <section> + Audio Codec: <select id="whip-audio-codec"> + <option value="" selected>default</option> + <option value="opus/48000">OPUS</option> + <option value="g722/8000">G722</option> + <option value="pcmu/8000">PCMU</option> + <option value="pcma/8000">PCMA</option> + </select> + Video Codec: <select id="whip-video-codec"> + <option value="" selected>default</option> + <option value="av1/90000">AV1</option> + <option value="vp9/90000">VP9</option> + <option value="vp8/90000">VP8</option> + <option value="h264/90000">H264</option> + </select> + </section> + <section>Max Width: <select id="whip-video-width-max"> <option value="" selected>Max</option> <option value="3840">3840px</option> @@ -51,10 +68,10 @@ Video<br /> <div id="remoteVideos"></div> <br /> - <script src="./whip.js"></script> - <script src="./whep.js"></script> - - <script> + <script type="module"> + import convertSessionDescription from "./sdp.js" + import { WHIPClient } from "./whip.js" + import { WHEPClient } from "./whep.js" // Common const idResourceId = "resource" @@ -111,6 +128,8 @@ // WHIP const idWhipLayerSelect = "whip-layer-select" + const idWhipAudioCodec = "whip-audio-codec" + const idWhipVideoCodec = "whip-video-codec" const idWhipVideoWidthMax = "whip-video-width-max" initLayerSelect(idWhipLayerSelect, [ @@ -140,13 +159,23 @@ const layer = getElementValue(idWhipLayerSelect) const index = layers.findIndex(i => i.rid === layer) - sendEncodings = layers.slice(0 - (layers.length - index)) pc.addTransceiver(stream.getVideoTracks()[0], { direction: 'sendonly', - sendEncodings: sendEncodings, + sendEncodings: layers.slice(0 - (layers.length - index)), }) + + const audioCodec = getElementValue(idWhipAudioCodec) + document.getElementById(idWhipAudioCodec).disabled = true + logWhip(`audio codec: ${!audioCodec ? "default" : audioCodec}`) + + const videoCodec = getElementValue(idWhipVideoCodec) + document.getElementById(idWhipVideoCodec).disabled = true + logWhip(`video codec: ${!videoCodec ? "default" : videoCodec}`) + const whip = new WHIPClient() + whip.onAnswer = answer => convertSessionDescription(answer, audioCodec, videoCodec) + const url = location.origin + "/whip/" + resource const token = getElementValue(idBearerToken) try { @@ -158,6 +187,7 @@ document.getElementById(idWhipLayerSelect).disabled = true } + window.startWhip = startWhip // WHEP const idWhepLayerSelect = "whep-layer-select" @@ -218,6 +248,7 @@ initEvevt() } } + window.startWhep = startWhep </script> </body> </html> diff --git a/assets/sdp.js b/assets/sdp.js new file mode 100644 index 00000000..1c5f2303 --- /dev/null +++ b/assets/sdp.js @@ -0,0 +1,52 @@ +// @params {string} SDP +// @params {string} audioCodec +// @params {string} videoCodec +// @return {string} SDP +function convertSessionDescription(sdp, audioCodec, videoCodec) { + const sections = sdp.split("m=") + for (let i = 0; i < sections.length; i++) { + const section = sections[i] + if (section.startsWith("audio") && !!audioCodec) { + sections[i] = setCodec(section, audioCodec) + } else if (section.startsWith("video") && !!videoCodec) { + sections[i] = setCodec(section, videoCodec) + } + } + return sections.join("m=") +} + +function setCodec(section, codec) { + const lines = section.split("\r\n") + const lines2 = [] + const payloadFormats = [] + for (const line of lines) { + if (!line.startsWith("a=rtpmap:")) { + lines2.push(line) + } else { + if (line.toLowerCase().includes(codec)) { + payloadFormats.push(line.slice("a=rtpmap:".length).split(" ")[0]) + lines2.push(line) + } + } + } + + const lines3 = [] + + for (const line of lines2) { + if (line.startsWith("a=fmtp:")) { + if (payloadFormats.includes(line.slice("a=fmtp:".length).split(" ")[0])) { + lines3.push(line) + } + } else if (line.startsWith("a=rtcp-fb:")) { + if (payloadFormats.includes(line.slice("a=rtcp-fb:".length).split(" ")[0])) { + lines3.push(line) + } + } else { + lines3.push(line) + } + } + + return lines3.join("\r\n") +} + +export default convertSessionDescription diff --git a/assets/whep.js b/assets/whep.js index 0f784dc6..d22dca9a 100644 --- a/assets/whep.js +++ b/assets/whep.js @@ -6,7 +6,7 @@ const Extensions = { } -class WHEPClient extends EventTarget { +export class WHEPClient extends EventTarget { constructor() { super(); //Ice properties diff --git a/assets/whip.js b/assets/whip.js index dbb2673d..fbdb9821 100644 --- a/assets/whip.js +++ b/assets/whip.js @@ -1,4 +1,4 @@ -class WHIPClient { +export class WHIPClient { constructor() { //Ice properties this.iceUsername = null; @@ -7,6 +7,9 @@ class WHIPClient { this.candidates = []; this.endOfcandidates = false; this.etag = ""; + + this.onOffer = offer => offer; + this.onAnswer = answer => answer; } async publish(pc, url, token) { @@ -38,6 +41,7 @@ class WHIPClient { } //Create SDP offer const offer = await pc.createOffer(); + offer.sdp = this.onOffer(offer.sdp) //Request headers const headers = { @@ -172,7 +176,7 @@ class WHIPClient { //} //And set remote description - await pc.setRemoteDescription({ type: "answer", sdp: answer }); + await pc.setRemoteDescription({ type: "answer", sdp: this.onAnswer(answer) }); } restart() {