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.
- package/README.md +23 -7
- package/TiRtcReactNative.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +2 -1
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcModule.kt +622 -28
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeCleanup.kt +115 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventBinder.kt +32 -8
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventSink.kt +16 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMaps.kt +62 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeModuleSupport.kt +6 -6
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeRegistry.kt +27 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeStorageSupport.kt +64 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcVideoOutputViewManager.kt +17 -2
- package/ios/TiRtcReactNative+Conn.mm +173 -2
- package/ios/TiRtcReactNative+Input.mm +18 -4
- package/ios/TiRtcReactNative+Output.mm +41 -4
- package/ios/TiRtcReactNative+Private.h +4 -0
- package/ios/TiRtcReactNative+Storage.mm +513 -0
- package/ios/TiRtcReactNative.h +2 -1
- package/ios/TiRtcReactNative.mm +109 -1
- package/ios/TiRtcReactNativeEventBinder.h +8 -0
- package/ios/TiRtcReactNativeEventBinder.mm +102 -0
- package/ios/TiRtcReactNativeEventSink.h +3 -0
- package/ios/TiRtcReactNativeEventSink.mm +16 -0
- package/ios/TiRtcReactNativeRegistry.h +5 -0
- package/ios/TiRtcReactNativeRegistry.mm +43 -0
- package/ios/TiRtcVideoOutputView.mm +11 -0
- package/lib/module/audio_input.js +33 -17
- package/lib/module/audio_input.js.map +1 -1
- package/lib/module/audio_output.js +27 -10
- package/lib/module/audio_output.js.map +1 -1
- package/lib/module/conn.js +31 -7
- package/lib/module/conn.js.map +1 -1
- package/lib/module/errors.js +4 -3
- package/lib/module/errors.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/logging.js +9 -1
- package/lib/module/logging.js.map +1 -1
- package/lib/module/logging_internal.js +27 -0
- package/lib/module/logging_internal.js.map +1 -0
- package/lib/module/recording.js +210 -0
- package/lib/module/recording.js.map +1 -0
- package/lib/module/runtime.js +13 -5
- package/lib/module/runtime.js.map +1 -1
- package/lib/module/specs/NativeTiRtc.js.map +1 -1
- package/lib/module/storage.js +706 -0
- package/lib/module/storage.js.map +1 -0
- package/lib/module/video_input.js +40 -22
- package/lib/module/video_input.js.map +1 -1
- package/lib/module/video_output.js +51 -10
- package/lib/module/video_output.js.map +1 -1
- package/lib/typescript/audio_input.d.ts +1 -0
- package/lib/typescript/conn.d.ts +2 -0
- package/lib/typescript/errors.d.ts +3 -2
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/logging_internal.d.ts +2 -0
- package/lib/typescript/recording.d.ts +53 -0
- package/lib/typescript/runtime.d.ts +2 -0
- package/lib/typescript/specs/NativeTiRtc.d.ts +88 -0
- package/lib/typescript/storage.d.ts +204 -0
- package/lib/typescript/video_input.d.ts +1 -0
- package/lib/typescript/video_output.d.ts +3 -0
- package/package.json +1 -1
- package/src/audio_input.ts +85 -59
- package/src/audio_output.ts +30 -17
- package/src/conn.ts +46 -28
- package/src/errors.ts +4 -3
- package/src/index.ts +54 -0
- package/src/logging.ts +7 -1
- package/src/logging_internal.ts +23 -0
- package/src/recording.ts +271 -0
- package/src/runtime.ts +14 -5
- package/src/specs/NativeTiRtc.ts +123 -0
- package/src/storage.ts +769 -0
- package/src/video_input.ts +97 -62
- package/src/video_output.ts +54 -16
package/src/storage.ts
ADDED
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {View, type ViewProps} from 'react-native';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
isFiniteSafeInteger,
|
|
6
|
+
isPlainObject,
|
|
7
|
+
isUInt8,
|
|
8
|
+
normalizeCreateResult,
|
|
9
|
+
TIRTC_ERROR_INVALID_ARGUMENT,
|
|
10
|
+
TIRTC_ERROR_OK,
|
|
11
|
+
} from './errors';
|
|
12
|
+
import {registerNativeEventHandler, unregisterNativeEventHandler} from './events';
|
|
13
|
+
import {TiRtcLogging} from './logging';
|
|
14
|
+
import {NativeTiRtc, NativeTiRtcVideoOutputView, type NativeTiRtcEvent} from './native';
|
|
15
|
+
import {
|
|
16
|
+
clearNativeIdentity,
|
|
17
|
+
readNativeIdentity,
|
|
18
|
+
setNativeIdentity,
|
|
19
|
+
subscribeNativeIdentity,
|
|
20
|
+
type NativeIdentity,
|
|
21
|
+
} from './native_object_id';
|
|
22
|
+
import {NativeMediaFileBase, recordingFailure, type Resp, type TiRtcGalleryAsset} from './recording';
|
|
23
|
+
|
|
24
|
+
export const TI_CLOUD_STORAGE_ERROR_OK = 0 as const;
|
|
25
|
+
export const TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT = 6000 as const;
|
|
26
|
+
export const TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED = 6001 as const;
|
|
27
|
+
export const TI_CLOUD_STORAGE_ERROR_TOKEN_EXPIRED = 6014 as const;
|
|
28
|
+
export const TI_CLOUD_STORAGE_ERROR_ALREADY_INITIALIZED = 6022 as const;
|
|
29
|
+
export const TI_CLOUD_STORAGE_ERROR_PERMISSION_DENIED = 6024 as const;
|
|
30
|
+
export const TI_CLOUD_STORAGE_ERROR_IN_USE = 6026 as const;
|
|
31
|
+
export const TI_CLOUD_STORAGE_ERROR_NOT_STARTED = 6027 as const;
|
|
32
|
+
export const TI_CLOUD_STORAGE_ERROR_NOT_BOUND = 6029 as const;
|
|
33
|
+
export const TI_CLOUD_STORAGE_ERROR_NOT_CONFIGURED = 6030 as const;
|
|
34
|
+
export const TI_CLOUD_STORAGE_ERROR_RESOURCE_EXHAUSTED = 6043 as const;
|
|
35
|
+
export const TI_CLOUD_STORAGE_ERROR_FILE_WRITE_FAILED = 6046 as const;
|
|
36
|
+
export const TI_CLOUD_STORAGE_ERROR_UNSUPPORTED_FORMAT = 6113 as const;
|
|
37
|
+
export const TI_CLOUD_STORAGE_ERROR_IO_FAILED = 6114 as const;
|
|
38
|
+
export const TI_CLOUD_STORAGE_ERROR_CANCELLED = 6115 as const;
|
|
39
|
+
export const TI_CLOUD_STORAGE_ERROR_RANGE_TOO_LARGE = 6117 as const;
|
|
40
|
+
export const TI_CLOUD_STORAGE_ERROR_NO_FRAME = 6118 as const;
|
|
41
|
+
export const TI_CLOUD_STORAGE_ERROR_NO_RECORDABLE_MEDIA = 6119 as const;
|
|
42
|
+
export const TI_CLOUD_STORAGE_ERROR_RECORDING_OVERRUN = 6120 as const;
|
|
43
|
+
export const TI_CLOUD_STORAGE_ERROR_RECORDING_UNREADABLE = 6122 as const;
|
|
44
|
+
export const TI_CLOUD_STORAGE_ERROR_UNAVAILABLE = 6123 as const;
|
|
45
|
+
export const TI_CLOUD_STORAGE_ERROR_STOPPED = 6124 as const;
|
|
46
|
+
|
|
47
|
+
export type TiCloudStorageRecordingRange = Readonly<{
|
|
48
|
+
startTimeMs: number;
|
|
49
|
+
endTimeMs: number;
|
|
50
|
+
}>;
|
|
51
|
+
export type TiCloudStorageRecordingRangesResult = Readonly<{
|
|
52
|
+
code: number;
|
|
53
|
+
recordings: ReadonlyArray<TiCloudStorageRecordingRange>;
|
|
54
|
+
}>;
|
|
55
|
+
export type TiCloudStorageRecordingDay = Readonly<{
|
|
56
|
+
date: string;
|
|
57
|
+
hasRecording: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
export type TiCloudStorageRecordingDaysResult = Readonly<{
|
|
60
|
+
code: number;
|
|
61
|
+
days: ReadonlyArray<TiCloudStorageRecordingDay>;
|
|
62
|
+
}>;
|
|
63
|
+
|
|
64
|
+
export const TiCloudStorageReplaySpeed = {
|
|
65
|
+
x0_125: 'x0_125',
|
|
66
|
+
x0_25: 'x0_25',
|
|
67
|
+
x0_5: 'x0_5',
|
|
68
|
+
x1: 'x1',
|
|
69
|
+
x2: 'x2',
|
|
70
|
+
x4: 'x4',
|
|
71
|
+
x8: 'x8',
|
|
72
|
+
} as const;
|
|
73
|
+
export type TiCloudStorageReplaySpeed = (typeof TiCloudStorageReplaySpeed)[keyof typeof TiCloudStorageReplaySpeed];
|
|
74
|
+
const tiCloudStorageLogTag = 'TIRTC_REACT_NATIVE_CLOUD_STORAGE';
|
|
75
|
+
|
|
76
|
+
function tiCloudStorageLogOutcome(operation: string, code: number, details = ''): void {
|
|
77
|
+
const message = `[${operation}] outcome=${code === TI_CLOUD_STORAGE_ERROR_OK ? 'ok' : 'failed'} error_code=${code}${details}`;
|
|
78
|
+
if (code === TI_CLOUD_STORAGE_ERROR_OK) TiRtcLogging.i(tiCloudStorageLogTag, message);
|
|
79
|
+
else TiRtcLogging.e(tiCloudStorageLogTag, message);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const TiCloudStorageAudioOutputState = {
|
|
83
|
+
idle: 'idle',
|
|
84
|
+
buffering: 'buffering',
|
|
85
|
+
playing: 'playing',
|
|
86
|
+
failed: 'failed',
|
|
87
|
+
paused: 'paused',
|
|
88
|
+
completed: 'completed',
|
|
89
|
+
} as const;
|
|
90
|
+
export type TiCloudStorageAudioOutputState = (typeof TiCloudStorageAudioOutputState)[keyof typeof TiCloudStorageAudioOutputState];
|
|
91
|
+
export const TiCloudStorageVideoOutputState = {
|
|
92
|
+
idle: 'idle',
|
|
93
|
+
buffering: 'buffering',
|
|
94
|
+
rendering: 'rendering',
|
|
95
|
+
failed: 'failed',
|
|
96
|
+
paused: 'paused',
|
|
97
|
+
completed: 'completed',
|
|
98
|
+
} as const;
|
|
99
|
+
export type TiCloudStorageVideoOutputState = (typeof TiCloudStorageVideoOutputState)[keyof typeof TiCloudStorageVideoOutputState];
|
|
100
|
+
|
|
101
|
+
export type TiCloudStorageStartRecordingOptions = Readonly<{
|
|
102
|
+
videoChannelId: number;
|
|
103
|
+
audioChannelId?: number;
|
|
104
|
+
}>;
|
|
105
|
+
export type TiCloudStorageExportOptions = Readonly<{
|
|
106
|
+
startTimeMs: number;
|
|
107
|
+
endTimeMs: number;
|
|
108
|
+
videoChannelId: number;
|
|
109
|
+
audioChannelId?: number;
|
|
110
|
+
}>;
|
|
111
|
+
export type TiCloudStorageGalleryAsset = TiRtcGalleryAsset;
|
|
112
|
+
|
|
113
|
+
export interface TiCloudStorageRecordingFile {
|
|
114
|
+
readonly path: string;
|
|
115
|
+
readonly durationMs: number;
|
|
116
|
+
moveToGallery(fileName?: string): Promise<Resp<TiCloudStorageGalleryAsset>>;
|
|
117
|
+
delete(): Promise<number>;
|
|
118
|
+
}
|
|
119
|
+
export interface TiCloudStorageSnapshotFile {
|
|
120
|
+
readonly path: string;
|
|
121
|
+
moveToGallery(fileName?: string): Promise<Resp<TiCloudStorageGalleryAsset>>;
|
|
122
|
+
delete(): Promise<number>;
|
|
123
|
+
}
|
|
124
|
+
class RecordingFile extends NativeMediaFileBase implements TiCloudStorageRecordingFile {
|
|
125
|
+
constructor(
|
|
126
|
+
path: string,
|
|
127
|
+
readonly durationMs: number,
|
|
128
|
+
id: number,
|
|
129
|
+
) {
|
|
130
|
+
super(path, id, 'video');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
class SnapshotFile extends NativeMediaFileBase implements TiCloudStorageSnapshotFile {
|
|
134
|
+
constructor(path: string, id: number) {
|
|
135
|
+
super(path, id, 'image');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function success<T>(data: T): Resp<T> {
|
|
140
|
+
return {success: true, code: null, message: null, data};
|
|
141
|
+
}
|
|
142
|
+
function validTime(value: number): boolean {
|
|
143
|
+
return isFiniteSafeInteger(value) && value >= 0;
|
|
144
|
+
}
|
|
145
|
+
function speedToNative(speed: TiCloudStorageReplaySpeed): number | null {
|
|
146
|
+
switch (speed) {
|
|
147
|
+
case TiCloudStorageReplaySpeed.x0_125:
|
|
148
|
+
return 6;
|
|
149
|
+
case TiCloudStorageReplaySpeed.x0_25:
|
|
150
|
+
return 5;
|
|
151
|
+
case TiCloudStorageReplaySpeed.x0_5:
|
|
152
|
+
return 4;
|
|
153
|
+
case TiCloudStorageReplaySpeed.x1:
|
|
154
|
+
return 0;
|
|
155
|
+
case TiCloudStorageReplaySpeed.x2:
|
|
156
|
+
return 1;
|
|
157
|
+
case TiCloudStorageReplaySpeed.x4:
|
|
158
|
+
return 2;
|
|
159
|
+
case TiCloudStorageReplaySpeed.x8:
|
|
160
|
+
return 3;
|
|
161
|
+
default:
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function speedFromNative(speed: number): TiCloudStorageReplaySpeed | null {
|
|
166
|
+
switch (speed) {
|
|
167
|
+
case 6:
|
|
168
|
+
return TiCloudStorageReplaySpeed.x0_125;
|
|
169
|
+
case 5:
|
|
170
|
+
return TiCloudStorageReplaySpeed.x0_25;
|
|
171
|
+
case 4:
|
|
172
|
+
return TiCloudStorageReplaySpeed.x0_5;
|
|
173
|
+
case 0:
|
|
174
|
+
return TiCloudStorageReplaySpeed.x1;
|
|
175
|
+
case 1:
|
|
176
|
+
return TiCloudStorageReplaySpeed.x2;
|
|
177
|
+
case 2:
|
|
178
|
+
return TiCloudStorageReplaySpeed.x4;
|
|
179
|
+
case 3:
|
|
180
|
+
return TiCloudStorageReplaySpeed.x8;
|
|
181
|
+
default:
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function audioStateFromNative(state: unknown): TiCloudStorageAudioOutputState {
|
|
186
|
+
switch (state) {
|
|
187
|
+
case TiCloudStorageAudioOutputState.idle:
|
|
188
|
+
case TiCloudStorageAudioOutputState.buffering:
|
|
189
|
+
case TiCloudStorageAudioOutputState.playing:
|
|
190
|
+
case TiCloudStorageAudioOutputState.failed:
|
|
191
|
+
case TiCloudStorageAudioOutputState.paused:
|
|
192
|
+
case TiCloudStorageAudioOutputState.completed:
|
|
193
|
+
return state;
|
|
194
|
+
default:
|
|
195
|
+
return TiCloudStorageAudioOutputState.failed;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function videoStateFromNative(state: unknown): TiCloudStorageVideoOutputState {
|
|
199
|
+
switch (state) {
|
|
200
|
+
case TiCloudStorageVideoOutputState.idle:
|
|
201
|
+
case TiCloudStorageVideoOutputState.buffering:
|
|
202
|
+
case TiCloudStorageVideoOutputState.rendering:
|
|
203
|
+
case TiCloudStorageVideoOutputState.failed:
|
|
204
|
+
case TiCloudStorageVideoOutputState.paused:
|
|
205
|
+
case TiCloudStorageVideoOutputState.completed:
|
|
206
|
+
return state;
|
|
207
|
+
default:
|
|
208
|
+
return TiCloudStorageVideoOutputState.failed;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export class TiCloudStorage {
|
|
213
|
+
private identity: NativeIdentity | null = null;
|
|
214
|
+
private disposed = false;
|
|
215
|
+
constructor(private token: string) {}
|
|
216
|
+
|
|
217
|
+
static init(
|
|
218
|
+
options: Readonly<{
|
|
219
|
+
appId: string;
|
|
220
|
+
endpoint?: string;
|
|
221
|
+
consoleLogEnabled?: boolean;
|
|
222
|
+
}>,
|
|
223
|
+
): Promise<number> {
|
|
224
|
+
if (!isPlainObject(options) || typeof options.appId !== 'string' || options.appId.length === 0) {
|
|
225
|
+
tiCloudStorageLogOutcome('cloud_storage_initialize', TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT);
|
|
226
|
+
return Promise.resolve(TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT);
|
|
227
|
+
}
|
|
228
|
+
return NativeTiRtc.tiCloudStorageInitialize({
|
|
229
|
+
appId: options.appId,
|
|
230
|
+
endpoint: options.endpoint ?? '',
|
|
231
|
+
consoleLogEnabled: options.consoleLogEnabled ?? false,
|
|
232
|
+
}).then((code) => {
|
|
233
|
+
tiCloudStorageLogOutcome('cloud_storage_initialize', code);
|
|
234
|
+
return code;
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
static shutdown(): number {
|
|
238
|
+
const nativeCode = NativeTiRtc.tiCloudStorageShutdown();
|
|
239
|
+
const code = nativeCode === TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED ? TI_CLOUD_STORAGE_ERROR_OK : nativeCode;
|
|
240
|
+
tiCloudStorageLogOutcome('cloud_storage_shutdown', code);
|
|
241
|
+
return code;
|
|
242
|
+
}
|
|
243
|
+
static errorToString(code: number): string {
|
|
244
|
+
return NativeTiRtc.errorToString(code);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
updateToken(token: string): number {
|
|
248
|
+
if (token.length === 0) return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
249
|
+
const identity = this.ensureIdentity();
|
|
250
|
+
if (typeof identity === 'number') return identity;
|
|
251
|
+
const code = NativeTiRtc.tiCloudStorageUpdateToken(identity.objectId, token);
|
|
252
|
+
if (code === 0) this.token = token;
|
|
253
|
+
tiCloudStorageLogOutcome('cloud_storage_update_token', code);
|
|
254
|
+
return code;
|
|
255
|
+
}
|
|
256
|
+
listRecordings(startTimeMs: number, endTimeMs: number): Promise<TiCloudStorageRecordingRangesResult> {
|
|
257
|
+
if (!validTime(startTimeMs) || !validTime(endTimeMs) || endTimeMs <= startTimeMs) {
|
|
258
|
+
return Promise.resolve({
|
|
259
|
+
code: TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT,
|
|
260
|
+
recordings: [],
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const identity = this.ensureIdentity();
|
|
264
|
+
if (typeof identity === 'number') {
|
|
265
|
+
tiCloudStorageLogOutcome('recording_list', identity);
|
|
266
|
+
return Promise.resolve({code: identity, recordings: []});
|
|
267
|
+
}
|
|
268
|
+
TiRtcLogging.i(tiCloudStorageLogTag, `[recording_list] outcome=started range_duration_ms=${endTimeMs - startTimeMs}`);
|
|
269
|
+
return NativeTiRtc.tiCloudStorageListRecordings(identity.objectId, startTimeMs, endTimeMs).then((result) => {
|
|
270
|
+
tiCloudStorageLogOutcome('recording_list', result.code, ` recording_count=${result.recordings.length}`);
|
|
271
|
+
return result;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
listRecordingDays(
|
|
275
|
+
startDate: string,
|
|
276
|
+
endDate: string,
|
|
277
|
+
timeZoneId = 'Asia/Shanghai',
|
|
278
|
+
): Promise<TiCloudStorageRecordingDaysResult> {
|
|
279
|
+
if (
|
|
280
|
+
typeof startDate !== 'string' ||
|
|
281
|
+
typeof endDate !== 'string' ||
|
|
282
|
+
typeof timeZoneId !== 'string' ||
|
|
283
|
+
timeZoneId.length === 0
|
|
284
|
+
) {
|
|
285
|
+
return Promise.resolve({code: TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT, days: []});
|
|
286
|
+
}
|
|
287
|
+
const identity = this.ensureIdentity();
|
|
288
|
+
if (typeof identity === 'number') {
|
|
289
|
+
tiCloudStorageLogOutcome('recording_days_list', identity);
|
|
290
|
+
return Promise.resolve({code: identity, days: []});
|
|
291
|
+
}
|
|
292
|
+
TiRtcLogging.i(tiCloudStorageLogTag, '[recording_days_list] outcome=started');
|
|
293
|
+
return NativeTiRtc.tiCloudStorageListRecordingDays(
|
|
294
|
+
identity.objectId,
|
|
295
|
+
startDate,
|
|
296
|
+
endDate,
|
|
297
|
+
timeZoneId,
|
|
298
|
+
).then((result) => {
|
|
299
|
+
tiCloudStorageLogOutcome('recording_days_list', result.code, ` day_count=${result.days.length}`);
|
|
300
|
+
return result;
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
createReplay(): TiCloudStorageReplay {
|
|
304
|
+
return new TiCloudStorageReplay(this);
|
|
305
|
+
}
|
|
306
|
+
exportRecording(options: TiCloudStorageExportOptions, onProgress?: (progress: number) => void): Resp<TiCloudStorageExportTask> {
|
|
307
|
+
if (
|
|
308
|
+
!validTime(options.startTimeMs) ||
|
|
309
|
+
!validTime(options.endTimeMs) ||
|
|
310
|
+
options.endTimeMs <= options.startTimeMs ||
|
|
311
|
+
!isUInt8(options.videoChannelId) ||
|
|
312
|
+
(options.audioChannelId !== undefined && !isUInt8(options.audioChannelId))
|
|
313
|
+
) {
|
|
314
|
+
return recordingFailure(TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT);
|
|
315
|
+
}
|
|
316
|
+
const identity = this.ensureIdentity();
|
|
317
|
+
if (typeof identity === 'number') return recordingFailure(identity);
|
|
318
|
+
const result = NativeTiRtc.tiCloudStorageExportRecording(
|
|
319
|
+
identity.objectId,
|
|
320
|
+
options.startTimeMs,
|
|
321
|
+
options.endTimeMs,
|
|
322
|
+
options.videoChannelId,
|
|
323
|
+
options.audioChannelId ?? null,
|
|
324
|
+
);
|
|
325
|
+
tiCloudStorageLogOutcome('export_start', result.code, ` range_duration_ms=${options.endTimeMs - options.startTimeMs}`);
|
|
326
|
+
return result.code === 0 && result.taskId !== null
|
|
327
|
+
? success(new TiCloudStorageExportTask(result.taskId, this, onProgress))
|
|
328
|
+
: recordingFailure(result.code);
|
|
329
|
+
}
|
|
330
|
+
dispose(): number {
|
|
331
|
+
if (this.disposed) return 0;
|
|
332
|
+
if (this.identity === null) {
|
|
333
|
+
this.disposed = true;
|
|
334
|
+
this.token = '';
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
const identity = this.identity;
|
|
338
|
+
const code = NativeTiRtc.tiCloudStorageDispose(identity.objectId);
|
|
339
|
+
if (code === 0) {
|
|
340
|
+
unregisterNativeEventHandler(identity);
|
|
341
|
+
clearNativeIdentity(this);
|
|
342
|
+
this.identity = null;
|
|
343
|
+
this.disposed = true;
|
|
344
|
+
this.token = '';
|
|
345
|
+
}
|
|
346
|
+
return code;
|
|
347
|
+
}
|
|
348
|
+
private ensureIdentity(): NativeIdentity | number {
|
|
349
|
+
if (this.disposed) return TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED;
|
|
350
|
+
if (this.identity !== null) return this.identity;
|
|
351
|
+
if (this.token.length === 0) return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
352
|
+
const result = normalizeCreateResult(NativeTiRtc.tiCloudStorageCreate(this.token));
|
|
353
|
+
if (result.code !== 0) return result.code;
|
|
354
|
+
this.identity = {
|
|
355
|
+
objectId: result.objectId,
|
|
356
|
+
generation: result.generation,
|
|
357
|
+
};
|
|
358
|
+
setNativeIdentity(this, this.identity);
|
|
359
|
+
return this.identity;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function tiCloudStorageNativeIdentity(cloudStorage: TiCloudStorage): NativeIdentity | number {
|
|
364
|
+
return (cloudStorage as unknown as {ensureIdentity(): NativeIdentity | number}).ensureIdentity();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export class TiCloudStorageReplay {
|
|
368
|
+
private identity: NativeIdentity | null = null;
|
|
369
|
+
private disposed = false;
|
|
370
|
+
private currentSpeed: TiCloudStorageReplaySpeed = 'x1';
|
|
371
|
+
private currentTime: number | null = null;
|
|
372
|
+
onTimeChanged: ((timeMs: number) => void) | null = null;
|
|
373
|
+
onError: ((code: number) => void) | null = null;
|
|
374
|
+
onCompleted: (() => void) | null = null;
|
|
375
|
+
constructor(private readonly cloudStorage: TiCloudStorage) {}
|
|
376
|
+
get speed(): TiCloudStorageReplaySpeed {
|
|
377
|
+
if (this.identity !== null) {
|
|
378
|
+
const info = NativeTiRtc.tiCloudStorageReplayGetInfo(this.identity.objectId);
|
|
379
|
+
if (info.code === 0) this.currentSpeed = speedFromNative(info.speed) ?? this.currentSpeed;
|
|
380
|
+
}
|
|
381
|
+
return this.currentSpeed;
|
|
382
|
+
}
|
|
383
|
+
get currentTimeMs(): number | null {
|
|
384
|
+
if (this.identity !== null) {
|
|
385
|
+
const info = NativeTiRtc.tiCloudStorageReplayGetInfo(this.identity.objectId);
|
|
386
|
+
if (info.code === 0) this.currentTime = info.currentTimeMs;
|
|
387
|
+
}
|
|
388
|
+
return this.currentTime;
|
|
389
|
+
}
|
|
390
|
+
play(startTimeMs: number, endTimeMs: number, initialTimeMs?: number): number {
|
|
391
|
+
if (
|
|
392
|
+
!validTime(startTimeMs) ||
|
|
393
|
+
!validTime(endTimeMs) ||
|
|
394
|
+
endTimeMs <= startTimeMs ||
|
|
395
|
+
(initialTimeMs !== undefined &&
|
|
396
|
+
(!validTime(initialTimeMs) || initialTimeMs < startTimeMs || initialTimeMs >= endTimeMs))
|
|
397
|
+
)
|
|
398
|
+
return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
399
|
+
const identity = this.ensureIdentity();
|
|
400
|
+
const code =
|
|
401
|
+
typeof identity === 'number'
|
|
402
|
+
? identity
|
|
403
|
+
: NativeTiRtc.tiCloudStorageReplayPlay(identity.objectId, startTimeMs, endTimeMs, initialTimeMs ?? null);
|
|
404
|
+
tiCloudStorageLogOutcome('replay_play', code, ` range_duration_ms=${endTimeMs - startTimeMs}`);
|
|
405
|
+
return code;
|
|
406
|
+
}
|
|
407
|
+
pause(): number {
|
|
408
|
+
const code = this.command(NativeTiRtc.tiCloudStorageReplayPause);
|
|
409
|
+
tiCloudStorageLogOutcome('replay_pause', code);
|
|
410
|
+
return code;
|
|
411
|
+
}
|
|
412
|
+
resume(): number {
|
|
413
|
+
const code = this.command(NativeTiRtc.tiCloudStorageReplayResume);
|
|
414
|
+
tiCloudStorageLogOutcome('replay_resume', code);
|
|
415
|
+
return code;
|
|
416
|
+
}
|
|
417
|
+
seek(timeMs: number): number {
|
|
418
|
+
if (!validTime(timeMs)) return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
419
|
+
const identity = this.ensureIdentity();
|
|
420
|
+
const code = typeof identity === 'number' ? identity : NativeTiRtc.tiCloudStorageReplaySeek(identity.objectId, timeMs);
|
|
421
|
+
tiCloudStorageLogOutcome('replay_seek', code);
|
|
422
|
+
return code;
|
|
423
|
+
}
|
|
424
|
+
setSpeed(speed: TiCloudStorageReplaySpeed): number {
|
|
425
|
+
const index = speedToNative(speed);
|
|
426
|
+
if (index === null) return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
427
|
+
const identity = this.ensureIdentity();
|
|
428
|
+
if (typeof identity === 'number') return identity;
|
|
429
|
+
const code = NativeTiRtc.tiCloudStorageReplaySetSpeed(identity.objectId, index);
|
|
430
|
+
if (code === 0) this.currentSpeed = speed;
|
|
431
|
+
tiCloudStorageLogOutcome('replay_speed', code, ` speed=${speed}`);
|
|
432
|
+
return code;
|
|
433
|
+
}
|
|
434
|
+
startRecording(options: TiCloudStorageStartRecordingOptions): Resp<TiCloudStorageRecordingTask> {
|
|
435
|
+
if (
|
|
436
|
+
!isUInt8(options.videoChannelId) ||
|
|
437
|
+
(options.audioChannelId !== undefined && !isUInt8(options.audioChannelId))
|
|
438
|
+
) {
|
|
439
|
+
return recordingFailure(TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT);
|
|
440
|
+
}
|
|
441
|
+
const identity = this.ensureIdentity();
|
|
442
|
+
if (typeof identity === 'number') return recordingFailure(identity);
|
|
443
|
+
const result = NativeTiRtc.tiCloudStorageReplayStartRecording(
|
|
444
|
+
identity.objectId,
|
|
445
|
+
options.videoChannelId,
|
|
446
|
+
options.audioChannelId ?? null,
|
|
447
|
+
);
|
|
448
|
+
tiCloudStorageLogOutcome('replay_recording_start', result.code);
|
|
449
|
+
return result.code === 0 && result.taskId !== null
|
|
450
|
+
? success(new TiCloudStorageRecordingTask(result.taskId, this))
|
|
451
|
+
: recordingFailure(result.code);
|
|
452
|
+
}
|
|
453
|
+
stop(): number {
|
|
454
|
+
const code = this.command(NativeTiRtc.tiCloudStorageReplayStop);
|
|
455
|
+
if (code === 0) this.currentTime = null;
|
|
456
|
+
tiCloudStorageLogOutcome('replay_stop', code);
|
|
457
|
+
return code;
|
|
458
|
+
}
|
|
459
|
+
dispose(): number {
|
|
460
|
+
if (this.disposed) return 0;
|
|
461
|
+
if (this.identity === null) {
|
|
462
|
+
this.disposed = true;
|
|
463
|
+
this.onTimeChanged = null;
|
|
464
|
+
this.onError = null;
|
|
465
|
+
this.onCompleted = null;
|
|
466
|
+
return 0;
|
|
467
|
+
}
|
|
468
|
+
const identity = this.identity;
|
|
469
|
+
const code = NativeTiRtc.tiCloudStorageReplayDispose(identity.objectId);
|
|
470
|
+
if (code === 0) {
|
|
471
|
+
unregisterNativeEventHandler(identity);
|
|
472
|
+
clearNativeIdentity(this);
|
|
473
|
+
this.identity = null;
|
|
474
|
+
this.disposed = true;
|
|
475
|
+
this.onTimeChanged = null;
|
|
476
|
+
this.onError = null;
|
|
477
|
+
this.onCompleted = null;
|
|
478
|
+
}
|
|
479
|
+
tiCloudStorageLogOutcome('replay_dispose', code);
|
|
480
|
+
return code;
|
|
481
|
+
}
|
|
482
|
+
private command(operation: (id: number) => number): number {
|
|
483
|
+
const identity = this.ensureIdentity();
|
|
484
|
+
return typeof identity === 'number' ? identity : operation(identity.objectId);
|
|
485
|
+
}
|
|
486
|
+
private ensureIdentity(): NativeIdentity | number {
|
|
487
|
+
if (this.disposed) return TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED;
|
|
488
|
+
if (this.identity !== null) return this.identity;
|
|
489
|
+
const cloudStorage = tiCloudStorageNativeIdentity(this.cloudStorage);
|
|
490
|
+
if (typeof cloudStorage === 'number') return cloudStorage;
|
|
491
|
+
const result = normalizeCreateResult(NativeTiRtc.tiCloudStorageCreateReplay(cloudStorage.objectId));
|
|
492
|
+
if (result.code !== 0) return result.code;
|
|
493
|
+
this.identity = {
|
|
494
|
+
objectId: result.objectId,
|
|
495
|
+
generation: result.generation,
|
|
496
|
+
};
|
|
497
|
+
setNativeIdentity(this, this.identity);
|
|
498
|
+
registerNativeEventHandler(this.identity, (event) => this.handleEvent(event));
|
|
499
|
+
return this.identity;
|
|
500
|
+
}
|
|
501
|
+
private handleEvent(event: NativeTiRtcEvent): void {
|
|
502
|
+
if (event.type === 'tiCloudStorageReplayTimeChanged' && event.timestampMs !== undefined) {
|
|
503
|
+
this.currentTime = event.timestampMs;
|
|
504
|
+
this.onTimeChanged?.(event.timestampMs);
|
|
505
|
+
} else if (event.type === 'tiCloudStorageReplayCompleted') this.onCompleted?.();
|
|
506
|
+
else if (event.type === 'tiCloudStorageReplayError') this.onError?.(event.code ?? TI_CLOUD_STORAGE_ERROR_UNAVAILABLE);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function nativeReplayIdentity(replay: TiCloudStorageReplay): NativeIdentity | number {
|
|
511
|
+
return (replay as unknown as {ensureIdentity(): NativeIdentity | number}).ensureIdentity();
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
abstract class TiCloudStorageOutputBase {
|
|
515
|
+
protected identity: NativeIdentity | null = null;
|
|
516
|
+
protected disposed = false;
|
|
517
|
+
protected bound = false;
|
|
518
|
+
protected abstract create(): ReturnType<typeof NativeTiRtc.tiCloudStorageCreateAudioOutput>;
|
|
519
|
+
protected ensureIdentity(): NativeIdentity | number {
|
|
520
|
+
if (this.disposed) return TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED;
|
|
521
|
+
if (this.identity !== null) return this.identity;
|
|
522
|
+
const result = normalizeCreateResult(this.create());
|
|
523
|
+
if (result.code !== 0) return result.code;
|
|
524
|
+
this.identity = {
|
|
525
|
+
objectId: result.objectId,
|
|
526
|
+
generation: result.generation,
|
|
527
|
+
};
|
|
528
|
+
setNativeIdentity(this, this.identity);
|
|
529
|
+
registerNativeEventHandler(this.identity, (event) => this.handleEvent(event));
|
|
530
|
+
return this.identity;
|
|
531
|
+
}
|
|
532
|
+
protected abstract handleEvent(event: NativeTiRtcEvent): void;
|
|
533
|
+
protected releaseIdentity(): void {
|
|
534
|
+
if (this.identity !== null) {
|
|
535
|
+
unregisterNativeEventHandler(this.identity);
|
|
536
|
+
clearNativeIdentity(this);
|
|
537
|
+
}
|
|
538
|
+
this.identity = null;
|
|
539
|
+
this.disposed = true;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export class TiCloudStorageAudioOutput extends TiCloudStorageOutputBase {
|
|
544
|
+
private currentState: TiCloudStorageAudioOutputState = 'idle';
|
|
545
|
+
onStateChanged: ((state: TiCloudStorageAudioOutputState) => void) | null = null;
|
|
546
|
+
onError: ((code: number) => void) | null = null;
|
|
547
|
+
protected create() {
|
|
548
|
+
return NativeTiRtc.tiCloudStorageCreateAudioOutput();
|
|
549
|
+
}
|
|
550
|
+
get state(): TiCloudStorageAudioOutputState {
|
|
551
|
+
if (this.identity !== null)
|
|
552
|
+
this.currentState = audioStateFromNative(
|
|
553
|
+
NativeTiRtc.tiCloudStorageAudioOutputGetState(this.identity.objectId).state ?? this.currentState,
|
|
554
|
+
);
|
|
555
|
+
return this.currentState;
|
|
556
|
+
}
|
|
557
|
+
attach(replay: TiCloudStorageReplay, channelId: number): number {
|
|
558
|
+
if (!isUInt8(channelId)) return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
559
|
+
const output = this.ensureIdentity();
|
|
560
|
+
const replayId = nativeReplayIdentity(replay);
|
|
561
|
+
if (typeof output === 'number') return output;
|
|
562
|
+
if (typeof replayId === 'number') return replayId;
|
|
563
|
+
const code = NativeTiRtc.tiCloudStorageAudioOutputAttach(output.objectId, replayId.objectId, channelId);
|
|
564
|
+
if (code === 0) this.bound = true;
|
|
565
|
+
tiCloudStorageLogOutcome('audio_output_attach', code);
|
|
566
|
+
return code;
|
|
567
|
+
}
|
|
568
|
+
setVolume(volumePercent: number): number {
|
|
569
|
+
if (!isFiniteSafeInteger(volumePercent) || volumePercent < 0 || volumePercent > 100)
|
|
570
|
+
return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
571
|
+
const output = this.ensureIdentity();
|
|
572
|
+
return typeof output === 'number' ? output : NativeTiRtc.tiCloudStorageAudioOutputSetVolume(output.objectId, volumePercent);
|
|
573
|
+
}
|
|
574
|
+
detach(): number {
|
|
575
|
+
if (this.identity === null) return 0;
|
|
576
|
+
const code = NativeTiRtc.tiCloudStorageAudioOutputDetach(this.identity.objectId);
|
|
577
|
+
if (code === 0) {
|
|
578
|
+
this.bound = false;
|
|
579
|
+
this.currentState = TiCloudStorageAudioOutputState.idle;
|
|
580
|
+
}
|
|
581
|
+
tiCloudStorageLogOutcome('audio_output_detach', code);
|
|
582
|
+
return code;
|
|
583
|
+
}
|
|
584
|
+
dispose(): number {
|
|
585
|
+
if (this.disposed) return 0;
|
|
586
|
+
if (this.bound) return TI_CLOUD_STORAGE_ERROR_IN_USE;
|
|
587
|
+
if (this.identity === null) {
|
|
588
|
+
this.disposed = true;
|
|
589
|
+
this.currentState = TiCloudStorageAudioOutputState.idle;
|
|
590
|
+
this.onStateChanged = null;
|
|
591
|
+
this.onError = null;
|
|
592
|
+
return 0;
|
|
593
|
+
}
|
|
594
|
+
const code = NativeTiRtc.tiCloudStorageAudioOutputDispose(this.identity.objectId);
|
|
595
|
+
if (code === 0) {
|
|
596
|
+
this.releaseIdentity();
|
|
597
|
+
this.currentState = TiCloudStorageAudioOutputState.idle;
|
|
598
|
+
this.onStateChanged = null;
|
|
599
|
+
this.onError = null;
|
|
600
|
+
}
|
|
601
|
+
tiCloudStorageLogOutcome('audio_output_dispose', code);
|
|
602
|
+
return code;
|
|
603
|
+
}
|
|
604
|
+
protected handleEvent(event: NativeTiRtcEvent): void {
|
|
605
|
+
if (event.type === 'audioOutputStateChanged' && event.state !== undefined) {
|
|
606
|
+
this.currentState = audioStateFromNative(event.state);
|
|
607
|
+
this.onStateChanged?.(this.currentState);
|
|
608
|
+
} else if (event.type === 'audioOutputError') this.onError?.(event.code ?? TI_CLOUD_STORAGE_ERROR_IO_FAILED);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export type TiCloudStorageVideoOutputViewProps = ViewProps &
|
|
613
|
+
Readonly<{
|
|
614
|
+
output: TiCloudStorageVideoOutput;
|
|
615
|
+
resizeMode?: 'contain' | 'cover' | 'stretch';
|
|
616
|
+
}>;
|
|
617
|
+
export class TiCloudStorageVideoOutput extends TiCloudStorageOutputBase {
|
|
618
|
+
private currentState: TiCloudStorageVideoOutputState = 'idle';
|
|
619
|
+
onStateChanged: ((state: TiCloudStorageVideoOutputState) => void) | null = null;
|
|
620
|
+
onError: ((code: number) => void) | null = null;
|
|
621
|
+
protected create() {
|
|
622
|
+
return NativeTiRtc.tiCloudStorageCreateVideoOutput();
|
|
623
|
+
}
|
|
624
|
+
get state(): TiCloudStorageVideoOutputState {
|
|
625
|
+
if (this.identity !== null)
|
|
626
|
+
this.currentState = videoStateFromNative(
|
|
627
|
+
NativeTiRtc.tiCloudStorageVideoOutputGetState(this.identity.objectId).state ?? this.currentState,
|
|
628
|
+
);
|
|
629
|
+
return this.currentState;
|
|
630
|
+
}
|
|
631
|
+
view(props: Omit<TiCloudStorageVideoOutputViewProps, 'output'> = {}): React.ReactElement {
|
|
632
|
+
this.ensureIdentity();
|
|
633
|
+
return React.createElement(TiCloudStorageVideoOutputView, {
|
|
634
|
+
...props,
|
|
635
|
+
output: this,
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
attach(replay: TiCloudStorageReplay, channelId: number): number {
|
|
639
|
+
if (!isUInt8(channelId)) return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
640
|
+
const output = this.ensureIdentity();
|
|
641
|
+
const replayId = nativeReplayIdentity(replay);
|
|
642
|
+
if (typeof output === 'number') return output;
|
|
643
|
+
if (typeof replayId === 'number') return replayId;
|
|
644
|
+
const code = NativeTiRtc.tiCloudStorageVideoOutputAttach(output.objectId, replayId.objectId, channelId);
|
|
645
|
+
if (code === 0) this.bound = true;
|
|
646
|
+
tiCloudStorageLogOutcome('video_output_attach', code);
|
|
647
|
+
return code;
|
|
648
|
+
}
|
|
649
|
+
async takeSnapshot(): Promise<Resp<TiCloudStorageSnapshotFile>> {
|
|
650
|
+
const output = this.ensureIdentity();
|
|
651
|
+
if (typeof output === 'number') return recordingFailure(output);
|
|
652
|
+
const result = await NativeTiRtc.tiCloudStorageVideoOutputTakeSnapshot(output.objectId);
|
|
653
|
+
tiCloudStorageLogOutcome('snapshot', result.code);
|
|
654
|
+
return result.code === 0 && result.fileId !== null && result.filePath !== null
|
|
655
|
+
? success(new SnapshotFile(result.filePath, result.fileId))
|
|
656
|
+
: recordingFailure(result.code);
|
|
657
|
+
}
|
|
658
|
+
detach(): number {
|
|
659
|
+
if (this.identity === null) return 0;
|
|
660
|
+
const code = NativeTiRtc.tiCloudStorageVideoOutputDetach(this.identity.objectId);
|
|
661
|
+
if (code === 0) {
|
|
662
|
+
this.bound = false;
|
|
663
|
+
this.currentState = TiCloudStorageVideoOutputState.idle;
|
|
664
|
+
}
|
|
665
|
+
tiCloudStorageLogOutcome('video_output_detach', code);
|
|
666
|
+
return code;
|
|
667
|
+
}
|
|
668
|
+
dispose(): number {
|
|
669
|
+
if (this.disposed) return 0;
|
|
670
|
+
if (this.bound) return TI_CLOUD_STORAGE_ERROR_IN_USE;
|
|
671
|
+
if (this.identity === null) {
|
|
672
|
+
this.disposed = true;
|
|
673
|
+
this.currentState = TiCloudStorageVideoOutputState.idle;
|
|
674
|
+
this.onStateChanged = null;
|
|
675
|
+
this.onError = null;
|
|
676
|
+
return 0;
|
|
677
|
+
}
|
|
678
|
+
const code = NativeTiRtc.tiCloudStorageVideoOutputDispose(this.identity.objectId);
|
|
679
|
+
if (code === 0) {
|
|
680
|
+
this.releaseIdentity();
|
|
681
|
+
this.currentState = TiCloudStorageVideoOutputState.idle;
|
|
682
|
+
this.onStateChanged = null;
|
|
683
|
+
this.onError = null;
|
|
684
|
+
}
|
|
685
|
+
tiCloudStorageLogOutcome('video_output_dispose', code);
|
|
686
|
+
return code;
|
|
687
|
+
}
|
|
688
|
+
protected handleEvent(event: NativeTiRtcEvent): void {
|
|
689
|
+
if (event.type === 'videoOutputStateChanged' && event.state !== undefined) {
|
|
690
|
+
this.currentState = videoStateFromNative(event.state);
|
|
691
|
+
this.onStateChanged?.(this.currentState);
|
|
692
|
+
} else if (event.type === 'videoOutputError') this.onError?.(event.code ?? TI_CLOUD_STORAGE_ERROR_IO_FAILED);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export function TiCloudStorageVideoOutputView(props: TiCloudStorageVideoOutputViewProps): React.ReactElement {
|
|
697
|
+
const {output, resizeMode = 'contain', ...viewProps} = props;
|
|
698
|
+
const identity = useOwnerIdentity(output);
|
|
699
|
+
return identity === null
|
|
700
|
+
? React.createElement(View, viewProps)
|
|
701
|
+
: React.createElement(NativeTiRtcVideoOutputView, {
|
|
702
|
+
...viewProps,
|
|
703
|
+
nativeObjectId: identity.objectId,
|
|
704
|
+
generation: identity.generation,
|
|
705
|
+
resizeMode,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
function useOwnerIdentity(owner: TiCloudStorageVideoOutput): NativeIdentity | null {
|
|
709
|
+
const [, update] = React.useReducer((value: number) => value + 1, 0);
|
|
710
|
+
React.useEffect(() => subscribeNativeIdentity(owner, update), [owner]);
|
|
711
|
+
return readNativeIdentity(owner);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export class TiCloudStorageRecordingTask {
|
|
715
|
+
private stopPromise: Promise<Resp<TiCloudStorageRecordingFile>> | null = null;
|
|
716
|
+
constructor(
|
|
717
|
+
private readonly taskId: number,
|
|
718
|
+
private readonly owner: TiCloudStorageReplay,
|
|
719
|
+
) {}
|
|
720
|
+
stop(): Promise<Resp<TiCloudStorageRecordingFile>> {
|
|
721
|
+
void this.owner;
|
|
722
|
+
return (this.stopPromise ??= NativeTiRtc.tiCloudStorageRecordingTaskStop(this.taskId).then((result) => {
|
|
723
|
+
tiCloudStorageLogOutcome('replay_recording_stop', result.code, ` duration_ms=${result.durationMs ?? 0}`);
|
|
724
|
+
return result.code === 0 && result.fileId !== null && result.filePath !== null && result.durationMs !== null
|
|
725
|
+
? success(new RecordingFile(result.filePath, result.durationMs, result.fileId))
|
|
726
|
+
: recordingFailure(result.code);
|
|
727
|
+
}));
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export class TiCloudStorageExportTask {
|
|
732
|
+
private resultPromise: Promise<Resp<TiCloudStorageRecordingFile>>;
|
|
733
|
+
constructor(
|
|
734
|
+
private readonly taskId: number,
|
|
735
|
+
private readonly owner: TiCloudStorage,
|
|
736
|
+
private readonly onProgress?: (progress: number) => void,
|
|
737
|
+
) {
|
|
738
|
+
this.resultPromise = NativeTiRtc.tiCloudStorageExportResult(taskId).then((result) => {
|
|
739
|
+
tiCloudStorageLogOutcome('export_terminal', result.code, ` duration_ms=${result.durationMs ?? 0}`);
|
|
740
|
+
return result.code === 0 && result.fileId !== null && result.filePath !== null && result.durationMs !== null
|
|
741
|
+
? success(new RecordingFile(result.filePath, result.durationMs, result.fileId))
|
|
742
|
+
: recordingFailure(result.code);
|
|
743
|
+
});
|
|
744
|
+
if (onProgress !== undefined) {
|
|
745
|
+
let lastProgress = -1;
|
|
746
|
+
const timer = setInterval(() => {
|
|
747
|
+
const progress = NativeTiRtc.tiCloudStorageExportProgress(taskId);
|
|
748
|
+
if (progress !== lastProgress) {
|
|
749
|
+
lastProgress = progress;
|
|
750
|
+
onProgress(progress);
|
|
751
|
+
}
|
|
752
|
+
}, 100);
|
|
753
|
+
const clear = () => clearInterval(timer);
|
|
754
|
+
void this.resultPromise.then(clear, clear);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
get progress(): number {
|
|
758
|
+
void this.owner;
|
|
759
|
+
return NativeTiRtc.tiCloudStorageExportProgress(this.taskId);
|
|
760
|
+
}
|
|
761
|
+
get result(): Promise<Resp<TiCloudStorageRecordingFile>> {
|
|
762
|
+
return this.resultPromise;
|
|
763
|
+
}
|
|
764
|
+
stop(): Promise<Resp<TiCloudStorageRecordingFile>> {
|
|
765
|
+
const code = NativeTiRtc.tiCloudStorageExportStop(this.taskId);
|
|
766
|
+
tiCloudStorageLogOutcome('export_stop', code);
|
|
767
|
+
return this.resultPromise;
|
|
768
|
+
}
|
|
769
|
+
}
|