@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
|
-
|
|
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
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
17
|
+
* Uses TextureView for video rendering with the native AppPlayer SDK.
|
|
17
18
|
*/
|
|
18
|
-
public class VStarCamVideoView extends FrameLayout
|
|
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
|
|
27
|
-
|
|
28
|
-
// Player
|
|
36
|
+
private boolean playerInitialized = false;
|
|
37
|
+
|
|
38
|
+
// Player class (from AAR)
|
|
29
39
|
private Class<?> appPlayerClass;
|
|
30
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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(
|
|
80
|
-
Log.d(TAG, "
|
|
81
|
-
|
|
82
|
-
Log
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
116
|
-
|
|
117
|
-
|
|
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 (
|
|
122
|
-
Log.e(TAG, "Cannot start
|
|
123
|
-
statusView.setText("Player
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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(
|
|
309
|
+
public void onSurfaceTextureAvailable(
|
|
310
|
+
SurfaceTexture surfaceTexture, int width, int height) {
|
|
154
311
|
Log.d(TAG, "Surface available: " + width + "x" + height);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
}
|