@tapflowio/agent-core 0.4.0 → 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/dist/utils/envelope.d.ts +11 -1
- package/dist/utils/envelope.d.ts.map +1 -1
- package/dist/utils/envelope.js +20 -2
- package/dist/utils/envelope.js.map +1 -1
- package/dist/utils/index.d.ts +8 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/sps.d.ts +38 -0
- package/dist/utils/sps.d.ts.map +1 -0
- package/dist/utils/sps.js +333 -0
- package/dist/utils/sps.js.map +1 -0
- package/dist/utils/stream.d.ts +23 -1
- package/dist/utils/stream.d.ts.map +1 -1
- package/dist/utils/stream.js +45 -2
- package/dist/utils/stream.js.map +1 -1
- package/dist/utils/throughput.d.ts +17 -0
- package/dist/utils/throughput.d.ts.map +1 -0
- package/dist/utils/throughput.js +42 -0
- package/dist/utils/throughput.js.map +1 -0
- package/package.json +1 -1
package/dist/utils/envelope.d.ts
CHANGED
|
@@ -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
|
|
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;
|
|
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"}
|
package/dist/utils/envelope.js
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export { createResourceSampler } from './resources.js';
|
|
2
|
-
export { registerStreamWs, sendBinaryWithBackpressure, createRateLimitedDropWarn, DEFAULT_BACKPRESSURE_BYTES, } from './stream.js';
|
|
3
|
-
export {
|
|
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"}
|
package/dist/utils/index.js
CHANGED
|
@@ -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
|
package/dist/utils/index.js.map
CHANGED
|
@@ -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;
|
|
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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"}
|
package/dist/utils/stream.d.ts
CHANGED
|
@@ -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):
|
|
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;
|
|
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"}
|
package/dist/utils/stream.js
CHANGED
|
@@ -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.
|
package/dist/utils/stream.js.map
CHANGED
|
@@ -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,
|
|
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"}
|