@bits-innovate/react-native-vstarcam 1.0.18 → 1.0.19

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.
@@ -140,7 +140,8 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
140
140
  private final ExecutorService executor;
141
141
 
142
142
  // Client tracking - maps our clientPtr to actual SDK client handle
143
- private Map<Integer, ClientInfo> clients = new HashMap<>();
143
+ // Made static so VStarCamVideoView can access SDK pointers
144
+ private static Map<Integer, ClientInfo> clients = new HashMap<>();
144
145
  private boolean isNativeLibraryLoaded = false;
145
146
  private boolean isP2PInitialized = false;
146
147
 
@@ -156,8 +157,21 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
156
157
  long sdkClientPtr = 0; // Actual SDK client pointer (long)
157
158
  boolean isConnected = false;
158
159
  boolean isLoggedIn = false;
160
+ String username; // Login username for CGI commands
161
+ String password; // Login password for CGI commands
159
162
  }
160
163
 
164
+ /**
165
+ * Get the actual SDK client pointer for a given internal client ID.
166
+ * This is needed by VStarCamVideoView to connect to the player.
167
+ */
168
+ public static long getSdkClientPtr(int clientPtr) {
169
+ ClientInfo info = clients.get(clientPtr);
170
+ if (info != null) {
171
+ return info.sdkClientPtr;
172
+ }
173
+ return 0;
174
+ }
161
175
 
162
176
  public VStarCamModule(ReactApplicationContext reactContext) {
163
177
  super(reactContext);
@@ -552,6 +566,8 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
552
566
  }
553
567
 
554
568
  clientInfo.isLoggedIn = true;
569
+ clientInfo.username = username;
570
+ clientInfo.password = password;
555
571
  Log.d(TAG, "JNIApi.login called successfully");
556
572
 
557
573
  promise.resolve(true);
@@ -680,13 +696,45 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
680
696
  }
681
697
 
682
698
  /**
683
- * Start video stream
699
+ * Start video stream by sending livestream.cgi command
700
+ * Resolution: 1=high, 2=general, 4=low, 100=superHD
684
701
  */
685
702
  @ReactMethod
686
703
  public void startVideoStream(int clientPtr, int streamType, Promise promise) {
687
- // TODO: Implement video streaming
688
- Log.d(TAG, "startVideoStream called - not yet implemented");
689
- promise.resolve(true);
704
+ Log.d(TAG, "startVideoStream called with type: " + streamType);
705
+ try {
706
+ ClientInfo clientInfo = clients.get(clientPtr);
707
+ if (clientInfo == null || clientInfo.sdkClientPtr == 0) {
708
+ promise.reject("E_NOT_CONNECTED", "Client not connected");
709
+ return;
710
+ }
711
+
712
+ // Send livestream.cgi command to start streaming
713
+ // streamid=10 starts the stream, substream is the resolution
714
+ String cgi = String.format(
715
+ "livestream.cgi?streamid=10&substream=%d&loginuse=%s&loginpas=%s",
716
+ streamType,
717
+ clientInfo.username != null ? clientInfo.username : "admin",
718
+ clientInfo.password != null ? clientInfo.password : ""
719
+ );
720
+
721
+ Log.d(TAG, "Sending livestream start command: " + cgi);
722
+
723
+ Method writeCgiMethod = jniApiClass.getMethod("writeCgi", long.class, String.class, int.class);
724
+ Object result = writeCgiMethod.invoke(null, clientInfo.sdkClientPtr, cgi, 5);
725
+
726
+ Log.d(TAG, "livestream start command sent, result: " + result);
727
+
728
+ WritableMap response = Arguments.createMap();
729
+ response.putBoolean("success", true);
730
+ response.putString("message", "Video stream started");
731
+ response.putInt("resolution", streamType);
732
+ promise.resolve(response);
733
+
734
+ } catch (Exception e) {
735
+ Log.e(TAG, "startVideoStream error", e);
736
+ promise.reject("E_VIDEO_START_FAILED", e.getMessage(), e);
737
+ }
690
738
  }
691
739
 
692
740
  /**
@@ -695,7 +743,37 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
695
743
  @ReactMethod
696
744
  public void stopVideoStream(int clientPtr, Promise promise) {
697
745
  Log.d(TAG, "stopVideoStream called");
698
- promise.resolve(true);
746
+ try {
747
+ ClientInfo clientInfo = clients.get(clientPtr);
748
+ if (clientInfo == null || clientInfo.sdkClientPtr == 0) {
749
+ promise.reject("E_NOT_CONNECTED", "Client not connected");
750
+ return;
751
+ }
752
+
753
+ // Send livestream.cgi command to stop streaming
754
+ // streamid=16 stops the stream
755
+ String cgi = String.format(
756
+ "livestream.cgi?streamid=16&substream=0&loginuse=%s&loginpas=%s",
757
+ clientInfo.username != null ? clientInfo.username : "admin",
758
+ clientInfo.password != null ? clientInfo.password : ""
759
+ );
760
+
761
+ Log.d(TAG, "Sending livestream stop command: " + cgi);
762
+
763
+ Method writeCgiMethod = jniApiClass.getMethod("writeCgi", long.class, String.class, int.class);
764
+ Object result = writeCgiMethod.invoke(null, clientInfo.sdkClientPtr, cgi, 5);
765
+
766
+ Log.d(TAG, "livestream stop command sent, result: " + result);
767
+
768
+ WritableMap response = Arguments.createMap();
769
+ response.putBoolean("success", true);
770
+ response.putString("message", "Video stream stopped");
771
+ promise.resolve(response);
772
+
773
+ } catch (Exception e) {
774
+ Log.e(TAG, "stopVideoStream error", e);
775
+ promise.reject("E_VIDEO_STOP_FAILED", e.getMessage(), e);
776
+ }
699
777
  }
700
778
 
701
779
  /**
@@ -757,7 +835,7 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
757
835
  @ReactMethod
758
836
  public void getSdkVersion(Promise promise) {
759
837
  WritableMap result = Arguments.createMap();
760
- result.putString("version", "1.0.18");
838
+ result.putString("version", "1.0.19");
761
839
  result.putBoolean("nativeLoaded", isNativeLibraryLoaded);
762
840
  result.putString("nativeLib", "OKSMARTPPCS");
763
841
  result.putBoolean("p2pInitialized", isP2PInitialized);
@@ -4,6 +4,7 @@ import android.content.Context;
4
4
  import android.graphics.Color;
5
5
  import android.graphics.SurfaceTexture;
6
6
  import android.util.Log;
7
+ import android.view.Surface;
7
8
  import android.view.TextureView;
8
9
  import android.view.Gravity;
9
10
  import android.widget.FrameLayout;
@@ -13,39 +14,46 @@ import java.lang.reflect.Method;
13
14
 
14
15
  /**
15
16
  * Native video view for VStarCam camera streaming.
16
- * Uses TextureView for video rendering with the native player SDK.
17
+ * Uses TextureView for video rendering with the native AppPlayer SDK.
17
18
  */
18
- public class VStarCamVideoView extends FrameLayout implements TextureView.SurfaceTextureListener {
19
+ public class VStarCamVideoView extends FrameLayout
20
+ implements TextureView.SurfaceTextureListener {
21
+
19
22
  private static final String TAG = "VStarCamVideoView";
20
-
23
+
21
24
  private TextureView textureView;
22
25
  private TextView statusView;
26
+ private Surface surface;
27
+
28
+ // Client info
23
29
  private long clientPtr = 0;
30
+ private long sdkClientPtr = 0; // Actual SDK pointer from VStarCamModule
31
+ private int resolution = 2;
32
+
33
+ // Player info
24
34
  private long playerPtr = 0;
25
35
  private boolean isStreaming = false;
26
- private int resolution = 2; // Default to general resolution
27
-
28
- // Player library class
36
+ private boolean playerInitialized = false;
37
+
38
+ // Player class (from AAR)
29
39
  private Class<?> appPlayerClass;
30
- private Object playerInstance;
31
- private boolean playerLibLoaded = false;
32
-
40
+
33
41
  public VStarCamVideoView(Context context) {
34
42
  super(context);
35
43
  init(context);
36
44
  }
37
-
45
+
38
46
  private void init(Context context) {
39
47
  setBackgroundColor(Color.BLACK);
40
-
41
- // Create texture view for video rendering
48
+
49
+ // Create TextureView for video rendering
42
50
  textureView = new TextureView(context);
43
51
  textureView.setSurfaceTextureListener(this);
44
52
  addView(textureView, new LayoutParams(
45
- LayoutParams.MATCH_PARENT,
53
+ LayoutParams.MATCH_PARENT,
46
54
  LayoutParams.MATCH_PARENT
47
55
  ));
48
-
56
+
49
57
  // Status overlay
50
58
  statusView = new TextView(context);
51
59
  statusView.setTextColor(Color.WHITE);
@@ -58,140 +66,289 @@ public class VStarCamVideoView extends FrameLayout implements TextureView.Surfac
58
66
  );
59
67
  statusParams.gravity = Gravity.CENTER;
60
68
  addView(statusView, statusParams);
61
-
62
- // Try to load player library
69
+
70
+ // Load player library and find class
63
71
  loadPlayerLibrary();
64
72
  }
65
-
73
+
66
74
  private void loadPlayerLibrary() {
67
75
  try {
76
+ // Load the player native lib
68
77
  System.loadLibrary("OKSMARTPLAY");
69
- playerLibLoaded = true;
70
- Log.d(TAG, "Player library loaded successfully");
71
-
72
- // Try to find player class via reflection
73
- try {
74
- appPlayerClass = Class.forName("com.vstarcam.player.AppPlayer");
75
- Log.d(TAG, "AppPlayer class found");
76
- } catch (ClassNotFoundException e) {
77
- // Try alternative class names
78
+ Log.d(TAG, "OKSMARTPLAY library loaded");
79
+
80
+ // Find AppPlayer class - try multiple possible package names
81
+ String[] classNames = {
82
+ "com.vstarcam.player.AppPlayer",
83
+ "com.vstarcam.AppPlayer",
84
+ "com.oksmartplay.AppPlayer",
85
+ "com.vstarcam.app_player.AppPlayer"
86
+ };
87
+
88
+ for (String className : classNames) {
78
89
  try {
79
- appPlayerClass = Class.forName("com.vstarcam.AppPlayer");
80
- Log.d(TAG, "AppPlayer class found (alt path)");
81
- } catch (ClassNotFoundException e2) {
82
- Log.w(TAG, "AppPlayer class not found, using native methods directly");
90
+ appPlayerClass = Class.forName(className);
91
+ Log.d(TAG, "Found player class: " + className);
92
+
93
+ // Log available methods for debugging
94
+ logPlayerMethods();
95
+ break;
96
+ } catch (ClassNotFoundException ignored) {
97
+ Log.d(TAG, "Class not found: " + className);
83
98
  }
84
99
  }
100
+
101
+ if (appPlayerClass == null) {
102
+ Log.e(TAG, "Could not find AppPlayer class in AAR");
103
+ }
104
+
85
105
  } catch (UnsatisfiedLinkError e) {
86
- Log.e(TAG, "Failed to load player library: " + e.getMessage());
87
- playerLibLoaded = false;
106
+ Log.e(TAG, "Failed to load OKSMARTPLAY: " + e.getMessage());
88
107
  }
89
108
  }
90
-
109
+
110
+ private void logPlayerMethods() {
111
+ if (appPlayerClass == null) return;
112
+
113
+ Log.d(TAG, "=== AppPlayer Methods ===");
114
+ for (Method m : appPlayerClass.getDeclaredMethods()) {
115
+ StringBuilder params = new StringBuilder();
116
+ for (Class<?> p : m.getParameterTypes()) {
117
+ if (params.length() > 0) params.append(", ");
118
+ params.append(p.getSimpleName());
119
+ }
120
+ Log.d(TAG, " " + m.getName() + "(" + params + ") -> " +
121
+ m.getReturnType().getSimpleName());
122
+ }
123
+ }
124
+
91
125
  /**
92
- * Set the client pointer for video streaming
126
+ * Set the client pointer from VStarCamModule.
127
+ * This is our internal pointer ID, not the SDK pointer.
93
128
  */
94
129
  public void setClientPtr(long ptr) {
95
130
  this.clientPtr = ptr;
96
131
  Log.d(TAG, "Client pointer set: " + ptr);
97
-
98
- if (clientPtr != 0) {
132
+
133
+ // Lookup the actual SDK pointer from VStarCamModule
134
+ this.sdkClientPtr = VStarCamModule.getSdkClientPtr((int) ptr);
135
+ Log.d(TAG, "SDK client pointer: " + sdkClientPtr);
136
+
137
+ if (sdkClientPtr != 0) {
99
138
  statusView.setText("Ready to stream");
139
+ } else {
140
+ statusView.setText("Waiting for connection...");
100
141
  }
101
142
  }
102
-
143
+
103
144
  /**
104
- * Set video resolution (1=high, 2=general, 4=low, 100=superHD)
145
+ * Set video resolution.
146
+ * 1=high, 2=general, 4=low, 100=superHD
105
147
  */
106
148
  public void setResolution(int res) {
107
149
  this.resolution = res;
108
150
  Log.d(TAG, "Resolution set: " + res);
109
151
  }
110
-
152
+
111
153
  /**
112
- * Start video streaming
154
+ * Start video streaming.
113
155
  */
114
156
  public void startStream() {
115
- if (clientPtr == 0) {
116
- Log.e(TAG, "Cannot start stream: no client pointer");
117
- statusView.setText("No connection");
157
+ if (sdkClientPtr == 0) {
158
+ // Try to get SDK pointer again in case it was set after setClientPtr
159
+ this.sdkClientPtr = VStarCamModule.getSdkClientPtr((int) clientPtr);
160
+ if (sdkClientPtr == 0) {
161
+ Log.e(TAG, "Cannot start: no SDK client pointer");
162
+ statusView.setText("Not connected");
163
+ return;
164
+ }
165
+ }
166
+
167
+ if (surface == null) {
168
+ Log.e(TAG, "Cannot start: no surface available");
169
+ statusView.setText("No surface");
118
170
  return;
119
171
  }
120
-
121
- if (!playerLibLoaded) {
122
- Log.e(TAG, "Cannot start stream: player library not loaded");
123
- statusView.setText("Player not available");
172
+
173
+ if (appPlayerClass == null) {
174
+ Log.e(TAG, "Cannot start: player class not found");
175
+ statusView.setText("Player unavailable");
124
176
  return;
125
177
  }
126
-
127
- Log.d(TAG, "Starting video stream...");
178
+
128
179
  statusView.setText("Starting stream...");
129
-
130
- // The actual streaming is handled by sending CGI command
131
- // Video frames come through the native player SDK
132
- isStreaming = true;
133
-
134
- // Hide status once streaming
135
- statusView.setVisibility(GONE);
180
+
181
+ try {
182
+ // 1. Create player instance if needed
183
+ if (playerPtr == 0) {
184
+ Method createMethod = appPlayerClass.getMethod("create");
185
+ Object result = createMethod.invoke(null);
186
+ playerPtr = (Long) result;
187
+ Log.d(TAG, "Player created: " + playerPtr);
188
+ }
189
+
190
+ // 2. Set the video source (P2P client)
191
+ try {
192
+ Method setSourceMethod = appPlayerClass.getMethod(
193
+ "setSource", long.class, long.class);
194
+ setSourceMethod.invoke(null, playerPtr, sdkClientPtr);
195
+ Log.d(TAG, "Source set to client: " + sdkClientPtr);
196
+ } catch (NoSuchMethodException e) {
197
+ Log.w(TAG, "setSource method not found, trying alternative...");
198
+ // Try alternative method signatures if the main one doesn't exist
199
+ }
200
+
201
+ // 3. Set the render surface
202
+ try {
203
+ Method setSurfaceMethod = appPlayerClass.getMethod(
204
+ "setSurface", long.class, Surface.class);
205
+ setSurfaceMethod.invoke(null, playerPtr, surface);
206
+ Log.d(TAG, "Surface set");
207
+ } catch (NoSuchMethodException e) {
208
+ Log.w(TAG, "setSurface method not found, trying alternative...");
209
+ // Try with Object parameter
210
+ try {
211
+ Method setSurfaceMethod = appPlayerClass.getMethod(
212
+ "setSurface", long.class, Object.class);
213
+ setSurfaceMethod.invoke(null, playerPtr, surface);
214
+ Log.d(TAG, "Surface set (via Object)");
215
+ } catch (NoSuchMethodException e2) {
216
+ Log.e(TAG, "No setSurface method found");
217
+ }
218
+ }
219
+
220
+ // 4. Start playback
221
+ try {
222
+ Method startMethod = appPlayerClass.getMethod("start", long.class);
223
+ startMethod.invoke(null, playerPtr);
224
+ Log.d(TAG, "Playback started");
225
+ } catch (NoSuchMethodException e) {
226
+ // Try play() instead of start()
227
+ try {
228
+ Method playMethod = appPlayerClass.getMethod("play", long.class);
229
+ playMethod.invoke(null, playerPtr);
230
+ Log.d(TAG, "Playback started (via play)");
231
+ } catch (NoSuchMethodException e2) {
232
+ Log.e(TAG, "No start/play method found");
233
+ }
234
+ }
235
+
236
+ isStreaming = true;
237
+ statusView.setVisibility(GONE);
238
+
239
+ } catch (Exception e) {
240
+ Log.e(TAG, "Failed to start stream", e);
241
+ statusView.setText("Start failed: " + e.getMessage());
242
+ }
136
243
  }
137
-
244
+
138
245
  /**
139
- * Stop video streaming
246
+ * Stop video streaming.
140
247
  */
141
248
  public void stopStream() {
142
- if (!isStreaming) return;
143
-
144
- Log.d(TAG, "Stopping video stream...");
145
- isStreaming = false;
146
- statusView.setVisibility(VISIBLE);
147
- statusView.setText("Stream stopped");
249
+ if (!isStreaming || playerPtr == 0) return;
250
+
251
+ Log.d(TAG, "Stopping stream...");
252
+
253
+ try {
254
+ try {
255
+ Method stopMethod = appPlayerClass.getMethod("stop", long.class);
256
+ stopMethod.invoke(null, playerPtr);
257
+ Log.d(TAG, "Playback stopped");
258
+ } catch (NoSuchMethodException e) {
259
+ // Try pause() instead
260
+ try {
261
+ Method pauseMethod = appPlayerClass.getMethod("pause", long.class);
262
+ pauseMethod.invoke(null, playerPtr);
263
+ Log.d(TAG, "Playback paused");
264
+ } catch (NoSuchMethodException e2) {
265
+ Log.w(TAG, "No stop/pause method found");
266
+ }
267
+ }
268
+
269
+ isStreaming = false;
270
+ statusView.setVisibility(VISIBLE);
271
+ statusView.setText("Stream stopped");
272
+
273
+ } catch (Exception e) {
274
+ Log.e(TAG, "Failed to stop stream", e);
275
+ }
148
276
  }
149
-
150
- // TextureView.SurfaceTextureListener implementation
151
-
277
+
278
+ private void releasePlayer() {
279
+ if (playerPtr == 0) return;
280
+
281
+ try {
282
+ stopStream();
283
+
284
+ try {
285
+ Method releaseMethod = appPlayerClass.getMethod("release", long.class);
286
+ releaseMethod.invoke(null, playerPtr);
287
+ Log.d(TAG, "Player released");
288
+ } catch (NoSuchMethodException e) {
289
+ // Try destroy() instead
290
+ try {
291
+ Method destroyMethod = appPlayerClass.getMethod("destroy", long.class);
292
+ destroyMethod.invoke(null, playerPtr);
293
+ Log.d(TAG, "Player destroyed");
294
+ } catch (NoSuchMethodException e2) {
295
+ Log.w(TAG, "No release/destroy method found");
296
+ }
297
+ }
298
+
299
+ playerPtr = 0;
300
+
301
+ } catch (Exception e) {
302
+ Log.e(TAG, "Failed to release player", e);
303
+ }
304
+ }
305
+
306
+ // TextureView.SurfaceTextureListener
307
+
152
308
  @Override
153
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
309
+ public void onSurfaceTextureAvailable(
310
+ SurfaceTexture surfaceTexture, int width, int height) {
154
311
  Log.d(TAG, "Surface available: " + width + "x" + height);
155
- // Initialize native player with surface when available
156
- if (playerLibLoaded && clientPtr != 0) {
157
- initializePlayer(surface, width, height);
312
+ this.surface = new Surface(surfaceTexture);
313
+
314
+ // If already supposed to be streaming, try to update surface
315
+ if (isStreaming && playerPtr != 0 && appPlayerClass != null) {
316
+ try {
317
+ Method setSurfaceMethod = appPlayerClass.getMethod(
318
+ "setSurface", long.class, Surface.class);
319
+ setSurfaceMethod.invoke(null, playerPtr, surface);
320
+ Log.d(TAG, "Surface updated on existing player");
321
+ } catch (Exception e) {
322
+ Log.e(TAG, "Failed to update surface", e);
323
+ }
158
324
  }
159
325
  }
160
-
326
+
161
327
  @Override
162
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
328
+ public void onSurfaceTextureSizeChanged(
329
+ SurfaceTexture surface, int width, int height) {
163
330
  Log.d(TAG, "Surface size changed: " + width + "x" + height);
164
331
  }
165
-
332
+
166
333
  @Override
167
334
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
168
335
  Log.d(TAG, "Surface destroyed");
169
336
  stopStream();
170
- releasePlayer();
337
+ if (this.surface != null) {
338
+ this.surface.release();
339
+ this.surface = null;
340
+ }
171
341
  return true;
172
342
  }
173
-
343
+
174
344
  @Override
175
345
  public void onSurfaceTextureUpdated(SurfaceTexture surface) {
176
- // Called when texture is updated with new frame
177
- }
178
-
179
- private void initializePlayer(SurfaceTexture surface, int width, int height) {
180
- // Native player initialization will be implemented based on SDK API
181
- Log.d(TAG, "Initializing player for surface");
346
+ // Frame rendered - nothing to do
182
347
  }
183
-
184
- private void releasePlayer() {
185
- if (playerPtr != 0) {
186
- // Release native player resources
187
- playerPtr = 0;
188
- }
189
- }
190
-
348
+
191
349
  @Override
192
350
  protected void onDetachedFromWindow() {
193
351
  super.onDetachedFromWindow();
194
- stopStream();
195
352
  releasePlayer();
196
353
  }
197
354
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bits-innovate/react-native-vstarcam",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "React Native bridge for VStarCam P2P SDK",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",