moqtail 0.10.0 → 0.11.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.cjs +81 -38
- package/dist/client.d.cts +30 -1
- package/dist/client.d.ts +30 -1
- package/dist/client.js +81 -38
- package/dist/index.cjs +81 -38
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +81 -38
- package/package.json +1 -1
package/dist/client.cjs
CHANGED
|
@@ -5123,6 +5123,7 @@ var SubscribeRequest = class {
|
|
|
5123
5123
|
priority;
|
|
5124
5124
|
forward;
|
|
5125
5125
|
subscribeParameters;
|
|
5126
|
+
earlyDiscardPolicy;
|
|
5126
5127
|
largestLocation;
|
|
5127
5128
|
// Updated on each received object
|
|
5128
5129
|
streamsAccepted = 0n;
|
|
@@ -5958,6 +5959,11 @@ var handlerRequestsBlocked = async (_client, msg) => {
|
|
|
5958
5959
|
logger.debug("handler/requests_blocked", "not implemented", msg);
|
|
5959
5960
|
};
|
|
5960
5961
|
|
|
5962
|
+
// src/client/util/validators.ts
|
|
5963
|
+
function isValidTrackAlias(trackAlias) {
|
|
5964
|
+
return trackAlias !== void 0 && trackAlias >= 0n;
|
|
5965
|
+
}
|
|
5966
|
+
|
|
5961
5967
|
// src/client/handler/subscribe.ts
|
|
5962
5968
|
var handlerSubscribe = async (client, msg) => {
|
|
5963
5969
|
logger.debug("handler/subscribe", `received requestId=${msg.requestId} ftn="${msg.fullTrackName}"`);
|
|
@@ -5990,7 +5996,7 @@ var handlerSubscribe = async (client, msg) => {
|
|
|
5990
5996
|
await client.controlStream.send(response);
|
|
5991
5997
|
return;
|
|
5992
5998
|
}
|
|
5993
|
-
if (!track.trackAlias) throw new Error("Expected track alias to be set");
|
|
5999
|
+
if (!isValidTrackAlias(track.trackAlias)) throw new Error("Expected track alias to be set");
|
|
5994
6000
|
const largestLocation = track.trackSource.live.largestLocation;
|
|
5995
6001
|
const parameters = [...msg.parameters];
|
|
5996
6002
|
if (largestLocation) {
|
|
@@ -6456,6 +6462,8 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6456
6462
|
#isDestroyed = false;
|
|
6457
6463
|
/** Internal monotonically increasing client-assigned request id counter (even/odd parity scheme advances by 2). */
|
|
6458
6464
|
#dontUseRequestId = 0n;
|
|
6465
|
+
/** Active early discard policy; undefined means no per-stream deadline is applied. */
|
|
6466
|
+
#earlyDiscardPolicy;
|
|
6459
6467
|
/**
|
|
6460
6468
|
* TODO: onNamespaceAnnounced may be a better name
|
|
6461
6469
|
* Fired when an PUBLISH_NAMESPACE control message is processed for a track namespace.
|
|
@@ -6907,6 +6915,28 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6907
6915
|
return FullTrackName.tryNew("unknown", `track-${trackAlias}`);
|
|
6908
6916
|
}
|
|
6909
6917
|
}
|
|
6918
|
+
/**
|
|
6919
|
+
* Sets (or replaces) the client-level default early discard policy for incoming subgroup streams.
|
|
6920
|
+
*
|
|
6921
|
+
* When set, each incoming subgroup QUIC stream is given a deadline of `subgroupReceiveTimeout` ms to
|
|
6922
|
+
* complete. If the stream has not finished within that window it is cancelled — objects already
|
|
6923
|
+
* delivered to the subscription are kept, but no further objects arrive from that stream.
|
|
6924
|
+
*
|
|
6925
|
+
* This is a client-wide default. Individual subscriptions can override it via the `earlyDiscardPolicy`
|
|
6926
|
+
* field in {@link SubscribeOptions}, which takes precedence over this setting.
|
|
6927
|
+
*
|
|
6928
|
+
* The policy takes effect on the next stream accepted after this call. Passing a new config
|
|
6929
|
+
* replaces the previous one. Pass `undefined` to remove the default.
|
|
6930
|
+
*
|
|
6931
|
+
* @example
|
|
6932
|
+
* ```ts
|
|
6933
|
+
* client.setEarlyDiscardPolicy({ subgroupReceiveTimeout: 2000 })
|
|
6934
|
+
* ```
|
|
6935
|
+
*/
|
|
6936
|
+
setEarlyDiscardPolicy(config) {
|
|
6937
|
+
this.#ensureActive();
|
|
6938
|
+
this.#earlyDiscardPolicy = config;
|
|
6939
|
+
}
|
|
6910
6940
|
/**
|
|
6911
6941
|
* Gracefully terminates this {@link MOQtailClient} session and releases underlying {@link https://developer.mozilla.org/docs/Web/API/WebTransport | WebTransport} resources.
|
|
6912
6942
|
*
|
|
@@ -6991,7 +7021,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6991
7021
|
*/
|
|
6992
7022
|
addOrUpdateTrack(track) {
|
|
6993
7023
|
this.#ensureActive();
|
|
6994
|
-
if (!track.trackAlias) {
|
|
7024
|
+
if (!isValidTrackAlias(track.trackAlias)) {
|
|
6995
7025
|
track.trackAlias = random60bitId();
|
|
6996
7026
|
}
|
|
6997
7027
|
this.trackSources.set(track.fullTrackName.toString(), track);
|
|
@@ -7123,6 +7153,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7123
7153
|
break;
|
|
7124
7154
|
}
|
|
7125
7155
|
const request = new SubscribeRequest(msg);
|
|
7156
|
+
request.earlyDiscardPolicy = args.earlyDiscardPolicy;
|
|
7126
7157
|
this.requests.set(request.requestId, request);
|
|
7127
7158
|
this.requestIdMap.addMapping(request.requestId, request.fullTrackName);
|
|
7128
7159
|
logger.debug("MOQtailClient", `subscribe: sending SUBSCRIBE requestId=${msg.requestId} ftn="${fullTrackName}"`);
|
|
@@ -7271,7 +7302,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7271
7302
|
const request = this.requests.get(subscriptionRequestId);
|
|
7272
7303
|
if (request instanceof SubscribeRequest) {
|
|
7273
7304
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7274
|
-
if (!trackAlias)
|
|
7305
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7275
7306
|
throw new InternalError("MOQtailClient.subscribeUpdate", "Request exists but track alias mapping does not");
|
|
7276
7307
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7277
7308
|
if (!subscription)
|
|
@@ -7324,7 +7355,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7324
7355
|
if (!(request instanceof SubscribeRequest))
|
|
7325
7356
|
throw new ProtocolViolationError("MOQtailClient.switch", "Request id is not a subscription");
|
|
7326
7357
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7327
|
-
if (!trackAlias)
|
|
7358
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7328
7359
|
throw new InternalError("MOQtailClient.switch", "Request exists but track alias mapping does not");
|
|
7329
7360
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7330
7361
|
if (!subscription) throw new InternalError("MOQtailClient.switch", "Request exists but subscription does not");
|
|
@@ -7988,43 +8019,55 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7988
8019
|
if (subscription) {
|
|
7989
8020
|
subscription.streamsAccepted++;
|
|
7990
8021
|
let firstObjectId = null;
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8022
|
+
let subgroupTimeoutId;
|
|
8023
|
+
const effectiveDiscardPolicy = subscription.earlyDiscardPolicy ?? this.#earlyDiscardPolicy;
|
|
8024
|
+
if (effectiveDiscardPolicy?.subgroupReceiveTimeout !== void 0) {
|
|
8025
|
+
subgroupTimeoutId = setTimeout(() => {
|
|
8026
|
+
reader.cancel("early discard: subgroupReceiveTimeout exceeded").catch(() => {
|
|
8027
|
+
});
|
|
8028
|
+
}, effectiveDiscardPolicy.subgroupReceiveTimeout);
|
|
8029
|
+
}
|
|
8030
|
+
try {
|
|
8031
|
+
while (true) {
|
|
8032
|
+
const { done, value: nextObject } = await reader.read();
|
|
8033
|
+
if (done) {
|
|
8034
|
+
break;
|
|
8035
|
+
}
|
|
8036
|
+
if (nextObject) {
|
|
8037
|
+
if (nextObject instanceof SubgroupObject) {
|
|
8038
|
+
if (!firstObjectId) {
|
|
8039
|
+
firstObjectId = nextObject.objectId;
|
|
8040
|
+
}
|
|
8041
|
+
let subgroupId = null;
|
|
8042
|
+
if (SubgroupHeaderType.isSubgroupIdZero(header.type)) {
|
|
8043
|
+
subgroupId = 0n;
|
|
8044
|
+
} else if (SubgroupHeaderType.isSubgroupIdFirstObjectId(header.type)) {
|
|
8045
|
+
subgroupId = firstObjectId ?? null;
|
|
8046
|
+
} else if (SubgroupHeaderType.hasExplicitSubgroupId(header.type)) {
|
|
8047
|
+
subgroupId = header.subgroupId ?? null;
|
|
8048
|
+
}
|
|
8049
|
+
const fullTrackName = this.aliasFullTrackNameMap.get(header.trackAlias);
|
|
8050
|
+
if (!fullTrackName) {
|
|
8051
|
+
throw new ProtocolViolationError("MOQtailClient", "No full track name for received track alias");
|
|
8052
|
+
}
|
|
8053
|
+
const moqtObject = MoqtObject.fromSubgroupObject(
|
|
8054
|
+
nextObject,
|
|
8055
|
+
header.groupId,
|
|
8056
|
+
header.publisherPriority,
|
|
8057
|
+
subgroupId,
|
|
8058
|
+
fullTrackName
|
|
8059
|
+
);
|
|
8060
|
+
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8061
|
+
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8062
|
+
subscription.largestLocation = moqtObject.location;
|
|
8063
|
+
subscription.controller?.enqueue(moqtObject);
|
|
8064
|
+
continue;
|
|
8012
8065
|
}
|
|
8013
|
-
|
|
8014
|
-
nextObject,
|
|
8015
|
-
header.groupId,
|
|
8016
|
-
header.publisherPriority,
|
|
8017
|
-
subgroupId,
|
|
8018
|
-
fullTrackName
|
|
8019
|
-
);
|
|
8020
|
-
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8021
|
-
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8022
|
-
subscription.largestLocation = moqtObject.location;
|
|
8023
|
-
subscription.controller?.enqueue(moqtObject);
|
|
8024
|
-
continue;
|
|
8066
|
+
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8025
8067
|
}
|
|
8026
|
-
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8027
8068
|
}
|
|
8069
|
+
} finally {
|
|
8070
|
+
if (subgroupTimeoutId !== void 0) clearTimeout(subgroupTimeoutId);
|
|
8028
8071
|
}
|
|
8029
8072
|
if (subscription.expectedStreams && subscription.expectedStreams === subscription.streamsAccepted) {
|
|
8030
8073
|
subscription.controller?.close();
|
package/dist/client.d.cts
CHANGED
|
@@ -494,6 +494,7 @@ declare class SubscribeRequest implements PromiseLike<SubscribeOk | RequestError
|
|
|
494
494
|
priority: number;
|
|
495
495
|
forward: boolean;
|
|
496
496
|
subscribeParameters: MessageParameter[];
|
|
497
|
+
earlyDiscardPolicy: EarlyDiscardPolicyConfig | undefined;
|
|
497
498
|
largestLocation: Location | undefined;
|
|
498
499
|
streamsAccepted: bigint;
|
|
499
500
|
expectedStreams: bigint | undefined;
|
|
@@ -716,6 +717,8 @@ type SubscribeOptions = {
|
|
|
716
717
|
startLocation?: Location;
|
|
717
718
|
/** Required for {@link FilterType.AbsoluteRange}; exclusive upper group boundary (coerced to bigint if number provided). */
|
|
718
719
|
endGroup?: bigint | number;
|
|
720
|
+
/** Per-subscription early discard policy. Overrides the client-level default set via {@link MOQtailClient.setEarlyDiscardPolicy}. */
|
|
721
|
+
earlyDiscardPolicy?: EarlyDiscardPolicyConfig;
|
|
719
722
|
};
|
|
720
723
|
/**
|
|
721
724
|
* Narrowing update constraints applied to an existing SUBSCRIBE via {@link MOQtailClient.subscribeUpdate}.
|
|
@@ -799,6 +802,13 @@ type SwitchOptions = {
|
|
|
799
802
|
* })
|
|
800
803
|
* ```
|
|
801
804
|
*/
|
|
805
|
+
/**
|
|
806
|
+
* Configuration for the early discard policy applied to incoming subgroup streams.
|
|
807
|
+
*/
|
|
808
|
+
type EarlyDiscardPolicyConfig = {
|
|
809
|
+
/** Cancel a subgroup QUIC stream if it has not fully completed within this many milliseconds. */
|
|
810
|
+
subgroupReceiveTimeout: number;
|
|
811
|
+
};
|
|
802
812
|
type FetchOptions = {
|
|
803
813
|
/** Request priority (0 = highest, 255 = lowest). Rounded & clamped. */
|
|
804
814
|
priority: number;
|
|
@@ -1277,6 +1287,25 @@ declare class MOQtailClient {
|
|
|
1277
1287
|
* ```
|
|
1278
1288
|
*/
|
|
1279
1289
|
sendDatagram(trackAlias: bigint, object: MoqtObject): Promise<void>;
|
|
1290
|
+
/**
|
|
1291
|
+
* Sets (or replaces) the client-level default early discard policy for incoming subgroup streams.
|
|
1292
|
+
*
|
|
1293
|
+
* When set, each incoming subgroup QUIC stream is given a deadline of `subgroupReceiveTimeout` ms to
|
|
1294
|
+
* complete. If the stream has not finished within that window it is cancelled — objects already
|
|
1295
|
+
* delivered to the subscription are kept, but no further objects arrive from that stream.
|
|
1296
|
+
*
|
|
1297
|
+
* This is a client-wide default. Individual subscriptions can override it via the `earlyDiscardPolicy`
|
|
1298
|
+
* field in {@link SubscribeOptions}, which takes precedence over this setting.
|
|
1299
|
+
*
|
|
1300
|
+
* The policy takes effect on the next stream accepted after this call. Passing a new config
|
|
1301
|
+
* replaces the previous one. Pass `undefined` to remove the default.
|
|
1302
|
+
*
|
|
1303
|
+
* @example
|
|
1304
|
+
* ```ts
|
|
1305
|
+
* client.setEarlyDiscardPolicy({ subgroupReceiveTimeout: 2000 })
|
|
1306
|
+
* ```
|
|
1307
|
+
*/
|
|
1308
|
+
setEarlyDiscardPolicy(config: EarlyDiscardPolicyConfig | undefined): void;
|
|
1280
1309
|
/**
|
|
1281
1310
|
* Gracefully terminates this {@link MOQtailClient} session and releases underlying {@link https://developer.mozilla.org/docs/Web/API/WebTransport | WebTransport} resources.
|
|
1282
1311
|
*
|
|
@@ -1820,4 +1849,4 @@ declare class RecvStream {
|
|
|
1820
1849
|
static new(readStream: ReadableStream<Uint8Array>, partialDataTimeout?: number, onDataReceived?: (data: SubgroupObject | SubgroupHeader | FetchObject | FetchHeader) => void): Promise<RecvStream>;
|
|
1821
1850
|
}
|
|
1822
1851
|
|
|
1823
|
-
export { ControlStream, type FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, type LiveObjectSource, LiveTrackSource, MOQtailClient, type MOQtailClientOptions, type MOQtailRequest, MemoryObjectCache, type ObjectCache, type PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, type SubscribeOptions, SubscribePublication, SubscribeRequest, type SubscribeUpdateOptions, type SwitchOptions, type Track, type TrackSource, TrackStatusRequest };
|
|
1852
|
+
export { ControlStream, type EarlyDiscardPolicyConfig, type FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, type LiveObjectSource, LiveTrackSource, MOQtailClient, type MOQtailClientOptions, type MOQtailRequest, MemoryObjectCache, type ObjectCache, type PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, type SubscribeOptions, SubscribePublication, SubscribeRequest, type SubscribeUpdateOptions, type SwitchOptions, type Track, type TrackSource, TrackStatusRequest };
|
package/dist/client.d.ts
CHANGED
|
@@ -494,6 +494,7 @@ declare class SubscribeRequest implements PromiseLike<SubscribeOk | RequestError
|
|
|
494
494
|
priority: number;
|
|
495
495
|
forward: boolean;
|
|
496
496
|
subscribeParameters: MessageParameter[];
|
|
497
|
+
earlyDiscardPolicy: EarlyDiscardPolicyConfig | undefined;
|
|
497
498
|
largestLocation: Location | undefined;
|
|
498
499
|
streamsAccepted: bigint;
|
|
499
500
|
expectedStreams: bigint | undefined;
|
|
@@ -716,6 +717,8 @@ type SubscribeOptions = {
|
|
|
716
717
|
startLocation?: Location;
|
|
717
718
|
/** Required for {@link FilterType.AbsoluteRange}; exclusive upper group boundary (coerced to bigint if number provided). */
|
|
718
719
|
endGroup?: bigint | number;
|
|
720
|
+
/** Per-subscription early discard policy. Overrides the client-level default set via {@link MOQtailClient.setEarlyDiscardPolicy}. */
|
|
721
|
+
earlyDiscardPolicy?: EarlyDiscardPolicyConfig;
|
|
719
722
|
};
|
|
720
723
|
/**
|
|
721
724
|
* Narrowing update constraints applied to an existing SUBSCRIBE via {@link MOQtailClient.subscribeUpdate}.
|
|
@@ -799,6 +802,13 @@ type SwitchOptions = {
|
|
|
799
802
|
* })
|
|
800
803
|
* ```
|
|
801
804
|
*/
|
|
805
|
+
/**
|
|
806
|
+
* Configuration for the early discard policy applied to incoming subgroup streams.
|
|
807
|
+
*/
|
|
808
|
+
type EarlyDiscardPolicyConfig = {
|
|
809
|
+
/** Cancel a subgroup QUIC stream if it has not fully completed within this many milliseconds. */
|
|
810
|
+
subgroupReceiveTimeout: number;
|
|
811
|
+
};
|
|
802
812
|
type FetchOptions = {
|
|
803
813
|
/** Request priority (0 = highest, 255 = lowest). Rounded & clamped. */
|
|
804
814
|
priority: number;
|
|
@@ -1277,6 +1287,25 @@ declare class MOQtailClient {
|
|
|
1277
1287
|
* ```
|
|
1278
1288
|
*/
|
|
1279
1289
|
sendDatagram(trackAlias: bigint, object: MoqtObject): Promise<void>;
|
|
1290
|
+
/**
|
|
1291
|
+
* Sets (or replaces) the client-level default early discard policy for incoming subgroup streams.
|
|
1292
|
+
*
|
|
1293
|
+
* When set, each incoming subgroup QUIC stream is given a deadline of `subgroupReceiveTimeout` ms to
|
|
1294
|
+
* complete. If the stream has not finished within that window it is cancelled — objects already
|
|
1295
|
+
* delivered to the subscription are kept, but no further objects arrive from that stream.
|
|
1296
|
+
*
|
|
1297
|
+
* This is a client-wide default. Individual subscriptions can override it via the `earlyDiscardPolicy`
|
|
1298
|
+
* field in {@link SubscribeOptions}, which takes precedence over this setting.
|
|
1299
|
+
*
|
|
1300
|
+
* The policy takes effect on the next stream accepted after this call. Passing a new config
|
|
1301
|
+
* replaces the previous one. Pass `undefined` to remove the default.
|
|
1302
|
+
*
|
|
1303
|
+
* @example
|
|
1304
|
+
* ```ts
|
|
1305
|
+
* client.setEarlyDiscardPolicy({ subgroupReceiveTimeout: 2000 })
|
|
1306
|
+
* ```
|
|
1307
|
+
*/
|
|
1308
|
+
setEarlyDiscardPolicy(config: EarlyDiscardPolicyConfig | undefined): void;
|
|
1280
1309
|
/**
|
|
1281
1310
|
* Gracefully terminates this {@link MOQtailClient} session and releases underlying {@link https://developer.mozilla.org/docs/Web/API/WebTransport | WebTransport} resources.
|
|
1282
1311
|
*
|
|
@@ -1820,4 +1849,4 @@ declare class RecvStream {
|
|
|
1820
1849
|
static new(readStream: ReadableStream<Uint8Array>, partialDataTimeout?: number, onDataReceived?: (data: SubgroupObject | SubgroupHeader | FetchObject | FetchHeader) => void): Promise<RecvStream>;
|
|
1821
1850
|
}
|
|
1822
1851
|
|
|
1823
|
-
export { ControlStream, type FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, type LiveObjectSource, LiveTrackSource, MOQtailClient, type MOQtailClientOptions, type MOQtailRequest, MemoryObjectCache, type ObjectCache, type PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, type SubscribeOptions, SubscribePublication, SubscribeRequest, type SubscribeUpdateOptions, type SwitchOptions, type Track, type TrackSource, TrackStatusRequest };
|
|
1852
|
+
export { ControlStream, type EarlyDiscardPolicyConfig, type FetchOptions, FetchPublication, FetchRequest, HybridTrackSource, type LiveObjectSource, LiveTrackSource, MOQtailClient, type MOQtailClientOptions, type MOQtailRequest, MemoryObjectCache, type ObjectCache, type PastObjectSource, PublishNamespaceRequest, PublishPublication, PublishRequest, RecvDatagramStream, RecvStream, RingBufferObjectCache, SendDatagramStream, SendStream, StaticTrackSource, SubscribeNamespaceRequest, type SubscribeOptions, SubscribePublication, SubscribeRequest, type SubscribeUpdateOptions, type SwitchOptions, type Track, type TrackSource, TrackStatusRequest };
|
package/dist/client.js
CHANGED
|
@@ -5121,6 +5121,7 @@ var SubscribeRequest = class {
|
|
|
5121
5121
|
priority;
|
|
5122
5122
|
forward;
|
|
5123
5123
|
subscribeParameters;
|
|
5124
|
+
earlyDiscardPolicy;
|
|
5124
5125
|
largestLocation;
|
|
5125
5126
|
// Updated on each received object
|
|
5126
5127
|
streamsAccepted = 0n;
|
|
@@ -5956,6 +5957,11 @@ var handlerRequestsBlocked = async (_client, msg) => {
|
|
|
5956
5957
|
logger.debug("handler/requests_blocked", "not implemented", msg);
|
|
5957
5958
|
};
|
|
5958
5959
|
|
|
5960
|
+
// src/client/util/validators.ts
|
|
5961
|
+
function isValidTrackAlias(trackAlias) {
|
|
5962
|
+
return trackAlias !== void 0 && trackAlias >= 0n;
|
|
5963
|
+
}
|
|
5964
|
+
|
|
5959
5965
|
// src/client/handler/subscribe.ts
|
|
5960
5966
|
var handlerSubscribe = async (client, msg) => {
|
|
5961
5967
|
logger.debug("handler/subscribe", `received requestId=${msg.requestId} ftn="${msg.fullTrackName}"`);
|
|
@@ -5988,7 +5994,7 @@ var handlerSubscribe = async (client, msg) => {
|
|
|
5988
5994
|
await client.controlStream.send(response);
|
|
5989
5995
|
return;
|
|
5990
5996
|
}
|
|
5991
|
-
if (!track.trackAlias) throw new Error("Expected track alias to be set");
|
|
5997
|
+
if (!isValidTrackAlias(track.trackAlias)) throw new Error("Expected track alias to be set");
|
|
5992
5998
|
const largestLocation = track.trackSource.live.largestLocation;
|
|
5993
5999
|
const parameters = [...msg.parameters];
|
|
5994
6000
|
if (largestLocation) {
|
|
@@ -6454,6 +6460,8 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6454
6460
|
#isDestroyed = false;
|
|
6455
6461
|
/** Internal monotonically increasing client-assigned request id counter (even/odd parity scheme advances by 2). */
|
|
6456
6462
|
#dontUseRequestId = 0n;
|
|
6463
|
+
/** Active early discard policy; undefined means no per-stream deadline is applied. */
|
|
6464
|
+
#earlyDiscardPolicy;
|
|
6457
6465
|
/**
|
|
6458
6466
|
* TODO: onNamespaceAnnounced may be a better name
|
|
6459
6467
|
* Fired when an PUBLISH_NAMESPACE control message is processed for a track namespace.
|
|
@@ -6905,6 +6913,28 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6905
6913
|
return FullTrackName.tryNew("unknown", `track-${trackAlias}`);
|
|
6906
6914
|
}
|
|
6907
6915
|
}
|
|
6916
|
+
/**
|
|
6917
|
+
* Sets (or replaces) the client-level default early discard policy for incoming subgroup streams.
|
|
6918
|
+
*
|
|
6919
|
+
* When set, each incoming subgroup QUIC stream is given a deadline of `subgroupReceiveTimeout` ms to
|
|
6920
|
+
* complete. If the stream has not finished within that window it is cancelled — objects already
|
|
6921
|
+
* delivered to the subscription are kept, but no further objects arrive from that stream.
|
|
6922
|
+
*
|
|
6923
|
+
* This is a client-wide default. Individual subscriptions can override it via the `earlyDiscardPolicy`
|
|
6924
|
+
* field in {@link SubscribeOptions}, which takes precedence over this setting.
|
|
6925
|
+
*
|
|
6926
|
+
* The policy takes effect on the next stream accepted after this call. Passing a new config
|
|
6927
|
+
* replaces the previous one. Pass `undefined` to remove the default.
|
|
6928
|
+
*
|
|
6929
|
+
* @example
|
|
6930
|
+
* ```ts
|
|
6931
|
+
* client.setEarlyDiscardPolicy({ subgroupReceiveTimeout: 2000 })
|
|
6932
|
+
* ```
|
|
6933
|
+
*/
|
|
6934
|
+
setEarlyDiscardPolicy(config) {
|
|
6935
|
+
this.#ensureActive();
|
|
6936
|
+
this.#earlyDiscardPolicy = config;
|
|
6937
|
+
}
|
|
6908
6938
|
/**
|
|
6909
6939
|
* Gracefully terminates this {@link MOQtailClient} session and releases underlying {@link https://developer.mozilla.org/docs/Web/API/WebTransport | WebTransport} resources.
|
|
6910
6940
|
*
|
|
@@ -6989,7 +7019,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6989
7019
|
*/
|
|
6990
7020
|
addOrUpdateTrack(track) {
|
|
6991
7021
|
this.#ensureActive();
|
|
6992
|
-
if (!track.trackAlias) {
|
|
7022
|
+
if (!isValidTrackAlias(track.trackAlias)) {
|
|
6993
7023
|
track.trackAlias = random60bitId();
|
|
6994
7024
|
}
|
|
6995
7025
|
this.trackSources.set(track.fullTrackName.toString(), track);
|
|
@@ -7121,6 +7151,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7121
7151
|
break;
|
|
7122
7152
|
}
|
|
7123
7153
|
const request = new SubscribeRequest(msg);
|
|
7154
|
+
request.earlyDiscardPolicy = args.earlyDiscardPolicy;
|
|
7124
7155
|
this.requests.set(request.requestId, request);
|
|
7125
7156
|
this.requestIdMap.addMapping(request.requestId, request.fullTrackName);
|
|
7126
7157
|
logger.debug("MOQtailClient", `subscribe: sending SUBSCRIBE requestId=${msg.requestId} ftn="${fullTrackName}"`);
|
|
@@ -7269,7 +7300,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7269
7300
|
const request = this.requests.get(subscriptionRequestId);
|
|
7270
7301
|
if (request instanceof SubscribeRequest) {
|
|
7271
7302
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7272
|
-
if (!trackAlias)
|
|
7303
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7273
7304
|
throw new InternalError("MOQtailClient.subscribeUpdate", "Request exists but track alias mapping does not");
|
|
7274
7305
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7275
7306
|
if (!subscription)
|
|
@@ -7322,7 +7353,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7322
7353
|
if (!(request instanceof SubscribeRequest))
|
|
7323
7354
|
throw new ProtocolViolationError("MOQtailClient.switch", "Request id is not a subscription");
|
|
7324
7355
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7325
|
-
if (!trackAlias)
|
|
7356
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7326
7357
|
throw new InternalError("MOQtailClient.switch", "Request exists but track alias mapping does not");
|
|
7327
7358
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7328
7359
|
if (!subscription) throw new InternalError("MOQtailClient.switch", "Request exists but subscription does not");
|
|
@@ -7986,43 +8017,55 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7986
8017
|
if (subscription) {
|
|
7987
8018
|
subscription.streamsAccepted++;
|
|
7988
8019
|
let firstObjectId = null;
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
|
|
8003
|
-
|
|
8004
|
-
|
|
8005
|
-
|
|
8006
|
-
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8020
|
+
let subgroupTimeoutId;
|
|
8021
|
+
const effectiveDiscardPolicy = subscription.earlyDiscardPolicy ?? this.#earlyDiscardPolicy;
|
|
8022
|
+
if (effectiveDiscardPolicy?.subgroupReceiveTimeout !== void 0) {
|
|
8023
|
+
subgroupTimeoutId = setTimeout(() => {
|
|
8024
|
+
reader.cancel("early discard: subgroupReceiveTimeout exceeded").catch(() => {
|
|
8025
|
+
});
|
|
8026
|
+
}, effectiveDiscardPolicy.subgroupReceiveTimeout);
|
|
8027
|
+
}
|
|
8028
|
+
try {
|
|
8029
|
+
while (true) {
|
|
8030
|
+
const { done, value: nextObject } = await reader.read();
|
|
8031
|
+
if (done) {
|
|
8032
|
+
break;
|
|
8033
|
+
}
|
|
8034
|
+
if (nextObject) {
|
|
8035
|
+
if (nextObject instanceof SubgroupObject) {
|
|
8036
|
+
if (!firstObjectId) {
|
|
8037
|
+
firstObjectId = nextObject.objectId;
|
|
8038
|
+
}
|
|
8039
|
+
let subgroupId = null;
|
|
8040
|
+
if (SubgroupHeaderType.isSubgroupIdZero(header.type)) {
|
|
8041
|
+
subgroupId = 0n;
|
|
8042
|
+
} else if (SubgroupHeaderType.isSubgroupIdFirstObjectId(header.type)) {
|
|
8043
|
+
subgroupId = firstObjectId ?? null;
|
|
8044
|
+
} else if (SubgroupHeaderType.hasExplicitSubgroupId(header.type)) {
|
|
8045
|
+
subgroupId = header.subgroupId ?? null;
|
|
8046
|
+
}
|
|
8047
|
+
const fullTrackName = this.aliasFullTrackNameMap.get(header.trackAlias);
|
|
8048
|
+
if (!fullTrackName) {
|
|
8049
|
+
throw new ProtocolViolationError("MOQtailClient", "No full track name for received track alias");
|
|
8050
|
+
}
|
|
8051
|
+
const moqtObject = MoqtObject.fromSubgroupObject(
|
|
8052
|
+
nextObject,
|
|
8053
|
+
header.groupId,
|
|
8054
|
+
header.publisherPriority,
|
|
8055
|
+
subgroupId,
|
|
8056
|
+
fullTrackName
|
|
8057
|
+
);
|
|
8058
|
+
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8059
|
+
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8060
|
+
subscription.largestLocation = moqtObject.location;
|
|
8061
|
+
subscription.controller?.enqueue(moqtObject);
|
|
8062
|
+
continue;
|
|
8010
8063
|
}
|
|
8011
|
-
|
|
8012
|
-
nextObject,
|
|
8013
|
-
header.groupId,
|
|
8014
|
-
header.publisherPriority,
|
|
8015
|
-
subgroupId,
|
|
8016
|
-
fullTrackName
|
|
8017
|
-
);
|
|
8018
|
-
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8019
|
-
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8020
|
-
subscription.largestLocation = moqtObject.location;
|
|
8021
|
-
subscription.controller?.enqueue(moqtObject);
|
|
8022
|
-
continue;
|
|
8064
|
+
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8023
8065
|
}
|
|
8024
|
-
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8025
8066
|
}
|
|
8067
|
+
} finally {
|
|
8068
|
+
if (subgroupTimeoutId !== void 0) clearTimeout(subgroupTimeoutId);
|
|
8026
8069
|
}
|
|
8027
8070
|
if (subscription.expectedStreams && subscription.expectedStreams === subscription.streamsAccepted) {
|
|
8028
8071
|
subscription.controller?.close();
|
package/dist/index.cjs
CHANGED
|
@@ -5617,6 +5617,7 @@ var SubscribeRequest = class {
|
|
|
5617
5617
|
priority;
|
|
5618
5618
|
forward;
|
|
5619
5619
|
subscribeParameters;
|
|
5620
|
+
earlyDiscardPolicy;
|
|
5620
5621
|
largestLocation;
|
|
5621
5622
|
// Updated on each received object
|
|
5622
5623
|
streamsAccepted = 0n;
|
|
@@ -6452,6 +6453,11 @@ var handlerRequestsBlocked = async (_client, msg) => {
|
|
|
6452
6453
|
logger.debug("handler/requests_blocked", "not implemented", msg);
|
|
6453
6454
|
};
|
|
6454
6455
|
|
|
6456
|
+
// src/client/util/validators.ts
|
|
6457
|
+
function isValidTrackAlias(trackAlias) {
|
|
6458
|
+
return trackAlias !== void 0 && trackAlias >= 0n;
|
|
6459
|
+
}
|
|
6460
|
+
|
|
6455
6461
|
// src/client/handler/subscribe.ts
|
|
6456
6462
|
var handlerSubscribe = async (client, msg) => {
|
|
6457
6463
|
logger.debug("handler/subscribe", `received requestId=${msg.requestId} ftn="${msg.fullTrackName}"`);
|
|
@@ -6484,7 +6490,7 @@ var handlerSubscribe = async (client, msg) => {
|
|
|
6484
6490
|
await client.controlStream.send(response);
|
|
6485
6491
|
return;
|
|
6486
6492
|
}
|
|
6487
|
-
if (!track.trackAlias) throw new Error("Expected track alias to be set");
|
|
6493
|
+
if (!isValidTrackAlias(track.trackAlias)) throw new Error("Expected track alias to be set");
|
|
6488
6494
|
const largestLocation = track.trackSource.live.largestLocation;
|
|
6489
6495
|
const parameters = [...msg.parameters];
|
|
6490
6496
|
if (largestLocation) {
|
|
@@ -6950,6 +6956,8 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6950
6956
|
#isDestroyed = false;
|
|
6951
6957
|
/** Internal monotonically increasing client-assigned request id counter (even/odd parity scheme advances by 2). */
|
|
6952
6958
|
#dontUseRequestId = 0n;
|
|
6959
|
+
/** Active early discard policy; undefined means no per-stream deadline is applied. */
|
|
6960
|
+
#earlyDiscardPolicy;
|
|
6953
6961
|
/**
|
|
6954
6962
|
* TODO: onNamespaceAnnounced may be a better name
|
|
6955
6963
|
* Fired when an PUBLISH_NAMESPACE control message is processed for a track namespace.
|
|
@@ -7401,6 +7409,28 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7401
7409
|
return FullTrackName.tryNew("unknown", `track-${trackAlias}`);
|
|
7402
7410
|
}
|
|
7403
7411
|
}
|
|
7412
|
+
/**
|
|
7413
|
+
* Sets (or replaces) the client-level default early discard policy for incoming subgroup streams.
|
|
7414
|
+
*
|
|
7415
|
+
* When set, each incoming subgroup QUIC stream is given a deadline of `subgroupReceiveTimeout` ms to
|
|
7416
|
+
* complete. If the stream has not finished within that window it is cancelled — objects already
|
|
7417
|
+
* delivered to the subscription are kept, but no further objects arrive from that stream.
|
|
7418
|
+
*
|
|
7419
|
+
* This is a client-wide default. Individual subscriptions can override it via the `earlyDiscardPolicy`
|
|
7420
|
+
* field in {@link SubscribeOptions}, which takes precedence over this setting.
|
|
7421
|
+
*
|
|
7422
|
+
* The policy takes effect on the next stream accepted after this call. Passing a new config
|
|
7423
|
+
* replaces the previous one. Pass `undefined` to remove the default.
|
|
7424
|
+
*
|
|
7425
|
+
* @example
|
|
7426
|
+
* ```ts
|
|
7427
|
+
* client.setEarlyDiscardPolicy({ subgroupReceiveTimeout: 2000 })
|
|
7428
|
+
* ```
|
|
7429
|
+
*/
|
|
7430
|
+
setEarlyDiscardPolicy(config) {
|
|
7431
|
+
this.#ensureActive();
|
|
7432
|
+
this.#earlyDiscardPolicy = config;
|
|
7433
|
+
}
|
|
7404
7434
|
/**
|
|
7405
7435
|
* Gracefully terminates this {@link MOQtailClient} session and releases underlying {@link https://developer.mozilla.org/docs/Web/API/WebTransport | WebTransport} resources.
|
|
7406
7436
|
*
|
|
@@ -7485,7 +7515,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7485
7515
|
*/
|
|
7486
7516
|
addOrUpdateTrack(track) {
|
|
7487
7517
|
this.#ensureActive();
|
|
7488
|
-
if (!track.trackAlias) {
|
|
7518
|
+
if (!isValidTrackAlias(track.trackAlias)) {
|
|
7489
7519
|
track.trackAlias = random60bitId();
|
|
7490
7520
|
}
|
|
7491
7521
|
this.trackSources.set(track.fullTrackName.toString(), track);
|
|
@@ -7617,6 +7647,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7617
7647
|
break;
|
|
7618
7648
|
}
|
|
7619
7649
|
const request = new SubscribeRequest(msg);
|
|
7650
|
+
request.earlyDiscardPolicy = args.earlyDiscardPolicy;
|
|
7620
7651
|
this.requests.set(request.requestId, request);
|
|
7621
7652
|
this.requestIdMap.addMapping(request.requestId, request.fullTrackName);
|
|
7622
7653
|
logger.debug("MOQtailClient", `subscribe: sending SUBSCRIBE requestId=${msg.requestId} ftn="${fullTrackName}"`);
|
|
@@ -7765,7 +7796,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7765
7796
|
const request = this.requests.get(subscriptionRequestId);
|
|
7766
7797
|
if (request instanceof SubscribeRequest) {
|
|
7767
7798
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7768
|
-
if (!trackAlias)
|
|
7799
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7769
7800
|
throw new InternalError("MOQtailClient.subscribeUpdate", "Request exists but track alias mapping does not");
|
|
7770
7801
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7771
7802
|
if (!subscription)
|
|
@@ -7818,7 +7849,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7818
7849
|
if (!(request instanceof SubscribeRequest))
|
|
7819
7850
|
throw new ProtocolViolationError("MOQtailClient.switch", "Request id is not a subscription");
|
|
7820
7851
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7821
|
-
if (!trackAlias)
|
|
7852
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7822
7853
|
throw new InternalError("MOQtailClient.switch", "Request exists but track alias mapping does not");
|
|
7823
7854
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7824
7855
|
if (!subscription) throw new InternalError("MOQtailClient.switch", "Request exists but subscription does not");
|
|
@@ -8482,43 +8513,55 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
8482
8513
|
if (subscription) {
|
|
8483
8514
|
subscription.streamsAccepted++;
|
|
8484
8515
|
let firstObjectId = null;
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8516
|
+
let subgroupTimeoutId;
|
|
8517
|
+
const effectiveDiscardPolicy = subscription.earlyDiscardPolicy ?? this.#earlyDiscardPolicy;
|
|
8518
|
+
if (effectiveDiscardPolicy?.subgroupReceiveTimeout !== void 0) {
|
|
8519
|
+
subgroupTimeoutId = setTimeout(() => {
|
|
8520
|
+
reader.cancel("early discard: subgroupReceiveTimeout exceeded").catch(() => {
|
|
8521
|
+
});
|
|
8522
|
+
}, effectiveDiscardPolicy.subgroupReceiveTimeout);
|
|
8523
|
+
}
|
|
8524
|
+
try {
|
|
8525
|
+
while (true) {
|
|
8526
|
+
const { done, value: nextObject } = await reader.read();
|
|
8527
|
+
if (done) {
|
|
8528
|
+
break;
|
|
8529
|
+
}
|
|
8530
|
+
if (nextObject) {
|
|
8531
|
+
if (nextObject instanceof SubgroupObject) {
|
|
8532
|
+
if (!firstObjectId) {
|
|
8533
|
+
firstObjectId = nextObject.objectId;
|
|
8534
|
+
}
|
|
8535
|
+
let subgroupId = null;
|
|
8536
|
+
if (exports.SubgroupHeaderType.isSubgroupIdZero(header.type)) {
|
|
8537
|
+
subgroupId = 0n;
|
|
8538
|
+
} else if (exports.SubgroupHeaderType.isSubgroupIdFirstObjectId(header.type)) {
|
|
8539
|
+
subgroupId = firstObjectId ?? null;
|
|
8540
|
+
} else if (exports.SubgroupHeaderType.hasExplicitSubgroupId(header.type)) {
|
|
8541
|
+
subgroupId = header.subgroupId ?? null;
|
|
8542
|
+
}
|
|
8543
|
+
const fullTrackName = this.aliasFullTrackNameMap.get(header.trackAlias);
|
|
8544
|
+
if (!fullTrackName) {
|
|
8545
|
+
throw new ProtocolViolationError("MOQtailClient", "No full track name for received track alias");
|
|
8546
|
+
}
|
|
8547
|
+
const moqtObject = MoqtObject.fromSubgroupObject(
|
|
8548
|
+
nextObject,
|
|
8549
|
+
header.groupId,
|
|
8550
|
+
header.publisherPriority,
|
|
8551
|
+
subgroupId,
|
|
8552
|
+
fullTrackName
|
|
8553
|
+
);
|
|
8554
|
+
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8555
|
+
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8556
|
+
subscription.largestLocation = moqtObject.location;
|
|
8557
|
+
subscription.controller?.enqueue(moqtObject);
|
|
8558
|
+
continue;
|
|
8506
8559
|
}
|
|
8507
|
-
|
|
8508
|
-
nextObject,
|
|
8509
|
-
header.groupId,
|
|
8510
|
-
header.publisherPriority,
|
|
8511
|
-
subgroupId,
|
|
8512
|
-
fullTrackName
|
|
8513
|
-
);
|
|
8514
|
-
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8515
|
-
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8516
|
-
subscription.largestLocation = moqtObject.location;
|
|
8517
|
-
subscription.controller?.enqueue(moqtObject);
|
|
8518
|
-
continue;
|
|
8560
|
+
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8519
8561
|
}
|
|
8520
|
-
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8521
8562
|
}
|
|
8563
|
+
} finally {
|
|
8564
|
+
if (subgroupTimeoutId !== void 0) clearTimeout(subgroupTimeoutId);
|
|
8522
8565
|
}
|
|
8523
8566
|
if (subscription.expectedStreams && subscription.expectedStreams === subscription.streamsAccepted) {
|
|
8524
8567
|
subscription.controller?.close();
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +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, TrackStatusRequest } from './client.cjs';
|
|
1
|
+
export { ControlStream, EarlyDiscardPolicyConfig, 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, TrackStatusRequest } from './client.cjs';
|
|
2
2
|
export { AudioLevel, CMSF, CMSFCatalog, CMSFTrack, CaptureTimestamp, CastingError, ExtensionHeader, ExtensionHeaders, ImmutableExtensionsObjectExtension, InternalError, InvalidTypeError, InvalidUTF8Error, KeyValueFormattingError, LengthExceedsMaxError, MOQtailError, NotEnoughBytesError, ObjectExtension, PriorGroupIdGapExtension, PriorObjectIdGapExtension, ProtocolViolationError, RequestIdError, Switch, TerminationCode, TerminationError, TimeoutError, TrackNameError, UnknownObjectExtension, VarIntOverflowError, VideoConfig, VideoFrameMarking } from './model.cjs';
|
|
3
3
|
export { A as AuthTokenVariant, a as AuthorizationToken, B as BaseByteBuffer, b as ByteBuffer, C as ClientSetup, c as ControlMessage, d as ControlMessageType, D as Datagram, e as DefaultPublisherGroupOrderExtension, f as DefaultPublisherPriorityExtension, g as DeliveryTimeout, h as DeliveryTimeoutExtension, i as DynamicGroupsExtension, E as EndOfRangeKind, j as Expires, F as Fetch, k as FetchCancel, l as FetchHeader, m as FetchHeaderType, n as FetchObject, o as FetchObjectContext, p as FetchOk, q as FetchType, r as FilterType, s as Forward, t as FrozenByteBuffer, u as FullTrackName, G as GoAway, v as GroupOrder, w as GroupOrderParam, H as Header, I as ImmutableExtensionsExtension, K as KeyValuePair, L as LOCHeaderExtensionId, x as LargestObject, y as Location, M as MAX_FULL_TRACK_NAME_LENGTH, z as MAX_NAMESPACE_TUPLE_COUNT, J as MAX_REASON_PHRASE_LEN, N as MaxAuthTokenCacheSize, O as MaxCacheDurationExtension, P as MaxRequestId, Q as MaxRequestIdParameter, R as MessageParameter, S as MessageParameterType, T as MessageParameters, U as MoqtObject, V as Namespace, W as NamespaceDone, X as NamespaceSubscribeOptions, Y as NewGroupRequest, Z as ObjectDatagramType, _ as ObjectForwardingPreference, $ as ObjectStatus, a0 as Parameter, a1 as Path, a2 as Publish, a3 as PublishDone, a4 as PublishDoneStatusCode, a5 as PublishNamespace, a6 as PublishNamespaceCancel, a7 as PublishNamespaceDone, a8 as PublishOk, a9 as ReasonPhrase, aa as RequestError, ab as RequestErrorCode, ac as RequestIdMap, ad as RequestOk, ae as RequestUpdate, af as RequestsBlocked, ag as SUPPORTED_VERSIONS, ah as ServerSetup, ai as SetupParameter, aj as SetupParameterType, ak as SetupParameters, al as SubgroupHeader, am as SubgroupHeaderType, an as SubgroupObject, ao as Subscribe, ap as SubscribeNamespace, aq as SubscribeOk, ar as SubscriberPriority, as as SubscriptionFilter, at as TokenAliasType, au as TrackExtension, av as TrackExtensionType, aw as TrackStatus, ax as TrackStatusCode, ay as Tuple, az as TupleField, aA as UnknownTrackExtension, aB as Unsubscribe, aC as UnsubscribeNamespace, aD as applyMessageParameterUpdate, aE as controlMessageTypeFromBigInt, aF as fetchTypeFromBigInt, aG as filterTypeFromBigInt, aH as groupOrderFromNumber, aI as isBytes, aJ as isVarInt, aK as locHeaderExtensionIdFromNumber, aL as messageParameterTypeFromNumber, aM as publishDoneStatusCodeFromBigInt, aN as requestErrorCodeFromBigInt, aO as setupParameterTypeFromNumber, aP as tokenAliasTypeFromNumber, aQ as trackStatusCodeFromBigInt } from './setup_parameter-BOeGq6Mv.cjs';
|
|
4
4
|
export { ClockNormalizer, NetworkTelemetry } from './util.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +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, TrackStatusRequest } from './client.js';
|
|
1
|
+
export { ControlStream, EarlyDiscardPolicyConfig, 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, TrackStatusRequest } from './client.js';
|
|
2
2
|
export { AudioLevel, CMSF, CMSFCatalog, CMSFTrack, CaptureTimestamp, CastingError, ExtensionHeader, ExtensionHeaders, ImmutableExtensionsObjectExtension, InternalError, InvalidTypeError, InvalidUTF8Error, KeyValueFormattingError, LengthExceedsMaxError, MOQtailError, NotEnoughBytesError, ObjectExtension, PriorGroupIdGapExtension, PriorObjectIdGapExtension, ProtocolViolationError, RequestIdError, Switch, TerminationCode, TerminationError, TimeoutError, TrackNameError, UnknownObjectExtension, VarIntOverflowError, VideoConfig, VideoFrameMarking } from './model.js';
|
|
3
3
|
export { A as AuthTokenVariant, a as AuthorizationToken, B as BaseByteBuffer, b as ByteBuffer, C as ClientSetup, c as ControlMessage, d as ControlMessageType, D as Datagram, e as DefaultPublisherGroupOrderExtension, f as DefaultPublisherPriorityExtension, g as DeliveryTimeout, h as DeliveryTimeoutExtension, i as DynamicGroupsExtension, E as EndOfRangeKind, j as Expires, F as Fetch, k as FetchCancel, l as FetchHeader, m as FetchHeaderType, n as FetchObject, o as FetchObjectContext, p as FetchOk, q as FetchType, r as FilterType, s as Forward, t as FrozenByteBuffer, u as FullTrackName, G as GoAway, v as GroupOrder, w as GroupOrderParam, H as Header, I as ImmutableExtensionsExtension, K as KeyValuePair, L as LOCHeaderExtensionId, x as LargestObject, y as Location, M as MAX_FULL_TRACK_NAME_LENGTH, z as MAX_NAMESPACE_TUPLE_COUNT, J as MAX_REASON_PHRASE_LEN, N as MaxAuthTokenCacheSize, O as MaxCacheDurationExtension, P as MaxRequestId, Q as MaxRequestIdParameter, R as MessageParameter, S as MessageParameterType, T as MessageParameters, U as MoqtObject, V as Namespace, W as NamespaceDone, X as NamespaceSubscribeOptions, Y as NewGroupRequest, Z as ObjectDatagramType, _ as ObjectForwardingPreference, $ as ObjectStatus, a0 as Parameter, a1 as Path, a2 as Publish, a3 as PublishDone, a4 as PublishDoneStatusCode, a5 as PublishNamespace, a6 as PublishNamespaceCancel, a7 as PublishNamespaceDone, a8 as PublishOk, a9 as ReasonPhrase, aa as RequestError, ab as RequestErrorCode, ac as RequestIdMap, ad as RequestOk, ae as RequestUpdate, af as RequestsBlocked, ag as SUPPORTED_VERSIONS, ah as ServerSetup, ai as SetupParameter, aj as SetupParameterType, ak as SetupParameters, al as SubgroupHeader, am as SubgroupHeaderType, an as SubgroupObject, ao as Subscribe, ap as SubscribeNamespace, aq as SubscribeOk, ar as SubscriberPriority, as as SubscriptionFilter, at as TokenAliasType, au as TrackExtension, av as TrackExtensionType, aw as TrackStatus, ax as TrackStatusCode, ay as Tuple, az as TupleField, aA as UnknownTrackExtension, aB as Unsubscribe, aC as UnsubscribeNamespace, aD as applyMessageParameterUpdate, aE as controlMessageTypeFromBigInt, aF as fetchTypeFromBigInt, aG as filterTypeFromBigInt, aH as groupOrderFromNumber, aI as isBytes, aJ as isVarInt, aK as locHeaderExtensionIdFromNumber, aL as messageParameterTypeFromNumber, aM as publishDoneStatusCodeFromBigInt, aN as requestErrorCodeFromBigInt, aO as setupParameterTypeFromNumber, aP as tokenAliasTypeFromNumber, aQ as trackStatusCodeFromBigInt } from './setup_parameter-BOeGq6Mv.js';
|
|
4
4
|
export { ClockNormalizer, NetworkTelemetry } from './util.js';
|
package/dist/index.js
CHANGED
|
@@ -5615,6 +5615,7 @@ var SubscribeRequest = class {
|
|
|
5615
5615
|
priority;
|
|
5616
5616
|
forward;
|
|
5617
5617
|
subscribeParameters;
|
|
5618
|
+
earlyDiscardPolicy;
|
|
5618
5619
|
largestLocation;
|
|
5619
5620
|
// Updated on each received object
|
|
5620
5621
|
streamsAccepted = 0n;
|
|
@@ -6450,6 +6451,11 @@ var handlerRequestsBlocked = async (_client, msg) => {
|
|
|
6450
6451
|
logger.debug("handler/requests_blocked", "not implemented", msg);
|
|
6451
6452
|
};
|
|
6452
6453
|
|
|
6454
|
+
// src/client/util/validators.ts
|
|
6455
|
+
function isValidTrackAlias(trackAlias) {
|
|
6456
|
+
return trackAlias !== void 0 && trackAlias >= 0n;
|
|
6457
|
+
}
|
|
6458
|
+
|
|
6453
6459
|
// src/client/handler/subscribe.ts
|
|
6454
6460
|
var handlerSubscribe = async (client, msg) => {
|
|
6455
6461
|
logger.debug("handler/subscribe", `received requestId=${msg.requestId} ftn="${msg.fullTrackName}"`);
|
|
@@ -6482,7 +6488,7 @@ var handlerSubscribe = async (client, msg) => {
|
|
|
6482
6488
|
await client.controlStream.send(response);
|
|
6483
6489
|
return;
|
|
6484
6490
|
}
|
|
6485
|
-
if (!track.trackAlias) throw new Error("Expected track alias to be set");
|
|
6491
|
+
if (!isValidTrackAlias(track.trackAlias)) throw new Error("Expected track alias to be set");
|
|
6486
6492
|
const largestLocation = track.trackSource.live.largestLocation;
|
|
6487
6493
|
const parameters = [...msg.parameters];
|
|
6488
6494
|
if (largestLocation) {
|
|
@@ -6948,6 +6954,8 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
6948
6954
|
#isDestroyed = false;
|
|
6949
6955
|
/** Internal monotonically increasing client-assigned request id counter (even/odd parity scheme advances by 2). */
|
|
6950
6956
|
#dontUseRequestId = 0n;
|
|
6957
|
+
/** Active early discard policy; undefined means no per-stream deadline is applied. */
|
|
6958
|
+
#earlyDiscardPolicy;
|
|
6951
6959
|
/**
|
|
6952
6960
|
* TODO: onNamespaceAnnounced may be a better name
|
|
6953
6961
|
* Fired when an PUBLISH_NAMESPACE control message is processed for a track namespace.
|
|
@@ -7399,6 +7407,28 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7399
7407
|
return FullTrackName.tryNew("unknown", `track-${trackAlias}`);
|
|
7400
7408
|
}
|
|
7401
7409
|
}
|
|
7410
|
+
/**
|
|
7411
|
+
* Sets (or replaces) the client-level default early discard policy for incoming subgroup streams.
|
|
7412
|
+
*
|
|
7413
|
+
* When set, each incoming subgroup QUIC stream is given a deadline of `subgroupReceiveTimeout` ms to
|
|
7414
|
+
* complete. If the stream has not finished within that window it is cancelled — objects already
|
|
7415
|
+
* delivered to the subscription are kept, but no further objects arrive from that stream.
|
|
7416
|
+
*
|
|
7417
|
+
* This is a client-wide default. Individual subscriptions can override it via the `earlyDiscardPolicy`
|
|
7418
|
+
* field in {@link SubscribeOptions}, which takes precedence over this setting.
|
|
7419
|
+
*
|
|
7420
|
+
* The policy takes effect on the next stream accepted after this call. Passing a new config
|
|
7421
|
+
* replaces the previous one. Pass `undefined` to remove the default.
|
|
7422
|
+
*
|
|
7423
|
+
* @example
|
|
7424
|
+
* ```ts
|
|
7425
|
+
* client.setEarlyDiscardPolicy({ subgroupReceiveTimeout: 2000 })
|
|
7426
|
+
* ```
|
|
7427
|
+
*/
|
|
7428
|
+
setEarlyDiscardPolicy(config) {
|
|
7429
|
+
this.#ensureActive();
|
|
7430
|
+
this.#earlyDiscardPolicy = config;
|
|
7431
|
+
}
|
|
7402
7432
|
/**
|
|
7403
7433
|
* Gracefully terminates this {@link MOQtailClient} session and releases underlying {@link https://developer.mozilla.org/docs/Web/API/WebTransport | WebTransport} resources.
|
|
7404
7434
|
*
|
|
@@ -7483,7 +7513,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7483
7513
|
*/
|
|
7484
7514
|
addOrUpdateTrack(track) {
|
|
7485
7515
|
this.#ensureActive();
|
|
7486
|
-
if (!track.trackAlias) {
|
|
7516
|
+
if (!isValidTrackAlias(track.trackAlias)) {
|
|
7487
7517
|
track.trackAlias = random60bitId();
|
|
7488
7518
|
}
|
|
7489
7519
|
this.trackSources.set(track.fullTrackName.toString(), track);
|
|
@@ -7615,6 +7645,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7615
7645
|
break;
|
|
7616
7646
|
}
|
|
7617
7647
|
const request = new SubscribeRequest(msg);
|
|
7648
|
+
request.earlyDiscardPolicy = args.earlyDiscardPolicy;
|
|
7618
7649
|
this.requests.set(request.requestId, request);
|
|
7619
7650
|
this.requestIdMap.addMapping(request.requestId, request.fullTrackName);
|
|
7620
7651
|
logger.debug("MOQtailClient", `subscribe: sending SUBSCRIBE requestId=${msg.requestId} ftn="${fullTrackName}"`);
|
|
@@ -7763,7 +7794,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7763
7794
|
const request = this.requests.get(subscriptionRequestId);
|
|
7764
7795
|
if (request instanceof SubscribeRequest) {
|
|
7765
7796
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7766
|
-
if (!trackAlias)
|
|
7797
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7767
7798
|
throw new InternalError("MOQtailClient.subscribeUpdate", "Request exists but track alias mapping does not");
|
|
7768
7799
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7769
7800
|
if (!subscription)
|
|
@@ -7816,7 +7847,7 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
7816
7847
|
if (!(request instanceof SubscribeRequest))
|
|
7817
7848
|
throw new ProtocolViolationError("MOQtailClient.switch", "Request id is not a subscription");
|
|
7818
7849
|
const trackAlias = this.subscriptionAliasMap.get(subscriptionRequestId);
|
|
7819
|
-
if (!trackAlias)
|
|
7850
|
+
if (!isValidTrackAlias(trackAlias))
|
|
7820
7851
|
throw new InternalError("MOQtailClient.switch", "Request exists but track alias mapping does not");
|
|
7821
7852
|
const subscription = this.subscriptions.get(trackAlias);
|
|
7822
7853
|
if (!subscription) throw new InternalError("MOQtailClient.switch", "Request exists but subscription does not");
|
|
@@ -8480,43 +8511,55 @@ var MOQtailClient = class _MOQtailClient {
|
|
|
8480
8511
|
if (subscription) {
|
|
8481
8512
|
subscription.streamsAccepted++;
|
|
8482
8513
|
let firstObjectId = null;
|
|
8483
|
-
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8514
|
+
let subgroupTimeoutId;
|
|
8515
|
+
const effectiveDiscardPolicy = subscription.earlyDiscardPolicy ?? this.#earlyDiscardPolicy;
|
|
8516
|
+
if (effectiveDiscardPolicy?.subgroupReceiveTimeout !== void 0) {
|
|
8517
|
+
subgroupTimeoutId = setTimeout(() => {
|
|
8518
|
+
reader.cancel("early discard: subgroupReceiveTimeout exceeded").catch(() => {
|
|
8519
|
+
});
|
|
8520
|
+
}, effectiveDiscardPolicy.subgroupReceiveTimeout);
|
|
8521
|
+
}
|
|
8522
|
+
try {
|
|
8523
|
+
while (true) {
|
|
8524
|
+
const { done, value: nextObject } = await reader.read();
|
|
8525
|
+
if (done) {
|
|
8526
|
+
break;
|
|
8527
|
+
}
|
|
8528
|
+
if (nextObject) {
|
|
8529
|
+
if (nextObject instanceof SubgroupObject) {
|
|
8530
|
+
if (!firstObjectId) {
|
|
8531
|
+
firstObjectId = nextObject.objectId;
|
|
8532
|
+
}
|
|
8533
|
+
let subgroupId = null;
|
|
8534
|
+
if (SubgroupHeaderType.isSubgroupIdZero(header.type)) {
|
|
8535
|
+
subgroupId = 0n;
|
|
8536
|
+
} else if (SubgroupHeaderType.isSubgroupIdFirstObjectId(header.type)) {
|
|
8537
|
+
subgroupId = firstObjectId ?? null;
|
|
8538
|
+
} else if (SubgroupHeaderType.hasExplicitSubgroupId(header.type)) {
|
|
8539
|
+
subgroupId = header.subgroupId ?? null;
|
|
8540
|
+
}
|
|
8541
|
+
const fullTrackName = this.aliasFullTrackNameMap.get(header.trackAlias);
|
|
8542
|
+
if (!fullTrackName) {
|
|
8543
|
+
throw new ProtocolViolationError("MOQtailClient", "No full track name for received track alias");
|
|
8544
|
+
}
|
|
8545
|
+
const moqtObject = MoqtObject.fromSubgroupObject(
|
|
8546
|
+
nextObject,
|
|
8547
|
+
header.groupId,
|
|
8548
|
+
header.publisherPriority,
|
|
8549
|
+
subgroupId,
|
|
8550
|
+
fullTrackName
|
|
8551
|
+
);
|
|
8552
|
+
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8553
|
+
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8554
|
+
subscription.largestLocation = moqtObject.location;
|
|
8555
|
+
subscription.controller?.enqueue(moqtObject);
|
|
8556
|
+
continue;
|
|
8504
8557
|
}
|
|
8505
|
-
|
|
8506
|
-
nextObject,
|
|
8507
|
-
header.groupId,
|
|
8508
|
-
header.publisherPriority,
|
|
8509
|
-
subgroupId,
|
|
8510
|
-
fullTrackName
|
|
8511
|
-
);
|
|
8512
|
-
if (!subscription.largestLocation) subscription.largestLocation = moqtObject.location;
|
|
8513
|
-
if (subscription.largestLocation.compare(moqtObject.location) == -1)
|
|
8514
|
-
subscription.largestLocation = moqtObject.location;
|
|
8515
|
-
subscription.controller?.enqueue(moqtObject);
|
|
8516
|
-
continue;
|
|
8558
|
+
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8517
8559
|
}
|
|
8518
|
-
throw new ProtocolViolationError("MOQtailClient", "Received fetch object after subgroup header");
|
|
8519
8560
|
}
|
|
8561
|
+
} finally {
|
|
8562
|
+
if (subgroupTimeoutId !== void 0) clearTimeout(subgroupTimeoutId);
|
|
8520
8563
|
}
|
|
8521
8564
|
if (subscription.expectedStreams && subscription.expectedStreams === subscription.streamsAccepted) {
|
|
8522
8565
|
subscription.controller?.close();
|