@eyepop.ai/eyepop 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -23
- package/dist/eyepop.min.js +1 -1
- package/dist/eyepop.min.js.LICENSE.txt +18 -0
- package/dist/index.d.mts +40 -12
- package/dist/index.d.ts +40 -12
- package/dist/index.js +590 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +580 -60
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __commonJS = (cb, mod) => function __require() {
|
|
7
9
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
@@ -18,6 +20,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
20
|
}
|
|
19
21
|
return to;
|
|
20
22
|
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
21
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
32
|
|
|
23
33
|
// node_modules/mime-db/db.json
|
|
@@ -8934,7 +8944,7 @@ var AbstractJob = class {
|
|
|
8934
8944
|
return Promise.reject("abstract class");
|
|
8935
8945
|
}
|
|
8936
8946
|
start(done, status) {
|
|
8937
|
-
this._responseStream = new Promise(async (
|
|
8947
|
+
this._responseStream = new Promise(async (resolve, reject) => {
|
|
8938
8948
|
try {
|
|
8939
8949
|
let retries = 1;
|
|
8940
8950
|
while (true) {
|
|
@@ -8963,7 +8973,7 @@ var AbstractJob = class {
|
|
|
8963
8973
|
reject(`response body is empty`);
|
|
8964
8974
|
break;
|
|
8965
8975
|
} else {
|
|
8966
|
-
|
|
8976
|
+
resolve(response.body);
|
|
8967
8977
|
break;
|
|
8968
8978
|
}
|
|
8969
8979
|
} catch (error) {
|
|
@@ -9054,6 +9064,39 @@ var LoadFromJob = class extends AbstractJob {
|
|
|
9054
9064
|
return response;
|
|
9055
9065
|
}
|
|
9056
9066
|
};
|
|
9067
|
+
var LoadLiveIngressJob = class extends AbstractJob {
|
|
9068
|
+
constructor(ingressId, getSession, client, requestLogger) {
|
|
9069
|
+
super(getSession, client, requestLogger);
|
|
9070
|
+
this._ingressId = ingressId;
|
|
9071
|
+
}
|
|
9072
|
+
get [Symbol.toStringTag]() {
|
|
9073
|
+
return "loadLiveIngressJob";
|
|
9074
|
+
}
|
|
9075
|
+
async startJob() {
|
|
9076
|
+
const body = {
|
|
9077
|
+
"sourceType": "LIVE_INGRESS",
|
|
9078
|
+
"liveIngressId": this._ingressId
|
|
9079
|
+
};
|
|
9080
|
+
const session = await this._getSession();
|
|
9081
|
+
const headers = {
|
|
9082
|
+
"Authorization": `Bearer ${session.accessToken}`,
|
|
9083
|
+
"Accept": "application/jsonl",
|
|
9084
|
+
"Content-Type": "application/json"
|
|
9085
|
+
};
|
|
9086
|
+
const patchUrl = `${session.baseUrl.replace(/\/+$/, "")}/pipelines/${session.pipelineId}/source?mode=queue&processing=sync`;
|
|
9087
|
+
this._requestLogger.debug("before PATCH %s with url %s as source", patchUrl, this._ingressId);
|
|
9088
|
+
const response = await this._client.fetch(patchUrl, {
|
|
9089
|
+
headers,
|
|
9090
|
+
method: "PATCH",
|
|
9091
|
+
body: JSON.stringify(body),
|
|
9092
|
+
signal: this._controller.signal,
|
|
9093
|
+
// @ts-ignore
|
|
9094
|
+
dispatcher: this._dispatcher
|
|
9095
|
+
});
|
|
9096
|
+
this._requestLogger.debug("after PATCH %s with url %s as source", patchUrl, this._ingressId);
|
|
9097
|
+
return response;
|
|
9098
|
+
}
|
|
9099
|
+
};
|
|
9057
9100
|
|
|
9058
9101
|
// src/shims/http_client.ts
|
|
9059
9102
|
var createHttpClient;
|
|
@@ -9222,45 +9265,391 @@ var Semaphore = class {
|
|
|
9222
9265
|
}
|
|
9223
9266
|
};
|
|
9224
9267
|
|
|
9225
|
-
//
|
|
9226
|
-
var
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9268
|
+
// node_modules/uuid/dist/esm-node/rng.js
|
|
9269
|
+
var import_crypto = __toESM(require("crypto"));
|
|
9270
|
+
var rnds8Pool = new Uint8Array(256);
|
|
9271
|
+
var poolPtr = rnds8Pool.length;
|
|
9272
|
+
function rng() {
|
|
9273
|
+
if (poolPtr > rnds8Pool.length - 16) {
|
|
9274
|
+
import_crypto.default.randomFillSync(rnds8Pool);
|
|
9275
|
+
poolPtr = 0;
|
|
9276
|
+
}
|
|
9277
|
+
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
9278
|
+
}
|
|
9279
|
+
|
|
9280
|
+
// node_modules/uuid/dist/esm-node/stringify.js
|
|
9281
|
+
var byteToHex = [];
|
|
9282
|
+
for (let i = 0; i < 256; ++i) {
|
|
9283
|
+
byteToHex.push((i + 256).toString(16).slice(1));
|
|
9284
|
+
}
|
|
9285
|
+
function unsafeStringify(arr, offset = 0) {
|
|
9286
|
+
return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
|
|
9287
|
+
}
|
|
9288
|
+
|
|
9289
|
+
// node_modules/uuid/dist/esm-node/native.js
|
|
9290
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
9291
|
+
var native_default = {
|
|
9292
|
+
randomUUID: import_crypto2.default.randomUUID
|
|
9293
|
+
};
|
|
9294
|
+
|
|
9295
|
+
// node_modules/uuid/dist/esm-node/v4.js
|
|
9296
|
+
function v4(options, buf, offset) {
|
|
9297
|
+
if (native_default.randomUUID && !buf && !options) {
|
|
9298
|
+
return native_default.randomUUID();
|
|
9299
|
+
}
|
|
9300
|
+
options = options || {};
|
|
9301
|
+
const rnds = options.random || (options.rng || rng)();
|
|
9302
|
+
rnds[6] = rnds[6] & 15 | 64;
|
|
9303
|
+
rnds[8] = rnds[8] & 63 | 128;
|
|
9304
|
+
if (buf) {
|
|
9305
|
+
offset = offset || 0;
|
|
9306
|
+
for (let i = 0; i < 16; ++i) {
|
|
9307
|
+
buf[offset + i] = rnds[i];
|
|
9308
|
+
}
|
|
9309
|
+
return buf;
|
|
9310
|
+
}
|
|
9311
|
+
return unsafeStringify(rnds);
|
|
9312
|
+
}
|
|
9313
|
+
var v4_default = v4;
|
|
9314
|
+
|
|
9315
|
+
// src/web_rtc_base.ts
|
|
9316
|
+
var WebRtcBase = class {
|
|
9317
|
+
constructor(getSession, client, requestLogger) {
|
|
9318
|
+
this._pc = null;
|
|
9319
|
+
this._getSession = getSession;
|
|
9320
|
+
this._client = client;
|
|
9321
|
+
this._requestLogger = requestLogger;
|
|
9322
|
+
this._eTag = "";
|
|
9323
|
+
this._queuedCandidates = [];
|
|
9324
|
+
this._offerData = null;
|
|
9325
|
+
}
|
|
9326
|
+
static linkToIceServers(links) {
|
|
9327
|
+
const servers = links !== null ? links.split(", ").map((link) => {
|
|
9328
|
+
const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
|
|
9329
|
+
if (!m) {
|
|
9330
|
+
return null;
|
|
9331
|
+
}
|
|
9332
|
+
const iceServer = {
|
|
9333
|
+
urls: [m[1]],
|
|
9334
|
+
username: Whip.unquoteCredential(m[3]),
|
|
9335
|
+
credential: Whip.unquoteCredential(m[4])
|
|
9336
|
+
// credentialType: m[3]? "password" : undefined
|
|
9337
|
+
};
|
|
9338
|
+
return iceServer;
|
|
9339
|
+
}) : [];
|
|
9340
|
+
return servers.filter(function(el) {
|
|
9341
|
+
return el != null;
|
|
9342
|
+
});
|
|
9343
|
+
}
|
|
9344
|
+
static editAnswer(answer, videoCodec, audioCodec, videoBitrate, audioBitrate, audioVoice) {
|
|
9345
|
+
const sections = answer.split("m=");
|
|
9346
|
+
for (let i = 0; i < sections.length; i++) {
|
|
9347
|
+
const section = sections[i];
|
|
9348
|
+
if (section.startsWith("video")) {
|
|
9349
|
+
sections[i] = Whip.setVideoBitrate(Whip.setCodec(section, videoCodec), videoBitrate);
|
|
9350
|
+
} else if (section.startsWith("audio")) {
|
|
9351
|
+
sections[i] = Whip.setAudioBitrate(Whip.setCodec(section, audioCodec), audioBitrate, audioVoice);
|
|
9237
9352
|
}
|
|
9238
9353
|
}
|
|
9239
|
-
|
|
9240
|
-
}
|
|
9241
|
-
|
|
9242
|
-
|
|
9243
|
-
|
|
9244
|
-
|
|
9354
|
+
return sections.join("m=");
|
|
9355
|
+
}
|
|
9356
|
+
static setCodec(section, codec) {
|
|
9357
|
+
const lines = section.split("\r\n");
|
|
9358
|
+
const lines2 = [];
|
|
9359
|
+
const payloadFormats = [];
|
|
9360
|
+
for (const line of lines) {
|
|
9361
|
+
if (!line.startsWith("a=rtpmap:")) {
|
|
9362
|
+
lines2.push(line);
|
|
9363
|
+
} else {
|
|
9364
|
+
if (codec && line.toLowerCase().includes(codec)) {
|
|
9365
|
+
payloadFormats.push(line.slice("a=rtpmap:".length).split(" ")[0]);
|
|
9366
|
+
lines2.push(line);
|
|
9367
|
+
}
|
|
9368
|
+
}
|
|
9245
9369
|
}
|
|
9246
|
-
const
|
|
9247
|
-
const
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9370
|
+
const lines3 = [];
|
|
9371
|
+
for (const line of lines2) {
|
|
9372
|
+
if (line.startsWith("a=fmtp:")) {
|
|
9373
|
+
if (payloadFormats.includes(line.slice("a=fmtp:".length).split(" ")[0])) {
|
|
9374
|
+
lines3.push(line);
|
|
9375
|
+
}
|
|
9376
|
+
} else if (line.startsWith("a=rtcp-fb:")) {
|
|
9377
|
+
if (payloadFormats.includes(line.slice("a=rtcp-fb:".length).split(" ")[0])) {
|
|
9378
|
+
lines3.push(line);
|
|
9379
|
+
}
|
|
9253
9380
|
} else {
|
|
9254
|
-
|
|
9381
|
+
lines3.push(line);
|
|
9382
|
+
}
|
|
9383
|
+
}
|
|
9384
|
+
return lines3.join("\r\n");
|
|
9385
|
+
}
|
|
9386
|
+
static setVideoBitrate(section, bitrate) {
|
|
9387
|
+
let lines = section.split("\r\n");
|
|
9388
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9389
|
+
if (lines[i].startsWith("c=")) {
|
|
9390
|
+
lines = [...lines.slice(0, i + 1), "b=TIAS:" + (bitrate * 1024).toString(), ...lines.slice(i + 1)];
|
|
9391
|
+
break;
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
return lines.join("\r\n");
|
|
9395
|
+
}
|
|
9396
|
+
static setAudioBitrate(section, bitrate, voice) {
|
|
9397
|
+
let opusPayloadFormat = "";
|
|
9398
|
+
let lines = section.split("\r\n");
|
|
9399
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9400
|
+
if (lines[i].startsWith("a=rtpmap:") && lines[i].toLowerCase().includes("opus/")) {
|
|
9401
|
+
opusPayloadFormat = lines[i].slice("a=rtpmap:".length).split(" ")[0];
|
|
9402
|
+
break;
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
if (opusPayloadFormat === "") {
|
|
9406
|
+
return section;
|
|
9407
|
+
}
|
|
9408
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9409
|
+
if (lines[i].startsWith("a=fmtp:" + opusPayloadFormat + " ")) {
|
|
9410
|
+
if (voice) {
|
|
9411
|
+
lines[i] = "a=fmtp:" + opusPayloadFormat + " minptime=10;useinbandfec=1;maxaveragebitrate=" + ((bitrate ?? 0) * 1024).toString();
|
|
9412
|
+
} else {
|
|
9413
|
+
lines[i] = "a=fmtp:" + opusPayloadFormat + " maxplaybackrate=48000;stereo=1;sprop-stereo=1;maxaveragebitrate" + ((bitrate ?? 0) * 1024).toString();
|
|
9414
|
+
}
|
|
9255
9415
|
}
|
|
9256
|
-
stream = fs.createReadStream(params.filePath);
|
|
9257
9416
|
}
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9417
|
+
return lines.join("\r\n");
|
|
9418
|
+
}
|
|
9419
|
+
static parseOffer(offer) {
|
|
9420
|
+
let medias = [];
|
|
9421
|
+
let iceUfrag = "";
|
|
9422
|
+
let icePwd = "";
|
|
9423
|
+
for (const line of offer.split("\r\n")) {
|
|
9424
|
+
if (line.startsWith("m=")) {
|
|
9425
|
+
medias.push(line.slice("m=".length));
|
|
9426
|
+
} else if (iceUfrag === "" && line.startsWith("a=ice-ufrag:")) {
|
|
9427
|
+
iceUfrag = line.slice("a=ice-ufrag:".length);
|
|
9428
|
+
} else if (icePwd === "" && line.startsWith("a=ice-pwd:")) {
|
|
9429
|
+
icePwd = line.slice("a=ice-pwd:".length);
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
return {
|
|
9433
|
+
iceUfrag,
|
|
9434
|
+
icePwd,
|
|
9435
|
+
medias
|
|
9436
|
+
};
|
|
9437
|
+
}
|
|
9438
|
+
static generateSdpFragment(offerData, candidates) {
|
|
9439
|
+
const candidatesByMedia = /* @__PURE__ */ new Map();
|
|
9440
|
+
for (const candidate of candidates) {
|
|
9441
|
+
if (candidate.sdpMLineIndex !== null) {
|
|
9442
|
+
const mid2 = candidate.sdpMLineIndex;
|
|
9443
|
+
let matched = candidatesByMedia.get(mid2);
|
|
9444
|
+
if (matched === void 0) {
|
|
9445
|
+
matched = [];
|
|
9446
|
+
candidatesByMedia.set(mid2, matched);
|
|
9447
|
+
}
|
|
9448
|
+
matched.push(candidate);
|
|
9449
|
+
}
|
|
9450
|
+
}
|
|
9451
|
+
let frag = "a=ice-ufrag:" + offerData.iceUfrag + "\r\na=ice-pwd:" + offerData.icePwd + "\r\n";
|
|
9452
|
+
let mid = 0;
|
|
9453
|
+
for (const media of offerData.medias) {
|
|
9454
|
+
let matched = candidatesByMedia.get(mid);
|
|
9455
|
+
if (matched !== void 0) {
|
|
9456
|
+
frag += "m=" + media + "\r\na=mid:" + mid + "\r\n";
|
|
9457
|
+
for (const candidate of matched) {
|
|
9458
|
+
frag += "a=" + candidate.candidate + "\r\n";
|
|
9459
|
+
}
|
|
9460
|
+
}
|
|
9461
|
+
mid++;
|
|
9462
|
+
}
|
|
9463
|
+
return frag;
|
|
9464
|
+
}
|
|
9465
|
+
static unquoteCredential(v) {
|
|
9466
|
+
if (!v) {
|
|
9467
|
+
return "";
|
|
9468
|
+
}
|
|
9469
|
+
return JSON.parse(`"${v}"`);
|
|
9470
|
+
}
|
|
9471
|
+
};
|
|
9472
|
+
|
|
9473
|
+
// src/whip.ts
|
|
9474
|
+
var Whip = class _Whip extends WebRtcBase {
|
|
9475
|
+
constructor(stream, getSession, client, requestLogger) {
|
|
9476
|
+
super(getSession, client, requestLogger);
|
|
9477
|
+
this._ingressId = v4_default().toString();
|
|
9478
|
+
this._stream = stream;
|
|
9479
|
+
}
|
|
9480
|
+
ingressId() {
|
|
9481
|
+
if (!this._ingressId) {
|
|
9482
|
+
throw new Error("not initialized");
|
|
9483
|
+
}
|
|
9484
|
+
return this._ingressId;
|
|
9485
|
+
}
|
|
9486
|
+
async start() {
|
|
9487
|
+
const session = await this._getSession();
|
|
9488
|
+
const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
|
|
9489
|
+
this._requestLogger.debug("before GET: %s", ingressUrl);
|
|
9490
|
+
const headers = {
|
|
9491
|
+
"Authorization": `Bearer ${session.accessToken}`
|
|
9492
|
+
};
|
|
9493
|
+
const response = await this._client.fetch(ingressUrl, {
|
|
9494
|
+
headers,
|
|
9495
|
+
method: "GET"
|
|
9496
|
+
});
|
|
9497
|
+
if (response.status >= 300) {
|
|
9498
|
+
return Promise.reject(`unknown status code for GET '${ingressUrl}': ${response.status} (${response.statusText})`);
|
|
9499
|
+
}
|
|
9500
|
+
return this.onIceServers(response);
|
|
9501
|
+
}
|
|
9502
|
+
async onIceServers(response) {
|
|
9503
|
+
if (!this._stream) {
|
|
9504
|
+
throw new Error("onIceServers no stream");
|
|
9505
|
+
}
|
|
9506
|
+
const stream = this._stream;
|
|
9507
|
+
const pc = new RTCPeerConnection({
|
|
9508
|
+
iceServers: _Whip.linkToIceServers(response.headers.get("Link"))
|
|
9509
|
+
});
|
|
9510
|
+
this._pc = pc;
|
|
9511
|
+
pc.onicecandidate = (evt) => this.onLocalCandidate(evt);
|
|
9512
|
+
return new Promise(async (resolve, reject) => {
|
|
9513
|
+
pc.oniceconnectionstatechange = () => {
|
|
9514
|
+
switch (pc.iceConnectionState) {
|
|
9515
|
+
case "failed":
|
|
9516
|
+
case "disconnected":
|
|
9517
|
+
reject(`peer connection: ${pc.iceConnectionState}`);
|
|
9518
|
+
break;
|
|
9519
|
+
case "connected":
|
|
9520
|
+
resolve(this);
|
|
9521
|
+
break;
|
|
9522
|
+
}
|
|
9523
|
+
};
|
|
9524
|
+
stream.getTracks().forEach((track) => {
|
|
9525
|
+
this._requestLogger.debug("onIceServers our stream has track: %s", track);
|
|
9526
|
+
pc.addTrack(track, stream);
|
|
9527
|
+
});
|
|
9528
|
+
stream.addEventListener("addtrack", (trackEvent) => {
|
|
9529
|
+
this._requestLogger.debug("onIceServers got lazy track: ", trackEvent.track);
|
|
9530
|
+
pc.addTrack(trackEvent.track, stream);
|
|
9531
|
+
});
|
|
9532
|
+
await this.onLocalOffer(await pc.createOffer());
|
|
9533
|
+
});
|
|
9534
|
+
}
|
|
9535
|
+
async onLocalCandidate(evt) {
|
|
9536
|
+
if (evt.candidate !== null) {
|
|
9537
|
+
if (this._eTag === "") {
|
|
9538
|
+
this._queuedCandidates.push(evt.candidate);
|
|
9539
|
+
} else {
|
|
9540
|
+
await this.sendLocalCandidates([evt.candidate]);
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
return this;
|
|
9544
|
+
}
|
|
9545
|
+
async onLocalOffer(offer) {
|
|
9546
|
+
if (!this._pc) {
|
|
9547
|
+
throw new Error("onLocalOffer no peer connection");
|
|
9548
|
+
}
|
|
9549
|
+
if (!offer.sdp) {
|
|
9550
|
+
throw new Error("onLocalOffer no sdp");
|
|
9551
|
+
}
|
|
9552
|
+
this._offerData = _Whip.parseOffer(offer.sdp);
|
|
9553
|
+
await this._pc.setLocalDescription(offer);
|
|
9554
|
+
const session = await this._getSession();
|
|
9555
|
+
const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
|
|
9556
|
+
this._requestLogger.debug("before GET: %s", ingressUrl);
|
|
9557
|
+
const headers = {
|
|
9558
|
+
"Authorization": `Bearer ${session.accessToken}`,
|
|
9559
|
+
"Content-Type": "application/sdp"
|
|
9560
|
+
};
|
|
9561
|
+
const response = await this._client.fetch(ingressUrl, {
|
|
9562
|
+
headers,
|
|
9563
|
+
method: "POST",
|
|
9564
|
+
body: offer.sdp
|
|
9565
|
+
});
|
|
9566
|
+
if (response.status != 201) {
|
|
9567
|
+
return Promise.reject(`unknown status code for POST '${ingressUrl}': ${response.status} (${response.statusText})`);
|
|
9568
|
+
}
|
|
9569
|
+
this._eTag = response.headers.get("ETag") ?? "";
|
|
9570
|
+
return this.onRemoteAnswer(new RTCSessionDescription({
|
|
9571
|
+
type: "answer",
|
|
9572
|
+
sdp: await response.text()
|
|
9573
|
+
}));
|
|
9574
|
+
}
|
|
9575
|
+
async onRemoteAnswer(answer) {
|
|
9576
|
+
if (!this._pc) {
|
|
9577
|
+
throw new Error("onLocalOffer no peer connection");
|
|
9578
|
+
}
|
|
9579
|
+
answer = new RTCSessionDescription({
|
|
9580
|
+
type: "answer",
|
|
9581
|
+
sdp: _Whip.editAnswer(
|
|
9582
|
+
answer.sdp,
|
|
9583
|
+
"h264/90000",
|
|
9584
|
+
void 0,
|
|
9585
|
+
5e3,
|
|
9586
|
+
void 0,
|
|
9587
|
+
true
|
|
9588
|
+
)
|
|
9589
|
+
});
|
|
9590
|
+
await this._pc.setRemoteDescription(new RTCSessionDescription(answer));
|
|
9591
|
+
if (this._queuedCandidates.length !== 0) {
|
|
9592
|
+
await this.sendLocalCandidates(this._queuedCandidates);
|
|
9593
|
+
this._queuedCandidates = [];
|
|
9594
|
+
}
|
|
9595
|
+
return this;
|
|
9596
|
+
}
|
|
9597
|
+
async sendLocalCandidates(candidates) {
|
|
9598
|
+
if (!this._offerData) {
|
|
9599
|
+
throw new Error("sendLocalCandidates no offerData");
|
|
9600
|
+
}
|
|
9601
|
+
const session = await this._getSession();
|
|
9602
|
+
const ingressUrl = new URL("/liveIngress/whip/" + this._ingressId, session.baseUrl);
|
|
9603
|
+
this._requestLogger.debug("before PATCH: %s", ingressUrl);
|
|
9604
|
+
const headers = {
|
|
9605
|
+
"Authorization": `Bearer ${session.accessToken}`,
|
|
9606
|
+
"Content-Type": "application/trickle-ice-sdpfrag",
|
|
9607
|
+
"If-Match": this._eTag
|
|
9608
|
+
};
|
|
9609
|
+
const response = await this._client.fetch(ingressUrl, {
|
|
9610
|
+
headers,
|
|
9611
|
+
method: "PATCH",
|
|
9612
|
+
body: _Whip.generateSdpFragment(this._offerData, candidates)
|
|
9613
|
+
});
|
|
9614
|
+
if (response.status != 204) {
|
|
9615
|
+
return Promise.reject(`unknown status code for PATCH '${ingressUrl}': ${response.status} (${response.statusText})`);
|
|
9616
|
+
}
|
|
9617
|
+
return this;
|
|
9618
|
+
}
|
|
9619
|
+
async close() {
|
|
9620
|
+
const pc = this._pc;
|
|
9621
|
+
const stream = this._stream;
|
|
9622
|
+
this._stream = null;
|
|
9623
|
+
this._pc = null;
|
|
9624
|
+
if (stream) {
|
|
9625
|
+
const tracks = stream.getTracks();
|
|
9626
|
+
tracks.forEach((track) => {
|
|
9627
|
+
track.stop();
|
|
9628
|
+
});
|
|
9629
|
+
}
|
|
9630
|
+
if (pc) {
|
|
9631
|
+
return pc.close();
|
|
9632
|
+
}
|
|
9633
|
+
}
|
|
9634
|
+
};
|
|
9635
|
+
|
|
9636
|
+
// src/shims/local_file.ts
|
|
9637
|
+
var resolvePath;
|
|
9638
|
+
if ("document" in globalThis && "implementation" in globalThis.document) {
|
|
9639
|
+
resolvePath = async (source) => {
|
|
9640
|
+
throw new DOMException("resolving a path to a file is not supported in browser");
|
|
9641
|
+
};
|
|
9642
|
+
} else {
|
|
9643
|
+
resolvePath = async (source) => {
|
|
9644
|
+
const mime = require_mime_types();
|
|
9645
|
+
const filehandle = require("fs/promises");
|
|
9646
|
+
const fd = await filehandle.open(source.path, "r");
|
|
9647
|
+
const stream = fd.createReadStream();
|
|
9648
|
+
const mimeType = mime.lookup(source.path) || "application/octet-stream";
|
|
9649
|
+
return {
|
|
9650
|
+
stream,
|
|
9651
|
+
mimeType
|
|
9262
9652
|
};
|
|
9263
|
-
return params;
|
|
9264
9653
|
};
|
|
9265
9654
|
}
|
|
9266
9655
|
|
|
@@ -9277,6 +9666,8 @@ var Endpoint = class {
|
|
|
9277
9666
|
this._client = null;
|
|
9278
9667
|
this._state = "Idle" /* Idle */;
|
|
9279
9668
|
this._stateChangeHandler = null;
|
|
9669
|
+
this._ingressEventHandler = null;
|
|
9670
|
+
this._ingressEventWs = null;
|
|
9280
9671
|
this._limit = new Semaphore(this._options.jobQueueLength ?? 1024);
|
|
9281
9672
|
let rootLogger;
|
|
9282
9673
|
if (options.logger) {
|
|
@@ -9291,6 +9682,51 @@ var Endpoint = class {
|
|
|
9291
9682
|
this._stateChangeHandler = handler;
|
|
9292
9683
|
return this;
|
|
9293
9684
|
}
|
|
9685
|
+
async startIngressWs() {
|
|
9686
|
+
if (this._ingressEventWs || !this._baseUrl || !this._client) {
|
|
9687
|
+
return;
|
|
9688
|
+
}
|
|
9689
|
+
try {
|
|
9690
|
+
const getTokenUrl = `${this._baseUrl}/liveIngress/events/token`;
|
|
9691
|
+
const headers = {
|
|
9692
|
+
"Authorization": await this.authorizationHeader()
|
|
9693
|
+
};
|
|
9694
|
+
this._requestLogger.debug("before GET %s", getTokenUrl);
|
|
9695
|
+
let response = await this._client.fetch(getTokenUrl, { headers });
|
|
9696
|
+
if (response.status == 401) {
|
|
9697
|
+
this._requestLogger.debug("after GET %s: 401, about to retry with fresh access token", getTokenUrl);
|
|
9698
|
+
this._token = null;
|
|
9699
|
+
const headers2 = {
|
|
9700
|
+
"Authorization": await this.authorizationHeader()
|
|
9701
|
+
};
|
|
9702
|
+
this.updateState("FetchConfig" /* FetchConfig */);
|
|
9703
|
+
response = await this._client.fetch(getTokenUrl, { headers: headers2 });
|
|
9704
|
+
}
|
|
9705
|
+
if (response.status != 200) {
|
|
9706
|
+
const message = await response.text();
|
|
9707
|
+
return Promise.reject(`Unexpected status ${response.status}: ${message}`);
|
|
9708
|
+
}
|
|
9709
|
+
const wsAuthToken = await response.json();
|
|
9710
|
+
const wsUrl = this._baseUrl.startsWith("https://") ? `${this._baseUrl.replace("https://", "wss://")}/liveIngress/events/${wsAuthToken.token}` : `${this._baseUrl.replace("http://", "ws://")}/liveIngress/events/${wsAuthToken.token}`;
|
|
9711
|
+
this._ingressEventWs = new WebSocket(wsUrl);
|
|
9712
|
+
this._ingressEventWs.addEventListener("message", (event) => {
|
|
9713
|
+
const ingressEvent = JSON.parse(event.data);
|
|
9714
|
+
if (this._ingressEventHandler) {
|
|
9715
|
+
this._ingressEventHandler(ingressEvent);
|
|
9716
|
+
}
|
|
9717
|
+
});
|
|
9718
|
+
} finally {
|
|
9719
|
+
this.updateState();
|
|
9720
|
+
}
|
|
9721
|
+
}
|
|
9722
|
+
onIngressEvent(handler) {
|
|
9723
|
+
this._ingressEventHandler = handler;
|
|
9724
|
+
this.startIngressWs().then(() => {
|
|
9725
|
+
}).catch((reason) => {
|
|
9726
|
+
this._logger.warn("unexpected error starting the ingress event handler: %s", reason);
|
|
9727
|
+
});
|
|
9728
|
+
return this;
|
|
9729
|
+
}
|
|
9294
9730
|
state() {
|
|
9295
9731
|
return this._state;
|
|
9296
9732
|
}
|
|
@@ -9323,39 +9759,130 @@ var Endpoint = class {
|
|
|
9323
9759
|
pipelineId: this._pipelineId
|
|
9324
9760
|
};
|
|
9325
9761
|
}
|
|
9326
|
-
async
|
|
9762
|
+
async liveIngress(stream) {
|
|
9763
|
+
if (!this._baseUrl || !this._client) {
|
|
9764
|
+
return Promise.reject("endpoint not connected, use connect() before ingress()");
|
|
9765
|
+
}
|
|
9766
|
+
const whip = new Whip(stream, async () => {
|
|
9767
|
+
return this.session();
|
|
9768
|
+
}, this._client, this._requestLogger);
|
|
9769
|
+
return whip.start();
|
|
9770
|
+
}
|
|
9771
|
+
async process(source) {
|
|
9772
|
+
if (source.file !== void 0) {
|
|
9773
|
+
return this.uploadFile(source);
|
|
9774
|
+
} else if (source.stream !== void 0) {
|
|
9775
|
+
return this.uploadStream(source);
|
|
9776
|
+
} else if (source.path !== void 0) {
|
|
9777
|
+
return this.uploadPath(source);
|
|
9778
|
+
} else if (source.url !== void 0) {
|
|
9779
|
+
return this.loadFrom(source);
|
|
9780
|
+
} else if (source.ingressId !== void 0) {
|
|
9781
|
+
return this.loadLiveIngress(source);
|
|
9782
|
+
} else {
|
|
9783
|
+
return Promise.reject("unknown source type");
|
|
9784
|
+
}
|
|
9785
|
+
}
|
|
9786
|
+
async uploadFile(source) {
|
|
9327
9787
|
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
9328
|
-
return Promise.reject("endpoint not connected, use
|
|
9788
|
+
return Promise.reject("endpoint not connected, use connect() before upload()");
|
|
9329
9789
|
}
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9790
|
+
await this._limit.acquire();
|
|
9791
|
+
try {
|
|
9792
|
+
this.updateState();
|
|
9793
|
+
const job = new UploadJob(source.file, source.file.type, async () => {
|
|
9794
|
+
return this.session();
|
|
9795
|
+
}, this._client, this._requestLogger);
|
|
9796
|
+
return job.start(() => {
|
|
9797
|
+
this.jobDone(job);
|
|
9798
|
+
}, (statusCode) => {
|
|
9799
|
+
this.jobStatus(job, statusCode);
|
|
9800
|
+
});
|
|
9801
|
+
} catch (e) {
|
|
9802
|
+
this._limit.release();
|
|
9803
|
+
throw e;
|
|
9804
|
+
}
|
|
9805
|
+
}
|
|
9806
|
+
async uploadStream(source) {
|
|
9807
|
+
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
9808
|
+
return Promise.reject("endpoint not connected, use connect() before upload()");
|
|
9333
9809
|
}
|
|
9334
9810
|
await this._limit.acquire();
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
|
|
9339
|
-
|
|
9340
|
-
|
|
9341
|
-
|
|
9342
|
-
|
|
9343
|
-
|
|
9811
|
+
try {
|
|
9812
|
+
this.updateState();
|
|
9813
|
+
const job = new UploadJob(source.stream, source.mimeType, async () => {
|
|
9814
|
+
return this.session();
|
|
9815
|
+
}, this._client, this._requestLogger);
|
|
9816
|
+
return job.start(() => {
|
|
9817
|
+
this.jobDone(job);
|
|
9818
|
+
}, (statusCode) => {
|
|
9819
|
+
this.jobStatus(job, statusCode);
|
|
9820
|
+
});
|
|
9821
|
+
} catch (e) {
|
|
9822
|
+
this._limit.release();
|
|
9823
|
+
throw e;
|
|
9824
|
+
}
|
|
9344
9825
|
}
|
|
9345
|
-
async
|
|
9826
|
+
async uploadPath(source) {
|
|
9346
9827
|
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
9347
|
-
throw new Error("endpoint not connected, use
|
|
9828
|
+
throw new Error("endpoint not connected, use connect() before process()");
|
|
9348
9829
|
}
|
|
9349
9830
|
await this._limit.acquire();
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9355
|
-
this.
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9831
|
+
try {
|
|
9832
|
+
this.updateState();
|
|
9833
|
+
const streamSource = await resolvePath(source);
|
|
9834
|
+
const job = new UploadJob(streamSource.stream, streamSource.mimeType, async () => {
|
|
9835
|
+
return this.session();
|
|
9836
|
+
}, this._client, this._requestLogger);
|
|
9837
|
+
return job.start(() => {
|
|
9838
|
+
this.jobDone(job);
|
|
9839
|
+
}, (statusCode) => {
|
|
9840
|
+
this.jobStatus(job, statusCode);
|
|
9841
|
+
});
|
|
9842
|
+
} catch (e) {
|
|
9843
|
+
this._limit.release();
|
|
9844
|
+
throw e;
|
|
9845
|
+
}
|
|
9846
|
+
}
|
|
9847
|
+
async loadFrom(source) {
|
|
9848
|
+
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
9849
|
+
throw new Error("endpoint not connected, use connect() before loadFrom()");
|
|
9850
|
+
}
|
|
9851
|
+
await this._limit.acquire();
|
|
9852
|
+
try {
|
|
9853
|
+
this.updateState();
|
|
9854
|
+
const job = new LoadFromJob(source.url, async () => {
|
|
9855
|
+
return this.session();
|
|
9856
|
+
}, this._client, this._requestLogger);
|
|
9857
|
+
return job.start(() => {
|
|
9858
|
+
this.jobDone(job);
|
|
9859
|
+
}, (statusCode) => {
|
|
9860
|
+
this.jobStatus(job, statusCode);
|
|
9861
|
+
});
|
|
9862
|
+
} catch (e) {
|
|
9863
|
+
this._limit.release();
|
|
9864
|
+
throw e;
|
|
9865
|
+
}
|
|
9866
|
+
}
|
|
9867
|
+
async loadLiveIngress(source) {
|
|
9868
|
+
if (!this._baseUrl || !this._pipelineId || !this._client || !this._limit) {
|
|
9869
|
+
throw new Error("endpoint not connected, use connect() before loadLiveIngress()");
|
|
9870
|
+
}
|
|
9871
|
+
await this._limit.acquire();
|
|
9872
|
+
try {
|
|
9873
|
+
this.updateState();
|
|
9874
|
+
const job = new LoadLiveIngressJob(source.ingressId, async () => {
|
|
9875
|
+
return this.session();
|
|
9876
|
+
}, this._client, this._requestLogger);
|
|
9877
|
+
return job.start(() => {
|
|
9878
|
+
this.jobDone(job);
|
|
9879
|
+
}, (statusCode) => {
|
|
9880
|
+
this.jobStatus(job, statusCode);
|
|
9881
|
+
});
|
|
9882
|
+
} catch (e) {
|
|
9883
|
+
this._limit.release();
|
|
9884
|
+
throw e;
|
|
9885
|
+
}
|
|
9359
9886
|
}
|
|
9360
9887
|
// noinspection TypeScriptValidateTypes
|
|
9361
9888
|
async connect() {
|
|
@@ -9394,6 +9921,9 @@ var Endpoint = class {
|
|
|
9394
9921
|
}
|
|
9395
9922
|
this._requestLogger.debug("after PATCH %s", stop_url);
|
|
9396
9923
|
}
|
|
9924
|
+
if (!this._ingressEventWs && this._ingressEventHandler) {
|
|
9925
|
+
await this.startIngressWs();
|
|
9926
|
+
}
|
|
9397
9927
|
return result;
|
|
9398
9928
|
}
|
|
9399
9929
|
jobDone(job) {
|
|
@@ -9556,7 +10086,7 @@ var _EyePopPlot = class _EyePopPlot {
|
|
|
9556
10086
|
}
|
|
9557
10087
|
}
|
|
9558
10088
|
}
|
|
9559
|
-
object(obj, x_scale, y_scale) {
|
|
10089
|
+
object(obj, x_scale = 1, y_scale = 1) {
|
|
9560
10090
|
const context = this._context;
|
|
9561
10091
|
const x = obj.x * x_scale;
|
|
9562
10092
|
const y = obj.y * y_scale;
|