@omercnet/paseo-shared-browser 4.1.1-next.338.1 → 4.1.1-next.348.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-shared-browser",
3
- "version": "4.1.1-next.338.1",
3
+ "version": "4.1.1-next.348.1",
4
4
  "description": "One real Chromium session per Paseo workspace, shared live across every connected client.",
5
5
  "keywords": [
6
6
  "browser",
@@ -17,7 +17,7 @@ async function fixture(failFirstEncoder = false) {
17
17
  }),
18
18
  cancel: async () => deliver({ done: true }),
19
19
  };
20
- const pending: { timestamp: number; type: string }[] = [];
20
+ const pending: { timestamp: number; type: string; queued: boolean }[] = [];
21
21
  const encoded: { timestamp: number; keyFrame: boolean }[] = [];
22
22
  let encoder: Encoder;
23
23
  const instances: Encoder[] = [];
@@ -37,14 +37,31 @@ async function fixture(failFirstEncoder = false) {
37
37
  if (failFirstEncoder && this === instances[0]) throw new Error("Codec stopped");
38
38
  this.encodeQueueSize++;
39
39
  encoded.push({ timestamp: frame.timestamp, keyFrame: options.keyFrame });
40
- pending.push({ timestamp: frame.timestamp, type: options.keyFrame ? "key" : "delta" });
40
+ pending.push({
41
+ timestamp: frame.timestamp,
42
+ type: options.keyFrame ? "key" : "delta",
43
+ queued: true,
44
+ });
45
+ }
46
+ /** WebCodecs lowers encodeQueueSize once the codec starts a frame, before its output. */
47
+ dequeue() {
48
+ const value = pending.find((entry) => entry.queued);
49
+ if (!value) throw new Error("No queued fixture frame");
50
+ value.queued = false;
51
+ this.encodeQueueSize--;
41
52
  }
53
+ /** Emits in submission order, as an in-order realtime codec does. */
42
54
  output() {
43
55
  const value = pending.shift();
44
56
  if (!value) throw new Error("No queued fixture frame");
45
- this.encodeQueueSize--;
57
+ if (value.queued) this.encodeQueueSize--;
46
58
  this.callbacks.output(
47
- { ...value, byteLength: 10, copyTo: (bytes: Uint8Array) => bytes.fill(1) },
59
+ {
60
+ timestamp: value.timestamp,
61
+ type: value.type,
62
+ byteLength: 10,
63
+ copyTo: (bytes: Uint8Array) => bytes.fill(1),
64
+ },
48
65
  {},
49
66
  );
50
67
  }
@@ -94,6 +111,7 @@ async function fixture(failFirstEncoder = false) {
94
111
  await turn();
95
112
  },
96
113
  output: () => encoder!.output(),
114
+ dequeue: () => encoder!.dequeue(),
97
115
  ack: () =>
98
116
  runInContext(
99
117
  'for(const p of fixturePackets) acknowledge("stream",p.sequence)',
@@ -189,6 +207,28 @@ it("still suppresses dependencies after an actual encoded output is dropped", as
189
207
  }
190
208
  });
191
209
 
210
+ it("emits outputs from a codec that lags further behind than its dequeued queue size", async () => {
211
+ const f = await fixture();
212
+ try {
213
+ const timestamps = [0, 33334, 66668, 100002, 133336, 166670];
214
+ for (const timestamp of timestamps) {
215
+ await f.frame(timestamp);
216
+ f.dequeue();
217
+ }
218
+ expect(f.encoded).toHaveLength(timestamps.length);
219
+ for (const _ of timestamps) {
220
+ f.output();
221
+ f.ack();
222
+ }
223
+ expect(f.packets.map((p) => [p.timestampUs, p.type])).toEqual(
224
+ timestamps.map((timestamp, index) => [timestamp, index ? "delta" : "key"]),
225
+ );
226
+ expect(f.encoded.filter((e) => e.keyFrame)).toHaveLength(1);
227
+ } finally {
228
+ await f.stop();
229
+ }
230
+ });
231
+
192
232
  it("shipped packet conversion is exact with native and legacy byte APIs", () => {
193
233
  for (const native of [true, false]) {
194
234
  const context = createContext({
@@ -132,7 +132,12 @@ async function startCapture(targetId, width, height, generation) {
132
132
  continue;
133
133
  state.lastEncodedTimestampUs = frame.timestamp;
134
134
  state.frameGenerations.set(frame.timestamp, current.generation);
135
- while (state.frameGenerations.size > 4)
135
+ // encodeQueueSize drops when the codec dequeues a frame, not when it
136
+ // outputs, so a slow encode keeps more frames in flight than the gate
137
+ // above admits. Bound by one second at the 60fps ceiling, the host's
138
+ // packet age limit, so an in-flight output keeps its generation
139
+ // for as long as the host would still accept it.
140
+ while (state.frameGenerations.size > 60)
136
141
  state.frameGenerations.delete(state.frameGenerations.keys().next().value);
137
142
  const keyFrame =
138
143
  state.forceKey || frame.timestamp - state.lastKeyTimestamp >= 1000000;