@byteplus/react-native-live-push 1.2.0-rc.0 → 1.3.0-rc.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/android/build.gradle +1 -1
- package/android/src/main/java/com/volcengine/velive/rn/push/VeLiveEffectHelper.java +222 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushModule.java +2 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerManager.java +1 -1
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerView.java +19 -125
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerViewManager.java +1 -37
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/TextureMgr.java +6 -1
- package/ios/VeLiveMixerHelper.h +9 -6
- package/ios/VeLiveMixerHelper.m +38 -12
- package/ios/VeLiveMixerView.h +1 -8
- package/ios/VeLiveMixerView.m +178 -231
- package/ios/VeLiveMixerViewManager.m +0 -36
- package/ios/VeLivePushHelper.h +2 -1
- package/ios/VeLivePushHelper.m +7 -0
- package/lib/commonjs/index.js +490 -283
- package/lib/commonjs/typescript/core/api.d.ts +7 -1
- package/lib/commonjs/typescript/core/effect.d.ts +15 -0
- package/lib/commonjs/typescript/core/index.d.ts +1 -0
- package/lib/commonjs/typescript/core/keytype.d.ts +2 -3
- package/lib/commonjs/typescript/platforms/android/extends.d.ts +2 -0
- package/lib/commonjs/typescript/platforms/android/helper.d.ts +10 -0
- package/lib/commonjs/typescript/platforms/ios/helper.d.ts +1 -0
- package/lib/commonjs/typescript/utils/index.d.ts +4 -0
- package/lib/commonjs/typescript/view/MixView.d.ts +1 -9
- package/lib/module/index.js +490 -284
- package/lib/module/typescript/core/api.d.ts +7 -1
- package/lib/module/typescript/core/effect.d.ts +15 -0
- package/lib/module/typescript/core/index.d.ts +1 -0
- package/lib/module/typescript/core/keytype.d.ts +2 -3
- package/lib/module/typescript/platforms/android/extends.d.ts +2 -0
- package/lib/module/typescript/platforms/android/helper.d.ts +10 -0
- package/lib/module/typescript/platforms/ios/helper.d.ts +1 -0
- package/lib/module/typescript/utils/index.d.ts +4 -0
- package/lib/module/typescript/view/MixView.d.ts +1 -9
- package/lib/typescript/core/api.d.ts +7 -1
- package/lib/typescript/core/effect.d.ts +15 -0
- package/lib/typescript/core/index.d.ts +1 -0
- package/lib/typescript/core/keytype.d.ts +2 -3
- package/lib/typescript/platforms/android/extends.d.ts +2 -0
- package/lib/typescript/platforms/android/helper.d.ts +10 -0
- package/lib/typescript/platforms/ios/helper.d.ts +1 -0
- package/lib/typescript/utils/index.d.ts +4 -0
- package/lib/typescript/view/MixView.d.ts +1 -9
- package/package.json +1 -1
- package/react-native-velive-push.podspec +3 -3
package/android/build.gradle
CHANGED
|
@@ -87,6 +87,6 @@ dependencies {
|
|
|
87
87
|
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
|
|
88
88
|
//noinspection GradleDynamicVersion
|
|
89
89
|
implementation "com.facebook.react:react-native:+"
|
|
90
|
-
implementation "com.volcengine:VolcApiEngine:1.6.
|
|
90
|
+
implementation "com.volcengine:VolcApiEngine:1.6.4"
|
|
91
91
|
api 'com.bytedanceapi:ttsdk-ttlivepush_rtc:1.46.300.2'
|
|
92
92
|
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
package com.volcengine.velive.rn.push;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.text.TextUtils;
|
|
5
|
+
|
|
6
|
+
import com.pandora.common.env.Env;
|
|
7
|
+
|
|
8
|
+
import java.io.File;
|
|
9
|
+
import java.io.FileInputStream;
|
|
10
|
+
import java.io.FileOutputStream;
|
|
11
|
+
import java.io.IOException;
|
|
12
|
+
import java.io.InputStream;
|
|
13
|
+
import java.io.OutputStream;
|
|
14
|
+
|
|
15
|
+
public class VeLiveEffectHelper {
|
|
16
|
+
static Context context;
|
|
17
|
+
/**
|
|
18
|
+
* 获取证书文件路径
|
|
19
|
+
*/
|
|
20
|
+
public static String getLicensePath(String name) {
|
|
21
|
+
// Context context = Env.getApplicationContext();
|
|
22
|
+
if (context == null) {
|
|
23
|
+
throw new IllegalStateException("context not set");
|
|
24
|
+
}
|
|
25
|
+
return context.getExternalFilesDir("assets").getAbsolutePath()
|
|
26
|
+
+ "/resource/LicenseBag.bundle/" + name;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 获取模型文件路径
|
|
31
|
+
*/
|
|
32
|
+
public static String getModelPath() {
|
|
33
|
+
// Context context = Env.getApplicationContext();
|
|
34
|
+
if (context == null) {
|
|
35
|
+
throw new IllegalStateException("context not set");
|
|
36
|
+
}
|
|
37
|
+
return context.getExternalFilesDir("assets").getAbsolutePath()
|
|
38
|
+
+ "/resource/ModelResource.bundle";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 获取美颜文件路径
|
|
43
|
+
*/
|
|
44
|
+
public static String getBeautyPathByName(String subPath) {
|
|
45
|
+
// Context context = Env.getApplicationContext();
|
|
46
|
+
if (context == null) {
|
|
47
|
+
throw new IllegalStateException("context not set");
|
|
48
|
+
}
|
|
49
|
+
return context.getExternalFilesDir("assets").getAbsolutePath()
|
|
50
|
+
+ "/resource/ComposeMakeup.bundle/ComposeMakeup/" + subPath;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 获取贴纸文件路径
|
|
55
|
+
*
|
|
56
|
+
* @param name 贴纸文件名称
|
|
57
|
+
*/
|
|
58
|
+
public static String getStickerPathByName(String name) {
|
|
59
|
+
// Context context = Env.getApplicationContext();
|
|
60
|
+
if (context == null) {
|
|
61
|
+
throw new IllegalStateException("context not set");
|
|
62
|
+
}
|
|
63
|
+
return context.getExternalFilesDir("assets").getAbsolutePath()
|
|
64
|
+
+ "/resource/StickerResource.bundle/stickers/" + name;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 获取滤镜文件路径
|
|
69
|
+
*
|
|
70
|
+
* @param name 滤镜文件名称
|
|
71
|
+
*/
|
|
72
|
+
public static String getFilterPathByName(String name) {
|
|
73
|
+
// Context context = Env.getApplicationContext();
|
|
74
|
+
if (context == null) {
|
|
75
|
+
throw new IllegalStateException("context not set");
|
|
76
|
+
}
|
|
77
|
+
return context.getExternalFilesDir("assets").getAbsolutePath()
|
|
78
|
+
+ "/resource/FilterResource.bundle/Filter/" + name;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 初始化美颜资源文件
|
|
83
|
+
* 将安装包内的资源文件拷贝到外部存储上
|
|
84
|
+
*/
|
|
85
|
+
public static void initVideoEffectResource() {
|
|
86
|
+
// Context context = Env.getApplicationContext();
|
|
87
|
+
if (context == null) {
|
|
88
|
+
throw new IllegalStateException("context not set");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// File versionFile = new File(getExternalResourcePath(), "version");
|
|
92
|
+
// if (versionFile.exists()) {
|
|
93
|
+
// String oldVer = readVersion(versionFile.getAbsolutePath());
|
|
94
|
+
// copyAssetFolder(context, "resource/version", versionFile.getAbsolutePath());
|
|
95
|
+
// String newVer = readVersion(versionFile.getAbsolutePath());
|
|
96
|
+
// if (TextUtils.equals(oldVer, newVer)) {
|
|
97
|
+
// return;
|
|
98
|
+
// }
|
|
99
|
+
// } else {
|
|
100
|
+
// copyAssetFile(context, "resource/version", versionFile.getAbsolutePath());
|
|
101
|
+
// }
|
|
102
|
+
updateEffectResource(context);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private static String readVersion(String fileName) {
|
|
106
|
+
String version = "";
|
|
107
|
+
try {
|
|
108
|
+
FileInputStream fin = new FileInputStream(fileName);
|
|
109
|
+
int length = fin.available();
|
|
110
|
+
byte[] buffer = new byte[length];
|
|
111
|
+
fin.read(buffer);
|
|
112
|
+
version = new String(buffer);
|
|
113
|
+
fin.close();
|
|
114
|
+
} catch (Exception e) {
|
|
115
|
+
e.printStackTrace();
|
|
116
|
+
}
|
|
117
|
+
return version;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private static void updateEffectResource(Context context) {
|
|
121
|
+
File licensePath = new File(getExternalResourcePath(), "LicenseBag.bundle");
|
|
122
|
+
removeFile(licensePath.getAbsolutePath());
|
|
123
|
+
copyAssetFolder(context, "resource/LicenseBag.bundle", licensePath.getAbsolutePath());
|
|
124
|
+
File modelPath = new File(getExternalResourcePath(), "ModelResource.bundle");
|
|
125
|
+
removeFile(modelPath.getAbsolutePath());
|
|
126
|
+
copyAssetFolder(context, "resource/ModelResource.bundle", modelPath.getAbsolutePath());
|
|
127
|
+
File stickerPath = new File(getExternalResourcePath(), "StickerResource.bundle");
|
|
128
|
+
removeFile(stickerPath.getAbsolutePath());
|
|
129
|
+
copyAssetFolder(context, "resource/StickerResource.bundle", stickerPath.getAbsolutePath());
|
|
130
|
+
File filterPath = new File(getExternalResourcePath(), "FilterResource.bundle");
|
|
131
|
+
removeFile(filterPath.getAbsolutePath());
|
|
132
|
+
copyAssetFolder(context, "resource/FilterResource.bundle", filterPath.getAbsolutePath());
|
|
133
|
+
File composerPath = new File(getExternalResourcePath(), "ComposeMakeup.bundle");
|
|
134
|
+
removeFile(composerPath.getAbsolutePath());
|
|
135
|
+
copyAssetFolder(context, "resource/ComposeMakeup.bundle", composerPath.getAbsolutePath());
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private static void removeFile(String filePath) {
|
|
139
|
+
if (filePath == null || filePath.length() == 0) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
File file = new File(filePath);
|
|
144
|
+
if (file.exists()) {
|
|
145
|
+
removeFile(file);
|
|
146
|
+
}
|
|
147
|
+
} catch (Exception ex) {
|
|
148
|
+
ex.printStackTrace();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private static void removeFile(File file) {
|
|
153
|
+
// 如果是文件直接删除
|
|
154
|
+
if (file.isFile()) {
|
|
155
|
+
file.delete();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// 如果是目录,递归判断,如果是空目录,直接删除,如果是文件,遍历删除
|
|
159
|
+
if (file.isDirectory()) {
|
|
160
|
+
File[] childFile = file.listFiles();
|
|
161
|
+
if (childFile == null || childFile.length == 0) {
|
|
162
|
+
file.delete();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
for (File f : childFile) {
|
|
166
|
+
removeFile(f);
|
|
167
|
+
}
|
|
168
|
+
file.delete();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public static String getExternalResourcePath() {
|
|
173
|
+
// Context context = Env.getApplicationContext();
|
|
174
|
+
if (context == null) {
|
|
175
|
+
throw new IllegalStateException("context not set");
|
|
176
|
+
}
|
|
177
|
+
return context.getExternalFilesDir("assets").getAbsolutePath() + "/resource/";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public static boolean copyAssetFolder(Context context, String srcName, String dstName) {
|
|
181
|
+
try {
|
|
182
|
+
boolean result = true;
|
|
183
|
+
String fileList[] = context.getAssets().list(srcName);
|
|
184
|
+
if (fileList == null)
|
|
185
|
+
return false;
|
|
186
|
+
|
|
187
|
+
if (fileList.length == 0) {
|
|
188
|
+
result = copyAssetFile(context, srcName, dstName);
|
|
189
|
+
} else {
|
|
190
|
+
File file = new File(dstName);
|
|
191
|
+
result = file.mkdirs();
|
|
192
|
+
for (String filename : fileList) {
|
|
193
|
+
result &= copyAssetFolder(context, srcName + File.separator + filename, dstName + File.separator + filename);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
} catch (IOException e) {
|
|
198
|
+
e.printStackTrace();
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public static boolean copyAssetFile(Context context, String srcName, String dstName) {
|
|
204
|
+
try {
|
|
205
|
+
InputStream in = context.getAssets().open(srcName);
|
|
206
|
+
File outFile = new File(dstName);
|
|
207
|
+
OutputStream out = new FileOutputStream(outFile);
|
|
208
|
+
byte[] buffer = new byte[1024];
|
|
209
|
+
int read;
|
|
210
|
+
while ((read = in.read(buffer)) != -1) {
|
|
211
|
+
out.write(buffer, 0, read);
|
|
212
|
+
}
|
|
213
|
+
in.close();
|
|
214
|
+
out.close();
|
|
215
|
+
return true;
|
|
216
|
+
} catch (IOException e) {
|
|
217
|
+
e.printStackTrace();
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
}
|
|
@@ -23,6 +23,7 @@ import com.ss.avframework.live.VeLivePusherObserver;
|
|
|
23
23
|
import com.ss.avframework.live.statistics.VeLivePusherStatisticsExt;
|
|
24
24
|
import com.volcengine.VolcApiEngine.*;
|
|
25
25
|
import com.volcengine.VolcApiEngine.view.*;
|
|
26
|
+
import com.volcengine.velive.rn.push.VeLiveEffectHelper;
|
|
26
27
|
|
|
27
28
|
import java.util.WeakHashMap;
|
|
28
29
|
|
|
@@ -37,6 +38,7 @@ public class VeLivePushModule extends VeLivePushModuleSpec implements IEventRece
|
|
|
37
38
|
|
|
38
39
|
VeLivePushModule(ReactApplicationContext context) {
|
|
39
40
|
super(context);
|
|
41
|
+
VeLiveEffectHelper.context = context;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
@Override
|
|
@@ -20,24 +20,14 @@ public class MixerView extends FrameLayout {
|
|
|
20
20
|
private ThemedReactContext context;
|
|
21
21
|
private String viewId;
|
|
22
22
|
|
|
23
|
-
// Mix configuration
|
|
24
|
-
private float mixX = 0f;
|
|
25
|
-
private float mixY = 0f;
|
|
26
|
-
private float mixWidth = 0f;
|
|
27
|
-
private float mixHeight = 0f;
|
|
28
|
-
private int mixZOrder = 0;
|
|
29
|
-
private int mixRenderMode = 0;
|
|
30
|
-
|
|
31
23
|
// Capture configuration
|
|
32
24
|
private String captureMode = "onchange";
|
|
33
|
-
private float captureFramerate =
|
|
34
|
-
private float autoSensitivity = 5f;
|
|
25
|
+
private float captureFramerate = 5f; // realTime mode default 5 fps
|
|
35
26
|
|
|
36
27
|
// Capture state
|
|
37
28
|
private Handler captureHandler;
|
|
38
29
|
private Runnable captureRunnable;
|
|
39
30
|
private ViewTreeObserver.OnGlobalLayoutListener layoutListener;
|
|
40
|
-
private ViewTreeObserver.OnDrawListener drawListener;
|
|
41
31
|
private AtomicBoolean isCapturing = new AtomicBoolean(false);
|
|
42
32
|
private AtomicBoolean isDestroyed = new AtomicBoolean(false);
|
|
43
33
|
|
|
@@ -46,14 +36,6 @@ public class MixerView extends FrameLayout {
|
|
|
46
36
|
private long lastCaptureTime = 0;
|
|
47
37
|
private final Object captureLock = new Object();
|
|
48
38
|
|
|
49
|
-
// Auto mode state
|
|
50
|
-
private int changeCount = 0;
|
|
51
|
-
private long windowStartTime = 0;
|
|
52
|
-
private boolean isInRealtimeMode = false;
|
|
53
|
-
private static final long AUTO_WINDOW_MS = 2000; // 2 second window
|
|
54
|
-
private static final int HIGH_CHANGE_THRESHOLD = 10;
|
|
55
|
-
private static final int LOW_CHANGE_THRESHOLD = 2;
|
|
56
|
-
|
|
57
39
|
// Add callback support for MixerManager integration
|
|
58
40
|
private MixerManager.BitmapCaptureCallback bitmapCaptureCallback;
|
|
59
41
|
|
|
@@ -67,19 +49,6 @@ public class MixerView extends FrameLayout {
|
|
|
67
49
|
private void initializeCapture() {
|
|
68
50
|
// Initialize layout change listener
|
|
69
51
|
layoutListener = this::onViewChanged;
|
|
70
|
-
|
|
71
|
-
// Initialize draw listener for auto mode
|
|
72
|
-
drawListener = this::onViewDraw;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Setters for mix configuration
|
|
76
|
-
public void setMixX(float x) { this.mixX = x; }
|
|
77
|
-
public void setMixY(float y) { this.mixY = y; }
|
|
78
|
-
public void setMixWidth(float width) { this.mixWidth = width; }
|
|
79
|
-
public void setMixHeight(float height) { this.mixHeight = height; }
|
|
80
|
-
public void setMixZOrder(int zOrder) { this.mixZOrder = zOrder; }
|
|
81
|
-
public void setMixRenderMode(int renderMode) {
|
|
82
|
-
this.mixRenderMode = renderMode;
|
|
83
52
|
}
|
|
84
53
|
|
|
85
54
|
public void setCaptureMode(String mode) {
|
|
@@ -98,10 +67,6 @@ public class MixerView extends FrameLayout {
|
|
|
98
67
|
}
|
|
99
68
|
}
|
|
100
69
|
|
|
101
|
-
public void setAutoSensitivity(float sensitivity) {
|
|
102
|
-
this.autoSensitivity = Math.max(1f, Math.min(10f, sensitivity));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
70
|
private void startCapture() {
|
|
106
71
|
if (isDestroyed.get())
|
|
107
72
|
return;
|
|
@@ -113,9 +78,6 @@ public class MixerView extends FrameLayout {
|
|
|
113
78
|
case "realtime":
|
|
114
79
|
startRealtimeCapture();
|
|
115
80
|
break;
|
|
116
|
-
case "auto":
|
|
117
|
-
startAutoCapture();
|
|
118
|
-
break;
|
|
119
81
|
case "manual":
|
|
120
82
|
// Manual mode doesn't auto-start
|
|
121
83
|
break;
|
|
@@ -128,9 +90,6 @@ public class MixerView extends FrameLayout {
|
|
|
128
90
|
if (layoutListener != null) {
|
|
129
91
|
getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener);
|
|
130
92
|
}
|
|
131
|
-
if (drawListener != null) {
|
|
132
|
-
getViewTreeObserver().removeOnDrawListener(drawListener);
|
|
133
|
-
}
|
|
134
93
|
}
|
|
135
94
|
|
|
136
95
|
// Stop realtime capture
|
|
@@ -143,9 +102,19 @@ public class MixerView extends FrameLayout {
|
|
|
143
102
|
}
|
|
144
103
|
|
|
145
104
|
private void startOnChangeCapture() {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
105
|
+
// onChange mode triggers every 2 seconds
|
|
106
|
+
long interval = 2000; // 2 seconds
|
|
107
|
+
|
|
108
|
+
captureRunnable = new Runnable() {
|
|
109
|
+
@Override
|
|
110
|
+
public void run() {
|
|
111
|
+
if (!isDestroyed.get() && "onchange".equals(captureMode)) {
|
|
112
|
+
performCapture("onchange");
|
|
113
|
+
captureHandler.postDelayed(this, interval);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
captureHandler.postDelayed(captureRunnable, interval);
|
|
149
118
|
}
|
|
150
119
|
|
|
151
120
|
private void startRealtimeCapture() {
|
|
@@ -170,82 +139,13 @@ public class MixerView extends FrameLayout {
|
|
|
170
139
|
}
|
|
171
140
|
}
|
|
172
141
|
|
|
173
|
-
private void startAutoCapture() {
|
|
174
|
-
// Start with onchange mode
|
|
175
|
-
isInRealtimeMode = false;
|
|
176
|
-
windowStartTime = System.currentTimeMillis();
|
|
177
|
-
changeCount = 0;
|
|
178
|
-
|
|
179
|
-
if (getViewTreeObserver().isAlive()) {
|
|
180
|
-
getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
|
|
181
|
-
getViewTreeObserver().addOnDrawListener(drawListener);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
142
|
private void onViewChanged() {
|
|
186
143
|
if (isDestroyed.get()) {
|
|
187
144
|
return;
|
|
188
145
|
}
|
|
189
146
|
|
|
190
|
-
|
|
191
|
-
case "onchange":
|
|
147
|
+
if ("onchange".equals(captureMode)) {
|
|
192
148
|
performCapture("onchange");
|
|
193
|
-
break;
|
|
194
|
-
case "auto":
|
|
195
|
-
handleAutoModeChange();
|
|
196
|
-
break;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
private void onViewDraw() {
|
|
201
|
-
if (isDestroyed.get() || !"auto".equals(captureMode))
|
|
202
|
-
return;
|
|
203
|
-
|
|
204
|
-
// Count draws for auto mode sensitivity
|
|
205
|
-
changeCount++;
|
|
206
|
-
|
|
207
|
-
long currentTime = System.currentTimeMillis();
|
|
208
|
-
if (currentTime - windowStartTime >= AUTO_WINDOW_MS) {
|
|
209
|
-
evaluateAutoMode();
|
|
210
|
-
// Reset window
|
|
211
|
-
windowStartTime = currentTime;
|
|
212
|
-
changeCount = 0;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private void handleAutoModeChange() {
|
|
217
|
-
if (!isInRealtimeMode) {
|
|
218
|
-
performCapture("auto_onchange");
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
private void evaluateAutoMode() {
|
|
223
|
-
float changesPerSecond = changeCount * 1000f / AUTO_WINDOW_MS;
|
|
224
|
-
float threshold = autoSensitivity;
|
|
225
|
-
|
|
226
|
-
boolean shouldBeRealtime =
|
|
227
|
-
changesPerSecond > (HIGH_CHANGE_THRESHOLD / threshold);
|
|
228
|
-
boolean shouldBeOnChange =
|
|
229
|
-
changesPerSecond < (LOW_CHANGE_THRESHOLD / threshold);
|
|
230
|
-
|
|
231
|
-
if (shouldBeRealtime && !isInRealtimeMode) {
|
|
232
|
-
switchToRealtimeMode();
|
|
233
|
-
} else if (shouldBeOnChange && isInRealtimeMode) {
|
|
234
|
-
switchToOnChangeMode();
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
private void switchToRealtimeMode() {
|
|
239
|
-
isInRealtimeMode = true;
|
|
240
|
-
startRealtimeCapture();
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
private void switchToOnChangeMode() {
|
|
244
|
-
isInRealtimeMode = false;
|
|
245
|
-
// Stop realtime capture but keep layout listener
|
|
246
|
-
if (captureRunnable != null) {
|
|
247
|
-
captureHandler.removeCallbacks(captureRunnable);
|
|
248
|
-
captureRunnable = null;
|
|
249
149
|
}
|
|
250
150
|
}
|
|
251
151
|
|
|
@@ -338,12 +238,6 @@ public class MixerView extends FrameLayout {
|
|
|
338
238
|
WritableMap event = Arguments.createMap();
|
|
339
239
|
event.putString("trigger", trigger);
|
|
340
240
|
event.putBoolean("success", true);
|
|
341
|
-
event.putDouble("x", mixX);
|
|
342
|
-
event.putDouble("y", mixY);
|
|
343
|
-
event.putDouble("width", mixWidth);
|
|
344
|
-
event.putDouble("height", mixHeight);
|
|
345
|
-
event.putInt("zOrder", mixZOrder);
|
|
346
|
-
event.putInt("renderMode", mixRenderMode);
|
|
347
241
|
event.putInt("bitmapWidth", bitmap.getWidth());
|
|
348
242
|
event.putInt("bitmapHeight", bitmap.getHeight());
|
|
349
243
|
} catch (IllegalStateException e) {
|
|
@@ -356,8 +250,8 @@ public class MixerView extends FrameLayout {
|
|
|
356
250
|
lastBitmap.recycle();
|
|
357
251
|
}
|
|
358
252
|
|
|
359
|
-
//
|
|
360
|
-
//
|
|
253
|
+
// If no callback is set, immediately release current bitmap to avoid memory accumulation
|
|
254
|
+
// If callback is set, bitmap will be managed by the callback receiver
|
|
361
255
|
if (bitmapCaptureCallback == null) {
|
|
362
256
|
if (bitmap != null && !bitmap.isRecycled()) {
|
|
363
257
|
bitmap.recycle();
|
|
@@ -392,7 +286,7 @@ public class MixerView extends FrameLayout {
|
|
|
392
286
|
super.onAttachedToWindow();
|
|
393
287
|
if (viewId != null) {
|
|
394
288
|
MixerManager.cachedMixedViews.put(viewId, this);
|
|
395
|
-
//
|
|
289
|
+
// Notify MixerManager that view is ready, check for pending callbacks
|
|
396
290
|
MixerManager.onViewReady(viewId, this);
|
|
397
291
|
}
|
|
398
292
|
}
|
|
@@ -409,7 +303,7 @@ public class MixerView extends FrameLayout {
|
|
|
409
303
|
public void setViewId(String viewId) {
|
|
410
304
|
this.viewId = viewId;
|
|
411
305
|
MixerManager.cachedMixedViews.put(viewId, this);
|
|
412
|
-
//
|
|
306
|
+
// Notify MixerManager that view is ready, check for pending callbacks
|
|
413
307
|
MixerManager.onViewReady(viewId, this);
|
|
414
308
|
}
|
|
415
309
|
|
|
@@ -22,52 +22,16 @@ public class MixerViewManager extends ViewGroupManager<MixerView> {
|
|
|
22
22
|
return new MixerView(reactContext);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// Props for mixing configuration
|
|
26
|
-
@ReactProp(name = "x", defaultFloat = 0f)
|
|
27
|
-
public void setX(MixerView view, float x) {
|
|
28
|
-
view.setMixX(x);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@ReactProp(name = "y", defaultFloat = 0f)
|
|
32
|
-
public void setY(MixerView view, float y) {
|
|
33
|
-
view.setMixY(y);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
@ReactProp(name = "width", defaultFloat = 0f)
|
|
37
|
-
public void setWidth(MixerView view, float width) {
|
|
38
|
-
view.setMixWidth(width);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
@ReactProp(name = "height", defaultFloat = 0f)
|
|
42
|
-
public void setHeight(MixerView view, float height) {
|
|
43
|
-
view.setMixHeight(height);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
@ReactProp(name = "zOrder", defaultInt = 0)
|
|
47
|
-
public void setZOrder(MixerView view, int zOrder) {
|
|
48
|
-
view.setMixZOrder(zOrder);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
@ReactProp(name = "renderMode", defaultInt = 0)
|
|
52
|
-
public void setRenderMode(MixerView view, int renderMode) {
|
|
53
|
-
view.setMixRenderMode(renderMode);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
25
|
@ReactProp(name = "captureMode")
|
|
57
26
|
public void setCaptureMode(MixerView view, @Nullable String captureMode) {
|
|
58
27
|
view.setCaptureMode(captureMode != null ? captureMode : "onchange");
|
|
59
28
|
}
|
|
60
29
|
|
|
61
|
-
@ReactProp(name = "captureFramerate", defaultFloat =
|
|
30
|
+
@ReactProp(name = "captureFramerate", defaultFloat = 5f)
|
|
62
31
|
public void setCaptureFramerate(MixerView view, float framerate) {
|
|
63
32
|
view.setCaptureFramerate(framerate);
|
|
64
33
|
}
|
|
65
34
|
|
|
66
|
-
@ReactProp(name = "autoSensitivity", defaultFloat = 5f)
|
|
67
|
-
public void setAutoSensitivity(MixerView view, float sensitivity) {
|
|
68
|
-
view.setAutoSensitivity(sensitivity);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
35
|
@ReactProp(name = "viewId")
|
|
72
36
|
public void setViewId(MixerView view, @Nullable String viewId) {
|
|
73
37
|
if (viewId != null) {
|
|
@@ -52,7 +52,12 @@ public class TextureMgr {
|
|
|
52
52
|
Bitmap bmp = null;
|
|
53
53
|
Bitmap rotatedBmp = null;
|
|
54
54
|
try {
|
|
55
|
-
|
|
55
|
+
android.content.Context context = Env.getApplicationContext();
|
|
56
|
+
if (context == null) {
|
|
57
|
+
// If context is null, skip processing and return
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
bm = new YuvHelper.NV21ToBitmap(context);
|
|
56
61
|
bmp = bm.nv21ToBitmap(byteBuffer.array(), width, height);
|
|
57
62
|
|
|
58
63
|
if (bmp == null || bmp.isRecycled()) {
|
package/ios/VeLiveMixerHelper.h
CHANGED
|
@@ -6,17 +6,18 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#import "VeLiveMixerView.h"
|
|
9
|
+
#import <CoreVideo/CoreVideo.h>
|
|
9
10
|
#import <Foundation/Foundation.h>
|
|
10
11
|
|
|
11
12
|
NS_ASSUME_NONNULL_BEGIN
|
|
12
13
|
|
|
13
|
-
//
|
|
14
|
+
// Forward declaration
|
|
14
15
|
@class VeLivePusher;
|
|
15
16
|
|
|
16
|
-
//
|
|
17
|
+
// Mixer helper class - fully aligned with Android MixerManager.java
|
|
17
18
|
@interface VeLiveMixerHelper : NSObject <VeLiveBitmapCaptureCallback>
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
+
// Aligned with Android's static cachedMixedViews
|
|
20
21
|
@property(class, nonatomic, strong)
|
|
21
22
|
NSMutableDictionary<NSString *, VeLiveMixerUIView *> *cachedMixedViews;
|
|
22
23
|
|
|
@@ -24,21 +25,23 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
24
25
|
|
|
25
26
|
- (instancetype)initWithPusher:(VeLivePusher *)pusher;
|
|
26
27
|
|
|
27
|
-
// Public
|
|
28
|
+
// Public methods - fully aligned with Android MixerManager's public interface
|
|
28
29
|
- (void)setPusher:(VeLivePusher *)pusher;
|
|
29
30
|
- (int)addView:(NSString *)viewId config:(NSDictionary *)viewInfo;
|
|
30
31
|
- (BOOL)updateView:(NSString *)viewId config:(NSDictionary *)viewInfo;
|
|
31
32
|
- (BOOL)removeView:(NSString *)viewId;
|
|
32
33
|
- (void)captureView:(NSString *)viewId;
|
|
33
34
|
|
|
34
|
-
// Static
|
|
35
|
+
// Static methods - for sending captured content to mixer
|
|
35
36
|
+ (void)setupCallbackForMixerView:(VeLiveMixerUIView *)mixerView
|
|
36
37
|
streamId:(int)streamId;
|
|
37
38
|
+ (void)sendBitmapToMixerStatic:(int)streamId bitmap:(UIImage *)bitmap;
|
|
39
|
+
+ (void)sendPixelBufferToMixerStatic:(int)streamId
|
|
40
|
+
pixelBuffer:(CVPixelBufferRef)pixelBuffer;
|
|
38
41
|
+ (VeLiveMixerHelper *)findActiveMixerManager;
|
|
39
42
|
+ (void)onViewReady:(NSString *)viewId mixerView:(VeLiveMixerUIView *)mixerView;
|
|
40
43
|
|
|
41
|
-
//
|
|
44
|
+
// Resource cleanup
|
|
42
45
|
- (void)cleanup;
|
|
43
46
|
|
|
44
47
|
@end
|