@byteplus/react-native-live-push 1.1.3-rc.2 → 1.1.3-rc.4

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.
@@ -4,34 +4,31 @@ import android.graphics.Bitmap;
4
4
  import android.util.Log;
5
5
  import android.view.View;
6
6
  import android.view.ViewTreeObserver;
7
-
8
7
  import com.ss.avframework.live.VeLivePusher;
9
8
  import com.ss.avframework.live.VeLivePusherDef.VeLiveMixVideoLayout;
10
- import com.ss.avframework.live.VeLivePusherDef.VeLiveStreamMixDescription;
11
9
  import com.ss.avframework.live.VeLivePusherDef.VeLivePusherRenderMode;
10
+ import com.ss.avframework.live.VeLivePusherDef.VeLiveStreamMixDescription;
12
11
  import com.ss.avframework.live.VeLiveVideoFrame;
13
-
14
- import org.json.JSONObject;
15
-
16
12
  import java.util.HashMap;
17
13
  import java.util.Map;
18
14
  import java.util.concurrent.ConcurrentHashMap;
19
-
20
15
  import javax.annotation.Nullable;
16
+ import org.json.JSONObject;
21
17
 
22
18
  public class MixerManager {
23
- public static Map<String, View> cachedMixedViews = new HashMap<>();
19
+ public static Map<String, View> cachedMixedViews = new HashMap<>();
24
20
 
25
21
  private static final String TAG = "MixerManager";
26
22
 
27
23
  private VeLivePusher pusher;
28
- private final Map<String, VeLiveMixVideoLayout> mixedViewLayout = new ConcurrentHashMap<>();
29
- private final Map<String, Integer> viewIdToStreamId = new ConcurrentHashMap<>();
30
- private final Map<String, Integer> pendingCallbacks = new ConcurrentHashMap<>();
31
-
32
- public void setPusher(VeLivePusher pusher) {
33
- this.pusher = pusher;
34
- }
24
+ private final Map<String, VeLiveMixVideoLayout> mixedViewLayout =
25
+ new ConcurrentHashMap<>();
26
+ private final Map<String, Integer> viewIdToStreamId =
27
+ new ConcurrentHashMap<>();
28
+ private final Map<String, Integer> pendingCallbacks =
29
+ new ConcurrentHashMap<>();
30
+
31
+ public void setPusher(VeLivePusher pusher) { this.pusher = pusher; }
35
32
 
36
33
  /**
37
34
  * Add a view to the mixer
@@ -40,85 +37,81 @@ public class MixerManager {
40
37
  * @return streamId for this view
41
38
  */
42
39
  public int addView(String viewId, JSONObject viewInfo) {
43
- try {
44
- if (pusher == null || pusher.getMixerManager() == null) {
45
- return -1;
46
- }
47
-
48
- if (viewIdToStreamId.containsKey(viewId)) {
49
- return viewIdToStreamId.get(viewId);
50
- }
51
-
52
- int videoStreamId = pusher.getMixerManager().addVideoStream();
53
- VeLiveStreamMixDescription description = new VeLiveStreamMixDescription();
54
- VeLiveMixVideoLayout layout = new VeLiveMixVideoLayout();
55
- layout.x = (float) viewInfo.getDouble("x");
56
- layout.y = (float) viewInfo.getDouble("y");
57
- layout.width = (float) viewInfo.getDouble("width");
58
- layout.height = (float) viewInfo.getDouble("height");
59
- layout.zOrder = viewInfo.optInt("zOrder", 0);
60
- int renderModeValue = viewInfo.optInt("renderMode", 0);
61
- VeLivePusherRenderMode[] renderModes = VeLivePusherRenderMode.values();
62
- if (renderModeValue >= 0 && renderModeValue < renderModes.length) {
63
- layout.renderMode = renderModes[renderModeValue];
64
- } else {
65
- layout.renderMode = VeLivePusherRenderMode.values()[0];
66
- }
67
- layout.streamId = videoStreamId;
68
- description.mixVideoStreams.add(layout);
69
- mixedViewLayout.put(viewId, layout);
70
- viewIdToStreamId.put(viewId, videoStreamId);
71
- pusher.getMixerManager().updateStreamMixDescription(description);
72
-
73
- View view = cachedMixedViews.get(viewId);
74
- if (view instanceof MixerView) {
75
- MixerView mixerView = (MixerView) view;
76
- setupCallbackForMixerView(mixerView, videoStreamId);
77
-
78
- android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
79
- mainHandler.postDelayed(new Runnable() {
80
- @Override
81
- public void run() {
82
- try {
83
- VeLiveMixVideoLayout layout = mixedViewLayout.get(viewId);
84
- if (layout != null && pusher != null && pusher.getMixerManager() != null) {
85
- VeLiveStreamMixDescription description = new VeLiveStreamMixDescription();
86
- description.mixVideoStreams.add(layout);
87
- pusher.getMixerManager().updateStreamMixDescription(description);
88
- }
89
- mixerView.performManualCapture();
90
- } catch (Exception e) {
91
- // Ignore
92
- }
93
- }
94
- }, 300);
95
-
96
- } else {
97
- pendingCallbacks.put(viewId, videoStreamId);
98
- }
99
-
100
- return videoStreamId;
101
- } catch (Exception e) {
102
- return -1;
103
- }
40
+ try {
41
+ if (pusher == null || pusher.getMixerManager() == null) {
42
+ return -1;
43
+ }
44
+
45
+ if (viewIdToStreamId.containsKey(viewId)) {
46
+ return viewIdToStreamId.get(viewId);
47
+ }
48
+
49
+ int videoStreamId = pusher.getMixerManager().addVideoStream();
50
+ VeLiveStreamMixDescription description = new VeLiveStreamMixDescription();
51
+ VeLiveMixVideoLayout layout = new VeLiveMixVideoLayout();
52
+ layout.x = (float)viewInfo.getDouble("x");
53
+ layout.y = (float)viewInfo.getDouble("y");
54
+ layout.width = (float)viewInfo.getDouble("width");
55
+ layout.height = (float)viewInfo.getDouble("height");
56
+ layout.alpha = (float)viewInfo.optDouble("alpha", 1.0);
57
+ layout.zOrder = viewInfo.optInt("zOrder", 0);
58
+ int renderModeValue = viewInfo.optInt("renderMode", 0);
59
+ VeLivePusherRenderMode[] renderModes = VeLivePusherRenderMode.values();
60
+ if (renderModeValue >= 0 && renderModeValue < renderModes.length) {
61
+ layout.renderMode = renderModes[renderModeValue];
62
+ } else {
63
+ layout.renderMode = VeLivePusherRenderMode.values()[0];
64
+ }
65
+ layout.streamId = videoStreamId;
66
+ description.mixVideoStreams.add(layout);
67
+ mixedViewLayout.put(viewId, layout);
68
+ viewIdToStreamId.put(viewId, videoStreamId);
69
+ pusher.getMixerManager().updateStreamMixDescription(description);
70
+
71
+ View view = cachedMixedViews.get(viewId);
72
+ if (view instanceof MixerView) {
73
+ MixerView mixerView = (MixerView)view;
74
+ setupCallbackForMixerView(mixerView, videoStreamId);
75
+
76
+ android.os.Handler mainHandler =
77
+ new android.os.Handler(android.os.Looper.getMainLooper());
78
+ mainHandler.postDelayed(new Runnable() {
79
+ @Override
80
+ public void run() {
81
+ try {
82
+ mixerView.performManualCapture();
83
+ } catch (Exception e) {
84
+ Log.w(TAG, "Failed to perform manual capture after addView", e);
85
+ }
86
+ }
87
+ }, 300);
88
+ } else {
89
+ pendingCallbacks.put(viewId, videoStreamId);
90
+ }
91
+
92
+ return videoStreamId;
93
+ } catch (Exception e) {
94
+ return -1;
95
+ }
104
96
  }
105
-
97
+
106
98
  /**
107
99
  * Update view configuration
108
100
  */
109
101
  public boolean updateView(String viewId, JSONObject viewInfo) {
110
102
  try {
111
103
  VeLiveMixVideoLayout layout = mixedViewLayout.get(viewId);
112
- if(layout == null) {
104
+ if (layout == null) {
113
105
  return false;
114
106
  }
115
107
  VeLiveStreamMixDescription description = new VeLiveStreamMixDescription();
116
- layout.x = (float) viewInfo.getDouble("x");
117
- layout.y = (float) viewInfo.getDouble("y");
118
- layout.width = (float) viewInfo.getDouble("width");
119
- layout.height = (float) viewInfo.getDouble("height");
108
+ layout.x = (float)viewInfo.getDouble("x");
109
+ layout.y = (float)viewInfo.getDouble("y");
110
+ layout.width = (float)viewInfo.getDouble("width");
111
+ layout.height = (float)viewInfo.getDouble("height");
112
+ layout.alpha = (float)viewInfo.optDouble("alpha", 1.0);
120
113
  Integer streamId = viewIdToStreamId.get(viewId);
121
- if(streamId != null) {
114
+ if (streamId != null) {
122
115
  layout.streamId = streamId;
123
116
  }
124
117
  if (viewInfo.has("zOrder")) {
@@ -134,10 +127,22 @@ public class MixerManager {
134
127
  mixedViewLayout.put(viewId, layout);
135
128
  description.mixVideoStreams.add(layout);
136
129
  pusher.getMixerManager().updateStreamMixDescription(description);
130
+
137
131
  View view = cachedMixedViews.get(viewId);
138
132
  if (view instanceof MixerView) {
139
- MixerView mixerView = (MixerView) view;
140
- mixerView.performManualCapture();
133
+ MixerView mixerView = (MixerView)view;
134
+ android.os.Handler mainHandler =
135
+ new android.os.Handler(android.os.Looper.getMainLooper());
136
+ mainHandler.postDelayed(new Runnable() {
137
+ @Override
138
+ public void run() {
139
+ try {
140
+ mixerView.performManualCapture();
141
+ } catch (Exception e) {
142
+ Log.w(TAG, "Failed to perform manual capture after update", e);
143
+ }
144
+ }
145
+ }, 300);
141
146
  }
142
147
  return true;
143
148
  } catch (Exception e) {
@@ -154,15 +159,15 @@ public class MixerManager {
154
159
  if (layout == null) {
155
160
  return false;
156
161
  }
157
-
162
+
158
163
  viewIdToStreamId.remove(viewId);
159
-
164
+
160
165
  View view = cachedMixedViews.get(viewId);
161
166
  if (view instanceof MixerView) {
162
- MixerView mixerView = (MixerView) view;
167
+ MixerView mixerView = (MixerView)view;
163
168
  mixerView.setBitmapCaptureCallback(null);
164
169
  }
165
-
170
+
166
171
  pusher.getMixerManager().removeVideoStream(layout.streamId);
167
172
  return true;
168
173
 
@@ -177,7 +182,7 @@ public class MixerManager {
177
182
  public void captureView(String viewId) {
178
183
  View view = cachedMixedViews.get(viewId);
179
184
  if (view instanceof MixerView) {
180
- MixerView mixerView = (MixerView) view;
185
+ MixerView mixerView = (MixerView)view;
181
186
  mixerView.performManualCapture();
182
187
  }
183
188
  }
@@ -192,28 +197,33 @@ public class MixerManager {
192
197
  Integer streamId = instance.pendingCallbacks.remove(viewId);
193
198
  if (streamId != null) {
194
199
  setupCallbackForMixerView(mixerView, streamId);
195
-
196
- android.os.Handler mainHandler = new android.os.Handler(android.os.Looper.getMainLooper());
200
+
201
+ android.os.Handler mainHandler =
202
+ new android.os.Handler(android.os.Looper.getMainLooper());
197
203
  mainHandler.postDelayed(new Runnable() {
198
204
  @Override
199
205
  public void run() {
200
206
  try {
201
- VeLiveMixVideoLayout layout = instance.mixedViewLayout.get(viewId);
202
- if (layout != null && instance.pusher != null && instance.pusher.getMixerManager() != null) {
203
- VeLiveStreamMixDescription description = new VeLiveStreamMixDescription();
207
+ VeLiveMixVideoLayout layout =
208
+ instance.mixedViewLayout.get(viewId);
209
+ if (layout != null && instance.pusher != null &&
210
+ instance.pusher.getMixerManager() != null) {
211
+ VeLiveStreamMixDescription description =
212
+ new VeLiveStreamMixDescription();
204
213
  description.mixVideoStreams.add(layout);
205
- instance.pusher.getMixerManager().updateStreamMixDescription(description);
214
+ instance.pusher.getMixerManager().updateStreamMixDescription(
215
+ description);
206
216
  }
207
217
  mixerView.performManualCapture();
208
218
  } catch (Exception e) {
209
- // Ignore
219
+ Log.w(TAG, "Failed to perform operations in onViewReady", e);
210
220
  }
211
221
  }
212
- }, 1000);
222
+ }, 300);
213
223
  }
214
224
  }
215
225
  } catch (Exception e) {
216
- // Ignore
226
+ Log.w(TAG, "Error in onViewReady", e);
217
227
  }
218
228
  }
219
229
 
@@ -225,18 +235,18 @@ public class MixerManager {
225
235
  if (bitmap == null || bitmap.isRecycled()) {
226
236
  return;
227
237
  }
228
-
238
+
229
239
  if (pusher == null || pusher.getMixerManager() == null) {
230
240
  return;
231
241
  }
232
-
242
+
233
243
  int width = bitmap.getWidth();
234
244
  int height = bitmap.getHeight();
235
-
245
+
236
246
  if (width <= 0 || height <= 0) {
237
247
  return;
238
248
  }
239
-
249
+
240
250
  TextureMgr textureMgr = new TextureMgr(width, height);
241
251
  textureMgr.dealWithTexture(bitmap, new TextureMgr.RenderListener() {
242
252
  @Override
@@ -245,15 +255,22 @@ public class MixerManager {
245
255
  if (texture <= 0) {
246
256
  return;
247
257
  }
248
-
258
+
249
259
  long pts = System.currentTimeMillis() * 1000;
250
- VeLiveVideoFrame videoFrame = new VeLiveVideoFrame(width, height, pts, texture, false, null);
251
-
260
+ VeLiveVideoFrame videoFrame =
261
+ new VeLiveVideoFrame(width, height, pts, texture, false, null);
262
+
252
263
  if (pusher != null && pusher.getMixerManager() != null) {
253
- pusher.getMixerManager().sendCustomVideoFrame(videoFrame, streamId);
264
+ pusher.getMixerManager().sendCustomVideoFrame(videoFrame,
265
+ streamId);
254
266
  }
255
267
  } catch (Exception e) {
256
268
  // Ignore
269
+ } finally {
270
+ if (bitmap != null && !bitmap.isRecycled()) {
271
+ bitmap.recycle();
272
+ }
273
+ textureMgr.cleanup();
257
274
  }
258
275
  }
259
276
  });
@@ -261,16 +278,17 @@ public class MixerManager {
261
278
  // Ignore
262
279
  }
263
280
  }
264
-
281
+
265
282
  /**
266
283
  * Static method to set up callback for MixerView
267
284
  * Now with intelligent layout waiting logic
268
285
  */
269
- private static void setupCallbackForMixerView(MixerView mixerView, int streamId) {
286
+ private static void setupCallbackForMixerView(MixerView mixerView,
287
+ int streamId) {
270
288
  if (mixerView == null) {
271
289
  return;
272
290
  }
273
-
291
+
274
292
  mixerView.setBitmapCaptureCallback(new BitmapCaptureCallback() {
275
293
  @Override
276
294
  public void onBitmapCaptured(Bitmap bitmap) {
@@ -284,13 +302,13 @@ public class MixerManager {
284
302
  // Ignore
285
303
  }
286
304
  }
287
-
305
+
288
306
  @Override
289
307
  public void onCaptureError(String error) {
290
308
  // Ignore
291
309
  }
292
310
  });
293
-
311
+
294
312
  if (mixerView.getWidth() > 0 && mixerView.getHeight() > 0) {
295
313
  try {
296
314
  mixerView.performManualCapture();
@@ -298,32 +316,36 @@ public class MixerManager {
298
316
  // Ignore
299
317
  }
300
318
  } else {
301
- ViewTreeObserver.OnGlobalLayoutListener layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
302
- @Override
303
- public void onGlobalLayout() {
304
- try {
305
- if (mixerView.getViewTreeObserver().isAlive()) {
306
- if (mixerView.getWidth() > 0 && mixerView.getHeight() > 0) {
307
- mixerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
308
- mixerView.performManualCapture();
319
+ ViewTreeObserver.OnGlobalLayoutListener layoutListener =
320
+ new ViewTreeObserver.OnGlobalLayoutListener() {
321
+ @Override
322
+ public void onGlobalLayout() {
323
+ try {
324
+ if (mixerView.getViewTreeObserver().isAlive()) {
325
+ if (mixerView.getWidth() > 0 && mixerView.getHeight() > 0) {
326
+ mixerView.getViewTreeObserver()
327
+ .removeOnGlobalLayoutListener(this);
328
+ mixerView.performManualCapture();
329
+ }
330
+ }
331
+ } catch (Exception e) {
332
+ // Ignore
309
333
  }
310
334
  }
311
- } catch (Exception e) {
312
- // Ignore
313
- }
314
- }
315
- };
316
-
335
+ };
336
+
317
337
  try {
318
- mixerView.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
338
+ mixerView.getViewTreeObserver().addOnGlobalLayoutListener(
339
+ layoutListener);
319
340
  } catch (Exception e) {
320
341
  // Ignore
321
342
  }
322
343
  }
323
344
  }
324
-
345
+
325
346
  /**
326
- * Static version of sendBitmapToMixer - finds the active MixerManager instance
347
+ * Static version of sendBitmapToMixer - finds the active MixerManager
348
+ * instance
327
349
  */
328
350
  private static void sendBitmapToMixerStatic(int streamId, Bitmap bitmap) {
329
351
  try {
@@ -335,7 +357,7 @@ public class MixerManager {
335
357
  // Ignore
336
358
  }
337
359
  }
338
-
360
+
339
361
  /**
340
362
  * Find the active MixerManager instance
341
363
  */
@@ -346,44 +368,42 @@ public class MixerManager {
346
368
  }
347
369
  return null;
348
370
  }
349
-
371
+
350
372
  private static volatile MixerManager currentInstance;
351
-
373
+
352
374
  public MixerManager() {
353
- synchronized (MixerManager.class) {
354
- currentInstance = this;
355
- }
375
+ synchronized (MixerManager.class) { currentInstance = this; }
356
376
  }
357
-
377
+
358
378
  /**
359
- * 清理资源,防止内存泄漏
379
+ * cleanup method to release resources
360
380
  */
361
381
  public void cleanup() {
362
382
  try {
363
383
  for (String viewId : cachedMixedViews.keySet()) {
364
384
  View view = cachedMixedViews.get(viewId);
365
385
  if (view instanceof MixerView) {
366
- MixerView mixerView = (MixerView) view;
386
+ MixerView mixerView = (MixerView)view;
367
387
  mixerView.setBitmapCaptureCallback(null);
368
388
  }
369
389
  }
370
-
390
+
371
391
  mixedViewLayout.clear();
372
392
  viewIdToStreamId.clear();
373
393
  pendingCallbacks.clear();
374
-
394
+
375
395
  synchronized (MixerManager.class) {
376
396
  if (currentInstance == this) {
377
397
  currentInstance = null;
378
398
  }
379
399
  }
380
-
400
+
381
401
  pusher = null;
382
402
  } catch (Exception e) {
383
403
  // Ignore
384
404
  }
385
405
  }
386
-
406
+
387
407
  // Callback interface for bitmap capture
388
408
  public interface BitmapCaptureCallback {
389
409
  void onBitmapCaptured(Bitmap bitmap);
@@ -255,7 +255,8 @@ public class MixerView extends FrameLayout {
255
255
  }
256
256
 
257
257
  long currentTime = System.currentTimeMillis();
258
- if (currentTime - lastCaptureTime < 16) {
258
+ long minInterval = 33; // 30 FPS ~ 33ms per frame
259
+ if (currentTime - lastCaptureTime < minInterval) {
259
260
  return;
260
261
  }
261
262
  lastCaptureTime = currentTime;
@@ -316,6 +317,7 @@ public class MixerView extends FrameLayout {
316
317
  // Call callback first if set (for MixerManager)
317
318
  if (bitmapCaptureCallback != null) {
318
319
  try {
320
+ // only capture if the callback is set
319
321
  bitmapCaptureCallback.onBitmapCaptured(bitmap);
320
322
  } catch (Exception e) {
321
323
  if (bitmapCaptureCallback != null) {
@@ -339,6 +341,12 @@ public class MixerView extends FrameLayout {
339
341
  } catch (IllegalStateException e) {
340
342
  // Ignore
341
343
  }
344
+
345
+ // if no callback, recycle immediately to avoid accumulation
346
+ if (bitmap != null && !bitmap.isRecycled()) {
347
+ bitmap.recycle();
348
+ return;
349
+ }
342
350
  }
343
351
 
344
352
  // Clean up old bitmap
@@ -137,14 +137,14 @@ public class TextureMgr {
137
137
  GLThreadManager.getMainGlHandle().post(() -> {
138
138
  if (texture > 0) {
139
139
  Bitmap processedBitmap = null;
140
+ boolean needRecycle = false;
140
141
  try {
141
142
  if (bitmap == null || bitmap.isRecycled()) {
142
143
  return;
143
144
  }
144
-
145
145
  GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
146
146
  processedBitmap = YuvHelper.rotateBitmap(bitmap, 0, false, true);
147
-
147
+ needRecycle = (processedBitmap != null && processedBitmap != bitmap);
148
148
  if (processedBitmap != null && !processedBitmap.isRecycled()) {
149
149
  GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, processedBitmap, 0);
150
150
  GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
@@ -158,7 +158,7 @@ public class TextureMgr {
158
158
  } catch (Exception e) {
159
159
  // Ignore
160
160
  } finally {
161
- if (processedBitmap != null && processedBitmap != bitmap && !processedBitmap.isRecycled()) {
161
+ if (needRecycle && processedBitmap != null && !processedBitmap.isRecycled()) {
162
162
  processedBitmap.recycle();
163
163
  }
164
164
  }
@@ -12,13 +12,26 @@ import android.renderscript.Type;
12
12
  import java.nio.ByteBuffer;
13
13
 
14
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
+ }
15
28
  public static class NV21ToBitmap {
16
29
  private RenderScript rs;
17
30
  private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic;
18
31
  private Type.Builder yuvType, rgbaType;
19
32
 
20
33
  public NV21ToBitmap(Context context) {
21
- rs = RenderScript.create(context);
34
+ rs = YuvHelper.getGlobalRS(context);
22
35
  yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
23
36
  yuvType = new Type.Builder(rs, Element.U8(rs));
24
37
  rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs));
@@ -30,10 +43,8 @@ public class YuvHelper {
30
43
  yuvToRgbIntrinsic.destroy();
31
44
  yuvToRgbIntrinsic = null;
32
45
  }
33
- if (rs != null) {
34
- rs.destroy();
35
- rs = null;
36
- }
46
+ // 不销毁全局 rs
47
+ // rs = null;
37
48
  } catch (Exception e) {
38
49
  // Ignore
39
50
  }