tirtc-react-native 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/LICENSE +11 -0
  2. package/README.md +140 -0
  3. package/TiRtcReactNative.podspec +35 -0
  4. package/android/build.gradle +37 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcModule.kt +482 -0
  7. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcPackage.kt +43 -0
  8. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeErrors.kt +32 -0
  9. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventBinder.kt +99 -0
  10. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventSink.kt +168 -0
  11. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMaps.kt +49 -0
  12. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMetricsMaps.kt +173 -0
  13. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeModuleSupport.kt +91 -0
  14. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeOptions.kt +127 -0
  15. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeRegistry.kt +71 -0
  16. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcVideoInputPreviewManager.kt +109 -0
  17. package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcVideoOutputViewManager.kt +109 -0
  18. package/ios/TiRtcReactNative+Conn.mm +112 -0
  19. package/ios/TiRtcReactNative+Input.mm +196 -0
  20. package/ios/TiRtcReactNative+Output.mm +172 -0
  21. package/ios/TiRtcReactNative+Private.h +28 -0
  22. package/ios/TiRtcReactNative.h +6 -0
  23. package/ios/TiRtcReactNative.mm +189 -0
  24. package/ios/TiRtcReactNativeErrors.h +18 -0
  25. package/ios/TiRtcReactNativeErrors.mm +105 -0
  26. package/ios/TiRtcReactNativeEventBinder.h +19 -0
  27. package/ios/TiRtcReactNativeEventBinder.mm +226 -0
  28. package/ios/TiRtcReactNativeEventSink.h +29 -0
  29. package/ios/TiRtcReactNativeEventSink.mm +125 -0
  30. package/ios/TiRtcReactNativeMaps.h +21 -0
  31. package/ios/TiRtcReactNativeMaps.mm +249 -0
  32. package/ios/TiRtcReactNativeNative.h +8 -0
  33. package/ios/TiRtcReactNativeOptions.h +11 -0
  34. package/ios/TiRtcReactNativeOptions.mm +123 -0
  35. package/ios/TiRtcReactNativeRegistry.h +20 -0
  36. package/ios/TiRtcReactNativeRegistry.mm +119 -0
  37. package/ios/TiRtcVideoInputPreview.h +5 -0
  38. package/ios/TiRtcVideoInputPreview.mm +149 -0
  39. package/ios/TiRtcVideoOutputView.h +5 -0
  40. package/ios/TiRtcVideoOutputView.mm +149 -0
  41. package/lib/module/audio_input.js +177 -0
  42. package/lib/module/audio_input.js.map +1 -0
  43. package/lib/module/audio_output.js +168 -0
  44. package/lib/module/audio_output.js.map +1 -0
  45. package/lib/module/conn.js +174 -0
  46. package/lib/module/conn.js.map +1 -0
  47. package/lib/module/errors.js +63 -0
  48. package/lib/module/errors.js.map +1 -0
  49. package/lib/module/events.js +37 -0
  50. package/lib/module/events.js.map +1 -0
  51. package/lib/module/index.js +9 -0
  52. package/lib/module/index.js.map +1 -0
  53. package/lib/module/logging.js +26 -0
  54. package/lib/module/logging.js.map +1 -0
  55. package/lib/module/native.js +5 -0
  56. package/lib/module/native.js.map +1 -0
  57. package/lib/module/native_object_id.js +47 -0
  58. package/lib/module/native_object_id.js.map +1 -0
  59. package/lib/module/runtime.js +27 -0
  60. package/lib/module/runtime.js.map +1 -0
  61. package/lib/module/specs/NativeTiRtc.js +3 -0
  62. package/lib/module/specs/NativeTiRtc.js.map +1 -0
  63. package/lib/module/specs/TiRtcVideoInputPreviewNativeComponent.js +24 -0
  64. package/lib/module/specs/TiRtcVideoOutputViewNativeComponent.js +24 -0
  65. package/lib/module/types.js +93 -0
  66. package/lib/module/types.js.map +1 -0
  67. package/lib/module/video_input.js +266 -0
  68. package/lib/module/video_input.js.map +1 -0
  69. package/lib/module/video_output.js +224 -0
  70. package/lib/module/video_output.js.map +1 -0
  71. package/lib/typescript/audio_input.d.ts +25 -0
  72. package/lib/typescript/audio_output.d.ts +25 -0
  73. package/lib/typescript/conn.d.ts +28 -0
  74. package/lib/typescript/errors.d.ts +19 -0
  75. package/lib/typescript/events.d.ts +6 -0
  76. package/lib/typescript/index.d.ts +9 -0
  77. package/lib/typescript/logging.d.ts +9 -0
  78. package/lib/typescript/native.d.ts +5 -0
  79. package/lib/typescript/native_object_id.d.ts +12 -0
  80. package/lib/typescript/runtime.d.ts +8 -0
  81. package/lib/typescript/specs/NativeTiRtc.d.ts +223 -0
  82. package/lib/typescript/specs/TiRtcVideoInputPreviewNativeComponent.d.ts +8 -0
  83. package/lib/typescript/specs/TiRtcVideoOutputViewNativeComponent.d.ts +8 -0
  84. package/lib/typescript/types.d.ts +281 -0
  85. package/lib/typescript/video_input.d.ts +50 -0
  86. package/lib/typescript/video_output.d.ts +46 -0
  87. package/package.json +80 -0
  88. package/react-native.config.js +10 -0
  89. package/src/audio_input.ts +205 -0
  90. package/src/audio_output.ts +202 -0
  91. package/src/conn.ts +222 -0
  92. package/src/errors.ts +81 -0
  93. package/src/events.ts +44 -0
  94. package/src/index.ts +61 -0
  95. package/src/logging.ts +33 -0
  96. package/src/native.ts +6 -0
  97. package/src/native_object_id.ts +63 -0
  98. package/src/runtime.ts +32 -0
  99. package/src/specs/NativeTiRtc.ts +256 -0
  100. package/src/specs/TiRtcVideoInputPreviewNativeComponent.ts +14 -0
  101. package/src/specs/TiRtcVideoOutputViewNativeComponent.ts +12 -0
  102. package/src/types.ts +340 -0
  103. package/src/video_input.ts +314 -0
  104. package/src/video_output.ts +273 -0
@@ -0,0 +1,168 @@
1
+ package com.tange.ai.tirtc.reactnative
2
+
3
+ import android.util.Size
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.WritableArray
6
+ import com.facebook.react.bridge.WritableMap
7
+
8
+ internal class TiRtcReactNativeEventSink(
9
+ private val registry: TiRtcReactNativeRegistry,
10
+ private val emit: (WritableMap) -> Unit,
11
+ ) {
12
+ fun connStateChanged(
13
+ objectId: Int,
14
+ state: String,
15
+ errorCode: Int,
16
+ ) {
17
+ emit(base("connStateChanged", objectId).apply {
18
+ putString("state", state)
19
+ putInt("errorCode", TiRtcReactNativeErrors.normalizeAndroid(errorCode))
20
+ })
21
+ }
22
+
23
+ fun connCommand(
24
+ objectId: Int,
25
+ commandId: Long,
26
+ data: ByteArray,
27
+ ) {
28
+ emit(base("connCommand", objectId).apply {
29
+ putDouble("commandId", commandId.toDouble())
30
+ putArray("data", data.toWritableArray())
31
+ })
32
+ }
33
+
34
+ fun connStreamMessage(
35
+ objectId: Int,
36
+ streamId: Int,
37
+ timestampMs: Long,
38
+ data: ByteArray,
39
+ ) {
40
+ emit(base("connStreamMessage", objectId).apply {
41
+ putInt("streamId", streamId)
42
+ putDouble("timestampMs", timestampMs.toDouble())
43
+ putArray("data", data.toWritableArray())
44
+ })
45
+ }
46
+
47
+ fun audioOutputStateChanged(
48
+ objectId: Int,
49
+ state: String,
50
+ ) {
51
+ emit(base("audioOutputStateChanged", objectId).apply {
52
+ putString("state", state)
53
+ })
54
+ }
55
+
56
+ fun audioOutputError(
57
+ objectId: Int,
58
+ code: Int,
59
+ ) {
60
+ emit(base("audioOutputError", objectId).apply {
61
+ putInt("code", TiRtcReactNativeErrors.normalizeAndroid(code))
62
+ })
63
+ }
64
+
65
+ fun videoOutputStateChanged(
66
+ objectId: Int,
67
+ state: String,
68
+ ) {
69
+ emit(base("videoOutputStateChanged", objectId).apply {
70
+ putString("state", state)
71
+ })
72
+ }
73
+
74
+ fun videoOutputRenderSizeChanged(
75
+ objectId: Int,
76
+ size: Size,
77
+ ) {
78
+ emit(base("videoOutputRenderSizeChanged", objectId).apply {
79
+ putMap("size", size.toWritableMap())
80
+ })
81
+ }
82
+
83
+ fun videoOutputError(
84
+ objectId: Int,
85
+ code: Int,
86
+ ) {
87
+ emit(base("videoOutputError", objectId).apply {
88
+ putInt("code", TiRtcReactNativeErrors.normalizeAndroid(code))
89
+ })
90
+ }
91
+
92
+ fun audioInputStateChanged(
93
+ objectId: Int,
94
+ state: String,
95
+ ) {
96
+ emit(base("audioInputStateChanged", objectId).apply {
97
+ putString("state", state)
98
+ })
99
+ }
100
+
101
+ fun audioInputError(
102
+ objectId: Int,
103
+ code: Int,
104
+ message: String?,
105
+ ) {
106
+ emit(base("audioInputError", objectId).apply {
107
+ putInt("code", TiRtcReactNativeErrors.normalizeAndroid(code))
108
+ putString("message", message)
109
+ })
110
+ }
111
+
112
+ fun videoInputStateChanged(
113
+ objectId: Int,
114
+ state: String,
115
+ ) {
116
+ emit(base("videoInputStateChanged", objectId).apply {
117
+ putString("state", state)
118
+ })
119
+ }
120
+
121
+ fun videoInputActualConfigChanged(
122
+ objectId: Int,
123
+ size: Size,
124
+ fps: Int,
125
+ ) {
126
+ emit(base("videoInputActualConfigChanged", objectId).apply {
127
+ putMap("size", size.toWritableMap())
128
+ putInt("fps", fps)
129
+ })
130
+ }
131
+
132
+ fun videoInputError(
133
+ objectId: Int,
134
+ code: Int,
135
+ message: String?,
136
+ ) {
137
+ emit(base("videoInputError", objectId).apply {
138
+ putInt("code", TiRtcReactNativeErrors.normalizeAndroid(code))
139
+ putString("message", message)
140
+ })
141
+ }
142
+
143
+ private fun base(
144
+ type: String,
145
+ objectId: Int,
146
+ ): WritableMap {
147
+ return Arguments.createMap().apply {
148
+ putString("type", type)
149
+ putInt("objectId", objectId)
150
+ putInt("generation", registry.generation(objectId) ?: 0)
151
+ }
152
+ }
153
+
154
+ private fun ByteArray.toWritableArray(): WritableArray {
155
+ return Arguments.createArray().apply {
156
+ for (byte in this@toWritableArray) {
157
+ pushInt(byte.toInt() and 0xff)
158
+ }
159
+ }
160
+ }
161
+
162
+ private fun Size.toWritableMap(): WritableMap {
163
+ return Arguments.createMap().apply {
164
+ putInt("width", width)
165
+ putInt("height", height)
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,49 @@
1
+ package com.tange.ai.tirtc.reactnative
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
5
+
6
+ internal fun createResult(
7
+ code: Int,
8
+ objectId: Int,
9
+ generation: Int,
10
+ message: String? = null,
11
+ ): WritableMap {
12
+ return Arguments.createMap().apply {
13
+ putInt("code", code)
14
+ putInt("objectId", objectId)
15
+ putInt("generation", generation)
16
+ putString("message", message)
17
+ }
18
+ }
19
+
20
+ internal fun stateResult(
21
+ code: Int,
22
+ state: String?,
23
+ ): WritableMap {
24
+ return Arguments.createMap().apply {
25
+ putInt("code", code)
26
+ putString("state", state)
27
+ }
28
+ }
29
+
30
+ internal fun sizeResult(
31
+ code: Int,
32
+ width: Int?,
33
+ height: Int?,
34
+ ): WritableMap {
35
+ return Arguments.createMap().apply {
36
+ putInt("code", code)
37
+ if (width == null || height == null) {
38
+ putNull("size")
39
+ } else {
40
+ putMap(
41
+ "size",
42
+ Arguments.createMap().apply {
43
+ putInt("width", width)
44
+ putInt("height", height)
45
+ },
46
+ )
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,173 @@
1
+ package com.tange.ai.tirtc.reactnative
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.WritableMap
5
+ import com.tange.ai.tirtc.TiRtcAudioOutputDebugSnapshotResult
6
+ import com.tange.ai.tirtc.TiRtcAudioOutputMetricsResult
7
+ import com.tange.ai.tirtc.TiRtcConnMetricsResult
8
+ import com.tange.ai.tirtc.TiRtcOutputStartupMetrics
9
+ import com.tange.ai.tirtc.TiRtcOutputStutterMetrics
10
+ import com.tange.ai.tirtc.TiRtcVideoOutputDebugSnapshotResult
11
+ import com.tange.ai.tirtc.TiRtcVideoOutputMetricsResult
12
+
13
+ internal fun audioMetricsResult(result: TiRtcAudioOutputMetricsResult): WritableMap =
14
+ snapshotResult(result.code, result.snapshot?.let { snapshot ->
15
+ Arguments.createMap().apply {
16
+ putMap("startup", startup(snapshot.startup))
17
+ putMap("stutter", stutter(snapshot.stutter))
18
+ putDouble("estimatedOutputLatencyMs", snapshot.estimatedOutputLatencyMs.toDouble())
19
+ putNullableString("audioCodec", audioCodec(snapshot.audioCodec))
20
+ putInt("audioSampleRateHz", snapshot.audioSampleRateHz)
21
+ putInt("audioChannels", snapshot.audioChannels)
22
+ putDouble("audioInputBitrateKbps", snapshot.audioInputBitrateKbps)
23
+ putDouble("audioInputPacketRate", snapshot.audioInputPacketRate)
24
+ putDouble("audioRenderCallbackRate", snapshot.audioRenderCallbackRate)
25
+ putDouble("statsRefreshIntervalMs", snapshot.statsRefreshIntervalMs.toDouble())
26
+ putDouble("statsUpdatedAtMonotonicMs", snapshot.statsUpdatedAtMs.toDouble())
27
+ }
28
+ })
29
+
30
+ internal fun videoMetricsResult(result: TiRtcVideoOutputMetricsResult): WritableMap =
31
+ snapshotResult(result.code, result.snapshot?.let { snapshot ->
32
+ Arguments.createMap().apply {
33
+ putMap("startup", startup(snapshot.startup))
34
+ putMap("stutter", stutter(snapshot.stutter))
35
+ putDouble("estimatedOutputLatencyMs", snapshot.estimatedOutputLatencyMs.toDouble())
36
+ putNullableString("videoCodec", videoCodec(snapshot.videoCodec))
37
+ putInt("videoWidth", snapshot.videoWidth)
38
+ putInt("videoHeight", snapshot.videoHeight)
39
+ putString("decoderBackend", decoderBackend(snapshot.decoderBackend))
40
+ putDouble("videoInputBitrateKbps", snapshot.videoInputBitrateKbps)
41
+ putDouble("videoInputFps", snapshot.videoInputFps)
42
+ putDouble("videoDecodedFps", snapshot.videoDecodedFps)
43
+ putDouble("videoRenderFps", snapshot.videoRenderFps)
44
+ putDouble("statsRefreshIntervalMs", snapshot.statsRefreshIntervalMs.toDouble())
45
+ putDouble("statsUpdatedAtMonotonicMs", snapshot.statsUpdatedAtMs.toDouble())
46
+ }
47
+ })
48
+
49
+ internal fun connMetricsResult(result: TiRtcConnMetricsResult): WritableMap =
50
+ snapshotResult(result.code, result.snapshot?.let { snapshot ->
51
+ Arguments.createMap().apply {
52
+ putBoolean("hasConnectStart", snapshot.hasConnectStart)
53
+ putBoolean("hasConnected", snapshot.hasConnected)
54
+ putDouble("connectStartMonotonicMs", snapshot.connectStartMonotonicMs.toDouble())
55
+ putDouble("connectedMonotonicMs", snapshot.connectedMonotonicMs.toDouble())
56
+ putBoolean("isReady", snapshot.isReady)
57
+ snapshot.connectDurationMs?.let { putDouble("connectDurationMs", it.toDouble()) } ?: putNull("connectDurationMs")
58
+ }
59
+ })
60
+
61
+ internal fun audioDebugResult(result: TiRtcAudioOutputDebugSnapshotResult): WritableMap =
62
+ snapshotResult(result.code, result.snapshot?.let { snapshot ->
63
+ Arguments.createMap().apply {
64
+ putNullableString("codec", audioCodec(snapshot.codec))
65
+ putInt("sampleRateHz", snapshot.sampleRate)
66
+ putInt("channels", snapshot.channels)
67
+ }
68
+ })
69
+
70
+ internal fun videoDebugResult(result: TiRtcVideoOutputDebugSnapshotResult): WritableMap =
71
+ snapshotResult(result.code, result.snapshot?.let { snapshot ->
72
+ Arguments.createMap().apply {
73
+ putNullableString("codec", videoCodec(snapshot.codec))
74
+ putString("bitstreamFormat", bitstreamFormat(snapshot.bitstreamFormat))
75
+ putInt("width", snapshot.width)
76
+ putInt("height", snapshot.height)
77
+ putString("decoderPreference", decoderPreference(snapshot.decoderPreference))
78
+ putString("resolvedDecoderBackend", decoderBackend(snapshot.resolvedDecoderBackend))
79
+ }
80
+ })
81
+
82
+ internal fun releasedSnapshotResult(): WritableMap =
83
+ snapshotResult(TiRtcReactNativeErrors.OBJECT_RELEASED, null)
84
+
85
+ private fun snapshotResult(
86
+ code: Int,
87
+ snapshot: WritableMap?,
88
+ ): WritableMap =
89
+ Arguments.createMap().apply {
90
+ putInt("code", TiRtcReactNativeErrors.normalizeAndroid(code))
91
+ if (snapshot == null) {
92
+ putNull("snapshot")
93
+ } else {
94
+ putMap("snapshot", snapshot)
95
+ }
96
+ }
97
+
98
+ private fun startup(metrics: TiRtcOutputStartupMetrics): WritableMap =
99
+ Arguments.createMap().apply {
100
+ putBoolean("hasConnectStart", metrics.hasConnectStart)
101
+ putBoolean("hasConnected", metrics.hasConnected)
102
+ putBoolean("hasFirstPacket", metrics.hasFirstPacket)
103
+ putBoolean("hasFirstOutput", metrics.hasFirstOutput)
104
+ putDouble("connectStartMonotonicMs", metrics.connectStartMonotonicMs.toDouble())
105
+ putDouble("connectedMonotonicMs", metrics.connectedMonotonicMs.toDouble())
106
+ putDouble("firstPacketMonotonicMs", metrics.firstPacketMonotonicMs.toDouble())
107
+ putDouble("firstOutputMonotonicMs", metrics.firstOutputMonotonicMs.toDouble())
108
+ putDouble("firstPacketAfterConnectedMs", metrics.firstPacketAfterConnectedMs.toDouble())
109
+ putDouble("firstOutputAfterConnectedMs", metrics.firstOutputAfterConnectedMs.toDouble())
110
+ putDouble("timeToFirstOutputMs", metrics.timeToFirstOutputMs.toDouble())
111
+ }
112
+
113
+ private fun stutter(metrics: TiRtcOutputStutterMetrics): WritableMap =
114
+ Arguments.createMap().apply {
115
+ putDouble("stutterThresholdMs", metrics.stutterThresholdMs.toDouble())
116
+ putDouble("outputDurationMs", metrics.outputDurationMs.toDouble())
117
+ putDouble("stutterTotalMs", metrics.stutterTotalMs.toDouble())
118
+ putDouble("stutterCount", metrics.stutterCount.toDouble())
119
+ putDouble("stutterPeakMs", metrics.stutterPeakMs.toDouble())
120
+ putDouble("stutterAverageMs", metrics.stutterAverageMs.toDouble())
121
+ putDouble("stutterRatePercent", metrics.stutterRate)
122
+ }
123
+
124
+ private fun WritableMap.putNullableString(
125
+ name: String,
126
+ value: String?,
127
+ ) {
128
+ if (value == null) {
129
+ putNull(name)
130
+ } else {
131
+ putString(name, value)
132
+ }
133
+ }
134
+
135
+ private fun audioCodec(codec: Int): String? =
136
+ when (codec) {
137
+ 1 -> "g711a"
138
+ 2 -> "aac"
139
+ 3 -> "pcm"
140
+ 4 -> "opus"
141
+ 5 -> "amr"
142
+ else -> null
143
+ }
144
+
145
+ private fun videoCodec(codec: Int): String? =
146
+ when (codec) {
147
+ 65 -> "h264"
148
+ 66 -> "h265"
149
+ 67 -> "mjpeg"
150
+ else -> null
151
+ }
152
+
153
+ private fun decoderBackend(backend: Int): String =
154
+ when (backend) {
155
+ 1 -> "software"
156
+ 2 -> "hardware"
157
+ else -> "unknown"
158
+ }
159
+
160
+ private fun decoderPreference(preference: Int): String =
161
+ when (preference) {
162
+ 1 -> "software"
163
+ 2 -> "hardware"
164
+ else -> "auto"
165
+ }
166
+
167
+ private fun bitstreamFormat(format: Int): String =
168
+ when (format) {
169
+ 1 -> "h264AnnexB"
170
+ 2 -> "h265AnnexB"
171
+ 3 -> "mjpegJfif"
172
+ else -> "unknown"
173
+ }
@@ -0,0 +1,91 @@
1
+ package com.tange.ai.tirtc.reactnative
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import android.util.Log
6
+ import com.facebook.react.bridge.Arguments
7
+ import com.facebook.react.bridge.Promise
8
+ import com.facebook.react.bridge.ReadableArray
9
+ import com.facebook.react.bridge.WritableMap
10
+ import com.tange.ai.tirtc.TiRtcLogging
11
+ import java.util.concurrent.atomic.AtomicBoolean
12
+
13
+ private const val LOG_UPLOAD_TIMEOUT_MS = 30_000L
14
+
15
+ internal fun uploadLogsToPromise(
16
+ promise: Promise,
17
+ tag: String,
18
+ ) {
19
+ Log.i(tag, "uploadLogs start")
20
+ val settled = AtomicBoolean(false)
21
+ val timeoutHandler = Handler(Looper.getMainLooper())
22
+ val timeout =
23
+ Runnable {
24
+ if (settled.compareAndSet(false, true)) {
25
+ Log.w(tag, "uploadLogs timeout")
26
+ promise.resolve(
27
+ Arguments.createMap().apply {
28
+ putInt("code", TiRtcReactNativeErrors.PLATFORM_INTERNAL)
29
+ putNull("logId")
30
+ },
31
+ )
32
+ }
33
+ }
34
+ timeoutHandler.postDelayed(timeout, LOG_UPLOAD_TIMEOUT_MS)
35
+ val startCode = TiRtcLogging.upload { code, logId ->
36
+ if (!settled.compareAndSet(false, true)) {
37
+ return@upload
38
+ }
39
+ timeoutHandler.removeCallbacks(timeout)
40
+ Log.i(tag, "uploadLogs completed code=$code logIdPresent=${!logId.isNullOrEmpty()}")
41
+ promise.resolve(
42
+ Arguments.createMap().apply {
43
+ putInt("code", TiRtcReactNativeErrors.normalizeAndroid(code))
44
+ putString("logId", logId)
45
+ },
46
+ )
47
+ }
48
+ if (startCode != 0) {
49
+ if (!settled.compareAndSet(false, true)) {
50
+ return
51
+ }
52
+ timeoutHandler.removeCallbacks(timeout)
53
+ Log.w(tag, "uploadLogs failed_to_start code=$startCode")
54
+ promise.resolve(
55
+ Arguments.createMap().apply {
56
+ putInt("code", TiRtcReactNativeErrors.normalizeAndroid(startCode))
57
+ putNull("logId")
58
+ },
59
+ )
60
+ }
61
+ }
62
+
63
+ internal fun registerReactNativeObject(
64
+ registry: TiRtcReactNativeRegistry,
65
+ factory: () -> Any,
66
+ onRegistered: ((
67
+ TiRtcReactNativeRegistry.Entry,
68
+ Any,
69
+ ) -> Unit)? = null,
70
+ ): WritableMap {
71
+ return try {
72
+ val value = factory()
73
+ val entry = registry.add(value)
74
+ onRegistered?.invoke(entry, value)
75
+ createResult(TiRtcReactNativeErrors.OK, entry.id, entry.generation)
76
+ } catch (error: Throwable) {
77
+ createResult(extractNativeCreateCode(error.message), 0, 0, error.message)
78
+ }
79
+ }
80
+
81
+ internal fun Int?.normalized(): Double =
82
+ TiRtcReactNativeErrors.normalizeAndroid(this ?: TiRtcReactNativeErrors.OBJECT_RELEASED).toDouble()
83
+
84
+ internal fun ReadableArray.toByteArray(): ByteArray {
85
+ return ByteArray(size()) { index -> getInt(index).toByte() }
86
+ }
87
+
88
+ private fun extractNativeCreateCode(message: String?): Int {
89
+ val code = Regex("code=(\\d+)").find(message ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
90
+ return TiRtcReactNativeErrors.normalizeAndroid(code ?: TiRtcReactNativeErrors.PLATFORM_INTERNAL)
91
+ }
@@ -0,0 +1,127 @@
1
+ package com.tange.ai.tirtc.reactnative
2
+
3
+ import com.facebook.react.bridge.ReadableMap
4
+ import com.tange.ai.tirtc.TiRtcAudioAecMode
5
+ import com.tange.ai.tirtc.TiRtcAudioAgcLevel
6
+ import com.tange.ai.tirtc.TiRtcAudioAnsLevel
7
+ import com.tange.ai.tirtc.TiRtcAudioChannelCount
8
+ import com.tange.ai.tirtc.TiRtcAudioCodec
9
+ import com.tange.ai.tirtc.TiRtcAudioInputOptions
10
+ import com.tange.ai.tirtc.TiRtcAudioOutputOptions
11
+ import com.tange.ai.tirtc.TiRtcAudioSampleRate
12
+ import com.tange.ai.tirtc.TiRtcCameraFacing
13
+ import com.tange.ai.tirtc.TiRtcOutputBufferStrategy
14
+ import com.tange.ai.tirtc.TiRtcVideoDecoderPreference
15
+ import com.tange.ai.tirtc.TiRtcVideoEncoderCodec
16
+ import com.tange.ai.tirtc.TiRtcVideoFrameRate
17
+ import com.tange.ai.tirtc.TiRtcVideoInputOptions
18
+ import com.tange.ai.tirtc.TiRtcVideoOutputOptions
19
+
20
+ internal fun audioOutputOptions(options: ReadableMap): TiRtcAudioOutputOptions {
21
+ return TiRtcAudioOutputOptions(
22
+ agcLevel = audioAgcLevel(options.getString("agcLevel")),
23
+ ansLevel = audioAnsLevel(options.getString("ansLevel")),
24
+ bufferStrategy = outputBufferStrategy(options),
25
+ maxBufferWatermarkMs = nullableInt(options, "maxBufferWatermarkMs"),
26
+ )
27
+ }
28
+
29
+ internal fun videoOutputOptions(options: ReadableMap): TiRtcVideoOutputOptions {
30
+ return TiRtcVideoOutputOptions(
31
+ decoderPreference = videoDecoderPreference(options.getString("decoderPreference")),
32
+ bufferStrategy = outputBufferStrategy(options),
33
+ maxBufferWatermarkMs = nullableInt(options, "maxBufferWatermarkMs"),
34
+ )
35
+ }
36
+
37
+ internal fun audioInputOptions(options: ReadableMap): TiRtcAudioInputOptions {
38
+ return TiRtcAudioInputOptions(
39
+ codec =
40
+ when (options.getString("codec")) {
41
+ "aac" -> TiRtcAudioCodec.AAC
42
+ "pcm" -> TiRtcAudioCodec.PCM
43
+ "opus" -> TiRtcAudioCodec.OPUS
44
+ "amr" -> TiRtcAudioCodec.AMR
45
+ else -> TiRtcAudioCodec.G711A
46
+ },
47
+ sampleRate =
48
+ if (options.getString("sampleRate") == "rate8k") {
49
+ TiRtcAudioSampleRate.RATE_8K
50
+ } else {
51
+ TiRtcAudioSampleRate.RATE_16K
52
+ },
53
+ channels = TiRtcAudioChannelCount.MONO,
54
+ aecMode =
55
+ if (options.getString("aecMode") == "enabled") {
56
+ TiRtcAudioAecMode.ENABLED
57
+ } else {
58
+ TiRtcAudioAecMode.DISABLED
59
+ },
60
+ agcLevel = audioAgcLevel(options.getString("agcLevel")),
61
+ ansLevel = audioAnsLevel(options.getString("ansLevel")),
62
+ )
63
+ }
64
+
65
+ internal fun videoInputOptions(options: ReadableMap): TiRtcVideoInputOptions {
66
+ return TiRtcVideoInputOptions(
67
+ codec =
68
+ when (options.getString("codec")) {
69
+ "mjpeg" -> TiRtcVideoEncoderCodec.MJPEG
70
+ else -> TiRtcVideoEncoderCodec.H264
71
+ },
72
+ width = options.getInt("width"),
73
+ height = options.getInt("height"),
74
+ frameRate =
75
+ when (options.getString("frameRate")) {
76
+ "fps10" -> TiRtcVideoFrameRate.FPS_10
77
+ "fps30" -> TiRtcVideoFrameRate.FPS_30
78
+ else -> TiRtcVideoFrameRate.FPS_15
79
+ },
80
+ bitrateKbps = options.getInt("bitrateKbps"),
81
+ cameraFacing =
82
+ if (options.getString("cameraFacing") == "back") {
83
+ TiRtcCameraFacing.BACK
84
+ } else {
85
+ TiRtcCameraFacing.FRONT
86
+ },
87
+ )
88
+ }
89
+
90
+ private fun audioAgcLevel(value: String?): TiRtcAudioAgcLevel {
91
+ return when (value) {
92
+ "low" -> TiRtcAudioAgcLevel.LOW
93
+ "medium" -> TiRtcAudioAgcLevel.MEDIUM
94
+ "high" -> TiRtcAudioAgcLevel.HIGH
95
+ else -> TiRtcAudioAgcLevel.DISABLED
96
+ }
97
+ }
98
+
99
+ private fun audioAnsLevel(value: String?): TiRtcAudioAnsLevel {
100
+ return when (value) {
101
+ "low" -> TiRtcAudioAnsLevel.LOW
102
+ "medium" -> TiRtcAudioAnsLevel.MEDIUM
103
+ "high" -> TiRtcAudioAnsLevel.HIGH
104
+ else -> TiRtcAudioAnsLevel.DISABLED
105
+ }
106
+ }
107
+
108
+ private fun videoDecoderPreference(value: String?): TiRtcVideoDecoderPreference {
109
+ return when (value) {
110
+ "software" -> TiRtcVideoDecoderPreference.SOFTWARE
111
+ "hardware" -> TiRtcVideoDecoderPreference.HARDWARE
112
+ else -> TiRtcVideoDecoderPreference.AUTO
113
+ }
114
+ }
115
+
116
+ private fun outputBufferStrategy(options: ReadableMap): TiRtcOutputBufferStrategy {
117
+ return if (options.getString("bufferStrategy") == "noBuffer") {
118
+ TiRtcOutputBufferStrategy.NO_BUFFER
119
+ } else {
120
+ TiRtcOutputBufferStrategy.AUTOMATIC
121
+ }
122
+ }
123
+
124
+ private fun nullableInt(
125
+ options: ReadableMap,
126
+ key: String,
127
+ ): Int? = if (options.isNull(key)) null else options.getInt(key)
@@ -0,0 +1,71 @@
1
+ package com.tange.ai.tirtc.reactnative
2
+
3
+ import com.tange.ai.tirtc.TiRtcAudioInput
4
+ import com.tange.ai.tirtc.TiRtcAudioOutput
5
+ import com.tange.ai.tirtc.TiRtcConn
6
+ import com.tange.ai.tirtc.TiRtcVideoInput
7
+ import com.tange.ai.tirtc.TiRtcVideoOutput
8
+ import java.util.concurrent.ConcurrentHashMap
9
+ import java.util.concurrent.atomic.AtomicInteger
10
+
11
+ internal object TiRtcReactNativeRegistry {
12
+ private val nextId = AtomicInteger(1)
13
+ private val nextGeneration = AtomicInteger(1)
14
+ private val entries = ConcurrentHashMap<Int, Entry>()
15
+ @Volatile private var events: TiRtcReactNativeEventSink? = null
16
+
17
+ data class Entry(
18
+ val id: Int,
19
+ val generation: Int,
20
+ val value: Any,
21
+ )
22
+
23
+ fun add(value: Any): Entry {
24
+ val id = nextId.getAndIncrement()
25
+ val generation = nextGeneration.getAndIncrement()
26
+ val entry = Entry(id, generation, value)
27
+ entries[id] = entry
28
+ return entry
29
+ }
30
+
31
+ fun bindEventSink(eventSink: TiRtcReactNativeEventSink) {
32
+ events = eventSink
33
+ }
34
+
35
+ fun release(id: Int) {
36
+ entries.remove(id)
37
+ }
38
+
39
+ fun generation(id: Int): Int? = entries[id]?.generation
40
+
41
+ fun conn(id: Int): TiRtcConn? = entries[id]?.value as? TiRtcConn
42
+
43
+ fun audioOutput(id: Int): TiRtcAudioOutput? = entries[id]?.value as? TiRtcAudioOutput
44
+
45
+ fun videoOutput(id: Int): TiRtcVideoOutput? = entries[id]?.value as? TiRtcVideoOutput
46
+
47
+ fun audioInput(id: Int): TiRtcAudioInput? = entries[id]?.value as? TiRtcAudioInput
48
+
49
+ fun videoInput(id: Int): TiRtcVideoInput? = entries[id]?.value as? TiRtcVideoInput
50
+
51
+ fun reportVideoOutputError(
52
+ id: Int,
53
+ code: Int,
54
+ ) {
55
+ if (entries.containsKey(id)) {
56
+ events?.videoOutputError(id, code)
57
+ }
58
+ }
59
+
60
+ fun reportVideoInputError(
61
+ id: Int,
62
+ code: Int,
63
+ message: String?,
64
+ ) {
65
+ if (entries.containsKey(id)) {
66
+ events?.videoInputError(id, code, message)
67
+ }
68
+ }
69
+
70
+ fun hasLiveObjects(): Boolean = entries.isNotEmpty()
71
+ }