@moqtap/codec 0.8.0 → 0.8.1
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/{chunk-5XZKO2U5.js → chunk-2XH6PHXR.js} +62 -51
- package/dist/{chunk-APTV6F72.js → chunk-3YAR3I3S.js} +67 -51
- package/dist/{chunk-BQHLUT37.cjs → chunk-7ENEWAMZ.cjs} +2 -6
- package/dist/{chunk-MD2OA7SE.js → chunk-BV6MITLO.js} +15 -8
- package/dist/{chunk-B56AG5HU.cjs → chunk-EVCPBFSR.cjs} +6 -4
- package/dist/{chunk-KNLPX3YA.js → chunk-H6DTAIWM.js} +6 -4
- package/dist/{chunk-J3ASFPCX.cjs → chunk-IADVBKEJ.cjs} +15 -8
- package/dist/{chunk-I55J3KXD.js → chunk-NH4L6NIY.js} +48 -33
- package/dist/{chunk-EQAYMCXO.cjs → chunk-PU2KV7VP.cjs} +48 -33
- package/dist/{chunk-HEO2JG6C.js → chunk-TMXRYOBS.js} +100 -92
- package/dist/{chunk-IEJULBKB.cjs → chunk-UA5XFP4O.cjs} +100 -92
- package/dist/{chunk-KTVN4P6J.cjs → chunk-UZO5Z2XT.cjs} +62 -51
- package/dist/{chunk-MH7537FB.cjs → chunk-VWXUT5R2.cjs} +67 -51
- package/dist/{chunk-JJEN2VG5.js → chunk-XQGEOFGE.js} +2 -6
- package/dist/{codec-BnamSx7L.d.cts → codec-N3g1lxQQ.d.cts} +1 -1
- package/dist/{codec-CFhUGTu2.d.ts → codec-v6Vloc7H.d.ts} +1 -1
- package/dist/draft12.cjs +2 -2
- package/dist/draft12.js +1 -1
- package/dist/draft14-session.d.cts +1 -1
- package/dist/draft14-session.d.ts +1 -1
- package/dist/draft14.cjs +2 -2
- package/dist/draft14.d.cts +3 -3
- package/dist/draft14.d.ts +3 -3
- package/dist/draft14.js +1 -1
- package/dist/draft15.cjs +2 -2
- package/dist/draft15.js +1 -1
- package/dist/draft16.cjs +2 -2
- package/dist/draft16.js +1 -1
- package/dist/draft17.cjs +2 -2
- package/dist/draft17.js +1 -1
- package/dist/draft18.cjs +2 -2
- package/dist/draft18.js +1 -1
- package/dist/draft19.cjs +2 -2
- package/dist/draft19.js +1 -1
- package/dist/index.cjs +14 -14
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -7
- package/dist/session.d.cts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/{types-CVeKIk7A.d.cts → types-HI_bbXka.d.cts} +0 -2
- package/dist/{types-CVeKIk7A.d.ts → types-HI_bbXka.d.ts} +0 -2
- package/package.json +2 -2
- package/src/drafts/draft12/data-streams.ts +13 -4
- package/src/drafts/draft14/codec.ts +2 -6
- package/src/drafts/draft14/types.ts +0 -2
- package/src/drafts/draft15/data-streams.ts +30 -16
- package/src/drafts/draft16/codec.ts +116 -93
- package/src/drafts/draft17/codec.ts +72 -35
- package/src/drafts/draft18/codec.ts +89 -53
- package/src/drafts/draft19/codec.ts +94 -53
|
@@ -75,19 +75,23 @@ export function encodeDatagram(dg: DatagramObject): Uint8Array {
|
|
|
75
75
|
w.writeVarInt(dg.trackAlias)
|
|
76
76
|
w.writeVarInt(dg.groupId)
|
|
77
77
|
|
|
78
|
-
// Datagram type flags:
|
|
78
|
+
// Datagram type flags, Table 5:
|
|
79
79
|
// bit 0 (0x01): extensions present
|
|
80
80
|
// bit 1 (0x02): end-of-group
|
|
81
81
|
// bit 2 (0x04): object_id ABSENT when set
|
|
82
|
+
// bit 3 (0x08): publisher priority ABSENT when set
|
|
82
83
|
// bit 5 (0x20): status (replaces payload)
|
|
83
84
|
const extensionsPresent = (dgType & 0x01) !== 0
|
|
84
85
|
const objectIdAbsent = (dgType & 0x04) !== 0
|
|
86
|
+
const defaultPriority = (dgType & 0x08) !== 0
|
|
85
87
|
const isStatus = (dgType & 0x20) !== 0
|
|
86
88
|
|
|
87
89
|
if (!objectIdAbsent) {
|
|
88
90
|
w.writeVarInt(dg.objectId)
|
|
89
91
|
}
|
|
90
|
-
|
|
92
|
+
if (!defaultPriority) {
|
|
93
|
+
w.writeUint8(dg.publisherPriority)
|
|
94
|
+
}
|
|
91
95
|
|
|
92
96
|
if (extensionsPresent) {
|
|
93
97
|
const extData = dg.extensionData ?? new Uint8Array(0)
|
|
@@ -141,29 +145,34 @@ export function decodeSubgroupStream(bytes: Uint8Array): DecodeResult<SubgroupSt
|
|
|
141
145
|
const r = new BufferReader(bytes)
|
|
142
146
|
const streamType = Number(r.readVarInt())
|
|
143
147
|
|
|
144
|
-
// Draft-15
|
|
145
|
-
//
|
|
148
|
+
// Draft-15 Table 6 assigns 0x10-0x15, 0x18-0x1D, 0x30-0x35 and 0x38-0x3D:
|
|
149
|
+
// the end-of-group marker at 0x08 doubles the range the previous drafts
|
|
150
|
+
// used. What the table leaves out is every Type whose two Subgroup ID mode
|
|
151
|
+
// bits are 0b11. Draft-16 names those reserved in prose; draft-15 only
|
|
152
|
+
// omits them, which comes to the same refusal by a different route.
|
|
146
153
|
if (
|
|
147
|
-
!((streamType >= 0x10 && streamType <=
|
|
154
|
+
!((streamType >= 0x10 && streamType <= 0x1f) || (streamType >= 0x30 && streamType <= 0x3f)) ||
|
|
155
|
+
(streamType & 0x06) === 0x06
|
|
148
156
|
) {
|
|
149
157
|
return {
|
|
150
158
|
ok: false,
|
|
151
159
|
error: new DecodeError(
|
|
152
160
|
'CONSTRAINT_VIOLATION',
|
|
153
|
-
`Expected subgroup stream type 0x10-
|
|
161
|
+
`Expected subgroup stream type 0x10-0x1F/0x30-0x3F, got 0x${streamType.toString(16)}`,
|
|
154
162
|
0,
|
|
155
163
|
),
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
166
|
|
|
159
167
|
// Decode type flags
|
|
160
|
-
// Draft-15
|
|
161
|
-
// bit 0: extensions present
|
|
162
|
-
// bit 1:
|
|
163
|
-
// bit 2: explicit subgroup_id field present
|
|
168
|
+
// Draft-15 Table 6, over the 0x10 base:
|
|
169
|
+
// bit 0 (0x01): extensions present
|
|
170
|
+
// bit 1 (0x02): subgroup id is the first Object ID, not transmitted
|
|
171
|
+
// bit 2 (0x04): explicit subgroup_id field present
|
|
172
|
+
// bit 3 (0x08): contains end of group
|
|
164
173
|
// bit 5 (0x20): no priority (>= 0x30)
|
|
165
174
|
const extensionsPresent = (streamType & 0x01) !== 0
|
|
166
|
-
const endOfGroup = (streamType &
|
|
175
|
+
const endOfGroup = (streamType & 0x08) !== 0
|
|
167
176
|
const hasSubgroupField = (streamType & 0x04) !== 0
|
|
168
177
|
const hasPriority = streamType < 0x30
|
|
169
178
|
|
|
@@ -260,10 +269,12 @@ export function decodeDatagram(bytes: Uint8Array): DecodeResult<DatagramObject>
|
|
|
260
269
|
// bit 0 (0x01): extensions present
|
|
261
270
|
// bit 1 (0x02): end-of-group
|
|
262
271
|
// bit 2 (0x04): object_id ABSENT when set
|
|
272
|
+
// bit 3 (0x08): publisher priority ABSENT when set
|
|
263
273
|
// bit 5 (0x20): status (replaces payload with status varint)
|
|
264
274
|
const extensionsPresent = (dgType & 0x01) !== 0
|
|
265
275
|
const objectIdAbsent = (dgType & 0x04) !== 0
|
|
266
276
|
const endOfGroup = (dgType & 0x02) !== 0
|
|
277
|
+
const defaultPriority = (dgType & 0x08) !== 0
|
|
267
278
|
const isStatus = (dgType & 0x20) !== 0
|
|
268
279
|
|
|
269
280
|
const trackAlias = r.readVarInt()
|
|
@@ -272,7 +283,10 @@ export function decodeDatagram(bytes: Uint8Array): DecodeResult<DatagramObject>
|
|
|
272
283
|
if (!objectIdAbsent) {
|
|
273
284
|
objectId = r.readVarInt()
|
|
274
285
|
}
|
|
275
|
-
|
|
286
|
+
let publisherPriority = 128 // default
|
|
287
|
+
if (!defaultPriority) {
|
|
288
|
+
publisherPriority = r.readUint8()
|
|
289
|
+
}
|
|
276
290
|
|
|
277
291
|
let extensionData: Uint8Array | undefined
|
|
278
292
|
if (extensionsPresent) {
|
|
@@ -531,8 +545,8 @@ export function createSubgroupStreamDecoder(): TransformStream<
|
|
|
531
545
|
|
|
532
546
|
if (
|
|
533
547
|
!(
|
|
534
|
-
(streamType >= 0x10 && streamType <=
|
|
535
|
-
(streamType >= 0x30 && streamType <=
|
|
548
|
+
(streamType >= 0x10 && streamType <= 0x1f) ||
|
|
549
|
+
(streamType >= 0x30 && streamType <= 0x3f)
|
|
536
550
|
)
|
|
537
551
|
) {
|
|
538
552
|
controller.error(
|
|
@@ -760,7 +774,7 @@ export function createDataStreamDecoder(): TransformStream<Uint8Array, DataStrea
|
|
|
760
774
|
if (offset >= buffer.length) return
|
|
761
775
|
const firstByte = buffer[offset]!
|
|
762
776
|
|
|
763
|
-
if ((firstByte >= 0x10 && firstByte <=
|
|
777
|
+
if ((firstByte >= 0x10 && firstByte <= 0x1f) || (firstByte >= 0x30 && firstByte <= 0x3f)) {
|
|
764
778
|
// Subgroup — delegate to subgroup decoder
|
|
765
779
|
// We need to feed the full buffer including the type byte
|
|
766
780
|
const decoder = createSubgroupStreamDecoder()
|
|
@@ -796,7 +810,7 @@ export function createDataStreamDecoder(): TransformStream<Uint8Array, DataStrea
|
|
|
796
810
|
const firstByte = view[0]!
|
|
797
811
|
let result: DecodeResult<Draft15DataStream>
|
|
798
812
|
|
|
799
|
-
if ((firstByte >= 0x10 && firstByte <=
|
|
813
|
+
if ((firstByte >= 0x10 && firstByte <= 0x1f) || (firstByte >= 0x30 && firstByte <= 0x3f)) {
|
|
800
814
|
result = decodeSubgroupStream(view)
|
|
801
815
|
} else if (firstByte === 0x05) {
|
|
802
816
|
result = decodeFetchStream(view)
|
|
@@ -311,116 +311,146 @@ function decodeTrackExtensions(reader: BufferReader): Draft16TrackExtensions {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
function encodeParams(params: Draft16Params, writer: BufferWriter): void {
|
|
314
|
-
|
|
315
|
-
if
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (params.publisher_priority !== undefined) count++
|
|
321
|
-
if (params.forward !== undefined) count++
|
|
322
|
-
if (params.subscriber_priority !== undefined) count++
|
|
323
|
-
if (params.subscription_filter !== undefined) count++
|
|
324
|
-
if (params.group_order !== undefined) count++
|
|
325
|
-
if (params.dynamic_groups !== undefined) count++
|
|
326
|
-
if (params.new_group_request !== undefined) count++
|
|
327
|
-
writer.writeVarInt(count)
|
|
314
|
+
// Draft-16 Section 1.4.2: "Key-Value-Pairs encode a Type value as a delta
|
|
315
|
+
// from the previous Type value, or from 0 if there is no previous Type
|
|
316
|
+
// value." That only defines a wire format if the order is fixed, which the
|
|
317
|
+
// same section fixes: "Parameters MUST be serialized in ascending order by
|
|
318
|
+
// Type." So the entries are collected, sorted, and written as differences.
|
|
319
|
+
const entries: Array<{ type: bigint; encode: (w: BufferWriter) => void }> = []
|
|
328
320
|
|
|
329
321
|
if (params.delivery_timeout !== undefined) {
|
|
330
|
-
|
|
331
|
-
|
|
322
|
+
entries.push({
|
|
323
|
+
type: PARAM_DELIVERY_TIMEOUT,
|
|
324
|
+
encode: (w) => w.writeVarInt(params.delivery_timeout!),
|
|
325
|
+
})
|
|
332
326
|
}
|
|
333
327
|
if (params.authorization_token !== undefined) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
328
|
+
entries.push({
|
|
329
|
+
type: PARAM_AUTHORIZATION_TOKEN,
|
|
330
|
+
encode: (w) => {
|
|
331
|
+
const tmpW = new BufferWriter(64)
|
|
332
|
+
encodeAuthorizationToken(params.authorization_token!, tmpW)
|
|
333
|
+
const raw = tmpW.finish()
|
|
334
|
+
w.writeVarInt(raw.byteLength)
|
|
335
|
+
w.writeBytes(raw)
|
|
336
|
+
},
|
|
337
|
+
})
|
|
340
338
|
}
|
|
341
339
|
if (params.max_cache_duration !== undefined) {
|
|
342
|
-
|
|
343
|
-
|
|
340
|
+
entries.push({
|
|
341
|
+
type: PARAM_MAX_CACHE_DURATION,
|
|
342
|
+
encode: (w) => w.writeVarInt(params.max_cache_duration!),
|
|
343
|
+
})
|
|
344
344
|
}
|
|
345
345
|
if (params.expires !== undefined) {
|
|
346
|
-
|
|
347
|
-
writer.writeVarInt(params.expires)
|
|
346
|
+
entries.push({ type: PARAM_EXPIRES, encode: (w) => w.writeVarInt(params.expires!) })
|
|
348
347
|
}
|
|
349
348
|
if (params.largest_object !== undefined) {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
349
|
+
entries.push({
|
|
350
|
+
type: PARAM_LARGEST_OBJECT,
|
|
351
|
+
// Draft-16 has no per-parameter value encoding: a Key-Value-Pair whose
|
|
352
|
+
// Type is odd carries a Length, and 0x09 is odd. Drafts 17 and later
|
|
353
|
+
// replaced that with "the encoding is specified by each parameter
|
|
354
|
+
// definition", which is where the same field loses its length.
|
|
355
|
+
encode: (w) => {
|
|
356
|
+
const tmpW = new BufferWriter(16)
|
|
357
|
+
tmpW.writeVarInt(params.largest_object!.group)
|
|
358
|
+
tmpW.writeVarInt(params.largest_object!.object)
|
|
359
|
+
const raw = tmpW.finish()
|
|
360
|
+
w.writeVarInt(raw.byteLength)
|
|
361
|
+
w.writeBytes(raw)
|
|
362
|
+
},
|
|
363
|
+
})
|
|
357
364
|
}
|
|
358
365
|
if (params.publisher_priority !== undefined) {
|
|
359
|
-
|
|
360
|
-
|
|
366
|
+
entries.push({
|
|
367
|
+
type: PARAM_PUBLISHER_PRIORITY,
|
|
368
|
+
encode: (w) => w.writeVarInt(params.publisher_priority!),
|
|
369
|
+
})
|
|
361
370
|
}
|
|
362
371
|
if (params.forward !== undefined) {
|
|
363
|
-
|
|
364
|
-
writer.writeVarInt(params.forward)
|
|
372
|
+
entries.push({ type: PARAM_FORWARD, encode: (w) => w.writeVarInt(params.forward!) })
|
|
365
373
|
}
|
|
366
374
|
if (params.subscriber_priority !== undefined) {
|
|
367
|
-
|
|
368
|
-
|
|
375
|
+
entries.push({
|
|
376
|
+
type: PARAM_SUBSCRIBER_PRIORITY,
|
|
377
|
+
encode: (w) => w.writeVarInt(params.subscriber_priority!),
|
|
378
|
+
})
|
|
369
379
|
}
|
|
370
380
|
if (params.subscription_filter !== undefined) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
381
|
+
entries.push({
|
|
382
|
+
type: PARAM_SUBSCRIPTION_FILTER,
|
|
383
|
+
encode: (w) => {
|
|
384
|
+
const tmpW = new BufferWriter(32)
|
|
385
|
+
const f = params.subscription_filter!
|
|
386
|
+
tmpW.writeVarInt(f.filter_type)
|
|
387
|
+
if (f.filter_type === 3n || f.filter_type === 4n) {
|
|
388
|
+
tmpW.writeVarInt(f.start_group!)
|
|
389
|
+
tmpW.writeVarInt(f.start_object!)
|
|
390
|
+
}
|
|
391
|
+
if (f.filter_type === 4n) {
|
|
392
|
+
tmpW.writeVarInt(f.end_group!)
|
|
393
|
+
}
|
|
394
|
+
const raw = tmpW.finish()
|
|
395
|
+
w.writeVarInt(raw.byteLength)
|
|
396
|
+
w.writeBytes(raw)
|
|
397
|
+
},
|
|
398
|
+
})
|
|
385
399
|
}
|
|
386
400
|
if (params.group_order !== undefined) {
|
|
387
|
-
|
|
388
|
-
writer.writeVarInt(params.group_order)
|
|
401
|
+
entries.push({ type: PARAM_GROUP_ORDER, encode: (w) => w.writeVarInt(params.group_order!) })
|
|
389
402
|
}
|
|
390
403
|
if (params.dynamic_groups !== undefined) {
|
|
391
|
-
|
|
392
|
-
|
|
404
|
+
entries.push({
|
|
405
|
+
type: PARAM_DYNAMIC_GROUPS,
|
|
406
|
+
encode: (w) => w.writeVarInt(params.dynamic_groups!),
|
|
407
|
+
})
|
|
393
408
|
}
|
|
394
409
|
if (params.new_group_request !== undefined) {
|
|
395
|
-
|
|
396
|
-
|
|
410
|
+
entries.push({
|
|
411
|
+
type: PARAM_NEW_GROUP_REQUEST,
|
|
412
|
+
encode: (w) => w.writeVarInt(params.new_group_request!),
|
|
413
|
+
})
|
|
397
414
|
}
|
|
398
415
|
|
|
399
416
|
if (params.unknown) {
|
|
400
417
|
for (const u of params.unknown) {
|
|
401
418
|
const id = BigInt(u.id)
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
419
|
+
entries.push({
|
|
420
|
+
type: id,
|
|
421
|
+
encode: (w) => {
|
|
422
|
+
const raw = hexToBytes(u.raw_hex)
|
|
423
|
+
if (id % 2n === 0n) {
|
|
424
|
+
w.writeVarInt(new BufferReader(raw).readVarInt())
|
|
425
|
+
} else {
|
|
426
|
+
w.writeVarInt(raw.byteLength)
|
|
427
|
+
w.writeBytes(raw)
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
})
|
|
413
431
|
}
|
|
414
432
|
}
|
|
433
|
+
|
|
434
|
+
entries.sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))
|
|
435
|
+
|
|
436
|
+
writer.writeVarInt(entries.length)
|
|
437
|
+
let prevType = 0n
|
|
438
|
+
for (const entry of entries) {
|
|
439
|
+
writer.writeVarInt(entry.type - prevType)
|
|
440
|
+
entry.encode(writer)
|
|
441
|
+
prevType = entry.type
|
|
442
|
+
}
|
|
415
443
|
}
|
|
416
444
|
|
|
417
445
|
function decodeParams(reader: BufferReader): Draft16Params {
|
|
418
446
|
const count = Number(reader.readVarInt())
|
|
419
447
|
const result: Draft16Params = {}
|
|
420
|
-
|
|
448
|
+
let prevType = 0n
|
|
421
449
|
|
|
422
450
|
for (let i = 0; i < count; i++) {
|
|
423
|
-
const
|
|
451
|
+
const delta = reader.readVarInt()
|
|
452
|
+
const paramType = prevType + delta
|
|
453
|
+
prevType = paramType
|
|
424
454
|
|
|
425
455
|
if (paramType === PARAM_DELIVERY_TIMEOUT) {
|
|
426
456
|
result.delivery_timeout = reader.readVarInt()
|
|
@@ -472,31 +502,24 @@ function decodeParams(reader: BufferReader): Draft16Params {
|
|
|
472
502
|
const consumed = reader.offset - startOff
|
|
473
503
|
if (consumed < length) reader.readBytes(length - consumed)
|
|
474
504
|
result.subscription_filter = filter
|
|
475
|
-
} else if (paramType % 2n === 0n) {
|
|
476
|
-
const value = reader.readVarInt()
|
|
477
|
-
const tmpWriter = new BufferWriter(16)
|
|
478
|
-
tmpWriter.writeVarInt(value)
|
|
479
|
-
const raw = tmpWriter.finish()
|
|
480
|
-
unknown.push({
|
|
481
|
-
id: `0x${paramType.toString(16)}`,
|
|
482
|
-
length: raw.byteLength,
|
|
483
|
-
raw_hex: bytesToHex(raw),
|
|
484
|
-
})
|
|
485
505
|
} else {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
unknown
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
506
|
+
// Drafts 16 and later: "All Message Parameters MUST be defined in the
|
|
507
|
+
// negotiated version of MOQT or negotiated via Setup Parameters. An
|
|
508
|
+
// endpoint that receives an unknown Message Parameter MUST close the
|
|
509
|
+
// session with PROTOCOL_VIOLATION." (Section 9.2)
|
|
510
|
+
//
|
|
511
|
+
// There is no skipping an unknown one and carrying on: the value's
|
|
512
|
+
// encoding comes from its definition, so a receiver that does not know
|
|
513
|
+
// the Type does not know how many bytes it spans either. Drafts 11
|
|
514
|
+
// through 15 say the opposite and keep collecting them.
|
|
515
|
+
throw new DecodeError(
|
|
516
|
+
'INVALID_PARAMETER',
|
|
517
|
+
`Unknown Message Parameter type 0x${paramType.toString(16)}`,
|
|
518
|
+
reader.offset,
|
|
519
|
+
)
|
|
493
520
|
}
|
|
494
521
|
}
|
|
495
522
|
|
|
496
|
-
if (unknown.length > 0) {
|
|
497
|
-
result.unknown = unknown
|
|
498
|
-
}
|
|
499
|
-
|
|
500
523
|
return result
|
|
501
524
|
}
|
|
502
525
|
|
|
@@ -236,6 +236,43 @@ const PARAM_SUBSCRIPTION_FILTER = 0x21n
|
|
|
236
236
|
const PARAM_GROUP_ORDER = 0x22n
|
|
237
237
|
const PARAM_NEW_GROUP_REQUEST = 0x32n
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Which messages each Message Parameter's own definition names.
|
|
241
|
+
*
|
|
242
|
+
* Section 9.3.1: "Each Message Parameter definition indicates the message types in
|
|
243
|
+
* which it can appear. If it appears in some other type of message, the
|
|
244
|
+
* receiving endpoint MUST close the connection with a PROTOCOL_VIOLATION."
|
|
245
|
+
* Drafts up to and including 16 end that sentence "it MUST be ignored", so this
|
|
246
|
+
* table has no counterpart there.
|
|
247
|
+
*
|
|
248
|
+
* A parameter type absent from this table is one no definition restricts. A
|
|
249
|
+
* type this draft does not define at all never reaches here: it is refused as
|
|
250
|
+
* unknown while its scope is still undecidable.
|
|
251
|
+
*/
|
|
252
|
+
const PARAMETER_SCOPE = new Map<bigint, Set<string>>([
|
|
253
|
+
[0x02n, new Set(['publish_ok', 'subscribe', 'request_update'])],
|
|
254
|
+
[
|
|
255
|
+
0x03n,
|
|
256
|
+
new Set([
|
|
257
|
+
'publish',
|
|
258
|
+
'subscribe',
|
|
259
|
+
'request_update',
|
|
260
|
+
'subscribe_namespace',
|
|
261
|
+
'publish_namespace',
|
|
262
|
+
'track_status',
|
|
263
|
+
'fetch',
|
|
264
|
+
]),
|
|
265
|
+
],
|
|
266
|
+
[0x04n, new Set(['subscribe'])],
|
|
267
|
+
[0x08n, new Set(['subscribe_ok', 'publish', 'publish_ok', 'request_ok'])],
|
|
268
|
+
[0x09n, new Set(['subscribe_ok', 'publish', 'request_ok'])],
|
|
269
|
+
[0x10n, new Set(['subscribe', 'request_update', 'publish', 'publish_ok', 'subscribe_namespace'])],
|
|
270
|
+
[0x20n, new Set(['subscribe', 'fetch', 'request_update', 'publish_ok'])],
|
|
271
|
+
[0x21n, new Set(['subscribe', 'publish_ok', 'request_update'])],
|
|
272
|
+
[0x22n, new Set(['subscribe', 'publish_ok', 'fetch'])],
|
|
273
|
+
[0x32n, new Set(['publish_ok', 'subscribe', 'request_update'])],
|
|
274
|
+
])
|
|
275
|
+
|
|
239
276
|
function encodeParams(params: Draft17Params, writer: BufferWriter): void {
|
|
240
277
|
// Collect and sort params by type
|
|
241
278
|
const entries: Array<{ type: bigint; encode: (w: BufferWriter) => void }> = []
|
|
@@ -350,10 +387,9 @@ function encodeParams(params: Draft17Params, writer: BufferWriter): void {
|
|
|
350
387
|
}
|
|
351
388
|
}
|
|
352
389
|
|
|
353
|
-
function decodeParams(reader: BufferReader): Draft17Params {
|
|
390
|
+
function decodeParams(reader: BufferReader, messageType: string): Draft17Params {
|
|
354
391
|
const count = Number(reader.readVarInt())
|
|
355
392
|
const result: Draft17Params = {}
|
|
356
|
-
const unknown: UnknownParam[] = []
|
|
357
393
|
let prevType = 0n
|
|
358
394
|
|
|
359
395
|
for (let i = 0; i < count; i++) {
|
|
@@ -361,6 +397,15 @@ function decodeParams(reader: BufferReader): Draft17Params {
|
|
|
361
397
|
const paramType = prevType + delta
|
|
362
398
|
prevType = paramType
|
|
363
399
|
|
|
400
|
+
const scope = PARAMETER_SCOPE.get(paramType)
|
|
401
|
+
if (scope !== undefined && !scope.has(messageType)) {
|
|
402
|
+
throw new DecodeError(
|
|
403
|
+
'CONSTRAINT_VIOLATION',
|
|
404
|
+
`Message Parameter 0x${paramType.toString(16)} may not appear in ${messageType}`,
|
|
405
|
+
reader.offset,
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
|
|
364
409
|
if (paramType === PARAM_DELIVERY_TIMEOUT) {
|
|
365
410
|
result.delivery_timeout = reader.readVarInt()
|
|
366
411
|
} else if (paramType === PARAM_AUTHORIZATION_TOKEN) {
|
|
@@ -408,31 +453,23 @@ function decodeParams(reader: BufferReader): Draft17Params {
|
|
|
408
453
|
} else if (paramType === PARAM_NEW_GROUP_REQUEST) {
|
|
409
454
|
result.new_group_request = reader.readVarInt()
|
|
410
455
|
} else {
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const bytes = reader.readBytes(length)
|
|
426
|
-
unknown.push({
|
|
427
|
-
id: `0x${paramType.toString(16)}`,
|
|
428
|
-
length,
|
|
429
|
-
raw_hex: bytesToHex(bytes),
|
|
430
|
-
})
|
|
431
|
-
}
|
|
456
|
+
// Drafts 16 and later: "All Message Parameters MUST be defined in the
|
|
457
|
+
// negotiated version of MOQT or negotiated via Setup Options. An
|
|
458
|
+
// endpoint that receives an unknown Message Parameter MUST close the
|
|
459
|
+
// session with PROTOCOL_VIOLATION." (Section 9.3)
|
|
460
|
+
//
|
|
461
|
+
// There is no skipping an unknown one and carrying on: the value's
|
|
462
|
+
// encoding comes from its definition, so a receiver that does not know
|
|
463
|
+
// the Type does not know how many bytes it spans either. Drafts 11
|
|
464
|
+
// through 15 say the opposite and keep collecting them.
|
|
465
|
+
throw new DecodeError(
|
|
466
|
+
'INVALID_PARAMETER',
|
|
467
|
+
`Unknown Message Parameter type 0x${paramType.toString(16)}`,
|
|
468
|
+
reader.offset,
|
|
469
|
+
)
|
|
432
470
|
}
|
|
433
471
|
}
|
|
434
472
|
|
|
435
|
-
if (unknown.length > 0) result.unknown = unknown
|
|
436
473
|
return result
|
|
437
474
|
}
|
|
438
475
|
|
|
@@ -747,7 +784,7 @@ function decodeSubscribePayload(r: BufferReader): Draft17Message {
|
|
|
747
784
|
const required_request_id_delta = r.readVarInt()
|
|
748
785
|
const track_namespace = r.readTuple()
|
|
749
786
|
const track_name = r.readString()
|
|
750
|
-
const parameters = decodeParams(r)
|
|
787
|
+
const parameters = decodeParams(r, 'subscribe')
|
|
751
788
|
return {
|
|
752
789
|
type: 'subscribe',
|
|
753
790
|
request_id,
|
|
@@ -760,7 +797,7 @@ function decodeSubscribePayload(r: BufferReader): Draft17Message {
|
|
|
760
797
|
|
|
761
798
|
function decodeSubscribeOkPayload(r: BufferReader, payloadEnd: number): Draft17Message {
|
|
762
799
|
const track_alias = r.readVarInt()
|
|
763
|
-
const parameters = decodeParams(r)
|
|
800
|
+
const parameters = decodeParams(r, 'subscribe_ok')
|
|
764
801
|
const track_properties = decodeTrackProperties(r, payloadEnd)
|
|
765
802
|
return { type: 'subscribe_ok', track_alias, parameters, track_properties }
|
|
766
803
|
}
|
|
@@ -768,7 +805,7 @@ function decodeSubscribeOkPayload(r: BufferReader, payloadEnd: number): Draft17M
|
|
|
768
805
|
function decodeRequestUpdatePayload(r: BufferReader): Draft17Message {
|
|
769
806
|
const request_id = r.readVarInt()
|
|
770
807
|
const required_request_id_delta = r.readVarInt()
|
|
771
|
-
const parameters = decodeParams(r)
|
|
808
|
+
const parameters = decodeParams(r, 'request_update')
|
|
772
809
|
return {
|
|
773
810
|
type: 'request_update',
|
|
774
811
|
request_id,
|
|
@@ -783,7 +820,7 @@ function decodePublishPayload(r: BufferReader, payloadEnd: number): Draft17Messa
|
|
|
783
820
|
const track_namespace = r.readTuple()
|
|
784
821
|
const track_name = r.readString()
|
|
785
822
|
const track_alias = r.readVarInt()
|
|
786
|
-
const parameters = decodeParams(r)
|
|
823
|
+
const parameters = decodeParams(r, 'publish')
|
|
787
824
|
const track_properties = decodeTrackProperties(r, payloadEnd)
|
|
788
825
|
return {
|
|
789
826
|
type: 'publish',
|
|
@@ -798,7 +835,7 @@ function decodePublishPayload(r: BufferReader, payloadEnd: number): Draft17Messa
|
|
|
798
835
|
}
|
|
799
836
|
|
|
800
837
|
function decodePublishOkPayload(r: BufferReader): Draft17Message {
|
|
801
|
-
const parameters = decodeParams(r)
|
|
838
|
+
const parameters = decodeParams(r, 'publish_ok')
|
|
802
839
|
return { type: 'publish_ok', parameters }
|
|
803
840
|
}
|
|
804
841
|
|
|
@@ -813,7 +850,7 @@ function decodePublishNamespacePayload(r: BufferReader): Draft17Message {
|
|
|
813
850
|
const request_id = r.readVarInt()
|
|
814
851
|
const required_request_id_delta = r.readVarInt()
|
|
815
852
|
const track_namespace = r.readTuple()
|
|
816
|
-
const parameters = decodeParams(r)
|
|
853
|
+
const parameters = decodeParams(r, 'publish_namespace')
|
|
817
854
|
return {
|
|
818
855
|
type: 'publish_namespace',
|
|
819
856
|
request_id,
|
|
@@ -838,7 +875,7 @@ function decodeSubscribeNamespacePayload(r: BufferReader): Draft17Message {
|
|
|
838
875
|
const required_request_id_delta = r.readVarInt()
|
|
839
876
|
const namespace_prefix = r.readTuple()
|
|
840
877
|
const subscribe_options = r.readVarInt()
|
|
841
|
-
const parameters = decodeParams(r)
|
|
878
|
+
const parameters = decodeParams(r, 'subscribe_namespace')
|
|
842
879
|
return {
|
|
843
880
|
type: 'subscribe_namespace',
|
|
844
881
|
request_id,
|
|
@@ -889,7 +926,7 @@ function decodeFetchPayload(r: BufferReader): Draft17Message {
|
|
|
889
926
|
joining = { joining_request_id, joining_start }
|
|
890
927
|
}
|
|
891
928
|
|
|
892
|
-
const parameters = decodeParams(r)
|
|
929
|
+
const parameters = decodeParams(r, 'fetch')
|
|
893
930
|
|
|
894
931
|
return {
|
|
895
932
|
type: 'fetch',
|
|
@@ -906,7 +943,7 @@ function decodeFetchOkPayload(r: BufferReader, payloadEnd: number): Draft17Messa
|
|
|
906
943
|
const end_of_track = r.readUint8()
|
|
907
944
|
const end_group = r.readVarInt()
|
|
908
945
|
const end_object = r.readVarInt()
|
|
909
|
-
const parameters = decodeParams(r)
|
|
946
|
+
const parameters = decodeParams(r, 'fetch_ok')
|
|
910
947
|
const track_properties = decodeTrackProperties(r, payloadEnd)
|
|
911
948
|
return {
|
|
912
949
|
type: 'fetch_ok',
|
|
@@ -923,7 +960,7 @@ function decodeTrackStatusPayload(r: BufferReader): Draft17Message {
|
|
|
923
960
|
const required_request_id_delta = r.readVarInt()
|
|
924
961
|
const track_namespace = r.readTuple()
|
|
925
962
|
const track_name = r.readString()
|
|
926
|
-
const parameters = decodeParams(r)
|
|
963
|
+
const parameters = decodeParams(r, 'track_status')
|
|
927
964
|
return {
|
|
928
965
|
type: 'track_status',
|
|
929
966
|
request_id,
|
|
@@ -935,7 +972,7 @@ function decodeTrackStatusPayload(r: BufferReader): Draft17Message {
|
|
|
935
972
|
}
|
|
936
973
|
|
|
937
974
|
function decodeRequestOkPayload(r: BufferReader): Draft17Message {
|
|
938
|
-
const parameters = decodeParams(r)
|
|
975
|
+
const parameters = decodeParams(r, 'request_ok')
|
|
939
976
|
return { type: 'request_ok', parameters }
|
|
940
977
|
}
|
|
941
978
|
|