@sarakusha/ebml 0.0.4 → 0.0.6

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/CHANGELOG.md CHANGED
@@ -2,4 +2,21 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.0.6](https://github.com/sarakusha/ebml/compare/v0.0.5...v0.0.6) (2026-06-25)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * не работал на смешанных (аудио+видео) данных ([c85d24f](https://github.com/sarakusha/ebml/commit/c85d24fa0424785af35849c430a57c238f2fd07e))
11
+ * parse video track by TrackNumber in VideoChunkGenerator ([ba83550](https://github.com/sarakusha/ebml/commit/ba8355026467ed8b3e1a2d5d29b68ff32bfc8b16))
12
+
13
+ ### [0.0.5](https://github.com/sarakusha/ebml/compare/v0.0.4...v0.0.5) (2026-05-26)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * keep video frame metadata during fade transforms ([3230d4d](https://github.com/sarakusha/ebml/commit/3230d4d635453a91155ba0e06f376cfc9c518539))
19
+ * preserve decoded frames through flush ([1cd3bd5](https://github.com/sarakusha/ebml/commit/1cd3bd521a98456e5be29afb201c2ee2f30e6aa8))
20
+ * tests ([e03e665](https://github.com/sarakusha/ebml/commit/e03e6654ffdaac596afa85711ef13c5851e27d52))
21
+
5
22
  ### 0.0.4 (2026-05-25)
@@ -4,27 +4,34 @@ Object.defineProperties(exports, {
4
4
  });
5
5
  const require_Deferred = require("./Deferred-CB69mtbF.cjs");
6
6
  //#region src/FadeTransform.ts
7
- const easeOutCubic = (t, b, c, d) => c * ((t = t / d - 1) * t * t + 1) + b;
7
+ const easeOutCubic = (t, b, c, d) => {
8
+ const normalized = t / d - 1;
9
+ return c * (normalized * normalized * normalized + 1) + b;
10
+ };
8
11
  var FadeTransform = class extends TransformStream {
9
12
  setDisableFadeOut;
13
+ setDisableFadeIn;
10
14
  constructor({ disableIn, disableOut, duration: fadeDuration = 200 } = {}) {
11
15
  let deferred;
12
16
  let last;
13
17
  let ctx = null;
14
- let init = {};
18
+ let init;
19
+ let disableFadeIn = disableIn;
15
20
  let disableFadeOut = disableOut;
16
21
  const initialize = (chunk) => {
17
- if (!ctx) ctx = new OffscreenCanvas(chunk.displayWidth, chunk.displayHeight).getContext("2d");
18
- if (!init) {
19
- const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;
20
- init = {
21
- displayWidth,
22
- displayHeight,
23
- visibleRect: visibleRect ?? void 0,
24
- duration: duration ?? void 0,
25
- timestamp: timestamp ?? void 0
26
- };
22
+ if (!ctx) {
23
+ const width = chunk.displayWidth || chunk.codedWidth;
24
+ const height = chunk.displayHeight || chunk.codedHeight;
25
+ ctx = new OffscreenCanvas(width, height).getContext("2d");
27
26
  }
27
+ const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;
28
+ init = {
29
+ displayWidth,
30
+ displayHeight,
31
+ visibleRect: visibleRect ?? void 0,
32
+ duration: duration ?? void 0,
33
+ timestamp: timestamp ?? 0
34
+ };
28
35
  };
29
36
  super({
30
37
  transform(chunk, controller) {
@@ -32,16 +39,18 @@ var FadeTransform = class extends TransformStream {
32
39
  last?.close();
33
40
  last = chunk.clone();
34
41
  }
35
- const timestamp = chunk.timestamp ? chunk.timestamp : 0;
36
- if (!disableIn && timestamp / 1e3 < fadeDuration) {
37
- if (!ctx) initialize(chunk);
38
- if (ctx) {
42
+ const timestamp = chunk.timestamp ?? 0;
43
+ if (!disableFadeIn && timestamp / 1e3 < fadeDuration) {
44
+ if (!ctx || !init) initialize(chunk);
45
+ if (ctx && init) {
39
46
  const brightness = easeOutCubic(timestamp / 1e3, 0, 100, fadeDuration);
40
47
  ctx.filter = `brightness(${brightness}%)`;
41
48
  ctx.drawImage(chunk, 0, 0);
42
49
  chunk.close();
43
- init.timestamp = timestamp;
44
- const frame = new VideoFrame(ctx.canvas, init);
50
+ const frame = new VideoFrame(ctx.canvas, {
51
+ ...init,
52
+ timestamp
53
+ });
45
54
  controller.enqueue(frame);
46
55
  return;
47
56
  }
@@ -50,8 +59,8 @@ var FadeTransform = class extends TransformStream {
50
59
  },
51
60
  flush: async (controller) => {
52
61
  if (last && last.timestamp && !disableFadeOut) {
53
- if (!ctx) initialize(last);
54
- if (ctx) {
62
+ if (!ctx || !init) initialize(last);
63
+ if (ctx && init) {
55
64
  deferred = new require_Deferred.Deferred();
56
65
  const context = ctx;
57
66
  const orig = last;
@@ -60,8 +69,10 @@ var FadeTransform = class extends TransformStream {
60
69
  const fadeOut = (time) => {
61
70
  context.filter = `brightness(${easeOutCubic(Math.max(0, fadeDuration - time), 0, 100, fadeDuration)}%)`;
62
71
  context.drawImage(orig, 0, 0);
63
- init.timestamp = start + time * 1e3;
64
- const frame = new VideoFrame(context.canvas, init);
72
+ const frame = new VideoFrame(context.canvas, {
73
+ ...init,
74
+ timestamp: start + time * 1e3
75
+ });
65
76
  controller.enqueue(frame);
66
77
  const next = time + duration;
67
78
  if (next > fadeDuration) {
@@ -82,6 +93,9 @@ var FadeTransform = class extends TransformStream {
82
93
  last = void 0;
83
94
  }
84
95
  };
96
+ this.setDisableFadeIn = (value) => {
97
+ disableFadeIn = value;
98
+ };
85
99
  }
86
100
  };
87
101
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"FadeTransform.cjs","names":["Deferred"],"sources":["../src/FadeTransform.ts"],"sourcesContent":["import Deferred from './Deferred';\n\nexport type FadeOptions = {\n disableIn?: boolean;\n disableOut?: boolean;\n duration?: number;\n};\n\nconst easeOutCubic = (t: number, b: number, c: number, d: number) =>\n // eslint-disable-next-line no-param-reassign\n c * ((t = t / d - 1) * t * t + 1) + b;\n\nexport default class FadeTransform extends TransformStream<VideoFrame, VideoFrame> {\n setDisableFadeOut: (value?: boolean) => void;\n\n constructor({ disableIn, disableOut, duration: fadeDuration = 200 }: FadeOptions = {}) {\n let deferred: Deferred | undefined;\n let last: VideoFrame | undefined;\n let ctx: OffscreenCanvasRenderingContext2D | null = null;\n let init: VideoFrameInit = {};\n let disableFadeOut = disableOut;\n const initialize = (chunk: VideoFrame): void => {\n if (!ctx) {\n const offscreen = new OffscreenCanvas(chunk.displayWidth, chunk.displayHeight);\n ctx = offscreen.getContext('2d');\n }\n if (!init) {\n const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;\n init = {\n displayWidth,\n displayHeight,\n visibleRect: visibleRect ?? undefined,\n duration: duration ?? undefined,\n timestamp: timestamp ?? undefined,\n };\n }\n };\n super({\n transform(chunk, controller) {\n if (!disableFadeOut) {\n last?.close();\n last = chunk.clone();\n }\n const timestamp = chunk.timestamp ? chunk.timestamp : 0;\n if (!disableIn && timestamp / 1000 < fadeDuration) {\n if (!ctx) initialize(chunk);\n if (ctx) {\n const brightness = easeOutCubic(timestamp / 1000, 0, 100, fadeDuration);\n ctx.filter = `brightness(${brightness}%)`;\n ctx.drawImage(chunk, 0, 0);\n chunk.close();\n init.timestamp = timestamp;\n const frame = new VideoFrame(ctx.canvas as unknown as ImageBitmap, init);\n controller.enqueue(frame);\n return;\n }\n }\n controller.enqueue(chunk);\n },\n flush: async (controller) => {\n if (last && last.timestamp && !disableFadeOut) {\n if (!ctx) initialize(last);\n if (ctx) {\n deferred = new Deferred();\n const context = ctx;\n const orig = last;\n const { timestamp: start } = last;\n const duration = last.duration ? last.duration / 1000 : 33;\n const fadeOut = (time: number) => {\n const brightness = easeOutCubic(\n Math.max(0, fadeDuration - time),\n 0,\n 100,\n fadeDuration,\n );\n context.filter = `brightness(${brightness}%)`;\n context.drawImage(orig, 0, 0);\n init.timestamp = start + time * 1000;\n const frame = new VideoFrame(context.canvas as unknown as ImageBitmap, init);\n controller.enqueue(frame);\n const next = time + duration;\n if (next > fadeDuration) {\n orig.close();\n deferred?.resolve();\n } else setTimeout(() => fadeOut(next), 0);\n };\n fadeOut(duration);\n }\n }\n await deferred?.promise;\n },\n });\n this.setDisableFadeOut = (value?: boolean) => {\n disableFadeOut = value;\n if (disableFadeOut) {\n last?.close();\n last = undefined;\n }\n };\n }\n}\n"],"mappings":";;;;;;AAQA,MAAM,gBAAgB,GAAW,GAAW,GAAW,MAErD,MAAM,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK;AAEtC,IAAqB,gBAArB,cAA2C,gBAAwC;CACjF;CAEA,YAAY,EAAE,WAAW,YAAY,UAAU,eAAe,QAAqB,CAAC,GAAG;EACrF,IAAI;EACJ,IAAI;EACJ,IAAI,MAAgD;EACpD,IAAI,OAAuB,CAAC;EAC5B,IAAI,iBAAiB;EACrB,MAAM,cAAc,UAA4B;GAC9C,IAAI,CAAC,KAEH,MAAM,IADgB,gBAAgB,MAAM,cAAc,MAAM,aAClD,EAAE,WAAW,IAAI;GAEjC,IAAI,CAAC,MAAM;IACT,MAAM,EAAE,cAAc,eAAe,aAAa,UAAU,cAAc;IAC1E,OAAO;KACL;KACA;KACA,aAAa,eAAe,KAAA;KAC5B,UAAU,YAAY,KAAA;KACtB,WAAW,aAAa,KAAA;IAC1B;GACF;EACF;EACA,MAAM;GACJ,UAAU,OAAO,YAAY;IAC3B,IAAI,CAAC,gBAAgB;KACnB,MAAM,MAAM;KACZ,OAAO,MAAM,MAAM;IACrB;IACA,MAAM,YAAY,MAAM,YAAY,MAAM,YAAY;IACtD,IAAI,CAAC,aAAa,YAAY,MAAO,cAAc;KACjD,IAAI,CAAC,KAAK,WAAW,KAAK;KAC1B,IAAI,KAAK;MACP,MAAM,aAAa,aAAa,YAAY,KAAM,GAAG,KAAK,YAAY;MACtE,IAAI,SAAS,cAAc,WAAW;MACtC,IAAI,UAAU,OAAO,GAAG,CAAC;MACzB,MAAM,MAAM;MACZ,KAAK,YAAY;MACjB,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAkC,IAAI;MACvE,WAAW,QAAQ,KAAK;MACxB;KACF;IACF;IACA,WAAW,QAAQ,KAAK;GAC1B;GACA,OAAO,OAAO,eAAe;IAC3B,IAAI,QAAQ,KAAK,aAAa,CAAC,gBAAgB;KAC7C,IAAI,CAAC,KAAK,WAAW,IAAI;KACzB,IAAI,KAAK;MACP,WAAW,IAAIA,iBAAAA,SAAS;MACxB,MAAM,UAAU;MAChB,MAAM,OAAO;MACb,MAAM,EAAE,WAAW,UAAU;MAC7B,MAAM,WAAW,KAAK,WAAW,KAAK,WAAW,MAAO;MACxD,MAAM,WAAW,SAAiB;OAOhC,QAAQ,SAAS,cANE,aACjB,KAAK,IAAI,GAAG,eAAe,IAAI,GAC/B,GACA,KACA,YAEsC,EAAE;OAC1C,QAAQ,UAAU,MAAM,GAAG,CAAC;OAC5B,KAAK,YAAY,QAAQ,OAAO;OAChC,MAAM,QAAQ,IAAI,WAAW,QAAQ,QAAkC,IAAI;OAC3E,WAAW,QAAQ,KAAK;OACxB,MAAM,OAAO,OAAO;OACpB,IAAI,OAAO,cAAc;QACvB,KAAK,MAAM;QACX,UAAU,QAAQ;OACpB,OAAO,iBAAiB,QAAQ,IAAI,GAAG,CAAC;MAC1C;MACA,QAAQ,QAAQ;KAClB;IACF;IACA,MAAM,UAAU;GAClB;EACF,CAAC;EACD,KAAK,qBAAqB,UAAoB;GAC5C,iBAAiB;GACjB,IAAI,gBAAgB;IAClB,MAAM,MAAM;IACZ,OAAO,KAAA;GACT;EACF;CACF;AACF"}
1
+ {"version":3,"file":"FadeTransform.cjs","names":["Deferred"],"sources":["../src/FadeTransform.ts"],"sourcesContent":["import Deferred from './Deferred';\n\nexport type FadeOptions = {\n disableIn?: boolean;\n disableOut?: boolean;\n duration?: number;\n};\n\nconst easeOutCubic = (t: number, b: number, c: number, d: number) => {\n const normalized = t / d - 1;\n return c * (normalized * normalized * normalized + 1) + b;\n};\n\nexport default class FadeTransform extends TransformStream<VideoFrame, VideoFrame> {\n setDisableFadeOut: (value?: boolean) => void;\n\n setDisableFadeIn: (value?: boolean) => void;\n\n constructor({ disableIn, disableOut, duration: fadeDuration = 200 }: FadeOptions = {}) {\n let deferred: Deferred | undefined;\n let last: VideoFrame | undefined;\n let ctx: OffscreenCanvasRenderingContext2D | null = null;\n let init: VideoFrameInit | undefined;\n let disableFadeIn = disableIn;\n let disableFadeOut = disableOut;\n const initialize = (chunk: VideoFrame): void => {\n if (!ctx) {\n const width = chunk.displayWidth || chunk.codedWidth;\n const height = chunk.displayHeight || chunk.codedHeight;\n const offscreen = new OffscreenCanvas(width, height);\n ctx = offscreen.getContext('2d');\n }\n const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;\n init = {\n displayWidth,\n displayHeight,\n visibleRect: visibleRect ?? undefined,\n duration: duration ?? undefined,\n timestamp: timestamp ?? 0,\n };\n };\n super({\n transform(chunk, controller) {\n if (!disableFadeOut) {\n last?.close();\n last = chunk.clone();\n }\n const timestamp = chunk.timestamp ?? 0;\n if (!disableFadeIn && timestamp / 1000 < fadeDuration) {\n if (!ctx || !init) initialize(chunk);\n if (ctx && init) {\n const brightness = easeOutCubic(timestamp / 1000, 0, 100, fadeDuration);\n ctx.filter = `brightness(${brightness}%)`;\n ctx.drawImage(chunk, 0, 0);\n chunk.close();\n const frame = new VideoFrame(ctx.canvas as unknown as ImageBitmap, {\n ...init,\n timestamp,\n });\n controller.enqueue(frame);\n return;\n }\n }\n controller.enqueue(chunk);\n },\n flush: async (controller) => {\n if (last && last.timestamp && !disableFadeOut) {\n if (!ctx || !init) initialize(last);\n if (ctx && init) {\n deferred = new Deferred();\n const context = ctx;\n const orig = last;\n const { timestamp: start } = last;\n const duration = last.duration ? last.duration / 1000 : 33;\n const fadeOut = (time: number) => {\n const brightness = easeOutCubic(\n Math.max(0, fadeDuration - time),\n 0,\n 100,\n fadeDuration,\n );\n context.filter = `brightness(${brightness}%)`;\n context.drawImage(orig, 0, 0);\n const frame = new VideoFrame(context.canvas as unknown as ImageBitmap, {\n ...init,\n timestamp: start + time * 1000,\n });\n controller.enqueue(frame);\n const next = time + duration;\n if (next > fadeDuration) {\n orig.close();\n deferred?.resolve();\n } else setTimeout(() => fadeOut(next), 0);\n };\n fadeOut(duration);\n }\n }\n await deferred?.promise;\n },\n });\n this.setDisableFadeOut = (value?: boolean) => {\n disableFadeOut = value;\n if (disableFadeOut) {\n last?.close();\n last = undefined;\n }\n };\n this.setDisableFadeIn = (value?: boolean) => {\n disableFadeIn = value;\n };\n }\n}\n"],"mappings":";;;;;;AAQA,MAAM,gBAAgB,GAAW,GAAW,GAAW,MAAc;CACnE,MAAM,aAAa,IAAI,IAAI;CAC3B,OAAO,KAAK,aAAa,aAAa,aAAa,KAAK;AAC1D;AAEA,IAAqB,gBAArB,cAA2C,gBAAwC;CACjF;CAEA;CAEA,YAAY,EAAE,WAAW,YAAY,UAAU,eAAe,QAAqB,CAAC,GAAG;EACrF,IAAI;EACJ,IAAI;EACJ,IAAI,MAAgD;EACpD,IAAI;EACJ,IAAI,gBAAgB;EACpB,IAAI,iBAAiB;EACrB,MAAM,cAAc,UAA4B;GAC9C,IAAI,CAAC,KAAK;IACR,MAAM,QAAQ,MAAM,gBAAgB,MAAM;IAC1C,MAAM,SAAS,MAAM,iBAAiB,MAAM;IAE5C,MAAM,IADgB,gBAAgB,OAAO,MAC/B,EAAE,WAAW,IAAI;GACjC;GACA,MAAM,EAAE,cAAc,eAAe,aAAa,UAAU,cAAc;GAC1E,OAAO;IACL;IACA;IACA,aAAa,eAAe,KAAA;IAC5B,UAAU,YAAY,KAAA;IACtB,WAAW,aAAa;GAC1B;EACF;EACA,MAAM;GACJ,UAAU,OAAO,YAAY;IAC3B,IAAI,CAAC,gBAAgB;KACnB,MAAM,MAAM;KACZ,OAAO,MAAM,MAAM;IACrB;IACA,MAAM,YAAY,MAAM,aAAa;IACrC,IAAI,CAAC,iBAAiB,YAAY,MAAO,cAAc;KACrD,IAAI,CAAC,OAAO,CAAC,MAAM,WAAW,KAAK;KACnC,IAAI,OAAO,MAAM;MACf,MAAM,aAAa,aAAa,YAAY,KAAM,GAAG,KAAK,YAAY;MACtE,IAAI,SAAS,cAAc,WAAW;MACtC,IAAI,UAAU,OAAO,GAAG,CAAC;MACzB,MAAM,MAAM;MACZ,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAkC;OACjE,GAAG;OACH;MACF,CAAC;MACD,WAAW,QAAQ,KAAK;MACxB;KACF;IACF;IACA,WAAW,QAAQ,KAAK;GAC1B;GACA,OAAO,OAAO,eAAe;IAC3B,IAAI,QAAQ,KAAK,aAAa,CAAC,gBAAgB;KAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,WAAW,IAAI;KAClC,IAAI,OAAO,MAAM;MACf,WAAW,IAAIA,iBAAAA,SAAS;MACxB,MAAM,UAAU;MAChB,MAAM,OAAO;MACb,MAAM,EAAE,WAAW,UAAU;MAC7B,MAAM,WAAW,KAAK,WAAW,KAAK,WAAW,MAAO;MACxD,MAAM,WAAW,SAAiB;OAOhC,QAAQ,SAAS,cANE,aACjB,KAAK,IAAI,GAAG,eAAe,IAAI,GAC/B,GACA,KACA,YAEsC,EAAE;OAC1C,QAAQ,UAAU,MAAM,GAAG,CAAC;OAC5B,MAAM,QAAQ,IAAI,WAAW,QAAQ,QAAkC;QACrE,GAAG;QACH,WAAW,QAAQ,OAAO;OAC5B,CAAC;OACD,WAAW,QAAQ,KAAK;OACxB,MAAM,OAAO,OAAO;OACpB,IAAI,OAAO,cAAc;QACvB,KAAK,MAAM;QACX,UAAU,QAAQ;OACpB,OAAO,iBAAiB,QAAQ,IAAI,GAAG,CAAC;MAC1C;MACA,QAAQ,QAAQ;KAClB;IACF;IACA,MAAM,UAAU;GAClB;EACF,CAAC;EACD,KAAK,qBAAqB,UAAoB;GAC5C,iBAAiB;GACjB,IAAI,gBAAgB;IAClB,MAAM,MAAM;IACZ,OAAO,KAAA;GACT;EACF;EACA,KAAK,oBAAoB,UAAoB;GAC3C,gBAAgB;EAClB;CACF;AACF"}
@@ -6,6 +6,7 @@ type FadeOptions = {
6
6
  };
7
7
  declare class FadeTransform extends TransformStream<VideoFrame, VideoFrame> {
8
8
  setDisableFadeOut: (value?: boolean) => void;
9
+ setDisableFadeIn: (value?: boolean) => void;
9
10
  constructor({
10
11
  disableIn,
11
12
  disableOut,
@@ -1 +1 @@
1
- {"version":3,"file":"FadeTransform.d.mts","names":[],"sources":["../src/FadeTransform.ts"],"mappings":";KAEY,WAAA;EACV,SAAA;EACA,UAAA;EACA,QAAA;AAAA;AAAA,cAOmB,aAAA,SAAsB,eAAA,CAAgB,UAAA,EAAY,UAAA;EACrE,iBAAA,GAAoB,KAAA;;IAEN,SAAA;IAAW,UAAA;IAAY,QAAA,EAAU;EAAA,IAAsB,WAAA;AAAA"}
1
+ {"version":3,"file":"FadeTransform.d.mts","names":[],"sources":["../src/FadeTransform.ts"],"mappings":";KAEY,WAAA;EACV,SAAA;EACA,UAAA;EACA,QAAA;AAAA;AAAA,cAQmB,aAAA,SAAsB,eAAA,CAAgB,UAAA,EAAY,UAAA;EACrE,iBAAA,GAAoB,KAAA;EAEpB,gBAAA,GAAmB,KAAA;;IAEL,SAAA;IAAW,UAAA;IAAY,QAAA,EAAU;EAAA,IAAsB,WAAA;AAAA"}
@@ -1,26 +1,33 @@
1
1
  import { t as Deferred } from "./Deferred-7Cu0KIje.mjs";
2
2
  //#region src/FadeTransform.ts
3
- const easeOutCubic = (t, b, c, d) => c * ((t = t / d - 1) * t * t + 1) + b;
3
+ const easeOutCubic = (t, b, c, d) => {
4
+ const normalized = t / d - 1;
5
+ return c * (normalized * normalized * normalized + 1) + b;
6
+ };
4
7
  var FadeTransform = class extends TransformStream {
5
8
  setDisableFadeOut;
9
+ setDisableFadeIn;
6
10
  constructor({ disableIn, disableOut, duration: fadeDuration = 200 } = {}) {
7
11
  let deferred;
8
12
  let last;
9
13
  let ctx = null;
10
- let init = {};
14
+ let init;
15
+ let disableFadeIn = disableIn;
11
16
  let disableFadeOut = disableOut;
12
17
  const initialize = (chunk) => {
13
- if (!ctx) ctx = new OffscreenCanvas(chunk.displayWidth, chunk.displayHeight).getContext("2d");
14
- if (!init) {
15
- const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;
16
- init = {
17
- displayWidth,
18
- displayHeight,
19
- visibleRect: visibleRect ?? void 0,
20
- duration: duration ?? void 0,
21
- timestamp: timestamp ?? void 0
22
- };
18
+ if (!ctx) {
19
+ const width = chunk.displayWidth || chunk.codedWidth;
20
+ const height = chunk.displayHeight || chunk.codedHeight;
21
+ ctx = new OffscreenCanvas(width, height).getContext("2d");
23
22
  }
23
+ const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;
24
+ init = {
25
+ displayWidth,
26
+ displayHeight,
27
+ visibleRect: visibleRect ?? void 0,
28
+ duration: duration ?? void 0,
29
+ timestamp: timestamp ?? 0
30
+ };
24
31
  };
25
32
  super({
26
33
  transform(chunk, controller) {
@@ -28,16 +35,18 @@ var FadeTransform = class extends TransformStream {
28
35
  last?.close();
29
36
  last = chunk.clone();
30
37
  }
31
- const timestamp = chunk.timestamp ? chunk.timestamp : 0;
32
- if (!disableIn && timestamp / 1e3 < fadeDuration) {
33
- if (!ctx) initialize(chunk);
34
- if (ctx) {
38
+ const timestamp = chunk.timestamp ?? 0;
39
+ if (!disableFadeIn && timestamp / 1e3 < fadeDuration) {
40
+ if (!ctx || !init) initialize(chunk);
41
+ if (ctx && init) {
35
42
  const brightness = easeOutCubic(timestamp / 1e3, 0, 100, fadeDuration);
36
43
  ctx.filter = `brightness(${brightness}%)`;
37
44
  ctx.drawImage(chunk, 0, 0);
38
45
  chunk.close();
39
- init.timestamp = timestamp;
40
- const frame = new VideoFrame(ctx.canvas, init);
46
+ const frame = new VideoFrame(ctx.canvas, {
47
+ ...init,
48
+ timestamp
49
+ });
41
50
  controller.enqueue(frame);
42
51
  return;
43
52
  }
@@ -46,8 +55,8 @@ var FadeTransform = class extends TransformStream {
46
55
  },
47
56
  flush: async (controller) => {
48
57
  if (last && last.timestamp && !disableFadeOut) {
49
- if (!ctx) initialize(last);
50
- if (ctx) {
58
+ if (!ctx || !init) initialize(last);
59
+ if (ctx && init) {
51
60
  deferred = new Deferred();
52
61
  const context = ctx;
53
62
  const orig = last;
@@ -56,8 +65,10 @@ var FadeTransform = class extends TransformStream {
56
65
  const fadeOut = (time) => {
57
66
  context.filter = `brightness(${easeOutCubic(Math.max(0, fadeDuration - time), 0, 100, fadeDuration)}%)`;
58
67
  context.drawImage(orig, 0, 0);
59
- init.timestamp = start + time * 1e3;
60
- const frame = new VideoFrame(context.canvas, init);
68
+ const frame = new VideoFrame(context.canvas, {
69
+ ...init,
70
+ timestamp: start + time * 1e3
71
+ });
61
72
  controller.enqueue(frame);
62
73
  const next = time + duration;
63
74
  if (next > fadeDuration) {
@@ -78,6 +89,9 @@ var FadeTransform = class extends TransformStream {
78
89
  last = void 0;
79
90
  }
80
91
  };
92
+ this.setDisableFadeIn = (value) => {
93
+ disableFadeIn = value;
94
+ };
81
95
  }
82
96
  };
83
97
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"FadeTransform.mjs","names":[],"sources":["../src/FadeTransform.ts"],"sourcesContent":["import Deferred from './Deferred';\n\nexport type FadeOptions = {\n disableIn?: boolean;\n disableOut?: boolean;\n duration?: number;\n};\n\nconst easeOutCubic = (t: number, b: number, c: number, d: number) =>\n // eslint-disable-next-line no-param-reassign\n c * ((t = t / d - 1) * t * t + 1) + b;\n\nexport default class FadeTransform extends TransformStream<VideoFrame, VideoFrame> {\n setDisableFadeOut: (value?: boolean) => void;\n\n constructor({ disableIn, disableOut, duration: fadeDuration = 200 }: FadeOptions = {}) {\n let deferred: Deferred | undefined;\n let last: VideoFrame | undefined;\n let ctx: OffscreenCanvasRenderingContext2D | null = null;\n let init: VideoFrameInit = {};\n let disableFadeOut = disableOut;\n const initialize = (chunk: VideoFrame): void => {\n if (!ctx) {\n const offscreen = new OffscreenCanvas(chunk.displayWidth, chunk.displayHeight);\n ctx = offscreen.getContext('2d');\n }\n if (!init) {\n const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;\n init = {\n displayWidth,\n displayHeight,\n visibleRect: visibleRect ?? undefined,\n duration: duration ?? undefined,\n timestamp: timestamp ?? undefined,\n };\n }\n };\n super({\n transform(chunk, controller) {\n if (!disableFadeOut) {\n last?.close();\n last = chunk.clone();\n }\n const timestamp = chunk.timestamp ? chunk.timestamp : 0;\n if (!disableIn && timestamp / 1000 < fadeDuration) {\n if (!ctx) initialize(chunk);\n if (ctx) {\n const brightness = easeOutCubic(timestamp / 1000, 0, 100, fadeDuration);\n ctx.filter = `brightness(${brightness}%)`;\n ctx.drawImage(chunk, 0, 0);\n chunk.close();\n init.timestamp = timestamp;\n const frame = new VideoFrame(ctx.canvas as unknown as ImageBitmap, init);\n controller.enqueue(frame);\n return;\n }\n }\n controller.enqueue(chunk);\n },\n flush: async (controller) => {\n if (last && last.timestamp && !disableFadeOut) {\n if (!ctx) initialize(last);\n if (ctx) {\n deferred = new Deferred();\n const context = ctx;\n const orig = last;\n const { timestamp: start } = last;\n const duration = last.duration ? last.duration / 1000 : 33;\n const fadeOut = (time: number) => {\n const brightness = easeOutCubic(\n Math.max(0, fadeDuration - time),\n 0,\n 100,\n fadeDuration,\n );\n context.filter = `brightness(${brightness}%)`;\n context.drawImage(orig, 0, 0);\n init.timestamp = start + time * 1000;\n const frame = new VideoFrame(context.canvas as unknown as ImageBitmap, init);\n controller.enqueue(frame);\n const next = time + duration;\n if (next > fadeDuration) {\n orig.close();\n deferred?.resolve();\n } else setTimeout(() => fadeOut(next), 0);\n };\n fadeOut(duration);\n }\n }\n await deferred?.promise;\n },\n });\n this.setDisableFadeOut = (value?: boolean) => {\n disableFadeOut = value;\n if (disableFadeOut) {\n last?.close();\n last = undefined;\n }\n };\n }\n}\n"],"mappings":";;AAQA,MAAM,gBAAgB,GAAW,GAAW,GAAW,MAErD,MAAM,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK;AAEtC,IAAqB,gBAArB,cAA2C,gBAAwC;CACjF;CAEA,YAAY,EAAE,WAAW,YAAY,UAAU,eAAe,QAAqB,CAAC,GAAG;EACrF,IAAI;EACJ,IAAI;EACJ,IAAI,MAAgD;EACpD,IAAI,OAAuB,CAAC;EAC5B,IAAI,iBAAiB;EACrB,MAAM,cAAc,UAA4B;GAC9C,IAAI,CAAC,KAEH,MAAM,IADgB,gBAAgB,MAAM,cAAc,MAAM,aAClD,EAAE,WAAW,IAAI;GAEjC,IAAI,CAAC,MAAM;IACT,MAAM,EAAE,cAAc,eAAe,aAAa,UAAU,cAAc;IAC1E,OAAO;KACL;KACA;KACA,aAAa,eAAe,KAAA;KAC5B,UAAU,YAAY,KAAA;KACtB,WAAW,aAAa,KAAA;IAC1B;GACF;EACF;EACA,MAAM;GACJ,UAAU,OAAO,YAAY;IAC3B,IAAI,CAAC,gBAAgB;KACnB,MAAM,MAAM;KACZ,OAAO,MAAM,MAAM;IACrB;IACA,MAAM,YAAY,MAAM,YAAY,MAAM,YAAY;IACtD,IAAI,CAAC,aAAa,YAAY,MAAO,cAAc;KACjD,IAAI,CAAC,KAAK,WAAW,KAAK;KAC1B,IAAI,KAAK;MACP,MAAM,aAAa,aAAa,YAAY,KAAM,GAAG,KAAK,YAAY;MACtE,IAAI,SAAS,cAAc,WAAW;MACtC,IAAI,UAAU,OAAO,GAAG,CAAC;MACzB,MAAM,MAAM;MACZ,KAAK,YAAY;MACjB,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAkC,IAAI;MACvE,WAAW,QAAQ,KAAK;MACxB;KACF;IACF;IACA,WAAW,QAAQ,KAAK;GAC1B;GACA,OAAO,OAAO,eAAe;IAC3B,IAAI,QAAQ,KAAK,aAAa,CAAC,gBAAgB;KAC7C,IAAI,CAAC,KAAK,WAAW,IAAI;KACzB,IAAI,KAAK;MACP,WAAW,IAAI,SAAS;MACxB,MAAM,UAAU;MAChB,MAAM,OAAO;MACb,MAAM,EAAE,WAAW,UAAU;MAC7B,MAAM,WAAW,KAAK,WAAW,KAAK,WAAW,MAAO;MACxD,MAAM,WAAW,SAAiB;OAOhC,QAAQ,SAAS,cANE,aACjB,KAAK,IAAI,GAAG,eAAe,IAAI,GAC/B,GACA,KACA,YAEsC,EAAE;OAC1C,QAAQ,UAAU,MAAM,GAAG,CAAC;OAC5B,KAAK,YAAY,QAAQ,OAAO;OAChC,MAAM,QAAQ,IAAI,WAAW,QAAQ,QAAkC,IAAI;OAC3E,WAAW,QAAQ,KAAK;OACxB,MAAM,OAAO,OAAO;OACpB,IAAI,OAAO,cAAc;QACvB,KAAK,MAAM;QACX,UAAU,QAAQ;OACpB,OAAO,iBAAiB,QAAQ,IAAI,GAAG,CAAC;MAC1C;MACA,QAAQ,QAAQ;KAClB;IACF;IACA,MAAM,UAAU;GAClB;EACF,CAAC;EACD,KAAK,qBAAqB,UAAoB;GAC5C,iBAAiB;GACjB,IAAI,gBAAgB;IAClB,MAAM,MAAM;IACZ,OAAO,KAAA;GACT;EACF;CACF;AACF"}
1
+ {"version":3,"file":"FadeTransform.mjs","names":[],"sources":["../src/FadeTransform.ts"],"sourcesContent":["import Deferred from './Deferred';\n\nexport type FadeOptions = {\n disableIn?: boolean;\n disableOut?: boolean;\n duration?: number;\n};\n\nconst easeOutCubic = (t: number, b: number, c: number, d: number) => {\n const normalized = t / d - 1;\n return c * (normalized * normalized * normalized + 1) + b;\n};\n\nexport default class FadeTransform extends TransformStream<VideoFrame, VideoFrame> {\n setDisableFadeOut: (value?: boolean) => void;\n\n setDisableFadeIn: (value?: boolean) => void;\n\n constructor({ disableIn, disableOut, duration: fadeDuration = 200 }: FadeOptions = {}) {\n let deferred: Deferred | undefined;\n let last: VideoFrame | undefined;\n let ctx: OffscreenCanvasRenderingContext2D | null = null;\n let init: VideoFrameInit | undefined;\n let disableFadeIn = disableIn;\n let disableFadeOut = disableOut;\n const initialize = (chunk: VideoFrame): void => {\n if (!ctx) {\n const width = chunk.displayWidth || chunk.codedWidth;\n const height = chunk.displayHeight || chunk.codedHeight;\n const offscreen = new OffscreenCanvas(width, height);\n ctx = offscreen.getContext('2d');\n }\n const { displayWidth, displayHeight, visibleRect, duration, timestamp } = chunk;\n init = {\n displayWidth,\n displayHeight,\n visibleRect: visibleRect ?? undefined,\n duration: duration ?? undefined,\n timestamp: timestamp ?? 0,\n };\n };\n super({\n transform(chunk, controller) {\n if (!disableFadeOut) {\n last?.close();\n last = chunk.clone();\n }\n const timestamp = chunk.timestamp ?? 0;\n if (!disableFadeIn && timestamp / 1000 < fadeDuration) {\n if (!ctx || !init) initialize(chunk);\n if (ctx && init) {\n const brightness = easeOutCubic(timestamp / 1000, 0, 100, fadeDuration);\n ctx.filter = `brightness(${brightness}%)`;\n ctx.drawImage(chunk, 0, 0);\n chunk.close();\n const frame = new VideoFrame(ctx.canvas as unknown as ImageBitmap, {\n ...init,\n timestamp,\n });\n controller.enqueue(frame);\n return;\n }\n }\n controller.enqueue(chunk);\n },\n flush: async (controller) => {\n if (last && last.timestamp && !disableFadeOut) {\n if (!ctx || !init) initialize(last);\n if (ctx && init) {\n deferred = new Deferred();\n const context = ctx;\n const orig = last;\n const { timestamp: start } = last;\n const duration = last.duration ? last.duration / 1000 : 33;\n const fadeOut = (time: number) => {\n const brightness = easeOutCubic(\n Math.max(0, fadeDuration - time),\n 0,\n 100,\n fadeDuration,\n );\n context.filter = `brightness(${brightness}%)`;\n context.drawImage(orig, 0, 0);\n const frame = new VideoFrame(context.canvas as unknown as ImageBitmap, {\n ...init,\n timestamp: start + time * 1000,\n });\n controller.enqueue(frame);\n const next = time + duration;\n if (next > fadeDuration) {\n orig.close();\n deferred?.resolve();\n } else setTimeout(() => fadeOut(next), 0);\n };\n fadeOut(duration);\n }\n }\n await deferred?.promise;\n },\n });\n this.setDisableFadeOut = (value?: boolean) => {\n disableFadeOut = value;\n if (disableFadeOut) {\n last?.close();\n last = undefined;\n }\n };\n this.setDisableFadeIn = (value?: boolean) => {\n disableFadeIn = value;\n };\n }\n}\n"],"mappings":";;AAQA,MAAM,gBAAgB,GAAW,GAAW,GAAW,MAAc;CACnE,MAAM,aAAa,IAAI,IAAI;CAC3B,OAAO,KAAK,aAAa,aAAa,aAAa,KAAK;AAC1D;AAEA,IAAqB,gBAArB,cAA2C,gBAAwC;CACjF;CAEA;CAEA,YAAY,EAAE,WAAW,YAAY,UAAU,eAAe,QAAqB,CAAC,GAAG;EACrF,IAAI;EACJ,IAAI;EACJ,IAAI,MAAgD;EACpD,IAAI;EACJ,IAAI,gBAAgB;EACpB,IAAI,iBAAiB;EACrB,MAAM,cAAc,UAA4B;GAC9C,IAAI,CAAC,KAAK;IACR,MAAM,QAAQ,MAAM,gBAAgB,MAAM;IAC1C,MAAM,SAAS,MAAM,iBAAiB,MAAM;IAE5C,MAAM,IADgB,gBAAgB,OAAO,MAC/B,EAAE,WAAW,IAAI;GACjC;GACA,MAAM,EAAE,cAAc,eAAe,aAAa,UAAU,cAAc;GAC1E,OAAO;IACL;IACA;IACA,aAAa,eAAe,KAAA;IAC5B,UAAU,YAAY,KAAA;IACtB,WAAW,aAAa;GAC1B;EACF;EACA,MAAM;GACJ,UAAU,OAAO,YAAY;IAC3B,IAAI,CAAC,gBAAgB;KACnB,MAAM,MAAM;KACZ,OAAO,MAAM,MAAM;IACrB;IACA,MAAM,YAAY,MAAM,aAAa;IACrC,IAAI,CAAC,iBAAiB,YAAY,MAAO,cAAc;KACrD,IAAI,CAAC,OAAO,CAAC,MAAM,WAAW,KAAK;KACnC,IAAI,OAAO,MAAM;MACf,MAAM,aAAa,aAAa,YAAY,KAAM,GAAG,KAAK,YAAY;MACtE,IAAI,SAAS,cAAc,WAAW;MACtC,IAAI,UAAU,OAAO,GAAG,CAAC;MACzB,MAAM,MAAM;MACZ,MAAM,QAAQ,IAAI,WAAW,IAAI,QAAkC;OACjE,GAAG;OACH;MACF,CAAC;MACD,WAAW,QAAQ,KAAK;MACxB;KACF;IACF;IACA,WAAW,QAAQ,KAAK;GAC1B;GACA,OAAO,OAAO,eAAe;IAC3B,IAAI,QAAQ,KAAK,aAAa,CAAC,gBAAgB;KAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,WAAW,IAAI;KAClC,IAAI,OAAO,MAAM;MACf,WAAW,IAAI,SAAS;MACxB,MAAM,UAAU;MAChB,MAAM,OAAO;MACb,MAAM,EAAE,WAAW,UAAU;MAC7B,MAAM,WAAW,KAAK,WAAW,KAAK,WAAW,MAAO;MACxD,MAAM,WAAW,SAAiB;OAOhC,QAAQ,SAAS,cANE,aACjB,KAAK,IAAI,GAAG,eAAe,IAAI,GAC/B,GACA,KACA,YAEsC,EAAE;OAC1C,QAAQ,UAAU,MAAM,GAAG,CAAC;OAC5B,MAAM,QAAQ,IAAI,WAAW,QAAQ,QAAkC;QACrE,GAAG;QACH,WAAW,QAAQ,OAAO;OAC5B,CAAC;OACD,WAAW,QAAQ,KAAK;OACxB,MAAM,OAAO,OAAO;OACpB,IAAI,OAAO,cAAc;QACvB,KAAK,MAAM;QACX,UAAU,QAAQ;OACpB,OAAO,iBAAiB,QAAQ,IAAI,GAAG,CAAC;MAC1C;MACA,QAAQ,QAAQ;KAClB;IACF;IACA,MAAM,UAAU;GAClB;EACF,CAAC;EACD,KAAK,qBAAqB,UAAoB;GAC5C,iBAAiB;GACjB,IAAI,gBAAgB;IAClB,MAAM,MAAM;IACZ,OAAO,KAAA;GACT;EACF;EACA,KAAK,oBAAoB,UAAoB;GAC3C,gBAAgB;EAClB;CACF;AACF"}
@@ -23,20 +23,23 @@ var VideoChunkGenerator = class extends TransformStream {
23
23
  }
24
24
  id = Date.now().toString(16).slice(-6);
25
25
  constructor() {
26
+ let inTrackEntry = false;
27
+ let trackNumber;
26
28
  let trackType;
29
+ let videoTrackNumber;
27
30
  let offset = 0;
28
31
  let block;
29
32
  let hasReference = false;
30
33
  let total = 0;
31
- const config = {};
32
- const elements = {};
33
- const required = {
34
+ const createRequired = () => ({
34
35
  PixelWidth: "codedWidth",
35
36
  PixelHeight: "codedHeight",
36
37
  CodecPrivate: "codec",
37
38
  CodecID: "codec"
38
- };
39
- const parseConfig = (meta) => {
39
+ });
40
+ let trackConfig = {};
41
+ let trackRequired = createRequired();
42
+ const parseConfig = (meta, config, required) => {
40
43
  switch (meta.name) {
41
44
  case "CodecID": {
42
45
  const codec = getCodec(meta);
@@ -58,19 +61,44 @@ var VideoChunkGenerator = class extends TransformStream {
58
61
  if (name) config[name] = meta.value;
59
62
  }
60
63
  }
64
+ delete required[meta.name];
61
65
  };
66
+ const isTrackNumber = (element) => element.name === "TrackNumber" && require_Element.isContentElement(element);
67
+ const isTrackType = (element) => element.name === "TrackType" && require_Element.isContentElement(element);
68
+ const isVideoBlock = (candidate) => videoTrackNumber != null && candidate.track === videoTrackNumber;
62
69
  super({
63
70
  transform: async (element, controller) => {
64
- elements[element.name] = (elements[element.name] ?? 0) + 1;
65
71
  try {
66
- if (require_Element.isTrackType(element)) {
67
- trackType = element.value;
72
+ if (require_Element.isMasterElement(element) && element.name === "TrackEntry") {
73
+ if (element.isClosing) {
74
+ if (trackType === VIDEO && trackNumber != null) {
75
+ videoTrackNumber = trackNumber;
76
+ if (Object.keys(trackRequired).length === 0) this.#config.resolve(trackConfig);
77
+ }
78
+ inTrackEntry = false;
79
+ trackNumber = void 0;
80
+ trackType = void 0;
81
+ trackConfig = {};
82
+ trackRequired = createRequired();
83
+ } else {
84
+ inTrackEntry = true;
85
+ trackNumber = void 0;
86
+ trackType = void 0;
87
+ trackConfig = {};
88
+ trackRequired = createRequired();
89
+ }
90
+ return;
91
+ }
92
+ if (inTrackEntry && isTrackNumber(element)) {
93
+ trackNumber = Number(element.value);
94
+ return;
95
+ }
96
+ if (inTrackEntry && isTrackType(element)) {
97
+ trackType = Number(element.value);
68
98
  return;
69
99
  }
70
- if (require_Element.isContentElement(element) && required[element.name] && (trackType == null || trackType === VIDEO)) {
71
- parseConfig(element);
72
- delete required[element.name];
73
- if (Object.keys(required).length === 0) this.#config.resolve(config);
100
+ if (inTrackEntry && require_Element.isContentElement(element) && trackRequired[element.name]) {
101
+ parseConfig(element, trackConfig, trackRequired);
74
102
  return;
75
103
  }
76
104
  if (require_Element.isTimestamp(element)) {
@@ -82,7 +110,7 @@ var VideoChunkGenerator = class extends TransformStream {
82
110
  return;
83
111
  }
84
112
  if (require_Element.isSimpleBlockElement(element)) {
85
- if (trackType === VIDEO) {
113
+ if (isVideoBlock(element)) {
86
114
  controller.enqueue(new EncodedVideoChunk({
87
115
  type: element.keyframe ? "key" : "delta",
88
116
  timestamp: (offset + element.value) * 1e3,
@@ -101,7 +129,7 @@ var VideoChunkGenerator = class extends TransformStream {
101
129
  return;
102
130
  }
103
131
  if (require_Element.isBlockGroupElement(element) && element.isClosing) {
104
- if (block) {
132
+ if (block && isVideoBlock(block)) {
105
133
  controller.enqueue(new EncodedVideoChunk({
106
134
  type: hasReference ? "delta" : "key",
107
135
  timestamp: (block.value + offset) * 1e3,
@@ -1 +1 @@
1
- {"version":3,"file":"VideoChunkGenerator.cjs","names":["Deferred","#config","readHexString","isTrackType","isContentElement","isTimestamp","isDuration","isSimpleBlockElement","isBlockElement","isBlockGroupElement","isMasterElement"],"sources":["../src/VideoChunkGenerator.ts"],"sourcesContent":["import Deferred from './Deferred';\nimport {\n isBlockElement,\n isBlockGroupElement,\n isContentElement,\n isDuration,\n isMasterElement,\n isSimpleBlockElement,\n isTimestamp,\n isTrackType,\n} from './Element';\nimport type { BlockElement, ContentElement, Element } from './Element';\nimport { readHexString } from './tools';\n\nconst VIDEO = 1;\n\nconst getCodec = (element: ContentElement<'string' | 'utf-8'>): string | undefined => {\n switch (element.value) {\n case 'V_MPEG4/ISO/AVC':\n return 'avc1.';\n case 'V_VP9':\n return 'vp09.00.50.08';\n case 'V_VP8':\n return 'vp8'; // 'vp08.00.41.08';\n default:\n return undefined;\n }\n};\n\nexport default class VideoChunkGenerator extends TransformStream<Element, EncodedVideoChunk> {\n #config = new Deferred<VideoDecoderConfig>();\n\n readonly clusters: number[] = [];\n\n get config(): Promise<VideoDecoderConfig> {\n return this.#config.promise;\n }\n\n readonly id: string = Date.now().toString(16).slice(-6);\n\n constructor() {\n let trackType: number | undefined;\n let offset = 0;\n let block: BlockElement | undefined;\n let hasReference = false;\n let total = 0;\n const config: Partial<VideoDecoderConfig> = {};\n const elements: Record<string, number> = {};\n const required: Record<string, keyof VideoDecoderConfig> = {\n PixelWidth: 'codedWidth',\n PixelHeight: 'codedHeight',\n CodecPrivate: 'codec',\n CodecID: 'codec',\n };\n\n const parseConfig = (meta: ContentElement): void => {\n switch (meta.name) {\n case 'CodecID': {\n const codec = getCodec(meta as ContentElement<'string'>);\n if (codec) {\n config.codec = codec;\n if (!codec.endsWith('.')) {\n delete required.CodecPrivate;\n }\n }\n break;\n }\n case 'CodecPrivate':\n if (config.codec === 'avc1.') {\n const codecPrivate = meta as ContentElement<'binary'>;\n config.codec += readHexString(codecPrivate.data.subarray(1, 4));\n config.description = codecPrivate.data.slice();\n }\n break;\n default: {\n const name = required[meta.name];\n if (name) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config[name] = meta.value as any;\n }\n }\n }\n };\n super({\n transform: async (element, controller) => {\n elements[element.name] = (elements[element.name] ?? 0) + 1;\n try {\n if (isTrackType(element)) {\n trackType = element.value as number;\n return;\n }\n if (\n isContentElement(element) &&\n required[element.name] &&\n (trackType == null || trackType === VIDEO)\n ) {\n parseConfig(element);\n delete required[element.name];\n if (Object.keys(required).length === 0) {\n // console.info(`VideoChunkGenerator#${this.id}:config ${JSON.stringify(config)}`);\n this.#config.resolve(config as VideoDecoderConfig);\n }\n return;\n }\n\n if (isTimestamp(element)) {\n offset = element.value as number;\n return;\n }\n\n if (isDuration(element)) {\n postMessage({ duration: element.value / 1000 });\n return;\n }\n\n if (isSimpleBlockElement(element)) {\n if (trackType === VIDEO) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: element.keyframe ? 'key' : 'delta',\n timestamp: (offset + element.value) * 1000,\n data: element.payload,\n }),\n );\n total += 1;\n }\n return;\n }\n\n if (isBlockElement(element)) {\n block = element;\n return;\n }\n\n if (element.name === 'ReferenceBlock') {\n hasReference = true;\n return;\n }\n\n if (isBlockGroupElement(element) && element.isClosing) {\n if (block) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: hasReference ? 'delta' : 'key',\n timestamp: (block.value + offset) * 1000,\n data: block.payload,\n }),\n );\n total += 1;\n }\n block = undefined;\n hasReference = false;\n }\n if (isMasterElement(element) && element.name === 'Cluster' && !element.isClosing)\n this.clusters.push(total);\n } catch (err) {\n console.error(`VideoChunkGenerator#${this.id} error while ELEMENT: ${err}`);\n controller.error(err);\n this.#config.reject(err as Error);\n }\n },\n flush: (controller) => {\n // console.info(\n // `VideoChunkGenerator#${this.id}:flush total: ${total}, clusters: ${this.clusters.join()}`\n // );\n controller.terminate();\n },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VideoChunkGenerator.cjs","names":["Deferred","#config","readHexString","isContentElement","isMasterElement","isTimestamp","isDuration","isSimpleBlockElement","isBlockElement","isBlockGroupElement"],"sources":["../src/VideoChunkGenerator.ts"],"sourcesContent":["import Deferred from './Deferred';\nimport {\n isBlockElement,\n isBlockGroupElement,\n isContentElement,\n isDuration,\n isMasterElement,\n isSimpleBlockElement,\n isTimestamp,\n} from './Element';\nimport type { BlockElement, ContentElement, Element } from './Element';\nimport { readHexString } from './tools';\n\nconst VIDEO = 1;\n\nconst getCodec = (element: ContentElement<'string' | 'utf-8'>): string | undefined => {\n switch (element.value) {\n case 'V_MPEG4/ISO/AVC':\n return 'avc1.';\n case 'V_VP9':\n return 'vp09.00.50.08';\n case 'V_VP8':\n return 'vp8'; // 'vp08.00.41.08';\n default:\n return undefined;\n }\n};\n\nexport default class VideoChunkGenerator extends TransformStream<Element, EncodedVideoChunk> {\n #config = new Deferred<VideoDecoderConfig>();\n\n readonly clusters: number[] = [];\n\n get config(): Promise<VideoDecoderConfig> {\n return this.#config.promise;\n }\n\n readonly id: string = Date.now().toString(16).slice(-6);\n\n constructor() {\n let inTrackEntry = false;\n let trackNumber: number | undefined;\n let trackType: number | undefined;\n let videoTrackNumber: number | undefined;\n let offset = 0;\n let block: BlockElement | undefined;\n let hasReference = false;\n let total = 0;\n const createRequired = (): Record<string, keyof VideoDecoderConfig> => ({\n PixelWidth: 'codedWidth',\n PixelHeight: 'codedHeight',\n CodecPrivate: 'codec',\n CodecID: 'codec',\n });\n let trackConfig: Partial<VideoDecoderConfig> = {};\n let trackRequired = createRequired();\n\n const parseConfig = (\n meta: ContentElement,\n config: Partial<VideoDecoderConfig>,\n required: Record<string, keyof VideoDecoderConfig>,\n ): void => {\n switch (meta.name) {\n case 'CodecID': {\n const codec = getCodec(meta as ContentElement<'string'>);\n if (codec) {\n config.codec = codec;\n if (!codec.endsWith('.')) {\n delete required.CodecPrivate;\n }\n }\n break;\n }\n case 'CodecPrivate':\n if (config.codec === 'avc1.') {\n const codecPrivate = meta as ContentElement<'binary'>;\n config.codec += readHexString(codecPrivate.data.subarray(1, 4));\n config.description = codecPrivate.data.slice();\n }\n break;\n default: {\n const name = required[meta.name];\n if (name) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config[name] = meta.value as any;\n }\n }\n }\n delete required[meta.name];\n };\n const isTrackNumber = (element: Element): element is ContentElement<'uinteger'> =>\n element.name === 'TrackNumber' && isContentElement(element);\n\n const isTrackType = (element: Element): element is ContentElement<'uinteger'> =>\n element.name === 'TrackType' && isContentElement(element);\n\n const isVideoBlock = (candidate: { track: number }): boolean =>\n videoTrackNumber != null && candidate.track === videoTrackNumber;\n\n super({\n transform: async (element, controller) => {\n try {\n if (isMasterElement(element) && element.name === 'TrackEntry') {\n if (element.isClosing) {\n if (trackType === VIDEO && trackNumber != null) {\n videoTrackNumber = trackNumber;\n if (Object.keys(trackRequired).length === 0) {\n // console.info(`VideoChunkGenerator#${this.id}:config ${JSON.stringify(trackConfig)}`);\n this.#config.resolve(trackConfig as VideoDecoderConfig);\n }\n }\n inTrackEntry = false;\n trackNumber = undefined;\n trackType = undefined;\n trackConfig = {};\n trackRequired = createRequired();\n } else {\n inTrackEntry = true;\n trackNumber = undefined;\n trackType = undefined;\n trackConfig = {};\n trackRequired = createRequired();\n }\n return;\n }\n\n if (inTrackEntry && isTrackNumber(element)) {\n trackNumber = Number(element.value);\n return;\n }\n\n if (inTrackEntry && isTrackType(element)) {\n trackType = Number(element.value);\n return;\n }\n if (\n inTrackEntry &&\n isContentElement(element) &&\n trackRequired[element.name]\n ) {\n parseConfig(element, trackConfig, trackRequired);\n return;\n }\n\n if (isTimestamp(element)) {\n offset = element.value as number;\n return;\n }\n\n if (isDuration(element)) {\n postMessage({ duration: element.value / 1000 });\n return;\n }\n\n if (isSimpleBlockElement(element)) {\n if (isVideoBlock(element)) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: element.keyframe ? 'key' : 'delta',\n timestamp: (offset + element.value) * 1000,\n data: element.payload,\n }),\n );\n total += 1;\n }\n return;\n }\n\n if (isBlockElement(element)) {\n block = element;\n return;\n }\n\n if (element.name === 'ReferenceBlock') {\n hasReference = true;\n return;\n }\n\n if (isBlockGroupElement(element) && element.isClosing) {\n if (block && isVideoBlock(block)) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: hasReference ? 'delta' : 'key',\n timestamp: (block.value + offset) * 1000,\n data: block.payload,\n }),\n );\n total += 1;\n }\n block = undefined;\n hasReference = false;\n }\n if (isMasterElement(element) && element.name === 'Cluster' && !element.isClosing)\n this.clusters.push(total);\n } catch (err) {\n console.error(`VideoChunkGenerator#${this.id} error while ELEMENT: ${err}`);\n controller.error(err);\n this.#config.reject(err as Error);\n }\n },\n flush: (controller) => {\n // console.info(\n // `VideoChunkGenerator#${this.id}:flush total: ${total}, clusters: ${this.clusters.join()}`\n // );\n controller.terminate();\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoChunkGenerator.d.mts","names":[],"sources":["../src/VideoChunkGenerator.ts"],"mappings":";;;cA6BqB,mBAAA,SAA4B,eAAA,CAAgB,OAAA,EAAS,iBAAA;EAAA;WAG/D,QAAA;EAAA,IAEL,MAAA,CAAA,GAAU,OAAA,CAAQ,kBAAA;EAAA,SAIb,EAAA"}
1
+ {"version":3,"file":"VideoChunkGenerator.d.mts","names":[],"sources":["../src/VideoChunkGenerator.ts"],"mappings":";;;cA4BqB,mBAAA,SAA4B,eAAA,CAAgB,OAAA,EAAS,iBAAA;EAAA;WAG/D,QAAA;EAAA,IAEL,MAAA,CAAA,GAAU,OAAA,CAAQ,kBAAA;EAAA,SAIb,EAAA"}
@@ -1,4 +1,4 @@
1
- import { a as isDuration, c as isTimestamp, i as isContentElement, l as isTrackType, n as isBlockElement, o as isMasterElement, r as isBlockGroupElement, s as isSimpleBlockElement } from "./Element-ChEar-fo.mjs";
1
+ import { a as isDuration, c as isTimestamp, i as isContentElement, n as isBlockElement, o as isMasterElement, r as isBlockGroupElement, s as isSimpleBlockElement } from "./Element-ChEar-fo.mjs";
2
2
  import { o as readHexString } from "./tools-CYLlbo9J.mjs";
3
3
  import { t as Deferred } from "./Deferred-7Cu0KIje.mjs";
4
4
  //#region src/VideoChunkGenerator.ts
@@ -19,20 +19,23 @@ var VideoChunkGenerator = class extends TransformStream {
19
19
  }
20
20
  id = Date.now().toString(16).slice(-6);
21
21
  constructor() {
22
+ let inTrackEntry = false;
23
+ let trackNumber;
22
24
  let trackType;
25
+ let videoTrackNumber;
23
26
  let offset = 0;
24
27
  let block;
25
28
  let hasReference = false;
26
29
  let total = 0;
27
- const config = {};
28
- const elements = {};
29
- const required = {
30
+ const createRequired = () => ({
30
31
  PixelWidth: "codedWidth",
31
32
  PixelHeight: "codedHeight",
32
33
  CodecPrivate: "codec",
33
34
  CodecID: "codec"
34
- };
35
- const parseConfig = (meta) => {
35
+ });
36
+ let trackConfig = {};
37
+ let trackRequired = createRequired();
38
+ const parseConfig = (meta, config, required) => {
36
39
  switch (meta.name) {
37
40
  case "CodecID": {
38
41
  const codec = getCodec(meta);
@@ -54,19 +57,44 @@ var VideoChunkGenerator = class extends TransformStream {
54
57
  if (name) config[name] = meta.value;
55
58
  }
56
59
  }
60
+ delete required[meta.name];
57
61
  };
62
+ const isTrackNumber = (element) => element.name === "TrackNumber" && isContentElement(element);
63
+ const isTrackType = (element) => element.name === "TrackType" && isContentElement(element);
64
+ const isVideoBlock = (candidate) => videoTrackNumber != null && candidate.track === videoTrackNumber;
58
65
  super({
59
66
  transform: async (element, controller) => {
60
- elements[element.name] = (elements[element.name] ?? 0) + 1;
61
67
  try {
62
- if (isTrackType(element)) {
63
- trackType = element.value;
68
+ if (isMasterElement(element) && element.name === "TrackEntry") {
69
+ if (element.isClosing) {
70
+ if (trackType === VIDEO && trackNumber != null) {
71
+ videoTrackNumber = trackNumber;
72
+ if (Object.keys(trackRequired).length === 0) this.#config.resolve(trackConfig);
73
+ }
74
+ inTrackEntry = false;
75
+ trackNumber = void 0;
76
+ trackType = void 0;
77
+ trackConfig = {};
78
+ trackRequired = createRequired();
79
+ } else {
80
+ inTrackEntry = true;
81
+ trackNumber = void 0;
82
+ trackType = void 0;
83
+ trackConfig = {};
84
+ trackRequired = createRequired();
85
+ }
86
+ return;
87
+ }
88
+ if (inTrackEntry && isTrackNumber(element)) {
89
+ trackNumber = Number(element.value);
90
+ return;
91
+ }
92
+ if (inTrackEntry && isTrackType(element)) {
93
+ trackType = Number(element.value);
64
94
  return;
65
95
  }
66
- if (isContentElement(element) && required[element.name] && (trackType == null || trackType === VIDEO)) {
67
- parseConfig(element);
68
- delete required[element.name];
69
- if (Object.keys(required).length === 0) this.#config.resolve(config);
96
+ if (inTrackEntry && isContentElement(element) && trackRequired[element.name]) {
97
+ parseConfig(element, trackConfig, trackRequired);
70
98
  return;
71
99
  }
72
100
  if (isTimestamp(element)) {
@@ -78,7 +106,7 @@ var VideoChunkGenerator = class extends TransformStream {
78
106
  return;
79
107
  }
80
108
  if (isSimpleBlockElement(element)) {
81
- if (trackType === VIDEO) {
109
+ if (isVideoBlock(element)) {
82
110
  controller.enqueue(new EncodedVideoChunk({
83
111
  type: element.keyframe ? "key" : "delta",
84
112
  timestamp: (offset + element.value) * 1e3,
@@ -97,7 +125,7 @@ var VideoChunkGenerator = class extends TransformStream {
97
125
  return;
98
126
  }
99
127
  if (isBlockGroupElement(element) && element.isClosing) {
100
- if (block) {
128
+ if (block && isVideoBlock(block)) {
101
129
  controller.enqueue(new EncodedVideoChunk({
102
130
  type: hasReference ? "delta" : "key",
103
131
  timestamp: (block.value + offset) * 1e3,
@@ -1 +1 @@
1
- {"version":3,"file":"VideoChunkGenerator.mjs","names":["#config"],"sources":["../src/VideoChunkGenerator.ts"],"sourcesContent":["import Deferred from './Deferred';\nimport {\n isBlockElement,\n isBlockGroupElement,\n isContentElement,\n isDuration,\n isMasterElement,\n isSimpleBlockElement,\n isTimestamp,\n isTrackType,\n} from './Element';\nimport type { BlockElement, ContentElement, Element } from './Element';\nimport { readHexString } from './tools';\n\nconst VIDEO = 1;\n\nconst getCodec = (element: ContentElement<'string' | 'utf-8'>): string | undefined => {\n switch (element.value) {\n case 'V_MPEG4/ISO/AVC':\n return 'avc1.';\n case 'V_VP9':\n return 'vp09.00.50.08';\n case 'V_VP8':\n return 'vp8'; // 'vp08.00.41.08';\n default:\n return undefined;\n }\n};\n\nexport default class VideoChunkGenerator extends TransformStream<Element, EncodedVideoChunk> {\n #config = new Deferred<VideoDecoderConfig>();\n\n readonly clusters: number[] = [];\n\n get config(): Promise<VideoDecoderConfig> {\n return this.#config.promise;\n }\n\n readonly id: string = Date.now().toString(16).slice(-6);\n\n constructor() {\n let trackType: number | undefined;\n let offset = 0;\n let block: BlockElement | undefined;\n let hasReference = false;\n let total = 0;\n const config: Partial<VideoDecoderConfig> = {};\n const elements: Record<string, number> = {};\n const required: Record<string, keyof VideoDecoderConfig> = {\n PixelWidth: 'codedWidth',\n PixelHeight: 'codedHeight',\n CodecPrivate: 'codec',\n CodecID: 'codec',\n };\n\n const parseConfig = (meta: ContentElement): void => {\n switch (meta.name) {\n case 'CodecID': {\n const codec = getCodec(meta as ContentElement<'string'>);\n if (codec) {\n config.codec = codec;\n if (!codec.endsWith('.')) {\n delete required.CodecPrivate;\n }\n }\n break;\n }\n case 'CodecPrivate':\n if (config.codec === 'avc1.') {\n const codecPrivate = meta as ContentElement<'binary'>;\n config.codec += readHexString(codecPrivate.data.subarray(1, 4));\n config.description = codecPrivate.data.slice();\n }\n break;\n default: {\n const name = required[meta.name];\n if (name) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config[name] = meta.value as any;\n }\n }\n }\n };\n super({\n transform: async (element, controller) => {\n elements[element.name] = (elements[element.name] ?? 0) + 1;\n try {\n if (isTrackType(element)) {\n trackType = element.value as number;\n return;\n }\n if (\n isContentElement(element) &&\n required[element.name] &&\n (trackType == null || trackType === VIDEO)\n ) {\n parseConfig(element);\n delete required[element.name];\n if (Object.keys(required).length === 0) {\n // console.info(`VideoChunkGenerator#${this.id}:config ${JSON.stringify(config)}`);\n this.#config.resolve(config as VideoDecoderConfig);\n }\n return;\n }\n\n if (isTimestamp(element)) {\n offset = element.value as number;\n return;\n }\n\n if (isDuration(element)) {\n postMessage({ duration: element.value / 1000 });\n return;\n }\n\n if (isSimpleBlockElement(element)) {\n if (trackType === VIDEO) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: element.keyframe ? 'key' : 'delta',\n timestamp: (offset + element.value) * 1000,\n data: element.payload,\n }),\n );\n total += 1;\n }\n return;\n }\n\n if (isBlockElement(element)) {\n block = element;\n return;\n }\n\n if (element.name === 'ReferenceBlock') {\n hasReference = true;\n return;\n }\n\n if (isBlockGroupElement(element) && element.isClosing) {\n if (block) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: hasReference ? 'delta' : 'key',\n timestamp: (block.value + offset) * 1000,\n data: block.payload,\n }),\n );\n total += 1;\n }\n block = undefined;\n hasReference = false;\n }\n if (isMasterElement(element) && element.name === 'Cluster' && !element.isClosing)\n this.clusters.push(total);\n } catch (err) {\n console.error(`VideoChunkGenerator#${this.id} error while ELEMENT: ${err}`);\n controller.error(err);\n this.#config.reject(err as Error);\n }\n },\n flush: (controller) => {\n // console.info(\n // `VideoChunkGenerator#${this.id}:flush total: ${total}, clusters: ${this.clusters.join()}`\n // );\n controller.terminate();\n },\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VideoChunkGenerator.mjs","names":["#config"],"sources":["../src/VideoChunkGenerator.ts"],"sourcesContent":["import Deferred from './Deferred';\nimport {\n isBlockElement,\n isBlockGroupElement,\n isContentElement,\n isDuration,\n isMasterElement,\n isSimpleBlockElement,\n isTimestamp,\n} from './Element';\nimport type { BlockElement, ContentElement, Element } from './Element';\nimport { readHexString } from './tools';\n\nconst VIDEO = 1;\n\nconst getCodec = (element: ContentElement<'string' | 'utf-8'>): string | undefined => {\n switch (element.value) {\n case 'V_MPEG4/ISO/AVC':\n return 'avc1.';\n case 'V_VP9':\n return 'vp09.00.50.08';\n case 'V_VP8':\n return 'vp8'; // 'vp08.00.41.08';\n default:\n return undefined;\n }\n};\n\nexport default class VideoChunkGenerator extends TransformStream<Element, EncodedVideoChunk> {\n #config = new Deferred<VideoDecoderConfig>();\n\n readonly clusters: number[] = [];\n\n get config(): Promise<VideoDecoderConfig> {\n return this.#config.promise;\n }\n\n readonly id: string = Date.now().toString(16).slice(-6);\n\n constructor() {\n let inTrackEntry = false;\n let trackNumber: number | undefined;\n let trackType: number | undefined;\n let videoTrackNumber: number | undefined;\n let offset = 0;\n let block: BlockElement | undefined;\n let hasReference = false;\n let total = 0;\n const createRequired = (): Record<string, keyof VideoDecoderConfig> => ({\n PixelWidth: 'codedWidth',\n PixelHeight: 'codedHeight',\n CodecPrivate: 'codec',\n CodecID: 'codec',\n });\n let trackConfig: Partial<VideoDecoderConfig> = {};\n let trackRequired = createRequired();\n\n const parseConfig = (\n meta: ContentElement,\n config: Partial<VideoDecoderConfig>,\n required: Record<string, keyof VideoDecoderConfig>,\n ): void => {\n switch (meta.name) {\n case 'CodecID': {\n const codec = getCodec(meta as ContentElement<'string'>);\n if (codec) {\n config.codec = codec;\n if (!codec.endsWith('.')) {\n delete required.CodecPrivate;\n }\n }\n break;\n }\n case 'CodecPrivate':\n if (config.codec === 'avc1.') {\n const codecPrivate = meta as ContentElement<'binary'>;\n config.codec += readHexString(codecPrivate.data.subarray(1, 4));\n config.description = codecPrivate.data.slice();\n }\n break;\n default: {\n const name = required[meta.name];\n if (name) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n config[name] = meta.value as any;\n }\n }\n }\n delete required[meta.name];\n };\n const isTrackNumber = (element: Element): element is ContentElement<'uinteger'> =>\n element.name === 'TrackNumber' && isContentElement(element);\n\n const isTrackType = (element: Element): element is ContentElement<'uinteger'> =>\n element.name === 'TrackType' && isContentElement(element);\n\n const isVideoBlock = (candidate: { track: number }): boolean =>\n videoTrackNumber != null && candidate.track === videoTrackNumber;\n\n super({\n transform: async (element, controller) => {\n try {\n if (isMasterElement(element) && element.name === 'TrackEntry') {\n if (element.isClosing) {\n if (trackType === VIDEO && trackNumber != null) {\n videoTrackNumber = trackNumber;\n if (Object.keys(trackRequired).length === 0) {\n // console.info(`VideoChunkGenerator#${this.id}:config ${JSON.stringify(trackConfig)}`);\n this.#config.resolve(trackConfig as VideoDecoderConfig);\n }\n }\n inTrackEntry = false;\n trackNumber = undefined;\n trackType = undefined;\n trackConfig = {};\n trackRequired = createRequired();\n } else {\n inTrackEntry = true;\n trackNumber = undefined;\n trackType = undefined;\n trackConfig = {};\n trackRequired = createRequired();\n }\n return;\n }\n\n if (inTrackEntry && isTrackNumber(element)) {\n trackNumber = Number(element.value);\n return;\n }\n\n if (inTrackEntry && isTrackType(element)) {\n trackType = Number(element.value);\n return;\n }\n if (\n inTrackEntry &&\n isContentElement(element) &&\n trackRequired[element.name]\n ) {\n parseConfig(element, trackConfig, trackRequired);\n return;\n }\n\n if (isTimestamp(element)) {\n offset = element.value as number;\n return;\n }\n\n if (isDuration(element)) {\n postMessage({ duration: element.value / 1000 });\n return;\n }\n\n if (isSimpleBlockElement(element)) {\n if (isVideoBlock(element)) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: element.keyframe ? 'key' : 'delta',\n timestamp: (offset + element.value) * 1000,\n data: element.payload,\n }),\n );\n total += 1;\n }\n return;\n }\n\n if (isBlockElement(element)) {\n block = element;\n return;\n }\n\n if (element.name === 'ReferenceBlock') {\n hasReference = true;\n return;\n }\n\n if (isBlockGroupElement(element) && element.isClosing) {\n if (block && isVideoBlock(block)) {\n controller.enqueue(\n new EncodedVideoChunk({\n type: hasReference ? 'delta' : 'key',\n timestamp: (block.value + offset) * 1000,\n data: block.payload,\n }),\n );\n total += 1;\n }\n block = undefined;\n hasReference = false;\n }\n if (isMasterElement(element) && element.name === 'Cluster' && !element.isClosing)\n this.clusters.push(total);\n } catch (err) {\n console.error(`VideoChunkGenerator#${this.id} error while ELEMENT: ${err}`);\n controller.error(err);\n this.#config.reject(err as Error);\n }\n },\n flush: (controller) => {\n // console.info(\n // `VideoChunkGenerator#${this.id}:flush total: ${total}, clusters: ${this.clusters.join()}`\n // );\n controller.terminate();\n },\n });\n }\n}\n"],"mappings":"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"}