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.
- package/LICENSE +11 -0
- package/README.md +140 -0
- package/TiRtcReactNative.podspec +35 -0
- package/android/build.gradle +37 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcModule.kt +482 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcPackage.kt +43 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeErrors.kt +32 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventBinder.kt +99 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeEventSink.kt +168 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMaps.kt +49 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeMetricsMaps.kt +173 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeModuleSupport.kt +91 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeOptions.kt +127 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcReactNativeRegistry.kt +71 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcVideoInputPreviewManager.kt +109 -0
- package/android/src/main/java/com/tange/ai/tirtc/reactnative/TiRtcVideoOutputViewManager.kt +109 -0
- package/ios/TiRtcReactNative+Conn.mm +112 -0
- package/ios/TiRtcReactNative+Input.mm +196 -0
- package/ios/TiRtcReactNative+Output.mm +172 -0
- package/ios/TiRtcReactNative+Private.h +28 -0
- package/ios/TiRtcReactNative.h +6 -0
- package/ios/TiRtcReactNative.mm +189 -0
- package/ios/TiRtcReactNativeErrors.h +18 -0
- package/ios/TiRtcReactNativeErrors.mm +105 -0
- package/ios/TiRtcReactNativeEventBinder.h +19 -0
- package/ios/TiRtcReactNativeEventBinder.mm +226 -0
- package/ios/TiRtcReactNativeEventSink.h +29 -0
- package/ios/TiRtcReactNativeEventSink.mm +125 -0
- package/ios/TiRtcReactNativeMaps.h +21 -0
- package/ios/TiRtcReactNativeMaps.mm +249 -0
- package/ios/TiRtcReactNativeNative.h +8 -0
- package/ios/TiRtcReactNativeOptions.h +11 -0
- package/ios/TiRtcReactNativeOptions.mm +123 -0
- package/ios/TiRtcReactNativeRegistry.h +20 -0
- package/ios/TiRtcReactNativeRegistry.mm +119 -0
- package/ios/TiRtcVideoInputPreview.h +5 -0
- package/ios/TiRtcVideoInputPreview.mm +149 -0
- package/ios/TiRtcVideoOutputView.h +5 -0
- package/ios/TiRtcVideoOutputView.mm +149 -0
- package/lib/module/audio_input.js +177 -0
- package/lib/module/audio_input.js.map +1 -0
- package/lib/module/audio_output.js +168 -0
- package/lib/module/audio_output.js.map +1 -0
- package/lib/module/conn.js +174 -0
- package/lib/module/conn.js.map +1 -0
- package/lib/module/errors.js +63 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/events.js +37 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/index.js +9 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/logging.js +26 -0
- package/lib/module/logging.js.map +1 -0
- package/lib/module/native.js +5 -0
- package/lib/module/native.js.map +1 -0
- package/lib/module/native_object_id.js +47 -0
- package/lib/module/native_object_id.js.map +1 -0
- package/lib/module/runtime.js +27 -0
- package/lib/module/runtime.js.map +1 -0
- package/lib/module/specs/NativeTiRtc.js +3 -0
- package/lib/module/specs/NativeTiRtc.js.map +1 -0
- package/lib/module/specs/TiRtcVideoInputPreviewNativeComponent.js +24 -0
- package/lib/module/specs/TiRtcVideoOutputViewNativeComponent.js +24 -0
- package/lib/module/types.js +93 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/video_input.js +266 -0
- package/lib/module/video_input.js.map +1 -0
- package/lib/module/video_output.js +224 -0
- package/lib/module/video_output.js.map +1 -0
- package/lib/typescript/audio_input.d.ts +25 -0
- package/lib/typescript/audio_output.d.ts +25 -0
- package/lib/typescript/conn.d.ts +28 -0
- package/lib/typescript/errors.d.ts +19 -0
- package/lib/typescript/events.d.ts +6 -0
- package/lib/typescript/index.d.ts +9 -0
- package/lib/typescript/logging.d.ts +9 -0
- package/lib/typescript/native.d.ts +5 -0
- package/lib/typescript/native_object_id.d.ts +12 -0
- package/lib/typescript/runtime.d.ts +8 -0
- package/lib/typescript/specs/NativeTiRtc.d.ts +223 -0
- package/lib/typescript/specs/TiRtcVideoInputPreviewNativeComponent.d.ts +8 -0
- package/lib/typescript/specs/TiRtcVideoOutputViewNativeComponent.d.ts +8 -0
- package/lib/typescript/types.d.ts +281 -0
- package/lib/typescript/video_input.d.ts +50 -0
- package/lib/typescript/video_output.d.ts +46 -0
- package/package.json +80 -0
- package/react-native.config.js +10 -0
- package/src/audio_input.ts +205 -0
- package/src/audio_output.ts +202 -0
- package/src/conn.ts +222 -0
- package/src/errors.ts +81 -0
- package/src/events.ts +44 -0
- package/src/index.ts +61 -0
- package/src/logging.ts +33 -0
- package/src/native.ts +6 -0
- package/src/native_object_id.ts +63 -0
- package/src/runtime.ts +32 -0
- package/src/specs/NativeTiRtc.ts +256 -0
- package/src/specs/TiRtcVideoInputPreviewNativeComponent.ts +14 -0
- package/src/specs/TiRtcVideoOutputViewNativeComponent.ts +12 -0
- package/src/types.ts +340 -0
- package/src/video_input.ts +314 -0
- 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
|
+
}
|