moqtail 0.8.0 → 0.9.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/dist/{client/index.cjs → client.cjs} +25 -14
- package/dist/{client/index.d.cts → client.d.cts} +8 -2
- package/dist/{client/index.d.ts → client.d.ts} +8 -2
- package/dist/{client/index.js → client.js} +25 -14
- package/dist/index.cjs +239 -316
- package/dist/index.d.cts +4 -5
- package/dist/index.d.ts +4 -5
- package/dist/index.js +239 -311
- package/dist/{model/index.cjs → model.cjs} +214 -0
- package/dist/{model/index.d.ts → model.d.cts} +53 -4
- package/dist/{model/index.d.cts → model.d.ts} +53 -4
- package/dist/{model/index.js → model.js} +214 -1
- package/dist/util.cjs +102 -0
- package/dist/util.d.cts +57 -0
- package/dist/util.d.ts +57 -0
- package/dist/util.js +99 -0
- package/dist/{version_parameter-BpmuiMQj.d.cts → version_parameter-DXBOBueW.d.cts} +986 -2
- package/dist/{version_parameter-f75NkWiO.d.ts → version_parameter-DXBOBueW.d.ts} +986 -2
- package/package.json +20 -23
- package/dist/byte_buffer-BM4uNj4n.d.cts +0 -987
- package/dist/byte_buffer-BM4uNj4n.d.ts +0 -987
- package/dist/util/index.cjs +0 -1804
- package/dist/util/index.d.cts +0 -164
- package/dist/util/index.d.ts +0 -164
- package/dist/util/index.js +0 -1795
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var Heap = require('heap-js');
|
|
4
|
-
|
|
5
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
-
|
|
7
|
-
var Heap__default = /*#__PURE__*/_interopDefault(Heap);
|
|
8
|
-
|
|
9
3
|
var __defProp = Object.defineProperty;
|
|
10
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
5
|
var __esm = (fn, res) => function __init() {
|
|
@@ -4540,6 +4534,219 @@ init_byte_buffer();
|
|
|
4540
4534
|
init_error();
|
|
4541
4535
|
init_constant2();
|
|
4542
4536
|
|
|
4537
|
+
// src/model/catalog/cmsf.ts
|
|
4538
|
+
function isString(v) {
|
|
4539
|
+
return typeof v === "string";
|
|
4540
|
+
}
|
|
4541
|
+
function isNumber(v) {
|
|
4542
|
+
return typeof v === "number";
|
|
4543
|
+
}
|
|
4544
|
+
function isStringArray(v) {
|
|
4545
|
+
return Array.isArray(v) && v.every((item) => isString(item));
|
|
4546
|
+
}
|
|
4547
|
+
function isValidPackaging(v) {
|
|
4548
|
+
return v === "cmaf" || v === "chunk-per-object" || v === "timeline";
|
|
4549
|
+
}
|
|
4550
|
+
function isValidRole(v) {
|
|
4551
|
+
return v === "video" || v === "audio" || v === "timeline";
|
|
4552
|
+
}
|
|
4553
|
+
function isValidBase64(str) {
|
|
4554
|
+
try {
|
|
4555
|
+
return btoa(atob(str)) === str;
|
|
4556
|
+
} catch {
|
|
4557
|
+
return false;
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
function validateTrack(track) {
|
|
4561
|
+
if (typeof track !== "object" || track === null) {
|
|
4562
|
+
throw new Error("Track must be an object");
|
|
4563
|
+
}
|
|
4564
|
+
const t = track;
|
|
4565
|
+
if (!isString(t["name"])) {
|
|
4566
|
+
throw new Error("Track.name must be a string");
|
|
4567
|
+
}
|
|
4568
|
+
if (!isValidPackaging(t["packaging"])) {
|
|
4569
|
+
throw new Error("Track.packaging must be 'cmaf', 'chunk-per-object', or 'timeline'");
|
|
4570
|
+
}
|
|
4571
|
+
if (!isValidRole(t["role"])) {
|
|
4572
|
+
throw new Error("Track.role must be 'video', 'audio', or 'timeline'");
|
|
4573
|
+
}
|
|
4574
|
+
const name = t["name"];
|
|
4575
|
+
const packaging = t["packaging"];
|
|
4576
|
+
const role = t["role"];
|
|
4577
|
+
let codec;
|
|
4578
|
+
if (t["codec"] !== void 0) {
|
|
4579
|
+
if (!isString(t["codec"])) {
|
|
4580
|
+
throw new Error("Track.codec must be a string");
|
|
4581
|
+
}
|
|
4582
|
+
codec = t["codec"];
|
|
4583
|
+
}
|
|
4584
|
+
let depends;
|
|
4585
|
+
if (t["depends"] !== void 0) {
|
|
4586
|
+
if (!isStringArray(t["depends"])) {
|
|
4587
|
+
throw new Error("Track.depends must be an array of strings");
|
|
4588
|
+
}
|
|
4589
|
+
depends = t["depends"];
|
|
4590
|
+
}
|
|
4591
|
+
let mimeType;
|
|
4592
|
+
if (t["mimeType"] !== void 0) {
|
|
4593
|
+
if (!isString(t["mimeType"])) {
|
|
4594
|
+
throw new Error("Track.mimeType must be a string");
|
|
4595
|
+
}
|
|
4596
|
+
mimeType = t["mimeType"];
|
|
4597
|
+
}
|
|
4598
|
+
let width;
|
|
4599
|
+
if (t["width"] !== void 0) {
|
|
4600
|
+
if (!isNumber(t["width"])) {
|
|
4601
|
+
throw new Error("Track.width must be a number");
|
|
4602
|
+
}
|
|
4603
|
+
width = t["width"];
|
|
4604
|
+
}
|
|
4605
|
+
let height;
|
|
4606
|
+
if (t["height"] !== void 0) {
|
|
4607
|
+
if (!isNumber(t["height"])) {
|
|
4608
|
+
throw new Error("Track.height must be a number");
|
|
4609
|
+
}
|
|
4610
|
+
height = t["height"];
|
|
4611
|
+
}
|
|
4612
|
+
let timescale;
|
|
4613
|
+
if (t["timescale"] !== void 0) {
|
|
4614
|
+
if (!isNumber(t["timescale"])) {
|
|
4615
|
+
throw new Error("Track.timescale must be a number");
|
|
4616
|
+
}
|
|
4617
|
+
timescale = t["timescale"];
|
|
4618
|
+
}
|
|
4619
|
+
let bitrate;
|
|
4620
|
+
if (t["bitrate"] !== void 0) {
|
|
4621
|
+
if (!isNumber(t["bitrate"])) {
|
|
4622
|
+
throw new Error("Track.bitrate must be a number");
|
|
4623
|
+
}
|
|
4624
|
+
bitrate = t["bitrate"];
|
|
4625
|
+
}
|
|
4626
|
+
let initData;
|
|
4627
|
+
if (t["initData"] !== void 0) {
|
|
4628
|
+
if (!isString(t["initData"])) {
|
|
4629
|
+
throw new Error("Track.initData must be a string");
|
|
4630
|
+
}
|
|
4631
|
+
if (!isValidBase64(t["initData"])) {
|
|
4632
|
+
throw new Error("Track.initData must be a valid base64 string");
|
|
4633
|
+
}
|
|
4634
|
+
initData = t["initData"];
|
|
4635
|
+
}
|
|
4636
|
+
if (packaging === "cmaf" && (!codec || codec.length === 0)) {
|
|
4637
|
+
throw new Error("codec is required when packaging is 'cmaf'");
|
|
4638
|
+
}
|
|
4639
|
+
if (packaging === "timeline" && mimeType !== "text/csv") {
|
|
4640
|
+
throw new Error("mimeType must be 'text/csv' when packaging is 'timeline'");
|
|
4641
|
+
}
|
|
4642
|
+
if (packaging === "timeline" && (!depends || depends.length === 0)) {
|
|
4643
|
+
throw new Error("At least one dependee must be present when packaging is 'timeline'");
|
|
4644
|
+
}
|
|
4645
|
+
const result = {
|
|
4646
|
+
name,
|
|
4647
|
+
packaging,
|
|
4648
|
+
role
|
|
4649
|
+
};
|
|
4650
|
+
if (codec !== void 0) result.codec = codec;
|
|
4651
|
+
if (depends !== void 0) result.depends = depends;
|
|
4652
|
+
if (mimeType !== void 0) result.mimeType = mimeType;
|
|
4653
|
+
if (width !== void 0) result.width = width;
|
|
4654
|
+
if (height !== void 0) result.height = height;
|
|
4655
|
+
if (timescale !== void 0) result.timescale = timescale;
|
|
4656
|
+
if (bitrate !== void 0) result.bitrate = bitrate;
|
|
4657
|
+
if (initData !== void 0) result.initData = initData;
|
|
4658
|
+
return result;
|
|
4659
|
+
}
|
|
4660
|
+
function validateCMSF(obj) {
|
|
4661
|
+
if (typeof obj !== "object" || obj === null) {
|
|
4662
|
+
throw new Error("CMSF must be an object");
|
|
4663
|
+
}
|
|
4664
|
+
const o = obj;
|
|
4665
|
+
if (!isNumber(o["version"])) {
|
|
4666
|
+
throw new Error("CMSF.version must be a number");
|
|
4667
|
+
}
|
|
4668
|
+
if (!Array.isArray(o["tracks"])) {
|
|
4669
|
+
throw new Error("CMSF.tracks must be an array");
|
|
4670
|
+
}
|
|
4671
|
+
const version = o["version"];
|
|
4672
|
+
const tracksArray = o["tracks"];
|
|
4673
|
+
const tracks = tracksArray.map(validateTrack);
|
|
4674
|
+
return {
|
|
4675
|
+
version,
|
|
4676
|
+
tracks
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
var CMSFCatalog = class _CMSFCatalog {
|
|
4680
|
+
#catalog;
|
|
4681
|
+
constructor(catalog) {
|
|
4682
|
+
this.#catalog = catalog;
|
|
4683
|
+
}
|
|
4684
|
+
static from(payload) {
|
|
4685
|
+
const decoder = new TextDecoder();
|
|
4686
|
+
const json = decoder.decode(payload);
|
|
4687
|
+
const catalog = validateCMSF(JSON.parse(json));
|
|
4688
|
+
return new _CMSFCatalog(catalog);
|
|
4689
|
+
}
|
|
4690
|
+
getByTrackName(trackName) {
|
|
4691
|
+
return this.#catalog.tracks.find((track) => track.name === this.extractTrackName(trackName));
|
|
4692
|
+
}
|
|
4693
|
+
getTracks(role) {
|
|
4694
|
+
if (role) return this.#catalog.tracks.filter((track) => track.role === role);
|
|
4695
|
+
return this.#catalog.tracks;
|
|
4696
|
+
}
|
|
4697
|
+
getVideo(index = 0) {
|
|
4698
|
+
const videos = this.#catalog.tracks.filter((track) => track.role === "video");
|
|
4699
|
+
if (index < 0 || index >= videos.length) return void 0;
|
|
4700
|
+
return videos[index];
|
|
4701
|
+
}
|
|
4702
|
+
getAudio(index = 0) {
|
|
4703
|
+
const audios = this.#catalog.tracks.filter((track) => track.role === "audio");
|
|
4704
|
+
if (index < 0 || index >= audios.length) return void 0;
|
|
4705
|
+
return audios[index];
|
|
4706
|
+
}
|
|
4707
|
+
getTimeline(index = 0) {
|
|
4708
|
+
const timelines = this.#catalog.tracks.filter((track) => track.role === "timeline");
|
|
4709
|
+
if (index < 0 || index >= timelines.length) return void 0;
|
|
4710
|
+
return timelines[index];
|
|
4711
|
+
}
|
|
4712
|
+
getCodecString(trackName) {
|
|
4713
|
+
const track = this.#catalog.tracks.find((track2) => track2.name === this.extractTrackName(trackName));
|
|
4714
|
+
if (!track) return void 0;
|
|
4715
|
+
if (!track.codec) throw new Error(`Track ${trackName} does not have a codec`);
|
|
4716
|
+
return track.codec;
|
|
4717
|
+
}
|
|
4718
|
+
getRole(trackName) {
|
|
4719
|
+
const track = this.#catalog.tracks.find((track2) => track2.name === this.extractTrackName(trackName));
|
|
4720
|
+
if (!track) return void 0;
|
|
4721
|
+
return track.role;
|
|
4722
|
+
}
|
|
4723
|
+
getPackaging(trackName) {
|
|
4724
|
+
const track = this.#catalog.tracks.find((track2) => track2.name === this.extractTrackName(trackName));
|
|
4725
|
+
if (!track) return void 0;
|
|
4726
|
+
return track.packaging;
|
|
4727
|
+
}
|
|
4728
|
+
isCMAF(trackName) {
|
|
4729
|
+
const packaging = this.getPackaging(trackName);
|
|
4730
|
+
return packaging === "cmaf" || packaging === "chunk-per-object";
|
|
4731
|
+
}
|
|
4732
|
+
getTimescale(trackName) {
|
|
4733
|
+
const track = this.#catalog.tracks.find((track2) => track2.name === this.extractTrackName(trackName));
|
|
4734
|
+
return track?.timescale;
|
|
4735
|
+
}
|
|
4736
|
+
getInitData(trackName) {
|
|
4737
|
+
const track = this.#catalog.tracks.find((track2) => track2.name === this.extractTrackName(trackName));
|
|
4738
|
+
if (!track) return void 0;
|
|
4739
|
+
if (!track.initData) throw new Error(`Track ${trackName} does not have initData`);
|
|
4740
|
+
return new Uint8Array(
|
|
4741
|
+
atob(track.initData).split("").map((c) => c.charCodeAt(0))
|
|
4742
|
+
);
|
|
4743
|
+
}
|
|
4744
|
+
extractTrackName(input) {
|
|
4745
|
+
const parts = input.split("/");
|
|
4746
|
+
return parts[parts.length - 1];
|
|
4747
|
+
}
|
|
4748
|
+
};
|
|
4749
|
+
|
|
4543
4750
|
// src/model/control/switch.ts
|
|
4544
4751
|
init_byte_buffer();
|
|
4545
4752
|
init_pair();
|
|
@@ -6177,13 +6384,13 @@ var handlerSubscribeNamespaceOk = async (_client, _msg) => {
|
|
|
6177
6384
|
};
|
|
6178
6385
|
|
|
6179
6386
|
// src/client/handler/publish_done.ts
|
|
6180
|
-
init_error2();
|
|
6181
6387
|
var handlerPublishDone = async (client, msg) => {
|
|
6388
|
+
if (client.onPeerPublishDone) {
|
|
6389
|
+
client.onPeerPublishDone(msg);
|
|
6390
|
+
}
|
|
6182
6391
|
const request = client.requests.get(msg.requestId);
|
|
6183
6392
|
if (request instanceof SubscribeRequest) {
|
|
6184
6393
|
request.expectedStreams = msg.streamCount;
|
|
6185
|
-
} else {
|
|
6186
|
-
throw new exports.ProtocolViolationError("handlerPublishDone", "No publish request was found with the given request id");
|
|
6187
6394
|
}
|
|
6188
6395
|
};
|
|
6189
6396
|
|
|
@@ -6273,17 +6480,6 @@ var handlerPublish = async (client, msg) => {
|
|
|
6273
6480
|
if (client.onPeerPublish) {
|
|
6274
6481
|
client.onPeerPublish(msg, stream);
|
|
6275
6482
|
}
|
|
6276
|
-
const publishOk = new PublishOk(
|
|
6277
|
-
msg.requestId,
|
|
6278
|
-
1,
|
|
6279
|
-
255,
|
|
6280
|
-
1 /* Ascending */,
|
|
6281
|
-
2 /* LatestObject */,
|
|
6282
|
-
void 0,
|
|
6283
|
-
void 0,
|
|
6284
|
-
[]
|
|
6285
|
-
);
|
|
6286
|
-
await client.controlStream.send(publishOk);
|
|
6287
6483
|
};
|
|
6288
6484
|
|
|
6289
6485
|
// src/client/publication/publish.ts
|
|
@@ -6805,6 +7001,8 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6805
7001
|
onDatagramSent;
|
|
6806
7002
|
/** Fired when an inbound PUBLISH control message is received. */
|
|
6807
7003
|
onPeerPublish;
|
|
7004
|
+
/** Fired when an inbound PUBLISH_DONE control message is received. */
|
|
7005
|
+
onPeerPublishDone;
|
|
6808
7006
|
/** Fired when an inbound SUBSCRIBE_NAMESPACE control message is received. */
|
|
6809
7007
|
onPeerSubscribeNamespace;
|
|
6810
7008
|
/** Datagram writer for sending datagrams. */
|
|
@@ -7886,6 +8084,26 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7886
8084
|
throw error;
|
|
7887
8085
|
}
|
|
7888
8086
|
}
|
|
8087
|
+
/**
|
|
8088
|
+
* Signals the end of a published track to the peer/relay.
|
|
8089
|
+
* * @param publishRequestId - The original requestId used when `publish()` was called.
|
|
8090
|
+
*/
|
|
8091
|
+
async publishDone(publishRequestId, statusCode = 0, reasonPhrase = "Track Ended") {
|
|
8092
|
+
this.#ensureActive();
|
|
8093
|
+
try {
|
|
8094
|
+
if (typeof publishRequestId === "number") publishRequestId = BigInt(publishRequestId);
|
|
8095
|
+
const msg = new PublishDone(publishRequestId, statusCode, 0n, new exports.ReasonPhrase(reasonPhrase));
|
|
8096
|
+
await this.controlStream.send(msg);
|
|
8097
|
+
this.requests.delete(publishRequestId);
|
|
8098
|
+
this.requestIdMap.removeMappingByRequestId(publishRequestId);
|
|
8099
|
+
this.subscriptionAliasMap.delete(publishRequestId);
|
|
8100
|
+
} catch (error) {
|
|
8101
|
+
await this.disconnect(
|
|
8102
|
+
new exports.InternalError("MOQtailClient.publishDone", error instanceof Error ? error.message : String(error))
|
|
8103
|
+
);
|
|
8104
|
+
throw error;
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
7889
8107
|
/**
|
|
7890
8108
|
* Registers an incoming PUBLISH announcement as a valid data receiver.
|
|
7891
8109
|
* This prepares the client to ingest pushed data streams matching the published alias.
|
|
@@ -8472,300 +8690,6 @@ var NetworkTelemetry = class {
|
|
|
8472
8690
|
}
|
|
8473
8691
|
};
|
|
8474
8692
|
|
|
8475
|
-
// src/util/playout_buffer.ts
|
|
8476
|
-
init_extension_header2();
|
|
8477
|
-
var DEFAULT_TARGET_LATENCY_MS = 100;
|
|
8478
|
-
var DEFAULT_MAX_LATENCY_MS = 1e3;
|
|
8479
|
-
var PlayoutBuffer = class {
|
|
8480
|
-
constructor(objectStream, options) {
|
|
8481
|
-
this.options = options;
|
|
8482
|
-
this.#targetLatencyMs = this.options?.targetLatencyMs ?? DEFAULT_TARGET_LATENCY_MS;
|
|
8483
|
-
this.#maxLatencyMs = this.options?.maxLatencyMs ?? DEFAULT_MAX_LATENCY_MS;
|
|
8484
|
-
this.#clock = this.options?.clock;
|
|
8485
|
-
this.#reader = objectStream.getReader();
|
|
8486
|
-
this.#fillBuffer();
|
|
8487
|
-
this.#serveBuffer();
|
|
8488
|
-
}
|
|
8489
|
-
#reader;
|
|
8490
|
-
#buffer = new Heap__default.default((a, b) => {
|
|
8491
|
-
return a.object.location.compare(b.object.location);
|
|
8492
|
-
});
|
|
8493
|
-
#isRunning = true;
|
|
8494
|
-
#targetLatencyMs;
|
|
8495
|
-
#moqObjectCountInBuffer = 0;
|
|
8496
|
-
#moqObjectCountExitingBuffer = 0;
|
|
8497
|
-
#maxLatencyMs;
|
|
8498
|
-
#clock;
|
|
8499
|
-
onObject = null;
|
|
8500
|
-
async hasObjectReady() {
|
|
8501
|
-
const now = this.#getNormalizedTime();
|
|
8502
|
-
const oldest = this.#buffer.peek();
|
|
8503
|
-
return oldest ? now - oldest.createdAt >= this.#targetLatencyMs : false;
|
|
8504
|
-
}
|
|
8505
|
-
getStatus() {
|
|
8506
|
-
const oldest = this.#buffer.peek();
|
|
8507
|
-
const result = {
|
|
8508
|
-
bufferSize: this.#buffer.length,
|
|
8509
|
-
isRunning: this.#isRunning
|
|
8510
|
-
};
|
|
8511
|
-
if (oldest) {
|
|
8512
|
-
result.oldestTimestamp = oldest.createdAt;
|
|
8513
|
-
}
|
|
8514
|
-
return result;
|
|
8515
|
-
}
|
|
8516
|
-
cleanup() {
|
|
8517
|
-
this.#isRunning = false;
|
|
8518
|
-
this.onObject?.(null);
|
|
8519
|
-
}
|
|
8520
|
-
#getNormalizedTime() {
|
|
8521
|
-
if (this.#clock) {
|
|
8522
|
-
return this.#clock.now();
|
|
8523
|
-
}
|
|
8524
|
-
return Date.now();
|
|
8525
|
-
}
|
|
8526
|
-
async #serveBuffer() {
|
|
8527
|
-
while (this.#isRunning) {
|
|
8528
|
-
const now = this.#getNormalizedTime();
|
|
8529
|
-
const oldest = this.#buffer.peek();
|
|
8530
|
-
if (oldest && this.onObject) {
|
|
8531
|
-
const timeUntilReady = this.#targetLatencyMs - (now - oldest.createdAt);
|
|
8532
|
-
if (timeUntilReady <= 0) {
|
|
8533
|
-
const bufferedObj = this.#buffer.pop();
|
|
8534
|
-
this.#moqObjectCountExitingBuffer++;
|
|
8535
|
-
if (this.#moqObjectCountExitingBuffer % 50 === 0) ;
|
|
8536
|
-
this.onObject(bufferedObj.object);
|
|
8537
|
-
if (this.#moqObjectCountExitingBuffer % 50 === 0) ;
|
|
8538
|
-
} else {
|
|
8539
|
-
const sleepTime = Math.min(timeUntilReady, 50);
|
|
8540
|
-
await new Promise((resolve) => setTimeout(resolve, sleepTime));
|
|
8541
|
-
}
|
|
8542
|
-
} else {
|
|
8543
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
8544
|
-
}
|
|
8545
|
-
}
|
|
8546
|
-
}
|
|
8547
|
-
async #fillBuffer() {
|
|
8548
|
-
while (this.#isRunning) {
|
|
8549
|
-
try {
|
|
8550
|
-
const { value, done } = await this.#reader.read();
|
|
8551
|
-
if (done) {
|
|
8552
|
-
this.cleanup();
|
|
8553
|
-
return;
|
|
8554
|
-
}
|
|
8555
|
-
this.#evictOnMaxLatency();
|
|
8556
|
-
const bufferedObject = {
|
|
8557
|
-
object: value,
|
|
8558
|
-
createdAt: this.#extractCreatedAt(value)
|
|
8559
|
-
};
|
|
8560
|
-
this.#moqObjectCountInBuffer++;
|
|
8561
|
-
if (this.#moqObjectCountInBuffer % 50 === 0) {
|
|
8562
|
-
}
|
|
8563
|
-
this.#buffer.push(bufferedObject);
|
|
8564
|
-
} catch (error) {
|
|
8565
|
-
this.cleanup();
|
|
8566
|
-
}
|
|
8567
|
-
}
|
|
8568
|
-
}
|
|
8569
|
-
#extractCreatedAt(object) {
|
|
8570
|
-
if (object.extensionHeaders) {
|
|
8571
|
-
const extensionHeaders = exports.ExtensionHeaders.fromKeyValuePairs(object.extensionHeaders);
|
|
8572
|
-
for (const header of extensionHeaders) {
|
|
8573
|
-
if (exports.ExtensionHeader.isCaptureTimestamp(header)) {
|
|
8574
|
-
return Number(header.timestamp);
|
|
8575
|
-
}
|
|
8576
|
-
}
|
|
8577
|
-
}
|
|
8578
|
-
return this.#getNormalizedTime();
|
|
8579
|
-
}
|
|
8580
|
-
#evictOnMaxLatency() {
|
|
8581
|
-
const now = this.#getNormalizedTime();
|
|
8582
|
-
const oldest = this.#buffer.peek();
|
|
8583
|
-
if (oldest && now - oldest.createdAt > this.#maxLatencyMs) {
|
|
8584
|
-
if (oldest.object.location.object === 0n) {
|
|
8585
|
-
const groupToDrop = oldest.object.location.group;
|
|
8586
|
-
this.#dropGop(groupToDrop);
|
|
8587
|
-
} else {
|
|
8588
|
-
this.#buffer.pop();
|
|
8589
|
-
}
|
|
8590
|
-
this.#evictOnMaxLatency();
|
|
8591
|
-
}
|
|
8592
|
-
}
|
|
8593
|
-
#dropGop(groupId) {
|
|
8594
|
-
while (this.#buffer.length > 0) {
|
|
8595
|
-
const oldest = this.#buffer.peek();
|
|
8596
|
-
if (oldest && oldest.object.location.group === groupId) {
|
|
8597
|
-
this.#buffer.pop();
|
|
8598
|
-
} else {
|
|
8599
|
-
break;
|
|
8600
|
-
}
|
|
8601
|
-
}
|
|
8602
|
-
}
|
|
8603
|
-
};
|
|
8604
|
-
var DEFAULT_BUFFER_CAPACITY = 50;
|
|
8605
|
-
var DEFAULT_TARGET_LATENCY_MS2 = 500;
|
|
8606
|
-
var DEFAULT_MAX_LATENCY_MS2 = 2e3;
|
|
8607
|
-
var PullPlayoutBuffer = class {
|
|
8608
|
-
constructor(writeStream, options) {
|
|
8609
|
-
this.options = options;
|
|
8610
|
-
this.#bucketCapacity = this.options.bucketCapacity ?? DEFAULT_BUFFER_CAPACITY;
|
|
8611
|
-
this.#targetLatencyMs = this.options.targetLatencyMs ?? DEFAULT_TARGET_LATENCY_MS2;
|
|
8612
|
-
this.#maxLatencyMs = this.options.maxLatencyMs ?? DEFAULT_MAX_LATENCY_MS2;
|
|
8613
|
-
this.#reader = writeStream.getReader();
|
|
8614
|
-
this.#fillBuffer();
|
|
8615
|
-
}
|
|
8616
|
-
#reader;
|
|
8617
|
-
#buffer = new Heap__default.default((a, b) => {
|
|
8618
|
-
if (a.location.compare(b.location) <= 0) {
|
|
8619
|
-
if (b.location.compare(a.location) <= 0) {
|
|
8620
|
-
return 0;
|
|
8621
|
-
}
|
|
8622
|
-
return -1;
|
|
8623
|
-
}
|
|
8624
|
-
return 1;
|
|
8625
|
-
});
|
|
8626
|
-
#isRunning = true;
|
|
8627
|
-
#bucketCapacity;
|
|
8628
|
-
#targetLatencyMs;
|
|
8629
|
-
#maxLatencyMs;
|
|
8630
|
-
#lastIncomingTimestamp = 0;
|
|
8631
|
-
#pendingCallback = null;
|
|
8632
|
-
// Pull-based API: Consumer calls this when ready for next object
|
|
8633
|
-
nextObject(callback) {
|
|
8634
|
-
if (this.#buffer.length > 0) {
|
|
8635
|
-
const obj = this.#buffer.pop();
|
|
8636
|
-
callback(obj || null);
|
|
8637
|
-
return;
|
|
8638
|
-
}
|
|
8639
|
-
this.#pendingCallback = callback;
|
|
8640
|
-
}
|
|
8641
|
-
// Check if there's an object ready immediately (non-blocking)
|
|
8642
|
-
hasObjectReady() {
|
|
8643
|
-
return this.#buffer.length > 0;
|
|
8644
|
-
}
|
|
8645
|
-
// Get current buffer status for debugging
|
|
8646
|
-
getStatus() {
|
|
8647
|
-
return {
|
|
8648
|
-
bufferSize: this.#buffer.length,
|
|
8649
|
-
isRunning: this.#isRunning
|
|
8650
|
-
};
|
|
8651
|
-
}
|
|
8652
|
-
// Cleanup method - called when buffer is destroyed
|
|
8653
|
-
cleanup() {
|
|
8654
|
-
this.#isRunning = false;
|
|
8655
|
-
}
|
|
8656
|
-
// Simple background filling - just fills the buffer as objects arrive
|
|
8657
|
-
async #fillBuffer() {
|
|
8658
|
-
while (this.#isRunning) {
|
|
8659
|
-
try {
|
|
8660
|
-
const { value, done } = await this.#reader.read();
|
|
8661
|
-
if (done) {
|
|
8662
|
-
this.#isRunning = false;
|
|
8663
|
-
if (this.#pendingCallback) {
|
|
8664
|
-
const callback = this.#pendingCallback;
|
|
8665
|
-
this.#pendingCallback = null;
|
|
8666
|
-
callback(null);
|
|
8667
|
-
}
|
|
8668
|
-
return;
|
|
8669
|
-
}
|
|
8670
|
-
this.#lastIncomingTimestamp = performance.now();
|
|
8671
|
-
if (this.#buffer.length >= this.#bucketCapacity) {
|
|
8672
|
-
this.#manageBufferOverflow();
|
|
8673
|
-
}
|
|
8674
|
-
this.#buffer.push(value);
|
|
8675
|
-
if (this.#pendingCallback) {
|
|
8676
|
-
const callback = this.#pendingCallback;
|
|
8677
|
-
this.#pendingCallback = null;
|
|
8678
|
-
const obj = this.#buffer.pop();
|
|
8679
|
-
callback(obj || null);
|
|
8680
|
-
}
|
|
8681
|
-
} catch (error) {
|
|
8682
|
-
console.error("Error in fillBuffer:", error);
|
|
8683
|
-
this.#isRunning = false;
|
|
8684
|
-
if (this.#pendingCallback) {
|
|
8685
|
-
const callback = this.#pendingCallback;
|
|
8686
|
-
this.#pendingCallback = null;
|
|
8687
|
-
callback(null);
|
|
8688
|
-
}
|
|
8689
|
-
}
|
|
8690
|
-
}
|
|
8691
|
-
}
|
|
8692
|
-
// Manage buffer overflow by dropping oldest GOPs
|
|
8693
|
-
#manageBufferOverflow() {
|
|
8694
|
-
const droppedGops = this.#dropMultipleGopsToTarget();
|
|
8695
|
-
if (droppedGops === 0) {
|
|
8696
|
-
const dropCount = Math.floor(this.#bucketCapacity * 0.2);
|
|
8697
|
-
for (let i = 0; i < dropCount; i++) {
|
|
8698
|
-
this.#buffer.pop();
|
|
8699
|
-
}
|
|
8700
|
-
console.log(`\u{1F5D1}\uFE0F [PULL BUFFER] Buffer overflow: dropped ${dropCount} oldest objects (fallback)`);
|
|
8701
|
-
}
|
|
8702
|
-
}
|
|
8703
|
-
// Find GOP boundaries based on group IDs
|
|
8704
|
-
#findGopBoundaries(objects) {
|
|
8705
|
-
const gops = [];
|
|
8706
|
-
if (objects.length === 0) return gops;
|
|
8707
|
-
const groupMap = /* @__PURE__ */ new Map();
|
|
8708
|
-
objects.forEach((obj, index) => {
|
|
8709
|
-
const groupId = obj.location.group;
|
|
8710
|
-
if (!groupMap.has(groupId)) {
|
|
8711
|
-
groupMap.set(groupId, []);
|
|
8712
|
-
}
|
|
8713
|
-
groupMap.get(groupId).push(index);
|
|
8714
|
-
});
|
|
8715
|
-
const sortedGroups = Array.from(groupMap.entries()).sort(([a], [b]) => {
|
|
8716
|
-
if (a < b) return -1;
|
|
8717
|
-
if (a > b) return 1;
|
|
8718
|
-
return 0;
|
|
8719
|
-
});
|
|
8720
|
-
sortedGroups.forEach(([, indices]) => {
|
|
8721
|
-
if (indices.length > 0) {
|
|
8722
|
-
gops.push({
|
|
8723
|
-
start: Math.min(...indices),
|
|
8724
|
-
end: Math.max(...indices)
|
|
8725
|
-
});
|
|
8726
|
-
}
|
|
8727
|
-
});
|
|
8728
|
-
console.log(`\u{1F3AC} [PULL BUFFER] Found ${gops.length} GOPs (groups) in buffer`);
|
|
8729
|
-
return gops;
|
|
8730
|
-
}
|
|
8731
|
-
// Drop oldest GOP (complete group)
|
|
8732
|
-
#dropOldestGop() {
|
|
8733
|
-
const allObjects = this.#buffer.toArray();
|
|
8734
|
-
const gops = this.#findGopBoundaries(allObjects);
|
|
8735
|
-
if (gops.length > 0) {
|
|
8736
|
-
const oldestGop = gops[0];
|
|
8737
|
-
if (oldestGop) {
|
|
8738
|
-
const firstObj = allObjects[oldestGop.start];
|
|
8739
|
-
const groupId = firstObj?.location.group;
|
|
8740
|
-
for (let i = oldestGop.start; i <= oldestGop.end; i++) {
|
|
8741
|
-
this.#buffer.pop();
|
|
8742
|
-
}
|
|
8743
|
-
console.log(`\u{1F5D1}\uFE0F [PULL BUFFER] Dropped GOP (Group ${groupId}): ${oldestGop.end - oldestGop.start + 1} objects`);
|
|
8744
|
-
return true;
|
|
8745
|
-
}
|
|
8746
|
-
}
|
|
8747
|
-
return false;
|
|
8748
|
-
}
|
|
8749
|
-
// Drop multiple GOPs to reach target buffer size
|
|
8750
|
-
#dropMultipleGopsToTarget() {
|
|
8751
|
-
const targetBufferSize = Math.floor(this.#bucketCapacity * 0.7);
|
|
8752
|
-
let droppedGops = 0;
|
|
8753
|
-
while (this.#buffer.length > targetBufferSize && droppedGops < 3) {
|
|
8754
|
-
if (this.#dropOldestGop()) {
|
|
8755
|
-
droppedGops++;
|
|
8756
|
-
} else {
|
|
8757
|
-
break;
|
|
8758
|
-
}
|
|
8759
|
-
}
|
|
8760
|
-
if (droppedGops > 0) {
|
|
8761
|
-
console.log(
|
|
8762
|
-
`\u{1F5D1}\uFE0F [PULL BUFFER] Dropped ${droppedGops} GOPs to reduce buffer size. New buffer size: ${this.#buffer.length}`
|
|
8763
|
-
);
|
|
8764
|
-
}
|
|
8765
|
-
return droppedGops;
|
|
8766
|
-
}
|
|
8767
|
-
};
|
|
8768
|
-
|
|
8769
8693
|
// src/util/clock_normalizer.ts
|
|
8770
8694
|
var DEFAULT_SAMPLE_SIZE = 5;
|
|
8771
8695
|
var DEFAULT_TIME_SERVER = "https://time.akamai.com/?ms";
|
|
@@ -8836,6 +8760,7 @@ var ClockNormalizer = class _ClockNormalizer {
|
|
|
8836
8760
|
};
|
|
8837
8761
|
|
|
8838
8762
|
exports.AuthorizationToken = AuthorizationToken;
|
|
8763
|
+
exports.CMSFCatalog = CMSFCatalog;
|
|
8839
8764
|
exports.ClientSetup = ClientSetup;
|
|
8840
8765
|
exports.ClockNormalizer = ClockNormalizer;
|
|
8841
8766
|
exports.CommonType = CommonType;
|
|
@@ -8864,7 +8789,6 @@ exports.MaxRequestIdParameter = MaxRequestId2;
|
|
|
8864
8789
|
exports.MemoryObjectCache = MemoryObjectCache;
|
|
8865
8790
|
exports.NetworkTelemetry = NetworkTelemetry;
|
|
8866
8791
|
exports.Path = Path;
|
|
8867
|
-
exports.PlayoutBuffer = PlayoutBuffer;
|
|
8868
8792
|
exports.Publish = Publish;
|
|
8869
8793
|
exports.PublishDone = PublishDone;
|
|
8870
8794
|
exports.PublishDoneStatusCode = PublishDoneStatusCode;
|
|
@@ -8880,7 +8804,6 @@ exports.PublishNamespaceRequest = PublishNamespaceRequest;
|
|
|
8880
8804
|
exports.PublishOk = PublishOk;
|
|
8881
8805
|
exports.PublishPublication = PublishPublication;
|
|
8882
8806
|
exports.PublishRequest = PublishRequest;
|
|
8883
|
-
exports.PullPlayoutBuffer = PullPlayoutBuffer;
|
|
8884
8807
|
exports.RecvDatagramStream = RecvDatagramStream;
|
|
8885
8808
|
exports.RecvStream = RecvStream;
|
|
8886
8809
|
exports.RequestsBlocked = RequestsBlocked;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { ControlStream, FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, LiveObjectSource, LiveTrackSource, MOQtailClient, MOQtailClientOptions, MOQtailRequest, MemoryObjectCache, ObjectCache, PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, SubscribeOptions, SubscribePublication, SubscribeRequest, SubscribeUpdateOptions, SwitchOptions, Track, TrackSource } from './client
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export { BufferedObject, Clock, ClockNormalizer, NetworkTelemetry, PlayoutBuffer, PullPlayoutBuffer } from './util/index.cjs';
|
|
1
|
+
export { ControlStream, FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, LiveObjectSource, LiveTrackSource, MOQtailClient, MOQtailClientOptions, MOQtailRequest, MemoryObjectCache, ObjectCache, PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, SubscribeOptions, SubscribePublication, SubscribeRequest, SubscribeUpdateOptions, SwitchOptions, Track, TrackSource } from './client.cjs';
|
|
2
|
+
export { AudioLevel, CMSF, CMSFCatalog, CMSFTrack, CaptureTimestamp, CastingError, ExtensionHeader, ExtensionHeaders, InternalError, InvalidTypeError, InvalidUTF8Error, KeyValueFormattingError, LOCHeaderExtensionId, LengthExceedsMaxError, MOQtailError, NotEnoughBytesError, ProtocolViolationError, RequestIdError, Switch, TerminationCode, TerminationError, TimeoutError, TrackNameError, VarIntOverflowError, VideoConfig, VideoFrameMarking, locHeaderExtensionIdFromNumber } from './model.cjs';
|
|
3
|
+
export { A as AuthTokenVariant, a as AuthorizationToken, B as BaseByteBuffer, b as ByteBuffer, C as ClientSetup, c as CommonType, d as ControlMessage, e as ControlMessageType, D as DRAFT_14, f as DatagramObject, g as DatagramStatus, h as DeliveryTimeout, F as Fetch, i as FetchCancel, j as FetchError, k as FetchErrorCode, l as FetchHeader, m as FetchHeaderType, n as FetchObject, o as FetchOk, p as FetchType, q as FilterType, r as FrozenByteBuffer, s as FullTrackName, G as GoAway, t as GroupOrder, H as Header, K as KeyValuePair, L as Location, M as MAX_FULL_TRACK_NAME_LENGTH, u as MAX_NAMESPACE_TUPLE_COUNT, v as MAX_REASON_PHRASE_LEN, w as MaxAuthTokenCacheSize, x as MaxCacheDuration, y as MaxRequestId, z as MaxRequestIdParameter, E as MoqtObject, O as ObjectDatagramStatusType, I as ObjectDatagramType, J as ObjectForwardingPreference, N as ObjectStatus, P as Parameter, Q as Path, R as Publish, S as PublishDone, T as PublishDoneStatusCode, U as PublishError, V as PublishErrorCode, W as PublishNamespace, X as PublishNamespaceCancel, Y as PublishNamespaceDone, Z as PublishNamespaceError, _ as PublishNamespaceErrorCode, $ as PublishNamespaceOk, a0 as PublishOk, a1 as ReasonPhrase, a2 as RequestIdMap, a3 as RequestsBlocked, a4 as ServerSetup, a5 as SetupParameter, a6 as SetupParameterType, a7 as SetupParameters, a8 as SubgroupHeader, a9 as SubgroupHeaderType, aa as SubgroupObject, ab as Subscribe, ac as SubscribeError, ad as SubscribeErrorCode, ae as SubscribeNamespace, af as SubscribeNamespaceError, ag as SubscribeNamespaceErrorCode, ah as SubscribeNamespaceOk, ai as SubscribeOk, aj as SubscribeUpdate, ak as TokenAliasType, al as TrackStatus, am as TrackStatusCode, an as TrackStatusError, ao as TrackStatusOk, ap as Tuple, aq as TupleField, ar as Unsubscribe, as as UnsubscribeNamespace, at as VersionSpecificParameter, au as VersionSpecificParameterType, av as VersionSpecificParameters, aw as commonTypeFromNumber, ax as controlMessageTypeFromBigInt, ay as fetchErrorCodeFromBigInt, az as fetchTypeFromBigInt, aA as filterTypeFromBigInt, aB as groupOrderFromNumber, aC as isBytes, aD as isVarInt, aE as publishDoneStatusCodeFromBigInt, aF as publishErrorCodeFromBigInt, aG as publishNamespaceErrorCodeFromBigInt, aH as setupParameterTypeFromNumber, aI as subscribeErrorCodeFromBigInt, aJ as subscribeNamespaceErrorCodeFromBigInt, aK as tokenAliasTypeFromNumber, aL as trackStatusCodeFromBigInt, aM as versionSpecificParameterTypeFromNumber } from './version_parameter-DXBOBueW.cjs';
|
|
4
|
+
export { ClockNormalizer, NetworkTelemetry } from './util.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { ControlStream, FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, LiveObjectSource, LiveTrackSource, MOQtailClient, MOQtailClientOptions, MOQtailRequest, MemoryObjectCache, ObjectCache, PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, SubscribeOptions, SubscribePublication, SubscribeRequest, SubscribeUpdateOptions, SwitchOptions, Track, TrackSource } from './client
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export { BufferedObject, Clock, ClockNormalizer, NetworkTelemetry, PlayoutBuffer, PullPlayoutBuffer } from './util/index.js';
|
|
1
|
+
export { ControlStream, FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, LiveObjectSource, LiveTrackSource, MOQtailClient, MOQtailClientOptions, MOQtailRequest, MemoryObjectCache, ObjectCache, PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, SubscribeOptions, SubscribePublication, SubscribeRequest, SubscribeUpdateOptions, SwitchOptions, Track, TrackSource } from './client.js';
|
|
2
|
+
export { AudioLevel, CMSF, CMSFCatalog, CMSFTrack, CaptureTimestamp, CastingError, ExtensionHeader, ExtensionHeaders, InternalError, InvalidTypeError, InvalidUTF8Error, KeyValueFormattingError, LOCHeaderExtensionId, LengthExceedsMaxError, MOQtailError, NotEnoughBytesError, ProtocolViolationError, RequestIdError, Switch, TerminationCode, TerminationError, TimeoutError, TrackNameError, VarIntOverflowError, VideoConfig, VideoFrameMarking, locHeaderExtensionIdFromNumber } from './model.js';
|
|
3
|
+
export { A as AuthTokenVariant, a as AuthorizationToken, B as BaseByteBuffer, b as ByteBuffer, C as ClientSetup, c as CommonType, d as ControlMessage, e as ControlMessageType, D as DRAFT_14, f as DatagramObject, g as DatagramStatus, h as DeliveryTimeout, F as Fetch, i as FetchCancel, j as FetchError, k as FetchErrorCode, l as FetchHeader, m as FetchHeaderType, n as FetchObject, o as FetchOk, p as FetchType, q as FilterType, r as FrozenByteBuffer, s as FullTrackName, G as GoAway, t as GroupOrder, H as Header, K as KeyValuePair, L as Location, M as MAX_FULL_TRACK_NAME_LENGTH, u as MAX_NAMESPACE_TUPLE_COUNT, v as MAX_REASON_PHRASE_LEN, w as MaxAuthTokenCacheSize, x as MaxCacheDuration, y as MaxRequestId, z as MaxRequestIdParameter, E as MoqtObject, O as ObjectDatagramStatusType, I as ObjectDatagramType, J as ObjectForwardingPreference, N as ObjectStatus, P as Parameter, Q as Path, R as Publish, S as PublishDone, T as PublishDoneStatusCode, U as PublishError, V as PublishErrorCode, W as PublishNamespace, X as PublishNamespaceCancel, Y as PublishNamespaceDone, Z as PublishNamespaceError, _ as PublishNamespaceErrorCode, $ as PublishNamespaceOk, a0 as PublishOk, a1 as ReasonPhrase, a2 as RequestIdMap, a3 as RequestsBlocked, a4 as ServerSetup, a5 as SetupParameter, a6 as SetupParameterType, a7 as SetupParameters, a8 as SubgroupHeader, a9 as SubgroupHeaderType, aa as SubgroupObject, ab as Subscribe, ac as SubscribeError, ad as SubscribeErrorCode, ae as SubscribeNamespace, af as SubscribeNamespaceError, ag as SubscribeNamespaceErrorCode, ah as SubscribeNamespaceOk, ai as SubscribeOk, aj as SubscribeUpdate, ak as TokenAliasType, al as TrackStatus, am as TrackStatusCode, an as TrackStatusError, ao as TrackStatusOk, ap as Tuple, aq as TupleField, ar as Unsubscribe, as as UnsubscribeNamespace, at as VersionSpecificParameter, au as VersionSpecificParameterType, av as VersionSpecificParameters, aw as commonTypeFromNumber, ax as controlMessageTypeFromBigInt, ay as fetchErrorCodeFromBigInt, az as fetchTypeFromBigInt, aA as filterTypeFromBigInt, aB as groupOrderFromNumber, aC as isBytes, aD as isVarInt, aE as publishDoneStatusCodeFromBigInt, aF as publishErrorCodeFromBigInt, aG as publishNamespaceErrorCodeFromBigInt, aH as setupParameterTypeFromNumber, aI as subscribeErrorCodeFromBigInt, aJ as subscribeNamespaceErrorCodeFromBigInt, aK as tokenAliasTypeFromNumber, aL as trackStatusCodeFromBigInt, aM as versionSpecificParameterTypeFromNumber } from './version_parameter-DXBOBueW.js';
|
|
4
|
+
export { ClockNormalizer, NetworkTelemetry } from './util.js';
|