@tapflowio/agent-core 0.4.1 → 0.5.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.
@@ -1,6 +1,16 @@
1
1
  export declare const TFFE_MAGIC: readonly [84, 70, 70, 69];
2
2
  export declare const HEADER_SIZE = 22;
3
+ export declare const CODEC_JPEG = 0;
4
+ export declare const CODEC_H264 = 1;
5
+ export interface EnvelopeFlags {
6
+ codec: number;
7
+ keyframe: boolean;
8
+ }
3
9
  export declare function hasEnvelope(frame: Buffer): boolean;
4
- export declare function writeEnvelopeHeader(payload: Buffer, capturedAt: number): Buffer;
10
+ export declare function writeEnvelopeHeader(payload: Buffer, capturedAt: number, opts?: {
11
+ codec?: number;
12
+ keyframe?: boolean;
13
+ }): Buffer;
14
+ export declare function readEnvelopeFlags(frame: Buffer): EnvelopeFlags;
5
15
  export declare function patchRelayedAt(frame: Buffer, relayedAt: number): void;
6
16
  //# sourceMappingURL=envelope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/utils/envelope.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,2BAAoC,CAAA;AAC3D,eAAO,MAAM,WAAW,KAAK,CAAA;AAE7B,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQlD;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ/E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAErE"}
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/utils/envelope.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,2BAAoC,CAAA;AAC3D,eAAO,MAAM,WAAW,KAAK,CAAA;AAI7B,eAAO,MAAM,UAAU,IAAI,CAAA;AAC3B,eAAO,MAAM,UAAU,IAAI,CAAA;AAK3B,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQlD;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5C,MAAM,CAUR;AAGD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAQ9D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAErE"}
@@ -1,5 +1,11 @@
1
1
  export const TFFE_MAGIC = [0x54, 0x46, 0x46, 0x45];
2
2
  export const HEADER_SIZE = 22;
3
+ // Codec marker carried in byte 5 (flags) bit 0. Absence (0) = JPEG keeps full
4
+ // backward compatibility with frames written before the marker existed.
5
+ export const CODEC_JPEG = 0;
6
+ export const CODEC_H264 = 1;
7
+ const FLAG_H264 = 0x01; // byte5 bit0: payload codec (0 = JPEG, 1 = H.264)
8
+ const FLAG_KEYFRAME = 0x02; // byte5 bit1: H.264 keyframe (IDR) — for keyframe-aware drop
3
9
  export function hasEnvelope(frame) {
4
10
  return (frame.length >= HEADER_SIZE &&
5
11
  frame[0] === 0x54 &&
@@ -7,18 +13,30 @@ export function hasEnvelope(frame) {
7
13
  frame[2] === 0x46 &&
8
14
  frame[3] === 0x45);
9
15
  }
10
- export function writeEnvelopeHeader(payload, capturedAt) {
16
+ export function writeEnvelopeHeader(payload, capturedAt, opts) {
11
17
  const header = Buffer.allocUnsafe(HEADER_SIZE);
12
18
  header[0] = 0x54;
13
19
  header[1] = 0x46;
14
20
  header[2] = 0x46;
15
21
  header[3] = 0x45;
16
22
  header[4] = 1; // version
17
- header[5] = 0; // flags (reserved)
23
+ // keyframe is an H.264 IDR marker — only set it when the codec is H.264.
24
+ const isH264 = opts?.codec === CODEC_H264;
25
+ header[5] = (isH264 ? FLAG_H264 : 0) | (isH264 && opts?.keyframe ? FLAG_KEYFRAME : 0);
18
26
  header.writeBigUInt64BE(BigInt(capturedAt), 6);
19
27
  header.writeBigUInt64BE(0n, 14); // relayedAt: filled in by relay
20
28
  return Buffer.concat([header, payload]);
21
29
  }
30
+ // Reads the codec/keyframe flags from byte 5. Caller must ensure hasEnvelope(frame).
31
+ export function readEnvelopeFlags(frame) {
32
+ const flags = frame[5];
33
+ const codec = flags & FLAG_H264 ? CODEC_H264 : CODEC_JPEG;
34
+ return {
35
+ codec,
36
+ // keyframe is only valid for H.264; normalize JPEG frames to false.
37
+ keyframe: codec === CODEC_H264 && (flags & FLAG_KEYFRAME) !== 0,
38
+ };
39
+ }
22
40
  export function patchRelayedAt(frame, relayedAt) {
23
41
  frame.writeBigUInt64BE(BigInt(relayedAt), 14);
24
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/utils/envelope.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAA;AAE7B,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,CACL,KAAK,CAAC,MAAM,IAAI,WAAW;QAC3B,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAClB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,UAAkB;IACrE,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;IAC9C,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IACtE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA,CAAG,UAAU;IAC1B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA,CAAG,mBAAmB;IACnC,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;IAC9C,MAAM,CAAC,gBAAgB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA,CAAC,gCAAgC;IAChE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,SAAiB;IAC7D,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAA;AAC/C,CAAC"}
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/utils/envelope.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAA;AAC3D,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAA;AAE7B,8EAA8E;AAC9E,wEAAwE;AACxE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAA;AAC3B,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAA;AAE3B,MAAM,SAAS,GAAG,IAAI,CAAA,CAAK,kDAAkD;AAC7E,MAAM,aAAa,GAAG,IAAI,CAAA,CAAC,6DAA6D;AAOxF,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,CACL,KAAK,CAAC,MAAM,IAAI,WAAW;QAC3B,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI;QACjB,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAClB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,OAAe,EACf,UAAkB,EAClB,IAA6C;IAE7C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;IAC9C,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;IACtE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA,CAAG,UAAU;IAC1B,yEAAyE;IACzE,MAAM,MAAM,GAAG,IAAI,EAAE,KAAK,KAAK,UAAU,CAAA;IACzC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACrF,MAAM,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAA;IAC9C,MAAM,CAAC,gBAAgB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA,CAAC,gCAAgC;IAChE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AACzC,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACtB,MAAM,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAA;IACzD,OAAO;QACL,KAAK;QACL,oEAAoE;QACpE,QAAQ,EAAE,KAAK,KAAK,UAAU,IAAI,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC;KAChE,CAAA;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,SAAiB;IAC7D,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAA;AAC/C,CAAC"}
@@ -1,4 +1,10 @@
1
1
  export { createResourceSampler } from './resources.js';
2
- export { registerStreamWs, sendBinaryWithBackpressure, createRateLimitedDropWarn, DEFAULT_BACKPRESSURE_BYTES, } from './stream.js';
3
- export { TFFE_MAGIC, HEADER_SIZE, hasEnvelope, writeEnvelopeHeader, patchRelayedAt, } from './envelope.js';
2
+ export { registerStreamWs, sendBinaryWithBackpressure, createKeyframeAwareSender, createRateLimitedDropWarn, DEFAULT_BACKPRESSURE_BYTES, } from './stream.js';
3
+ export type { KeyframeAwareSender } from './stream.js';
4
+ export { TFFE_MAGIC, HEADER_SIZE, CODEC_JPEG, CODEC_H264, hasEnvelope, writeEnvelopeHeader, readEnvelopeFlags, patchRelayedAt, } from './envelope.js';
5
+ export type { EnvelopeFlags } from './envelope.js';
6
+ export { createThroughputSampler } from './throughput.js';
7
+ export type { ThroughputSample } from './throughput.js';
8
+ export { parseSpsVui, rewriteSpsLowLatency, rewriteLowLatencySpsInFrame, } from './sps.js';
9
+ export type { SpsVuiInfo } from './sps.js';
4
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,cAAc,GACf,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EACL,UAAU,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA"}
@@ -1,4 +1,6 @@
1
1
  export { createResourceSampler } from './resources.js';
2
- export { registerStreamWs, sendBinaryWithBackpressure, createRateLimitedDropWarn, DEFAULT_BACKPRESSURE_BYTES, } from './stream.js';
3
- export { TFFE_MAGIC, HEADER_SIZE, hasEnvelope, writeEnvelopeHeader, patchRelayedAt, } from './envelope.js';
2
+ export { registerStreamWs, sendBinaryWithBackpressure, createKeyframeAwareSender, createRateLimitedDropWarn, DEFAULT_BACKPRESSURE_BYTES, } from './stream.js';
3
+ export { TFFE_MAGIC, HEADER_SIZE, CODEC_JPEG, CODEC_H264, hasEnvelope, writeEnvelopeHeader, readEnvelopeFlags, patchRelayedAt, } from './envelope.js';
4
+ export { createThroughputSampler } from './throughput.js';
5
+ export { parseSpsVui, rewriteSpsLowLatency, rewriteLowLatencySpsInFrame, } from './sps.js';
4
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,cAAc,GACf,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,yBAAyB,EACzB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,UAAU,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,GACf,MAAM,eAAe,CAAA;AAEtB,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAEzD,OAAO,EACL,WAAW,EACX,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,UAAU,CAAA"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Minimal H.264 SPS tooling — reads and rewrites the VUI bitstream_restriction
3
+ * fields that govern decoder latency: max_num_reorder_frames / max_dec_frame_buffering.
4
+ *
5
+ * Why: a decoder buffers up to max_num_reorder_frames before emitting output. If
6
+ * the encoder doesn't declare 0 (VUI bitstream_restriction), the decoder assumes
7
+ * the level's max DPB and adds frames of latency even with no B-frames (~250ms on
8
+ * an iPhone at Level 5.0). VideoToolbox omits this even with B-frames off, so we
9
+ * inject "reorder=0" on the keyframe SPS before sending.
10
+ *
11
+ * Mirror: packages/dashboard/lib/decoders/parseSpsVui.ts keeps a browser copy
12
+ * (defense-in-depth when the agent is unpatched). Keep the two in sync.
13
+ */
14
+ export interface SpsVuiInfo {
15
+ profileIdc: number;
16
+ levelIdc: number;
17
+ vuiPresent: boolean;
18
+ bitstreamRestriction: boolean;
19
+ /** null when not declared (decoder then assumes the level max). */
20
+ maxNumReorderFrames: number | null;
21
+ maxDecFrameBuffering: number | null;
22
+ }
23
+ export declare function parseSpsVui(spsNal: Uint8Array): SpsVuiInfo;
24
+ /**
25
+ * Rewrites the SPS to declare bitstream_restriction with max_num_reorder_frames=0
26
+ * (and max_dec_frame_buffering = max_num_ref_frames) so decoders emit frames
27
+ * immediately. Returns null when it can't safely rewrite (no VUI, or restriction
28
+ * already declared) — caller falls back to the original SPS.
29
+ */
30
+ export declare function rewriteSpsLowLatency(spsNal: Uint8Array): Uint8Array | null;
31
+ /**
32
+ * Rewrites the SPS NAL inside an Annex B frame to declare reorder=0. Returns the
33
+ * SAME reference when there's nothing to change (no SPS, or already declared), so
34
+ * P-frames are zero-cost; only keyframes carry an SPS. Reassembles with 4-byte
35
+ * start codes.
36
+ */
37
+ export declare function rewriteLowLatencySpsInFrame(frame: Uint8Array): Uint8Array;
38
+ //# sourceMappingURL=sps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sps.d.ts","sourceRoot":"","sources":["../../src/utils/sps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAwLD,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAU1D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAkB1E;AAyBD;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAwBzE"}
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Minimal H.264 SPS tooling — reads and rewrites the VUI bitstream_restriction
3
+ * fields that govern decoder latency: max_num_reorder_frames / max_dec_frame_buffering.
4
+ *
5
+ * Why: a decoder buffers up to max_num_reorder_frames before emitting output. If
6
+ * the encoder doesn't declare 0 (VUI bitstream_restriction), the decoder assumes
7
+ * the level's max DPB and adds frames of latency even with no B-frames (~250ms on
8
+ * an iPhone at Level 5.0). VideoToolbox omits this even with B-frames off, so we
9
+ * inject "reorder=0" on the keyframe SPS before sending.
10
+ *
11
+ * Mirror: packages/dashboard/lib/decoders/parseSpsVui.ts keeps a browser copy
12
+ * (defense-in-depth when the agent is unpatched). Keep the two in sync.
13
+ */
14
+ class BitReader {
15
+ bytes;
16
+ bitPos = 0;
17
+ constructor(bytes) {
18
+ this.bytes = bytes;
19
+ }
20
+ get position() { return this.bitPos; }
21
+ bit() {
22
+ const byteIdx = this.bitPos >> 3;
23
+ const bitIdx = 7 - (this.bitPos & 7);
24
+ this.bitPos++;
25
+ if (byteIdx >= this.bytes.length)
26
+ return 0;
27
+ return (this.bytes[byteIdx] >> bitIdx) & 1;
28
+ }
29
+ bits(n) {
30
+ let v = 0;
31
+ for (let i = 0; i < n; i++)
32
+ v = (v << 1) | this.bit();
33
+ return v >>> 0;
34
+ }
35
+ flag() { return this.bit() === 1; }
36
+ ue() {
37
+ let zeros = 0;
38
+ while (this.bit() === 0 && zeros < 32)
39
+ zeros++;
40
+ if (zeros === 0)
41
+ return 0;
42
+ return (1 << zeros) - 1 + this.bits(zeros);
43
+ }
44
+ se() {
45
+ const k = this.ue();
46
+ return (k & 1) ? Math.ceil(k / 2) : -Math.ceil(k / 2);
47
+ }
48
+ }
49
+ class BitWriter {
50
+ bitsArr = [];
51
+ bit(b) { this.bitsArr.push(b & 1); }
52
+ ue(val) {
53
+ const code = val + 1;
54
+ const len = Math.floor(Math.log2(code));
55
+ for (let i = 0; i < len; i++)
56
+ this.bit(0);
57
+ for (let i = len; i >= 0; i--)
58
+ this.bit((code >> i) & 1);
59
+ }
60
+ toBytes() {
61
+ const out = [];
62
+ for (let i = 0; i < this.bitsArr.length; i += 8) {
63
+ let b = 0;
64
+ for (let j = 0; j < 8; j++)
65
+ b = (b << 1) | (this.bitsArr[i + j] ?? 0);
66
+ out.push(b);
67
+ }
68
+ return new Uint8Array(out);
69
+ }
70
+ }
71
+ // Strip the 1-byte NAL header and emulation_prevention_three_byte (00 00 03 → 00 00).
72
+ function toRbsp(nal) {
73
+ const out = [];
74
+ let zeros = 0;
75
+ for (let i = 1; i < nal.length; i++) {
76
+ const b = nal[i];
77
+ if (zeros >= 2 && b === 0x03) {
78
+ zeros = 0;
79
+ continue;
80
+ }
81
+ out.push(b);
82
+ zeros = b === 0 ? zeros + 1 : 0;
83
+ }
84
+ return new Uint8Array(out);
85
+ }
86
+ // Re-insert emulation_prevention_three_byte before any 00 00 {00,01,02,03}.
87
+ function addEmulation(rbsp) {
88
+ const out = [];
89
+ let zeros = 0;
90
+ for (const b of rbsp) {
91
+ if (zeros >= 2 && b <= 0x03) {
92
+ out.push(0x03);
93
+ zeros = 0;
94
+ }
95
+ out.push(b);
96
+ zeros = b === 0 ? zeros + 1 : 0;
97
+ }
98
+ return new Uint8Array(out);
99
+ }
100
+ const HIGH_PROFILES = new Set([100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135]);
101
+ function skipHrd(r) {
102
+ const cpbCnt = r.ue() + 1;
103
+ r.bits(4);
104
+ r.bits(4); // bit_rate_scale, cpb_size_scale
105
+ for (let i = 0; i < cpbCnt; i++) {
106
+ r.ue();
107
+ r.ue();
108
+ r.flag();
109
+ }
110
+ r.bits(5);
111
+ r.bits(5);
112
+ r.bits(5);
113
+ r.bits(5);
114
+ }
115
+ function walkSps(r) {
116
+ const profileIdc = r.bits(8);
117
+ r.bits(8); // constraint flags + reserved
118
+ const levelIdc = r.bits(8);
119
+ r.ue(); // seq_parameter_set_id
120
+ if (HIGH_PROFILES.has(profileIdc)) {
121
+ const chromaFormatIdc = r.ue();
122
+ if (chromaFormatIdc === 3)
123
+ r.flag(); // separate_colour_plane_flag
124
+ r.ue();
125
+ r.ue(); // bit_depth_luma/chroma_minus8
126
+ r.flag(); // qpprime_y_zero_transform_bypass_flag
127
+ if (r.flag()) { // seq_scaling_matrix_present_flag
128
+ const lists = chromaFormatIdc !== 3 ? 8 : 12;
129
+ for (let i = 0; i < lists; i++) {
130
+ if (r.flag()) {
131
+ const size = i < 6 ? 16 : 64;
132
+ let lastScale = 8, nextScale = 8;
133
+ for (let j = 0; j < size; j++) {
134
+ if (nextScale !== 0)
135
+ nextScale = (lastScale + r.se() + 256) % 256;
136
+ lastScale = nextScale === 0 ? lastScale : nextScale;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ r.ue(); // log2_max_frame_num_minus4
143
+ const picOrderCntType = r.ue();
144
+ if (picOrderCntType === 0) {
145
+ r.ue(); // log2_max_pic_order_cnt_lsb_minus4
146
+ }
147
+ else if (picOrderCntType === 1) {
148
+ r.flag();
149
+ r.se();
150
+ r.se();
151
+ const n = r.ue();
152
+ for (let i = 0; i < n; i++)
153
+ r.se();
154
+ }
155
+ const numRefFrames = r.ue(); // max_num_ref_frames
156
+ r.flag(); // gaps_in_frame_num_value_allowed_flag
157
+ r.ue(); // pic_width_in_mbs_minus1
158
+ r.ue(); // pic_height_in_map_units_minus1
159
+ if (!r.flag())
160
+ r.flag(); // frame_mbs_only_flag → mb_adaptive_frame_field_flag
161
+ r.flag(); // direct_8x8_inference_flag
162
+ if (r.flag()) {
163
+ r.ue();
164
+ r.ue();
165
+ r.ue();
166
+ r.ue();
167
+ } // frame_cropping
168
+ const base = {
169
+ profileIdc, levelIdc, numRefFrames,
170
+ bitstreamRestriction: false,
171
+ maxNumReorderFrames: null,
172
+ maxDecFrameBuffering: null,
173
+ };
174
+ const vuiPresent = r.flag();
175
+ if (!vuiPresent)
176
+ return { ...base, vuiPresent: false, restrictionFlagPos: -1 };
177
+ if (r.flag()) {
178
+ if (r.bits(8) === 255) {
179
+ r.bits(16);
180
+ r.bits(16);
181
+ }
182
+ } // aspect_ratio
183
+ if (r.flag())
184
+ r.flag(); // overscan
185
+ if (r.flag()) {
186
+ r.bits(3);
187
+ r.flag();
188
+ if (r.flag()) {
189
+ r.bits(8);
190
+ r.bits(8);
191
+ r.bits(8);
192
+ }
193
+ } // video_signal_type
194
+ if (r.flag()) {
195
+ r.ue();
196
+ r.ue();
197
+ } // chroma_loc_info
198
+ if (r.flag()) {
199
+ r.bits(32);
200
+ r.bits(32);
201
+ r.flag();
202
+ } // timing_info
203
+ const nalHrd = r.flag();
204
+ if (nalHrd)
205
+ skipHrd(r);
206
+ const vclHrd = r.flag();
207
+ if (vclHrd)
208
+ skipHrd(r);
209
+ if (nalHrd || vclHrd)
210
+ r.flag(); // low_delay_hrd_flag
211
+ r.flag(); // pic_struct_present_flag
212
+ const restrictionFlagPos = r.position;
213
+ const bitstreamRestriction = r.flag();
214
+ if (!bitstreamRestriction) {
215
+ return { ...base, vuiPresent: true, restrictionFlagPos };
216
+ }
217
+ r.flag(); // motion_vectors_over_pic_boundaries_flag
218
+ r.ue();
219
+ r.ue();
220
+ r.ue();
221
+ r.ue(); // denoms + log2 max mv h/v
222
+ return {
223
+ ...base,
224
+ vuiPresent: true,
225
+ bitstreamRestriction: true,
226
+ maxNumReorderFrames: r.ue(),
227
+ maxDecFrameBuffering: r.ue(),
228
+ restrictionFlagPos,
229
+ };
230
+ }
231
+ export function parseSpsVui(spsNal) {
232
+ const w = walkSps(new BitReader(toRbsp(spsNal)));
233
+ return {
234
+ profileIdc: w.profileIdc,
235
+ levelIdc: w.levelIdc,
236
+ vuiPresent: w.vuiPresent,
237
+ bitstreamRestriction: w.bitstreamRestriction,
238
+ maxNumReorderFrames: w.maxNumReorderFrames,
239
+ maxDecFrameBuffering: w.maxDecFrameBuffering,
240
+ };
241
+ }
242
+ /**
243
+ * Rewrites the SPS to declare bitstream_restriction with max_num_reorder_frames=0
244
+ * (and max_dec_frame_buffering = max_num_ref_frames) so decoders emit frames
245
+ * immediately. Returns null when it can't safely rewrite (no VUI, or restriction
246
+ * already declared) — caller falls back to the original SPS.
247
+ */
248
+ export function rewriteSpsLowLatency(spsNal) {
249
+ const rbsp = toRbsp(spsNal);
250
+ const walk = walkSps(new BitReader(rbsp));
251
+ if (!walk.vuiPresent || walk.restrictionFlagPos < 0)
252
+ return null;
253
+ if (walk.bitstreamRestriction)
254
+ return null; // already declared — leave as-is
255
+ const w = new BitWriter();
256
+ const copy = new BitReader(rbsp);
257
+ for (let i = 0; i < walk.restrictionFlagPos; i++)
258
+ w.bit(copy.bit()); // verbatim prefix
259
+ w.bit(1); // bitstream_restriction_flag = 1
260
+ w.bit(1); // motion_vectors_over_pic_boundaries_flag
261
+ w.ue(0);
262
+ w.ue(0);
263
+ w.ue(0);
264
+ w.ue(0); // denoms + log2 max mv h/v (neutral)
265
+ w.ue(0); // max_num_reorder_frames = 0
266
+ w.ue(walk.numRefFrames); // max_dec_frame_buffering ≥ max_num_ref_frames (valid)
267
+ w.bit(1); // rbsp_stop_one_bit (toBytes zero-pads the rest)
268
+ // Preserve the original NAL header byte (nal_ref_idc may differ from 0x67).
269
+ return Uint8Array.from([spsNal[0], ...addEmulation(w.toBytes())]);
270
+ }
271
+ const START_CODE = Uint8Array.of(0, 0, 0, 1);
272
+ // Splits an Annex B buffer (3- or 4-byte start codes) into NAL units (no start codes).
273
+ function splitNalUnits(buf) {
274
+ const nals = [];
275
+ const n = buf.length;
276
+ let start = -1;
277
+ let p = 0;
278
+ while (p + 2 < n) {
279
+ const sc4 = p + 3 < n && buf[p] === 0 && buf[p + 1] === 0 && buf[p + 2] === 0 && buf[p + 3] === 1;
280
+ const sc3 = buf[p] === 0 && buf[p + 1] === 0 && buf[p + 2] === 1;
281
+ if (sc4 || sc3) {
282
+ if (start >= 0)
283
+ nals.push(buf.subarray(start, p));
284
+ p += sc4 ? 4 : 3;
285
+ start = p;
286
+ }
287
+ else {
288
+ p++;
289
+ }
290
+ }
291
+ if (start >= 0)
292
+ nals.push(buf.subarray(start, n));
293
+ return nals;
294
+ }
295
+ /**
296
+ * Rewrites the SPS NAL inside an Annex B frame to declare reorder=0. Returns the
297
+ * SAME reference when there's nothing to change (no SPS, or already declared), so
298
+ * P-frames are zero-cost; only keyframes carry an SPS. Reassembles with 4-byte
299
+ * start codes.
300
+ */
301
+ export function rewriteLowLatencySpsInFrame(frame) {
302
+ const nals = splitNalUnits(frame);
303
+ if (nals.length === 0)
304
+ return frame;
305
+ let changed = false;
306
+ const out = [];
307
+ for (const nal of nals) {
308
+ if ((nal[0] & 0x1f) === 7) { // SPS
309
+ const rewritten = rewriteSpsLowLatency(nal);
310
+ if (rewritten) {
311
+ out.push(rewritten);
312
+ changed = true;
313
+ continue;
314
+ }
315
+ }
316
+ out.push(nal);
317
+ }
318
+ if (!changed)
319
+ return frame;
320
+ let total = 0;
321
+ for (const nal of out)
322
+ total += START_CODE.length + nal.length;
323
+ const result = new Uint8Array(total);
324
+ let off = 0;
325
+ for (const nal of out) {
326
+ result.set(START_CODE, off);
327
+ off += START_CODE.length;
328
+ result.set(nal, off);
329
+ off += nal.length;
330
+ }
331
+ return result;
332
+ }
333
+ //# sourceMappingURL=sps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sps.js","sourceRoot":"","sources":["../../src/utils/sps.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,29 @@
1
1
  import { WebSocket } from 'ws';
2
2
  import type { Logger } from '../logger.js';
3
3
  export declare const DEFAULT_BACKPRESSURE_BYTES = 1048576;
4
- export declare function sendBinaryWithBackpressure(ws: WebSocket, data: Parameters<WebSocket['send']>[0], threshold: number, onDrop: () => void): void;
4
+ export declare function sendBinaryWithBackpressure(ws: WebSocket, data: Parameters<WebSocket['send']>[0], threshold: number, onDrop: () => void): boolean;
5
+ export interface KeyframeAwareSender {
6
+ /**
7
+ * Sends `frame`, or drops it to keep the H.264 reference chain intact.
8
+ * `isKeyframe` must be true for IDR frames and for independent frames (JPEG).
9
+ * Returns true if sent, false if dropped.
10
+ */
11
+ send(ws: WebSocket, frame: Parameters<WebSocket['send']>[0], threshold: number, isKeyframe: boolean, onDrop: () => void,
12
+ /** Fired while dropping when the socket has room but no keyframe has arrived —
13
+ * the moment to request an on-demand IDR to resync faster. */
14
+ onWantKeyframe?: () => void): boolean;
15
+ }
16
+ /**
17
+ * Stateful, keyframe-aware backpressure sender (one per session/stream).
18
+ *
19
+ * Unlike sendBinaryWithBackpressure (drop-to-latest, fine for independent JPEG
20
+ * frames), this preserves the H.264 reference chain: once a frame is dropped under
21
+ * backpressure it enters a "dropping" state and discards every frame until a keyframe
22
+ * (IDR) can be sent — so the decoder never receives a P-frame that references a
23
+ * dropped frame, which would tear until the next IDR. Independent frames (JPEG) pass
24
+ * isKeyframe=true every frame, reproducing drop-to-latest exactly.
25
+ */
26
+ export declare function createKeyframeAwareSender(): KeyframeAwareSender;
5
27
  export declare function createRateLimitedDropWarn(logger: Logger, context: string, intervalMs?: number): () => void;
6
28
  export declare function registerStreamWs(ws: WebSocket, sessionId: string): Promise<void>;
7
29
  //# sourceMappingURL=stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/utils/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAC9B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,eAAO,MAAM,0BAA0B,UAAY,CAAA;AAEnD,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,IAAI,GACjB,IAAI,CAON;AAID,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,UAAU,SAAO,GAChB,MAAM,IAAI,CAYZ;AAID,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhF"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/utils/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAC9B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAE1C,eAAO,MAAM,0BAA0B,UAAY,CAAA;AAGnD,wBAAgB,0BAA0B,CACxC,EAAE,EAAE,SAAS,EACb,IAAI,EAAE,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,IAAI,GACjB,OAAO,CAQT;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,IAAI,CACF,EAAE,EAAE,SAAS,EACb,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EACvC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,OAAO,EACnB,MAAM,EAAE,MAAM,IAAI;IAClB;mEAC+D;IAC/D,cAAc,CAAC,EAAE,MAAM,IAAI,GAC1B,OAAO,CAAA;CACX;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,IAAI,mBAAmB,CA+B/D;AAID,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,UAAU,SAAO,GAChB,MAAM,IAAI,CAYZ;AAID,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhF"}
@@ -1,13 +1,56 @@
1
1
  import { WebSocket } from 'ws';
2
2
  export const DEFAULT_BACKPRESSURE_BYTES = 1_048_576; // 1 MB
3
+ // Returns true if the frame was sent, false if it was dropped (backpressure or closed socket).
3
4
  export function sendBinaryWithBackpressure(ws, data, threshold, onDrop) {
4
5
  if (ws.readyState !== WebSocket.OPEN)
5
- return;
6
+ return false;
6
7
  if (ws.bufferedAmount >= threshold) {
7
8
  onDrop();
8
- return;
9
+ return false;
9
10
  }
10
11
  ws.send(data, { binary: true });
12
+ return true;
13
+ }
14
+ /**
15
+ * Stateful, keyframe-aware backpressure sender (one per session/stream).
16
+ *
17
+ * Unlike sendBinaryWithBackpressure (drop-to-latest, fine for independent JPEG
18
+ * frames), this preserves the H.264 reference chain: once a frame is dropped under
19
+ * backpressure it enters a "dropping" state and discards every frame until a keyframe
20
+ * (IDR) can be sent — so the decoder never receives a P-frame that references a
21
+ * dropped frame, which would tear until the next IDR. Independent frames (JPEG) pass
22
+ * isKeyframe=true every frame, reproducing drop-to-latest exactly.
23
+ */
24
+ export function createKeyframeAwareSender() {
25
+ let dropping = false;
26
+ return {
27
+ send(ws, frame, threshold, isKeyframe, onDrop, onWantKeyframe) {
28
+ if (ws.readyState !== WebSocket.OPEN)
29
+ return false;
30
+ const full = ws.bufferedAmount >= threshold;
31
+ if (dropping) {
32
+ // Resync only on a keyframe we can actually send; otherwise keep dropping.
33
+ if (isKeyframe && !full) {
34
+ dropping = false;
35
+ ws.send(frame, { binary: true });
36
+ return true;
37
+ }
38
+ onDrop();
39
+ // Buffer drained but still no keyframe — ask the source for an IDR to resync.
40
+ if (!full)
41
+ onWantKeyframe?.();
42
+ return false;
43
+ }
44
+ if (full) {
45
+ // Drop this frame and stop forwarding until the next sendable keyframe.
46
+ dropping = true;
47
+ onDrop();
48
+ return false;
49
+ }
50
+ ws.send(frame, { binary: true });
51
+ return true;
52
+ },
53
+ };
11
54
  }
12
55
  // Returns a rate-limited warn callback for use as onDrop.
13
56
  // At most one warn per intervalMs per call site; resets the drop counter after each warn.
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/utils/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAG9B,MAAM,CAAC,MAAM,0BAA0B,GAAG,SAAS,CAAA,CAAC,OAAO;AAE3D,MAAM,UAAU,0BAA0B,CACxC,EAAa,EACb,IAAsC,EACtC,SAAiB,EACjB,MAAkB;IAElB,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI;QAAE,OAAM;IAC5C,IAAI,EAAE,CAAC,cAAc,IAAI,SAAS,EAAE,CAAC;QACnC,MAAM,EAAE,CAAA;QACR,OAAM;IACR,CAAC;IACD,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;AACjC,CAAC;AAED,0DAA0D;AAC1D,0FAA0F;AAC1F,MAAM,UAAU,yBAAyB,CACvC,MAAc,EACd,OAAe,EACf,UAAU,GAAG,IAAI;IAEjB,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,OAAO,GAAG,EAAE;QACV,SAAS,EAAE,CAAA;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,IAAI,GAAG,GAAG,UAAU,IAAI,UAAU,EAAE,CAAC;YACnC,UAAU,GAAG,GAAG,CAAA;YAChB,MAAM,CAAC,IAAI,CAAC,oBAAoB,SAAS,sBAAsB,OAAO,GAAG,CAAC,CAAA;YAC1E,SAAS,GAAG,CAAC,CAAA;QACf,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED,0EAA0E;AAC1E,mGAAmG;AACnG,MAAM,UAAU,gBAAgB,CAAC,EAAa,EAAE,SAAiB;IAC/D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;YACnB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAA;QACjE,CAAC,CAAC,CAAA;QAEF,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE;YAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAA;YACvC,IAAI,GAAG,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBACrC,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;gBACxB,OAAO,EAAE,CAAA;YACX,CAAC;QACH,CAAC,CAAA;QACD,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;QACvB,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC1B,CAAC,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/utils/stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface ThroughputSample {
2
+ elapsedMs: number;
3
+ sentFrames: number;
4
+ droppedFrames: number;
5
+ producedFrames: number;
6
+ sentBytes: number;
7
+ fpsSent: number;
8
+ kbPerSec: number;
9
+ avgFrameKB: number;
10
+ dropRate: number;
11
+ }
12
+ export declare function createThroughputSampler(): {
13
+ recordSent(bytes: number): void;
14
+ recordDropped(): void;
15
+ sample(): ThroughputSample;
16
+ };
17
+ //# sourceMappingURL=throughput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"throughput.d.ts","sourceRoot":"","sources":["../../src/utils/throughput.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAOD,wBAAgB,uBAAuB;sBAOjB,MAAM,GAAG,IAAI;qBAId,IAAI;cAGX,gBAAgB;EAuB7B"}
@@ -0,0 +1,42 @@
1
+ const round1 = (n) => Math.round(n * 10) / 10;
2
+ const round3 = (n) => Math.round(n * 1000) / 1000;
3
+ // Aggregates per-frame send/drop counts into a windowed throughput sample.
4
+ // Mirrors createResourceSampler: a stateful factory, reset on each sample().
5
+ export function createThroughputSampler() {
6
+ let sentFrames = 0;
7
+ let sentBytes = 0;
8
+ let droppedFrames = 0;
9
+ let windowStart = Date.now();
10
+ return {
11
+ recordSent(bytes) {
12
+ sentFrames++;
13
+ sentBytes += bytes;
14
+ },
15
+ recordDropped() {
16
+ droppedFrames++;
17
+ },
18
+ sample() {
19
+ const now = Date.now();
20
+ const elapsedMs = now - windowStart;
21
+ const elapsedSec = elapsedMs / 1000;
22
+ const producedFrames = sentFrames + droppedFrames;
23
+ const result = {
24
+ elapsedMs,
25
+ sentFrames,
26
+ droppedFrames,
27
+ producedFrames,
28
+ sentBytes,
29
+ fpsSent: elapsedSec > 0 ? round1(sentFrames / elapsedSec) : 0,
30
+ kbPerSec: elapsedSec > 0 ? round1(sentBytes / 1024 / elapsedSec) : 0,
31
+ avgFrameKB: sentFrames > 0 ? round1(sentBytes / 1024 / sentFrames) : 0,
32
+ dropRate: producedFrames > 0 ? round3(droppedFrames / producedFrames) : 0,
33
+ };
34
+ sentFrames = 0;
35
+ sentBytes = 0;
36
+ droppedFrames = 0;
37
+ windowStart = now;
38
+ return result;
39
+ },
40
+ };
41
+ }
42
+ //# sourceMappingURL=throughput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"throughput.js","sourceRoot":"","sources":["../../src/utils/throughput.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAA;AACrD,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;AAEzD,2EAA2E;AAC3E,6EAA6E;AAC7E,MAAM,UAAU,uBAAuB;IACrC,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,IAAI,aAAa,GAAG,CAAC,CAAA;IACrB,IAAI,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAE5B,OAAO;QACL,UAAU,CAAC,KAAa;YACtB,UAAU,EAAE,CAAA;YACZ,SAAS,IAAI,KAAK,CAAA;QACpB,CAAC;QACD,aAAa;YACX,aAAa,EAAE,CAAA;QACjB,CAAC;QACD,MAAM;YACJ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,MAAM,SAAS,GAAG,GAAG,GAAG,WAAW,CAAA;YACnC,MAAM,UAAU,GAAG,SAAS,GAAG,IAAI,CAAA;YACnC,MAAM,cAAc,GAAG,UAAU,GAAG,aAAa,CAAA;YACjD,MAAM,MAAM,GAAqB;gBAC/B,SAAS;gBACT,UAAU;gBACV,aAAa;gBACb,cAAc;gBACd,SAAS;gBACT,OAAO,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7D,QAAQ,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;gBACpE,UAAU,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtE,QAAQ,EAAE,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;aAC1E,CAAA;YACD,UAAU,GAAG,CAAC,CAAA;YACd,SAAS,GAAG,CAAC,CAAA;YACb,aAAa,GAAG,CAAC,CAAA;YACjB,WAAW,GAAG,GAAG,CAAA;YACjB,OAAO,MAAM,CAAA;QACf,CAAC;KACF,CAAA;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tapflowio/agent-core",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "DeviceAgent interface, AgentRegistry, and shared utilities for tapflow agents",
5
5
  "keywords": [
6
6
  "tapflow",