@bits-innovate/react-native-vstarcam 1.0.53 → 1.0.55

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.
@@ -17,9 +17,7 @@ import com.facebook.react.modules.core.DeviceEventManagerModule;
17
17
 
18
18
  import java.io.BufferedReader;
19
19
  import java.io.InputStreamReader;
20
- import java.lang.reflect.InvocationHandler;
21
20
  import java.lang.reflect.Method;
22
- import java.lang.reflect.Proxy;
23
21
  import java.net.HttpURLConnection;
24
22
  import java.net.URL;
25
23
  import java.util.HashMap;
@@ -283,126 +281,62 @@ public class VStarCamModule extends ReactContextBaseJavaModule implements Lifecy
283
281
  }
284
282
 
285
283
  try {
286
- // Create dynamic proxy for ClientStateListener
287
- stateListenerProxy = Proxy.newProxyInstance(
288
- stateListenerClass.getClassLoader(),
289
- new Class<?>[] { stateListenerClass },
290
- new InvocationHandler() {
284
+ // Create CONCRETE listener implementations (NOT dynamic proxies).
285
+ // Dynamic Proxies ($Proxy0 classes) crash under CheckJNI when invoked
286
+ // from the C++ SDK's native pthreads via CallVoidMethod. Concrete
287
+ // anonymous classes have real vtables that work correctly.
288
+
289
+ com.vstarcam.app_p2p_api.ClientStateListener stateListener =
290
+ new com.vstarcam.app_p2p_api.ClientStateListener() {
291
291
  @Override
292
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
292
+ public void stateListener(long clientPtr, int state) {
293
293
  try {
294
- String methodName = method.getName();
295
-
296
- // Handle standard Object methods
297
- if (method.getDeclaringClass() == Object.class) {
298
- if (methodName.equals("equals")) return proxy == args[0];
299
- if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
300
- if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
301
- }
302
-
303
- Log.d(TAG, "ClientStateListener." + methodName + " called");
304
- if (methodName.equals("stateListener")) {
305
- // args: long clientPtr, int state
306
- long clientPtr = 0;
307
- int state = 0;
308
-
309
- if (args != null && args.length >= 2) {
310
- if (args[0] instanceof Number) clientPtr = ((Number) args[0]).longValue();
311
- if (args[1] instanceof Number) state = ((Number) args[1]).intValue();
312
- }
313
-
314
- Log.d(TAG, "State callback: clientPtr=" + clientPtr + ", state=" + state);
315
-
316
- // Post to main thread for React Native compatibility
317
- VStarCamModule.handleStateChange(clientPtr, state);
318
- }
294
+ Log.d(TAG, "State callback: clientPtr=" + clientPtr + ", state=" + state);
295
+ VStarCamModule.handleStateChange(clientPtr, state);
319
296
  } catch (Exception e) {
320
297
  Log.e(TAG, "Error in stateListener callback", e);
321
298
  }
322
- return null;
323
299
  }
324
- }
325
- );
326
- jniProxyRoots.add(stateListenerProxy);
300
+ };
327
301
 
328
- // Create dynamic proxy for ClientCommandListener
329
- commandListenerProxy = Proxy.newProxyInstance(
330
- commandListenerClass.getClassLoader(),
331
- new Class<?>[] { commandListenerClass },
332
- new InvocationHandler() {
302
+ com.vstarcam.app_p2p_api.ClientCommandListener commandListener =
303
+ new com.vstarcam.app_p2p_api.ClientCommandListener() {
333
304
  @Override
334
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
305
+ public void commandListener(long clientPtr, byte[] data, int length) {
335
306
  try {
336
- String methodName = method.getName();
337
-
338
- // Handle standard Object methods
339
- if (method.getDeclaringClass() == Object.class) {
340
- if (methodName.equals("equals")) return proxy == args[0];
341
- if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
342
- if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
343
- }
344
-
345
- Log.d(TAG, "ClientCommandListener." + methodName + " called");
346
- if (methodName.equals("commandListener")) {
347
- // args: long clientPtr, byte[] data, int length (from interface)
348
- long clientPtr = 0;
349
- byte[] data = null;
350
- int length = 0;
351
-
352
- if (args != null && args.length >= 3) {
353
- if (args[0] instanceof Number) clientPtr = ((Number) args[0]).longValue();
354
- if (args[1] instanceof byte[]) data = (byte[]) args[1];
355
- if (args[2] instanceof Number) length = ((Number) args[2]).intValue();
356
- }
357
-
358
- Log.d(TAG, "Command callback: clientPtr=" + clientPtr + ", dataLen=" + (data != null ? data.length : 0));
359
-
360
- // Post to main thread for React Native compatibility
361
- VStarCamModule.handleCommandReceive(clientPtr, 0, data);
362
- }
307
+ Log.d(TAG, "Command callback: clientPtr=" + clientPtr + ", dataLen=" + (data != null ? data.length : 0));
308
+ VStarCamModule.handleCommandReceive(clientPtr, 0, data);
363
309
  } catch (Exception e) {
364
310
  Log.e(TAG, "Error in commandListener callback", e);
365
311
  }
366
- return null;
367
312
  }
368
- }
369
- );
370
- jniProxyRoots.add(commandListenerProxy);
313
+ };
371
314
 
372
- // Create dynamic proxy for ClientReleaseListener
373
- releaseListenerProxy = Proxy.newProxyInstance(
374
- releaseListenerClass.getClassLoader(),
375
- new Class<?>[] { releaseListenerClass },
376
- new InvocationHandler() {
315
+ com.vstarcam.app_p2p_api.ClientReleaseListener releaseListener =
316
+ new com.vstarcam.app_p2p_api.ClientReleaseListener() {
377
317
  @Override
378
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
318
+ public void releaseListener(long clientPtr) {
379
319
  try {
380
- String methodName = method.getName();
381
-
382
- // Handle standard Object methods
383
- if (method.getDeclaringClass() == Object.class) {
384
- if (methodName.equals("equals")) return proxy == args[0];
385
- if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
386
- if (methodName.equals("toString")) return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
387
- }
388
-
389
- Log.d(TAG, "ClientReleaseListener." + methodName + " called");
320
+ Log.d(TAG, "Release callback: clientPtr=" + clientPtr);
390
321
  } catch (Exception e) {
391
322
  Log.e(TAG, "Error in releaseListener callback", e);
392
323
  }
393
- return null;
394
324
  }
395
- }
396
- );
397
- jniProxyRoots.add(releaseListenerProxy);
325
+ };
326
+
327
+ // Keep strong references to prevent GC
328
+ stateListenerProxy = stateListener;
329
+ commandListenerProxy = commandListener;
330
+ releaseListenerProxy = releaseListener;
331
+ jniProxyRoots.add(stateListener);
332
+ jniProxyRoots.add(commandListener);
333
+ jniProxyRoots.add(releaseListener);
398
334
 
399
335
  // Call JNIApi.init(stateListener, commandListener, releaseListener)
400
- Method initMethod = VStarCamModule.jniApiClass.getMethod("init",
401
- VStarCamModule.stateListenerClass, VStarCamModule.commandListenerClass, VStarCamModule.releaseListenerClass);
402
- initMethod.invoke(null, stateListenerProxy, commandListenerProxy, releaseListenerProxy);
336
+ com.vstarcam.JNIApi.init(stateListener, commandListener, releaseListener);
403
337
 
404
338
  VStarCamModule.isP2PInitialized = true;
405
- Log.d(TAG, "P2P system initialized successfully!");
339
+ Log.d(TAG, "P2P system initialized successfully with concrete listeners!");
406
340
  } catch (Exception e) {
407
341
  Log.e(TAG, "Failed to initialize P2P: " + e.getMessage(), e);
408
342
  }
@@ -46,6 +46,7 @@ public class VStarCamVideoView extends FrameLayout
46
46
  private long playerPtr = 0;
47
47
  private boolean isStreaming = false;
48
48
  private boolean playerInitialized = false;
49
+ private boolean pendingStartStream = false;
49
50
 
50
51
  // Player class (from AAR)
51
52
  private Class<?> appPlayerClass;
@@ -184,8 +185,9 @@ public class VStarCamVideoView extends FrameLayout
184
185
  }
185
186
 
186
187
  if (surface == null) {
187
- Log.e(TAG, "Cannot start: no surface available");
188
- statusView.setText("No surface");
188
+ Log.d(TAG, "Surface not yet available — deferring stream start");
189
+ statusView.setText("Preparing...");
190
+ pendingStartStream = true;
189
191
  return;
190
192
  }
191
193
 
@@ -344,7 +346,15 @@ public class VStarCamVideoView extends FrameLayout
344
346
  Log.d(TAG, "Surface available: " + width + "x" + height);
345
347
  this.surface = new Surface(surfaceTexture);
346
348
 
347
- // If already supposed to be streaming, try to update surface
349
+ // If startStream() was called before surface was ready, start now
350
+ if (pendingStartStream) {
351
+ Log.d(TAG, "Deferred stream start — surface now available");
352
+ pendingStartStream = false;
353
+ startStream();
354
+ return;
355
+ }
356
+
357
+ // If already streaming, update the surface on the existing player
348
358
  if (isStreaming && playerPtr != 0 && appPlayerClass != null) {
349
359
  try {
350
360
  Method setSurfaceMethod = appPlayerClass.getMethod(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bits-innovate/react-native-vstarcam",
3
- "version": "1.0.53",
3
+ "version": "1.0.55",
4
4
  "description": "React Native bridge for VStarCam P2P SDK",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",