@volcengine/react-native-live-push 1.1.3-rc.1 → 1.3.0-rc.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/android/build.gradle +2 -2
- package/android/src/main/java/com/volcengine/velive/rn/push/ClassHelper.java +9 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/NativeVariableManager.java +5 -8
- package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushModule.java +14 -2
- package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushPackage.java +16 -13
- package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushView.java +16 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/VeLivePushViewManager.java +7 -2
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerManager.java +410 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerView.java +328 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/MixerViewManager.java +43 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/TextureMgr.java +168 -0
- package/android/src/main/java/com/volcengine/velive/rn/push/mixer/YuvHelper.java +165 -0
- package/ios/VeLiveMixerHelper.h +49 -0
- package/ios/VeLiveMixerHelper.m +646 -0
- package/ios/VeLiveMixerView.h +62 -0
- package/ios/VeLiveMixerView.m +547 -0
- package/ios/VeLiveMixerViewManager.m +56 -0
- package/lib/commonjs/index.js +22697 -20359
- package/lib/commonjs/typescript/android/index.d.ts +44 -0
- package/lib/commonjs/typescript/codegen/android/api.d.ts +1068 -0
- package/lib/commonjs/typescript/codegen/android/callback.d.ts +333 -0
- package/lib/commonjs/typescript/codegen/android/errorcode.d.ts +92 -0
- package/lib/commonjs/typescript/codegen/android/index.d.ts +5 -0
- package/lib/commonjs/typescript/codegen/android/keytype.d.ts +1693 -0
- package/lib/commonjs/typescript/codegen/android/types.d.ts +33 -0
- package/lib/commonjs/typescript/codegen/ios/api.d.ts +1125 -0
- package/lib/commonjs/typescript/codegen/ios/callback.d.ts +242 -0
- package/lib/commonjs/typescript/codegen/ios/errorcode.d.ts +154 -0
- package/lib/commonjs/typescript/codegen/ios/external.d.ts +1 -0
- package/lib/commonjs/typescript/codegen/ios/index.d.ts +6 -0
- package/lib/commonjs/typescript/codegen/ios/keytype.d.ts +1154 -0
- package/lib/commonjs/typescript/codegen/ios/types.d.ts +46 -0
- package/lib/commonjs/typescript/codegen/pack/api.d.ts +1470 -0
- package/lib/commonjs/typescript/codegen/pack/callback.d.ts +446 -0
- package/lib/commonjs/typescript/codegen/pack/errorcode.d.ts +109 -0
- package/lib/commonjs/typescript/codegen/pack/index.d.ts +5 -0
- package/lib/commonjs/typescript/codegen/pack/keytype.d.ts +2248 -0
- package/lib/commonjs/typescript/codegen/pack/types.d.ts +68 -0
- package/lib/commonjs/typescript/codegen/type-shim.d.ts +6 -0
- package/lib/commonjs/typescript/component.d.ts +15 -0
- package/lib/commonjs/typescript/core/api.d.ts +18 -0
- package/lib/commonjs/typescript/core/callback.d.ts +2 -0
- package/lib/commonjs/typescript/core/env.d.ts +29 -0
- package/lib/commonjs/typescript/core/errorcode.d.ts +2 -0
- package/lib/commonjs/typescript/core/index.d.ts +6 -0
- package/lib/commonjs/typescript/core/keytype.d.ts +17 -0
- package/lib/commonjs/typescript/core/mixer.d.ts +26 -0
- package/lib/commonjs/typescript/core/pusher.d.ts +13 -0
- package/lib/commonjs/typescript/index.d.ts +3 -0
- package/lib/commonjs/typescript/ios/extends.d.ts +41 -0
- package/lib/commonjs/typescript/platforms/android/extends.d.ts +8 -0
- package/lib/commonjs/typescript/platforms/android/helper.d.ts +8 -0
- package/lib/commonjs/typescript/platforms/android/mixer.d.ts +8 -0
- package/lib/commonjs/typescript/platforms/ios/extends.d.ts +17 -0
- package/lib/commonjs/typescript/platforms/ios/helper.d.ts +8 -0
- package/lib/commonjs/typescript/platforms/ios/mixer.d.ts +9 -0
- package/lib/commonjs/typescript/runtime.d.ts +1 -0
- package/lib/commonjs/typescript/view/MixView.d.ts +44 -0
- package/lib/commonjs/typescript/view/VeImageView.d.ts +19 -0
- package/lib/commonjs/typescript/view/VeTextView.d.ts +7 -0
- package/lib/commonjs/typescript/view/VeView.d.ts +7 -0
- package/lib/commonjs/typescript/view/VeWebView.d.ts +7 -0
- package/lib/commonjs/typescript/view/index.d.ts +5 -0
- package/lib/module/index.js +22694 -20360
- package/lib/module/typescript/android/index.d.ts +44 -0
- package/lib/module/typescript/codegen/android/api.d.ts +1068 -0
- package/lib/module/typescript/codegen/android/callback.d.ts +333 -0
- package/lib/module/typescript/codegen/android/errorcode.d.ts +92 -0
- package/lib/module/typescript/codegen/android/index.d.ts +5 -0
- package/lib/module/typescript/codegen/android/keytype.d.ts +1693 -0
- package/lib/module/typescript/codegen/android/types.d.ts +33 -0
- package/lib/module/typescript/codegen/ios/api.d.ts +1125 -0
- package/lib/module/typescript/codegen/ios/callback.d.ts +242 -0
- package/lib/module/typescript/codegen/ios/errorcode.d.ts +154 -0
- package/lib/module/typescript/codegen/ios/external.d.ts +1 -0
- package/lib/module/typescript/codegen/ios/index.d.ts +6 -0
- package/lib/module/typescript/codegen/ios/keytype.d.ts +1154 -0
- package/lib/module/typescript/codegen/ios/types.d.ts +46 -0
- package/lib/module/typescript/codegen/pack/api.d.ts +1470 -0
- package/lib/module/typescript/codegen/pack/callback.d.ts +446 -0
- package/lib/module/typescript/codegen/pack/errorcode.d.ts +109 -0
- package/lib/module/typescript/codegen/pack/index.d.ts +5 -0
- package/lib/module/typescript/codegen/pack/keytype.d.ts +2248 -0
- package/lib/module/typescript/codegen/pack/types.d.ts +68 -0
- package/lib/module/typescript/codegen/type-shim.d.ts +6 -0
- package/lib/module/typescript/component.d.ts +15 -0
- package/lib/module/typescript/core/api.d.ts +18 -0
- package/lib/module/typescript/core/callback.d.ts +2 -0
- package/lib/module/typescript/core/env.d.ts +29 -0
- package/lib/module/typescript/core/errorcode.d.ts +2 -0
- package/lib/module/typescript/core/index.d.ts +6 -0
- package/lib/module/typescript/core/keytype.d.ts +17 -0
- package/lib/module/typescript/core/mixer.d.ts +26 -0
- package/lib/module/typescript/core/pusher.d.ts +13 -0
- package/lib/module/typescript/index.d.ts +3 -0
- package/lib/module/typescript/ios/extends.d.ts +41 -0
- package/lib/module/typescript/platforms/android/extends.d.ts +8 -0
- package/lib/module/typescript/platforms/android/helper.d.ts +8 -0
- package/lib/module/typescript/platforms/android/mixer.d.ts +8 -0
- package/lib/module/typescript/platforms/ios/extends.d.ts +17 -0
- package/lib/module/typescript/platforms/ios/helper.d.ts +8 -0
- package/lib/module/typescript/platforms/ios/mixer.d.ts +9 -0
- package/lib/module/typescript/runtime.d.ts +1 -0
- package/lib/module/typescript/view/MixView.d.ts +44 -0
- package/lib/module/typescript/view/VeImageView.d.ts +19 -0
- package/lib/module/typescript/view/VeTextView.d.ts +7 -0
- package/lib/module/typescript/view/VeView.d.ts +7 -0
- package/lib/module/typescript/view/VeWebView.d.ts +7 -0
- package/lib/module/typescript/view/index.d.ts +5 -0
- package/lib/typescript/android/index.d.ts +0 -3
- package/lib/typescript/codegen/android/api.d.ts +194 -762
- package/lib/typescript/codegen/android/callback.d.ts +85 -48
- package/lib/typescript/codegen/android/errorcode.d.ts +30 -0
- package/lib/typescript/codegen/android/keytype.d.ts +514 -122
- package/lib/typescript/codegen/ios/api.d.ts +380 -351
- package/lib/typescript/codegen/ios/callback.d.ts +33 -6
- package/lib/typescript/codegen/ios/errorcode.d.ts +52 -2
- package/lib/typescript/codegen/ios/keytype.d.ts +313 -35
- package/lib/typescript/codegen/pack/api.d.ts +302 -821
- package/lib/typescript/codegen/pack/callback.d.ts +54 -49
- package/lib/typescript/codegen/pack/errorcode.d.ts +38 -5
- package/lib/typescript/codegen/pack/keytype.d.ts +672 -228
- package/lib/typescript/core/api.d.ts +18 -2
- package/lib/typescript/core/keytype.d.ts +15 -0
- package/lib/typescript/core/mixer.d.ts +26 -0
- package/lib/typescript/core/pusher.d.ts +0 -3
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/platforms/android/extends.d.ts +8 -0
- package/lib/typescript/platforms/android/mixer.d.ts +8 -0
- package/lib/typescript/platforms/ios/mixer.d.ts +9 -0
- package/lib/typescript/view/MixView.d.ts +44 -0
- package/lib/typescript/view/VeImageView.d.ts +19 -0
- package/lib/typescript/view/VeTextView.d.ts +7 -0
- package/lib/typescript/view/VeView.d.ts +7 -0
- package/lib/typescript/view/VeWebView.d.ts +7 -0
- package/lib/typescript/view/index.d.ts +5 -0
- package/package.json +1 -1
- package/react-native-velive-push.podspec +3 -3
- package/android/src/main/java/com/volcengine/velive/rn/push/ScreenCaptureHelper.java +0 -73
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
package com.volcengine.velive.rn.push.mixer;
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap;
|
|
4
|
+
import android.graphics.Canvas;
|
|
5
|
+
import android.os.Handler;
|
|
6
|
+
import android.os.Looper;
|
|
7
|
+
import android.view.ViewTreeObserver;
|
|
8
|
+
import android.widget.FrameLayout;
|
|
9
|
+
import androidx.annotation.Nullable;
|
|
10
|
+
import com.facebook.react.bridge.Arguments;
|
|
11
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
12
|
+
import com.facebook.react.bridge.WritableMap;
|
|
13
|
+
import com.facebook.react.uimanager.ThemedReactContext;
|
|
14
|
+
import java.util.concurrent.atomic.AtomicBoolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom MixerView that supports different capture modes
|
|
18
|
+
*/
|
|
19
|
+
public class MixerView extends FrameLayout {
|
|
20
|
+
private ThemedReactContext context;
|
|
21
|
+
private String viewId;
|
|
22
|
+
|
|
23
|
+
// Capture configuration
|
|
24
|
+
private String captureMode = "onchange";
|
|
25
|
+
private float captureFramerate = 5f; // realTime mode default 5 fps
|
|
26
|
+
|
|
27
|
+
// Capture state
|
|
28
|
+
private Handler captureHandler;
|
|
29
|
+
private Runnable captureRunnable;
|
|
30
|
+
private ViewTreeObserver.OnGlobalLayoutListener layoutListener;
|
|
31
|
+
private AtomicBoolean isCapturing = new AtomicBoolean(false);
|
|
32
|
+
private AtomicBoolean isDestroyed = new AtomicBoolean(false);
|
|
33
|
+
|
|
34
|
+
// Performance optimization
|
|
35
|
+
private Bitmap lastBitmap;
|
|
36
|
+
private long lastCaptureTime = 0;
|
|
37
|
+
private final Object captureLock = new Object();
|
|
38
|
+
|
|
39
|
+
// Add callback support for MixerManager integration
|
|
40
|
+
private MixerManager.BitmapCaptureCallback bitmapCaptureCallback;
|
|
41
|
+
|
|
42
|
+
public MixerView(ThemedReactContext context) {
|
|
43
|
+
super(context);
|
|
44
|
+
this.context = context;
|
|
45
|
+
this.captureHandler = new Handler(Looper.getMainLooper());
|
|
46
|
+
initializeCapture();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private void initializeCapture() {
|
|
50
|
+
// Initialize layout change listener
|
|
51
|
+
layoutListener = this::onViewChanged;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public void setCaptureMode(String mode) {
|
|
55
|
+
if (mode.equals(this.captureMode))
|
|
56
|
+
return;
|
|
57
|
+
|
|
58
|
+
stopCapture();
|
|
59
|
+
this.captureMode = mode;
|
|
60
|
+
startCapture();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public void setCaptureFramerate(float framerate) {
|
|
64
|
+
this.captureFramerate = Math.max(1f, Math.min(60f, framerate));
|
|
65
|
+
if ("realtime".equals(captureMode)) {
|
|
66
|
+
restartRealtimeCapture();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private void startCapture() {
|
|
71
|
+
if (isDestroyed.get())
|
|
72
|
+
return;
|
|
73
|
+
|
|
74
|
+
switch (captureMode) {
|
|
75
|
+
case "onchange":
|
|
76
|
+
startOnChangeCapture();
|
|
77
|
+
break;
|
|
78
|
+
case "realtime":
|
|
79
|
+
startRealtimeCapture();
|
|
80
|
+
break;
|
|
81
|
+
case "manual":
|
|
82
|
+
// Manual mode doesn't auto-start
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private void stopCapture() {
|
|
88
|
+
// Remove listeners
|
|
89
|
+
if (getViewTreeObserver().isAlive()) {
|
|
90
|
+
if (layoutListener != null) {
|
|
91
|
+
getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Stop realtime capture
|
|
96
|
+
if (captureRunnable != null) {
|
|
97
|
+
captureHandler.removeCallbacks(captureRunnable);
|
|
98
|
+
captureRunnable = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
isCapturing.set(false);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private void startOnChangeCapture() {
|
|
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);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private void startRealtimeCapture() {
|
|
121
|
+
long interval = (long)(1000f / captureFramerate);
|
|
122
|
+
|
|
123
|
+
captureRunnable = new Runnable() {
|
|
124
|
+
@Override
|
|
125
|
+
public void run() {
|
|
126
|
+
if (!isDestroyed.get() && "realtime".equals(captureMode)) {
|
|
127
|
+
performCapture("realtime");
|
|
128
|
+
captureHandler.postDelayed(this, interval);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
captureHandler.postDelayed(captureRunnable, interval);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private void restartRealtimeCapture() {
|
|
136
|
+
if ("realtime".equals(captureMode)) {
|
|
137
|
+
stopCapture();
|
|
138
|
+
startRealtimeCapture();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private void onViewChanged() {
|
|
143
|
+
if (isDestroyed.get()) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if ("onchange".equals(captureMode)) {
|
|
148
|
+
performCapture("onchange");
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private void performCapture(String trigger) {
|
|
153
|
+
if (isCapturing.get() || isDestroyed.get()) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
long currentTime = System.currentTimeMillis();
|
|
158
|
+
long minInterval = 33; // 30 FPS ~ 33ms per frame
|
|
159
|
+
if (currentTime - lastCaptureTime < minInterval) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
lastCaptureTime = currentTime;
|
|
163
|
+
|
|
164
|
+
synchronized (captureLock) {
|
|
165
|
+
if (isCapturing.compareAndSet(false, true)) {
|
|
166
|
+
Bitmap bitmap = null;
|
|
167
|
+
try {
|
|
168
|
+
bitmap = createBitmap();
|
|
169
|
+
if (bitmap != null) {
|
|
170
|
+
processBitmap(bitmap, trigger);
|
|
171
|
+
}
|
|
172
|
+
} catch (OutOfMemoryError e) {
|
|
173
|
+
System.gc();
|
|
174
|
+
// If an OOM occurs and the bitmap has not been passed to
|
|
175
|
+
// processBitmap, recycle it
|
|
176
|
+
if (bitmap != null && !bitmap.isRecycled()) {
|
|
177
|
+
bitmap.recycle();
|
|
178
|
+
}
|
|
179
|
+
} catch (Exception e) {
|
|
180
|
+
// If any other exception occurs and the bitmap has not been passed to
|
|
181
|
+
// processBitmap, recycle it
|
|
182
|
+
if (bitmap != null && !bitmap.isRecycled()) {
|
|
183
|
+
bitmap.recycle();
|
|
184
|
+
}
|
|
185
|
+
} finally {
|
|
186
|
+
isCapturing.set(false);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private Bitmap createBitmap() {
|
|
193
|
+
if (getWidth() <= 0 || getHeight() <= 0)
|
|
194
|
+
return null;
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
Bitmap bitmap =
|
|
198
|
+
Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
|
|
199
|
+
Canvas canvas = new Canvas(bitmap);
|
|
200
|
+
draw(canvas);
|
|
201
|
+
return bitmap;
|
|
202
|
+
} catch (OutOfMemoryError e) {
|
|
203
|
+
// Try with lower quality
|
|
204
|
+
try {
|
|
205
|
+
Bitmap bitmap =
|
|
206
|
+
Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
|
|
207
|
+
Canvas canvas = new Canvas(bitmap);
|
|
208
|
+
draw(canvas);
|
|
209
|
+
return bitmap;
|
|
210
|
+
} catch (Exception ex) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private void processBitmap(Bitmap bitmap, String trigger) {
|
|
217
|
+
if (bitmap == null || bitmap.isRecycled()) {
|
|
218
|
+
if (bitmapCaptureCallback != null) {
|
|
219
|
+
bitmapCaptureCallback.onCaptureError("Bitmap is null or recycled");
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Call callback first if set (for MixerManager)
|
|
225
|
+
if (bitmapCaptureCallback != null) {
|
|
226
|
+
try {
|
|
227
|
+
// only capture if the callback is set
|
|
228
|
+
bitmapCaptureCallback.onBitmapCaptured(bitmap);
|
|
229
|
+
} catch (Exception e) {
|
|
230
|
+
if (bitmapCaptureCallback != null) {
|
|
231
|
+
bitmapCaptureCallback.onCaptureError("Callback error: " +
|
|
232
|
+
e.getMessage());
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
// Send event to JS (existing functionality)
|
|
237
|
+
try {
|
|
238
|
+
WritableMap event = Arguments.createMap();
|
|
239
|
+
event.putString("trigger", trigger);
|
|
240
|
+
event.putBoolean("success", true);
|
|
241
|
+
event.putInt("bitmapWidth", bitmap.getWidth());
|
|
242
|
+
event.putInt("bitmapHeight", bitmap.getHeight());
|
|
243
|
+
} catch (IllegalStateException e) {
|
|
244
|
+
// Ignore
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Clean up old bitmap
|
|
249
|
+
if (lastBitmap != null && !lastBitmap.isRecycled()) {
|
|
250
|
+
lastBitmap.recycle();
|
|
251
|
+
}
|
|
252
|
+
|
|
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
|
|
255
|
+
if (bitmapCaptureCallback == null) {
|
|
256
|
+
if (bitmap != null && !bitmap.isRecycled()) {
|
|
257
|
+
bitmap.recycle();
|
|
258
|
+
}
|
|
259
|
+
lastBitmap = null;
|
|
260
|
+
} else {
|
|
261
|
+
lastBitmap = bitmap;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public void handleCommand(String command, @Nullable ReadableArray args) {
|
|
266
|
+
switch (command) {
|
|
267
|
+
case "capture":
|
|
268
|
+
performCapture("manual");
|
|
269
|
+
break;
|
|
270
|
+
case "startCapture":
|
|
271
|
+
startCapture();
|
|
272
|
+
break;
|
|
273
|
+
case "stopCapture":
|
|
274
|
+
stopCapture();
|
|
275
|
+
break;
|
|
276
|
+
case "setCaptureMode":
|
|
277
|
+
if (args != null && args.size() > 0) {
|
|
278
|
+
setCaptureMode(args.getString(0));
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@Override
|
|
285
|
+
protected void onAttachedToWindow() {
|
|
286
|
+
super.onAttachedToWindow();
|
|
287
|
+
if (viewId != null) {
|
|
288
|
+
MixerManager.cachedMixedViews.put(viewId, this);
|
|
289
|
+
// Notify MixerManager that view is ready, check for pending callbacks
|
|
290
|
+
MixerManager.onViewReady(viewId, this);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
@Override
|
|
295
|
+
protected void onDetachedFromWindow() {
|
|
296
|
+
super.onDetachedFromWindow();
|
|
297
|
+
if (viewId != null) {
|
|
298
|
+
MixerManager.cachedMixedViews.remove(viewId);
|
|
299
|
+
}
|
|
300
|
+
cleanup();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
public void setViewId(String viewId) {
|
|
304
|
+
this.viewId = viewId;
|
|
305
|
+
MixerManager.cachedMixedViews.put(viewId, this);
|
|
306
|
+
// Notify MixerManager that view is ready, check for pending callbacks
|
|
307
|
+
MixerManager.onViewReady(viewId, this);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Add callback support for MixerManager integration
|
|
311
|
+
public void
|
|
312
|
+
setBitmapCaptureCallback(MixerManager.BitmapCaptureCallback callback) {
|
|
313
|
+
this.bitmapCaptureCallback = callback;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Public method for manual capture (called by MixerManager)
|
|
317
|
+
public void performManualCapture() { performCapture("manual"); }
|
|
318
|
+
|
|
319
|
+
private void cleanup() {
|
|
320
|
+
isDestroyed.set(true);
|
|
321
|
+
stopCapture();
|
|
322
|
+
|
|
323
|
+
if (lastBitmap != null && !lastBitmap.isRecycled()) {
|
|
324
|
+
lastBitmap.recycle();
|
|
325
|
+
lastBitmap = null;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.volcengine.velive.rn.push.mixer;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.facebook.react.uimanager.ViewGroupManager;
|
|
6
|
+
import com.facebook.react.uimanager.ThemedReactContext;
|
|
7
|
+
import com.facebook.react.uimanager.annotations.ReactProp;
|
|
8
|
+
|
|
9
|
+
public class MixerViewManager extends ViewGroupManager<MixerView> {
|
|
10
|
+
public static final String NAME = "VeLiveMixView";
|
|
11
|
+
|
|
12
|
+
@NonNull
|
|
13
|
+
@Override
|
|
14
|
+
public String getName() {
|
|
15
|
+
return NAME;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@NonNull
|
|
19
|
+
@Override
|
|
20
|
+
protected MixerView
|
|
21
|
+
createViewInstance(@NonNull ThemedReactContext reactContext) {
|
|
22
|
+
return new MixerView(reactContext);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@ReactProp(name = "captureMode")
|
|
26
|
+
public void setCaptureMode(MixerView view, @Nullable String captureMode) {
|
|
27
|
+
view.setCaptureMode(captureMode != null ? captureMode : "onchange");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@ReactProp(name = "captureFramerate", defaultFloat = 5f)
|
|
31
|
+
public void setCaptureFramerate(MixerView view, float framerate) {
|
|
32
|
+
view.setCaptureFramerate(framerate);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@ReactProp(name = "viewId")
|
|
36
|
+
public void setViewId(MixerView view, @Nullable String viewId) {
|
|
37
|
+
if (viewId != null) {
|
|
38
|
+
view.setViewId(viewId);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
package com.volcengine.velive.rn.push.mixer;
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap;
|
|
4
|
+
import android.opengl.GLES20;
|
|
5
|
+
import android.opengl.GLUtils;
|
|
6
|
+
|
|
7
|
+
import com.pandora.common.env.Env;
|
|
8
|
+
import com.ss.avframework.opengl.GLThreadManager;
|
|
9
|
+
import com.ss.avframework.opengl.GlUtil;
|
|
10
|
+
|
|
11
|
+
import java.nio.ByteBuffer;
|
|
12
|
+
|
|
13
|
+
public class TextureMgr {
|
|
14
|
+
private int texture;
|
|
15
|
+
private int width;
|
|
16
|
+
private int height;
|
|
17
|
+
public TextureMgr(int width, int height) {
|
|
18
|
+
this.width = width;
|
|
19
|
+
this.height = height;
|
|
20
|
+
GLThreadManager.getMainGlHandle().post(() -> {
|
|
21
|
+
if (texture <= 0) {
|
|
22
|
+
texture = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
|
|
23
|
+
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
|
24
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
|
|
25
|
+
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
|
|
26
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
27
|
+
GLES20.glFinish();
|
|
28
|
+
GlUtil.checkNoGLES2Error("clearBackgroundTex");
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public void cleanup() {
|
|
34
|
+
GLThreadManager.getMainGlHandle().post(() -> {
|
|
35
|
+
if (texture > 0) {
|
|
36
|
+
GLES20.glDeleteTextures(1, new int[]{texture}, 0);
|
|
37
|
+
texture = 0;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public interface RenderListener {
|
|
43
|
+
void doBusiness(int texture);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public void dealWithTexture(ByteBuffer byteBuffer, RenderListener listener) {
|
|
47
|
+
GLThreadManager.getMainGlHandle().post(new Runnable() {
|
|
48
|
+
@Override
|
|
49
|
+
public void run() {
|
|
50
|
+
if (texture > 0) {
|
|
51
|
+
YuvHelper.NV21ToBitmap bm = null;
|
|
52
|
+
Bitmap bmp = null;
|
|
53
|
+
Bitmap rotatedBmp = null;
|
|
54
|
+
try {
|
|
55
|
+
bm = new YuvHelper.NV21ToBitmap(Env.getApplicationContext());
|
|
56
|
+
bmp = bm.nv21ToBitmap(byteBuffer.array(), width, height);
|
|
57
|
+
|
|
58
|
+
if (bmp == null || bmp.isRecycled()) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
|
|
63
|
+
rotatedBmp = YuvHelper.rotateBitmap(bmp, 0, false, true);
|
|
64
|
+
|
|
65
|
+
if (rotatedBmp != null && !rotatedBmp.isRecycled()) {
|
|
66
|
+
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, rotatedBmp, 0);
|
|
67
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
68
|
+
GLES20.glFlush();
|
|
69
|
+
listener.doBusiness(texture);
|
|
70
|
+
} else {
|
|
71
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
72
|
+
}
|
|
73
|
+
} catch (OutOfMemoryError e) {
|
|
74
|
+
System.gc();
|
|
75
|
+
} catch (Exception e) {
|
|
76
|
+
// Ignore
|
|
77
|
+
} finally {
|
|
78
|
+
if (bmp != null && !bmp.isRecycled()) {
|
|
79
|
+
bmp.recycle();
|
|
80
|
+
}
|
|
81
|
+
if (rotatedBmp != null && rotatedBmp != bmp && !rotatedBmp.isRecycled()) {
|
|
82
|
+
rotatedBmp.recycle();
|
|
83
|
+
}
|
|
84
|
+
if (bm != null) {
|
|
85
|
+
bm.cleanup();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public void dealI420WithTexture(ByteBuffer byteBuffer, RenderListener listener) {
|
|
94
|
+
GLThreadManager.getMainGlHandle().post(new Runnable() {
|
|
95
|
+
@Override
|
|
96
|
+
public void run() {
|
|
97
|
+
if (texture > 0) {
|
|
98
|
+
Bitmap bm = null;
|
|
99
|
+
Bitmap rotatedBm = null;
|
|
100
|
+
try {
|
|
101
|
+
bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
102
|
+
bm.copyPixelsFromBuffer(byteBuffer);
|
|
103
|
+
|
|
104
|
+
if (bm.isRecycled()) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
|
|
109
|
+
rotatedBm = YuvHelper.rotateBitmap(bm, 0, false, true);
|
|
110
|
+
|
|
111
|
+
if (rotatedBm != null && !rotatedBm.isRecycled()) {
|
|
112
|
+
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, rotatedBm, 0);
|
|
113
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
114
|
+
GLES20.glFlush();
|
|
115
|
+
listener.doBusiness(texture);
|
|
116
|
+
} else {
|
|
117
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
118
|
+
}
|
|
119
|
+
} catch (OutOfMemoryError e) {
|
|
120
|
+
System.gc();
|
|
121
|
+
} catch (Exception e) {
|
|
122
|
+
// Ignore
|
|
123
|
+
} finally {
|
|
124
|
+
if (bm != null && !bm.isRecycled()) {
|
|
125
|
+
bm.recycle();
|
|
126
|
+
}
|
|
127
|
+
if (rotatedBm != null && rotatedBm != bm && !rotatedBm.isRecycled()) {
|
|
128
|
+
rotatedBm.recycle();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public void dealWithTexture(Bitmap bitmap, RenderListener listener) {
|
|
137
|
+
GLThreadManager.getMainGlHandle().post(() -> {
|
|
138
|
+
if (texture > 0) {
|
|
139
|
+
Bitmap processedBitmap = null;
|
|
140
|
+
boolean needRecycle = false;
|
|
141
|
+
try {
|
|
142
|
+
if (bitmap == null || bitmap.isRecycled()) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
|
|
146
|
+
processedBitmap = YuvHelper.rotateBitmap(bitmap, 0, false, true);
|
|
147
|
+
needRecycle = (processedBitmap != null && processedBitmap != bitmap);
|
|
148
|
+
if (processedBitmap != null && !processedBitmap.isRecycled()) {
|
|
149
|
+
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, processedBitmap, 0);
|
|
150
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
151
|
+
GLES20.glFlush();
|
|
152
|
+
listener.doBusiness(texture);
|
|
153
|
+
} else {
|
|
154
|
+
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
|
155
|
+
}
|
|
156
|
+
} catch (OutOfMemoryError e) {
|
|
157
|
+
System.gc();
|
|
158
|
+
} catch (Exception e) {
|
|
159
|
+
// Ignore
|
|
160
|
+
} finally {
|
|
161
|
+
if (needRecycle && processedBitmap != null && !processedBitmap.isRecycled()) {
|
|
162
|
+
processedBitmap.recycle();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
package com.volcengine.velive.rn.push.mixer;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.graphics.Bitmap;
|
|
5
|
+
import android.graphics.Matrix;
|
|
6
|
+
import android.renderscript.Allocation;
|
|
7
|
+
import android.renderscript.Element;
|
|
8
|
+
import android.renderscript.RenderScript;
|
|
9
|
+
import android.renderscript.ScriptIntrinsicYuvToRGB;
|
|
10
|
+
import android.renderscript.Type;
|
|
11
|
+
|
|
12
|
+
import java.nio.ByteBuffer;
|
|
13
|
+
|
|
14
|
+
public class YuvHelper {
|
|
15
|
+
private static RenderScript globalRS = null;
|
|
16
|
+
public static synchronized RenderScript getGlobalRS(Context context) {
|
|
17
|
+
if (globalRS == null) {
|
|
18
|
+
globalRS = RenderScript.create(context.getApplicationContext());
|
|
19
|
+
}
|
|
20
|
+
return globalRS;
|
|
21
|
+
}
|
|
22
|
+
public static void destroyGlobalRS() {
|
|
23
|
+
if (globalRS != null) {
|
|
24
|
+
globalRS.destroy();
|
|
25
|
+
globalRS = null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
public static class NV21ToBitmap {
|
|
29
|
+
private RenderScript rs;
|
|
30
|
+
private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
|
|
31
|
+
private Type.Builder yuvType, rgbaType;
|
|
32
|
+
|
|
33
|
+
public NV21ToBitmap(Context context) {
|
|
34
|
+
rs = YuvHelper.getGlobalRS(context);
|
|
35
|
+
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
|
|
36
|
+
yuvType = new Type.Builder(rs, Element.U8(rs));
|
|
37
|
+
rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public void cleanup() {
|
|
41
|
+
try {
|
|
42
|
+
if (yuvToRgbIntrinsic != null) {
|
|
43
|
+
yuvToRgbIntrinsic.destroy();
|
|
44
|
+
yuvToRgbIntrinsic = null;
|
|
45
|
+
}
|
|
46
|
+
// 不销毁全局 rs
|
|
47
|
+
// rs = null;
|
|
48
|
+
} catch (Exception e) {
|
|
49
|
+
// Ignore
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
|
|
54
|
+
Allocation in = null;
|
|
55
|
+
Allocation out = null;
|
|
56
|
+
try {
|
|
57
|
+
yuvType.setX(nv21.length);
|
|
58
|
+
in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
|
|
59
|
+
in.copyFrom(nv21);
|
|
60
|
+
|
|
61
|
+
rgbaType.setX(width).setY(height);
|
|
62
|
+
out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
|
|
63
|
+
yuvToRgbIntrinsic.setInput(in);
|
|
64
|
+
yuvToRgbIntrinsic.forEach(out);
|
|
65
|
+
|
|
66
|
+
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
67
|
+
out.copyTo(bmp);
|
|
68
|
+
|
|
69
|
+
return bmp;
|
|
70
|
+
} finally {
|
|
71
|
+
if (in != null) {
|
|
72
|
+
in.destroy();
|
|
73
|
+
}
|
|
74
|
+
if (out != null) {
|
|
75
|
+
out.destroy();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public static void NV21toI420(ByteBuffer input, ByteBuffer output, final int width, final int height) {
|
|
82
|
+
final int size = width * height;
|
|
83
|
+
final int quarter = size / 4;
|
|
84
|
+
final int v0 = size + quarter;
|
|
85
|
+
|
|
86
|
+
System.arraycopy(input.array(), 0, output.array(), 0, size);
|
|
87
|
+
|
|
88
|
+
for (int u = size, v = v0, o = size; u < v0; u++, v++, o += 2) {
|
|
89
|
+
output.array()[v] = input.array()[o];
|
|
90
|
+
output.array()[u] = input.array()[o + 1];
|
|
91
|
+
}
|
|
92
|
+
output.limit(width * height * 3 / 2);
|
|
93
|
+
output.position(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public static void NV21toNV12(ByteBuffer input, ByteBuffer output, final int width, final int height) {
|
|
97
|
+
final int size = width * height;
|
|
98
|
+
|
|
99
|
+
System.arraycopy(input.array(), 0, output.array(), 0, size);
|
|
100
|
+
for (int i = size; i < 1.5f * size; i += 2) {
|
|
101
|
+
output.array()[i] = input.array()[i + 1];
|
|
102
|
+
output.array()[i + 1] = input.array()[i];
|
|
103
|
+
}
|
|
104
|
+
output.position(0);
|
|
105
|
+
output.limit(width * height * 3 / 2);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public static void NV12toI420(ByteBuffer input, ByteBuffer output, final int width, final int height) {
|
|
109
|
+
final int size = width * height;
|
|
110
|
+
final int quarter = size / 4;
|
|
111
|
+
final int v0 = size + quarter;
|
|
112
|
+
|
|
113
|
+
System.arraycopy(input.array(), 0, output.array(), 0, size);
|
|
114
|
+
|
|
115
|
+
for (int u = size, v = v0, o = size; u < v0; u++, v++, o += 2) {
|
|
116
|
+
output.array()[v] = input.array()[o + 1];
|
|
117
|
+
output.array()[u] = input.array()[o];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public static void I420toNV21(ByteBuffer input, ByteBuffer output, final int width, final int height) {
|
|
122
|
+
final int size = width * height;
|
|
123
|
+
final int quarter = size / 4;
|
|
124
|
+
final int v0 = size + quarter;
|
|
125
|
+
|
|
126
|
+
System.arraycopy(input.array(), 0, output.array(), 0, size);
|
|
127
|
+
|
|
128
|
+
for (int u = size, v = v0, o = size; u < v0; u++, v++, o += 2) {
|
|
129
|
+
output.array()[o + 1] = input.array()[v];
|
|
130
|
+
output.array()[o] = input.array()[u];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public static Bitmap rotateBitmap(Bitmap origin, float alpha, boolean horizontalMirror, boolean verticalMirror) {
|
|
135
|
+
if (origin == null || origin.isRecycled()) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
int width = origin.getWidth();
|
|
141
|
+
int height = origin.getHeight();
|
|
142
|
+
Matrix matrix = new Matrix();
|
|
143
|
+
float sx = 1;
|
|
144
|
+
float sy = 1;
|
|
145
|
+
if (horizontalMirror) {
|
|
146
|
+
sx = -1;
|
|
147
|
+
}
|
|
148
|
+
if (verticalMirror) {
|
|
149
|
+
sy = -1;
|
|
150
|
+
}
|
|
151
|
+
matrix.postScale(sx, sy);
|
|
152
|
+
matrix.postRotate(alpha);
|
|
153
|
+
|
|
154
|
+
if (origin.isRecycled()) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
|
|
159
|
+
} catch (IllegalStateException e) {
|
|
160
|
+
return null;
|
|
161
|
+
} catch (Exception e) {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|