expo-nodemediaclient 0.2.0 → 0.2.1
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/CHANGELOG.md +4 -0
- package/CLAUDE.md +55 -0
- package/android/build.gradle +3 -3
- package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodePublisherView.kt +37 -4
- package/android/src/main/java/expo/modules/nodemediaclient/ExpoNodePublisherViewModule.kt +20 -4
- package/build/ExpoNodePublisherView.d.ts +6 -2
- package/build/ExpoNodePublisherView.d.ts.map +1 -1
- package/build/ExpoNodePublisherView.js.map +1 -1
- package/ios/ExpoNodePublisherView.swift +19 -11
- package/ios/ExpoNodePublisherViewModule.swift +102 -95
- package/ios/ExpoNodemediaclient.podspec +1 -1
- package/package.json +1 -1
- package/src/ExpoNodePublisherView.tsx +6 -2
package/CHANGELOG.md
CHANGED
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is **expo-nodemediaclient**, an Expo native module providing live streaming capabilities using the NodeMedia SDK 4.0. It enables RTMP/RTSP/HLS/HTTP-FLV video playback and RTMP live streaming (publishing) for React Native applications using Expo's Continuous Native Generation (CNG) workflow.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
The module follows Expo's custom native module pattern with three main components:
|
|
12
|
+
|
|
13
|
+
- **NodeMediaClient** (`ExpoNodeMediaClientModule.tsx`) - SDK license management, requires separate license keys for iOS and Android
|
|
14
|
+
- **NodePlayer** (`ExpoNodePlayerView.tsx`) - Video playback component using `requireNativeViewManager`
|
|
15
|
+
- **NodePublisher** (`ExpoNodePublisherView.tsx`) - Camera capture and streaming component using `requireNativeViewManager`
|
|
16
|
+
|
|
17
|
+
The native implementations are in:
|
|
18
|
+
- `ios/ExpoNodemediaclient/` - iOS native code (Objective-C/Swift)
|
|
19
|
+
- `android/src/main/java/` - Android native code (Java)
|
|
20
|
+
|
|
21
|
+
Module configuration is in `expo-module.config.json` which maps platform-specific module names.
|
|
22
|
+
|
|
23
|
+
## Development Commands
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm run build # Build the module (TypeScript -> build/)
|
|
27
|
+
npm run clean # Clean build artifacts
|
|
28
|
+
npm run lint # Run linting
|
|
29
|
+
npm run test # Run tests
|
|
30
|
+
npm run prepare # Prepare module for development
|
|
31
|
+
npm run prepublishOnly # Prepare for publishing
|
|
32
|
+
npm run open:ios # Open example iOS project in Xcode
|
|
33
|
+
npm run open:android # Open example Android project in Android Studio
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The example app in `example/` demonstrates both Player and Publisher functionality with navigation between screens.
|
|
37
|
+
|
|
38
|
+
## Key Constants
|
|
39
|
+
|
|
40
|
+
NodePublisher exposes these important constants:
|
|
41
|
+
- `NMC_CODEC_ID_AAC`, `NMC_CODEC_ID_H264`, `NMC_CODEC_ID_H265` - Codec identifiers
|
|
42
|
+
- `NMC_PROFILE_AUTO` - Auto profile selection
|
|
43
|
+
- `VIDEO_ORIENTATION_PORTRAIT`, `VIDEO_ORIENTATION_LANDSCAPE` - Video orientation
|
|
44
|
+
- `EFFECTOR_STYLE_ID_FAIRSKIN` - Beauty filter style
|
|
45
|
+
|
|
46
|
+
## Event Callbacks
|
|
47
|
+
|
|
48
|
+
Both NodePlayer and NodePublisher support `onEventCallback` that receives `{ nativeEvent: { event: number, msg: string } }`. The `event` code indicates status changes (connection, buffering, error, etc.).
|
|
49
|
+
|
|
50
|
+
## Important Notes
|
|
51
|
+
|
|
52
|
+
- **License Required**: NodeMedia SDK requires separate license keys for iOS and Android from https://www.nodemedia.cn
|
|
53
|
+
- **CNG Required**: Projects must use Expo's Continuous Native Generation - run `npx expo prebuild` if using managed workflow
|
|
54
|
+
- **Permissions**: Publisher requires camera and microphone permissions; use `expo-camera` plugin for permission handling
|
|
55
|
+
- **SDK Version**: Currently on NodeMedia SDK 4.0 (updated in v0.2.0)
|
package/android/build.gradle
CHANGED
|
@@ -33,13 +33,13 @@ if (useManagedAndroidSdkVersions) {
|
|
|
33
33
|
android {
|
|
34
34
|
namespace "expo.modules.nodemediaclient"
|
|
35
35
|
defaultConfig {
|
|
36
|
-
versionCode
|
|
37
|
-
versionName "0.
|
|
36
|
+
versionCode 201
|
|
37
|
+
versionName "0.2.1"
|
|
38
38
|
}
|
|
39
39
|
lintOptions {
|
|
40
40
|
abortOnError false
|
|
41
41
|
}
|
|
42
42
|
dependencies {
|
|
43
|
-
implementation 'com.github.NodeMedia:NodeMediaClient-Android:4.0
|
|
43
|
+
implementation 'com.github.NodeMedia:NodeMediaClient-Android:4.1.0'
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -19,6 +19,24 @@ class ExpoNodePublisherView(context: Context, appContext: AppContext) :
|
|
|
19
19
|
var HWAccelEnable = true
|
|
20
20
|
var denoiseEnable = true
|
|
21
21
|
|
|
22
|
+
var zoomRatio: Float? = null
|
|
23
|
+
set(value) {
|
|
24
|
+
field = value
|
|
25
|
+
value?.let { np?.setZoomRatio(it) }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var volume: Float? = null
|
|
29
|
+
set(value) {
|
|
30
|
+
field = value
|
|
31
|
+
value?.let { np?.setVolume(it) }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var torchEnable: Boolean? = null
|
|
35
|
+
set(value) {
|
|
36
|
+
field = value
|
|
37
|
+
value?.let { np?.setTorchEnable(it) }
|
|
38
|
+
}
|
|
39
|
+
|
|
22
40
|
// Color and effect parameters
|
|
23
41
|
var colorStyleId: Int? = null
|
|
24
42
|
set(value) {
|
|
@@ -58,7 +76,7 @@ class ExpoNodePublisherView(context: Context, appContext: AppContext) :
|
|
|
58
76
|
field = value
|
|
59
77
|
value?.let {
|
|
60
78
|
np?.closeCamera()
|
|
61
|
-
np?.openCamera(if (it)
|
|
79
|
+
np?.openCamera(if (it) NodePublisher.NMC_CAMERA_FRONT else NodePublisher.NMC_CAMERA_BACK)
|
|
62
80
|
}
|
|
63
81
|
}
|
|
64
82
|
|
|
@@ -86,17 +104,28 @@ class ExpoNodePublisherView(context: Context, appContext: AppContext) :
|
|
|
86
104
|
np?.setOnNodePublisherEventListener { obj, event, msg ->
|
|
87
105
|
onEventCallback(mapOf("event" to event, "msg" to msg))
|
|
88
106
|
}
|
|
107
|
+
// Apply audio and video params
|
|
89
108
|
applyAudioParams()
|
|
90
109
|
applyVideoParams()
|
|
110
|
+
|
|
111
|
+
// Apply crypto key and HWAccelEnable
|
|
91
112
|
np?.setCryptoKey(this.cryptoKey)
|
|
92
113
|
np?.setHWAccelEnable(this.HWAccelEnable)
|
|
93
|
-
np?.
|
|
94
|
-
|
|
95
|
-
|
|
114
|
+
np?.setDenoiseEnable(this.denoiseEnable)
|
|
115
|
+
|
|
96
116
|
// Apply color and effect params if set
|
|
97
117
|
colorStyleId?.let { np?.setEffectStyle(it) }
|
|
98
118
|
colorStyleIntensity?.let { np?.setEffectParameter("style", it) }
|
|
99
119
|
smoothskinIntensity?.let { np?.setEffectParameter("smoothskin", it) }
|
|
120
|
+
|
|
121
|
+
// Apply volume if set
|
|
122
|
+
volume?.let { np?.setVolume(it) }
|
|
123
|
+
|
|
124
|
+
// opencamera and attachview
|
|
125
|
+
np?.openCamera(if (frontCamera == true) 0 else 1)
|
|
126
|
+
np?.attachView(videoView)
|
|
127
|
+
|
|
128
|
+
np?.setZoomRatio(zoomRatio ?: 1f)
|
|
100
129
|
}
|
|
101
130
|
|
|
102
131
|
override fun onDetachedFromWindow() {
|
|
@@ -139,4 +168,8 @@ class ExpoNodePublisherView(context: Context, appContext: AppContext) :
|
|
|
139
168
|
fun setEffectStyle(style: Int) {
|
|
140
169
|
np?.setEffectStyle(style)
|
|
141
170
|
}
|
|
171
|
+
|
|
172
|
+
fun startFocusAndMeteringCenter() {
|
|
173
|
+
np?.startFocusAndMeteringCenter()
|
|
174
|
+
}
|
|
142
175
|
}
|
|
@@ -61,17 +61,29 @@ class ExpoNodePublisherViewModule : Module() {
|
|
|
61
61
|
view.denoiseEnable = denoiseEnable
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
Prop("zoomRatio") { view: ExpoNodePublisherView, zoomRatio: Float ->
|
|
65
|
+
view.zoomRatio = zoomRatio
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Prop("volume") { view: ExpoNodePublisherView, volume: Float ->
|
|
69
|
+
view.volume = volume
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Prop("torchEnable") { view: ExpoNodePublisherView, torchEnable: Boolean ->
|
|
73
|
+
view.torchEnable = torchEnable
|
|
74
|
+
}
|
|
75
|
+
|
|
64
76
|
// Color and effect parameters
|
|
65
77
|
Prop("colorStyleId") { view: ExpoNodePublisherView, colorStyleId: Int ->
|
|
66
78
|
view.colorStyleId = colorStyleId
|
|
67
79
|
}
|
|
68
80
|
|
|
69
|
-
Prop("colorStyleIntensity") { view: ExpoNodePublisherView, colorStyleIntensity:
|
|
70
|
-
view.colorStyleIntensity = colorStyleIntensity
|
|
81
|
+
Prop("colorStyleIntensity") { view: ExpoNodePublisherView, colorStyleIntensity: Float ->
|
|
82
|
+
view.colorStyleIntensity = colorStyleIntensity
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
Prop("smoothskinIntensity") { view: ExpoNodePublisherView, smoothskinIntensity:
|
|
74
|
-
view.smoothskinIntensity = smoothskinIntensity
|
|
85
|
+
Prop("smoothskinIntensity") { view: ExpoNodePublisherView, smoothskinIntensity: Float ->
|
|
86
|
+
view.smoothskinIntensity = smoothskinIntensity
|
|
75
87
|
}
|
|
76
88
|
|
|
77
89
|
// Methods
|
|
@@ -90,6 +102,10 @@ class ExpoNodePublisherViewModule : Module() {
|
|
|
90
102
|
AsyncFunction("setEffectStyle") { view: ExpoNodePublisherView, style: Int ->
|
|
91
103
|
view.setEffectStyle(style)
|
|
92
104
|
}
|
|
105
|
+
|
|
106
|
+
AsyncFunction("startFocusAndMeteringCenter") { view: ExpoNodePublisherView ->
|
|
107
|
+
view.startFocusAndMeteringCenter()
|
|
108
|
+
}
|
|
93
109
|
}
|
|
94
110
|
}
|
|
95
111
|
}
|
|
@@ -2,8 +2,9 @@ import { ViewProps } from 'react-native';
|
|
|
2
2
|
export type NodePublisherRef = {
|
|
3
3
|
start: (url?: string) => Promise<void>;
|
|
4
4
|
stop: () => Promise<void>;
|
|
5
|
-
setEffectParameter: (key: string, value: number) => void
|
|
6
|
-
setEffectStyle: (style: number) => void
|
|
5
|
+
setEffectParameter: (key: string, value: number) => Promise<void>;
|
|
6
|
+
setEffectStyle: (style: number) => Promise<void>;
|
|
7
|
+
startFocusAndMeteringCenter: () => Promise<void>;
|
|
7
8
|
};
|
|
8
9
|
export type NodePublisherEventCallback = {
|
|
9
10
|
event: number;
|
|
@@ -34,6 +35,9 @@ export type NodePublisherProps = {
|
|
|
34
35
|
videoOrientation?: number;
|
|
35
36
|
frontCamera?: boolean;
|
|
36
37
|
cameraFrontMirror?: boolean;
|
|
38
|
+
zoomRatio?: number;
|
|
39
|
+
volume?: number;
|
|
40
|
+
torchEnable?: boolean;
|
|
37
41
|
HWAccelEnable?: boolean;
|
|
38
42
|
denoiseEnable?: boolean;
|
|
39
43
|
colorStyleId?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoNodePublisherView.d.ts","sourceRoot":"","sources":["../src/ExpoNodePublisherView.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoNodePublisherView.d.ts","sourceRoot":"","sources":["../src/ExpoNodePublisherView.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,2BAA2B,EAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,CAAC,EAAE,gBAAgB,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,0BAA0B,CAAA;KAAE,KAAK,IAAI,CAAC;CAChF,GAAG,SAAS,CAAC;AAGd,iBAAS,qBAAqB,CAAC,KAAK,EAAE,kBAAkB,+BAEvD;kBAFQ,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0C9B,eAAe,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoNodePublisherView.js","sourceRoot":"","sources":["../src/ExpoNodePublisherView.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoNodePublisherView.js","sourceRoot":"","sources":["../src/ExpoNodePublisherView.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAqD7D,MAAM,UAAU,GAA4C,wBAAwB,CAAC,uBAAuB,CAAC,CAAC;AAC9G,SAAS,qBAAqB,CAAC,KAAyB;IACtD,OAAO,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AACnC,CAAC;AAED,qBAAqB,CAAC,iBAAiB,GAAG,EAAE,CAAC;AAC7C,qBAAqB,CAAC,iBAAiB,GAAG,GAAG,CAAC;AAC9C,qBAAqB,CAAC,gBAAgB,GAAG,KAAK,CAAC;AAC/C,qBAAqB,CAAC,iBAAiB,GAAG,KAAK,CAAC;AAChD,qBAAqB,CAAC,iBAAiB,GAAG,KAAK,CAAC;AAChD,qBAAqB,CAAC,iBAAiB,GAAG,KAAK,CAAC;AAChD,qBAAqB,CAAC,gBAAgB,GAAG,CAAC,CAAC;AAC3C,qBAAqB,CAAC,yBAAyB,GAAG,EAAE,CAAC;AACrD,qBAAqB,CAAC,qBAAqB,GAAG,EAAE,CAAC;AACjD,qBAAqB,CAAC,qBAAqB,GAAG,GAAG,CAAC;AAClD,qBAAqB,CAAC,qBAAqB,GAAG,CAAC,CAAC;AAChD,qBAAqB,CAAC,kBAAkB,GAAG,CAAC,CAAC;AAC7C,qBAAqB,CAAC,kBAAkB,GAAG,CAAC,CAAC;AAC7C,qBAAqB,CAAC,qBAAqB,GAAG,EAAE,CAAC;AACjD,qBAAqB,CAAC,0BAA0B,GAAG,CAAC,CAAC;AACrD,qBAAqB,CAAC,iCAAiC,GAAG,CAAC,CAAC;AAC5D,qBAAqB,CAAC,gCAAgC,GAAG,CAAC,CAAC;AAC3D,qBAAqB,CAAC,OAAO,GAAG,CAAC,CAAC;AAClC,qBAAqB,CAAC,OAAO,GAAG,CAAC,CAAC;AAClC,qBAAqB,CAAC,QAAQ,GAAG,CAAC,CAAC;AACnC,qBAAqB,CAAC,+BAA+B,GAAG,CAAC,CAAC;AAC1D,qBAAqB,CAAC,+BAA+B,GAAG,CAAC,CAAC;AAC1D,qBAAqB,CAAC,+BAA+B,GAAG,CAAC,CAAC;AAC1D,qBAAqB,CAAC,0BAA0B,GAAG,CAAC,CAAC;AACrD,qBAAqB,CAAC,4BAA4B,GAAG,CAAC,CAAC;AACvD,qBAAqB,CAAC,mBAAmB,GAAG,YAAY,CAAC;AACzD,qBAAqB,CAAC,iBAAiB,GAAG,UAAU,CAAC;AACrD,qBAAqB,CAAC,mBAAmB,GAAG,YAAY,CAAC;AACzD,qBAAqB,CAAC,gBAAgB,GAAG,SAAS,CAAC;AACnD,qBAAqB,CAAC,mBAAmB,GAAG,YAAY,CAAC;AACzD,qBAAqB,CAAC,cAAc,GAAG,OAAO,CAAC;AAC/C,qBAAqB,CAAC,0BAA0B,GAAG,CAAC,CAAC;AACrD,qBAAqB,CAAC,0BAA0B,GAAG,CAAC,CAAC;AACrD,qBAAqB,CAAC,0BAA0B,GAAG,CAAC,CAAC;AACrD,qBAAqB,CAAC,sBAAsB,GAAG,CAAC,CAAC;AACjD,qBAAqB,CAAC,sBAAsB,GAAG,CAAC,CAAC;AACjD,qBAAqB,CAAC,uBAAuB,GAAG,CAAC,CAAC;AAElD,eAAe,qBAAqB,CAAC","sourcesContent":["\nimport { ViewProps } from 'react-native';\nimport { requireNativeViewManager } from 'expo-modules-core';\n\nexport type NodePublisherRef = {\n start: (url?: string) => Promise<void>;\n stop: () => Promise<void>;\n setEffectParameter: (key: string, value: number) => Promise<void>;\n setEffectStyle: (style: number) => Promise<void>;\n startFocusAndMeteringCenter:() => Promise<void>;\n}\n\nexport type NodePublisherEventCallback = {\n event: number;\n msg: string;\n}\n\nexport type AudioParam = {\n codecid?: number;\n profile?: number;\n samplingRate?: number;\n channels?: number;\n bitrate?: number;\n}\n\nexport type VideoParam = {\n codecid?: number;\n profile?: number;\n width?: number;\n height?: number;\n fps?: number;\n bitrate?: number;\n}\n\nexport type NodePublisherProps = {\n ref?: NodePublisherRef;\n url?: string;\n cryptoKey?: string;\n audioParam?: AudioParam\n videoParam?: VideoParam;\n keyFrameInterval?: number;\n videoOrientation?: number;\n frontCamera?: boolean;\n cameraFrontMirror?: boolean;\n zoomRatio?: number;\n volume?: number;\n torchEnable?: boolean;\n HWAccelEnable?: boolean;\n denoiseEnable?: boolean;\n colorStyleId?: number;\n colorStyleIntensity?: number;\n smoothskinIntensity?: number;\n onEventCallback?: (event: { nativeEvent: NodePublisherEventCallback }) => void;\n} & ViewProps;\n\nconst NativeView: React.ComponentType<NodePublisherProps> = requireNativeViewManager('ExpoNodePublisherView');\nfunction ExpoNodePublisherView(props: NodePublisherProps) {\n return <NativeView {...props} />;\n}\n\nExpoNodePublisherView.NMC_CODEC_ID_H264 = 27;\nExpoNodePublisherView.NMC_CODEC_ID_H265 = 173;\nExpoNodePublisherView.NMC_CODEC_ID_AAC = 86018;\nExpoNodePublisherView.NMC_CODEC_ID_OPUS = 86076;\nExpoNodePublisherView.NMC_CODEC_ID_PCMA = 65543;\nExpoNodePublisherView.NMC_CODEC_ID_PCMU = 65542;\nExpoNodePublisherView.NMC_PROFILE_AUTO = 0;\nExpoNodePublisherView.NMC_PROFILE_H264_BASELINE = 66;\nExpoNodePublisherView.NMC_PROFILE_H264_MAIN = 77;\nExpoNodePublisherView.NMC_PROFILE_H264_HIGH = 100;\nExpoNodePublisherView.NMC_PROFILE_H265_MAIN = 1;\nExpoNodePublisherView.NMC_PROFILE_AAC_LC = 1;\nExpoNodePublisherView.NMC_PROFILE_AAC_HE = 4;\nExpoNodePublisherView.NMC_PROFILE_AAC_HE_V2 = 28;\nExpoNodePublisherView.VIDEO_ORIENTATION_PORTRAIT = 1;\nExpoNodePublisherView.VIDEO_ORIENTATION_LANDSCAPE_RIGHT = 3;\nExpoNodePublisherView.VIDEO_ORIENTATION_LANDSCAPE_LEFT = 4;\nExpoNodePublisherView.FLAG_AF = 1;\nExpoNodePublisherView.FLAG_AE = 2;\nExpoNodePublisherView.FLAG_AWB = 4;\nExpoNodePublisherView.NMC_DEVICE_TYPE_WideAngleCamera = 0;\nExpoNodePublisherView.NMC_DEVICE_TYPE_TelephotoCamera = 1;\nExpoNodePublisherView.NMC_DEVICE_TYPE_UltraWideCamera = 2;\nExpoNodePublisherView.NMC_DEVICE_TYPE_DualCamera = 3;\nExpoNodePublisherView.NMC_DEVICE_TYPE_TripleCamera = 4;\nExpoNodePublisherView.EFFECTOR_BRIGHTNESS = \"brightness\";\nExpoNodePublisherView.EFFECTOR_CONTRAST = \"contrast\";\nExpoNodePublisherView.EFFECTOR_SATURATION = \"saturation\";\nExpoNodePublisherView.EFFECTOR_SHARPEN = \"sharpen\";\nExpoNodePublisherView.EFFECTOR_SMOOTHSKIN = \"smoothskin\";\nExpoNodePublisherView.EFFECTOR_STYLE = \"style\";\nExpoNodePublisherView.EFFECTOR_STYLE_ID_ORIGINAL = 0;\nExpoNodePublisherView.EFFECTOR_STYLE_ID_ENHANCED = 1;\nExpoNodePublisherView.EFFECTOR_STYLE_ID_FAIRSKIN = 2;\nExpoNodePublisherView.EFFECTOR_STYLE_ID_COOL = 3;\nExpoNodePublisherView.EFFECTOR_STYLE_ID_FILM = 4;\nExpoNodePublisherView.EFFECTOR_STYLE_ID_BOOST = 5;\n\nexport default ExpoNodePublisherView;"]}
|
|
@@ -63,33 +63,35 @@ class ExpoNodePublisherView: ExpoView, NodePublisherDelegate {
|
|
|
63
63
|
|
|
64
64
|
var frontCamera: Bool? {
|
|
65
65
|
didSet {
|
|
66
|
-
if let fc = frontCamera {
|
|
66
|
+
if let fc = frontCamera, oldValue != frontCamera {
|
|
67
67
|
_np?.closeCamera()
|
|
68
68
|
_np?.openCamera(fc)
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
var
|
|
73
|
+
var zoomRatio: Float = 0.0{
|
|
74
74
|
didSet {
|
|
75
|
-
|
|
76
|
-
_np?.setRoomRatio(ratio)
|
|
77
|
-
}
|
|
75
|
+
_np?.zoomRatio = zoomRatio
|
|
78
76
|
}
|
|
79
77
|
}
|
|
80
78
|
|
|
81
|
-
var torchEnable: Bool
|
|
79
|
+
var torchEnable: Bool = false {
|
|
82
80
|
didSet {
|
|
83
|
-
|
|
84
|
-
_np?.enableTorch(torch)
|
|
85
|
-
}
|
|
81
|
+
_np?.torchEnable = torchEnable
|
|
86
82
|
}
|
|
87
83
|
}
|
|
88
|
-
|
|
84
|
+
|
|
85
|
+
var volume: Float = 1.0 {
|
|
86
|
+
didSet {
|
|
87
|
+
_np?.volume = volume
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
89
91
|
required init(appContext: AppContext? = nil) {
|
|
90
92
|
super.init(appContext: appContext)
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
|
|
93
95
|
override func didMoveToWindow() {
|
|
94
96
|
super.didMoveToWindow()
|
|
95
97
|
if window != nil {
|
|
@@ -104,6 +106,8 @@ class ExpoNodePublisherView: ExpoView, NodePublisherDelegate {
|
|
|
104
106
|
_np?.cryptoKey = cryptoKey
|
|
105
107
|
_np?.hwAccelEnable = HWAccelEnable
|
|
106
108
|
_np?.denoiseEnable = denoiseEnable
|
|
109
|
+
_np?.torchEnable = torchEnable
|
|
110
|
+
_np?.volume = volume
|
|
107
111
|
_np?.openCamera(frontCamera == true)
|
|
108
112
|
_np?.attach(self)
|
|
109
113
|
|
|
@@ -153,6 +157,10 @@ class ExpoNodePublisherView: ExpoView, NodePublisherDelegate {
|
|
|
153
157
|
func setEffectStyle(style: Int) {
|
|
154
158
|
_np?.setEffectStyleWithId(style)
|
|
155
159
|
}
|
|
160
|
+
|
|
161
|
+
func startFocusAndMeteringCenter() {
|
|
162
|
+
_np?.startFocusAndMeteringCenter()
|
|
163
|
+
}
|
|
156
164
|
|
|
157
165
|
// MARK: - NodePublisherDelegate
|
|
158
166
|
|
|
@@ -7,101 +7,108 @@
|
|
|
7
7
|
import ExpoModulesCore
|
|
8
8
|
|
|
9
9
|
public class ExpoNodePublisherViewModule: Module {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
View(ExpoNodePublisherView.self) {
|
|
14
|
-
Events("onEventCallback")
|
|
15
|
-
|
|
16
|
-
Prop("url") { (view, url: String) in
|
|
17
|
-
view.url = url
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
Prop("cryptoKey") { (view, key: String) in
|
|
21
|
-
view.cryptoKey = key
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
Prop("HWAccelEnable") { (view, enable: Bool) in
|
|
25
|
-
view.HWAccelEnable = enable
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Audio parameters
|
|
29
|
-
Prop("audioParam") { (view, audioParam: [String: Int]) in
|
|
30
|
-
view.audioCodecid = audioParam["codecid"]!
|
|
31
|
-
view.audioProfile = audioParam["profile"]!
|
|
32
|
-
view.audioChannels = audioParam["channels"]!
|
|
33
|
-
view.audioSamplingRate = audioParam["samplingRate"]!
|
|
34
|
-
view.audioBitrate = audioParam["bitrate"]!
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Video parameters
|
|
38
|
-
Prop("videoParam") { (view, videoParam: [String: Int]) in
|
|
39
|
-
view.videoCodecid = videoParam["codecid"]!
|
|
40
|
-
view.videoProfile = videoParam["profile"]!
|
|
41
|
-
view.videoWidth = videoParam["width"]!
|
|
42
|
-
view.videoHeight = videoParam["height"]!
|
|
43
|
-
view.videoFps = videoParam["fps"]!
|
|
44
|
-
view.videoBitrate = videoParam["bitrate"]!
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
Prop("keyFrameInterval") { (view, interval: Int) in
|
|
48
|
-
view.keyFrameInterval = interval
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
Prop("videoOrientation") { (view, orientation: Int) in
|
|
52
|
-
view.videoOrientation = orientation
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
Prop("frontCamera") { (view, front: Bool) in
|
|
56
|
-
view.frontCamera = front
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
Prop("cameraFrontMirror") { (view, mirror: Bool) in
|
|
60
|
-
view.cameraFrontMirror = mirror
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
Prop("roomRatio") { (view, ratio: Double) in
|
|
64
|
-
view.roomRatio = Float(ratio)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
Prop("torchEnable") { (view, enable: Bool) in
|
|
68
|
-
view.torchEnable = enable
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
Prop("denoiseEnable") { (view, enable: Bool) in
|
|
72
|
-
view.denoiseEnable = enable
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Color and effect parameters
|
|
76
|
-
Prop("colorStyleId") { (view, styleId: Int) in
|
|
77
|
-
view.colorStyleId = styleId
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
Prop("colorStyleIntensity") { (view, intensity: Double) in
|
|
81
|
-
view.colorStyleIntensity = Float(intensity)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
Prop("smoothskinIntensity") { (view, intensity: Double) in
|
|
85
|
-
view.smoothskinIntensity = Float(intensity)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Methods
|
|
89
|
-
AsyncFunction("start") { (view: ExpoNodePublisherView, url: String?) in
|
|
90
|
-
view.start(u: url)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
AsyncFunction("stop") { (view: ExpoNodePublisherView) in
|
|
94
|
-
view.stop()
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
AsyncFunction("setEffectParameter") { (view: ExpoNodePublisherView, key: String, value: Float) in
|
|
98
|
-
view.setEffectParameter(key: key, value: value)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
AsyncFunction("setEffectStyle") { (view: ExpoNodePublisherView, style: Int) in
|
|
102
|
-
view.setEffectStyle(style: style)
|
|
103
|
-
}
|
|
10
|
+
public func definition() -> ModuleDefinition {
|
|
11
|
+
Name("ExpoNodePublisherView")
|
|
104
12
|
|
|
13
|
+
View(ExpoNodePublisherView.self) {
|
|
14
|
+
Events("onEventCallback")
|
|
15
|
+
|
|
16
|
+
Prop("url") { (view, url: String) in
|
|
17
|
+
view.url = url
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Prop("cryptoKey") { (view, key: String) in
|
|
21
|
+
view.cryptoKey = key
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Prop("HWAccelEnable") { (view, enable: Bool) in
|
|
25
|
+
view.HWAccelEnable = enable
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Audio parameters
|
|
29
|
+
Prop("audioParam") { (view, audioParam: [String: Int]) in
|
|
30
|
+
view.audioCodecid = audioParam["codecid"]!
|
|
31
|
+
view.audioProfile = audioParam["profile"]!
|
|
32
|
+
view.audioChannels = audioParam["channels"]!
|
|
33
|
+
view.audioSamplingRate = audioParam["samplingRate"]!
|
|
34
|
+
view.audioBitrate = audioParam["bitrate"]!
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Video parameters
|
|
38
|
+
Prop("videoParam") { (view, videoParam: [String: Int]) in
|
|
39
|
+
view.videoCodecid = videoParam["codecid"]!
|
|
40
|
+
view.videoProfile = videoParam["profile"]!
|
|
41
|
+
view.videoWidth = videoParam["width"]!
|
|
42
|
+
view.videoHeight = videoParam["height"]!
|
|
43
|
+
view.videoFps = videoParam["fps"]!
|
|
44
|
+
view.videoBitrate = videoParam["bitrate"]!
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Prop("keyFrameInterval") { (view, interval: Int) in
|
|
48
|
+
view.keyFrameInterval = interval
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Prop("videoOrientation") { (view, orientation: Int) in
|
|
52
|
+
view.videoOrientation = orientation
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Prop("frontCamera") { (view, front: Bool) in
|
|
56
|
+
view.frontCamera = front
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Prop("cameraFrontMirror") { (view, mirror: Bool) in
|
|
60
|
+
view.cameraFrontMirror = mirror
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Prop("zoomRatio") { (view, ratio: Float) in
|
|
64
|
+
view.zoomRatio = ratio
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
Prop("torchEnable") { (view, enable: Bool) in
|
|
68
|
+
view.torchEnable = enable
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Prop("denoiseEnable") { (view, enable: Bool) in
|
|
72
|
+
view.denoiseEnable = enable
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Prop("volume") { (view, volume: Float) in
|
|
76
|
+
view.volume = volume
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Color and effect parameters
|
|
80
|
+
Prop("colorStyleId") { (view, styleId: Int) in
|
|
81
|
+
view.colorStyleId = styleId
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
Prop("colorStyleIntensity") { (view, intensity: Float) in
|
|
85
|
+
view.colorStyleIntensity = intensity
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Prop("smoothskinIntensity") { (view, intensity: Float) in
|
|
89
|
+
view.smoothskinIntensity = intensity
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Methods
|
|
93
|
+
AsyncFunction("start") { (view: ExpoNodePublisherView, url: String?) in
|
|
94
|
+
view.start(u: url)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
AsyncFunction("stop") { (view: ExpoNodePublisherView) in
|
|
98
|
+
view.stop()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
AsyncFunction("setEffectParameter") { (view: ExpoNodePublisherView, key: String, value: Float) in
|
|
102
|
+
view.setEffectParameter(key: key, value: value)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
AsyncFunction("setEffectStyle") { (view: ExpoNodePublisherView, style: Int) in
|
|
106
|
+
view.setEffectStyle(style: style)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
AsyncFunction("startFocusAndMeteringCenter") { (view: ExpoNodePublisherView) in
|
|
110
|
+
view.startFocusAndMeteringCenter()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
105
113
|
}
|
|
106
|
-
}
|
|
107
114
|
}
|
|
@@ -19,7 +19,7 @@ Pod::Spec.new do |s|
|
|
|
19
19
|
s.static_framework = true
|
|
20
20
|
|
|
21
21
|
s.dependency 'ExpoModulesCore'
|
|
22
|
-
s.dependency 'NodeMediaClient', '~> 4.0
|
|
22
|
+
s.dependency 'NodeMediaClient', '~> 4.1.0'
|
|
23
23
|
# Swift/Objective-C compatibility
|
|
24
24
|
s.pod_target_xcconfig = {
|
|
25
25
|
'DEFINES_MODULE' => 'YES',
|
package/package.json
CHANGED
|
@@ -5,8 +5,9 @@ import { requireNativeViewManager } from 'expo-modules-core';
|
|
|
5
5
|
export type NodePublisherRef = {
|
|
6
6
|
start: (url?: string) => Promise<void>;
|
|
7
7
|
stop: () => Promise<void>;
|
|
8
|
-
setEffectParameter: (key: string, value: number) => void
|
|
9
|
-
setEffectStyle: (style: number) => void
|
|
8
|
+
setEffectParameter: (key: string, value: number) => Promise<void>;
|
|
9
|
+
setEffectStyle: (style: number) => Promise<void>;
|
|
10
|
+
startFocusAndMeteringCenter:() => Promise<void>;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
export type NodePublisherEventCallback = {
|
|
@@ -41,6 +42,9 @@ export type NodePublisherProps = {
|
|
|
41
42
|
videoOrientation?: number;
|
|
42
43
|
frontCamera?: boolean;
|
|
43
44
|
cameraFrontMirror?: boolean;
|
|
45
|
+
zoomRatio?: number;
|
|
46
|
+
volume?: number;
|
|
47
|
+
torchEnable?: boolean;
|
|
44
48
|
HWAccelEnable?: boolean;
|
|
45
49
|
denoiseEnable?: boolean;
|
|
46
50
|
colorStyleId?: number;
|