expo-nodemediaclient 0.1.2 → 0.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 (34) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +203 -19
  3. package/android/build.gradle +4 -5
  4. package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodeMediaClientModule.kt +1 -1
  5. package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodePlayerView.kt +15 -20
  6. package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodePlayerViewModule.kt +58 -0
  7. package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodePublisherView.kt +142 -0
  8. package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodePublisherViewModule.kt +95 -0
  9. package/build/ExpoNodeMediaClientModule.js.map +1 -1
  10. package/build/ExpoNodePlayerView.d.ts +9 -3
  11. package/build/ExpoNodePlayerView.d.ts.map +1 -1
  12. package/build/ExpoNodePlayerView.js +3 -18
  13. package/build/ExpoNodePlayerView.js.map +1 -1
  14. package/build/ExpoNodePublisherView.d.ts +87 -0
  15. package/build/ExpoNodePublisherView.d.ts.map +1 -0
  16. package/build/ExpoNodePublisherView.js +44 -0
  17. package/build/ExpoNodePublisherView.js.map +1 -0
  18. package/build/index.d.ts +2 -1
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js +3 -5
  21. package/build/index.js.map +1 -1
  22. package/expo-module.config.json +4 -2
  23. package/ios/ExpoNodeMediaClientModule.swift +1 -1
  24. package/ios/ExpoNodePlayerView.swift +76 -70
  25. package/ios/{ExpoNodePlayerModule.swift → ExpoNodePlayerViewModule.swift} +8 -6
  26. package/ios/ExpoNodePublisherView.swift +166 -0
  27. package/ios/ExpoNodePublisherViewModule.swift +107 -0
  28. package/ios/ExpoNodemediaclient.podspec +2 -3
  29. package/package.json +1 -1
  30. package/src/ExpoNodeMediaClientModule.tsx +1 -1
  31. package/src/ExpoNodePlayerView.tsx +12 -25
  32. package/src/ExpoNodePublisherView.tsx +95 -0
  33. package/src/index.ts +4 -7
  34. package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodePlayerModule.kt +0 -60
package/CHANGELOG.md CHANGED
@@ -1,2 +1,8 @@
1
+ ## [0.2.0] 2025-12-31
2
+ - SDK更新到4.0
3
+ - 添加推流实现
4
+
5
+ ## [0.1.2] 2025-10-13
6
+
1
7
  ## [0.1.1] 2025-07-25
2
8
  - 实现播放功能
package/README.md CHANGED
@@ -39,6 +39,7 @@ if (Platform.OS === 'ios') {
39
39
  ```
40
40
 
41
41
  ### 播放
42
+
42
43
  ```js
43
44
  import { NodePlayer, NodePlayerRef } from 'expo-nodemediaclient';
44
45
  import { useRef, useState } from 'react';
@@ -71,36 +72,219 @@ export default function App() {
71
72
  </SafeAreaView>
72
73
  );
73
74
  }
75
+ ```
76
+
77
+ ### 组件属性
78
+
79
+ | 属性 | 类型 | 说明 |
80
+ |------|------|------|
81
+ | `url` | `string` | 播放地址 (支持 RTMP、RTSP、HLS、HTTP-FLV) |
82
+ | `bufferTime` | `number` | 缓冲时间 (毫秒),默认 1000 |
83
+ | `scaleMode` | `number` | 缩放模式,`0`=填充,`1`=适应,`2`=拉伸 |
84
+ | `volume` | `number` | 音量 (0.0 - 1.0),默认 1.0 |
85
+ | `onEventCallback` | `(event) => void` | 事件回调 |
86
+
87
+ ### 常用方法
74
88
 
89
+ ```js
90
+ // 开始播放
91
+ playerRef.current?.start(url);
92
+
93
+ // 停止播放
94
+ playerRef.current?.stop();
75
95
  ```
76
96
 
77
- ### 进入页面自动播放
97
+ ### 事件回调
98
+
78
99
  ```js
79
- export default function App() {
80
- const [url, setUrl] = useState('rtmp://192.168.0.2/live/bbb');
81
- const [bufferTime, setBufferTime] = useState(0);
82
- const [scaleMode, setScaleMode] = useState(2);
83
- const playerRef = useRef<NodePlayerRef>(null);
84
-
85
- useEffect(() => {
86
- playerRef.current?.start(url);
87
- return () => {
88
- playerRef.current?.stop();
89
- };
90
- }, []);
100
+ const handleEvent = (event: { nativeEvent: NodePlayerEventCallback }) => {
101
+ console.log('事件码:', event.nativeEvent.event);
102
+ console.log('消息:', event.nativeEvent.msg);
103
+ };
104
+
105
+ <NodePlayer
106
+ onEventCallback={handleEvent}
107
+ // ...其他属性
108
+ />
109
+ ```
110
+
111
+
112
+ ## 推流
113
+
114
+ ### 基本用法
115
+
116
+ ```js
117
+ import { NodePublisher, NodePublisherRef } from 'expo-nodemediaclient';
118
+ import { useRef, useState } from 'react';
119
+
120
+ export default function PublisherScreen() {
121
+ const [url, setUrl] = useState('rtmp://192.168.0.2/live/stream');
122
+ const [isPublishing, setIsPublishing] = useState(false);
123
+ const publisherRef = useRef<NodePublisherRef>(null);
124
+
125
+ const handleTogglePublish = () => {
126
+ if (!isPublishing) {
127
+ setIsPublishing(true);
128
+ publisherRef.current?.start(url);
129
+ } else {
130
+ setIsPublishing(false);
131
+ publisherRef.current?.stop();
132
+ }
133
+ };
91
134
 
92
135
  return (
93
136
  <SafeAreaView style={{ flex: 1 }}>
94
- <NodePlayer
95
- ref={playerRef}
137
+ <NodePublisher
138
+ ref={publisherRef}
139
+ style={{ flex: 1, backgroundColor: '#000' }}
96
140
  url={url}
97
- bufferTime={bufferTime}
98
- scaleMode={scaleMode}
99
- style={{ backgroundColor: '#000000', height: 300 }}
141
+ audioParam={{
142
+ codecid: NodePublisher.NMC_CODEC_ID_AAC,
143
+ profile: NodePublisher.NMC_PROFILE_AUTO,
144
+ channels: 2,
145
+ samplingRate: 44100,
146
+ bitrate: 64_000,
147
+ }}
148
+ videoParam={{
149
+ codecid: NodePublisher.NMC_CODEC_ID_H264,
150
+ profile: NodePublisher.NMC_PROFILE_AUTO,
151
+ width: 720,
152
+ height: 1280,
153
+ fps: 30,
154
+ bitrate: 2000_000,
155
+ }}
156
+ />
157
+ <Button
158
+ title={isPublishing ? "停止" : "推流"}
159
+ onPress={handleTogglePublish}
100
160
  />
101
161
  </SafeAreaView>
102
162
  );
103
163
  }
104
164
  ```
105
165
 
106
- ## 推流
166
+ ### 组件属性
167
+
168
+ | 属性 | 类型 | 说明 |
169
+ |------|------|------|
170
+ | `url` | `string` | 推流地址 (RTMP) |
171
+ | `audioParam` | `AudioParam` | 音频编码参数 |
172
+ | `videoParam` | `VideoParam` | 视频编码参数 |
173
+ | `videoOrientation` | `number` | 视频方向,`NodePublisher.VIDEO_ORIENTATION_PORTRAIT` 或 `NodePublisher.VIDEO_ORIENTATION_LANDSCAPE` |
174
+ | `keyFrameInterval` | `number` | 关键帧间隔 (秒),默认 2 |
175
+ | `frontCamera` | `boolean` | 是否使用前置摄像头,默认 false |
176
+ | `cameraFrontMirror` | `boolean` | 前置摄像头是否镜像,默认 true |
177
+ | `HWAccelEnable` | `boolean` | 是否启用硬件加速,默认 true |
178
+ | `denoiseEnable` | `boolean` | 是否启用降噪,默认 true |
179
+ | `colorStyleId` | `number` | 色彩风格ID,如 `NodePublisher.EFFECTOR_STYLE_ID_FAIRSKIN` |
180
+ | `colorStyleIntensity` | `number` | 色彩强度 (0.0 - 1.0) |
181
+ | `smoothskinIntensity` | `number` | 磨皮强度 (0.0 - 1.0) |
182
+ | `onEventCallback` | `(event) => void` | 事件回调 |
183
+
184
+ ### AudioParam
185
+
186
+ | 属性 | 类型 | 说明 |
187
+ |------|------|------|
188
+ | `codecid` | `number` | 编码ID,`NodePublisher.NMC_CODEC_ID_AAC` |
189
+ | `profile` | `number` | 编码profile,`NodePublisher.NMC_PROFILE_AUTO` |
190
+ | `channels` | `number` | 声道数,1 或 2 |
191
+ | `samplingRate` | `number` | 采样率,如 44100 |
192
+ | `bitrate` | `number` | 比特率,如 64000 |
193
+
194
+ ### VideoParam
195
+
196
+ | 属性 | 类型 | 说明 |
197
+ |------|------|------|
198
+ | `codecid` | `number` | 编码ID,`NodePublisher.NMC_CODEC_ID_H264` 或 `NodePublisher.NMC_CODEC_ID_H265` |
199
+ | `profile` | `number` | 编码profile,`NodePublisher.NMC_PROFILE_AUTO` |
200
+ | `width` | `number` | 视频宽度 |
201
+ | `height` | `number` | 视频高度 |
202
+ | `fps` | `number` | 帧率 |
203
+ | `bitrate` | `number` | 比特率,如 2000000 |
204
+
205
+ ### 常用方法
206
+
207
+ ```js
208
+ // 开始推流
209
+ publisherRef.current?.start(url);
210
+
211
+ // 停止推流
212
+ publisherRef.current?.stop();
213
+ ```
214
+
215
+ ### 事件回调
216
+
217
+ ```js
218
+ const handleEvent = (event: { nativeEvent: NodePlayerEventCallback }) => {
219
+ console.log('事件码:', event.nativeEvent.event);
220
+ console.log('消息:', event.nativeEvent.msg);
221
+ };
222
+
223
+ <NodePublisher
224
+ onEventCallback={handleEvent}
225
+ // ...其他属性
226
+ />
227
+ ```
228
+
229
+ ### 权限说明
230
+
231
+ 使用推流功能需要摄像头和麦克风权限,建议使用 `expo-camera` 来申请权限。
232
+
233
+ 首先安装依赖:
234
+
235
+ ```bash
236
+ npm install expo-camera
237
+ ```
238
+
239
+ 在 `app.json` 中配置 `expo-camera` 插件:
240
+
241
+ ```json
242
+ {
243
+ "expo": {
244
+ "plugins": [
245
+ [
246
+ "expo-camera",
247
+ {
248
+ "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
249
+ "microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone",
250
+ "recordAudioAndroid": true
251
+ }
252
+ ]
253
+ ],
254
+ "ios": {
255
+ "infoPlist": {
256
+ "NSCameraUsageDescription": "需要访问摄像头以进行直播推流",
257
+ "NSMicrophoneUsageDescription": "需要访问麦克风以进行直播推流"
258
+ }
259
+ },
260
+ "android": {
261
+ "permissions": [
262
+ "android.permission.CAMERA",
263
+ "android.permission.RECORD_AUDIO",
264
+ "android.permission.INTERNET"
265
+ ]
266
+ }
267
+ }
268
+ }
269
+ ```
270
+
271
+ 在代码中申请权限:
272
+
273
+ ```js
274
+ import { useCameraPermissions, useMicrophonePermissions } from 'expo-camera';
275
+ import { useEffect } from 'react';
276
+
277
+ export default function App() {
278
+ const [cameraPermission, requestCameraPermission] = useCameraPermissions();
279
+ const [microphonePermission, requestMicrophonePermission] = useMicrophonePermissions();
280
+
281
+ useEffect(() => {
282
+ requestCameraPermission();
283
+ requestMicrophonePermission();
284
+ }, []);
285
+
286
+ const hasPermission = cameraPermission?.granted && microphonePermission?.granted;
287
+
288
+ // ...
289
+ }
290
+ ```
@@ -1,7 +1,6 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'expo.modules.nodemediaclient'
4
- version = '0.1.1'
5
4
 
6
5
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
6
  apply from: expoModulesCorePlugin
@@ -23,10 +22,10 @@ if (useManagedAndroidSdkVersions) {
23
22
  }
24
23
  }
25
24
  project.android {
26
- compileSdkVersion safeExtGet("compileSdkVersion", 34)
25
+ compileSdkVersion safeExtGet("compileSdkVersion", 36)
27
26
  defaultConfig {
28
- minSdkVersion safeExtGet("minSdkVersion", 21)
29
- targetSdkVersion safeExtGet("targetSdkVersion", 34)
27
+ minSdkVersion safeExtGet("minSdkVersion", 24)
28
+ targetSdkVersion safeExtGet("targetSdkVersion", 36)
30
29
  }
31
30
  }
32
31
  }
@@ -41,6 +40,6 @@ android {
41
40
  abortOnError false
42
41
  }
43
42
  dependencies {
44
- implementation 'com.github.NodeMedia:NodeMediaClient-Android:3.2.12'
43
+ implementation 'com.github.NodeMedia:NodeMediaClient-Android:4.0.11'
45
44
  }
46
45
  }
@@ -20,4 +20,4 @@ class ExpoNodeMediaClientModule : Module() {
20
20
  LICENSE_KEY = license
21
21
  }
22
22
  }
23
- }
23
+ }
@@ -1,30 +1,22 @@
1
- //
2
- // Copyright (c) 2025 NodeMedia Technology Co., Ltd.
3
- // Created by Chen Mingliang on 2025-07-22.
4
- // All rights reserved.
5
- //
6
-
7
1
  package expo.modules.nodemediaclient
8
2
 
9
3
  import android.content.Context
10
- import android.util.Log
11
- import android.webkit.WebView
12
- import android.webkit.WebViewClient
13
- import android.widget.FrameLayout
14
4
  import expo.modules.kotlin.AppContext
15
5
  import expo.modules.kotlin.viewevent.EventDispatcher
16
6
  import expo.modules.kotlin.views.ExpoView
17
- import cn.nodemedia.NodePlayer;
7
+
8
+ import android.util.Log
9
+ import android.widget.FrameLayout
10
+ import cn.nodemedia.NodePlayer
18
11
 
19
12
  class ExpoNodePlayerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
20
- // Creates and initializes an event dispatcher for the `onLoad` event.
21
- // The name of the event is inferred from the value and needs to match the event name defined in the module.
22
- private val onLoad by EventDispatcher()
23
13
  private val TAG = "ExpoNodePlayerView"
24
14
  private var np: NodePlayer? = null
15
+ private val onEventCallback by EventDispatcher()
16
+
25
17
  var url = ""
26
18
  var cryptoKey = "" // Add this property
27
- set(value) {
19
+ set(value) {
28
20
  field = value
29
21
  np?.setCryptoKey(value)
30
22
  }
@@ -60,7 +52,7 @@ class ExpoNodePlayerView(context: Context, appContext: AppContext) : ExpoView(co
60
52
  np?.setRTSPTransport(value)
61
53
  }
62
54
 
63
- var HWAccelEnable= true
55
+ var HWAccelEnable = true
64
56
  set(value) {
65
57
  field = value
66
58
  np?.setHWAccelEnable(value)
@@ -76,6 +68,9 @@ class ExpoNodePlayerView(context: Context, appContext: AppContext) : ExpoView(co
76
68
  override fun onAttachedToWindow() {
77
69
  super.onAttachedToWindow()
78
70
  np = NodePlayer(context, LICENSE_KEY)
71
+ np?.setOnNodePlayerEventListener { obj, event, msg ->
72
+ onEventCallback(mapOf("event" to event, "msg" to msg))
73
+ }
79
74
  np?.attachView(videoView)
80
75
  }
81
76
 
@@ -86,8 +81,8 @@ class ExpoNodePlayerView(context: Context, appContext: AppContext) : ExpoView(co
86
81
  super.onDetachedFromWindow()
87
82
  }
88
83
 
89
- fun start(url: String?){
90
- if(!url.isNullOrEmpty()) {
84
+ fun start(url: String?) {
85
+ if (!url.isNullOrEmpty()) {
91
86
  this.url = url
92
87
  }
93
88
  np?.setVolume(this.volume)
@@ -101,7 +96,7 @@ class ExpoNodePlayerView(context: Context, appContext: AppContext) : ExpoView(co
101
96
  np?.start(this.url)
102
97
  }
103
98
 
104
- fun stop(){
99
+ fun stop() {
105
100
  np?.stop()
106
101
  }
107
- }
102
+ }
@@ -0,0 +1,58 @@
1
+ package expo.modules.nodemediaclient
2
+
3
+ import expo.modules.kotlin.modules.Module
4
+ import expo.modules.kotlin.modules.ModuleDefinition
5
+
6
+ class ExpoNodePlayerViewModule : Module() {
7
+ override fun definition() = ModuleDefinition {
8
+ Name("ExpoNodePlayerView")
9
+
10
+ View(ExpoNodePlayerView::class) {
11
+ Events("onEventCallback")
12
+
13
+ Prop("url") { view: ExpoNodePlayerView, url: String ->
14
+ view.url = url
15
+ }
16
+
17
+ Prop("volume") { view: ExpoNodePlayerView, volume: Float ->
18
+ view.volume = volume
19
+ }
20
+
21
+ Prop("cryptoKey") { view: ExpoNodePlayerView, cryptoKey: String ->
22
+ view.cryptoKey = cryptoKey
23
+ }
24
+
25
+ Prop("scaleMode") { view: ExpoNodePlayerView, scaleMode: Int ->
26
+ view.scaleMode = scaleMode
27
+ }
28
+
29
+ Prop("bufferTime") { view: ExpoNodePlayerView, bufferTime: Int ->
30
+ view.bufferTime = bufferTime
31
+ }
32
+
33
+ Prop("HTTPReferer") { view: ExpoNodePlayerView, HTTPReferer: String ->
34
+ view.HTTPReferer = HTTPReferer
35
+ }
36
+
37
+ Prop("HTTPUserAgent") { view: ExpoNodePlayerView, HTTPUserAgent: String ->
38
+ view.HTTPUserAgent = HTTPUserAgent
39
+ }
40
+
41
+ Prop("RTSPTransport") { view: ExpoNodePlayerView, RTSPTransport: String ->
42
+ view.RTSPTransport = RTSPTransport
43
+ }
44
+
45
+ Prop("HWAccelEnable") { view: ExpoNodePlayerView, HWAccelEnable: Boolean ->
46
+ view.HWAccelEnable = HWAccelEnable
47
+ }
48
+
49
+ AsyncFunction("start") { view: ExpoNodePlayerView, url: String? ->
50
+ view.start(url)
51
+ }
52
+
53
+ AsyncFunction("stop") { view: ExpoNodePlayerView ->
54
+ view.stop()
55
+ }
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,142 @@
1
+ package expo.modules.nodemediaclient
2
+
3
+ import android.content.Context
4
+ import expo.modules.kotlin.AppContext
5
+ import expo.modules.kotlin.views.ExpoView
6
+
7
+ import android.widget.FrameLayout
8
+ import cn.nodemedia.NodePublisher
9
+ import expo.modules.kotlin.viewevent.EventDispatcher
10
+
11
+ class ExpoNodePublisherView(context: Context, appContext: AppContext) :
12
+ ExpoView(context, appContext) {
13
+ private val TAG = "ExpoNodePublisherView"
14
+ private var np: NodePublisher? = null
15
+ private val onEventCallback by EventDispatcher()
16
+
17
+ var url = ""
18
+ var cryptoKey = ""
19
+ var HWAccelEnable = true
20
+ var denoiseEnable = true
21
+
22
+ // Color and effect parameters
23
+ var colorStyleId: Int? = null
24
+ set(value) {
25
+ field = value
26
+ value?.let { np?.setEffectStyle(it) }
27
+ }
28
+
29
+ var colorStyleIntensity: Float? = null
30
+ set(value) {
31
+ field = value
32
+ value?.let { np?.setEffectParameter("style", it) }
33
+ }
34
+
35
+ var smoothskinIntensity: Float? = null
36
+ set(value) {
37
+ field = value
38
+ value?.let { np?.setEffectParameter("smoothskin", it) }
39
+ }
40
+
41
+ // Audio parameters
42
+ var audioCodecId = NodePublisher.NMC_CODEC_ID_AAC
43
+ var audioProfile = NodePublisher.NMC_PROFILE_AUTO
44
+ var audioChannels = 2
45
+ var audioSamplingRate = 44100
46
+ var audioBitrate = 64000
47
+
48
+ // Video parameters
49
+ var videoCodecId = NodePublisher.NMC_CODEC_ID_H264
50
+ var videoProfile = NodePublisher.NMC_PROFILE_AUTO
51
+ var videoWidth = 720
52
+ var videoHeight = 1280
53
+ var videoFps = 30
54
+ var videoBitrate = 2000000
55
+
56
+ var frontCamera: Boolean? = null
57
+ set(value) {
58
+ field = value
59
+ value?.let {
60
+ np?.closeCamera()
61
+ np?.openCamera(if (it) 0 else 1)
62
+ }
63
+ }
64
+
65
+ // Other parameters
66
+ var keyFrameInterval = 2
67
+ var videoOrientation = 1
68
+
69
+ var cameraFrontMirror: Boolean? = null
70
+ set(value) {
71
+ field = value
72
+ value?.let { np?.setCameraFrontMirror(it) }
73
+ }
74
+
75
+ private val videoView = FrameLayout(context).apply {
76
+ layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
77
+ }
78
+
79
+ init {
80
+ addView(videoView)
81
+ }
82
+
83
+ override fun onAttachedToWindow() {
84
+ super.onAttachedToWindow()
85
+ np = NodePublisher(context, LICENSE_KEY)
86
+ np?.setOnNodePublisherEventListener { obj, event, msg ->
87
+ onEventCallback(mapOf("event" to event, "msg" to msg))
88
+ }
89
+ applyAudioParams()
90
+ applyVideoParams()
91
+ np?.setCryptoKey(this.cryptoKey)
92
+ np?.setHWAccelEnable(this.HWAccelEnable)
93
+ np?.openCamera(if (frontCamera == true) 0 else 1)
94
+ np?.attachView(videoView)
95
+
96
+ // Apply color and effect params if set
97
+ colorStyleId?.let { np?.setEffectStyle(it) }
98
+ colorStyleIntensity?.let { np?.setEffectParameter("style", it) }
99
+ smoothskinIntensity?.let { np?.setEffectParameter("smoothskin", it) }
100
+ }
101
+
102
+ override fun onDetachedFromWindow() {
103
+ np?.stop()
104
+ np?.closeCamera()
105
+ np?.detachView()
106
+ np = null
107
+ super.onDetachedFromWindow()
108
+ }
109
+
110
+ private fun applyAudioParams() {
111
+ np?.setAudioCodecParam(
112
+ audioCodecId, audioProfile,
113
+ audioSamplingRate, audioChannels, audioBitrate
114
+ )
115
+ }
116
+
117
+ private fun applyVideoParams() {
118
+ np?.setVideoCodecParam(
119
+ videoCodecId,
120
+ videoProfile, videoWidth, videoHeight, videoFps, videoBitrate
121
+ )
122
+ }
123
+
124
+ fun start(url: String?) {
125
+ if (!url.isNullOrEmpty()) {
126
+ this.url = url
127
+ }
128
+ np?.start(this.url)
129
+ }
130
+
131
+ fun stop() {
132
+ np?.stop()
133
+ }
134
+
135
+ fun setEffectParameter(key: String, value: Float) {
136
+ np?.setEffectParameter(key, value)
137
+ }
138
+
139
+ fun setEffectStyle(style: Int) {
140
+ np?.setEffectStyle(style)
141
+ }
142
+ }
@@ -0,0 +1,95 @@
1
+ package expo.modules.nodemediaclient
2
+
3
+ import expo.modules.kotlin.modules.Module
4
+ import expo.modules.kotlin.modules.ModuleDefinition
5
+
6
+ class ExpoNodePublisherViewModule : Module() {
7
+ override fun definition() = ModuleDefinition {
8
+ Name("ExpoNodePublisherView")
9
+
10
+ View(ExpoNodePublisherView::class) {
11
+ Events("onEventCallback")
12
+
13
+ Prop("url") { view: ExpoNodePublisherView, url: String ->
14
+ view.url = url
15
+ }
16
+
17
+ Prop("cryptoKey") { view: ExpoNodePublisherView, cryptoKey: String ->
18
+ view.cryptoKey = cryptoKey
19
+ }
20
+
21
+ Prop("HWAccelEnable") { view: ExpoNodePublisherView, HWAccelEnable: Boolean ->
22
+ view.HWAccelEnable = HWAccelEnable
23
+ }
24
+
25
+ // Audio parameters
26
+ Prop("audioParam") { view: ExpoNodePublisherView, audioParam: Map<String, Int> ->
27
+ view.audioCodecId = audioParam["codecid"] as Int
28
+ view.audioProfile = audioParam["profile"] as Int
29
+ view.audioChannels = audioParam["channels"] as Int
30
+ view.audioSamplingRate = audioParam["samplingRate"] as Int
31
+ view.audioBitrate = audioParam["bitrate"] as Int
32
+ }
33
+
34
+ // Video parameters
35
+ Prop("videoParam") { view: ExpoNodePublisherView, videoParam: Map<String, Int> ->
36
+ view.videoCodecId = videoParam["codecid"] as Int
37
+ view.videoProfile = videoParam["profile"] as Int
38
+ view.videoWidth = videoParam["width"] as Int
39
+ view.videoHeight = videoParam["height"] as Int
40
+ view.videoFps = videoParam["fps"] as Int
41
+ view.videoBitrate = videoParam["bitrate"] as Int
42
+ }
43
+
44
+ Prop("keyFrameInterval") { view: ExpoNodePublisherView, keyFrameInterval: Int ->
45
+ view.keyFrameInterval = keyFrameInterval
46
+ }
47
+
48
+ Prop("videoOrientation") { view: ExpoNodePublisherView, videoOrientation: Int ->
49
+ view.videoOrientation = videoOrientation
50
+ }
51
+
52
+ Prop("frontCamera") { view: ExpoNodePublisherView, frontCamera: Boolean ->
53
+ view.frontCamera = frontCamera
54
+ }
55
+
56
+ Prop("cameraFrontMirror") { view: ExpoNodePublisherView, cameraFrontMirror: Boolean ->
57
+ view.cameraFrontMirror = cameraFrontMirror
58
+ }
59
+
60
+ Prop("denoiseEnable") { view: ExpoNodePublisherView, denoiseEnable: Boolean ->
61
+ view.denoiseEnable = denoiseEnable
62
+ }
63
+
64
+ // Color and effect parameters
65
+ Prop("colorStyleId") { view: ExpoNodePublisherView, colorStyleId: Int ->
66
+ view.colorStyleId = colorStyleId
67
+ }
68
+
69
+ Prop("colorStyleIntensity") { view: ExpoNodePublisherView, colorStyleIntensity: Double ->
70
+ view.colorStyleIntensity = colorStyleIntensity.toFloat()
71
+ }
72
+
73
+ Prop("smoothskinIntensity") { view: ExpoNodePublisherView, smoothskinIntensity: Double ->
74
+ view.smoothskinIntensity = smoothskinIntensity.toFloat()
75
+ }
76
+
77
+ // Methods
78
+ AsyncFunction("start") { view: ExpoNodePublisherView, url: String? ->
79
+ view.start(url)
80
+ }
81
+
82
+ AsyncFunction("stop") { view: ExpoNodePublisherView ->
83
+ view.stop()
84
+ }
85
+
86
+ AsyncFunction("setEffectParameter") { view: ExpoNodePublisherView, key: String, value: Float ->
87
+ view.setEffectParameter(key, value)
88
+ }
89
+
90
+ AsyncFunction("setEffectStyle") { view: ExpoNodePublisherView, style: Int ->
91
+ view.setEffectStyle(style)
92
+ }
93
+ }
94
+ }
95
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoNodeMediaClientModule.js","sourceRoot":"","sources":["../src/ExpoNodeMediaClientModule.tsx"],"names":[],"mappings":"AAAA,EAAE;AACF,qDAAqD;AACrD,4CAA4C;AAC5C,wBAAwB;AACxB,EAAE;AAGF,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAMzD,yDAAyD;AACzD,eAAe,mBAAmB,CAA4B,2BAA2B,CAAC,CAAC","sourcesContent":["//\n// Copyright (c) 2025 NodeMedia Technology Co., Ltd.\n// Created by Chen Mingliang on 2025-07-22.\n// All rights reserved.\n//\n\n\nimport { NativeModule, requireNativeModule } from 'expo';\n\ndeclare class ExpoNodeMediaClientModule extends NativeModule{\n setLicense(license: string): void;\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<ExpoNodeMediaClientModule>('ExpoNodeMediaClientModule');\n"]}
1
+ {"version":3,"file":"ExpoNodeMediaClientModule.js","sourceRoot":"","sources":["../src/ExpoNodeMediaClientModule.tsx"],"names":[],"mappings":"AAAA,EAAE;AACF,qDAAqD;AACrD,4CAA4C;AAC5C,wBAAwB;AACxB,EAAE;AAGF,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAMzD,yDAAyD;AACzD,eAAe,mBAAmB,CAA4B,2BAA2B,CAAC,CAAC","sourcesContent":["//\n// Copyright (c) 2025 NodeMedia Technology Co., Ltd.\n// Created by Chen Mingliang on 2025-07-22.\n// All rights reserved.\n//\n\n\nimport { NativeModule, requireNativeModule } from 'expo';\n\ndeclare class ExpoNodeMediaClientModule extends NativeModule{\n setLicense(license: string): void;\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<ExpoNodeMediaClientModule>('ExpoNodeMediaClientModule');"]}