Create "working" example
This commit is contained in:
3
gobot-gui/api/.vscode/settings.json
vendored
Normal file
3
gobot-gui/api/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"deno.enable": true
|
||||
}
|
||||
0
gobot-gui/api/asd.json
Normal file
0
gobot-gui/api/asd.json
Normal file
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"tasks": {
|
||||
"dev": "deno run --watch main.ts"
|
||||
},
|
||||
"imports": {
|
||||
"@earthstar/dns-sd": "jsr:@earthstar/dns-sd@^3.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
169
gobot-gui/api/deno.lock
generated
Normal file
169
gobot-gui/api/deno.lock
generated
Normal file
@@ -0,0 +1,169 @@
|
||||
{
|
||||
"version": "3",
|
||||
"packages": {
|
||||
"specifiers": {
|
||||
"jsr:@earthstar/dns-sd": "jsr:@earthstar/dns-sd@3.1.0",
|
||||
"jsr:@oak/commons@0.7": "jsr:@oak/commons@0.7.0",
|
||||
"jsr:@oak/oak@14": "jsr:@oak/oak@14.2.0",
|
||||
"jsr:@std/assert": "jsr:@std/assert@0.218.2",
|
||||
"jsr:@std/assert@0.218": "jsr:@std/assert@0.218.2",
|
||||
"jsr:@std/assert@^0.218.2": "jsr:@std/assert@0.218.2",
|
||||
"jsr:@std/bytes@0.218": "jsr:@std/bytes@0.218.2",
|
||||
"jsr:@std/bytes@^0.218.2": "jsr:@std/bytes@0.218.2",
|
||||
"jsr:@std/bytes@^0.224.0": "jsr:@std/bytes@0.224.0",
|
||||
"jsr:@std/crypto": "jsr:@std/crypto@0.218.2",
|
||||
"jsr:@std/crypto@0.218": "jsr:@std/crypto@0.218.2",
|
||||
"jsr:@std/encoding": "jsr:@std/encoding@0.218.2",
|
||||
"jsr:@std/encoding@0.218": "jsr:@std/encoding@0.218.2",
|
||||
"jsr:@std/encoding@^0.218.2": "jsr:@std/encoding@0.218.2",
|
||||
"jsr:@std/fmt@^0.218.2": "jsr:@std/fmt@0.218.2",
|
||||
"jsr:@std/http@0.218": "jsr:@std/http@0.218.2",
|
||||
"jsr:@std/io@0.218": "jsr:@std/io@0.218.2",
|
||||
"jsr:@std/media-types@0.218": "jsr:@std/media-types@0.218.2",
|
||||
"jsr:@std/path@0.218": "jsr:@std/path@0.218.2",
|
||||
"npm:@types/node": "npm:@types/node@18.16.19",
|
||||
"npm:@types/sdp-transform": "npm:@types/sdp-transform@2.4.9",
|
||||
"npm:path-to-regexp@6.2.1": "npm:path-to-regexp@6.2.1",
|
||||
"npm:sdp-transform@2.14.2": "npm:sdp-transform@2.14.2"
|
||||
},
|
||||
"jsr": {
|
||||
"@earthstar/dns-sd@3.1.0": {
|
||||
"integrity": "8b76b7ccfe230677f6177227e0250bff350b3b19604b3d8c534c1e2e3318d411",
|
||||
"dependencies": [
|
||||
"jsr:@std/bytes@^0.224.0"
|
||||
]
|
||||
},
|
||||
"@oak/commons@0.7.0": {
|
||||
"integrity": "4bd889b3dc9ddac1b602034d88c137f06de7078775961b51081beb5f175c120b"
|
||||
},
|
||||
"@oak/oak@14.2.0": {
|
||||
"integrity": "b683b089693004ac3bca80b52159b3e9ad214dc8246ff5dc61ba658da78bc166",
|
||||
"dependencies": [
|
||||
"jsr:@oak/commons@0.7",
|
||||
"jsr:@std/assert@0.218",
|
||||
"jsr:@std/bytes@0.218",
|
||||
"jsr:@std/crypto@0.218",
|
||||
"jsr:@std/encoding@0.218",
|
||||
"jsr:@std/http@0.218",
|
||||
"jsr:@std/io@0.218",
|
||||
"jsr:@std/media-types@0.218",
|
||||
"jsr:@std/path@0.218",
|
||||
"npm:path-to-regexp@6.2.1"
|
||||
]
|
||||
},
|
||||
"@std/assert@0.218.2": {
|
||||
"integrity": "7f0a5a1a8cf86607cd6c2c030584096e1ffad27fc9271429a8cb48cfbdee5eaf",
|
||||
"dependencies": [
|
||||
"jsr:@std/fmt@^0.218.2"
|
||||
]
|
||||
},
|
||||
"@std/bytes@0.218.2": {
|
||||
"integrity": "91fe54b232dcca73856b79a817247f4a651dbb60d51baafafb6408c137241670"
|
||||
},
|
||||
"@std/bytes@0.224.0": {
|
||||
"integrity": "a2250e1d0eb7d1c5a426f21267ab9bdeac2447fa87a3d0d1a467d3f7a6058e49"
|
||||
},
|
||||
"@std/crypto@0.218.2": {
|
||||
"integrity": "8c5031a3a1c3ac3bed3c0d4bed2fe7e7faedcb673bbfa0edd10570c8452f5cd2",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@^0.218.2",
|
||||
"jsr:@std/encoding@^0.218.2"
|
||||
]
|
||||
},
|
||||
"@std/encoding@0.218.2": {
|
||||
"integrity": "da55a763c29bf0dbf06fd286430b358266eb99c28789d89fe9a3e28edecb8d8e"
|
||||
},
|
||||
"@std/fmt@0.218.2": {
|
||||
"integrity": "99526449d2505aa758b6cbef81e7dd471d8b28ec0dcb1491d122b284c548788a"
|
||||
},
|
||||
"@std/http@0.218.2": {
|
||||
"integrity": "54223b62702e665b9dab6373ea2e51235e093ef47228d21cfa0469ee5ac75c9b",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@^0.218.2",
|
||||
"jsr:@std/encoding@^0.218.2"
|
||||
]
|
||||
},
|
||||
"@std/io@0.218.2": {
|
||||
"integrity": "c64fbfa087b7c9d4d386c5672f291f607d88cb7d44fc299c20c713e345f2785f",
|
||||
"dependencies": [
|
||||
"jsr:@std/bytes@^0.218.2"
|
||||
]
|
||||
},
|
||||
"@std/media-types@0.218.2": {
|
||||
"integrity": "1ed3bd2a05e44bad3fc2bab1767d0ce7f2fd68baee62a980751ce51633acb788"
|
||||
},
|
||||
"@std/path@0.218.2": {
|
||||
"integrity": "b568fd923d9e53ad76d17c513e7310bda8e755a3e825e6289a0ce536404e2662",
|
||||
"dependencies": [
|
||||
"jsr:@std/assert@^0.218.2"
|
||||
]
|
||||
}
|
||||
},
|
||||
"npm": {
|
||||
"@types/node@18.16.19": {
|
||||
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"@types/sdp-transform@2.4.9": {
|
||||
"integrity": "sha512-bVr+/OoZZy7wrHlNcEAAa6PAgKA4BoXPYVN2EijMC5WnGgQ4ZEuixmKnVs2roiAvr7RhIFVH17QD27cojgIZCg==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"path-to-regexp@6.2.1": {
|
||||
"integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
|
||||
"dependencies": {}
|
||||
},
|
||||
"sdp-transform@2.14.2": {
|
||||
"integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==",
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"redirects": {
|
||||
"https://deno.land/std/testing/asserts.ts": "https://deno.land/std@0.224.0/testing/asserts.ts",
|
||||
"https://deno.land/x/port/mod.ts": "https://deno.land/x/port@1.0.0/mod.ts"
|
||||
},
|
||||
"remote": {
|
||||
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
|
||||
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
|
||||
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
|
||||
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
|
||||
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
|
||||
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
|
||||
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
|
||||
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
|
||||
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
|
||||
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
|
||||
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
|
||||
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
|
||||
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
|
||||
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
|
||||
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
|
||||
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
|
||||
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
|
||||
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
|
||||
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
|
||||
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
|
||||
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
|
||||
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
|
||||
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
|
||||
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
|
||||
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
|
||||
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
|
||||
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
|
||||
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
|
||||
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
|
||||
"https://deno.land/std@0.224.0/testing/asserts.ts": "d0cdbabadc49cc4247a50732ee0df1403fdcd0f95360294ad448ae8c240f3f5c",
|
||||
"https://deno.land/x/free_port@v1.2.0/mod.ts": "512646732aaea41fbfd1f210f3ae82660f38251777d189d290da331d0235a58e",
|
||||
"https://deno.land/x/port@1.0.0/mod.ts": "2dc04ce1ccf133ae09205e30b550044c4c6f64a1a7d00ea91c66dbb9f6cc00f5",
|
||||
"https://deno.land/x/port@1.0.0/types.ts": "42d6ae4147d5d67408d60209da070ddfa79ec8389c6cab1b8002df0cf6c03af6"
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"jsr:@earthstar/dns-sd@^3.1.0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export function add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
// Learn more at https://deno.land/manual/examples/module_metadata#concepts
|
||||
if (import.meta.main) {
|
||||
console.log("Add 2 + 3 =", add(2, 3));
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { assertEquals } from "jsr:@std/assert";
|
||||
import { add } from "./main.ts";
|
||||
|
||||
Deno.test(function addTest() {
|
||||
assertEquals(add(2, 3), 5);
|
||||
});
|
||||
10
gobot-gui/api/output.sdp
Normal file
10
gobot-gui/api/output.sdp
Normal file
@@ -0,0 +1,10 @@
|
||||
v=0
|
||||
o=- 0 0 IN IP4 127.0.0.1
|
||||
s=No Name
|
||||
c=IN IP4 127.0.0.1
|
||||
t=0 0
|
||||
a=tool:libavformat 60.16.100
|
||||
m=video 1234 RTP/AVP 96
|
||||
b=AS:3000
|
||||
a=rtpmap:96 MP4V-ES/90000
|
||||
a=fmtp:96 profile-level-id=1
|
||||
12
gobot-gui/api/scripts/ffmpeg_stream.bash
Normal file
12
gobot-gui/api/scripts/ffmpeg_stream.bash
Normal file
@@ -0,0 +1,12 @@
|
||||
#/usr/bin/bash
|
||||
|
||||
SDP_FILE="output.sdp"
|
||||
|
||||
ffmpeg \
|
||||
-stream_loop -1 \
|
||||
-rtbufsize 2M \
|
||||
-re \
|
||||
-i "test/Big_Buck_Bunny_1080_10s_10MB.mp4" \
|
||||
-b:v 3M \
|
||||
-sdp_file $SDP_FILE \
|
||||
-f rtp rtp://127.0.0.1:1234
|
||||
48
gobot-gui/api/src/iceCandiate.ts
Normal file
48
gobot-gui/api/src/iceCandiate.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
export interface IceCandidate {
|
||||
foundation: string;
|
||||
componentId: number;
|
||||
transport: string;
|
||||
priority: number;
|
||||
ipAddress: string;
|
||||
port: number;
|
||||
candidateType: string;
|
||||
relatedAddress: string | null;
|
||||
relatedPort: number | null;
|
||||
}
|
||||
|
||||
|
||||
export function parseIceCandidate(candidate: string): IceCandidate {
|
||||
const parts = candidate.split(' ');
|
||||
const iceCandidate: IceCandidate = {
|
||||
foundation: parts[0].split(':')[1],
|
||||
componentId: Number.parseInt(parts[1]),
|
||||
transport: parts[2],
|
||||
priority: Number.parseInt(parts[3]),
|
||||
ipAddress: parts[4],
|
||||
port: Number.parseInt(parts[5]),
|
||||
candidateType: parts[7],
|
||||
relatedAddress: null,
|
||||
relatedPort: null
|
||||
};
|
||||
|
||||
for (let i = 8; i < parts.length; i++) {
|
||||
if (parts[i] === 'raddr') {
|
||||
iceCandidate.relatedAddress = parts[i + 1];
|
||||
} else if (parts[i] === 'rport') {
|
||||
iceCandidate.relatedPort = Number.parseInt(parts[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return iceCandidate;
|
||||
}
|
||||
|
||||
export function findHighestPriorityCandidate(candidates: IceCandidate[]): IceCandidate | null {
|
||||
if (candidates.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return candidates.reduce((highest, candidate) =>
|
||||
candidate.priority > highest.priority ? candidate : highest
|
||||
);
|
||||
}
|
||||
145
gobot-gui/api/src/main.ts
Normal file
145
gobot-gui/api/src/main.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { Application,Router } from "jsr:@oak/oak@14";
|
||||
import { crypto } from "jsr:@std/crypto";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { encodeHex } from "jsr:@std/encoding/hex";
|
||||
// @deno-types="npm:@types/sdp-transform"
|
||||
import sdpTransform from "npm:sdp-transform@2.14.2";
|
||||
import { IceCandidate, findHighestPriorityCandidate, parseIceCandidate } from "./iceCandiate.ts";
|
||||
import { browse, MulticastInterface } from "jsr:@earthstar/dns-sd";
|
||||
|
||||
const app = new Application();
|
||||
const router = new Router({
|
||||
prefix: "/api",
|
||||
});
|
||||
|
||||
const rtpIncommingConnection = Deno.listenDatagram({ port: 1234, transport: "udp" });
|
||||
let outgoingAddresses: Map<string, Deno.Addr> = new Map();
|
||||
|
||||
(async () => {
|
||||
for await (const [data, addr] of rtpIncommingConnection) {
|
||||
//console.log(data.length);
|
||||
|
||||
outgoingAddresses.forEach((outgoingAddr) => {
|
||||
rtpIncommingConnection.send(data, outgoingAddr);
|
||||
});
|
||||
}
|
||||
})()
|
||||
|
||||
async function getFingerprint(): Promise<string> {
|
||||
const cert = randomBytes(32);
|
||||
const hash = encodeHex(await crypto.subtle.digest("SHA-256", cert));
|
||||
return hash.split(/(..)/g).filter(s => s !== "").join(":");
|
||||
}
|
||||
|
||||
async function resolveMDNS(address: string): Promise<string | undefined> {
|
||||
const pingCommand = new Deno.Command("timeout", {
|
||||
args: ["2", "ping", "-c", "1", address],
|
||||
});
|
||||
|
||||
const { code, stdout, stderr } = await pingCommand.output();
|
||||
|
||||
if (code !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const realAddr = (new TextDecoder().decode(stdout))
|
||||
.match(/\((.*)\):/)?.at(1);
|
||||
|
||||
return realAddr
|
||||
}
|
||||
|
||||
async function findWorkingIceCandiate(candidates: IceCandidate[]): Promise<{ candiate: IceCandidate, ipAddress: string } | undefined> {
|
||||
const workingCandiates: {
|
||||
candiate: IceCandidate,
|
||||
ipAddress: string,
|
||||
priority: number
|
||||
}[] = [];
|
||||
|
||||
candidates = candidates.sort((a, b) => a.priority - b.priority);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if(candidate.ipAddress === undefined || candidate.transport.toLowerCase() !== "udp") continue;
|
||||
|
||||
const ipAddr = await resolveMDNS(candidate.ipAddress);
|
||||
console.log(ipAddr, candidate.ipAddress);
|
||||
if (ipAddr) {
|
||||
workingCandiates.push({
|
||||
candiate: candidate,
|
||||
ipAddress: ipAddr,
|
||||
priority: candidate.priority
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (workingCandiates.length === 0) {
|
||||
return undefined;
|
||||
} else {
|
||||
return workingCandiates.reduce((highest, candidate) =>
|
||||
candidate.priority > highest.priority ? candidate : highest
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
router.post("/webrtc-offer", async (ctx) => {
|
||||
const body = await ctx.request.body.json();
|
||||
const clientSdp = sdpTransform.parse(body.offer.sdp);
|
||||
const clientIceRaw: {
|
||||
candidate: string,
|
||||
sdpMid: string,
|
||||
sdpMLineIndex: number
|
||||
usernameFragment: string
|
||||
}[] = body.ice;
|
||||
|
||||
const clientIce = clientIceRaw.map((ice) => parseIceCandidate(ice.candidate));
|
||||
const remoteIceCandiate = await findWorkingIceCandiate(clientIce);
|
||||
|
||||
if (!remoteIceCandiate) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.type = "application/json";
|
||||
ctx.response.body = { error: "None of the provided IceCandiates where reachable" };
|
||||
return;
|
||||
}
|
||||
|
||||
const clientAddr: Deno.NetAddr = {
|
||||
transport: "udp",
|
||||
hostname: remoteIceCandiate.ipAddress,
|
||||
port: remoteIceCandiate.candiate.port
|
||||
};
|
||||
|
||||
console.log(clientAddr);
|
||||
|
||||
const ffmpegSdp = sdpTransform.parse(await Deno.readTextFile("./output.sdp"));
|
||||
ffmpegSdp.media[0].fingerprint = {
|
||||
type: "sha-256",
|
||||
hash: await getFingerprint()
|
||||
}
|
||||
|
||||
ffmpegSdp.iceUfrag = encodeHex(randomBytes(32));
|
||||
ffmpegSdp.icePwd = encodeHex(randomBytes(32));
|
||||
ffmpegSdp.media[0].fmtp = [];
|
||||
ffmpegSdp.
|
||||
//ffmpegSdp.media[0].rtcpMux = "";
|
||||
|
||||
const sessionId = clientSdp.origin?.sessionId;
|
||||
if (!sessionId) {
|
||||
ctx.response.status = 400;
|
||||
ctx.response.type = "application/json";
|
||||
ctx.response.body = { error: "sessionId is undefined" };
|
||||
return;
|
||||
}
|
||||
|
||||
outgoingAddresses.set(sessionId.toString(), clientAddr);
|
||||
|
||||
ctx.response.type = "application/json";
|
||||
ctx.response.body = {
|
||||
"type": "answer",
|
||||
"sdp": sdpTransform.write(ffmpegSdp)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
|
||||
app.listen({ port: 3000 });
|
||||
BIN
gobot-gui/api/test/Big_Buck_Bunny_1080_10s_10MB.mp4
Normal file
BIN
gobot-gui/api/test/Big_Buck_Bunny_1080_10s_10MB.mp4
Normal file
Binary file not shown.
114
gobot-gui/api/test/iceCandiates.test.ts
Normal file
114
gobot-gui/api/test/iceCandiates.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { assertEquals } from "jsr:@std/assert";
|
||||
import { IceCandidate, parseIceCandidate } from '../src/iceCandiate.ts';
|
||||
|
||||
const iceCandidates = [
|
||||
"candidate:842163049 1 udp 1677729535 192.168.1.2 3478 typ host",
|
||||
"candidate:842163049 2 udp 1677729534 192.168.1.3 3479 typ srflx raddr 192.168.1.2 rport 3478",
|
||||
"candidate:842163049 1 tcp 1677729533 192.168.1.4 3480 typ relay raddr 192.168.1.3 rport 3479",
|
||||
"candidate:842163049 2 tcp 1677729532 192.168.1.5 3481 typ host",
|
||||
"candidate:842163049 1 udp 1677729531 192.168.1.6 3482 typ srflx rport 3480",
|
||||
"candidate:842163049 2 udp 1677729530 192.168.1.7 3483 typ relay raddr 192.168.1.5",
|
||||
"candidate:842163049 1 tcp 1677729529 192.168.1.8 3484 typ host",
|
||||
"candidate:842163049 2 tcp 1677729528 192.168.1.9 3485 typ srflx raddr 192.168.1.6 rport 3482"
|
||||
];
|
||||
|
||||
const parsedExamples: IceCandidate[] = [
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 1,
|
||||
transport: "udp",
|
||||
priority: 1677729535,
|
||||
ipAddress: "192.168.1.2",
|
||||
port: 3478,
|
||||
candidateType: "host",
|
||||
relatedAddress: null,
|
||||
relatedPort: null
|
||||
},
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 2,
|
||||
transport: "udp",
|
||||
priority: 1677729534,
|
||||
ipAddress: "192.168.1.3",
|
||||
port: 3479,
|
||||
candidateType: "srflx",
|
||||
relatedAddress: "192.168.1.2",
|
||||
relatedPort: 3478
|
||||
},
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 1,
|
||||
transport: "tcp",
|
||||
priority: 1677729533,
|
||||
ipAddress: "192.168.1.4",
|
||||
port: 3480,
|
||||
candidateType: "relay",
|
||||
relatedAddress: "192.168.1.3",
|
||||
relatedPort: 3479
|
||||
},
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 2,
|
||||
transport: "tcp",
|
||||
priority: 1677729532,
|
||||
ipAddress: "192.168.1.5",
|
||||
port: 3481,
|
||||
candidateType: "host",
|
||||
relatedAddress: null,
|
||||
relatedPort: null
|
||||
},
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 1,
|
||||
transport: "udp",
|
||||
priority: 1677729531,
|
||||
ipAddress: "192.168.1.6",
|
||||
port: 3482,
|
||||
candidateType: "srflx",
|
||||
relatedPort: 3480,
|
||||
relatedAddress: null
|
||||
},
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 2,
|
||||
transport: "udp",
|
||||
priority: 1677729530,
|
||||
ipAddress: "192.168.1.7",
|
||||
port: 3483,
|
||||
candidateType: "relay",
|
||||
relatedAddress: "192.168.1.5",
|
||||
relatedPort: null
|
||||
},
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 1,
|
||||
transport: "tcp",
|
||||
priority: 1677729529,
|
||||
ipAddress: "192.168.1.8",
|
||||
port: 3484,
|
||||
candidateType: "host",
|
||||
relatedAddress: null,
|
||||
relatedPort: null
|
||||
},
|
||||
{
|
||||
foundation: "842163049",
|
||||
componentId: 2,
|
||||
transport: "tcp",
|
||||
priority: 1677729528,
|
||||
ipAddress: "192.168.1.9",
|
||||
port: 3485,
|
||||
candidateType: "srflx",
|
||||
relatedAddress: "192.168.1.6",
|
||||
relatedPort: 3482
|
||||
}
|
||||
];
|
||||
|
||||
Deno.test("parseIceCandidate should correctly parse ICE candidates", async (t) => {
|
||||
for (const [index, candidate] of iceCandidates.entries()) {
|
||||
await t.step(`${index}-${candidate}`, () => {
|
||||
const parsed = parseIceCandidate(candidate);
|
||||
console.log(candidate);
|
||||
assertEquals(parsed, parsedExamples[index]);
|
||||
})
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user