agora-electron-sdk 4.5.1 → 4.5.2-dev.2

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.
Files changed (99) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/js/Decoder/gpu-utils.js +74 -0
  3. package/js/Decoder/index.js +172 -0
  4. package/js/Private/AgoraBase.js +15 -3
  5. package/js/Private/extension/AgoraBaseExtension.js +1 -0
  6. package/js/Private/internal/IrisApiEngine.js +20 -7
  7. package/js/Private/internal/RtcEngineExInternal.js +31 -16
  8. package/js/Private/ipc/main.js +21 -0
  9. package/js/Private/ipc/renderer.js +21 -0
  10. package/js/Renderer/CapabilityManager.js +99 -0
  11. package/js/Renderer/IRenderer.js +40 -11
  12. package/js/Renderer/IRendererCache.js +75 -0
  13. package/js/Renderer/RendererCache.js +26 -63
  14. package/js/Renderer/RendererManager.js +259 -38
  15. package/js/Renderer/WebCodecsRenderer/index.js +109 -0
  16. package/js/Renderer/WebCodecsRendererCache.js +115 -0
  17. package/js/Renderer/WebGLRenderer/index.js +115 -67
  18. package/js/Renderer/YUVCanvasRenderer/index.js +5 -3
  19. package/js/Renderer/index.js +0 -1
  20. package/js/Types.js +51 -1
  21. package/js/Utils.js +47 -7
  22. package/package.json +28 -11
  23. package/scripts/checkElectron.js +4 -2
  24. package/scripts/download.js +102 -0
  25. package/scripts/downloadPrebuild.js +2 -1
  26. package/scripts/synclib.js +2 -2
  27. package/ts/Decoder/gpu-utils.ts +92 -0
  28. package/ts/Decoder/index.ts +206 -0
  29. package/ts/Private/AgoraBase.ts +18 -4
  30. package/ts/Private/IAgoraRtcEngine.ts +6 -7
  31. package/ts/Private/IAgoraRtcEngineEx.ts +2 -1
  32. package/ts/Private/extension/AgoraBaseExtension.ts +14 -1
  33. package/ts/Private/internal/IrisApiEngine.ts +21 -7
  34. package/ts/Private/internal/RtcEngineExInternal.ts +27 -8
  35. package/ts/Private/ipc/main.ts +22 -0
  36. package/ts/Private/ipc/renderer.ts +21 -0
  37. package/ts/Renderer/CapabilityManager.ts +126 -0
  38. package/ts/Renderer/IRenderer.ts +52 -17
  39. package/ts/Renderer/IRendererCache.ts +96 -0
  40. package/ts/Renderer/RendererCache.ts +42 -85
  41. package/ts/Renderer/RendererManager.ts +342 -52
  42. package/ts/Renderer/WebCodecsRenderer/index.ts +145 -0
  43. package/ts/Renderer/WebCodecsRendererCache.ts +137 -0
  44. package/ts/Renderer/WebGLRenderer/index.ts +153 -107
  45. package/ts/Renderer/YUVCanvasRenderer/index.ts +24 -22
  46. package/ts/Renderer/index.ts +0 -1
  47. package/ts/Types.ts +130 -7
  48. package/ts/Utils.ts +53 -7
  49. package/types/Decoder/gpu-utils.d.ts +21 -0
  50. package/types/Decoder/gpu-utils.d.ts.map +1 -0
  51. package/types/Decoder/index.d.ts +26 -0
  52. package/types/Decoder/index.d.ts.map +1 -0
  53. package/types/Private/AgoraBase.d.ts +18 -4
  54. package/types/Private/AgoraBase.d.ts.map +1 -1
  55. package/types/Private/IAgoraRtcEngine.d.ts +6 -7
  56. package/types/Private/IAgoraRtcEngine.d.ts.map +1 -1
  57. package/types/Private/IAgoraRtcEngineEx.d.ts +2 -1
  58. package/types/Private/IAgoraRtcEngineEx.d.ts.map +1 -1
  59. package/types/Private/extension/AgoraBaseExtension.d.ts +13 -1
  60. package/types/Private/extension/AgoraBaseExtension.d.ts.map +1 -1
  61. package/types/Private/internal/IrisApiEngine.d.ts +2 -0
  62. package/types/Private/internal/IrisApiEngine.d.ts.map +1 -1
  63. package/types/Private/internal/RtcEngineExInternal.d.ts +2 -0
  64. package/types/Private/internal/RtcEngineExInternal.d.ts.map +1 -1
  65. package/types/Private/ipc/main.d.ts +2 -0
  66. package/types/Private/ipc/main.d.ts.map +1 -0
  67. package/types/Private/ipc/renderer.d.ts +3 -0
  68. package/types/Private/ipc/renderer.d.ts.map +1 -0
  69. package/types/Renderer/CapabilityManager.d.ts +20 -0
  70. package/types/Renderer/CapabilityManager.d.ts.map +1 -0
  71. package/types/Renderer/IRenderer.d.ts +8 -7
  72. package/types/Renderer/IRenderer.d.ts.map +1 -1
  73. package/types/Renderer/IRendererCache.d.ts +26 -0
  74. package/types/Renderer/IRendererCache.d.ts.map +1 -0
  75. package/types/Renderer/RendererCache.d.ts +6 -18
  76. package/types/Renderer/RendererCache.d.ts.map +1 -1
  77. package/types/Renderer/RendererManager.d.ts +49 -15
  78. package/types/Renderer/RendererManager.d.ts.map +1 -1
  79. package/types/Renderer/WebCodecsRenderer/index.d.ts +14 -0
  80. package/types/Renderer/WebCodecsRenderer/index.d.ts.map +1 -0
  81. package/types/Renderer/WebCodecsRendererCache.d.ts +15 -0
  82. package/types/Renderer/WebCodecsRendererCache.d.ts.map +1 -0
  83. package/types/Renderer/WebGLRenderer/index.d.ts +5 -3
  84. package/types/Renderer/WebGLRenderer/index.d.ts.map +1 -1
  85. package/types/Renderer/YUVCanvasRenderer/index.d.ts +1 -1
  86. package/types/Renderer/YUVCanvasRenderer/index.d.ts.map +1 -1
  87. package/types/Renderer/index.d.ts +0 -1
  88. package/types/Renderer/index.d.ts.map +1 -1
  89. package/types/Types.d.ts +99 -8
  90. package/types/Types.d.ts.map +1 -1
  91. package/types/Utils.d.ts +4 -0
  92. package/types/Utils.d.ts.map +1 -1
  93. package/js/Renderer/IRendererManager.js +0 -229
  94. package/scripts/publishCN/common.sh +0 -19
  95. package/scripts/publishCN/rewrite-dep.sh +0 -22
  96. package/scripts/publishCN/rewrite-example.sh +0 -22
  97. package/ts/Renderer/IRendererManager.ts +0 -316
  98. package/types/Renderer/IRendererManager.d.ts +0 -56
  99. package/types/Renderer/IRendererManager.d.ts.map +0 -1
@@ -1,110 +1,400 @@
1
+ import createAgoraRtcEngine from '../AgoraSdk';
2
+ import {
3
+ VideoMirrorModeType,
4
+ VideoStreamType,
5
+ VideoViewSetupMode,
6
+ } from '../Private/AgoraBase';
7
+ import {
8
+ RenderModeType,
9
+ VideoModulePosition,
10
+ VideoSourceType,
11
+ } from '../Private/AgoraMediaBase';
1
12
  import {
2
- RENDER_MODE,
3
13
  RendererCacheContext,
14
+ RendererCacheType,
4
15
  RendererContext,
5
16
  RendererType,
6
17
  } from '../Types';
7
- import { isSupportWebGL } from '../Utils';
18
+ import { AgoraEnv, isSupportWebGL, logDebug } from '../Utils';
8
19
 
9
20
  import { IRenderer } from './IRenderer';
10
- import { IRendererManager } from './IRendererManager';
21
+ import { generateRendererCacheKey, isUseConnection } from './IRendererCache';
11
22
  import { RendererCache } from './RendererCache';
23
+ import { WebCodecsRenderer } from './WebCodecsRenderer';
24
+ import { WebCodecsRendererCache } from './WebCodecsRendererCache';
12
25
  import { WebGLFallback, WebGLRenderer } from './WebGLRenderer';
13
26
  import { YUVCanvasRenderer } from './YUVCanvasRenderer';
14
27
 
15
28
  /**
16
29
  * @ignore
17
30
  */
18
- export class RendererManager extends IRendererManager {
31
+ export class RendererManager {
32
+ /**
33
+ * @ignore
34
+ */
35
+ private renderingFps: number;
36
+ /**
37
+ * @ignore
38
+ */
39
+ private _currentFrameCount: number;
40
+ /**
41
+ * @ignore
42
+ */
43
+ private _previousFirstFrameTime: number;
44
+ /**
45
+ * @ignore
46
+ */
47
+ private _renderingTimer?: number;
19
48
  /**
20
49
  * @ignore
21
50
  */
22
- private _rendererType: RendererType;
51
+ private _rendererCaches: RendererCacheType[];
52
+ /**
53
+ * @ignore
54
+ */
55
+ private _context: RendererContext;
23
56
 
24
- public set rendererType(rendererType: RendererType) {
25
- if (this._rendererType !== rendererType) {
26
- this._rendererType = rendererType;
27
- }
28
- }
57
+ /**
58
+ * @ignore
59
+ */
60
+ private rendererType: RendererType;
29
61
 
30
- public get rendererType(): RendererType {
31
- return this._rendererType;
32
- }
62
+ /**
63
+ * Currently, the remote video frame is observed in the pre-renderer position and you can not change it.
64
+ * the local video frame is observed in the pre-encoder position by default, and you can change it.
65
+ * @ignore
66
+ */
67
+ private defaultObservedFramePosition: number =
68
+ VideoModulePosition.PositionPreRenderer |
69
+ VideoModulePosition.PositionPreEncoder;
33
70
 
34
71
  constructor() {
35
- super();
36
- this._rendererType = isSupportWebGL()
72
+ this.renderingFps = 15;
73
+ this._currentFrameCount = 0;
74
+ this._previousFirstFrameTime = 0;
75
+ this._rendererCaches = [];
76
+ this._context = {
77
+ renderMode: RenderModeType.RenderModeHidden,
78
+ mirrorMode: VideoMirrorModeType.VideoMirrorModeDisabled,
79
+ };
80
+ this.rendererType = isSupportWebGL()
37
81
  ? RendererType.WEBGL
38
82
  : RendererType.SOFTWARE;
39
83
  }
40
84
 
41
- /**
42
- * @deprecated Use rendererType instead
43
- */
44
- public setRenderMode(mode: RENDER_MODE) {
45
- this.rendererType = mode;
85
+ public setRenderingFps(fps: number) {
86
+ this.renderingFps = fps;
87
+ if (this._renderingTimer) {
88
+ this.stopRendering();
89
+ this.startRendering();
90
+ }
46
91
  }
47
92
 
48
- /**
49
- * @deprecated Use renderingFps instead
50
- */
51
- public setFPS(fps: number) {
52
- this.renderingFps = fps;
93
+ public set defaultChannelId(channelId: string) {
94
+ this._context.channelId = channelId;
53
95
  }
54
96
 
55
- /**
56
- * @deprecated Use getRendererCache instead
57
- */
58
- public getRender(context: RendererCacheContext): RendererCache | undefined {
59
- return this.getRendererCache(context);
97
+ public get defaultChannelId(): string {
98
+ return this._context.channelId ?? '';
60
99
  }
61
100
 
62
- protected override createRenderer(
63
- context: RendererContext,
64
- rendererType?: RendererType
65
- ): IRenderer {
66
- if (rendererType === undefined) {
67
- rendererType = this.rendererType;
101
+ public get defaultRenderMode(): RenderModeType {
102
+ return this._context.renderMode!;
103
+ }
104
+
105
+ public get defaultMirrorMode(): VideoMirrorModeType {
106
+ return this._context.mirrorMode!;
107
+ }
108
+
109
+ public release(): void {
110
+ this.stopRendering();
111
+ this.clearRendererCache();
112
+ }
113
+
114
+ private presetRendererContext(context: RendererContext): RendererContext {
115
+ //this is for preset default value
116
+ context.renderMode = context.renderMode || this.defaultRenderMode;
117
+ context.mirrorMode = context.mirrorMode || this.defaultMirrorMode;
118
+ context.useWebCodecsDecoder = context.useWebCodecsDecoder || false;
119
+ context.enableFps = context.enableFps || false;
120
+ context.position = context.position || this.defaultObservedFramePosition;
121
+
122
+ if (!AgoraEnv.CapabilityManager?.webCodecsDecoderEnabled) {
123
+ context.useWebCodecsDecoder = false;
68
124
  }
69
125
 
126
+ switch (context.sourceType) {
127
+ case VideoSourceType.VideoSourceRemote:
128
+ if (context.uid === undefined) {
129
+ throw new Error('uid is required');
130
+ }
131
+ context.channelId = context.channelId ?? this.defaultChannelId;
132
+ break;
133
+ case VideoSourceType.VideoSourceMediaPlayer:
134
+ if (context.mediaPlayerId === undefined) {
135
+ throw new Error('mediaPlayerId is required');
136
+ }
137
+ context.channelId = '';
138
+ context.uid = context.mediaPlayerId;
139
+ break;
140
+ case undefined:
141
+ if (context.uid) {
142
+ context.sourceType = VideoSourceType.VideoSourceRemote;
143
+ }
144
+ break;
145
+ default:
146
+ context.channelId = '';
147
+ context.uid = 0;
148
+ break;
149
+ }
150
+ return context;
151
+ }
152
+
153
+ public addOrRemoveRenderer(
154
+ context: RendererContext
155
+ ): RendererCacheType | undefined {
156
+ // To be compatible with the old API
157
+ let { setupMode = VideoViewSetupMode.VideoViewSetupAdd } = context;
158
+ if (!context.view) setupMode = VideoViewSetupMode.VideoViewSetupRemove;
159
+ switch (setupMode) {
160
+ case VideoViewSetupMode.VideoViewSetupAdd:
161
+ return this.addRendererToCache(context);
162
+ case VideoViewSetupMode.VideoViewSetupRemove:
163
+ this.removeRendererFromCache(context);
164
+ return undefined;
165
+ case VideoViewSetupMode.VideoViewSetupReplace:
166
+ this.removeRendererFromCache(context);
167
+ return this.addRendererToCache(context);
168
+ }
169
+ }
170
+
171
+ private addRendererToCache(
172
+ context: RendererContext
173
+ ): RendererCacheType | undefined {
174
+ const checkedContext = this.presetRendererContext(context);
175
+
176
+ if (!checkedContext.view) return undefined;
177
+
178
+ if (this.findRenderer(checkedContext.view)) {
179
+ throw new Error('You have already added this view to the renderer');
180
+ }
181
+
182
+ let rendererCache = this.getRendererCache(checkedContext);
183
+ if (!rendererCache) {
184
+ if (context.useWebCodecsDecoder) {
185
+ rendererCache = new WebCodecsRendererCache(checkedContext);
186
+ } else {
187
+ rendererCache = new RendererCache(checkedContext);
188
+ }
189
+ this._rendererCaches.push(rendererCache);
190
+ }
191
+ rendererCache.addRenderer(this.createRenderer(checkedContext));
192
+ if (!context.useWebCodecsDecoder) {
193
+ this.startRendering();
194
+ }
195
+ return rendererCache;
196
+ }
197
+
198
+ public removeRendererFromCache(context: RendererContext): void {
199
+ const checkedContext = this.presetRendererContext(context);
200
+
201
+ const rendererCache = this.getRendererCache(checkedContext);
202
+ if (!rendererCache) return;
203
+ if (checkedContext.view) {
204
+ const renderer = rendererCache.findRenderer(checkedContext.view);
205
+ if (!renderer) return;
206
+ rendererCache.removeRenderer(renderer);
207
+ } else {
208
+ rendererCache.removeRenderer();
209
+ }
210
+ if (rendererCache.renderers.length === 0) {
211
+ rendererCache.release();
212
+ this._rendererCaches.splice(
213
+ this._rendererCaches.indexOf(rendererCache),
214
+ 1
215
+ );
216
+ }
217
+ }
218
+
219
+ public clearRendererCache(): void {
220
+ for (const rendererCache of this._rendererCaches) {
221
+ rendererCache.release();
222
+ }
223
+ this._rendererCaches.splice(0);
224
+ }
225
+
226
+ public getRendererCache(
227
+ context: RendererContext
228
+ ): RendererCacheType | undefined {
229
+ return this._rendererCaches.find(
230
+ (cache) => cache.key === generateRendererCacheKey(context)
231
+ );
232
+ }
233
+
234
+ public getRenderers(context: RendererContext): IRenderer[] {
235
+ return this.getRendererCache(context)?.renderers || [];
236
+ }
237
+
238
+ public findRenderer(view: Element): IRenderer | undefined {
239
+ for (const rendererCache of this._rendererCaches) {
240
+ const renderer = rendererCache.findRenderer(view);
241
+ if (renderer) return renderer;
242
+ }
243
+ return undefined;
244
+ }
245
+
246
+ protected createRenderer(
247
+ context: RendererContext,
248
+ rendererType: RendererType = this.rendererType
249
+ ): IRenderer {
70
250
  let renderer: IRenderer;
71
251
  switch (rendererType) {
72
252
  case RendererType.WEBGL:
73
- renderer = new WebGLRenderer(
74
- this.handleWebGLFallback(context).bind(this)
75
- );
253
+ if (context.useWebCodecsDecoder) {
254
+ renderer = new WebCodecsRenderer();
255
+ } else {
256
+ renderer = new WebGLRenderer(
257
+ this.handleWebGLFallback(context).bind(this)
258
+ );
259
+ renderer.bind(context.view);
260
+ }
76
261
  break;
77
262
  case RendererType.SOFTWARE:
78
263
  renderer = new YUVCanvasRenderer();
264
+ renderer.bind(context.view);
79
265
  break;
80
266
  default:
81
267
  throw new Error('Unknown renderer type');
82
268
  }
83
269
 
84
- renderer.bind(context.view);
85
- renderer.context = {
86
- renderMode: context.renderMode,
87
- mirrorMode: context.mirrorMode,
88
- };
270
+ renderer.setContext(context);
89
271
  return renderer;
90
272
  }
91
273
 
92
- public override doRendering(rendererCache: RendererCache): void {
274
+ public startRendering(): void {
275
+ if (this._renderingTimer) return;
276
+
277
+ const renderingLooper = () => {
278
+ if (this._previousFirstFrameTime === 0) {
279
+ // Get the current time as the time of the first frame of per second
280
+ this._previousFirstFrameTime = performance.now();
281
+ // Reset the frame count
282
+ this._currentFrameCount = 0;
283
+ }
284
+
285
+ // Increase the frame count
286
+ ++this._currentFrameCount;
287
+
288
+ // Get the current time
289
+ const currentFrameTime = performance.now();
290
+ // Calculate the time difference between the current frame and the previous frame
291
+ const deltaTime = currentFrameTime - this._previousFirstFrameTime;
292
+ // Calculate the expected time of the current frame
293
+ const expectedTime = (this._currentFrameCount * 1000) / this.renderingFps;
294
+ logDebug(
295
+ new Date().toLocaleTimeString(),
296
+ 'currentFrameCount',
297
+ this._currentFrameCount,
298
+ 'expectedTime',
299
+ expectedTime,
300
+ 'deltaTime',
301
+ deltaTime
302
+ );
303
+
304
+ if (this._rendererCaches.length === 0) {
305
+ // If there is no renderer, stop rendering
306
+ this.stopRendering();
307
+ return;
308
+ }
309
+
310
+ // Render all renderers that do not use WebCodecs
311
+ for (const rendererCache of this._rendererCaches.filter(
312
+ (cache) => cache instanceof RendererCache
313
+ )) {
314
+ this.doRendering(rendererCache);
315
+ }
316
+
317
+ if (this._currentFrameCount >= this.renderingFps) {
318
+ this._previousFirstFrameTime = 0;
319
+ }
320
+
321
+ if (deltaTime < expectedTime) {
322
+ // If the time difference between the current frame and the previous frame is less than the expected time, then wait for the difference
323
+ this._renderingTimer = window.setTimeout(
324
+ renderingLooper,
325
+ expectedTime - deltaTime
326
+ );
327
+ } else {
328
+ // If the time difference between the current frame and the previous frame is greater than the expected time, then render immediately
329
+ renderingLooper();
330
+ }
331
+ };
332
+ renderingLooper();
333
+ }
334
+
335
+ public doRendering(rendererCache: RendererCacheType): void {
93
336
  rendererCache.draw();
94
337
  }
95
338
 
96
339
  private handleWebGLFallback(context: RendererContext): WebGLFallback {
97
340
  return (renderer: WebGLRenderer) => {
98
- const {
99
- context: { renderMode, mirrorMode },
100
- } = renderer;
101
341
  const renderers = this.getRenderers(context);
102
342
  renderer.unbind();
103
- const newRenderer = this.createRenderer(
104
- { ...context, renderMode, mirrorMode },
105
- RendererType.SOFTWARE
106
- );
343
+ const newRenderer = this.createRenderer(context, RendererType.SOFTWARE);
107
344
  renderers.splice(renderers.indexOf(renderer), 1, newRenderer);
108
345
  };
109
346
  }
347
+
348
+ public handleWebCodecsFallback(context: RendererCacheContext): void {
349
+ let engine = createAgoraRtcEngine();
350
+ engine.getMediaEngine().unregisterVideoEncodedFrameObserver({});
351
+ if (context.uid) {
352
+ if (isUseConnection(context)) {
353
+ engine.setRemoteVideoSubscriptionOptionsEx(
354
+ context.uid,
355
+ {
356
+ type: VideoStreamType.VideoStreamHigh,
357
+ encodedFrameOnly: false,
358
+ },
359
+ {
360
+ channelId: context.channelId,
361
+ localUid: context.localUid,
362
+ }
363
+ );
364
+ } else {
365
+ engine.setRemoteVideoSubscriptionOptions(context.uid, {
366
+ type: VideoStreamType.VideoStreamHigh,
367
+ encodedFrameOnly: false,
368
+ });
369
+ }
370
+ }
371
+ AgoraEnv.enableWebCodecsDecoder = false;
372
+ AgoraEnv.CapabilityManager?.setWebCodecsDecoderEnabled(false);
373
+ let renderers = this.getRenderers(context);
374
+ for (let renderer of renderers) {
375
+ this.addOrRemoveRenderer({
376
+ ...renderer.context,
377
+ setupMode: VideoViewSetupMode.VideoViewSetupReplace,
378
+ });
379
+ }
380
+ }
381
+
382
+ public stopRendering(): void {
383
+ if (this._renderingTimer) {
384
+ window.clearTimeout(this._renderingTimer);
385
+ this._renderingTimer = undefined;
386
+ }
387
+ }
388
+
389
+ public setRendererContext(context: RendererContext): boolean {
390
+ const checkedContext = this.presetRendererContext(context);
391
+
392
+ for (const rendererCache of this._rendererCaches) {
393
+ const result = rendererCache.setRendererContext(checkedContext);
394
+ if (result) {
395
+ return true;
396
+ }
397
+ }
398
+ return false;
399
+ }
110
400
  }
@@ -0,0 +1,145 @@
1
+ import { VideoFrame } from '../../Private/AgoraMediaBase';
2
+ import { CodecConfigInfo, RendererType } from '../../Types';
3
+ import { getContextByCanvas } from '../../Utils';
4
+ import { IRenderer } from '../IRenderer';
5
+
6
+ export class WebCodecsRenderer extends IRenderer {
7
+ gl?: WebGLRenderingContext | WebGL2RenderingContext | null;
8
+ // eslint-disable-next-line auto-import/auto-import
9
+ offscreenCanvas: OffscreenCanvas | undefined;
10
+
11
+ constructor() {
12
+ super();
13
+ this.rendererType = RendererType.WEBCODECSRENDERER;
14
+ }
15
+
16
+ static vertexShaderSource = `
17
+ attribute vec2 xy;
18
+ varying highp vec2 uv;
19
+ void main(void) {
20
+ gl_Position = vec4(xy, 0.0, 1.0);
21
+ // Map vertex coordinates (-1 to +1) to UV coordinates (0 to 1).
22
+ // UV coordinates are Y-flipped relative to vertex coordinates.
23
+ uv = vec2((1.0 + xy.x) / 2.0, (1.0 - xy.y) / 2.0);
24
+ }
25
+ `;
26
+ static fragmentShaderSource = `
27
+ varying highp vec2 uv;
28
+ uniform sampler2D texture;
29
+ void main(void) {
30
+ gl_FragColor = texture2D(texture, uv);
31
+ }
32
+ `;
33
+
34
+ bind(element: HTMLElement) {
35
+ super.bind(element);
36
+ if (!this.canvas) return;
37
+ this.offscreenCanvas = this.canvas.transferControlToOffscreen();
38
+ this.gl = getContextByCanvas(this.offscreenCanvas);
39
+ if (!this.gl) return;
40
+ const vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
41
+ if (!vertexShader) return;
42
+ this.gl.shaderSource(vertexShader, WebCodecsRenderer.vertexShaderSource);
43
+ this.gl.compileShader(vertexShader);
44
+ if (!this.gl.getShaderParameter(vertexShader, this.gl.COMPILE_STATUS)) {
45
+ throw this.gl.getShaderInfoLog(vertexShader);
46
+ }
47
+ const fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
48
+ if (!fragmentShader) return;
49
+ this.gl.shaderSource(
50
+ fragmentShader,
51
+ WebCodecsRenderer.fragmentShaderSource
52
+ );
53
+ this.gl.compileShader(fragmentShader);
54
+ if (!this.gl.getShaderParameter(fragmentShader, this.gl.COMPILE_STATUS)) {
55
+ throw this.gl.getShaderInfoLog(fragmentShader);
56
+ }
57
+ const shaderProgram = this.gl.createProgram();
58
+ if (!shaderProgram) return;
59
+ this.gl.attachShader(shaderProgram, vertexShader);
60
+ this.gl.attachShader(shaderProgram, fragmentShader);
61
+ this.gl.linkProgram(shaderProgram);
62
+ if (!this.gl.getProgramParameter(shaderProgram, this.gl.LINK_STATUS)) {
63
+ throw this.gl.getProgramInfoLog(shaderProgram);
64
+ }
65
+ this.gl.useProgram(shaderProgram);
66
+ // Vertex coordinates, clockwise from bottom-left.
67
+ const vertexBuffer = this.gl.createBuffer();
68
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, vertexBuffer);
69
+ this.gl.bufferData(
70
+ this.gl.ARRAY_BUFFER,
71
+ new Float32Array([-1.0, -1.0, -1.0, +1.0, +1.0, +1.0, +1.0, -1.0]),
72
+ this.gl.STATIC_DRAW
73
+ );
74
+ const xyLocation = this.gl.getAttribLocation(shaderProgram, 'xy');
75
+ this.gl.vertexAttribPointer(xyLocation, 2, this.gl.FLOAT, false, 0, 0);
76
+ this.gl.enableVertexAttribArray(xyLocation);
77
+ // Create one texture to upload frames to.
78
+ const texture = this.gl.createTexture();
79
+ this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
80
+ this.gl.texParameteri(
81
+ this.gl.TEXTURE_2D,
82
+ this.gl.TEXTURE_MAG_FILTER,
83
+ this.gl.NEAREST
84
+ );
85
+ this.gl.texParameteri(
86
+ this.gl.TEXTURE_2D,
87
+ this.gl.TEXTURE_MIN_FILTER,
88
+ this.gl.NEAREST
89
+ );
90
+ this.gl.texParameteri(
91
+ this.gl.TEXTURE_2D,
92
+ this.gl.TEXTURE_WRAP_S,
93
+ this.gl.CLAMP_TO_EDGE
94
+ );
95
+ this.gl.texParameteri(
96
+ this.gl.TEXTURE_2D,
97
+ this.gl.TEXTURE_WRAP_T,
98
+ this.gl.CLAMP_TO_EDGE
99
+ );
100
+ }
101
+
102
+ drawFrame(frame: any, _codecConfig: CodecConfigInfo) {
103
+ if (!this.offscreenCanvas || !frame) return;
104
+
105
+ this.offscreenCanvas.width = _codecConfig.codedWidth!;
106
+ this.offscreenCanvas.height = _codecConfig.codedHeight!;
107
+
108
+ this.updateRenderMode();
109
+ this.rotateCanvas({
110
+ rotation: _codecConfig.rotation,
111
+ });
112
+ if (!this.gl) return;
113
+
114
+ if (this.gl) {
115
+ // Upload the frame.
116
+ this.gl.texImage2D(
117
+ this.gl.TEXTURE_2D,
118
+ 0,
119
+ this.gl.RGBA,
120
+ this.gl.RGBA,
121
+ this.gl.UNSIGNED_BYTE,
122
+ frame
123
+ );
124
+ frame.close();
125
+ // Configure and clear the drawing area.
126
+ this.gl.viewport(
127
+ 0,
128
+ 0,
129
+ this.gl.drawingBufferWidth,
130
+ this.gl.drawingBufferHeight
131
+ );
132
+ this.gl.clearColor(0.0, 0.0, 0.0, 0.0);
133
+ this.gl.clear(this.gl.COLOR_BUFFER_BIT);
134
+ // Draw the frame.
135
+ this.gl.drawArrays(this.gl.TRIANGLE_FAN, 0, 4);
136
+ }
137
+ super.drawFrame();
138
+ this.getFps();
139
+ }
140
+
141
+ protected override rotateCanvas({ rotation }: VideoFrame) {
142
+ if (!this.offscreenCanvas || !this.canvas) return;
143
+ this.canvas.style.transform += ` rotateZ(${rotation}deg)`;
144
+ }
145
+ }