tirtc-react-native 2.4.2 → 2.5.0

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 (71) hide show
  1. package/README.md +39 -6
  2. package/TiRtcReactNative.podspec +1 -1
  3. package/android/build.gradle +1 -1
  4. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcModule.kt +140 -17
  5. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeCleanup.kt +54 -19
  6. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeCloudStorageCallbacks.kt +60 -0
  7. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventBinder.kt +2 -0
  8. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventSink.kt +20 -0
  9. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMaps.kt +112 -5
  10. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeOptions.kt +2 -7
  11. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeRegistry.kt +3 -0
  12. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeStorageSupport.kt +46 -22
  13. package/ios/TiRtcReactNative+Conn.mm +61 -0
  14. package/ios/TiRtcReactNative+Output.mm +1 -7
  15. package/ios/TiRtcReactNative+Private.h +1 -0
  16. package/ios/TiRtcReactNative+Storage.mm +133 -35
  17. package/ios/TiRtcReactNative.mm +142 -51
  18. package/ios/TiRtcReactNativeCloudStorageCallbacks.h +13 -0
  19. package/ios/TiRtcReactNativeCloudStorageCallbacks.mm +67 -0
  20. package/ios/TiRtcReactNativeEventBinder.h +4 -0
  21. package/ios/TiRtcReactNativeEventBinder.mm +11 -0
  22. package/ios/TiRtcReactNativeEventSink.h +5 -0
  23. package/ios/TiRtcReactNativeEventSink.mm +18 -0
  24. package/ios/TiRtcReactNativeMaps.h +6 -0
  25. package/ios/TiRtcReactNativeMaps.mm +47 -0
  26. package/ios/TiRtcReactNativeOptions.mm +2 -6
  27. package/lib/module/audio_input.js +23 -4
  28. package/lib/module/audio_input.js.map +1 -1
  29. package/lib/module/build_identity.js +1 -0
  30. package/lib/module/conn.js +19 -0
  31. package/lib/module/conn.js.map +1 -1
  32. package/lib/module/events.js +16 -9
  33. package/lib/module/events.js.map +1 -1
  34. package/lib/module/index.js +3 -2
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/logging_internal.js +16 -0
  37. package/lib/module/logging_internal.js.map +1 -1
  38. package/lib/module/raw_dump.js +53 -0
  39. package/lib/module/raw_dump.js.map +1 -0
  40. package/lib/module/runtime.js +3 -1
  41. package/lib/module/runtime.js.map +1 -1
  42. package/lib/module/specs/NativeTiRtc.js.map +1 -1
  43. package/lib/module/storage.js +190 -33
  44. package/lib/module/storage.js.map +1 -1
  45. package/lib/module/types.js +0 -4
  46. package/lib/module/types.js.map +1 -1
  47. package/lib/module/video_input.js +16 -6
  48. package/lib/module/video_input.js.map +1 -1
  49. package/lib/typescript/build_identity.d.ts +6 -0
  50. package/lib/typescript/conn.d.ts +2 -0
  51. package/lib/typescript/index.d.ts +5 -3
  52. package/lib/typescript/logging_internal.d.ts +1 -0
  53. package/lib/typescript/native.d.ts +1 -1
  54. package/lib/typescript/raw_dump.d.ts +35 -0
  55. package/lib/typescript/specs/NativeTiRtc.d.ts +75 -3
  56. package/lib/typescript/storage.d.ts +84 -3
  57. package/lib/typescript/types.d.ts +5 -6
  58. package/package.json +3 -3
  59. package/src/audio_input.ts +30 -11
  60. package/src/build_identity.ts +6 -0
  61. package/src/conn.ts +23 -0
  62. package/src/events.ts +14 -9
  63. package/src/index.ts +19 -1
  64. package/src/logging_internal.ts +18 -0
  65. package/src/native.ts +7 -1
  66. package/src/raw_dump.ts +84 -0
  67. package/src/runtime.ts +2 -1
  68. package/src/specs/NativeTiRtc.ts +80 -3
  69. package/src/storage.ts +260 -39
  70. package/src/types.ts +5 -8
  71. package/src/video_input.ts +22 -12
@@ -3,6 +3,7 @@ import { type ViewProps } from 'react-native';
3
3
  import { NativeTiRtc, type NativeTiRtcEvent } from './native';
4
4
  import { type NativeIdentity } from './native_object_id';
5
5
  import { type Resp, type TiRtcGalleryAsset } from './recording';
6
+ import { TiRawDump, type TiCloudStorageRawDumpOptions } from './raw_dump';
6
7
  export declare const TI_CLOUD_STORAGE_ERROR_OK: 0;
7
8
  export declare const TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT: 6000;
8
9
  export declare const TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED: 6001;
@@ -27,6 +28,8 @@ export declare const TI_CLOUD_STORAGE_ERROR_UNAVAILABLE: 6123;
27
28
  export declare const TI_CLOUD_STORAGE_ERROR_STOPPED: 6124;
28
29
  export declare const TI_CLOUD_STORAGE_ERROR_RECORDING_NOT_FOUND: 6134;
29
30
  export declare const TI_CLOUD_STORAGE_ERROR_RECORDING_DOWNLOAD_FAILED: 6135;
31
+ export declare const TI_CLOUD_STORAGE_ERROR_NETWORK_UNAVAILABLE: 6136;
32
+ export declare const TI_CLOUD_STORAGE_ERROR_ENDPOINT_DNS_RESOLUTION_FAILED: 6137;
30
33
  export type TiCloudStorageRecordingRange = Readonly<{
31
34
  startTimeMs: number;
32
35
  endTimeMs: number;
@@ -43,6 +46,69 @@ export type TiCloudStorageRecordingDaysResult = Readonly<{
43
46
  code: number;
44
47
  days: ReadonlyArray<TiCloudStorageRecordingDay>;
45
48
  }>;
49
+ export declare const TiCloudStorageRecordingTrackKind: {
50
+ readonly video: "video";
51
+ readonly audio: "audio";
52
+ };
53
+ export type TiCloudStorageRecordingTrackKind = (typeof TiCloudStorageRecordingTrackKind)[keyof typeof TiCloudStorageRecordingTrackKind];
54
+ export type TiCloudStorageRecordingTrack = Readonly<{
55
+ kind: TiCloudStorageRecordingTrackKind;
56
+ channelId: number;
57
+ }>;
58
+ export declare const TiCloudStorageRecordingGapReason: {
59
+ readonly unknown: "unknown";
60
+ readonly notFound: "notFound";
61
+ readonly downloadFailed: "downloadFailed";
62
+ readonly integrityFailed: "integrityFailed";
63
+ readonly mediaUnreadable: "mediaUnreadable";
64
+ readonly noKeyFrame: "noKeyFrame";
65
+ readonly noRecording: "noRecording";
66
+ readonly decryptionFailed: "decryptionFailed";
67
+ readonly unsupportedMedia: "unsupportedMedia";
68
+ readonly trackUnavailable: "trackUnavailable";
69
+ };
70
+ export type TiCloudStorageRecordingGapReason = (typeof TiCloudStorageRecordingGapReason)[keyof typeof TiCloudStorageRecordingGapReason];
71
+ export type TiCloudStorageRecordingGap = Readonly<{
72
+ range: TiCloudStorageRecordingRange;
73
+ tracks: ReadonlyArray<TiCloudStorageRecordingTrack>;
74
+ reasons: ReadonlyArray<TiCloudStorageRecordingGapReason>;
75
+ }>;
76
+ export type TiCloudStorageExportProgress = Readonly<{
77
+ fraction: number;
78
+ coveredDurationMs: number;
79
+ }>;
80
+ export type TiCloudStorageExportSegment = Readonly<{
81
+ sourceRange: TiCloudStorageRecordingRange;
82
+ outputStartMs: number;
83
+ outputEndMs: number;
84
+ }>;
85
+ export declare const TiCloudStorageExportTermination: {
86
+ readonly exhausted: "exhausted";
87
+ readonly interrupted: "interrupted";
88
+ readonly cancelled: "cancelled";
89
+ readonly failed: "failed";
90
+ };
91
+ export type TiCloudStorageExportTermination = (typeof TiCloudStorageExportTermination)[keyof typeof TiCloudStorageExportTermination];
92
+ export type TiCloudStorageExportReport = Readonly<{
93
+ requestedRange: TiCloudStorageRecordingRange;
94
+ coveredDurationMs: number;
95
+ segments: ReadonlyArray<TiCloudStorageExportSegment>;
96
+ gaps: ReadonlyArray<TiCloudStorageRecordingGap>;
97
+ unprocessedRanges: ReadonlyArray<TiCloudStorageRecordingRange>;
98
+ complete: boolean;
99
+ termination: TiCloudStorageExportTermination;
100
+ cause: number;
101
+ }>;
102
+ export type TiCloudStorageExportOutcome = Readonly<{
103
+ code: number;
104
+ file: TiCloudStorageRecordingFile | null;
105
+ report: TiCloudStorageExportReport | null;
106
+ }>;
107
+ export type TiCloudStorageExportObserver = Readonly<{
108
+ onProgress?: (progress: number) => void;
109
+ onProgressDetail?: (progress: TiCloudStorageExportProgress) => void;
110
+ onRecordingGap?: (gap: TiCloudStorageRecordingGap) => void;
111
+ }>;
46
112
  export declare const TiCloudStorageReplaySpeed: {
47
113
  readonly x0_125: "x0_125";
48
114
  readonly x0_25: "x0_25";
@@ -97,6 +163,7 @@ export declare class TiCloudStorage {
97
163
  private token;
98
164
  private identity;
99
165
  private disposed;
166
+ private pendingAsyncOperations;
100
167
  constructor(token: string);
101
168
  static init(options: Readonly<{
102
169
  appId: string;
@@ -109,19 +176,22 @@ export declare class TiCloudStorage {
109
176
  listRecordings(startTimeMs: number, endTimeMs: number): Promise<TiCloudStorageRecordingRangesResult>;
110
177
  listRecordingDays(startDate: string, endDate: string, timeZoneId?: string): Promise<TiCloudStorageRecordingDaysResult>;
111
178
  createReplay(): TiCloudStorageReplay;
112
- exportRecording(options: TiCloudStorageExportOptions, onProgress?: (progress: number) => void): Resp<TiCloudStorageExportTask>;
179
+ exportRecording(options: TiCloudStorageExportOptions, observer?: ((progress: number) => void) | TiCloudStorageExportObserver): Resp<TiCloudStorageExportTask>;
113
180
  dispose(): number;
181
+ private trackAsyncOperation;
114
182
  private ensureIdentity;
115
183
  }
116
184
  export declare class TiCloudStorageReplay {
117
185
  private readonly cloudStorage;
118
186
  private identity;
119
187
  private disposed;
188
+ private inCallback;
120
189
  private currentSpeed;
121
190
  private currentTime;
122
191
  onTimeChanged: ((timeMs: number) => void) | null;
123
192
  onError: ((code: number) => void) | null;
124
193
  onCompleted: (() => void) | null;
194
+ onRecordingGap: ((gap: TiCloudStorageRecordingGap) => void) | null;
125
195
  constructor(cloudStorage: TiCloudStorage);
126
196
  get speed(): TiCloudStorageReplaySpeed;
127
197
  get currentTimeMs(): number | null;
@@ -131,6 +201,7 @@ export declare class TiCloudStorageReplay {
131
201
  seek(timeMs: number): number;
132
202
  setSpeed(speed: TiCloudStorageReplaySpeed): number;
133
203
  startRecording(options: TiCloudStorageStartRecordingOptions): Resp<TiCloudStorageRecordingTask>;
204
+ startRawDump(options: TiCloudStorageRawDumpOptions): Promise<Resp<TiRawDump>>;
134
205
  stop(): number;
135
206
  dispose(): number;
136
207
  private command;
@@ -196,11 +267,21 @@ export declare class TiCloudStorageRecordingTask {
196
267
  export declare class TiCloudStorageExportTask {
197
268
  private readonly taskId;
198
269
  private readonly owner;
199
- private readonly onProgress?;
200
270
  private resultPromise;
201
- constructor(taskId: number, owner: TiCloudStorage, onProgress?: ((progress: number) => void) | undefined);
271
+ private completionPromise;
272
+ private readonly identity;
273
+ private currentProgressDetail;
274
+ private currentReport;
275
+ private readonly observer;
276
+ private terminal;
277
+ constructor(taskId: number, generation: number, owner: TiCloudStorage, observer?: ((progress: number) => void) | TiCloudStorageExportObserver);
202
278
  get progress(): number;
279
+ get progressDetail(): TiCloudStorageExportProgress;
280
+ get report(): TiCloudStorageExportReport | null;
281
+ get completion(): Promise<TiCloudStorageExportOutcome>;
203
282
  get result(): Promise<Resp<TiCloudStorageRecordingFile>>;
283
+ cancel(): number;
204
284
  stop(): Promise<Resp<TiCloudStorageRecordingFile>>;
285
+ private handleEvent;
205
286
  }
206
287
  export {};
@@ -66,11 +66,6 @@ export declare const TiRtcVideoCodec: {
66
66
  readonly mjpeg: "mjpeg";
67
67
  };
68
68
  export type TiRtcVideoCodec = (typeof TiRtcVideoCodec)[keyof typeof TiRtcVideoCodec];
69
- export declare const TiRtcVideoEncoderCodec: {
70
- readonly h264: "h264";
71
- readonly mjpeg: "mjpeg";
72
- };
73
- export type TiRtcVideoEncoderCodec = (typeof TiRtcVideoEncoderCodec)[keyof typeof TiRtcVideoEncoderCodec];
74
69
  export declare const TiRtcVideoFrameRate: {
75
70
  readonly fps10: "fps10";
76
71
  readonly fps15: "fps15";
@@ -145,6 +140,9 @@ export type TiRtcVideoOutputViewProps = ViewProps & Readonly<{
145
140
  resizeMode?: 'contain' | 'cover' | 'stretch';
146
141
  }>;
147
142
  export type TiRtcAudioInputOptions = Readonly<{
143
+ /** Nano audio media: 1 PCM, 2 G711A, 3 AAC, 4 Opus, 5 AMR. Omitted or -1 uses codec. */
144
+ media?: number;
145
+ /** @deprecated Use media. The legacy default is g711a. */
148
146
  codec?: TiRtcAudioCodec;
149
147
  sampleRate?: TiRtcAudioSampleRate;
150
148
  channels?: TiRtcAudioChannelCount;
@@ -153,7 +151,8 @@ export type TiRtcAudioInputOptions = Readonly<{
153
151
  ansLevel?: TiRtcAudioAnsLevel;
154
152
  }>;
155
153
  export type TiRtcVideoInputOptions = Readonly<{
156
- codec?: TiRtcVideoEncoderCodec;
154
+ /** Nano video media: 66 H.264 (default) or 65 MJPEG. */
155
+ media?: number;
157
156
  width?: number;
158
157
  height?: number;
159
158
  frameRate?: TiRtcVideoFrameRate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tirtc-react-native",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "description": "TiRTC React Native SDK",
5
5
  "homepage": "https://tange.ai/",
6
6
  "repository": {
@@ -46,10 +46,10 @@
46
46
  "react-native.config.js"
47
47
  ],
48
48
  "scripts": {
49
- "build": "node scripts/clean_generated_lib.mjs && tsc -p tsconfig.build.json && node scripts/write_codegen_component_specs.mjs",
49
+ "build": "node scripts/clean_generated_lib.mjs && tsc -p tsconfig.build.json && node scripts/freeze_build_identity.mjs && node scripts/write_codegen_component_specs.mjs",
50
50
  "lint": "node test/source_guard.mjs",
51
51
  "preflight:npm": "node scripts/npm_preflight.mjs",
52
- "test": "node test/parity_guard.mjs && node test/package_artifact_guard.mjs && node test/lifecycle_contract_guard.mjs && node test/lifecycle_behavior_guard.mjs && node test/integration_contract_guard.mjs && node test/runner_schema_guard.mjs",
52
+ "test": "node test/parity_guard.mjs && node test/package_artifact_guard.mjs && node test/lifecycle_contract_guard.mjs && node test/lifecycle_behavior_guard.mjs && node test/integration_contract_guard.mjs && node test/runner_schema_guard.mjs && node --test test/source_fingerprint.test.mjs",
53
53
  "test:contract-red": "node test/contract_red_summary.mjs",
54
54
  "typecheck": "tsc -p tsconfig.json --noEmit",
55
55
  "codegen:check": "node test/codegen_contract_guard.mjs"
@@ -21,6 +21,7 @@ export class TiRtcAudioInput {
21
21
  private queue: Promise<unknown> = Promise.resolve();
22
22
  private currentState: TiRtcInputState = 'idle';
23
23
  private options: Required<TiRtcAudioInputOptions> = {
24
+ media: -1,
24
25
  codec: 'g711a',
25
26
  sampleRate: 'rate16k',
26
27
  channels: 'mono',
@@ -58,8 +59,20 @@ export class TiRtcAudioInput {
58
59
  if (!isPlainObject(options)) {
59
60
  return Promise.resolve(TIRTC_ERROR_INVALID_ARGUMENT);
60
61
  }
62
+ const media = options.media === undefined ? -1 : options.media;
63
+ const source = media === -1 ? (options.codec === undefined ? 'default' : 'legacy') : 'media';
64
+ const rawLegacy = options.codec === undefined ? 'omitted' :
65
+ ['g711a', 'aac', 'pcm', 'opus', 'amr'].includes(options.codec) ? options.codec : 'invalid';
66
+ const legacyCodec = options.codec ?? 'g711a';
67
+ if (!Number.isInteger(media) || ![-1, 1, 2, 3, 4, 5].includes(media) ||
68
+ (media === -1 && !['g711a', 'aac', 'pcm', 'opus', 'amr'].includes(legacyCodec))) {
69
+ logRtcOutcome('audio_input_set_options', TIRTC_ERROR_INVALID_ARGUMENT,
70
+ ` nano_media=${typeof media === 'number' ? media : typeof media} legacy_codec=${rawLegacy} source=${source}`);
71
+ return Promise.resolve(TIRTC_ERROR_INVALID_ARGUMENT);
72
+ }
61
73
  const normalized = {
62
- codec: options.codec ?? 'g711a',
74
+ media,
75
+ codec: media === -1 ? legacyCodec : 'g711a' as const,
63
76
  sampleRate: options.sampleRate ?? 'rate16k',
64
77
  channels: options.channels ?? 'mono',
65
78
  aecMode: options.aecMode ?? 'disabled',
@@ -72,6 +85,8 @@ export class TiRtcAudioInput {
72
85
  return identity;
73
86
  }
74
87
  const code = await NativeTiRtc.audioInputSetOptions(identity.objectId, normalized);
88
+ logRtcOutcome('audio_input_set_options', code,
89
+ ` nano_media=${media} legacy_codec=${rawLegacy} source=${source} sample_rate=${['rate8k', 'rate16k'].includes(normalized.sampleRate) ? normalized.sampleRate : 'invalid'} channels=${normalized.channels === 'mono' ? 'mono' : 'invalid'}`);
75
90
  if (code === TIRTC_ERROR_OK) {
76
91
  this.options = normalized;
77
92
  }
@@ -100,16 +115,20 @@ export class TiRtcAudioInput {
100
115
  }
101
116
 
102
117
  start(): Promise<number> {
103
- return this.logged(
104
- 'audio_input_start',
105
- this.enqueue(async () => {
106
- const identity = this.ensureIdentity();
107
- if (typeof identity === 'number') {
108
- return identity;
109
- }
110
- return NativeTiRtc.audioInputStart(identity.objectId);
111
- }),
112
- );
118
+ return this.enqueue(async () => {
119
+ const identity = this.ensureIdentity();
120
+ if (typeof identity === 'number') {
121
+ logRtcOutcome('audio_input_start', identity);
122
+ return identity;
123
+ }
124
+ const wasRunning = this.state === 'running';
125
+ const code = await NativeTiRtc.audioInputStart(identity.objectId);
126
+ if (code !== TIRTC_ERROR_OK || !wasRunning) {
127
+ logRtcOutcome('audio_input_start', code,
128
+ ` object_id=${identity.objectId} nano_media=${this.options.media} legacy_codec=${this.options.codec} sample_rate=${this.options.sampleRate} channels=${this.options.channels}`);
129
+ }
130
+ return code;
131
+ });
113
132
  }
114
133
 
115
134
  stop(): Promise<number> {
@@ -0,0 +1,6 @@
1
+ export const buildIdentity = {
2
+ releaseVersion: '',
3
+ buildTimeUtc: '',
4
+ sourceRevision: '',
5
+ sourceDirty: -1,
6
+ } as const;
package/src/conn.ts CHANGED
@@ -27,6 +27,12 @@ import type {
27
27
  TiRtcOnConnStateChanged,
28
28
  TiRtcOnConnStreamMessage,
29
29
  } from './types';
30
+ import {
31
+ createRawDumpFromNative,
32
+ normalizeRawDumpIds,
33
+ TiRawDump,
34
+ type TiRtcRawDumpOptions,
35
+ } from './raw_dump';
30
36
 
31
37
  export class TiRtcConn {
32
38
  private identity: NativeIdentity | null = null;
@@ -59,6 +65,23 @@ export class TiRtcConn {
59
65
  return typeof identity === 'number' ? recordingFailure(identity) : startRecording(identity.objectId, options);
60
66
  }
61
67
 
68
+ async startRawDump(options: TiRtcRawDumpOptions): Promise<Resp<TiRawDump>> {
69
+ if (options === null || typeof options !== 'object') return recordingFailure(TIRTC_ERROR_INVALID_ARGUMENT);
70
+ const audio = normalizeRawDumpIds(options.audioStreamIds ?? [], 15);
71
+ const video = normalizeRawDumpIds(options.videoStreamIds ?? [], 15);
72
+ const uplinkAudio = normalizeRawDumpIds(options.uplinkAudioStreamIds ?? [], 15);
73
+ if (audio === null || video === null || uplinkAudio === null ||
74
+ (audio.length === 0 && video.length === 0 && uplinkAudio.length === 0)) {
75
+ return recordingFailure(TIRTC_ERROR_INVALID_ARGUMENT);
76
+ }
77
+ const identity = this.ensureIdentity();
78
+ if (typeof identity === 'number') return recordingFailure(identity);
79
+ const result = await NativeTiRtc.connStartRawDump(identity.objectId, audio, video, uplinkAudio);
80
+ return result.code === 0 && result.dumpId !== null
81
+ ? {success: true, code: null, message: null, data: createRawDumpFromNative(result.dumpId)}
82
+ : recordingFailure(result.code);
83
+ }
84
+
62
85
  get state(): TiRtcConnState {
63
86
  if (this.identity !== null && !this.disposed) {
64
87
  const result = NativeTiRtc.connGetState(this.identity.objectId);
package/src/events.ts CHANGED
@@ -16,20 +16,25 @@ function ensureSubscribed(): void {
16
16
  return;
17
17
  }
18
18
  subscription = NativeTiRtc.onTiRtcEvent((event) => {
19
- if (!isPositiveInt32(event.objectId) || !isPositiveInt32(event.generation)) {
20
- return;
21
- }
22
- const handler = handlers.get(eventKey({
23
- objectId: event.objectId,
24
- generation: event.generation,
25
- }));
26
- if (handler !== undefined) {
27
- handler(event);
19
+ if (event.type === 'tiCloudStorageCallbackReady') {
20
+ const pending = NativeTiRtc.tiCloudStorageTakeCallback();
21
+ if (pending === null) return;
22
+ try {
23
+ deliver(pending.event);
24
+ } finally {
25
+ NativeTiRtc.tiCloudStorageAcknowledgeCallback(pending.deliveryId);
26
+ }
28
27
  return;
29
28
  }
29
+ deliver(event);
30
30
  });
31
31
  }
32
32
 
33
+ function deliver(event: NativeTiRtcEvent): void {
34
+ if (!isPositiveInt32(event.objectId) || !isPositiveInt32(event.generation)) return;
35
+ handlers.get(eventKey({objectId: event.objectId, generation: event.generation}))?.(event);
36
+ }
37
+
33
38
  export function registerNativeEventHandler(identity: NativeIdentity, handler: NativeEventHandler): void {
34
39
  handlers.set(eventKey(identity), handler);
35
40
  ensureSubscribed();
package/src/index.ts CHANGED
@@ -9,7 +9,6 @@ export {
9
9
  TiRtcAudioAgcLevel,
10
10
  TiRtcAudioAnsLevel,
11
11
  TiRtcVideoCodec,
12
- TiRtcVideoEncoderCodec,
13
12
  TiRtcVideoFrameRate,
14
13
  TiRtcCameraFacing,
15
14
  TiRtcVideoDecoderPreference,
@@ -60,6 +59,13 @@ export {TiRtcAudioInput} from './audio_input';
60
59
  export {TiRtcVideoInput, TiRtcVideoInputPreview} from './video_input';
61
60
  export {TiRtcLogging} from './logging';
62
61
  export {TiRtcRecordingTask} from './recording';
62
+ export {TiRawDump} from './raw_dump';
63
+ export type {
64
+ TiRtcRawDumpOptions,
65
+ TiCloudStorageRawDumpOptions,
66
+ TiRawDumpStopReason,
67
+ TiRawDumpArchive,
68
+ } from './raw_dump';
63
69
  export type {
64
70
  Resp,
65
71
  TiRtcGalleryAsset,
@@ -92,7 +98,12 @@ export {
92
98
  TI_CLOUD_STORAGE_ERROR_STOPPED,
93
99
  TI_CLOUD_STORAGE_ERROR_RECORDING_NOT_FOUND,
94
100
  TI_CLOUD_STORAGE_ERROR_RECORDING_DOWNLOAD_FAILED,
101
+ TI_CLOUD_STORAGE_ERROR_NETWORK_UNAVAILABLE,
102
+ TI_CLOUD_STORAGE_ERROR_ENDPOINT_DNS_RESOLUTION_FAILED,
95
103
  TiCloudStorageReplaySpeed,
104
+ TiCloudStorageRecordingTrackKind,
105
+ TiCloudStorageRecordingGapReason,
106
+ TiCloudStorageExportTermination,
96
107
  TiCloudStorageAudioOutputState,
97
108
  TiCloudStorageVideoOutputState,
98
109
  TiCloudStorage,
@@ -105,6 +116,13 @@ export {
105
116
  } from './storage';
106
117
  export type {
107
118
  TiCloudStorageRecordingRange,
119
+ TiCloudStorageRecordingTrack,
120
+ TiCloudStorageRecordingGap,
121
+ TiCloudStorageExportProgress,
122
+ TiCloudStorageExportSegment,
123
+ TiCloudStorageExportReport,
124
+ TiCloudStorageExportOutcome,
125
+ TiCloudStorageExportObserver,
108
126
  TiCloudStorageRecordingRangesResult,
109
127
  TiCloudStorageRecordingDay,
110
128
  TiCloudStorageRecordingDaysResult,
@@ -1,6 +1,24 @@
1
1
  import {TiRtcLogging} from './logging';
2
+ import {NativeTiRtc} from './native';
3
+ import {buildIdentity} from './build_identity';
2
4
 
3
5
  const rtcLogTag = 'TIRTC_REACT_NATIVE';
6
+ const loggedIdentityProducts = new Set<string>();
7
+
8
+ export function logBuildIdentity(product: 'rtc' | 'cloud_storage'): void {
9
+ if (loggedIdentityProducts.has(product)) return;
10
+ try {
11
+ const code = NativeTiRtc.writeLog(
12
+ 1,
13
+ rtcLogTag,
14
+ '[build_identity] summary: component build identity; component=react_native ' +
15
+ `product=${product} release_version=${buildIdentity.releaseVersion} ` +
16
+ `build_time_utc=${buildIdentity.buildTimeUtc} source_revision=${buildIdentity.sourceRevision} ` +
17
+ `source_dirty=${buildIdentity.sourceDirty}`,
18
+ );
19
+ if (code === 0) loggedIdentityProducts.add(product);
20
+ } catch {}
21
+ }
4
22
 
5
23
  export function logRtcOutcome(operation: string, code: number, details = ''): void {
6
24
  const message = `[${operation}] completed code=${code}${details}`;
package/src/native.ts CHANGED
@@ -3,4 +3,10 @@ import NativeTiRtcVideoInputPreview from './specs/TiRtcVideoInputPreviewNativeCo
3
3
  import NativeTiRtcVideoOutputView from './specs/TiRtcVideoOutputViewNativeComponent';
4
4
 
5
5
  export {NativeTiRtc, NativeTiRtcVideoOutputView, NativeTiRtcVideoInputPreview};
6
- export type {NativeTiRtcEvent} from './specs/NativeTiRtc';
6
+ export type {
7
+ NativeTiRtcEvent,
8
+ NativeTiCloudStorageExportProgress,
9
+ NativeTiCloudStorageExportReport,
10
+ NativeTiCloudStorageRecordingGap,
11
+ NativeRawDumpArchive,
12
+ } from './specs/NativeTiRtc';
@@ -0,0 +1,84 @@
1
+ import {isFiniteSafeInteger, TIRTC_ERROR_INVALID_ARGUMENT} from './errors';
2
+ import {NativeTiRtc, type NativeRawDumpArchive} from './native';
3
+ import type {Resp} from './recording';
4
+
5
+ export type TiRawDumpStopReason =
6
+ | 'unknown' | 'user' | 'timeLimit' | 'byteLimit' | 'sourceClosed'
7
+ | 'resourceLimit' | 'writeFailed';
8
+
9
+ export type TiRtcRawDumpOptions = Readonly<{
10
+ /** Remote downlink audio stream IDs. */
11
+ audioStreamIds?: ReadonlyArray<number>;
12
+ /** Remote downlink video stream IDs. */
13
+ videoStreamIds?: ReadonlyArray<number>;
14
+ /** Local uplink audio stream IDs used by TiRtcAudioInput.attach. */
15
+ uplinkAudioStreamIds?: ReadonlyArray<number>;
16
+ }>;
17
+
18
+ export type TiCloudStorageRawDumpOptions = Readonly<{
19
+ audioChannelIds?: ReadonlyArray<number>;
20
+ videoChannelIds?: ReadonlyArray<number>;
21
+ }>;
22
+
23
+ export type TiRawDumpArchive = Readonly<{
24
+ captureId: string; path: string; size: number; sha256: string; capturedDurationMs: number;
25
+ captureComplete: boolean; stopReason: TiRawDumpStopReason;
26
+ unsavedPacketCount: number; unsavedByteCount: number; empty: boolean;
27
+ }>;
28
+
29
+ const reasons: ReadonlyArray<TiRawDumpStopReason> = [
30
+ 'unknown', 'user', 'timeLimit', 'byteLimit', 'sourceClosed', 'resourceLimit', 'writeFailed',
31
+ ];
32
+
33
+ function archiveFromNative(value: NativeRawDumpArchive): TiRawDumpArchive {
34
+ return {...value, stopReason: reasons[value.stopReason] ?? 'unknown'};
35
+ }
36
+
37
+ function failure<T>(code: number): Resp<T> {
38
+ return {success: false, code, message: null, data: null};
39
+ }
40
+
41
+ export function normalizeRawDumpIds(
42
+ value: unknown,
43
+ maximum: number,
44
+ ): ReadonlyArray<number> | null {
45
+ if (!Array.isArray(value)) return null;
46
+ const result = new Set<number>();
47
+ for (const item of value) {
48
+ if (!isFiniteSafeInteger(item) || item < 0 || item > maximum) return null;
49
+ result.add(item);
50
+ }
51
+ return [...result];
52
+ }
53
+
54
+ export class TiRawDump {
55
+ private stopPromise: Promise<Resp<TiRawDumpArchive>> | null = null;
56
+
57
+ private constructor(private readonly nativeId: number) {}
58
+
59
+ stop(): Promise<Resp<TiRawDumpArchive>> {
60
+ if (this.stopPromise === null) {
61
+ this.stopPromise = NativeTiRtc.rawDumpStop(this.nativeId).then((result) => {
62
+ if (result.code !== 0 || result.archive === null) {
63
+ if (!result.terminal) this.stopPromise = null;
64
+ return failure<TiRawDumpArchive>(result.code);
65
+ }
66
+ const archive = archiveFromNative(result.archive);
67
+ return {success: true, code: null, message: null, data: archive};
68
+ }).catch(() => {
69
+ this.stopPromise = null;
70
+ return failure<TiRawDumpArchive>(TIRTC_ERROR_INVALID_ARGUMENT);
71
+ });
72
+ }
73
+ return this.stopPromise!;
74
+ }
75
+
76
+ async dispose(): Promise<number> {
77
+ const result = await this.stop();
78
+ return result.success ? 0 : result.code ?? TIRTC_ERROR_INVALID_ARGUMENT;
79
+ }
80
+ }
81
+
82
+ export function createRawDumpFromNative(nativeId: number): TiRawDump {
83
+ return Reflect.construct(TiRawDump, [nativeId]) as TiRawDump;
84
+ }
package/src/runtime.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import {isPlainObject, TIRTC_ERROR_INVALID_ARGUMENT} from './errors';
2
2
  import {NativeTiRtc} from './native';
3
- import {logRtcOutcome} from './logging_internal';
3
+ import {logBuildIdentity, logRtcOutcome} from './logging_internal';
4
4
  import type {TiRtcInitOptions} from './types';
5
5
 
6
6
  export class TiRtc {
@@ -15,6 +15,7 @@ export class TiRtc {
15
15
  endpoint: typeof config.endpoint === 'string' ? config.endpoint : '',
16
16
  consoleLogEnabled: typeof config.consoleLogEnabled === 'boolean' ? config.consoleLogEnabled : false,
17
17
  }).then((code) => {
18
+ if (code === 0) logBuildIdentity('rtc');
18
19
  logRtcOutcome('runtime_initialize', code);
19
20
  return code;
20
21
  });