import { Router } from "jsr:@oak/oak@14"; // @deno-types="npm:@types/sdp-transform" import sdpTransform from "npm:sdp-transform@2.14.2"; import { encodeHex,} from "https://deno.land/std@0.224.0/encoding/hex.ts"; import { parseIceCandidate, IceCandidate } from "./src/iceCandiate.ts"; const router = new Router({ prefix: "/api", }); export {router as router_webrtc}; let rxAddress: Deno.Addr | undefined = undefined; const ffmpegSocket = Deno.listenDatagram({ hostname: "localhost", port: 1234, transport: "udp" }); let ffmpegRxRun = true; // deno-lint-ignore no-async-promise-executor const _ffmpegRxPromose = new Promise(async (resolve, _reject) => { while(ffmpegRxRun) { const data = new Uint8Array(65536); await ffmpegSocket.receive(data); if(rxAddress !== undefined) { await ffmpegSocket.send(data, rxAddress); } } resolve(null); }); interface RTCIceCandidate { candidate: string; sdpMid: string; sdpMLineIndex: number; usernameFragment: string; }; interface RTCSessionDescription { sdp: string; type: "offer" | "answer"; } async function getSDP(): Promise { return await Deno.readTextFile("./output.sdp"); } async function getFingerprint(): Promise { const cert = new Uint8Array(32); crypto.getRandomValues(cert); const hash = encodeHex(await crypto.subtle.digest("SHA-256", cert)).toUpperCase(); return hash.split(/(..)/g).filter((s: string) => s !== "").join(":"); } async function modifySDP(sdp: string): Promise { //let sdpObj = sdpTransform.parse(sdp); const localAddress = "127.0.0.1"; let sdpObj: sdpTransform.SessionDescription = { fingerprint: { "type": "SHA-256", "hash": await getFingerprint() }, icePwd : "AAAABBBB", iceUfrag: "AAAABBBB", origin: { username: "-", sessionId: (Math.random()*10000000).toFixed(0), sessionVersion: 0, netType: "IN", ipVer: 4, address: localAddress, }, timing: { start: 0, stop: 0 }, media: [ { type: "video", port: 1235, protocol: "RTP/AVP", direction: "sendrecv", connection: { ip: localAddress, version: 4, }, payloads: "126", fmtp: [ ], rtp: [ { payload: 126, codec: "H264", rate: 90000 } ] } ] }; console.log(sdpObj); return sdpTransform.write(sdpObj); } router.post("/webrtc", async (ctx) => { const body: { ice: RTCIceCandidate[]; offer: RTCSessionDescription } = await ctx.request.body.json(); const ice: RTCIceCandidate[] = []; const answer: RTCSessionDescription = { sdp: await modifySDP(await getSDP()), type: "answer" }; console.log(body.offer); const remoteIce = body.ice.map((candidate: RTCIceCandidate) => parseIceCandidate(candidate.candidate)) .filter((c: IceCandidate | null) => c !== null) .filter((c: IceCandidate | null) => c?.transport === "UDP" && c?.candidateType === "host") //console.log(remoteIce); if (body) { ctx.response.status = 200; ctx.response.body = { ice: ice, answer: answer }; } else { ctx.response.status = 400; ctx.response.body = { error: "Invalid JSON" }; } });