@moqtap/trace 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/SPEC.md +30 -1
- package/dist/index.cjs +6 -3
- package/dist/index.d.cts +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +6 -3
- package/package.json +1 -1
- package/src/binary.ts +2 -0
- package/src/recorder.ts +9 -2
- package/src/types.ts +17 -0
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ import { MESSAGE_ID_MAP } from '@moqtap/codec/draft21'
|
|
|
23
23
|
import { createDraft21SessionState } from '@moqtap/codec/draft21/session'
|
|
24
24
|
|
|
25
25
|
const recorder = createRecorder({
|
|
26
|
-
protocol: '
|
|
26
|
+
protocol: 'moq-transport-21',
|
|
27
27
|
perspective: 'client',
|
|
28
28
|
detail: 'control',
|
|
29
29
|
messageTypeId: (name) => Number(MESSAGE_ID_MAP.get(name) ?? 0),
|
package/SPEC.md
CHANGED
|
@@ -44,7 +44,9 @@ and Event 4's `"pl"` begins after the object header, so the bytes of a
|
|
|
44
44
|
including `"full"`. What those headers carry is recorded as decoded fields
|
|
45
45
|
instead — see [Event 1](#event-1-stream-opened). Closing that gap needs a new
|
|
46
46
|
key and is not in this revision; the level's description no longer implies
|
|
47
|
-
otherwise.
|
|
47
|
+
otherwise. The one part of an object's framing recorded as bytes is its
|
|
48
|
+
property block, [Event 3's `"props"`](#event-3-object-header), because the
|
|
49
|
+
block's encoding is the draft's and has no decoded form this format could hold.
|
|
48
50
|
|
|
49
51
|
At any detail level, the recorder MAY be configured to **mask payloads** — replacing payload bytes with zeroes before writing. This preserves payload sizes for bandwidth analysis while stripping media content. Sources MUST advertise masking via `"payloadMasked": true` in the header's `"custom"` map when active.
|
|
50
52
|
|
|
@@ -261,6 +263,33 @@ Object header events and object payload events (event 4) for the same object sha
|
|
|
261
263
|
| `"o"` | integer | `headers`+ | Object ID |
|
|
262
264
|
| `"pp"` | integer | `headers`+ | Publisher priority |
|
|
263
265
|
| `"os"` | integer | `headers`+ | Object status (0=normal, 1=end-of-group, 2=end-of-track, 3=does-not-exist) |
|
|
266
|
+
| `"props"` | byte string | `headers`+ | The object's property block as the wire carried it, past its length field. **Optional.** See below. |
|
|
267
|
+
|
|
268
|
+
#### `"props"` — the property block
|
|
269
|
+
|
|
270
|
+
Object Properties — called Extension Headers before draft-17 — are the part of
|
|
271
|
+
an object a consumer reads before its payload: a LOC object's timestamp, frame
|
|
272
|
+
marking and codec configuration, the gaps a publisher signals after a restart,
|
|
273
|
+
the key ID of an encrypted object. Before this key no level recorded them, and
|
|
274
|
+
none of them can be recovered from the payload, so a trace could show an
|
|
275
|
+
object's bytes but not what decided how they were decoded.
|
|
276
|
+
|
|
277
|
+
- **Bytes, not a decoded list.** Every draft encodes the block differently —
|
|
278
|
+
absolute or delta types, two varint encodings — and a reader already holds
|
|
279
|
+
the codec for the session's draft. A decoded form would freeze one draft's
|
|
280
|
+
rules into this format, and the block MUST be recorded byte for byte so a
|
|
281
|
+
reader decodes exactly what the endpoint saw. Immutable Properties, which an
|
|
282
|
+
encrypted object authenticates, depend on that.
|
|
283
|
+
- **Absent and empty differ.** A writer MUST omit the key when the object had no
|
|
284
|
+
properties field, and MUST write an empty byte string when the field was
|
|
285
|
+
present with a length of 0 — on a subgroup stream with the PROPERTIES flag,
|
|
286
|
+
every object without properties carries one. A reader MUST keep the
|
|
287
|
+
distinction.
|
|
288
|
+
- **`headers`+, not payload-bearing.** The block is object metadata, not media.
|
|
289
|
+
A property value can still carry application data — a codec configuration is
|
|
290
|
+
a few dozen bytes of it — so a source that must not share any object content
|
|
291
|
+
MAY drop the key along with the payload. Dropping it does not change the
|
|
292
|
+
trace's detail level.
|
|
264
293
|
|
|
265
294
|
### Event 4: Object Payload
|
|
266
295
|
|
package/dist/index.cjs
CHANGED
|
@@ -404,6 +404,7 @@ function eventToCbor(event) {
|
|
|
404
404
|
base.o = event.objectId;
|
|
405
405
|
base.pp = int(event.publisherPriority);
|
|
406
406
|
base.os = int(event.objectStatus);
|
|
407
|
+
if (event.properties != null) base.props = event.properties;
|
|
407
408
|
break;
|
|
408
409
|
}
|
|
409
410
|
case "object-payload": {
|
|
@@ -661,7 +662,8 @@ function decodeEvent(src) {
|
|
|
661
662
|
groupId: requireUint(src, "g"),
|
|
662
663
|
objectId: requireUint(src, "o"),
|
|
663
664
|
publisherPriority: Number(_nullishCoalesce(take(src, "pp"), () => ( 0))),
|
|
664
|
-
objectStatus: Number(_nullishCoalesce(take(src, "os"), () => ( 0)))
|
|
665
|
+
objectStatus: Number(_nullishCoalesce(take(src, "os"), () => ( 0))),
|
|
666
|
+
...optional(src, "props", "properties", asByteString)
|
|
665
667
|
};
|
|
666
668
|
case "object-payload":
|
|
667
669
|
return {
|
|
@@ -1215,7 +1217,7 @@ function createRecorder(options) {
|
|
|
1215
1217
|
errorCode
|
|
1216
1218
|
});
|
|
1217
1219
|
},
|
|
1218
|
-
recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus) {
|
|
1220
|
+
recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus, properties) {
|
|
1219
1221
|
if (detailRank < DETAIL_RANK.headers) return;
|
|
1220
1222
|
addEvent({
|
|
1221
1223
|
type: "object-header",
|
|
@@ -1225,7 +1227,8 @@ function createRecorder(options) {
|
|
|
1225
1227
|
groupId,
|
|
1226
1228
|
objectId,
|
|
1227
1229
|
publisherPriority,
|
|
1228
|
-
objectStatus
|
|
1230
|
+
objectStatus,
|
|
1231
|
+
...properties !== void 0 ? { properties } : {}
|
|
1229
1232
|
});
|
|
1230
1233
|
},
|
|
1231
1234
|
recordObjectPayload(streamId, groupId, objectId, size, payload) {
|
package/dist/index.d.cts
CHANGED
|
@@ -298,6 +298,23 @@ interface ObjectHeaderEvent extends BaseEvent {
|
|
|
298
298
|
readonly objectId: bigint;
|
|
299
299
|
readonly publisherPriority: number;
|
|
300
300
|
readonly objectStatus: number;
|
|
301
|
+
/**
|
|
302
|
+
* The Object's property block — Object Properties, Extension Headers before
|
|
303
|
+
* draft-17 — exactly as the wire carried it, past its length field.
|
|
304
|
+
*
|
|
305
|
+
* Absent when the Object had no properties field, which is not the same as an
|
|
306
|
+
* empty block: a subgroup stream with the PROPERTIES flag carries a zero
|
|
307
|
+
* length on every Object that has none. Kept as bytes rather than decoded
|
|
308
|
+
* because the encoding is the draft's, and a reader decodes it with the codec
|
|
309
|
+
* that reads the rest of the session; a decoded form would freeze one draft's
|
|
310
|
+
* rules into the format.
|
|
311
|
+
*
|
|
312
|
+
* Recorded from `'headers'` up. It is object metadata a payload's consumer
|
|
313
|
+
* reads before the payload — timestamps, frame marking, a codec
|
|
314
|
+
* configuration — and before this key no level recorded it, so a trace could
|
|
315
|
+
* not show what decided how its media was decoded.
|
|
316
|
+
*/
|
|
317
|
+
readonly properties?: Uint8Array;
|
|
301
318
|
}
|
|
302
319
|
interface ObjectPayloadEvent extends BaseEvent {
|
|
303
320
|
readonly type: 'object-payload';
|
|
@@ -917,8 +934,13 @@ interface TraceRecorder {
|
|
|
917
934
|
recordStreamOpened(streamId: bigint, direction: 0 | 1, streamType: 0 | 1 | 2): void;
|
|
918
935
|
/** Record a stream-closed event. Ignored at 'control' detail level. */
|
|
919
936
|
recordStreamClosed(streamId: bigint, errorCode?: number): void;
|
|
920
|
-
/**
|
|
921
|
-
|
|
937
|
+
/**
|
|
938
|
+
* Record an object header event. Ignored below 'headers' detail level.
|
|
939
|
+
*
|
|
940
|
+
* `properties` is the Object's property block as the wire carried it; omit it
|
|
941
|
+
* when the Object had no properties field.
|
|
942
|
+
*/
|
|
943
|
+
recordObjectHeader(streamId: bigint, groupId: bigint, objectId: bigint, publisherPriority: number, objectStatus: number, properties?: Uint8Array): void;
|
|
922
944
|
/** Record an object payload event. Ignored below 'headers+sizes' detail level. */
|
|
923
945
|
recordObjectPayload(streamId: bigint, groupId: bigint, objectId: bigint, size: number, payload?: Uint8Array): void;
|
|
924
946
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -298,6 +298,23 @@ interface ObjectHeaderEvent extends BaseEvent {
|
|
|
298
298
|
readonly objectId: bigint;
|
|
299
299
|
readonly publisherPriority: number;
|
|
300
300
|
readonly objectStatus: number;
|
|
301
|
+
/**
|
|
302
|
+
* The Object's property block — Object Properties, Extension Headers before
|
|
303
|
+
* draft-17 — exactly as the wire carried it, past its length field.
|
|
304
|
+
*
|
|
305
|
+
* Absent when the Object had no properties field, which is not the same as an
|
|
306
|
+
* empty block: a subgroup stream with the PROPERTIES flag carries a zero
|
|
307
|
+
* length on every Object that has none. Kept as bytes rather than decoded
|
|
308
|
+
* because the encoding is the draft's, and a reader decodes it with the codec
|
|
309
|
+
* that reads the rest of the session; a decoded form would freeze one draft's
|
|
310
|
+
* rules into the format.
|
|
311
|
+
*
|
|
312
|
+
* Recorded from `'headers'` up. It is object metadata a payload's consumer
|
|
313
|
+
* reads before the payload — timestamps, frame marking, a codec
|
|
314
|
+
* configuration — and before this key no level recorded it, so a trace could
|
|
315
|
+
* not show what decided how its media was decoded.
|
|
316
|
+
*/
|
|
317
|
+
readonly properties?: Uint8Array;
|
|
301
318
|
}
|
|
302
319
|
interface ObjectPayloadEvent extends BaseEvent {
|
|
303
320
|
readonly type: 'object-payload';
|
|
@@ -917,8 +934,13 @@ interface TraceRecorder {
|
|
|
917
934
|
recordStreamOpened(streamId: bigint, direction: 0 | 1, streamType: 0 | 1 | 2): void;
|
|
918
935
|
/** Record a stream-closed event. Ignored at 'control' detail level. */
|
|
919
936
|
recordStreamClosed(streamId: bigint, errorCode?: number): void;
|
|
920
|
-
/**
|
|
921
|
-
|
|
937
|
+
/**
|
|
938
|
+
* Record an object header event. Ignored below 'headers' detail level.
|
|
939
|
+
*
|
|
940
|
+
* `properties` is the Object's property block as the wire carried it; omit it
|
|
941
|
+
* when the Object had no properties field.
|
|
942
|
+
*/
|
|
943
|
+
recordObjectHeader(streamId: bigint, groupId: bigint, objectId: bigint, publisherPriority: number, objectStatus: number, properties?: Uint8Array): void;
|
|
922
944
|
/** Record an object payload event. Ignored below 'headers+sizes' detail level. */
|
|
923
945
|
recordObjectPayload(streamId: bigint, groupId: bigint, objectId: bigint, size: number, payload?: Uint8Array): void;
|
|
924
946
|
/**
|
package/dist/index.js
CHANGED
|
@@ -404,6 +404,7 @@ function eventToCbor(event) {
|
|
|
404
404
|
base.o = event.objectId;
|
|
405
405
|
base.pp = int(event.publisherPriority);
|
|
406
406
|
base.os = int(event.objectStatus);
|
|
407
|
+
if (event.properties != null) base.props = event.properties;
|
|
407
408
|
break;
|
|
408
409
|
}
|
|
409
410
|
case "object-payload": {
|
|
@@ -661,7 +662,8 @@ function decodeEvent(src) {
|
|
|
661
662
|
groupId: requireUint(src, "g"),
|
|
662
663
|
objectId: requireUint(src, "o"),
|
|
663
664
|
publisherPriority: Number(take(src, "pp") ?? 0),
|
|
664
|
-
objectStatus: Number(take(src, "os") ?? 0)
|
|
665
|
+
objectStatus: Number(take(src, "os") ?? 0),
|
|
666
|
+
...optional(src, "props", "properties", asByteString)
|
|
665
667
|
};
|
|
666
668
|
case "object-payload":
|
|
667
669
|
return {
|
|
@@ -1215,7 +1217,7 @@ function createRecorder(options) {
|
|
|
1215
1217
|
errorCode
|
|
1216
1218
|
});
|
|
1217
1219
|
},
|
|
1218
|
-
recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus) {
|
|
1220
|
+
recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus, properties) {
|
|
1219
1221
|
if (detailRank < DETAIL_RANK.headers) return;
|
|
1220
1222
|
addEvent({
|
|
1221
1223
|
type: "object-header",
|
|
@@ -1225,7 +1227,8 @@ function createRecorder(options) {
|
|
|
1225
1227
|
groupId,
|
|
1226
1228
|
objectId,
|
|
1227
1229
|
publisherPriority,
|
|
1228
|
-
objectStatus
|
|
1230
|
+
objectStatus,
|
|
1231
|
+
...properties !== void 0 ? { properties } : {}
|
|
1229
1232
|
});
|
|
1230
1233
|
},
|
|
1231
1234
|
recordObjectPayload(streamId, groupId, objectId, size, payload) {
|
package/package.json
CHANGED
package/src/binary.ts
CHANGED
|
@@ -542,6 +542,7 @@ function eventToCbor(event: TraceEvent): Record<string, unknown> {
|
|
|
542
542
|
base.o = event.objectId
|
|
543
543
|
base.pp = int(event.publisherPriority)
|
|
544
544
|
base.os = int(event.objectStatus)
|
|
545
|
+
if (event.properties != null) base.props = event.properties
|
|
545
546
|
break
|
|
546
547
|
}
|
|
547
548
|
case 'object-payload': {
|
|
@@ -986,6 +987,7 @@ function decodeEvent(src: Decoding): TraceEvent {
|
|
|
986
987
|
objectId: requireUint(src, 'o'),
|
|
987
988
|
publisherPriority: Number(take(src, 'pp') ?? 0),
|
|
988
989
|
objectStatus: Number(take(src, 'os') ?? 0),
|
|
990
|
+
...optional(src, 'props', 'properties', asByteString),
|
|
989
991
|
}
|
|
990
992
|
|
|
991
993
|
case 'object-payload':
|
package/src/recorder.ts
CHANGED
|
@@ -104,13 +104,19 @@ export interface TraceRecorder {
|
|
|
104
104
|
/** Record a stream-closed event. Ignored at 'control' detail level. */
|
|
105
105
|
recordStreamClosed(streamId: bigint, errorCode?: number): void
|
|
106
106
|
|
|
107
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Record an object header event. Ignored below 'headers' detail level.
|
|
109
|
+
*
|
|
110
|
+
* `properties` is the Object's property block as the wire carried it; omit it
|
|
111
|
+
* when the Object had no properties field.
|
|
112
|
+
*/
|
|
108
113
|
recordObjectHeader(
|
|
109
114
|
streamId: bigint,
|
|
110
115
|
groupId: bigint,
|
|
111
116
|
objectId: bigint,
|
|
112
117
|
publisherPriority: number,
|
|
113
118
|
objectStatus: number,
|
|
119
|
+
properties?: Uint8Array,
|
|
114
120
|
): void
|
|
115
121
|
|
|
116
122
|
/** Record an object payload event. Ignored below 'headers+sizes' detail level. */
|
|
@@ -342,7 +348,7 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
|
|
|
342
348
|
})
|
|
343
349
|
},
|
|
344
350
|
|
|
345
|
-
recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus) {
|
|
351
|
+
recordObjectHeader(streamId, groupId, objectId, publisherPriority, objectStatus, properties) {
|
|
346
352
|
if (detailRank < DETAIL_RANK.headers) return
|
|
347
353
|
addEvent({
|
|
348
354
|
type: 'object-header',
|
|
@@ -353,6 +359,7 @@ export function createRecorder(options: RecorderOptions): TraceRecorder {
|
|
|
353
359
|
objectId,
|
|
354
360
|
publisherPriority,
|
|
355
361
|
objectStatus,
|
|
362
|
+
...(properties !== undefined ? { properties } : {}),
|
|
356
363
|
})
|
|
357
364
|
},
|
|
358
365
|
|
package/src/types.ts
CHANGED
|
@@ -319,6 +319,23 @@ export interface ObjectHeaderEvent extends BaseEvent {
|
|
|
319
319
|
readonly objectId: bigint
|
|
320
320
|
readonly publisherPriority: number
|
|
321
321
|
readonly objectStatus: number
|
|
322
|
+
/**
|
|
323
|
+
* The Object's property block — Object Properties, Extension Headers before
|
|
324
|
+
* draft-17 — exactly as the wire carried it, past its length field.
|
|
325
|
+
*
|
|
326
|
+
* Absent when the Object had no properties field, which is not the same as an
|
|
327
|
+
* empty block: a subgroup stream with the PROPERTIES flag carries a zero
|
|
328
|
+
* length on every Object that has none. Kept as bytes rather than decoded
|
|
329
|
+
* because the encoding is the draft's, and a reader decodes it with the codec
|
|
330
|
+
* that reads the rest of the session; a decoded form would freeze one draft's
|
|
331
|
+
* rules into the format.
|
|
332
|
+
*
|
|
333
|
+
* Recorded from `'headers'` up. It is object metadata a payload's consumer
|
|
334
|
+
* reads before the payload — timestamps, frame marking, a codec
|
|
335
|
+
* configuration — and before this key no level recorded it, so a trace could
|
|
336
|
+
* not show what decided how its media was decoded.
|
|
337
|
+
*/
|
|
338
|
+
readonly properties?: Uint8Array
|
|
322
339
|
}
|
|
323
340
|
|
|
324
341
|
export interface ObjectPayloadEvent extends BaseEvent {
|