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,8 +1,8 @@
1
1
  const path = require('path');
2
2
 
3
- const download = require('download');
4
-
5
3
  const { destIrisSDKDir, cleanDir, destNativeSDKDir } = require('./clean');
4
+ const download = require('./download');
5
+
6
6
  const getConfig = require('./getConfig');
7
7
  const logger = require('./logger');
8
8
  const { getOS, moveFile, getIrisStandAlone } = require('./util');
@@ -0,0 +1,92 @@
1
+ //@ts-ignore
2
+ import { BrowserWindow } from 'electron';
3
+
4
+ /**
5
+ * @ignore
6
+ */
7
+
8
+ export type VideoDecodeAcceleratorSupportedProfile = {
9
+ codec: string;
10
+ minWidth: number;
11
+ maxWidth: number;
12
+ minHeight: number;
13
+ maxHeight: number;
14
+ };
15
+
16
+ /**
17
+ * @ignore
18
+ */
19
+ export class GpuInfo {
20
+ videoDecodeAcceleratorSupportedProfile: VideoDecodeAcceleratorSupportedProfile[] =
21
+ [];
22
+ }
23
+
24
+ /**
25
+ * @ignore
26
+ */
27
+ export const getGpuInfoInternal = (callback: any): void => {
28
+ //@ts-ignore
29
+ if (process.type === 'renderer') {
30
+ console.error('getGpuInfoInternal should be called in main process');
31
+ return;
32
+ }
33
+ const gpuPage = new BrowserWindow({
34
+ show: false,
35
+ webPreferences: { offscreen: true },
36
+ });
37
+ gpuPage.loadURL('chrome://gpu');
38
+ let executeJavaScriptText =
39
+ `` +
40
+ `let videoAccelerationInfo = [];` +
41
+ `let nodeList = document.querySelector('info-view')?.shadowRoot?.querySelector('#video-acceleration-info info-view-table')?.shadowRoot?.querySelectorAll('#info-view-table info-view-table-row') || [];` +
42
+ `for (node of nodeList) {` +
43
+ ` videoAccelerationInfo.push({` +
44
+ ` title: node.shadowRoot.querySelector('#title')?.innerText,` +
45
+ ` value: node.shadowRoot.querySelector('#value')?.innerText,` +
46
+ ` })` +
47
+ `}` +
48
+ `JSON.stringify(videoAccelerationInfo)`;
49
+ gpuPage.webContents
50
+ .executeJavaScript(executeJavaScriptText)
51
+ .then((result: string) => {
52
+ if (!result) {
53
+ console.error(
54
+ 'Failed to get GPU info, chrome://gpu is not available in this environment.'
55
+ );
56
+ }
57
+ let filterResult: { title: string; value: string }[] = JSON.parse(
58
+ result
59
+ ).filter((item: any) => {
60
+ return item.title.indexOf('Decode') !== -1;
61
+ });
62
+ let convertResult: VideoDecodeAcceleratorSupportedProfile[] = [];
63
+ const resolutionPattern = /(\d+)x(\d+) to (\d+)x(\d+)/;
64
+ for (const profile of filterResult) {
65
+ const match = profile.value.match(resolutionPattern);
66
+ if (!match) {
67
+ continue;
68
+ }
69
+
70
+ const [_resolution, minWidth, minHeight, maxWidth, maxHeight] = match;
71
+
72
+ convertResult.push({
73
+ codec: profile.title,
74
+ minWidth: minWidth ? Number(minWidth) : 0,
75
+ maxWidth: maxWidth ? Number(maxWidth) : 0,
76
+ minHeight: minHeight ? Number(minHeight) : 0,
77
+ maxHeight: maxHeight ? Number(maxHeight) : 0,
78
+ });
79
+ }
80
+ typeof callback === 'function' && callback(convertResult);
81
+ })
82
+ .catch((error: any) => {
83
+ console.error(
84
+ 'Failed to get GPU info, please import agora-electron-sdk in main process',
85
+ error
86
+ );
87
+ typeof callback === 'function' && callback(error);
88
+ })
89
+ .finally(() => {
90
+ gpuPage.close();
91
+ });
92
+ };
@@ -0,0 +1,206 @@
1
+ import { EncodedVideoFrameInfo, VideoFrameType } from '../Private/AgoraBase';
2
+
3
+ import { WebCodecsRenderer } from '../Renderer/WebCodecsRenderer/index';
4
+ import { CodecConfigInfo, RendererCacheContext, RendererType } from '../Types';
5
+ import { AgoraEnv, logDebug, logInfo } from '../Utils';
6
+
7
+ const frameTypeMapping = {
8
+ [VideoFrameType.VideoFrameTypeDeltaFrame]: 'delta',
9
+ [VideoFrameType.VideoFrameTypeKeyFrame]: 'key',
10
+ [VideoFrameType.VideoFrameTypeDroppableFrame]: 'delta', // this is a workaround for the issue that the frameType is not correct
11
+ };
12
+
13
+ export class WebCodecsDecoder {
14
+ private _decoder: VideoDecoder;
15
+ private renderers: WebCodecsRenderer[] = [];
16
+ private _cacheContext: RendererCacheContext;
17
+ // eslint-disable-next-line auto-import/auto-import
18
+ private pendingFrame: VideoFrame | null = null;
19
+ private _currentCodecConfig: CodecConfigInfo | null = null;
20
+
21
+ private _base_ts = 0;
22
+ private _base_ts_ntp = 1;
23
+ private _last_ts_ntp = 1;
24
+ private _decode_retry_count = 0;
25
+
26
+ constructor(
27
+ renders: WebCodecsRenderer[],
28
+ onError: (e: any) => void,
29
+ context: RendererCacheContext
30
+ ) {
31
+ this.renderers = renders;
32
+ this._cacheContext = context;
33
+ this._decoder = new VideoDecoder({
34
+ // @ts-ignore
35
+ output: this._output.bind(this),
36
+ error: (e) => {
37
+ onError(e);
38
+ },
39
+ });
40
+ }
41
+
42
+ // eslint-disable-next-line auto-import/auto-import
43
+ _output(frame: VideoFrame) {
44
+ // Schedule the frame to be rendered.
45
+ this._renderFrame(frame);
46
+ }
47
+
48
+ // eslint-disable-next-line auto-import/auto-import
49
+ private _renderFrame(frame: VideoFrame) {
50
+ if (!this.pendingFrame) {
51
+ // Schedule rendering in the next animation frame.
52
+ // eslint-disable-next-line auto-import/auto-import
53
+ requestAnimationFrame(this.renderAnimationFrame.bind(this));
54
+ } else {
55
+ // Close the current pending frame before replacing it.
56
+ this.pendingFrame.close();
57
+ }
58
+ // Set or replace the pending frame.
59
+ this.pendingFrame = frame;
60
+ }
61
+
62
+ renderAnimationFrame() {
63
+ for (let renderer of this.renderers) {
64
+ if (renderer.rendererType !== RendererType.WEBCODECSRENDERER) {
65
+ continue;
66
+ }
67
+ renderer.drawFrame(this.pendingFrame, this._currentCodecConfig!);
68
+ this.pendingFrame = null;
69
+ }
70
+ }
71
+
72
+ decoderConfigure(frameInfo: EncodedVideoFrameInfo) {
73
+ this.pendingFrame = null;
74
+ // @ts-ignore
75
+ let codec =
76
+ AgoraEnv.CapabilityManager?.frameCodecMapping[frameInfo.codecType!]
77
+ ?.codec;
78
+ if (!codec) {
79
+ AgoraEnv.AgoraRendererManager?.handleWebCodecsFallback(
80
+ this._cacheContext
81
+ );
82
+ throw new Error(
83
+ `codec ${frameInfo.codecType} is not in frameCodecMapping,failed to configure decoder, fallback to native decoder`
84
+ );
85
+ }
86
+ this._currentCodecConfig = {
87
+ codecType: frameInfo.codecType,
88
+ codedWidth: frameInfo.width,
89
+ codedHeight: frameInfo.height,
90
+ rotation: frameInfo.rotation,
91
+ };
92
+ this._decoder!.configure({
93
+ codec: codec,
94
+ codedWidth: frameInfo.width,
95
+ codedHeight: frameInfo.height,
96
+ });
97
+ logInfo(
98
+ `configure decoder: codedWidth: ${frameInfo.width}, codedHeight: ${frameInfo.height},codec: ${codec}`
99
+ );
100
+ }
101
+
102
+ updateTimestamps(ts: number) {
103
+ if (this._base_ts !== 0) {
104
+ if (ts > this._base_ts) {
105
+ this._last_ts_ntp =
106
+ this._base_ts_ntp + Math.floor(((ts - this._base_ts) * 1000) / 90);
107
+ } else {
108
+ this._base_ts = ts;
109
+ this._last_ts_ntp++;
110
+ this._base_ts_ntp = this._last_ts_ntp;
111
+ }
112
+ } else {
113
+ this._base_ts = ts;
114
+ this._last_ts_ntp = 1;
115
+ }
116
+ }
117
+
118
+ handleCodecIsChanged(frameInfo: EncodedVideoFrameInfo) {
119
+ if (
120
+ this._currentCodecConfig?.codecType !== frameInfo.codecType ||
121
+ this._currentCodecConfig?.codedWidth !== frameInfo.width ||
122
+ this._currentCodecConfig?.codedHeight !== frameInfo.height ||
123
+ this._currentCodecConfig?.rotation !== frameInfo.rotation
124
+ ) {
125
+ logInfo('frameInfo has changed, reconfigure decoder');
126
+ this._decoder.reset();
127
+ this.decoderConfigure(frameInfo);
128
+ }
129
+ }
130
+
131
+ // @ts-ignore
132
+ decodeFrame(
133
+ imageBuffer: Uint8Array,
134
+ frameInfo: EncodedVideoFrameInfo,
135
+ ts: number
136
+ ) {
137
+ try {
138
+ this.handleCodecIsChanged(frameInfo);
139
+ } catch (error: any) {
140
+ logInfo(error);
141
+ return;
142
+ }
143
+
144
+ if (!imageBuffer) {
145
+ logDebug('imageBuffer is empty, skip decode frame');
146
+ return;
147
+ }
148
+
149
+ let frameType: string | undefined;
150
+ if (frameInfo.frameType !== undefined) {
151
+ // @ts-ignore
152
+ frameType = frameTypeMapping[frameInfo.frameType];
153
+ }
154
+ if (!frameType) {
155
+ logDebug('frameType is not in frameTypeMapping, skip decode frame');
156
+ return;
157
+ }
158
+
159
+ this.updateTimestamps(ts);
160
+
161
+ try {
162
+ this._decoder.decode(
163
+ new EncodedVideoChunk({
164
+ data: imageBuffer,
165
+ timestamp: this._last_ts_ntp,
166
+ // @ts-ignore
167
+ type: frameType,
168
+ // @ts-ignore
169
+ transfer: [imageBuffer.buffer],
170
+ })
171
+ );
172
+ this._decode_retry_count = 0;
173
+ } catch (e) {
174
+ /// There are some cases that the decoder failed to decode the frame, we will retry for a few times
175
+ /// If the decoder still failed to decode the frame, we will fallback to native decoder
176
+ /// The retry count is defined in AgoraEnv.maxDecodeRetryCount
177
+ /// The retry count will be reset to 0 when the decoder successfully decode a frame
178
+ if (this._decode_retry_count >= AgoraEnv.maxDecodeRetryCount) {
179
+ AgoraEnv.AgoraRendererManager?.handleWebCodecsFallback(
180
+ this._cacheContext
181
+ );
182
+ throw new Error(
183
+ `failed to decode frame over ${this._decode_retry_count} times, fallback to native decoder`
184
+ );
185
+ }
186
+ this._decode_retry_count++;
187
+ }
188
+ }
189
+
190
+ reset() {
191
+ this._base_ts = 0;
192
+ this._base_ts_ntp = 1;
193
+ this._last_ts_ntp = 1;
194
+ this._decoder.reset();
195
+ }
196
+
197
+ release() {
198
+ try {
199
+ if (this.pendingFrame) {
200
+ this.pendingFrame.close();
201
+ }
202
+ this._decoder.close();
203
+ } catch (e) {}
204
+ this.pendingFrame = null;
205
+ }
206
+ }
@@ -148,6 +148,10 @@ export enum WarnCodeType {
148
148
  * @ignore
149
149
  */
150
150
  WarnAdmImproperSettings = 1053,
151
+ /**
152
+ * @ignore
153
+ */
154
+ WarnAdmPopState = 1055,
151
155
  /**
152
156
  * @ignore
153
157
  */
@@ -1985,7 +1989,7 @@ export enum AudioProfileType {
1985
1989
  */
1986
1990
  AudioProfileMusicHighQualityStereo = 5,
1987
1991
  /**
1988
- * 6: A sample rate of 16 kHz, audio encoding, mono, and Acoustic Echo Cancellation (AES) enabled.
1992
+ * 6: A sample rate of 16 kHz, audio encoding, mono, and Acoustic Echo Cancellation (AEC) enabled.
1989
1993
  */
1990
1994
  AudioProfileIot = 6,
1991
1995
  /**
@@ -2018,10 +2022,18 @@ export enum AudioScenarioType {
2018
2022
  * 8: Meeting scenario that mainly contains the human voice.
2019
2023
  */
2020
2024
  AudioScenarioMeeting = 8,
2025
+ /**
2026
+ * @ignore
2027
+ */
2028
+ AudioScenarioAiServer = 9,
2029
+ /**
2030
+ * 10: AI conversation scenario, which is only applicable to scenarios where the user interacts with the conversational AI agent created by.
2031
+ */
2032
+ AudioScenarioAiClient = 10,
2021
2033
  /**
2022
2034
  * The number of enumerations.
2023
2035
  */
2024
- AudioScenarioNum = 9,
2036
+ AudioScenarioNum = 11,
2025
2037
  }
2026
2038
 
2027
2039
  /**
@@ -2397,7 +2409,7 @@ export enum LocalVideoStreamReason {
2397
2409
  */
2398
2410
  LocalVideoStreamReasonScreenCaptureResumed = 29,
2399
2411
  /**
2400
- * 30: The displayer used for screen capture is disconnected.
2412
+ * 30: The displayer used for screen capture is disconnected. The current screen sharing has been paused. Prompt the user to restart the screen sharing.
2401
2413
  */
2402
2414
  LocalVideoStreamReasonScreenCaptureDisplayDisconnected = 30,
2403
2415
  }
@@ -3190,7 +3202,9 @@ export class TranscodingVideoStream {
3190
3202
  */
3191
3203
  remoteUserUid?: number;
3192
3204
  /**
3193
- * The URL of the image. Use this parameter only when the source type is the image for local video mixing.
3205
+ * The file path of local images. Use this parameter only when the source type is the image for local video mixing. Examples:
3206
+ * macOS: ~/Pictures/image.png
3207
+ * Windows: C:\\Users\\{username}\\Pictures\\image.png
3194
3208
  */
3195
3209
  imageUrl?: string;
3196
3210
  /**
@@ -1646,7 +1646,7 @@ export interface IRtcEngineEventHandler {
1646
1646
  /**
1647
1647
  * Occurs when the remote video stream state changes.
1648
1648
  *
1649
- * This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 17.
1649
+ * This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 32.
1650
1650
  *
1651
1651
  * @param connection The connection information. See RtcConnection.
1652
1652
  * @param remoteUid The ID of the remote user whose video state changes.
@@ -1685,7 +1685,7 @@ export interface IRtcEngineEventHandler {
1685
1685
  * Occurs when a remote user (in the communication profile)/ host (in the live streaming profile) joins the channel.
1686
1686
  *
1687
1687
  * In a communication channel, this callback indicates that a remote user joins the channel. The SDK also triggers this callback to report the existing users in the channel when a user joins the channel.
1688
- * In a live-broadcast channel, this callback indicates that a host joins the channel. The SDK also triggers this callback to report the existing hosts in the channel when a host joins the channel. Agora recommends limiting the number of hosts to 17.
1688
+ * In a live-broadcast channel, this callback indicates that a host joins the channel. The SDK also triggers this callback to report the existing hosts in the channel when a host joins the channel. Agora recommends limiting the number of co-hosts to 32, with a maximum of 17 video hosts.
1689
1689
  *
1690
1690
  * @param connection The connection information. See RtcConnection.
1691
1691
  * @param remoteUid The ID of the user or host who joins the channel.
@@ -1717,7 +1717,7 @@ export interface IRtcEngineEventHandler {
1717
1717
  /**
1718
1718
  * Occurs when a remote user (in the communication profile) or a host (in the live streaming profile) stops/resumes sending the audio stream.
1719
1719
  *
1720
- * The SDK triggers this callback when the remote user stops or resumes sending the audio stream by calling the muteLocalAudioStream method. This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 17.
1720
+ * The SDK triggers this callback when the remote user stops or resumes sending the audio stream by calling the muteLocalAudioStream method. This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 32.
1721
1721
  *
1722
1722
  * @param connection The connection information. See RtcConnection.
1723
1723
  * @param remoteUid The user ID.
@@ -1732,7 +1732,7 @@ export interface IRtcEngineEventHandler {
1732
1732
  /**
1733
1733
  * Occurs when a remote user stops or resumes publishing the video stream.
1734
1734
  *
1735
- * When a remote user calls muteLocalVideoStream to stop or resume publishing the video stream, the SDK triggers this callback to report to the local user the state of the streams published by the remote user. This callback can be inaccurate when the number of users (in the communication profile) or hosts (in the live streaming profile) in a channel exceeds 17.
1735
+ * When a remote user calls muteLocalVideoStream to stop or resume publishing the video stream, the SDK triggers this callback to report to the local user the state of the streams published by the remote user. This callback can be inaccurate when the number of users (in the communication profile) or hosts (in the live streaming profile) in a channel exceeds 32.
1736
1736
  *
1737
1737
  * @param connection The connection information. See RtcConnection.
1738
1738
  * @param remoteUid The user ID of the remote user.
@@ -2070,7 +2070,7 @@ export interface IRtcEngineEventHandler {
2070
2070
  /**
2071
2071
  * Occurs when the remote audio state changes.
2072
2072
  *
2073
- * When the audio state of a remote user (in a voice/video call channel) or host (in a live streaming channel) changes, the SDK triggers this callback to report the current state of the remote audio stream. This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 17.
2073
+ * When the audio state of a remote user (in a voice/video call channel) or host (in a live streaming channel) changes, the SDK triggers this callback to report the current state of the remote audio stream. This callback does not work properly when the number of users (in the communication profile) or hosts (in the live streaming channel) in a channel exceeds 32.
2074
2074
  *
2075
2075
  * @param connection The connection information. See RtcConnection.
2076
2076
  * @param remoteUid The ID of the remote user whose audio state changes.
@@ -5228,7 +5228,6 @@ export abstract class IRtcEngine {
5228
5228
  * Enables loopback audio capturing.
5229
5229
  *
5230
5230
  * If you enable loopback audio capturing, the output of the sound card is mixed into the audio stream sent to the other end.
5231
- * The macOS system's default sound card does not support recording functionality. As of v4.5.0, when you call this method for the first time, the SDK will automatically install the built-in AgoraALD virtual sound card developed by Agora. After successful installation, the audio routing will automatically switch to the virtual sound card and use it for audio capturing.
5232
5231
  * You can call this method either before or after joining a channel.
5233
5232
  * If you call the disableAudio method to disable the audio module, audio capturing will be disabled as well. If you need to enable audio capturing, call the enableAudio method to enable the audio module and then call the enableLoopbackRecording method.
5234
5233
  *
@@ -6681,7 +6680,7 @@ export abstract class IRtcEngine {
6681
6680
  * Enables tracing the video frame rendering process.
6682
6681
  *
6683
6682
  * The SDK starts tracing the rendering status of the video frames in the channel from the moment this method is successfully called and reports information about the event through the onVideoRenderingTracingResult callback.
6684
- * By default, the SDK starts tracing the video rendering event automatically when the local user successfully joins the channel. You can call this method at an appropriate time according to the actual application scenario to customize the tracing process.
6683
+ * The SDK automatically starts tracking the rendering events of the video from the moment that you call joinChannel to join the channel. You can call this method at an appropriate time according to the actual application scenario to customize the tracing process.
6685
6684
  * After the local user leaves the current channel, the SDK automatically resets the time point to the next time when the user successfully joins the channel.
6686
6685
  *
6687
6686
  * @returns
@@ -145,6 +145,7 @@ export abstract class IRtcEngineEx extends IRtcEngine {
145
145
  * Initializes the video view of a remote user.
146
146
  *
147
147
  * This method initializes the video view of a remote stream on the local device. It affects only the video view that the local user sees. Call this method to bind the remote video stream to a video view and to set the rendering and mirror modes of the video view. The application specifies the uid of the remote video in the VideoCanvas method before the remote user joins the channel. If the remote uid is unknown to the application, set it after the application receives the onUserJoined callback. If the Video Recording function is enabled, the Video Recording Service joins the channel as a dummy client, causing other clients to also receive the onUserJoined callback. Do not bind the dummy client to the application view because the dummy client does not send any video streams. To unbind the remote user from the view, set the view parameter to NULL. Once the remote user leaves the channel, the SDK unbinds the remote user.
148
+ * Call this method after joinChannelEx.
148
149
  * To update the rendering or mirror mode of the remote video view during a call, use the setRemoteRenderModeEx method.
149
150
  *
150
151
  * @param canvas The remote video view settings. See VideoCanvas.
@@ -929,7 +930,7 @@ export abstract class IRtcEngineEx extends IRtcEngine {
929
930
  /**
930
931
  * Enables tracing the video frame rendering process.
931
932
  *
932
- * By default, the SDK starts tracing the video rendering event automatically when the local user successfully joins the channel. You can call this method at an appropriate time according to the actual application scenario to customize the tracing process.
933
+ * The SDK automatically starts tracking the rendering events of the video from the moment that you call joinChannel to join the channel. You can call this method at an appropriate time according to the actual application scenario to customize the tracing process.
933
934
  * After the local user leaves the current channel, the SDK automatically resets the time point to the next time when the user successfully joins the channel. The SDK starts tracing the rendering status of the video frames in the channel from the moment this method is successfully called and reports information about the event through the onVideoRenderingTracingResult callback.
934
935
  *
935
936
  * @param connection The connection information. See RtcConnection.
@@ -1 +1,14 @@
1
- export {};
1
+ import '../AgoraBase';
2
+
3
+ declare module '../AgoraBase' {
4
+ interface VideoCanvas {
5
+ /**
6
+ * @ignore
7
+ */
8
+ useWebCodecsDecoder?: boolean;
9
+ /**
10
+ * @ignore
11
+ */
12
+ enableFps?: boolean;
13
+ }
14
+ }
@@ -2,6 +2,8 @@ import EventEmitter from 'eventemitter3';
2
2
  import JSONBigInt from 'json-bigint';
3
3
  const JSON = JSONBigInt({ storeAsString: true });
4
4
 
5
+ import createAgoraRtcEngine from '../../AgoraSdk';
6
+ import { IAgoraElectronBridge } from '../../Types';
5
7
  import { AgoraEnv, logDebug, logError, logInfo, logWarn } from '../../Utils';
6
8
  import { IAudioEncodedFrameObserver } from '../AgoraBase';
7
9
  import {
@@ -67,8 +69,11 @@ import { RtcEngineExInternal } from './RtcEngineExInternal';
67
69
  // @ts-ignore
68
70
  export const DeviceEventEmitter: EventEmitter = new EventEmitter();
69
71
 
70
- const AgoraRtcNg = AgoraEnv.AgoraElectronBridge;
71
- AgoraRtcNg.OnEvent('call_back_with_buffer', (...params: any) => {
72
+ const AgoraNode = require('../../../build/Release/agora_node_ext');
73
+ export const AgoraElectronBridge: IAgoraElectronBridge =
74
+ new AgoraNode.AgoraElectronBridge();
75
+
76
+ AgoraElectronBridge.OnEvent('call_back_with_buffer', (...params: any) => {
72
77
  try {
73
78
  handleEvent(...params);
74
79
  } catch (e) {
@@ -441,7 +446,7 @@ function handleEvent(...[event, data, buffers]: any) {
441
446
  export function callIrisApi(funcName: string, params: any): any {
442
447
  try {
443
448
  const buffers: Uint8Array[] = [];
444
-
449
+ const rtcEngine = createAgoraRtcEngine();
445
450
  if (funcName.startsWith('MediaEngine_')) {
446
451
  switch (funcName) {
447
452
  case 'MediaEngine_pushAudioFrame_c71f4ab':
@@ -485,16 +490,16 @@ export function callIrisApi(funcName: string, params: any): any {
485
490
  } else if (funcName.startsWith('RtcEngine_')) {
486
491
  switch (funcName) {
487
492
  case 'RtcEngine_initialize_0320339':
488
- AgoraRtcNg.InitializeEnv();
493
+ AgoraElectronBridge.InitializeEnv();
489
494
  break;
490
495
  case 'RtcEngine_release':
491
- AgoraRtcNg.CallApi(
496
+ AgoraElectronBridge.CallApi(
492
497
  funcName,
493
498
  JSON.stringify(params),
494
499
  buffers,
495
500
  buffers.length
496
501
  );
497
- AgoraRtcNg.ReleaseEnv();
502
+ AgoraElectronBridge.ReleaseEnv();
498
503
  return;
499
504
  case 'RtcEngine_sendMetaData':
500
505
  // metadata.buffer
@@ -522,8 +527,17 @@ export function callIrisApi(funcName: string, params: any): any {
522
527
  break;
523
528
  }
524
529
  }
530
+ if (funcName.indexOf('joinChannel') != -1) {
531
+ if (AgoraEnv.CapabilityManager?.webCodecsDecoderEnabled) {
532
+ rtcEngine.getMediaEngine().registerVideoEncodedFrameObserver({});
533
+ }
534
+ } else if (funcName.indexOf('leaveChannel') != -1) {
535
+ if (AgoraEnv.CapabilityManager?.webCodecsDecoderEnabled) {
536
+ rtcEngine.getMediaEngine().unregisterVideoEncodedFrameObserver({});
537
+ }
538
+ }
525
539
 
526
- let { callApiReturnCode, callApiResult } = AgoraRtcNg.CallApi(
540
+ let { callApiReturnCode, callApiResult } = AgoraElectronBridge.CallApi(
527
541
  funcName,
528
542
  JSON.stringify(params),
529
543
  buffers,
@@ -1,11 +1,18 @@
1
1
  import { createCheckers } from 'ts-interface-checker';
2
2
 
3
+ import { AgoraElectronBridge } from '../../Private/internal/IrisApiEngine';
3
4
  import { AgoraEnv, logError, parseIntPtr2Number } from '../../Utils';
5
+ import { VideoEncoderConfiguration } from '../AgoraBase';
4
6
  let RendererManager: any;
5
- if (typeof window !== 'undefined') {
7
+ let CapabilityManager: any;
8
+ //@ts-ignore
9
+ if (process.type === 'renderer') {
6
10
  RendererManager = require('../../Renderer/RendererManager').RendererManager;
11
+ CapabilityManager =
12
+ require('../../Renderer/CapabilityManager').CapabilityManager;
7
13
  } else {
8
14
  RendererManager = undefined;
15
+ CapabilityManager = undefined;
9
16
  }
10
17
  import {
11
18
  AudioEncodedFrameObserverConfig,
@@ -92,22 +99,25 @@ export class RtcEngineExInternal extends IRtcEngineExImpl {
92
99
  private _h265_transcoder: IH265Transcoder = new H265TranscoderInternal();
93
100
 
94
101
  override initialize(context: RtcEngineContext): number {
102
+ const ret = super.initialize(context);
103
+ callIrisApi.call(this, 'RtcEngine_setAppType', {
104
+ appType: 3,
105
+ });
95
106
  if (AgoraEnv.webEnvReady) {
96
107
  // @ts-ignore
97
108
  window.AgoraEnv = AgoraEnv;
98
109
  if (AgoraEnv.AgoraRendererManager === undefined && RendererManager) {
99
110
  AgoraEnv.AgoraRendererManager = new RendererManager();
100
111
  }
112
+ if (AgoraEnv.CapabilityManager === undefined && CapabilityManager) {
113
+ AgoraEnv.CapabilityManager = new CapabilityManager();
114
+ }
101
115
  }
102
- const ret = super.initialize(context);
103
- callIrisApi.call(this, 'RtcEngine_setAppType', {
104
- appType: 3,
105
- });
106
116
  return ret;
107
117
  }
108
118
 
109
119
  override release(sync: boolean = false) {
110
- AgoraEnv.AgoraElectronBridge.ReleaseRenderer();
120
+ AgoraElectronBridge.ReleaseRenderer();
111
121
  AgoraEnv.AgoraRendererManager?.release();
112
122
  AgoraEnv.AgoraRendererManager = undefined;
113
123
  this._media_engine.release();
@@ -127,6 +137,8 @@ export class RtcEngineExInternal extends IRtcEngineExImpl {
127
137
  MediaRecorderInternal._observers.clear();
128
138
  this._h265_transcoder.release();
129
139
  this.removeAllListeners();
140
+ AgoraEnv.CapabilityManager?.release();
141
+ AgoraEnv.CapabilityManager = undefined;
130
142
  super.release(sync);
131
143
  }
132
144
 
@@ -326,6 +338,13 @@ export class RtcEngineExInternal extends IRtcEngineExImpl {
326
338
  return super.unregisterMediaMetadataObserver(observer, type);
327
339
  }
328
340
 
341
+ override setVideoEncoderConfiguration(
342
+ config: VideoEncoderConfiguration
343
+ ): number {
344
+ AgoraEnv.encodeAlpha = config?.advanceOptions?.encodeAlpha ?? false;
345
+ return super.setVideoEncoderConfiguration(config);
346
+ }
347
+
329
348
  protected override getApiTypeFromJoinChannel(
330
349
  token: string,
331
350
  channelId: string,
@@ -502,7 +521,7 @@ export class RtcEngineExInternal extends IRtcEngineExImpl {
502
521
  if (!value.thumbImage?.buffer || !value.thumbImage?.length) {
503
522
  value.thumbImage!.buffer = undefined;
504
523
  } else {
505
- value.thumbImage!.buffer = AgoraEnv.AgoraElectronBridge.GetBuffer(
524
+ value.thumbImage!.buffer = AgoraElectronBridge.GetBuffer(
506
525
  value.thumbImage!.buffer as unknown as number,
507
526
  value.thumbImage.length!
508
527
  );
@@ -510,7 +529,7 @@ export class RtcEngineExInternal extends IRtcEngineExImpl {
510
529
  if (!value.iconImage?.buffer || !value.iconImage?.length) {
511
530
  value.iconImage!.buffer = undefined;
512
531
  } else {
513
- value.iconImage.buffer = AgoraEnv.AgoraElectronBridge.GetBuffer(
532
+ value.iconImage.buffer = AgoraElectronBridge.GetBuffer(
514
533
  value.iconImage!.buffer as unknown as number,
515
534
  value.iconImage.length!
516
535
  );
@@ -0,0 +1,22 @@
1
+ //@ts-ignore
2
+ import { app, ipcMain } from 'electron';
3
+
4
+ import { getGpuInfoInternal } from '../../Decoder/gpu-utils';
5
+
6
+ import { IPCMessageType } from '../../Types';
7
+ //@ts-ignore
8
+ if (process.type !== 'renderer') {
9
+ ipcMain.handle(IPCMessageType.AGORA_IPC_GET_GPU_INFO, () => {
10
+ return new Promise((resolve) => {
11
+ getGpuInfoInternal((result: any) => {
12
+ resolve(result);
13
+ });
14
+ });
15
+ });
16
+ console.log('[agora-electron] main process AgoraIPCMain handler registered');
17
+
18
+ app.on('quit', () => {
19
+ ipcMain.removeHandler(IPCMessageType.AGORA_IPC_GET_GPU_INFO);
20
+ console.log('[agora-electron] main process AgoraIPCMain handler removed');
21
+ });
22
+ }
@@ -0,0 +1,21 @@
1
+ //@ts-ignore
2
+ import { ipcRenderer } from 'electron';
3
+
4
+ import { IPCMessageType } from '../../Types';
5
+ import { logError } from '../../Utils';
6
+
7
+ export async function ipcSend(
8
+ channel: IPCMessageType,
9
+ ...args: any[]
10
+ ): Promise<any> {
11
+ if (!Object.values(IPCMessageType).includes(channel)) {
12
+ logError('Invalid IPCMessageType');
13
+ return;
14
+ }
15
+ //@ts-ignore
16
+ if (process.type === 'renderer') {
17
+ return await ipcRenderer.invoke(channel, ...args);
18
+ } else {
19
+ logError('Not in renderer process, cannot send ipc message');
20
+ }
21
+ }