@bits-innovate/react-native-vstarcam 1.0.17 → 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.
@@ -54,6 +54,8 @@ repositories {
54
54
 
55
55
  dependencies {
56
56
  implementation "com.facebook.react:react-native:+"
57
- // AAR for Java classes only (SDK wrapper classes)
57
+ // AAR for P2P connectivity
58
58
  implementation files('libs/app_p2p_api-5.0.0.aar')
59
+ // AAR for video player/decoder
60
+ implementation files('libs/app_player-5.0.0.aar')
59
61
  }
@@ -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.17");
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);
@@ -8,7 +8,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
8
8
  import com.facebook.react.uimanager.ViewManager;
9
9
 
10
10
  import java.util.ArrayList;
11
- import java.util.Collections;
11
+ import java.util.Arrays;
12
12
  import java.util.List;
13
13
 
14
14
  public class VStarCamPackage implements ReactPackage {
@@ -23,6 +23,9 @@ public class VStarCamPackage implements ReactPackage {
23
23
  @NonNull
24
24
  @Override
25
25
  public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
26
- return Collections.emptyList();
26
+ return Arrays.<ViewManager>asList(
27
+ new VStarCamVideoViewManager()
28
+ );
27
29
  }
28
30
  }
31
+
@@ -0,0 +1,354 @@
1
+ package com.reactnativevstarcam;
2
+
3
+ import android.content.Context;
4
+ import android.graphics.Color;
5
+ import android.graphics.SurfaceTexture;
6
+ import android.util.Log;
7
+ import android.view.Surface;
8
+ import android.view.TextureView;
9
+ import android.view.Gravity;
10
+ import android.widget.FrameLayout;
11
+ import android.widget.TextView;
12
+
13
+ import java.lang.reflect.Method;
14
+
15
+ /**
16
+ * Native video view for VStarCam camera streaming.
17
+ * Uses TextureView for video rendering with the native AppPlayer SDK.
18
+ */
19
+ public class VStarCamVideoView extends FrameLayout
20
+ implements TextureView.SurfaceTextureListener {
21
+
22
+ private static final String TAG = "VStarCamVideoView";
23
+
24
+ private TextureView textureView;
25
+ private TextView statusView;
26
+ private Surface surface;
27
+
28
+ // Client info
29
+ private long clientPtr = 0;
30
+ private long sdkClientPtr = 0; // Actual SDK pointer from VStarCamModule
31
+ private int resolution = 2;
32
+
33
+ // Player info
34
+ private long playerPtr = 0;
35
+ private boolean isStreaming = false;
36
+ private boolean playerInitialized = false;
37
+
38
+ // Player class (from AAR)
39
+ private Class<?> appPlayerClass;
40
+
41
+ public VStarCamVideoView(Context context) {
42
+ super(context);
43
+ init(context);
44
+ }
45
+
46
+ private void init(Context context) {
47
+ setBackgroundColor(Color.BLACK);
48
+
49
+ // Create TextureView for video rendering
50
+ textureView = new TextureView(context);
51
+ textureView.setSurfaceTextureListener(this);
52
+ addView(textureView, new LayoutParams(
53
+ LayoutParams.MATCH_PARENT,
54
+ LayoutParams.MATCH_PARENT
55
+ ));
56
+
57
+ // Status overlay
58
+ statusView = new TextView(context);
59
+ statusView.setTextColor(Color.WHITE);
60
+ statusView.setTextSize(14);
61
+ statusView.setGravity(Gravity.CENTER);
62
+ statusView.setText("Waiting for connection...");
63
+ LayoutParams statusParams = new LayoutParams(
64
+ LayoutParams.WRAP_CONTENT,
65
+ LayoutParams.WRAP_CONTENT
66
+ );
67
+ statusParams.gravity = Gravity.CENTER;
68
+ addView(statusView, statusParams);
69
+
70
+ // Load player library and find class
71
+ loadPlayerLibrary();
72
+ }
73
+
74
+ private void loadPlayerLibrary() {
75
+ try {
76
+ // Load the player native lib
77
+ System.loadLibrary("OKSMARTPLAY");
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) {
89
+ try {
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);
98
+ }
99
+ }
100
+
101
+ if (appPlayerClass == null) {
102
+ Log.e(TAG, "Could not find AppPlayer class in AAR");
103
+ }
104
+
105
+ } catch (UnsatisfiedLinkError e) {
106
+ Log.e(TAG, "Failed to load OKSMARTPLAY: " + e.getMessage());
107
+ }
108
+ }
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
+
125
+ /**
126
+ * Set the client pointer from VStarCamModule.
127
+ * This is our internal pointer ID, not the SDK pointer.
128
+ */
129
+ public void setClientPtr(long ptr) {
130
+ this.clientPtr = ptr;
131
+ Log.d(TAG, "Client pointer set: " + ptr);
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) {
138
+ statusView.setText("Ready to stream");
139
+ } else {
140
+ statusView.setText("Waiting for connection...");
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Set video resolution.
146
+ * 1=high, 2=general, 4=low, 100=superHD
147
+ */
148
+ public void setResolution(int res) {
149
+ this.resolution = res;
150
+ Log.d(TAG, "Resolution set: " + res);
151
+ }
152
+
153
+ /**
154
+ * Start video streaming.
155
+ */
156
+ public void startStream() {
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");
170
+ return;
171
+ }
172
+
173
+ if (appPlayerClass == null) {
174
+ Log.e(TAG, "Cannot start: player class not found");
175
+ statusView.setText("Player unavailable");
176
+ return;
177
+ }
178
+
179
+ statusView.setText("Starting stream...");
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
+ }
243
+ }
244
+
245
+ /**
246
+ * Stop video streaming.
247
+ */
248
+ public void stopStream() {
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
+ }
276
+ }
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
+
308
+ @Override
309
+ public void onSurfaceTextureAvailable(
310
+ SurfaceTexture surfaceTexture, int width, int height) {
311
+ Log.d(TAG, "Surface available: " + width + "x" + 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
+ }
324
+ }
325
+ }
326
+
327
+ @Override
328
+ public void onSurfaceTextureSizeChanged(
329
+ SurfaceTexture surface, int width, int height) {
330
+ Log.d(TAG, "Surface size changed: " + width + "x" + height);
331
+ }
332
+
333
+ @Override
334
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
335
+ Log.d(TAG, "Surface destroyed");
336
+ stopStream();
337
+ if (this.surface != null) {
338
+ this.surface.release();
339
+ this.surface = null;
340
+ }
341
+ return true;
342
+ }
343
+
344
+ @Override
345
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
346
+ // Frame rendered - nothing to do
347
+ }
348
+
349
+ @Override
350
+ protected void onDetachedFromWindow() {
351
+ super.onDetachedFromWindow();
352
+ releasePlayer();
353
+ }
354
+ }
@@ -0,0 +1,106 @@
1
+ package com.reactnativevstarcam;
2
+
3
+ import android.view.View;
4
+
5
+ import androidx.annotation.NonNull;
6
+ import androidx.annotation.Nullable;
7
+
8
+ import com.facebook.react.bridge.ReadableArray;
9
+ import com.facebook.react.common.MapBuilder;
10
+ import com.facebook.react.uimanager.SimpleViewManager;
11
+ import com.facebook.react.uimanager.ThemedReactContext;
12
+ import com.facebook.react.uimanager.annotations.ReactProp;
13
+
14
+ import java.util.Map;
15
+
16
+ /**
17
+ * React Native ViewManager for VStarCamVideoView.
18
+ * Exposes the native video view as a React component.
19
+ */
20
+ public class VStarCamVideoViewManager extends SimpleViewManager<VStarCamVideoView> {
21
+ public static final String REACT_CLASS = "VStarCamVideoView";
22
+
23
+ // Command IDs for direct commands from JS
24
+ public static final int COMMAND_START_STREAM = 1;
25
+ public static final int COMMAND_STOP_STREAM = 2;
26
+
27
+ @NonNull
28
+ @Override
29
+ public String getName() {
30
+ return REACT_CLASS;
31
+ }
32
+
33
+ @NonNull
34
+ @Override
35
+ protected VStarCamVideoView createViewInstance(@NonNull ThemedReactContext reactContext) {
36
+ return new VStarCamVideoView(reactContext);
37
+ }
38
+
39
+ /**
40
+ * Set the client pointer for the video stream
41
+ */
42
+ @ReactProp(name = "clientPtr")
43
+ public void setClientPtr(VStarCamVideoView view, double clientPtr) {
44
+ // React Native passes numbers as doubles, convert to long
45
+ view.setClientPtr((long) clientPtr);
46
+ }
47
+
48
+ /**
49
+ * Set the video resolution
50
+ * 1 = high, 2 = general/default, 4 = low, 100 = superHD
51
+ */
52
+ @ReactProp(name = "resolution", defaultInt = 2)
53
+ public void setResolution(VStarCamVideoView view, int resolution) {
54
+ view.setResolution(resolution);
55
+ }
56
+
57
+ /**
58
+ * Control streaming via prop
59
+ */
60
+ @ReactProp(name = "streaming", defaultBoolean = false)
61
+ public void setStreaming(VStarCamVideoView view, boolean streaming) {
62
+ if (streaming) {
63
+ view.startStream();
64
+ } else {
65
+ view.stopStream();
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Commands that can be called from JS via dispatchViewManagerCommand
71
+ */
72
+ @Nullable
73
+ @Override
74
+ public Map<String, Integer> getCommandsMap() {
75
+ return MapBuilder.of(
76
+ "startStream", COMMAND_START_STREAM,
77
+ "stopStream", COMMAND_STOP_STREAM
78
+ );
79
+ }
80
+
81
+ @Override
82
+ public void receiveCommand(@NonNull VStarCamVideoView view, String commandId, @Nullable ReadableArray args) {
83
+ switch (commandId) {
84
+ case "startStream":
85
+ view.startStream();
86
+ break;
87
+ case "stopStream":
88
+ view.stopStream();
89
+ break;
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Events that can be sent to JS
95
+ */
96
+ @Nullable
97
+ @Override
98
+ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
99
+ return MapBuilder.<String, Object>builder()
100
+ .put("onStreamStarted", MapBuilder.of("registrationName", "onStreamStarted"))
101
+ .put("onStreamStopped", MapBuilder.of("registrationName", "onStreamStopped"))
102
+ .put("onStreamError", MapBuilder.of("registrationName", "onStreamError"))
103
+ .put("onFrameReceived", MapBuilder.of("registrationName", "onFrameReceived"))
104
+ .build();
105
+ }
106
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bits-innovate/react-native-vstarcam",
3
- "version": "1.0.17",
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",
package/src/index.ts CHANGED
@@ -10,7 +10,50 @@
10
10
  * - Camera settings
11
11
  */
12
12
 
13
- import { NativeModules, NativeEventEmitter, Platform } from "react-native";
13
+ import {
14
+ NativeModules,
15
+ NativeEventEmitter,
16
+ Platform,
17
+ requireNativeComponent,
18
+ ViewStyle,
19
+ } from "react-native";
20
+ import React from "react";
21
+
22
+ // Video View Component
23
+ export interface VStarCamVideoViewProps {
24
+ /** Client pointer from create() call */
25
+ clientPtr: number;
26
+ /** Video resolution: 1=high, 2=general, 4=low, 100=superHD */
27
+ resolution?: 1 | 2 | 4 | 100;
28
+ /** Whether streaming is active */
29
+ streaming?: boolean;
30
+ /** Style for the video view */
31
+ style?: ViewStyle;
32
+ /** Called when stream starts */
33
+ onStreamStarted?: () => void;
34
+ /** Called when stream stops */
35
+ onStreamStopped?: () => void;
36
+ /** Called on stream error */
37
+ onStreamError?: (error: { message: string }) => void;
38
+ }
39
+
40
+ /**
41
+ * Native video view component for VStarCam camera streaming.
42
+ *
43
+ * Usage:
44
+ * ```tsx
45
+ * <VStarCamVideoView
46
+ * clientPtr={client.clientPtr}
47
+ * resolution={2}
48
+ * streaming={isPlaying}
49
+ * style={{ width: 300, height: 200 }}
50
+ * />
51
+ * ```
52
+ */
53
+ export const VStarCamVideoView =
54
+ Platform.OS === "android"
55
+ ? requireNativeComponent<VStarCamVideoViewProps>("VStarCamVideoView")
56
+ : () => null; // iOS not implemented yet
14
57
 
15
58
  const LINKING_ERROR =
16
59
  `The package 'react-native-vstarcam' doesn't seem to be linked. Make sure: \n\n` +
Binary file