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/README.md
CHANGED
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
npm install tirtc-react-native
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
The package ships ESM JavaScript, TypeScript declarations, Codegen specs, Android native wrapper code and iOS native wrapper code. RTC and CloudStorage use the same native Runtime delivery but keep separate initialization and resource lifecycles. The package requires React Native `>=0.80.0`, Android `com.tange.ai:tirtc:2.5.0
|
|
9
|
+
The package ships ESM JavaScript, TypeScript declarations, Codegen specs, Android native wrapper code and iOS native wrapper code. RTC and CloudStorage use the same native Runtime delivery but keep separate initialization and resource lifecycles. The package requires React Native `>=0.80.0`, Android `com.tange.ai:tirtc:2.5.0`, and iOS `TiRTC` `2.5.2` with iOS minimum `15.1`.
|
|
10
10
|
|
|
11
11
|
The iOS package supports arm64 devices and Apple Silicon iOS Simulator. Simulator validation covers build, launch, connection, downlink audio, and downlink video; capture and iOS behavioral qualification remain real-device gates.
|
|
12
12
|
|
|
13
|
-
Version `2.5.
|
|
13
|
+
Version `2.5.2` uses the TiRTC 2.5.2 Darwin SDK for iOS and retains the TiRTC 2.5.0 Android SDK.
|
|
14
14
|
|
|
15
15
|
## Imports
|
|
16
16
|
|
|
@@ -37,7 +37,7 @@ The root export is the public API. Deep imports are not supported.
|
|
|
37
37
|
|
|
38
38
|
String values are exported as stable `as const` objects and matching TypeScript union types:
|
|
39
39
|
|
|
40
|
-
`TiRtcOutputBufferStrategy`, `TiRtcConnState`, `TiRtcInputState`, `TiRtcAudioCodec`, `TiRtcAudioSampleRate`, `TiRtcAudioChannelCount`, `TiRtcAudioAecMode`, `TiRtcAudioAgcLevel`, `TiRtcAudioAnsLevel`, `TiRtcVideoCodec`, `
|
|
40
|
+
`TiRtcOutputBufferStrategy`, `TiRtcConnState`, `TiRtcInputState`, `TiRtcAudioCodec`, `TiRtcAudioSampleRate`, `TiRtcAudioChannelCount`, `TiRtcAudioAecMode`, `TiRtcAudioAgcLevel`, `TiRtcAudioAnsLevel`, `TiRtcVideoCodec`, `TiRtcVideoFrameRate`, `TiRtcCameraFacing`, `TiRtcVideoDecoderPreference`, `TiRtcVideoDecoderBackend`, `TiRtcVideoBitstreamFormat`, `TiRtcAudioOutputState`, `TiRtcVideoOutputState`.
|
|
41
41
|
|
|
42
42
|
Use the exported values instead of hard-coded strings in app code.
|
|
43
43
|
|
|
@@ -95,7 +95,7 @@ import {TiRtcAudioCodec, TiRtcAudioInput} from 'tirtc-react-native';
|
|
|
95
95
|
const audioInput = new TiRtcAudioInput();
|
|
96
96
|
|
|
97
97
|
await audioInput.setOptions({
|
|
98
|
-
|
|
98
|
+
media: 2,
|
|
99
99
|
});
|
|
100
100
|
await audioInput.attach(conn, talkbackStreamId);
|
|
101
101
|
await audioInput.start();
|
|
@@ -139,7 +139,7 @@ Constructors create native owner identities, so outputs and inputs can attach to
|
|
|
139
139
|
|
|
140
140
|
## Release
|
|
141
141
|
|
|
142
|
-
Version `2.5.
|
|
142
|
+
Version `2.5.2` is the current stable React Native release for TiRTC and Ti Cloud Storage. It supports React Native New Architecture apps on Android and iOS and pins Android `com.tange.ai:tirtc:2.5.0` and iOS `TiRTC` `2.5.2`.
|
|
143
143
|
|
|
144
144
|
## License
|
|
145
145
|
|
|
@@ -155,12 +155,45 @@ npm run build
|
|
|
155
155
|
npm run codegen:check
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
+
On macOS, `python3 test/cloud_storage_callbacks_test.py` compiles and executes the iOS Foundation callback queue to check acknowledgement, bounded delivery, and invalidation. Android exercises its native callback queue in the Cloud Storage integration instrumentation. These checks supplement the owner product lanes defined in `react-native-test`.
|
|
159
|
+
|
|
158
160
|
The external application sample is published under `react-native/` in
|
|
159
161
|
`tirtc-api-samples`. The owner-local projection and recovery entry is:
|
|
160
162
|
|
|
161
163
|
```sh
|
|
162
|
-
./scripts/publish_api_samples.sh --version 2.5.
|
|
164
|
+
./scripts/publish_api_samples.sh --version 2.5.2
|
|
163
165
|
```
|
|
164
166
|
|
|
165
167
|
Use `--no-push` for a complete local projection/build rehearsal without writing
|
|
166
168
|
the public repository.
|
|
169
|
+
|
|
170
|
+
## Microphone media configuration
|
|
171
|
+
|
|
172
|
+
`TiRtcAudioInputOptions.media` accepts the integer audio media number returned by
|
|
173
|
+
Nano device capabilities:
|
|
174
|
+
|
|
175
|
+
| media | Audio format |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| 1 | PCM |
|
|
178
|
+
| 2 | G711A (A-law) |
|
|
179
|
+
| 3 | AAC |
|
|
180
|
+
| 4 | Opus |
|
|
181
|
+
| 5 | AMR (8 kHz mono) |
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const code = await input.setOptions({media: 2});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Omitting `media` or setting it to `-1` uses the deprecated `codec` field, whose
|
|
188
|
+
default remains G711A. Existing AAC calls keep selecting AAC. An explicit supported
|
|
189
|
+
`media` takes precedence over `codec`; any other value returns the existing
|
|
190
|
+
parameter error and leaves the accepted configuration intact. Stop capture before
|
|
191
|
+
applying new options, and follow the input's existing detach requirements.
|
|
192
|
+
|
|
193
|
+
The deprecated audio codec values keep their original meanings. Codec fields on
|
|
194
|
+
received frames and metrics retain their output numbering; they are not Nano
|
|
195
|
+
input media numbers.
|
|
196
|
+
|
|
197
|
+
## Targeted input validation
|
|
198
|
+
|
|
199
|
+
Prepare the current native dependency, then run `python3 react-native/tirtc/example/tool/integration.py --platform android --case rn_input_media_all` from the repository root. For iOS, select `--platform ios-arm64-simulator --prepared-state <ios-consumer-state>`. Simulator evidence distinguishes decoded constant audio and an unavailable camera from real capture signal verification. Individual cases are `rn_input_media_g711a_h264`, `rn_input_media_aac_mjpeg` and `rn_input_media_legacy_aac_h264`. The test-owned React fixture consumes the public facade; its peer records and decodes the encoded microphone/camera payload. Full integration requirements remain separate.
|
package/TiRtcReactNative.podspec
CHANGED
|
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
|
|
|
13
13
|
s.source = { :git => 'https://gitlab.tange-ai.com/tirtc-nexus/tirtc.git', :tag => s.version.to_s }
|
|
14
14
|
s.source_files = 'ios/**/*.{h,m,mm,swift}'
|
|
15
15
|
s.dependency 'React-Core'
|
|
16
|
-
s.dependency 'TiRTC', '2.5.
|
|
16
|
+
s.dependency 'TiRTC', '2.5.2'
|
|
17
17
|
|
|
18
18
|
if respond_to?(:install_modules_dependencies, true)
|
|
19
19
|
install_modules_dependencies(s)
|
package/android/build.gradle
CHANGED
|
@@ -29,7 +29,7 @@ repositories {
|
|
|
29
29
|
|
|
30
30
|
dependencies {
|
|
31
31
|
implementation "com.facebook.react:react-android"
|
|
32
|
-
implementation "com.tange.ai:tirtc:${safeExtGet("tirtcAndroidVersion", "2.5.0
|
|
32
|
+
implementation "com.tange.ai:tirtc:${safeExtGet("tirtcAndroidVersion", "2.5.0")}"
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
def safeExtGet(prop, fallback) {
|
|
@@ -25,9 +25,15 @@ import com.tange.ai.tirtc.TiRtcRecordingFile
|
|
|
25
25
|
import com.tange.ai.tirtc.TiRtcSnapshotFile
|
|
26
26
|
import com.tange.ai.tirtc.TiRtcVideoInput
|
|
27
27
|
import com.tange.ai.tirtc.TiRtcVideoOutput
|
|
28
|
+
import com.tange.ai.tirtc.TiRtcRawDumpOptions
|
|
29
|
+
import com.tange.ai.tirtc.TiCloudStorageRawDumpOptions
|
|
28
30
|
import com.tange.ai.tirtc.TiCloudStorage
|
|
31
|
+
import com.tange.ai.tirtc.TiCloudStorageCallbackThread
|
|
32
|
+
import com.tange.ai.tirtc.TiCloudStorageErrorCode
|
|
33
|
+
import com.facebook.react.bridge.Arguments
|
|
29
34
|
import com.tange.ai.tirtc.TiCloudStorageAudioOutput
|
|
30
35
|
import com.tange.ai.tirtc.TiCloudStorageExportRequest
|
|
36
|
+
import com.tange.ai.tirtc.TiCloudStorageExportResult
|
|
31
37
|
import com.tange.ai.tirtc.TiCloudStorageRecordingFile
|
|
32
38
|
import com.tange.ai.tirtc.TiCloudStorageRecordingResult
|
|
33
39
|
import com.tange.ai.tirtc.TiCloudStorageReplaySpeed
|
|
@@ -48,14 +54,28 @@ class TiRtcModule(
|
|
|
48
54
|
) : NativeTiRtcSpec(reactContext) {
|
|
49
55
|
private val registry = TiRtcReactNativeRegistry
|
|
50
56
|
private val invalidated = AtomicBoolean(false)
|
|
51
|
-
private val
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
private val cloudStorageCallbacks = TiRtcReactNativeCloudStorageCallbacks {
|
|
58
|
+
emitEvent(Arguments.createMap().apply {
|
|
59
|
+
putString("type", "tiCloudStorageCallbackReady")
|
|
60
|
+
putInt("objectId", 0)
|
|
61
|
+
putInt("generation", 0)
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
private val events = TiRtcReactNativeEventSink(registry) { event ->
|
|
65
|
+
val type = event.getString("type") ?: ""
|
|
66
|
+
if (type.startsWith("tiCloudStorageReplay") || type.startsWith("tiCloudStorageExport")) {
|
|
67
|
+
cloudStorageCallbacks.deliver(event)
|
|
68
|
+
} else {
|
|
69
|
+
emitEvent(event)
|
|
58
70
|
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private fun emitEvent(event: WritableMap) {
|
|
74
|
+
if (!invalidated.get()) reactContext.runOnJSQueueThread {
|
|
75
|
+
if (!invalidated.get()) emitOnTiRtcEvent(event)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
59
79
|
private val eventBinder = TiRtcReactNativeEventBinder(events)
|
|
60
80
|
private val nextMediaFileId = AtomicInteger(1)
|
|
61
81
|
private val mediaFiles = ConcurrentHashMap<Int, Any>()
|
|
@@ -69,6 +89,7 @@ class TiRtcModule(
|
|
|
69
89
|
|
|
70
90
|
override fun invalidate() {
|
|
71
91
|
if (!invalidated.compareAndSet(false, true)) return
|
|
92
|
+
cloudStorageCallbacks.close()
|
|
72
93
|
val objects = registry.drain(events)
|
|
73
94
|
val files = mediaFiles.values.toList()
|
|
74
95
|
mediaFiles.clear()
|
|
@@ -182,7 +203,7 @@ class TiRtcModule(
|
|
|
182
203
|
?: return createResult(TiRtcReactNativeErrors.OBJECT_RELEASED, 0, 0)
|
|
183
204
|
return registerReactNativeObject(
|
|
184
205
|
registry,
|
|
185
|
-
factory = { cloudStorage.createReplay() },
|
|
206
|
+
factory = { cloudStorage.createReplay(TiCloudStorageCallbackThread.BACKGROUND) },
|
|
186
207
|
onRegistered = { entry, value -> eventBinder.wireCloudStorageReplay(entry.id, value as com.tange.ai.tirtc.TiCloudStorageReplay) },
|
|
187
208
|
)
|
|
188
209
|
}
|
|
@@ -196,7 +217,10 @@ class TiRtcModule(
|
|
|
196
217
|
): WritableMap {
|
|
197
218
|
val cloudStorage = registry.cloudStorage(id.toInt())
|
|
198
219
|
?: return recordingStartResult(TiRtcReactNativeErrors.OBJECT_RELEASED, null)
|
|
199
|
-
val bridge = CloudStorageExportBridge(::
|
|
220
|
+
val bridge = CloudStorageExportBridge(::resolveCloudStorageExport, events)
|
|
221
|
+
val entry = registry.add(bridge)
|
|
222
|
+
bridge.objectId = entry.id
|
|
223
|
+
bridge.onDrained = { registry.release(entry.id) }
|
|
200
224
|
val started = cloudStorage.exportRecording(
|
|
201
225
|
TiCloudStorageExportRequest(
|
|
202
226
|
startTimeMs.toLong(),
|
|
@@ -205,17 +229,34 @@ class TiRtcModule(
|
|
|
205
229
|
audioChannelId?.toInt(),
|
|
206
230
|
),
|
|
207
231
|
null,
|
|
232
|
+
{ progress -> bridge.progress(progress) },
|
|
233
|
+
{ gap -> bridge.recordingGap(gap) },
|
|
234
|
+
TiCloudStorageCallbackThread.BACKGROUND,
|
|
208
235
|
) { result -> bridge.complete(result) }
|
|
209
|
-
val task = started.task
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
236
|
+
val task = started.task
|
|
237
|
+
if (task == null) {
|
|
238
|
+
bridge.rejectStart()
|
|
239
|
+
registry.release(entry.id)
|
|
240
|
+
return recordingStartResult(started.code, null)
|
|
241
|
+
}
|
|
242
|
+
bridge.bindTask(task)
|
|
243
|
+
return recordingStartResult(started.code, entry.id, entry.generation)
|
|
214
244
|
}
|
|
215
245
|
|
|
246
|
+
override fun tiCloudStorageTakeCallback(): WritableMap? = cloudStorageCallbacks.take()
|
|
247
|
+
|
|
248
|
+
override fun tiCloudStorageAcknowledgeCallback(deliveryId: Double): Double =
|
|
249
|
+
cloudStorageCallbacks.acknowledge(deliveryId)
|
|
250
|
+
|
|
216
251
|
override fun tiCloudStorageExportProgress(id: Double): Double =
|
|
217
252
|
registry.tiCloudStorageExportTask(id.toInt())?.task?.progress ?: 0.0
|
|
218
253
|
|
|
254
|
+
override fun tiCloudStorageExportProgressDetail(id: Double): WritableMap =
|
|
255
|
+
tiCloudStorageExportProgress(
|
|
256
|
+
registry.tiCloudStorageExportTask(id.toInt())?.task?.progressDetail
|
|
257
|
+
?: com.tange.ai.tirtc.TiCloudStorageExportProgress(0.0, 0),
|
|
258
|
+
)
|
|
259
|
+
|
|
219
260
|
@ReactMethod
|
|
220
261
|
override fun tiCloudStorageExportResult(id: Double, promise: Promise) {
|
|
221
262
|
registry.tiCloudStorageExportTask(id.toInt())?.await(promise)
|
|
@@ -225,6 +266,9 @@ class TiRtcModule(
|
|
|
225
266
|
override fun tiCloudStorageExportStop(id: Double): Double =
|
|
226
267
|
registry.tiCloudStorageExportTask(id.toInt())?.task?.stop().normalized()
|
|
227
268
|
|
|
269
|
+
override fun tiCloudStorageExportCancel(id: Double): Double =
|
|
270
|
+
registry.tiCloudStorageExportTask(id.toInt())?.task?.cancel().normalized()
|
|
271
|
+
|
|
228
272
|
override fun tiCloudStorageDispose(id: Double): Double {
|
|
229
273
|
val objectId = id.toInt()
|
|
230
274
|
val code = registry.cloudStorage(objectId)?.dispose().normalized()
|
|
@@ -290,6 +334,30 @@ class TiRtcModule(
|
|
|
290
334
|
return recordingStartResult(result.code, entry?.id)
|
|
291
335
|
}
|
|
292
336
|
|
|
337
|
+
@ReactMethod
|
|
338
|
+
override fun tiCloudStorageReplayStartRawDump(
|
|
339
|
+
id: Double,
|
|
340
|
+
audioChannelIds: ReadableArray,
|
|
341
|
+
videoChannelIds: ReadableArray,
|
|
342
|
+
promise: Promise,
|
|
343
|
+
) {
|
|
344
|
+
val replay = registry.tiCloudStorageReplay(id.toInt())
|
|
345
|
+
if (replay == null) {
|
|
346
|
+
promise.resolve(rawDumpStartResult(TiRtcReactNativeErrors.OBJECT_RELEASED, null))
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
val options = TiCloudStorageRawDumpOptions(
|
|
350
|
+
IntArray(audioChannelIds.size()) { audioChannelIds.getInt(it) },
|
|
351
|
+
IntArray(videoChannelIds.size()) { videoChannelIds.getInt(it) },
|
|
352
|
+
)
|
|
353
|
+
replay.startRawDump(options) { result ->
|
|
354
|
+
val entry = result.dump?.let(registry::add)
|
|
355
|
+
reactContext.runOnJSQueueThread {
|
|
356
|
+
promise.resolve(rawDumpStartResult(result.code, entry?.id))
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
293
361
|
@ReactMethod
|
|
294
362
|
override fun tiCloudStorageRecordingTaskStop(id: Double, promise: Promise) {
|
|
295
363
|
val objectId = id.toInt()
|
|
@@ -426,6 +494,18 @@ class TiRtcModule(
|
|
|
426
494
|
}
|
|
427
495
|
}
|
|
428
496
|
|
|
497
|
+
private fun resolveCloudStorageExport(
|
|
498
|
+
promise: Promise, result: TiCloudStorageExportResult,
|
|
499
|
+
progress: com.tange.ai.tirtc.TiCloudStorageExportProgress?,
|
|
500
|
+
) {
|
|
501
|
+
val fileId = result.file?.let { file ->
|
|
502
|
+
nextMediaFileId.getAndIncrement().also { mediaFiles[it] = file }
|
|
503
|
+
}
|
|
504
|
+
reactContext.runOnJSQueueThread {
|
|
505
|
+
promise.resolve(tiCloudStorageExportResult(result, fileId, progress))
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
429
509
|
@ReactMethod
|
|
430
510
|
override fun uploadLogs(promise: Promise) {
|
|
431
511
|
uploadLogsToPromise(promise)
|
|
@@ -495,6 +575,49 @@ class TiRtcModule(
|
|
|
495
575
|
return recordingStartResult(result.code, entry?.id)
|
|
496
576
|
}
|
|
497
577
|
|
|
578
|
+
@ReactMethod
|
|
579
|
+
override fun connStartRawDump(
|
|
580
|
+
id: Double,
|
|
581
|
+
audioStreamIds: ReadableArray,
|
|
582
|
+
videoStreamIds: ReadableArray,
|
|
583
|
+
uplinkAudioStreamIds: ReadableArray,
|
|
584
|
+
promise: Promise,
|
|
585
|
+
) {
|
|
586
|
+
val conn = registry.conn(id.toInt())
|
|
587
|
+
if (conn == null) {
|
|
588
|
+
promise.resolve(rawDumpStartResult(TiRtcReactNativeErrors.OBJECT_RELEASED, null))
|
|
589
|
+
return
|
|
590
|
+
}
|
|
591
|
+
val options = TiRtcRawDumpOptions(
|
|
592
|
+
IntArray(audioStreamIds.size()) { audioStreamIds.getInt(it) },
|
|
593
|
+
IntArray(videoStreamIds.size()) { videoStreamIds.getInt(it) },
|
|
594
|
+
IntArray(uplinkAudioStreamIds.size()) { uplinkAudioStreamIds.getInt(it) },
|
|
595
|
+
)
|
|
596
|
+
conn.startRawDump(options) { result ->
|
|
597
|
+
val entry = result.dump?.let(registry::add)
|
|
598
|
+
reactContext.runOnJSQueueThread {
|
|
599
|
+
promise.resolve(rawDumpStartResult(result.code, entry?.id))
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
@ReactMethod
|
|
605
|
+
override fun rawDumpStop(id: Double, promise: Promise) {
|
|
606
|
+
val objectId = id.toInt()
|
|
607
|
+
val dump = registry.rawDump(objectId)
|
|
608
|
+
if (dump == null) {
|
|
609
|
+
promise.resolve(rawDumpStopResult(TiRtcReactNativeErrors.OBJECT_RELEASED, null))
|
|
610
|
+
return
|
|
611
|
+
}
|
|
612
|
+
dump.stop { result ->
|
|
613
|
+
val terminal = result.code != TiCloudStorageErrorCode.IN_USE
|
|
614
|
+
if (terminal) registry.release(objectId)
|
|
615
|
+
reactContext.runOnJSQueueThread {
|
|
616
|
+
promise.resolve(rawDumpStopResult(result.code, result.archive, terminal))
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
498
621
|
@ReactMethod
|
|
499
622
|
override fun recordingTaskStop(id: Double, promise: Promise) {
|
|
500
623
|
val objectId = id.toInt()
|
|
@@ -12,7 +12,9 @@ import com.tange.ai.tirtc.TiRtcRecordingTask
|
|
|
12
12
|
import com.tange.ai.tirtc.TiRtcSnapshotFile
|
|
13
13
|
import com.tange.ai.tirtc.TiRtcVideoInput
|
|
14
14
|
import com.tange.ai.tirtc.TiRtcVideoOutput
|
|
15
|
+
import com.tange.ai.tirtc.TiRawDump
|
|
15
16
|
import com.tange.ai.tirtc.TiCloudStorage
|
|
17
|
+
import com.tange.ai.tirtc.TiCloudStorageErrorCode
|
|
16
18
|
import com.tange.ai.tirtc.TiCloudStorageAudioOutput
|
|
17
19
|
import com.tange.ai.tirtc.TiCloudStorageRecordingFile
|
|
18
20
|
import com.tange.ai.tirtc.TiCloudStorageRecordingTask
|
|
@@ -31,7 +33,7 @@ internal object TiRtcReactNativeCleanup {
|
|
|
31
33
|
}
|
|
32
34
|
fun finish() {
|
|
33
35
|
if (pending.decrementAndGet() == 0) {
|
|
34
|
-
Handler(Looper.getMainLooper()).post { releaseOwners(objects) }
|
|
36
|
+
Handler(Looper.getMainLooper()).post { releaseOwners(objects.toMutableList()) }
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
fun delete(file: Any?) {
|
|
@@ -53,6 +55,10 @@ internal object TiRtcReactNativeCleanup {
|
|
|
53
55
|
files.forEach(::delete)
|
|
54
56
|
objects.forEach { value ->
|
|
55
57
|
when (value) {
|
|
58
|
+
is TiRawDump -> {
|
|
59
|
+
retain()
|
|
60
|
+
value.stop { finish() }
|
|
61
|
+
}
|
|
56
62
|
is TiRtcRecordingTask -> {
|
|
57
63
|
retain()
|
|
58
64
|
value.stop { result ->
|
|
@@ -79,31 +85,60 @@ internal object TiRtcReactNativeCleanup {
|
|
|
79
85
|
finish()
|
|
80
86
|
}
|
|
81
87
|
|
|
82
|
-
private fun releaseOwners(objects:
|
|
88
|
+
private fun releaseOwners(objects: MutableList<Any>, delayMs: Long = 10) {
|
|
89
|
+
fun released(operation: String, code: Int): Boolean {
|
|
90
|
+
if (code == 0 || code == TiRtcReactNativeErrors.OBJECT_RELEASED) return true
|
|
91
|
+
if (delayMs == 10L || code != TiCloudStorageErrorCode.IN_USE) logFailure(operation, code)
|
|
92
|
+
// Closing the JS handoff wakes its waiter; the Native callback may still be
|
|
93
|
+
// unwinding. Retain the owners and yield the main queue until disposal succeeds.
|
|
94
|
+
Handler(Looper.getMainLooper()).postDelayed({ releaseOwners(objects, minOf(1000, delayMs * 2)) }, delayMs)
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
objects.filterIsInstance<TiCloudStorageReplay>().forEach { replay ->
|
|
98
|
+
if (!released("ti-cloud-storage-replay-stop", replay.stop())) return
|
|
99
|
+
}
|
|
83
100
|
objects.filterIsInstance<TiCloudStorageAudioOutput>().forEach { output ->
|
|
84
|
-
|
|
85
|
-
|
|
101
|
+
if (!released("ti-cloud-storage-audio-detach", output.detach())) return
|
|
102
|
+
if (!released("ti-cloud-storage-audio-dispose", output.dispose())) return
|
|
103
|
+
objects.remove(output)
|
|
86
104
|
}
|
|
87
105
|
objects.filterIsInstance<TiCloudStorageVideoOutput>().forEach { output ->
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
objects.filterIsInstance<
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
if (!released("ti-cloud-storage-video-detach", output.detach())) return
|
|
107
|
+
if (!released("ti-cloud-storage-video-view-detach", output.detachView())) return
|
|
108
|
+
if (!released("ti-cloud-storage-video-dispose", output.dispose())) return
|
|
109
|
+
objects.remove(output)
|
|
110
|
+
}
|
|
111
|
+
objects.filterIsInstance<TiRtcAudioOutput>().forEach {
|
|
112
|
+
if (!released("audio_output_dispose", it.dispose())) return
|
|
113
|
+
objects.remove(it)
|
|
114
|
+
}
|
|
115
|
+
objects.filterIsInstance<TiRtcVideoOutput>().forEach {
|
|
116
|
+
if (!released("video_output_dispose", it.dispose())) return
|
|
117
|
+
objects.remove(it)
|
|
118
|
+
}
|
|
119
|
+
objects.filterIsInstance<TiRtcAudioInput>().forEach {
|
|
120
|
+
if (!released("audio_input_dispose", it.dispose())) return
|
|
121
|
+
objects.remove(it)
|
|
122
|
+
}
|
|
123
|
+
objects.filterIsInstance<TiRtcVideoInput>().forEach {
|
|
124
|
+
if (!released("video_input_dispose", it.dispose())) return
|
|
125
|
+
objects.remove(it)
|
|
126
|
+
}
|
|
96
127
|
objects.filterIsInstance<TiCloudStorageReplay>().forEach { replay ->
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
if (!released("ti-cloud-storage-replay-dispose", replay.dispose())) return
|
|
129
|
+
objects.remove(replay)
|
|
99
130
|
}
|
|
100
131
|
objects.filterIsInstance<TiRtcConn>().forEach { conn ->
|
|
101
|
-
|
|
102
|
-
|
|
132
|
+
if (!released("conn_disconnect", conn.disconnect())) return
|
|
133
|
+
if (!released("conn_dispose", conn.dispose())) return
|
|
134
|
+
objects.remove(conn)
|
|
135
|
+
}
|
|
136
|
+
objects.filterIsInstance<TiCloudStorage>().forEach {
|
|
137
|
+
if (!released("ti-cloud-storage-dispose", it.dispose())) return
|
|
138
|
+
objects.remove(it)
|
|
103
139
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
logFailure("runtime_shutdown", TiRtc.shutdown())
|
|
140
|
+
if (!released("ti-cloud-storage-shutdown", TiCloudStorage.shutdown())) return
|
|
141
|
+
if (!released("runtime_shutdown", TiRtc.shutdown())) return
|
|
107
142
|
TiRtcLogging.i(TAG, "[module_invalidate] native owners released")
|
|
108
143
|
}
|
|
109
144
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
package com.tange.ai.tirtc.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
|
|
6
|
+
/** The SDK's single serial background queue permits one borrowed event per module. */
|
|
7
|
+
internal class TiRtcReactNativeCloudStorageCallbacks(private val wake: () -> Unit) {
|
|
8
|
+
private val monitor = Object()
|
|
9
|
+
private var closed = false
|
|
10
|
+
private var pending: WritableMap? = null
|
|
11
|
+
private var claimed = false
|
|
12
|
+
private var sequence = 0L
|
|
13
|
+
|
|
14
|
+
fun deliver(event: WritableMap) {
|
|
15
|
+
val deliveryId = synchronized(monitor) {
|
|
16
|
+
if (closed) return
|
|
17
|
+
check(pending == null) { "Cloud Storage callbacks require serial SDK delivery" }
|
|
18
|
+
sequence += 1
|
|
19
|
+
pending = event
|
|
20
|
+
claimed = false
|
|
21
|
+
sequence
|
|
22
|
+
}
|
|
23
|
+
var interrupted = false
|
|
24
|
+
try {
|
|
25
|
+
wake()
|
|
26
|
+
synchronized(monitor) {
|
|
27
|
+
while (!closed && pending != null && sequence == deliveryId) {
|
|
28
|
+
try { monitor.wait() } catch (_: InterruptedException) { interrupted = true }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} finally {
|
|
32
|
+
if (interrupted) Thread.currentThread().interrupt()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun take(): WritableMap? = synchronized(monitor) {
|
|
37
|
+
val event = pending ?: return@synchronized null
|
|
38
|
+
if (closed || claimed) return@synchronized null
|
|
39
|
+
claimed = true
|
|
40
|
+
Arguments.createMap().apply {
|
|
41
|
+
putDouble("deliveryId", sequence.toDouble())
|
|
42
|
+
putMap("event", event)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fun acknowledge(deliveryId: Double): Double = synchronized(monitor) {
|
|
47
|
+
if (!closed && claimed && deliveryId == sequence.toDouble()) {
|
|
48
|
+
pending = null
|
|
49
|
+
claimed = false
|
|
50
|
+
monitor.notifyAll()
|
|
51
|
+
}
|
|
52
|
+
0.0
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fun close() = synchronized(monitor) {
|
|
56
|
+
closed = true
|
|
57
|
+
pending = null
|
|
58
|
+
monitor.notifyAll()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -23,6 +23,7 @@ import com.tange.ai.tirtc.TiCloudStorageOutputErrorListener
|
|
|
23
23
|
import com.tange.ai.tirtc.TiCloudStorageReplay
|
|
24
24
|
import com.tange.ai.tirtc.TiCloudStorageReplayCompletedListener
|
|
25
25
|
import com.tange.ai.tirtc.TiCloudStorageReplayErrorListener
|
|
26
|
+
import com.tange.ai.tirtc.TiCloudStorageRecordingGapListener
|
|
26
27
|
import com.tange.ai.tirtc.TiCloudStorageTimeChangedListener
|
|
27
28
|
import com.tange.ai.tirtc.TiCloudStorageVideoOutput
|
|
28
29
|
import com.tange.ai.tirtc.TiCloudStorageVideoOutputStateListener
|
|
@@ -36,6 +37,7 @@ internal class TiRtcReactNativeEventBinder(
|
|
|
36
37
|
replay.onTimeChanged = TiCloudStorageTimeChangedListener { events.tiCloudStorageReplayTimeChanged(id, it) }
|
|
37
38
|
replay.onCompleted = TiCloudStorageReplayCompletedListener { events.tiCloudStorageReplayCompleted(id) }
|
|
38
39
|
replay.onError = TiCloudStorageReplayErrorListener { events.tiCloudStorageReplayError(id, it) }
|
|
40
|
+
replay.onRecordingGap = TiCloudStorageRecordingGapListener { events.tiCloudStorageReplayRecordingGap(id, it) }
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
fun wireCloudStorageAudioOutput(id: Int, output: TiCloudStorageAudioOutput) {
|
|
@@ -4,6 +4,8 @@ import android.util.Size
|
|
|
4
4
|
import com.facebook.react.bridge.Arguments
|
|
5
5
|
import com.facebook.react.bridge.WritableArray
|
|
6
6
|
import com.facebook.react.bridge.WritableMap
|
|
7
|
+
import com.tange.ai.tirtc.TiCloudStorageExportProgress
|
|
8
|
+
import com.tange.ai.tirtc.TiCloudStorageRecordingGap
|
|
7
9
|
|
|
8
10
|
internal class TiRtcReactNativeEventSink(
|
|
9
11
|
private val registry: TiRtcReactNativeRegistry,
|
|
@@ -60,6 +62,24 @@ internal class TiRtcReactNativeEventSink(
|
|
|
60
62
|
})
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
fun tiCloudStorageReplayRecordingGap(objectId: Int, gap: TiCloudStorageRecordingGap) {
|
|
66
|
+
emit(base("tiCloudStorageReplayRecordingGap", objectId).apply {
|
|
67
|
+
putMap("recordingGap", tiCloudStorageRecordingGap(gap))
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fun tiCloudStorageExportProgress(objectId: Int, progress: TiCloudStorageExportProgress) {
|
|
72
|
+
emit(base("tiCloudStorageExportProgress", objectId).apply {
|
|
73
|
+
putMap("exportProgress", tiCloudStorageExportProgress(progress))
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
fun tiCloudStorageExportRecordingGap(objectId: Int, gap: TiCloudStorageRecordingGap) {
|
|
78
|
+
emit(base("tiCloudStorageExportRecordingGap", objectId).apply {
|
|
79
|
+
putMap("recordingGap", tiCloudStorageRecordingGap(gap))
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
63
83
|
fun audioOutputStateChanged(
|
|
64
84
|
objectId: Int,
|
|
65
85
|
state: String,
|