@blockcast/mmt-base 0.4.0-main.bbd83cb48eff → 0.4.0-main.bbdfdbf39ded
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/catalog-fec-source.d.ts +16 -3
- package/dist/catalog-fec-source.d.ts.map +1 -1
- package/dist/catalog-fec-source.js +24 -5
- package/dist/catalog-fec-source.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mmtp.d.ts +2 -2
- package/dist/mmtp.d.ts.map +1 -1
- package/dist/mmtp.js +4 -3
- package/dist/mmtp.js.map +1 -1
- package/dist/multicast-catalog.d.ts +75 -0
- package/dist/multicast-catalog.d.ts.map +1 -0
- package/dist/multicast-catalog.js +318 -0
- package/dist/multicast-catalog.js.map +1 -0
- package/dist/signalling.d.ts +35 -21
- package/dist/signalling.d.ts.map +1 -1
- package/dist/signalling.js +68 -54
- package/dist/signalling.js.map +1 -1
- package/package.json +1 -1
- package/src/catalog-fec-source.ts +24 -5
- package/src/index.ts +1 -0
- package/src/mmtp.ts +5 -4
- package/src/multicast-catalog.ts +350 -0
- package/src/signalling.ts +73 -59
package/src/signalling.ts
CHANGED
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
import {
|
|
24
24
|
ALFEC_MESSAGE_ID,
|
|
25
25
|
FEC_CODE_RAPTORQ,
|
|
26
|
+
FEC_CODE_REED_SOLOMON,
|
|
27
|
+
MMTP_SIGNALING_PACKET_ID,
|
|
26
28
|
PACKET_TYPE_SIGNALING,
|
|
27
29
|
parseMMTPHeader,
|
|
28
30
|
type MMTPHeader,
|
|
@@ -453,100 +455,112 @@ export function parseAlFecMessage(mmtpPacket: Uint8Array): AlFecMessage | null {
|
|
|
453
455
|
}
|
|
454
456
|
|
|
455
457
|
/**
|
|
456
|
-
* Decoder geometry of one FEC flow
|
|
457
|
-
*
|
|
458
|
-
*
|
|
458
|
+
* Decoder geometry of one FEC flow's AL-FEC message entry under
|
|
459
|
+
* draft-ramadan-moq-mmt-01 Section 8.3 ("iso-ssbg1-v1"): one Asset, coding
|
|
460
|
+
* byte 0x14, fec_code_id_for_repair_flow 3 (RaptorQ) or 1 (Reed-Solomon),
|
|
461
|
+
* and no OTI (draft-ramadan-moq-mmt-01 Section 7.2). K, T and P come from
|
|
462
|
+
* the entry's own fields; a private field, if present, is ignored.
|
|
459
463
|
*/
|
|
460
464
|
export interface AlFecRaptorQFlowConfig {
|
|
465
|
+
/** The flow's one Asset packet_id (number_of_assets is 1). */
|
|
461
466
|
sourcePacketIds: number[]
|
|
462
467
|
repairFlowId: number
|
|
468
|
+
/** `FEC_CODE_RAPTORQ` (3) or `FEC_CODE_REED_SOLOMON` (1). */
|
|
469
|
+
fecCodeId: number
|
|
463
470
|
/** Always `SSBG_MODE_1` (`iso-ssbg1-v1`). */
|
|
464
471
|
ssbgMode: number
|
|
465
|
-
/**
|
|
472
|
+
/** Always 0: the profile's coding byte is exactly 0x14. */
|
|
466
473
|
fecPayloadIdMode: 0 | 1
|
|
474
|
+
/** T, from length_of_repair_symbol. */
|
|
467
475
|
repairSymbolSize: number
|
|
476
|
+
/** K, from maximum_k_for_repair_flow. */
|
|
468
477
|
maximumK: number
|
|
478
|
+
/** P, from maximum_p_for_repair_flow: the RSB_length total over every published layer. */
|
|
469
479
|
maximumP: number
|
|
470
|
-
interleaveDepth: number
|
|
471
|
-
oti: Uint8Array
|
|
472
480
|
protectionWindowTime: number
|
|
473
481
|
protectionWindowSize: number
|
|
474
482
|
}
|
|
475
483
|
|
|
476
|
-
const RAPTORQ_PRIVATE_FIELD_LENGTH = 13
|
|
477
484
|
const RAPTORQ_MAX_SOURCE_SYMBOLS = 56_403
|
|
478
485
|
const REPAIR_SYMBOL_ID_MAX = 0x00ff_ffff
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const transferLength = (oti[0]! * 0x1_0000_0000) +
|
|
483
|
-
(((oti[1]! << 24) | (oti[2]! << 16) | (oti[3]! << 8) | oti[4]!) >>> 0)
|
|
484
|
-
const symbolSize = (oti[6]! << 8) | oti[7]!
|
|
485
|
-
const sourceBlocks = oti[8]!
|
|
486
|
-
const subBlocks = (oti[9]! << 8) | oti[10]!
|
|
487
|
-
const alignment = oti[11]!
|
|
488
|
-
if (!Number.isSafeInteger(transferLength) || transferLength <= 0 ||
|
|
489
|
-
symbolSize !== repairSymbolSize || symbolSize === 0 || symbolSize % 8 !== 0 ||
|
|
490
|
-
sourceBlocks !== 1 || subBlocks !== 1 || alignment !== 8 ||
|
|
491
|
-
transferLength % symbolSize !== 0) {
|
|
492
|
-
return null
|
|
493
|
-
}
|
|
494
|
-
const k = transferLength / symbolSize
|
|
495
|
-
return Number.isInteger(k) && k >= 1 && k <= RAPTORQ_MAX_SOURCE_SYMBOLS ? k : null
|
|
496
|
-
}
|
|
486
|
+
/** The 12-octet MMTP header plus the 2-octet length of the T - 2 construction (moq-fec-01 Section 7.3). */
|
|
487
|
+
const MIN_REPAIR_SYMBOL_SIZE = 14
|
|
488
|
+
const REED_SOLOMON_MAX_BLOCK = 255
|
|
497
489
|
|
|
498
490
|
/**
|
|
499
|
-
* Validate one parsed FEC flow against
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
491
|
+
* Validate one parsed FEC flow against draft-ramadan-moq-mmt-01 Sections 8.3
|
|
492
|
+
* and 12.2 and return its decoder geometry, or null when the entry is
|
|
493
|
+
* outside them. Fail closed on everything else:
|
|
494
|
+
*
|
|
495
|
+
* - coding byte other than 0x14 (one-stage, ssbg_mode1, ffsrpts_flag 0,
|
|
496
|
+
* fec_payload_id_mode 0). 0x15 is fec_payload_id_mode 1 under Amd 1:2025,
|
|
497
|
+
* which "iso-ssbg1-v1" does not describe (moq-fec-01 Section 7.3);
|
|
498
|
+
* - number_of_assets other than 1;
|
|
499
|
+
* - fec_code_id_for_repair_flow other than 3 or 1, the retired Blockcast
|
|
500
|
+
* code 2 included (ISO/IEC 23008-10 Table 1);
|
|
501
|
+
* - T < 14, K = 0, P = 0, K + P - 1 > 2^24 - 1;
|
|
502
|
+
* - RaptorQ: T not a multiple of 8, or K > 56403;
|
|
503
|
+
* - Reed-Solomon: K + P > 255;
|
|
504
|
+
* - repair_flow_id 0 (the signalling flow) or the flow's own packet_id.
|
|
503
505
|
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
* Amd 1:2025): one-stage, ssbg_mode1, ffsrpts_flag 0, fec_payload_id_mode 0.
|
|
507
|
-
* 0x15 is accepted too: its low bit is fec_payload_id_mode 1 under Amd
|
|
508
|
-
* 1:2025 but reserved "1" in the unamended 2023 Table C.3, so a 2023 sender
|
|
509
|
-
* of the same profile emits it. No later field depends on the bit, and each
|
|
510
|
-
* source packet's FEC_type says whether it carries a Source FEC Payload ID,
|
|
511
|
-
* so the bit is reported, not interpreted (BLO-35597 N1). Any other coding
|
|
512
|
-
* byte is refused here, though `parseAlFecMessageBody` still reports it:
|
|
513
|
-
* ssbg_mode0 (0x10/0x11, the retired `k-padded-v1` wire) carries no length
|
|
514
|
-
* field; ffsrpts_flag=1 changes the source/repair packet trailer layout.
|
|
506
|
+
* Checks that span entries (another entry's packet_id, a duplicate
|
|
507
|
+
* repair_flow_id or Asset) are `alFecMessageFlowConfigs`'.
|
|
515
508
|
*/
|
|
516
509
|
export function alFecRaptorQFlowConfig(flow: AlFecFlow): AlFecRaptorQFlowConfig | null {
|
|
517
510
|
if (flow.fecCodingStructure !== FEC_CODING_STRUCTURE_ONE_STAGE || flow.repairFlows.length !== 1) return null
|
|
518
|
-
if (flow.ssbgMode !== SSBG_MODE_1) return null
|
|
519
|
-
if (flow.
|
|
511
|
+
if (flow.ssbgMode !== SSBG_MODE_1 || flow.ffsrptsFlag !== 0 || flow.fecPayloadIdMode !== 0) return null
|
|
512
|
+
if (flow.packetIds.length !== 1) return null
|
|
520
513
|
const repair = flow.repairFlows[0]!
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
if (
|
|
524
|
-
|
|
525
|
-
return null
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const otiK = canonicalRaptorQOtiK(oti, flow.repairSymbolSize)
|
|
530
|
-
if (otiK === null || interleaveDepth === 0) return null
|
|
531
|
-
if (repair.maximumK !== otiK || repair.maximumP === 0 ||
|
|
532
|
-
repair.maximumK + repair.maximumP - 1 > REPAIR_SYMBOL_ID_MAX) {
|
|
514
|
+
const { fecCodeId, maximumK: k, maximumP: p } = repair
|
|
515
|
+
const t = flow.repairSymbolSize
|
|
516
|
+
if (t < MIN_REPAIR_SYMBOL_SIZE || k < 1 || p < 1 || k + p - 1 > REPAIR_SYMBOL_ID_MAX) return null
|
|
517
|
+
if (fecCodeId === FEC_CODE_RAPTORQ) {
|
|
518
|
+
if (t % 8 !== 0 || k > RAPTORQ_MAX_SOURCE_SYMBOLS) return null
|
|
519
|
+
} else if (fecCodeId === FEC_CODE_REED_SOLOMON) {
|
|
520
|
+
if (k + p > REED_SOLOMON_MAX_BLOCK) return null
|
|
521
|
+
} else {
|
|
533
522
|
return null
|
|
534
523
|
}
|
|
524
|
+
if (repair.repairFlowId === MMTP_SIGNALING_PACKET_ID || repair.repairFlowId === flow.packetIds[0]) return null
|
|
535
525
|
return {
|
|
536
526
|
sourcePacketIds: [...flow.packetIds],
|
|
537
527
|
repairFlowId: repair.repairFlowId,
|
|
528
|
+
fecCodeId,
|
|
538
529
|
ssbgMode: flow.ssbgMode,
|
|
539
530
|
fecPayloadIdMode: flow.fecPayloadIdMode,
|
|
540
|
-
repairSymbolSize:
|
|
541
|
-
maximumK:
|
|
542
|
-
maximumP:
|
|
543
|
-
interleaveDepth,
|
|
544
|
-
oti,
|
|
531
|
+
repairSymbolSize: t,
|
|
532
|
+
maximumK: k,
|
|
533
|
+
maximumP: p,
|
|
545
534
|
protectionWindowTime: repair.protectionWindowTime,
|
|
546
535
|
protectionWindowSize: repair.protectionWindowSize,
|
|
547
536
|
}
|
|
548
537
|
}
|
|
549
538
|
|
|
539
|
+
/**
|
|
540
|
+
* `alFecRaptorQFlowConfig` for every entry of a message, index for index,
|
|
541
|
+
* plus the draft-ramadan-moq-mmt-01 Section 12.2 checks that span entries: an
|
|
542
|
+
* entry whose repair_flow_id is another entry's Asset packet_id, and every
|
|
543
|
+
* entry of two that name one repair_flow_id or list one Asset, is null.
|
|
544
|
+
*/
|
|
545
|
+
export function alFecMessageFlowConfigs(message: AlFecMessageBody): (AlFecRaptorQFlowConfig | null)[] {
|
|
546
|
+
const assetCount = new Map<number, number>()
|
|
547
|
+
const repairCount = new Map<number, number>()
|
|
548
|
+
const bump = (map: Map<number, number>, key: number) => map.set(key, (map.get(key) ?? 0) + 1)
|
|
549
|
+
for (const flow of message.flows) {
|
|
550
|
+
for (const id of flow.packetIds) bump(assetCount, id)
|
|
551
|
+
for (const repair of flow.repairFlows) bump(repairCount, repair.repairFlowId)
|
|
552
|
+
}
|
|
553
|
+
return message.flows.map(flow => {
|
|
554
|
+
const config = alFecRaptorQFlowConfig(flow)
|
|
555
|
+
if (!config) return null
|
|
556
|
+
const [asset] = config.sourcePacketIds
|
|
557
|
+
if (assetCount.get(asset!) !== 1 || repairCount.get(config.repairFlowId) !== 1) return null
|
|
558
|
+
// Its own packet_id is already refused, so any count here is another entry's.
|
|
559
|
+
if (assetCount.has(config.repairFlowId)) return null
|
|
560
|
+
return config
|
|
561
|
+
})
|
|
562
|
+
}
|
|
563
|
+
|
|
550
564
|
// ---------------------------------------------------------------------------
|
|
551
565
|
// 10.6 — location, asset_id and identifier mapping
|
|
552
566
|
// ---------------------------------------------------------------------------
|