tirtc-react-native 2.3.1 → 2.4.0-alpha.4

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 (77) hide show
  1. package/README.md +23 -7
  2. package/TiRtcReactNative.podspec +1 -1
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/AndroidManifest.xml +2 -1
  5. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcModule.kt +622 -28
  6. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeCleanup.kt +115 -0
  7. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventBinder.kt +32 -8
  8. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventSink.kt +16 -0
  9. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMaps.kt +62 -0
  10. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeModuleSupport.kt +6 -6
  11. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeRegistry.kt +27 -0
  12. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeStorageSupport.kt +64 -0
  13. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcVideoOutputViewManager.kt +17 -2
  14. package/ios/TiRtcReactNative+Conn.mm +173 -2
  15. package/ios/TiRtcReactNative+Input.mm +18 -4
  16. package/ios/TiRtcReactNative+Output.mm +41 -4
  17. package/ios/TiRtcReactNative+Private.h +4 -0
  18. package/ios/TiRtcReactNative+Storage.mm +513 -0
  19. package/ios/TiRtcReactNative.h +2 -1
  20. package/ios/TiRtcReactNative.mm +109 -1
  21. package/ios/TiRtcReactNativeEventBinder.h +8 -0
  22. package/ios/TiRtcReactNativeEventBinder.mm +102 -0
  23. package/ios/TiRtcReactNativeEventSink.h +3 -0
  24. package/ios/TiRtcReactNativeEventSink.mm +16 -0
  25. package/ios/TiRtcReactNativeRegistry.h +5 -0
  26. package/ios/TiRtcReactNativeRegistry.mm +43 -0
  27. package/ios/TiRtcVideoOutputView.mm +11 -0
  28. package/lib/module/audio_input.js +33 -17
  29. package/lib/module/audio_input.js.map +1 -1
  30. package/lib/module/audio_output.js +27 -10
  31. package/lib/module/audio_output.js.map +1 -1
  32. package/lib/module/conn.js +31 -7
  33. package/lib/module/conn.js.map +1 -1
  34. package/lib/module/errors.js +4 -3
  35. package/lib/module/errors.js.map +1 -1
  36. package/lib/module/index.js +2 -0
  37. package/lib/module/index.js.map +1 -1
  38. package/lib/module/logging.js +9 -1
  39. package/lib/module/logging.js.map +1 -1
  40. package/lib/module/logging_internal.js +27 -0
  41. package/lib/module/logging_internal.js.map +1 -0
  42. package/lib/module/recording.js +210 -0
  43. package/lib/module/recording.js.map +1 -0
  44. package/lib/module/runtime.js +13 -5
  45. package/lib/module/runtime.js.map +1 -1
  46. package/lib/module/specs/NativeTiRtc.js.map +1 -1
  47. package/lib/module/storage.js +706 -0
  48. package/lib/module/storage.js.map +1 -0
  49. package/lib/module/video_input.js +40 -22
  50. package/lib/module/video_input.js.map +1 -1
  51. package/lib/module/video_output.js +51 -10
  52. package/lib/module/video_output.js.map +1 -1
  53. package/lib/typescript/audio_input.d.ts +1 -0
  54. package/lib/typescript/conn.d.ts +2 -0
  55. package/lib/typescript/errors.d.ts +3 -2
  56. package/lib/typescript/index.d.ts +4 -0
  57. package/lib/typescript/logging_internal.d.ts +2 -0
  58. package/lib/typescript/recording.d.ts +53 -0
  59. package/lib/typescript/runtime.d.ts +2 -0
  60. package/lib/typescript/specs/NativeTiRtc.d.ts +88 -0
  61. package/lib/typescript/storage.d.ts +204 -0
  62. package/lib/typescript/video_input.d.ts +1 -0
  63. package/lib/typescript/video_output.d.ts +3 -0
  64. package/package.json +1 -1
  65. package/src/audio_input.ts +85 -59
  66. package/src/audio_output.ts +30 -17
  67. package/src/conn.ts +46 -28
  68. package/src/errors.ts +4 -3
  69. package/src/index.ts +54 -0
  70. package/src/logging.ts +7 -1
  71. package/src/logging_internal.ts +23 -0
  72. package/src/recording.ts +271 -0
  73. package/src/runtime.ts +14 -5
  74. package/src/specs/NativeTiRtc.ts +123 -0
  75. package/src/storage.ts +769 -0
  76. package/src/video_input.ts +97 -62
  77. package/src/video_output.ts +54 -16
@@ -6,10 +6,10 @@ import {
6
6
  isPositiveInt32Size,
7
7
  isUInt8,
8
8
  normalizeCreateResult,
9
- returnReleasedIfDisposed,
9
+ returnNotInitializedIfDisposed,
10
10
  TIRTC_ERROR_INVALID_ARGUMENT,
11
11
  TIRTC_ERROR_OK,
12
- TIRTC_HOST_ERROR_OBJECT_RELEASED,
12
+ TIRTC_ERROR_NOT_INITIALIZED,
13
13
  } from './errors';
14
14
  import {registerNativeEventHandler, unregisterNativeEventHandler} from './events';
15
15
  import {
@@ -20,6 +20,7 @@ import {
20
20
  type NativeIdentity,
21
21
  } from './native_object_id';
22
22
  import {NativeTiRtc, NativeTiRtcVideoInputPreview, type NativeTiRtcEvent} from './native';
23
+ import {logRtcOutcome, logRtcState} from './logging_internal';
23
24
  import {TiRtcConn} from './conn';
24
25
  import type {
25
26
  TiRtcInputState,
@@ -58,8 +59,12 @@ export class TiRtcVideoInput {
58
59
  const result = normalizeCreateResult(NativeTiRtc.createVideoInput());
59
60
  this.createCode = result.code;
60
61
  if (result.code === TIRTC_ERROR_OK) {
61
- this.adoptIdentity({objectId: result.objectId, generation: result.generation});
62
+ this.adoptIdentity({
63
+ objectId: result.objectId,
64
+ generation: result.generation,
65
+ });
62
66
  }
67
+ logRtcOutcome('video_input_create', result.code);
63
68
  }
64
69
 
65
70
  get state(): TiRtcInputState {
@@ -94,11 +99,13 @@ export class TiRtcVideoInput {
94
99
  bitrateKbps: options.bitrateKbps ?? 0,
95
100
  cameraFacing: options.cameraFacing ?? 'front',
96
101
  };
97
- if (!isPositiveInt32Size(normalized.width)
98
- || !isPositiveInt32Size(normalized.height)
99
- || !Number.isSafeInteger(normalized.bitrateKbps)
100
- || normalized.bitrateKbps < 0
101
- || normalized.bitrateKbps > 4294967295) {
102
+ if (
103
+ !isPositiveInt32Size(normalized.width) ||
104
+ !isPositiveInt32Size(normalized.height) ||
105
+ !Number.isSafeInteger(normalized.bitrateKbps) ||
106
+ normalized.bitrateKbps < 0 ||
107
+ normalized.bitrateKbps > 4294967295
108
+ ) {
102
109
  return Promise.resolve(TIRTC_ERROR_INVALID_ARGUMENT);
103
110
  }
104
111
  return this.enqueue(async () => {
@@ -115,13 +122,16 @@ export class TiRtcVideoInput {
115
122
  }
116
123
 
117
124
  start(): Promise<number> {
118
- return this.enqueue(async () => {
119
- const identity = this.ensureIdentity();
120
- if (typeof identity === 'number') {
121
- return identity;
122
- }
123
- return NativeTiRtc.videoInputStart(identity.objectId);
124
- });
125
+ return this.logged(
126
+ 'video_input_start',
127
+ this.enqueue(async () => {
128
+ const identity = this.ensureIdentity();
129
+ if (typeof identity === 'number') {
130
+ return identity;
131
+ }
132
+ return NativeTiRtc.videoInputStart(identity.objectId);
133
+ }),
134
+ );
125
135
  }
126
136
 
127
137
  attach(connection: TiRtcConn, streamId: number): Promise<number> {
@@ -132,13 +142,16 @@ export class TiRtcVideoInput {
132
142
  if (connIdentity === null) {
133
143
  return Promise.resolve(TIRTC_ERROR_INVALID_ARGUMENT);
134
144
  }
135
- return this.enqueue(async () => {
136
- const identity = this.ensureIdentity();
137
- if (typeof identity === 'number') {
138
- return identity;
139
- }
140
- return NativeTiRtc.videoInputAttach(identity.objectId, connIdentity.objectId, streamId);
141
- });
145
+ return this.logged(
146
+ 'video_input_attach',
147
+ this.enqueue(async () => {
148
+ const identity = this.ensureIdentity();
149
+ if (typeof identity === 'number') {
150
+ return identity;
151
+ }
152
+ return NativeTiRtc.videoInputAttach(identity.objectId, connIdentity.objectId, streamId);
153
+ }),
154
+ );
142
155
  }
143
156
 
144
157
  detach(connection: TiRtcConn): Promise<number> {
@@ -146,21 +159,27 @@ export class TiRtcVideoInput {
146
159
  if (connIdentity === null) {
147
160
  return Promise.resolve(TIRTC_ERROR_INVALID_ARGUMENT);
148
161
  }
149
- return this.enqueue(async () => {
150
- if (this.identity === null) {
151
- return TIRTC_ERROR_OK;
152
- }
153
- return NativeTiRtc.videoInputDetach(this.identity.objectId, connIdentity.objectId);
154
- });
162
+ return this.logged(
163
+ 'video_input_detach',
164
+ this.enqueue(async () => {
165
+ if (this.identity === null) {
166
+ return TIRTC_ERROR_OK;
167
+ }
168
+ return NativeTiRtc.videoInputDetach(this.identity.objectId, connIdentity.objectId);
169
+ }),
170
+ );
155
171
  }
156
172
 
157
173
  stop(): Promise<number> {
158
- return this.enqueue(async () => {
159
- if (this.identity === null) {
160
- return TIRTC_ERROR_OK;
161
- }
162
- return NativeTiRtc.videoInputStop(this.identity.objectId);
163
- });
174
+ return this.logged(
175
+ 'video_input_stop',
176
+ this.enqueue(async () => {
177
+ if (this.identity === null) {
178
+ return TIRTC_ERROR_OK;
179
+ }
180
+ return NativeTiRtc.videoInputStop(this.identity.objectId);
181
+ }),
182
+ );
164
183
  }
165
184
 
166
185
  /**
@@ -181,39 +200,42 @@ export class TiRtcVideoInput {
181
200
  if (this.disposed) {
182
201
  return Promise.resolve(TIRTC_ERROR_OK);
183
202
  }
184
- return this.enqueue(async () => {
185
- if (this.identity === null) {
186
- this.disposed = true;
187
- this.clearCallbacks();
188
- this.currentState = 'stopped';
189
- this.currentOutputSize = null;
190
- this.currentOutputFps = null;
191
- return TIRTC_ERROR_OK;
192
- }
193
- const identity = this.identity;
194
- const code = await NativeTiRtc.videoInputDispose(identity.objectId);
195
- if (code === TIRTC_ERROR_OK) {
196
- this.disposed = true;
197
- this.identity = null;
198
- unregisterNativeEventHandler(identity);
199
- clearNativeIdentity(this);
200
- this.clearCallbacks();
201
- this.currentState = 'stopped';
202
- this.currentOutputSize = null;
203
- this.currentOutputFps = null;
204
- }
205
- return code;
206
- }, true);
203
+ return this.logged(
204
+ 'video_input_dispose',
205
+ this.enqueue(async () => {
206
+ if (this.identity === null) {
207
+ this.disposed = true;
208
+ this.clearCallbacks();
209
+ this.currentState = 'stopped';
210
+ this.currentOutputSize = null;
211
+ this.currentOutputFps = null;
212
+ return TIRTC_ERROR_OK;
213
+ }
214
+ const identity = this.identity;
215
+ const code = await NativeTiRtc.videoInputDispose(identity.objectId);
216
+ if (code === TIRTC_ERROR_OK) {
217
+ this.disposed = true;
218
+ this.identity = null;
219
+ unregisterNativeEventHandler(identity);
220
+ clearNativeIdentity(this);
221
+ this.clearCallbacks();
222
+ this.currentState = 'stopped';
223
+ this.currentOutputSize = null;
224
+ this.currentOutputFps = null;
225
+ }
226
+ return code;
227
+ }, true),
228
+ );
207
229
  }
208
230
 
209
231
  private enqueue(operation: () => Promise<number>, allowDisposed = false): Promise<number> {
210
- const released = returnReleasedIfDisposed(this.disposed);
232
+ const released = returnNotInitializedIfDisposed(this.disposed);
211
233
  if (released !== null && !allowDisposed) {
212
234
  return Promise.resolve(released);
213
235
  }
214
236
  const next = this.queue.then(async () => {
215
237
  if (this.disposed && !allowDisposed) {
216
- return TIRTC_HOST_ERROR_OBJECT_RELEASED;
238
+ return TIRTC_ERROR_NOT_INITIALIZED;
217
239
  }
218
240
  return operation();
219
241
  });
@@ -221,8 +243,15 @@ export class TiRtcVideoInput {
221
243
  return next;
222
244
  }
223
245
 
246
+ private logged(operation: string, result: Promise<number>): Promise<number> {
247
+ return result.then((code) => {
248
+ logRtcOutcome(operation, code);
249
+ return code;
250
+ });
251
+ }
252
+
224
253
  private ensureIdentity(): NativeIdentity | number {
225
- const released = returnReleasedIfDisposed(this.disposed);
254
+ const released = returnNotInitializedIfDisposed(this.disposed);
226
255
  if (released !== null) {
227
256
  return released;
228
257
  }
@@ -261,7 +290,9 @@ export class TiRtcVideoInput {
261
290
  switch (event.type) {
262
291
  case 'videoInputStateChanged':
263
292
  if (event.state !== undefined) {
264
- this.currentState = event.state as TiRtcInputState;
293
+ const nextState = event.state as TiRtcInputState;
294
+ if (this.currentState !== nextState) logRtcState('video_input_state', nextState);
295
+ this.currentState = nextState;
265
296
  this.onStateChanged?.(this.currentState);
266
297
  }
267
298
  break;
@@ -273,7 +304,11 @@ export class TiRtcVideoInput {
273
304
  }
274
305
  break;
275
306
  case 'videoInputError':
276
- this.onError?.(event.code ?? TIRTC_ERROR_INVALID_ARGUMENT, event.message ?? null);
307
+ {
308
+ const code = event.code ?? TIRTC_ERROR_INVALID_ARGUMENT;
309
+ logRtcOutcome('video_input', code);
310
+ this.onError?.(code, event.message ?? null);
311
+ }
277
312
  break;
278
313
  default:
279
314
  break;
@@ -5,8 +5,9 @@ import {
5
5
  isPositiveInt32,
6
6
  isUInt8,
7
7
  normalizeCreateResult,
8
- returnReleasedIfDisposed,
8
+ returnNotInitializedIfDisposed,
9
9
  TIRTC_ERROR_INVALID_ARGUMENT,
10
+ TIRTC_ERROR_IN_USE,
10
11
  TIRTC_ERROR_OK,
11
12
  } from './errors';
12
13
  import {registerNativeEventHandler, unregisterNativeEventHandler} from './events';
@@ -18,7 +19,9 @@ import {
18
19
  type NativeIdentity,
19
20
  } from './native_object_id';
20
21
  import {NativeTiRtc, NativeTiRtcVideoOutputView, type NativeTiRtcEvent} from './native';
22
+ import {logRtcOutcome, logRtcState} from './logging_internal';
21
23
  import {TiRtcConn} from './conn';
24
+ import {recordingFailure, takeSnapshot, type Resp, type TiRtcSnapshotFile} from './recording';
22
25
  import type {
23
26
  TiRtcOnVideoOutputError,
24
27
  TiRtcOnVideoOutputRenderSizeChanged,
@@ -39,23 +42,46 @@ export class TiRtcVideoOutput {
39
42
  private createCode: number = TIRTC_ERROR_OK;
40
43
  private currentState: TiRtcVideoOutputState = 'idle';
41
44
  private currentRenderSize: TiRtcSize | null = null;
45
+ private snapshotInFlight = false;
42
46
  private options: Required<Omit<TiRtcVideoOutputOptions, 'maxBufferWatermarkMs'>> &
43
47
  Pick<TiRtcVideoOutputOptions, 'maxBufferWatermarkMs'> = {
44
- decoderPreference: 'auto',
45
- bufferStrategy: 'automatic',
46
- maxBufferWatermarkMs: undefined,
47
- };
48
+ decoderPreference: 'auto',
49
+ bufferStrategy: 'automatic',
50
+ maxBufferWatermarkMs: undefined,
51
+ };
48
52
 
49
53
  onStateChanged: TiRtcOnVideoOutputStateChanged | null = null;
50
54
  onRenderSizeChanged: TiRtcOnVideoOutputRenderSizeChanged | null = null;
51
55
  onError: TiRtcOnVideoOutputError | null = null;
52
56
 
57
+ async takeSnapshot(): Promise<Resp<TiRtcSnapshotFile>> {
58
+ const identity = this.ensureIdentity();
59
+ if (typeof identity === 'number') {
60
+ return recordingFailure(identity);
61
+ }
62
+ if (this.snapshotInFlight) {
63
+ return recordingFailure(TIRTC_ERROR_IN_USE);
64
+ }
65
+ this.snapshotInFlight = true;
66
+ try {
67
+ const result = await takeSnapshot(identity.objectId);
68
+ logRtcOutcome('video_output_snapshot', result.code ?? TIRTC_ERROR_OK);
69
+ return result;
70
+ } finally {
71
+ this.snapshotInFlight = false;
72
+ }
73
+ }
74
+
53
75
  constructor() {
54
76
  const result = normalizeCreateResult(NativeTiRtc.createVideoOutput());
55
77
  this.createCode = result.code;
56
78
  if (result.code === TIRTC_ERROR_OK) {
57
- this.adoptIdentity({objectId: result.objectId, generation: result.generation});
79
+ this.adoptIdentity({
80
+ objectId: result.objectId,
81
+ generation: result.generation,
82
+ });
58
83
  }
84
+ logRtcOutcome('video_output_create', result.code);
59
85
  }
60
86
 
61
87
  get state(): TiRtcVideoOutputState {
@@ -81,10 +107,7 @@ export class TiRtcVideoOutput {
81
107
  bufferStrategy: options.bufferStrategy ?? 'automatic',
82
108
  maxBufferWatermarkMs: options.maxBufferWatermarkMs,
83
109
  };
84
- if (
85
- normalized.maxBufferWatermarkMs !== undefined
86
- && !isPositiveInt32(normalized.maxBufferWatermarkMs)
87
- ) {
110
+ if (normalized.maxBufferWatermarkMs !== undefined && !isPositiveInt32(normalized.maxBufferWatermarkMs)) {
88
111
  return TIRTC_ERROR_INVALID_ARGUMENT;
89
112
  }
90
113
  const identity = this.ensureIdentity();
@@ -113,15 +136,20 @@ export class TiRtcVideoOutput {
113
136
  if (typeof identity === 'number') {
114
137
  return identity;
115
138
  }
116
- return NativeTiRtc.videoOutputAttach(identity.objectId, connIdentity.objectId, streamId);
139
+ const code = NativeTiRtc.videoOutputAttach(identity.objectId, connIdentity.objectId, streamId);
140
+ logRtcOutcome('video_output_attach', code);
141
+ return code;
117
142
  }
118
143
 
119
144
  detach(): number {
120
- const released = returnReleasedIfDisposed(this.disposed);
145
+ const released = returnNotInitializedIfDisposed(this.disposed);
121
146
  if (released !== null) {
122
147
  return released;
123
148
  }
124
- return this.identity === null ? TIRTC_ERROR_OK : NativeTiRtc.videoOutputDetach(this.identity.objectId);
149
+ const code = this.identity === null ? TIRTC_ERROR_OK : NativeTiRtc.videoOutputDetach(this.identity.objectId);
150
+ if (code === TIRTC_ERROR_OK) this.currentState = 'idle';
151
+ logRtcOutcome('video_output_detach', code);
152
+ return code;
125
153
  }
126
154
 
127
155
  /**
@@ -166,6 +194,9 @@ export class TiRtcVideoOutput {
166
194
  if (this.disposed) {
167
195
  return TIRTC_ERROR_OK;
168
196
  }
197
+ if (this.snapshotInFlight) {
198
+ return TIRTC_ERROR_IN_USE;
199
+ }
169
200
  if (this.identity === null) {
170
201
  this.disposed = true;
171
202
  this.clearCallbacks();
@@ -184,11 +215,12 @@ export class TiRtcVideoOutput {
184
215
  this.currentState = 'idle';
185
216
  this.currentRenderSize = null;
186
217
  }
218
+ logRtcOutcome('video_output_dispose', code);
187
219
  return code;
188
220
  }
189
221
 
190
222
  private ensureIdentity(): NativeIdentity | number {
191
- const released = returnReleasedIfDisposed(this.disposed);
223
+ const released = returnNotInitializedIfDisposed(this.disposed);
192
224
  if (released !== null) {
193
225
  return released;
194
226
  }
@@ -221,7 +253,9 @@ export class TiRtcVideoOutput {
221
253
  switch (event.type) {
222
254
  case 'videoOutputStateChanged':
223
255
  if (event.state !== undefined) {
224
- this.currentState = event.state as TiRtcVideoOutputState;
256
+ const nextState = event.state as TiRtcVideoOutputState;
257
+ if (this.currentState !== nextState) logRtcState('video_output_state', nextState);
258
+ this.currentState = nextState;
225
259
  this.onStateChanged?.(this.currentState);
226
260
  }
227
261
  break;
@@ -232,7 +266,11 @@ export class TiRtcVideoOutput {
232
266
  }
233
267
  break;
234
268
  case 'videoOutputError':
235
- this.onError?.(event.code ?? TIRTC_ERROR_INVALID_ARGUMENT);
269
+ {
270
+ const code = event.code ?? TIRTC_ERROR_INVALID_ARGUMENT;
271
+ logRtcOutcome('video_output', code);
272
+ this.onError?.(code);
273
+ }
236
274
  break;
237
275
  default:
238
276
  break;