tirtc-react-native 2.5.0-alpha.1 → 2.5.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.
- package/README.md +39 -6
- package/TiRtcReactNative.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcModule.kt +137 -14
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeCleanup.kt +54 -19
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeCloudStorageCallbacks.kt +60 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventBinder.kt +2 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventSink.kt +20 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMaps.kt +112 -5
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeOptions.kt +2 -7
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeRegistry.kt +3 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeStorageSupport.kt +46 -22
- package/ios/TiRtcReactNative+Conn.mm +61 -0
- package/ios/TiRtcReactNative+Output.mm +1 -7
- package/ios/TiRtcReactNative+Private.h +1 -0
- package/ios/TiRtcReactNative+Storage.mm +133 -35
- package/ios/TiRtcReactNative.mm +137 -48
- package/ios/TiRtcReactNativeCloudStorageCallbacks.h +13 -0
- package/ios/TiRtcReactNativeCloudStorageCallbacks.mm +67 -0
- package/ios/TiRtcReactNativeEventBinder.h +4 -0
- package/ios/TiRtcReactNativeEventBinder.mm +11 -0
- package/ios/TiRtcReactNativeEventSink.h +5 -0
- package/ios/TiRtcReactNativeEventSink.mm +18 -0
- package/ios/TiRtcReactNativeMaps.h +6 -0
- package/ios/TiRtcReactNativeMaps.mm +47 -0
- package/ios/TiRtcReactNativeOptions.mm +2 -6
- package/lib/module/audio_input.js +23 -4
- package/lib/module/audio_input.js.map +1 -1
- package/lib/module/build_identity.js +1 -1
- package/lib/module/conn.js +19 -0
- package/lib/module/conn.js.map +1 -1
- package/lib/module/events.js +16 -9
- package/lib/module/events.js.map +1 -1
- package/lib/module/index.js +3 -2
- package/lib/module/index.js.map +1 -1
- package/lib/module/raw_dump.js +53 -0
- package/lib/module/raw_dump.js.map +1 -0
- package/lib/module/specs/NativeTiRtc.js.map +1 -1
- package/lib/module/storage.js +185 -33
- package/lib/module/storage.js.map +1 -1
- package/lib/module/types.js +0 -4
- package/lib/module/types.js.map +1 -1
- package/lib/module/video_input.js +16 -6
- package/lib/module/video_input.js.map +1 -1
- package/lib/typescript/conn.d.ts +2 -0
- package/lib/typescript/index.d.ts +5 -3
- package/lib/typescript/native.d.ts +1 -1
- package/lib/typescript/raw_dump.d.ts +35 -0
- package/lib/typescript/specs/NativeTiRtc.d.ts +74 -2
- package/lib/typescript/storage.d.ts +82 -3
- package/lib/typescript/types.d.ts +5 -6
- package/package.json +2 -2
- package/src/audio_input.ts +30 -11
- package/src/conn.ts +23 -0
- package/src/events.ts +14 -9
- package/src/index.ts +17 -1
- package/src/native.ts +7 -1
- package/src/raw_dump.ts +84 -0
- package/src/specs/NativeTiRtc.ts +79 -2
- package/src/storage.ts +256 -39
- package/src/types.ts +5 -8
- package/src/video_input.ts +22 -12
package/src/storage.ts
CHANGED
|
@@ -12,7 +12,14 @@ import {
|
|
|
12
12
|
import {registerNativeEventHandler, unregisterNativeEventHandler} from './events';
|
|
13
13
|
import {TiRtcLogging} from './logging';
|
|
14
14
|
import {logBuildIdentity} from './logging_internal';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
NativeTiRtc,
|
|
17
|
+
NativeTiRtcVideoOutputView,
|
|
18
|
+
type NativeTiRtcEvent,
|
|
19
|
+
type NativeTiCloudStorageExportProgress,
|
|
20
|
+
type NativeTiCloudStorageExportReport,
|
|
21
|
+
type NativeTiCloudStorageRecordingGap,
|
|
22
|
+
} from './native';
|
|
16
23
|
import {
|
|
17
24
|
clearNativeIdentity,
|
|
18
25
|
readNativeIdentity,
|
|
@@ -21,6 +28,12 @@ import {
|
|
|
21
28
|
type NativeIdentity,
|
|
22
29
|
} from './native_object_id';
|
|
23
30
|
import {NativeMediaFileBase, recordingFailure, type Resp, type TiRtcGalleryAsset} from './recording';
|
|
31
|
+
import {
|
|
32
|
+
createRawDumpFromNative,
|
|
33
|
+
normalizeRawDumpIds,
|
|
34
|
+
TiRawDump,
|
|
35
|
+
type TiCloudStorageRawDumpOptions,
|
|
36
|
+
} from './raw_dump';
|
|
24
37
|
|
|
25
38
|
export const TI_CLOUD_STORAGE_ERROR_OK = 0 as const;
|
|
26
39
|
export const TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT = 6000 as const;
|
|
@@ -66,6 +79,70 @@ export type TiCloudStorageRecordingDaysResult = Readonly<{
|
|
|
66
79
|
days: ReadonlyArray<TiCloudStorageRecordingDay>;
|
|
67
80
|
}>;
|
|
68
81
|
|
|
82
|
+
export const TiCloudStorageRecordingTrackKind = {video: 'video', audio: 'audio'} as const;
|
|
83
|
+
export type TiCloudStorageRecordingTrackKind =
|
|
84
|
+
(typeof TiCloudStorageRecordingTrackKind)[keyof typeof TiCloudStorageRecordingTrackKind];
|
|
85
|
+
export type TiCloudStorageRecordingTrack = Readonly<{
|
|
86
|
+
kind: TiCloudStorageRecordingTrackKind;
|
|
87
|
+
channelId: number;
|
|
88
|
+
}>;
|
|
89
|
+
export const TiCloudStorageRecordingGapReason = {
|
|
90
|
+
unknown: 'unknown',
|
|
91
|
+
notFound: 'notFound',
|
|
92
|
+
downloadFailed: 'downloadFailed',
|
|
93
|
+
integrityFailed: 'integrityFailed',
|
|
94
|
+
mediaUnreadable: 'mediaUnreadable',
|
|
95
|
+
noKeyFrame: 'noKeyFrame',
|
|
96
|
+
noRecording: 'noRecording',
|
|
97
|
+
decryptionFailed: 'decryptionFailed',
|
|
98
|
+
unsupportedMedia: 'unsupportedMedia',
|
|
99
|
+
trackUnavailable: 'trackUnavailable',
|
|
100
|
+
} as const;
|
|
101
|
+
export type TiCloudStorageRecordingGapReason =
|
|
102
|
+
(typeof TiCloudStorageRecordingGapReason)[keyof typeof TiCloudStorageRecordingGapReason];
|
|
103
|
+
export type TiCloudStorageRecordingGap = Readonly<{
|
|
104
|
+
range: TiCloudStorageRecordingRange;
|
|
105
|
+
tracks: ReadonlyArray<TiCloudStorageRecordingTrack>;
|
|
106
|
+
reasons: ReadonlyArray<TiCloudStorageRecordingGapReason>;
|
|
107
|
+
}>;
|
|
108
|
+
export type TiCloudStorageExportProgress = Readonly<{
|
|
109
|
+
fraction: number;
|
|
110
|
+
coveredDurationMs: number;
|
|
111
|
+
}>;
|
|
112
|
+
export type TiCloudStorageExportSegment = Readonly<{
|
|
113
|
+
sourceRange: TiCloudStorageRecordingRange;
|
|
114
|
+
outputStartMs: number;
|
|
115
|
+
outputEndMs: number;
|
|
116
|
+
}>;
|
|
117
|
+
export const TiCloudStorageExportTermination = {
|
|
118
|
+
exhausted: 'exhausted',
|
|
119
|
+
interrupted: 'interrupted',
|
|
120
|
+
cancelled: 'cancelled',
|
|
121
|
+
failed: 'failed',
|
|
122
|
+
} as const;
|
|
123
|
+
export type TiCloudStorageExportTermination =
|
|
124
|
+
(typeof TiCloudStorageExportTermination)[keyof typeof TiCloudStorageExportTermination];
|
|
125
|
+
export type TiCloudStorageExportReport = Readonly<{
|
|
126
|
+
requestedRange: TiCloudStorageRecordingRange;
|
|
127
|
+
coveredDurationMs: number;
|
|
128
|
+
segments: ReadonlyArray<TiCloudStorageExportSegment>;
|
|
129
|
+
gaps: ReadonlyArray<TiCloudStorageRecordingGap>;
|
|
130
|
+
unprocessedRanges: ReadonlyArray<TiCloudStorageRecordingRange>;
|
|
131
|
+
complete: boolean;
|
|
132
|
+
termination: TiCloudStorageExportTermination;
|
|
133
|
+
cause: number;
|
|
134
|
+
}>;
|
|
135
|
+
export type TiCloudStorageExportOutcome = Readonly<{
|
|
136
|
+
code: number;
|
|
137
|
+
file: TiCloudStorageRecordingFile | null;
|
|
138
|
+
report: TiCloudStorageExportReport | null;
|
|
139
|
+
}>;
|
|
140
|
+
export type TiCloudStorageExportObserver = Readonly<{
|
|
141
|
+
onProgress?: (progress: number) => void;
|
|
142
|
+
onProgressDetail?: (progress: TiCloudStorageExportProgress) => void;
|
|
143
|
+
onRecordingGap?: (gap: TiCloudStorageRecordingGap) => void;
|
|
144
|
+
}>;
|
|
145
|
+
|
|
69
146
|
export const TiCloudStorageReplaySpeed = {
|
|
70
147
|
x0_125: 'x0_125',
|
|
71
148
|
x0_25: 'x0_25',
|
|
@@ -147,6 +224,37 @@ function success<T>(data: T): Resp<T> {
|
|
|
147
224
|
function validTime(value: number): boolean {
|
|
148
225
|
return isFiniteSafeInteger(value) && value >= 0;
|
|
149
226
|
}
|
|
227
|
+
|
|
228
|
+
function recordingGapFromNative(value: NativeTiCloudStorageRecordingGap): TiCloudStorageRecordingGap {
|
|
229
|
+
const kinds = [TiCloudStorageRecordingTrackKind.video, TiCloudStorageRecordingTrackKind.audio] as const;
|
|
230
|
+
const reasons = Object.values(TiCloudStorageRecordingGapReason);
|
|
231
|
+
return {
|
|
232
|
+
range: value.range,
|
|
233
|
+
tracks: value.tracks.flatMap((track) => {
|
|
234
|
+
const kind = kinds[track.kind - 1];
|
|
235
|
+
return kind === undefined ? [] : [{kind, channelId: track.channelId}];
|
|
236
|
+
}),
|
|
237
|
+
reasons: value.reasons.map((reason) => reasons[reason] ?? TiCloudStorageRecordingGapReason.unknown),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function exportProgressFromNative(value: NativeTiCloudStorageExportProgress): TiCloudStorageExportProgress {
|
|
242
|
+
return {fraction: value.fraction, coveredDurationMs: value.coveredDurationMs};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function exportReportFromNative(value: NativeTiCloudStorageExportReport): TiCloudStorageExportReport {
|
|
246
|
+
const terminations = Object.values(TiCloudStorageExportTermination);
|
|
247
|
+
return {
|
|
248
|
+
requestedRange: value.requestedRange,
|
|
249
|
+
coveredDurationMs: value.coveredDurationMs,
|
|
250
|
+
segments: value.segments,
|
|
251
|
+
gaps: value.gaps.map(recordingGapFromNative),
|
|
252
|
+
unprocessedRanges: value.unprocessedRanges,
|
|
253
|
+
complete: value.complete,
|
|
254
|
+
termination: terminations[value.termination] ?? TiCloudStorageExportTermination.failed,
|
|
255
|
+
cause: value.cause,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
150
258
|
function speedToNative(speed: TiCloudStorageReplaySpeed): number | null {
|
|
151
259
|
switch (speed) {
|
|
152
260
|
case TiCloudStorageReplaySpeed.x0_125:
|
|
@@ -217,6 +325,7 @@ function videoStateFromNative(state: unknown): TiCloudStorageVideoOutputState {
|
|
|
217
325
|
export class TiCloudStorage {
|
|
218
326
|
private identity: NativeIdentity | null = null;
|
|
219
327
|
private disposed = false;
|
|
328
|
+
private pendingAsyncOperations = 0;
|
|
220
329
|
constructor(private token: string) {}
|
|
221
330
|
|
|
222
331
|
static init(
|
|
@@ -272,10 +381,12 @@ export class TiCloudStorage {
|
|
|
272
381
|
return Promise.resolve({code: identity, recordings: []});
|
|
273
382
|
}
|
|
274
383
|
TiRtcLogging.i(tiCloudStorageLogTag, `[recording_list] outcome=started range_duration_ms=${endTimeMs - startTimeMs}`);
|
|
275
|
-
return
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
384
|
+
return this.trackAsyncOperation(() =>
|
|
385
|
+
NativeTiRtc.tiCloudStorageListRecordings(identity.objectId, startTimeMs, endTimeMs).then((result) => {
|
|
386
|
+
tiCloudStorageLogOutcome('recording_list', result.code, ` recording_count=${result.recordings.length}`);
|
|
387
|
+
return result;
|
|
388
|
+
}),
|
|
389
|
+
);
|
|
279
390
|
}
|
|
280
391
|
listRecordingDays(
|
|
281
392
|
startDate: string,
|
|
@@ -296,20 +407,25 @@ export class TiCloudStorage {
|
|
|
296
407
|
return Promise.resolve({code: identity, days: []});
|
|
297
408
|
}
|
|
298
409
|
TiRtcLogging.i(tiCloudStorageLogTag, '[recording_days_list] outcome=started');
|
|
299
|
-
return
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
410
|
+
return this.trackAsyncOperation(() =>
|
|
411
|
+
NativeTiRtc.tiCloudStorageListRecordingDays(
|
|
412
|
+
identity.objectId,
|
|
413
|
+
startDate,
|
|
414
|
+
endDate,
|
|
415
|
+
timeZoneId,
|
|
416
|
+
).then((result) => {
|
|
417
|
+
tiCloudStorageLogOutcome('recording_days_list', result.code, ` day_count=${result.days.length}`);
|
|
418
|
+
return result;
|
|
419
|
+
}),
|
|
420
|
+
);
|
|
308
421
|
}
|
|
309
422
|
createReplay(): TiCloudStorageReplay {
|
|
310
423
|
return new TiCloudStorageReplay(this);
|
|
311
424
|
}
|
|
312
|
-
exportRecording(
|
|
425
|
+
exportRecording(
|
|
426
|
+
options: TiCloudStorageExportOptions,
|
|
427
|
+
observer?: ((progress: number) => void) | TiCloudStorageExportObserver,
|
|
428
|
+
): Resp<TiCloudStorageExportTask> {
|
|
313
429
|
if (
|
|
314
430
|
!validTime(options.startTimeMs) ||
|
|
315
431
|
!validTime(options.endTimeMs) ||
|
|
@@ -330,11 +446,12 @@ export class TiCloudStorage {
|
|
|
330
446
|
);
|
|
331
447
|
tiCloudStorageLogOutcome('export_start', result.code, ` range_duration_ms=${options.endTimeMs - options.startTimeMs}`);
|
|
332
448
|
return result.code === 0 && result.taskId !== null
|
|
333
|
-
? success(new TiCloudStorageExportTask(result.taskId, this,
|
|
449
|
+
? success(new TiCloudStorageExportTask(result.taskId, result.generation, this, observer))
|
|
334
450
|
: recordingFailure(result.code);
|
|
335
451
|
}
|
|
336
452
|
dispose(): number {
|
|
337
453
|
if (this.disposed) return 0;
|
|
454
|
+
if (this.pendingAsyncOperations > 0) return TI_CLOUD_STORAGE_ERROR_IN_USE;
|
|
338
455
|
if (this.identity === null) {
|
|
339
456
|
this.disposed = true;
|
|
340
457
|
this.token = '';
|
|
@@ -351,6 +468,17 @@ export class TiCloudStorage {
|
|
|
351
468
|
}
|
|
352
469
|
return code;
|
|
353
470
|
}
|
|
471
|
+
private trackAsyncOperation<T>(operation: () => Promise<T>): Promise<T> {
|
|
472
|
+
this.pendingAsyncOperations += 1;
|
|
473
|
+
try {
|
|
474
|
+
return operation().finally(() => {
|
|
475
|
+
this.pendingAsyncOperations -= 1;
|
|
476
|
+
});
|
|
477
|
+
} catch (error) {
|
|
478
|
+
this.pendingAsyncOperations -= 1;
|
|
479
|
+
return Promise.reject(error);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
354
482
|
private ensureIdentity(): NativeIdentity | number {
|
|
355
483
|
if (this.disposed) return TI_CLOUD_STORAGE_ERROR_NOT_INITIALIZED;
|
|
356
484
|
if (this.identity !== null) return this.identity;
|
|
@@ -373,11 +501,13 @@ function tiCloudStorageNativeIdentity(cloudStorage: TiCloudStorage): NativeIdent
|
|
|
373
501
|
export class TiCloudStorageReplay {
|
|
374
502
|
private identity: NativeIdentity | null = null;
|
|
375
503
|
private disposed = false;
|
|
504
|
+
private inCallback = false;
|
|
376
505
|
private currentSpeed: TiCloudStorageReplaySpeed = 'x1';
|
|
377
506
|
private currentTime: number | null = null;
|
|
378
507
|
onTimeChanged: ((timeMs: number) => void) | null = null;
|
|
379
508
|
onError: ((code: number) => void) | null = null;
|
|
380
509
|
onCompleted: (() => void) | null = null;
|
|
510
|
+
onRecordingGap: ((gap: TiCloudStorageRecordingGap) => void) | null = null;
|
|
381
511
|
constructor(private readonly cloudStorage: TiCloudStorage) {}
|
|
382
512
|
get speed(): TiCloudStorageReplaySpeed {
|
|
383
513
|
if (this.identity !== null) {
|
|
@@ -394,6 +524,7 @@ export class TiCloudStorageReplay {
|
|
|
394
524
|
return this.currentTime;
|
|
395
525
|
}
|
|
396
526
|
play(startTimeMs: number, endTimeMs: number, initialTimeMs?: number): number {
|
|
527
|
+
if (this.inCallback) return TI_CLOUD_STORAGE_ERROR_IN_USE;
|
|
397
528
|
if (
|
|
398
529
|
!validTime(startTimeMs) ||
|
|
399
530
|
!validTime(endTimeMs) ||
|
|
@@ -421,6 +552,7 @@ export class TiCloudStorageReplay {
|
|
|
421
552
|
return code;
|
|
422
553
|
}
|
|
423
554
|
seek(timeMs: number): number {
|
|
555
|
+
if (this.inCallback) return TI_CLOUD_STORAGE_ERROR_IN_USE;
|
|
424
556
|
if (!validTime(timeMs)) return TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT;
|
|
425
557
|
const identity = this.ensureIdentity();
|
|
426
558
|
const code = typeof identity === 'number' ? identity : NativeTiRtc.tiCloudStorageReplaySeek(identity.objectId, timeMs);
|
|
@@ -456,19 +588,38 @@ export class TiCloudStorageReplay {
|
|
|
456
588
|
? success(new TiCloudStorageRecordingTask(result.taskId, this))
|
|
457
589
|
: recordingFailure(result.code);
|
|
458
590
|
}
|
|
591
|
+
async startRawDump(options: TiCloudStorageRawDumpOptions): Promise<Resp<TiRawDump>> {
|
|
592
|
+
if (options === null || typeof options !== 'object')
|
|
593
|
+
return recordingFailure(TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT);
|
|
594
|
+
const audio = normalizeRawDumpIds(options.audioChannelIds ?? [], 255);
|
|
595
|
+
const video = normalizeRawDumpIds(options.videoChannelIds ?? [], 255);
|
|
596
|
+
if (audio === null || video === null || (audio.length === 0 && video.length === 0))
|
|
597
|
+
return recordingFailure(TI_CLOUD_STORAGE_ERROR_INVALID_ARGUMENT);
|
|
598
|
+
const identity = this.ensureIdentity();
|
|
599
|
+
if (typeof identity === 'number') return recordingFailure(identity);
|
|
600
|
+
const result = await NativeTiRtc.tiCloudStorageReplayStartRawDump(
|
|
601
|
+
identity.objectId, audio, video,
|
|
602
|
+
);
|
|
603
|
+
return result.code === 0 && result.dumpId !== null
|
|
604
|
+
? {success: true, code: null, message: null, data: createRawDumpFromNative(result.dumpId)}
|
|
605
|
+
: recordingFailure(result.code);
|
|
606
|
+
}
|
|
459
607
|
stop(): number {
|
|
608
|
+
if (this.inCallback) return TI_CLOUD_STORAGE_ERROR_IN_USE;
|
|
460
609
|
const code = this.command(NativeTiRtc.tiCloudStorageReplayStop);
|
|
461
610
|
if (code === 0) this.currentTime = null;
|
|
462
611
|
tiCloudStorageLogOutcome('replay_stop', code);
|
|
463
612
|
return code;
|
|
464
613
|
}
|
|
465
614
|
dispose(): number {
|
|
615
|
+
if (this.inCallback) return TI_CLOUD_STORAGE_ERROR_IN_USE;
|
|
466
616
|
if (this.disposed) return 0;
|
|
467
617
|
if (this.identity === null) {
|
|
468
618
|
this.disposed = true;
|
|
469
619
|
this.onTimeChanged = null;
|
|
470
620
|
this.onError = null;
|
|
471
621
|
this.onCompleted = null;
|
|
622
|
+
this.onRecordingGap = null;
|
|
472
623
|
return 0;
|
|
473
624
|
}
|
|
474
625
|
const identity = this.identity;
|
|
@@ -481,6 +632,7 @@ export class TiCloudStorageReplay {
|
|
|
481
632
|
this.onTimeChanged = null;
|
|
482
633
|
this.onError = null;
|
|
483
634
|
this.onCompleted = null;
|
|
635
|
+
this.onRecordingGap = null;
|
|
484
636
|
}
|
|
485
637
|
tiCloudStorageLogOutcome('replay_dispose', code);
|
|
486
638
|
return code;
|
|
@@ -505,11 +657,18 @@ export class TiCloudStorageReplay {
|
|
|
505
657
|
return this.identity;
|
|
506
658
|
}
|
|
507
659
|
private handleEvent(event: NativeTiRtcEvent): void {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
660
|
+
this.inCallback = true;
|
|
661
|
+
try {
|
|
662
|
+
if (event.type === 'tiCloudStorageReplayTimeChanged' && event.timestampMs !== undefined) {
|
|
663
|
+
this.currentTime = event.timestampMs;
|
|
664
|
+
this.onTimeChanged?.(event.timestampMs);
|
|
665
|
+
} else if (event.type === 'tiCloudStorageReplayCompleted') this.onCompleted?.();
|
|
666
|
+
else if (event.type === 'tiCloudStorageReplayError') this.onError?.(event.code ?? TI_CLOUD_STORAGE_ERROR_UNAVAILABLE);
|
|
667
|
+
else if (event.type === 'tiCloudStorageReplayRecordingGap' && event.recordingGap !== undefined)
|
|
668
|
+
this.onRecordingGap?.(recordingGapFromNative(event.recordingGap));
|
|
669
|
+
} finally {
|
|
670
|
+
this.inCallback = false;
|
|
671
|
+
}
|
|
513
672
|
}
|
|
514
673
|
}
|
|
515
674
|
|
|
@@ -736,40 +895,98 @@ export class TiCloudStorageRecordingTask {
|
|
|
736
895
|
|
|
737
896
|
export class TiCloudStorageExportTask {
|
|
738
897
|
private resultPromise: Promise<Resp<TiCloudStorageRecordingFile>>;
|
|
898
|
+
private completionPromise: Promise<TiCloudStorageExportOutcome>;
|
|
899
|
+
private readonly identity: NativeIdentity;
|
|
900
|
+
private currentProgressDetail: TiCloudStorageExportProgress = {fraction: 0, coveredDurationMs: 0};
|
|
901
|
+
private currentReport: TiCloudStorageExportReport | null = null;
|
|
902
|
+
private readonly observer: TiCloudStorageExportObserver;
|
|
903
|
+
private terminal = false;
|
|
904
|
+
|
|
739
905
|
constructor(
|
|
740
906
|
private readonly taskId: number,
|
|
907
|
+
generation: number,
|
|
741
908
|
private readonly owner: TiCloudStorage,
|
|
742
|
-
|
|
909
|
+
observer?: ((progress: number) => void) | TiCloudStorageExportObserver,
|
|
743
910
|
) {
|
|
744
|
-
this.
|
|
911
|
+
this.identity = {objectId: taskId, generation};
|
|
912
|
+
this.observer = typeof observer === 'function' ? {onProgress: observer} : (observer ?? {});
|
|
913
|
+
registerNativeEventHandler(this.identity, (event) => this.handleEvent(event));
|
|
914
|
+
this.completionPromise = NativeTiRtc.tiCloudStorageExportResult(taskId).then((result) => {
|
|
915
|
+
if (result.progressDetail != null) {
|
|
916
|
+
const progress = exportProgressFromNative(result.progressDetail);
|
|
917
|
+
this.currentProgressDetail = {
|
|
918
|
+
fraction: Math.max(this.currentProgressDetail.fraction, progress.fraction),
|
|
919
|
+
coveredDurationMs: Math.max(this.currentProgressDetail.coveredDurationMs, progress.coveredDurationMs),
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
this.terminal = true;
|
|
923
|
+
unregisterNativeEventHandler(this.identity);
|
|
745
924
|
tiCloudStorageLogOutcome('export_terminal', result.code, ` duration_ms=${result.durationMs ?? 0}`);
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
925
|
+
const file =
|
|
926
|
+
result.fileId !== null && result.filePath !== null && result.durationMs !== null
|
|
927
|
+
? new RecordingFile(result.filePath, result.durationMs, result.fileId)
|
|
928
|
+
: null;
|
|
929
|
+
this.currentReport = result.report === null ? null : exportReportFromNative(result.report);
|
|
930
|
+
if (this.currentReport !== null) {
|
|
931
|
+
this.currentProgressDetail = {
|
|
932
|
+
fraction: this.currentProgressDetail.fraction,
|
|
933
|
+
coveredDurationMs: this.currentReport.coveredDurationMs,
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
return {code: result.code, file, report: this.currentReport};
|
|
749
937
|
});
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
lastProgress = progress;
|
|
756
|
-
onProgress(progress);
|
|
757
|
-
}
|
|
758
|
-
}, 100);
|
|
759
|
-
const clear = () => clearInterval(timer);
|
|
760
|
-
void this.resultPromise.then(clear, clear);
|
|
761
|
-
}
|
|
938
|
+
this.resultPromise = this.completionPromise.then((outcome) =>
|
|
939
|
+
outcome.code === TI_CLOUD_STORAGE_ERROR_OK && outcome.file !== null
|
|
940
|
+
? success(outcome.file)
|
|
941
|
+
: recordingFailure(outcome.code),
|
|
942
|
+
);
|
|
762
943
|
}
|
|
763
944
|
get progress(): number {
|
|
764
945
|
void this.owner;
|
|
765
|
-
return
|
|
946
|
+
return this.progressDetail.fraction;
|
|
947
|
+
}
|
|
948
|
+
get progressDetail(): TiCloudStorageExportProgress {
|
|
949
|
+
if (this.terminal) return this.currentProgressDetail;
|
|
950
|
+
const value = exportProgressFromNative(NativeTiRtc.tiCloudStorageExportProgressDetail(this.taskId));
|
|
951
|
+
this.currentProgressDetail = {
|
|
952
|
+
fraction: Math.max(this.currentProgressDetail.fraction, value.fraction),
|
|
953
|
+
coveredDurationMs: Math.max(this.currentProgressDetail.coveredDurationMs, value.coveredDurationMs),
|
|
954
|
+
};
|
|
955
|
+
return this.currentProgressDetail;
|
|
956
|
+
}
|
|
957
|
+
get report(): TiCloudStorageExportReport | null {
|
|
958
|
+
return this.currentReport;
|
|
959
|
+
}
|
|
960
|
+
get completion(): Promise<TiCloudStorageExportOutcome> {
|
|
961
|
+
return this.completionPromise;
|
|
766
962
|
}
|
|
767
963
|
get result(): Promise<Resp<TiCloudStorageRecordingFile>> {
|
|
768
964
|
return this.resultPromise;
|
|
769
965
|
}
|
|
966
|
+
cancel(): number {
|
|
967
|
+
if (this.terminal) return TI_CLOUD_STORAGE_ERROR_OK;
|
|
968
|
+
const code = NativeTiRtc.tiCloudStorageExportCancel(this.taskId);
|
|
969
|
+
tiCloudStorageLogOutcome('export_cancel', code);
|
|
970
|
+
return code;
|
|
971
|
+
}
|
|
770
972
|
stop(): Promise<Resp<TiCloudStorageRecordingFile>> {
|
|
973
|
+
if (this.terminal) return this.resultPromise;
|
|
771
974
|
const code = NativeTiRtc.tiCloudStorageExportStop(this.taskId);
|
|
772
975
|
tiCloudStorageLogOutcome('export_stop', code);
|
|
773
976
|
return this.resultPromise;
|
|
774
977
|
}
|
|
978
|
+
private handleEvent(event: NativeTiRtcEvent): void {
|
|
979
|
+
if (this.terminal) return;
|
|
980
|
+
if (event.type === 'tiCloudStorageExportProgress' && event.exportProgress !== undefined) {
|
|
981
|
+
const progress = exportProgressFromNative(event.exportProgress);
|
|
982
|
+
this.currentProgressDetail = {
|
|
983
|
+
fraction: Math.max(this.currentProgressDetail.fraction, progress.fraction),
|
|
984
|
+
coveredDurationMs: Math.max(this.currentProgressDetail.coveredDurationMs, progress.coveredDurationMs),
|
|
985
|
+
};
|
|
986
|
+
this.observer.onProgress?.(this.currentProgressDetail.fraction);
|
|
987
|
+
this.observer.onProgressDetail?.(this.currentProgressDetail);
|
|
988
|
+
} else if (event.type === 'tiCloudStorageExportRecordingGap' && event.recordingGap !== undefined) {
|
|
989
|
+
this.observer.onRecordingGap?.(recordingGapFromNative(event.recordingGap));
|
|
990
|
+
}
|
|
991
|
+
}
|
|
775
992
|
}
|
package/src/types.ts
CHANGED
|
@@ -84,13 +84,6 @@ export const TiRtcVideoCodec = {
|
|
|
84
84
|
} as const;
|
|
85
85
|
export type TiRtcVideoCodec = (typeof TiRtcVideoCodec)[keyof typeof TiRtcVideoCodec];
|
|
86
86
|
|
|
87
|
-
export const TiRtcVideoEncoderCodec = {
|
|
88
|
-
h264: 'h264',
|
|
89
|
-
mjpeg: 'mjpeg',
|
|
90
|
-
} as const;
|
|
91
|
-
export type TiRtcVideoEncoderCodec =
|
|
92
|
-
(typeof TiRtcVideoEncoderCodec)[keyof typeof TiRtcVideoEncoderCodec];
|
|
93
|
-
|
|
94
87
|
export const TiRtcVideoFrameRate = {
|
|
95
88
|
fps10: 'fps10',
|
|
96
89
|
fps15: 'fps15',
|
|
@@ -183,6 +176,9 @@ export type TiRtcVideoOutputViewProps = ViewProps &
|
|
|
183
176
|
}>;
|
|
184
177
|
|
|
185
178
|
export type TiRtcAudioInputOptions = Readonly<{
|
|
179
|
+
/** Nano audio media: 1 PCM, 2 G711A, 3 AAC, 4 Opus, 5 AMR. Omitted or -1 uses codec. */
|
|
180
|
+
media?: number;
|
|
181
|
+
/** @deprecated Use media. The legacy default is g711a. */
|
|
186
182
|
codec?: TiRtcAudioCodec;
|
|
187
183
|
sampleRate?: TiRtcAudioSampleRate;
|
|
188
184
|
channels?: TiRtcAudioChannelCount;
|
|
@@ -192,7 +188,8 @@ export type TiRtcAudioInputOptions = Readonly<{
|
|
|
192
188
|
}>;
|
|
193
189
|
|
|
194
190
|
export type TiRtcVideoInputOptions = Readonly<{
|
|
195
|
-
|
|
191
|
+
/** Nano video media: 66 H.264 (default) or 65 MJPEG. */
|
|
192
|
+
media?: number;
|
|
196
193
|
width?: number;
|
|
197
194
|
height?: number;
|
|
198
195
|
frameRate?: TiRtcVideoFrameRate;
|
package/src/video_input.ts
CHANGED
|
@@ -43,7 +43,7 @@ export class TiRtcVideoInput {
|
|
|
43
43
|
private currentOutputSize: TiRtcSize | null = null;
|
|
44
44
|
private currentOutputFps: number | null = null;
|
|
45
45
|
private options: Required<TiRtcVideoInputOptions> = {
|
|
46
|
-
|
|
46
|
+
media: 66,
|
|
47
47
|
width: 640,
|
|
48
48
|
height: 480,
|
|
49
49
|
frameRate: 'fps15',
|
|
@@ -92,7 +92,7 @@ export class TiRtcVideoInput {
|
|
|
92
92
|
return Promise.resolve(TIRTC_ERROR_INVALID_ARGUMENT);
|
|
93
93
|
}
|
|
94
94
|
const normalized = {
|
|
95
|
-
|
|
95
|
+
media: options.media === undefined ? 66 : options.media,
|
|
96
96
|
width: options.width ?? 640,
|
|
97
97
|
height: options.height ?? 480,
|
|
98
98
|
frameRate: options.frameRate ?? 'fps15',
|
|
@@ -100,12 +100,16 @@ export class TiRtcVideoInput {
|
|
|
100
100
|
cameraFacing: options.cameraFacing ?? 'front',
|
|
101
101
|
};
|
|
102
102
|
if (
|
|
103
|
+
!Number.isInteger(normalized.media) ||
|
|
104
|
+
![65, 66].includes(normalized.media) ||
|
|
103
105
|
!isPositiveInt32Size(normalized.width) ||
|
|
104
106
|
!isPositiveInt32Size(normalized.height) ||
|
|
105
107
|
!Number.isSafeInteger(normalized.bitrateKbps) ||
|
|
106
108
|
normalized.bitrateKbps < 0 ||
|
|
107
109
|
normalized.bitrateKbps > 4294967295
|
|
108
110
|
) {
|
|
111
|
+
logRtcOutcome('video_input_set_options', TIRTC_ERROR_INVALID_ARGUMENT,
|
|
112
|
+
` nano_media=${typeof normalized.media === 'number' ? normalized.media : typeof normalized.media}`);
|
|
109
113
|
return Promise.resolve(TIRTC_ERROR_INVALID_ARGUMENT);
|
|
110
114
|
}
|
|
111
115
|
return this.enqueue(async () => {
|
|
@@ -114,6 +118,8 @@ export class TiRtcVideoInput {
|
|
|
114
118
|
return identity;
|
|
115
119
|
}
|
|
116
120
|
const code = await NativeTiRtc.videoInputSetOptions(identity.objectId, normalized);
|
|
121
|
+
logRtcOutcome('video_input_set_options', code,
|
|
122
|
+
` nano_media=${normalized.media} width=${normalized.width} height=${normalized.height} fps=${['fps10', 'fps15', 'fps30'].includes(normalized.frameRate) ? normalized.frameRate : 'invalid'}`);
|
|
117
123
|
if (code === TIRTC_ERROR_OK) {
|
|
118
124
|
this.options = normalized;
|
|
119
125
|
}
|
|
@@ -122,16 +128,20 @@ export class TiRtcVideoInput {
|
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
start(): Promise<number> {
|
|
125
|
-
return this.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
return this.enqueue(async () => {
|
|
132
|
+
const identity = this.ensureIdentity();
|
|
133
|
+
if (typeof identity === 'number') {
|
|
134
|
+
logRtcOutcome('video_input_start', identity);
|
|
135
|
+
return identity;
|
|
136
|
+
}
|
|
137
|
+
const wasRunning = this.state === 'running';
|
|
138
|
+
const code = await NativeTiRtc.videoInputStart(identity.objectId);
|
|
139
|
+
if (code !== TIRTC_ERROR_OK || !wasRunning) {
|
|
140
|
+
logRtcOutcome('video_input_start', code,
|
|
141
|
+
` object_id=${identity.objectId} nano_media=${this.options.media} width=${this.options.width} height=${this.options.height} fps=${this.options.frameRate}`);
|
|
142
|
+
}
|
|
143
|
+
return code;
|
|
144
|
+
});
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
attach(connection: TiRtcConn, streamId: number): Promise<number> {
|