package com.videogo.ui.login;
import android.content.res.Configuration;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.videogo.exception.BaseException;
import com.videogo.exception.ErrorCode;
import com.videogo.openapi.EZConstants;
import com.videogo.openapi.EZOpenSDK;
import com.videogo.openapi.EZPlayer;
import ezviz.ezopensdk.R;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.LinearLayout;
import android.widget.ImageButton;
import android.widget.Toast;
import android.view.MotionEvent;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Handler.Callback {
private static final String TAG = "EZPreview";
private static final int MSG_VIDEO_SIZE_CHANGED = 1;
private static final int MSG_REALPLAY_PLAY_SUCCESS = 2001;
private static final int MSG_REALPLAY_PLAY_FAIL = 2002;
private static final int MSG_SHOW_STREAM_TYPE = 3001; // 新增消息类型
private static final int DIRECTION_UP = 0;
private static final int DIRECTION_DOWN = 1;
private static final int DIRECTION_LEFT = 2;
private static final int DIRECTION_RIGHT = 3;
private static final int DIRECTION_ZOOM_IN = 8; // 物理放大
private static final int DIRECTION_ZOOM_OUT = 9; // 物理缩小
private static final int SPEED_MEDIUM = 1; // 速度适中
private static final String PTZ_START_URL = "https://open.ys7.com/api/lapp/device/ptz/start";
private static final String PTZ_STOP_URL = "https://open.ys7.com/api/lapp/device/ptz/stop";
private boolean mIsSupportPTZ = false; // 设备是否支持云台
private ExecutorService mExecutorService; // 线程池
private volatile boolean isPTZActive = false; // 标记是否有云台操作正在进行
private volatile int activeDirection = -1; // 当前活动的方向
// 接收的参数键
private static final String KEY_APPKEY = "appkey";
private static final String KEY_SERIAL = "serial";
private static final String KEY_VERIFYCODE = "VerifyCode";
private static final String KEY_ACCESSTOKEN = "accessToken";
private static final String KEY_CAMERANO = "cameraNo";
private boolean mIsPlaying = false;
private EZPlayer mEZPlayer;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private ProgressBar mLiveProgressBar;
private RelativeLayout mRlControl;
private LinearLayout mLlHc;
private ImageButton mIbRotate2;
// 从Intent中获取的参数
private String mAppKey;
private String mDeviceSerial;
private String mVerifyCode;
private String mAccessToken;
private int mCameraNo = 0; // 默认通道号0
private Handler mHandler;
private boolean mP2PEnabled = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activitymain);
mHandler = new Handler(this);
// 1. 从Intent中获取参数
extractParametersFromIntent();
// 2. 初始化UI
initUI();
initOrientationSensitiveViews();
View fanHui = findViewById(R.id.back);
fanHui.setOnClickListener(v -> finish());
// 3. 初始化SDK并创建播放器
initSDKAndCreatePlayer();
}
private void initOrientationSensitiveViews() {
mLiveProgressBar = findViewById(R.id.liveProgressBar);
mRlControl = findViewById(R.id.rl_control);
mLlHc = findViewById(R.id.ll_hc);
mIbRotate2 = findViewById(R.id.ib_rotate2);
// 初始状态显示加载
if (mLiveProgressBar != null) {
mLiveProgressBar.setVisibility(View.VISIBLE);
}
// 初始根据方向更新UI
updateLayoutByOrientation();
// 创建线程池
mExecutorService = Executors.newFixedThreadPool(2);
// 4. 初始化云台控制按钮
initPTZButtons();
}
private void initPTZButtons() {
// 竖屏布局的按钮
setupPTZButton(R.id.ptz_left_btn, DIRECTION_LEFT);
setupPTZButton(R.id.ptz_right_btn, DIRECTION_RIGHT);
setupPTZButton(R.id.ptz_top_btn, DIRECTION_UP);
setupPTZButton(R.id.ptz_bottom_btn, DIRECTION_DOWN);
setupPTZButton(R.id.zoom_add, DIRECTION_ZOOM_IN); // 物理放大
setupPTZButton(R.id.zoom_reduce, DIRECTION_ZOOM_OUT); // 物理缩小
// 横屏布局的按钮
setupPTZButton(R.id.ptz_left_btn2, DIRECTION_LEFT);
setupPTZButton(R.id.ptz_right_btn2, DIRECTION_RIGHT);
setupPTZButton(R.id.ptz_top_btn2, DIRECTION_UP);
setupPTZButton(R.id.ptz_bottom_btn2, DIRECTION_DOWN);
setupPTZButton(R.id.zoom_add2, DIRECTION_ZOOM_IN);
setupPTZButton(R.id.zoom_reduce2, DIRECTION_ZOOM_OUT);
}
private void setupPTZButton(int buttonId, final int direction) {
ImageButton button = findViewById(buttonId);
if (button != null) {
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 按下时开始云台动作
controlPTZ(direction, SPEED_MEDIUM, true);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// 如果这是当前活动的方向,则停止
if (isPTZActive && activeDirection == direction) {
controlPTZ(direction, SPEED_MEDIUM, false);
}
return true;
}
return false;
}
});
}
}
// 云台控制方法
private void controlPTZ(final int direction, final int speed, final boolean isStart) {
// 检查是否允许开始新操作
if (isStart && isPTZActive) {
runOnUiThread(() -> Toast.makeText(MainActivity.this,
"请先停止当前云台操作", Toast.LENGTH_SHORT).show());
return;
}
mExecutorService.execute(() -> {
try {
String urlStr = isStart ? PTZ_START_URL : PTZ_STOP_URL;
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
// 构建POST数据
StringBuilder postData = new StringBuilder();
postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8"));
postData.append("&deviceSerial=").append(URLEncoder.encode(mDeviceSerial, "UTF-8"));
postData.append("&channelNo=").append(mCameraNo);
if (isStart) {
postData.append("&direction=").append(direction);
postData.append("&speed=").append(speed);
} else {
// 停止时带上方向参数(建议)
postData.append("&direction=").append(direction);
}
// 发送请求
OutputStream os = conn.getOutputStream();
os.write(postData.toString().getBytes("UTF-8"));
os.flush();
os.close();
// 处理响应
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取响应内容
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
// 解析JSON响应
JSONObject json = new JSONObject(response.toString());
String code = json.optString("code", "0");
String msg = json.optString("msg", "未知错误");
if ("200".equals(code)) {
Log.d(TAG, "PTZ控制成功: " + (isStart ? "开始" : "停止") +
" | 方向: " + direction);
// 更新状态
if (isStart) {
isPTZActive = true;
activeDirection = direction;
} else {
isPTZActive = false;
activeDirection = -1;
}
} else {
handlePTZError(code, msg, isStart);
}
} else {
Log.e(TAG, "HTTP错误: " + responseCode);
runOnUiThread(() -> Toast.makeText(MainActivity.this,
"网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show());
}
conn.disconnect();
} catch (Exception e) {
Log.e(TAG, "PTZ控制异常", e);
runOnUiThread(() -> Toast.makeText(MainActivity.this,
"云台控制出错: " + e.getMessage(), Toast.LENGTH_SHORT).show());
}
});
}
private void handlePTZError(String code, String msg, boolean isStart) {
String errorMessage = msg;
// 根据文档映射错误信息
switch (code) {
case "10001":
errorMessage = "参数错误";
break;
case "10002":
errorMessage = "accessToken异常或过期";
break;
case "20002":
errorMessage = "设备不存在";
break;
case "20007":
errorMessage = "设备不在线";
break;
case "60000":
errorMessage = "设备不支持云台控制";
mIsSupportPTZ = false; // 更新设备支持状态
break;
case "60001":
errorMessage = "用户无云台控制权限";
break;
case "60020":
errorMessage = "不支持该命令";
break;
// 添加其他错误码处理...
}
final String finalMsg = errorMessage;
runOnUiThread(() -> {
Toast.makeText(MainActivity.this,
(isStart ? "开始" : "停止") + "云台控制失败: " + finalMsg,
Toast.LENGTH_LONG).show();
// 对于特定错误,重置云台状态
if ("10002".equals(code) || "60000".equals(code)) {
isPTZActive = false;
activeDirection = -1;
}
});
}
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 方向变化时重新初始化视图
initOrientationSensitiveViews();
updateLayoutByOrientation();
}
@Override
protected void onResume() {
super.onResume();
if (mLiveProgressBar != null) {
if (mIsPlaying) {
mLiveProgressBar.setVisibility(View.GONE);
} else {
mLiveProgressBar.setVisibility(View.VISIBLE);
}
}
}
private void updateLayoutByOrientation() {
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
// 竖屏模式
mRlControl.setVisibility(View.VISIBLE);
mLlHc.setVisibility(View.GONE);
mIbRotate2.setVisibility(View.GONE);
} else {
// 横屏模式
mRlControl.setVisibility(View.GONE);
mLlHc.setVisibility(View.VISIBLE);
mIbRotate2.setVisibility(View.VISIBLE);
}
}
/**
* 从Intent中提取传递的参数
*/
private void extractParametersFromIntent() {
Bundle extras = getIntent().getExtras();
if (extras != null) {
mAppKey = extras.getString(KEY_APPKEY, "");
mDeviceSerial = extras.getString(KEY_SERIAL, "");
mVerifyCode = extras.getString(KEY_VERIFYCODE, "");
mAccessToken = extras.getString(KEY_ACCESSTOKEN, "");
mCameraNo = extras.getInt(KEY_CAMERANO, 0);
Log.d(TAG, "Received parameters:");
Log.d(TAG, "AppKey: " + mAppKey);
Log.d(TAG, "DeviceSerial: " + mDeviceSerial);
Log.d(TAG, "VerifyCode: " + mVerifyCode);
Log.d(TAG, "AccessToken: " + mAccessToken);
Log.d(TAG, "CameraNo: " + mCameraNo);
} else {
Log.e(TAG, "No parameters received from intent");
// 如果没有参数,可以显示错误信息并退出
finish();
}
}
/**
* 初始化UI组件
*/
private void initUI() {
mSurfaceView = findViewById(R.id.realplay_sv);
if (mSurfaceView != null) {
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
} else {
Log.e(TAG, "SurfaceView not found with ID realplay_sv");
}
}
/**
* 初始化SDK并创建播放器
*/
private void initSDKAndCreatePlayer() {
try {
// 1. 初始化SDK
EZOpenSDK.initLib(getApplication(), mAppKey);
EZOpenSDK.getInstance().setAccessToken(mAccessToken);
// +++ 开启P2P取流方式 +++
EZOpenSDK.enableP2P(true); // 开启P2P取流
Log.d(TAG, "P2P取流已启用");
mP2PEnabled = true;
// 2. 创建播放器
createPlayer();
} catch (Exception e) {
Log.e(TAG, "SDK初始化失败", e);
Toast.makeText(this, "SDK初始化失败", Toast.LENGTH_SHORT).show();
}
}
/**
* 创建播放器并开始播放
*/
private void createPlayer() {
try {
// 1. 创建播放器实例
mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo);
// 2. 配置播放器
mEZPlayer.setHandler(mHandler);
if (mSurfaceHolder != null) {
mEZPlayer.setSurfaceHold(mSurfaceHolder);
}
if (mVerifyCode != null && !mVerifyCode.isEmpty()) {
mEZPlayer.setPlayVerifyCode(mVerifyCode);
}
// 3. 开始播放
mEZPlayer.startRealPlay();
mIsPlaying = true; // 标记为正在播放
} catch (Exception e) {
Log.e(TAG, "Player creation failed", e);
mIsPlaying = false; // 标记为未播放
}
}
// 处理屏幕旋转按钮点击
public void changeScreen(View view) {
Log.d(TAG, "Change screen orientation requested");
int currentOrientation = getResources().getConfiguration().orientation;
if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
// 更新UI布局
updateLayoutByOrientation();
}
// Surface回调接口实现
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
if (mEZPlayer != null) {
mEZPlayer.setSurfaceHold(holder);
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
if (mEZPlayer != null) {
mEZPlayer.setSurfaceHold(null);
}
}
@Override
protected void onStop() {
super.onStop();
if (mEZPlayer != null) {
mEZPlayer.stopRealPlay();
mIsPlaying = false; // 标记为已停止
}
}
// Handler回调处理播放状态
@Override
public boolean handleMessage(@NonNull Message msg) {
Log.d(TAG, "handleMessage: " + msg.what);
switch (msg.what) {
case MSG_VIDEO_SIZE_CHANGED:
break;
case MSG_REALPLAY_PLAY_SUCCESS:
Log.i(TAG, "播放成功");
mIsPlaying = true;
// 获取并显示取流方式
int streamType = mEZPlayer.getStreamFetchType();
String streamTypeName = getStreamTypeName(streamType);
Log.d(TAG, "当前取流方式: " + streamTypeName);
// 发送消息显示取流方式
Message showMsg = new Message();
showMsg.what = MSG_SHOW_STREAM_TYPE;
showMsg.obj = streamTypeName;
mHandler.sendMessage(showMsg);
runOnUiThread(() -> {
if (mLiveProgressBar != null) {
mLiveProgressBar.setVisibility(View.GONE);
}
});
// 在播放成功后检查设备是否支持云台
checkDevicePTZSupport();
break;
case MSG_REALPLAY_PLAY_FAIL:
Log.e(TAG, "播放失败");
mIsPlaying = false;
runOnUiThread(() -> {
if (mLiveProgressBar != null) {
mLiveProgressBar.setVisibility(View.VISIBLE);
}
});
BaseException error = (BaseException) msg.obj;
int errorCode = error.getErrorCode();
if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_NEED ||
errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_ERROR) {
mVerifyCode = "123456";
if (mEZPlayer != null) {
mEZPlayer.setPlayVerifyCode(mVerifyCode);
mEZPlayer.startRealPlay();
}
} else {
Log.e(TAG, "播放失败,错误码: " + errorCode);
}
break;
case MSG_SHOW_STREAM_TYPE:
String type = (String) msg.obj;
Toast.makeText(MainActivity.this,
"取流方式: " + type + (mP2PEnabled ? " (P2P已启用)" : ""),
Toast.LENGTH_LONG).show();
break;
}
return true;
}
private void checkDevicePTZSupport() {
new Thread(() -> {
try {
// 这里使用SDK方法检查设备能力
// 如果SDK没有提供方法,可以使用HTTP API查询设备能力
// 简化处理:假设所有设备都支持云台
mIsSupportPTZ = true;
// 实际项目中应该查询设备能力
// mIsSupportPTZ = queryDevicePTZCapability();
} catch (Exception e) {
Log.e(TAG, "Failed to check PTZ support", e);
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mExecutorService != null) {
mExecutorService.shutdown();
}
// +++移除Handler回调避免内存泄漏 +++
if (mHandler != null) {
mHandler.removeCallbacksAndMessages(null);
}
if (mEZPlayer != null) {
mEZPlayer.release();
mEZPlayer = null;
}
}
private String getStreamTypeName(int type) {
switch (type) {
case 0: return "流媒体";
case 1: return "P2P";
case 2: return "内网直连";
case 4: return "云存储";
default: return "未知(" + type + ")";
}
}
}
依据上述代码解决报错:Cannot resolve symbol 'URLEncoder' Cannot resolve symbol 'InputStream' Cannot resolve symbol 'BufferedReader' Cannot resolve symbol 'InputStreamReader' Cannot resolve method 'readLine()' Cannot resolve method 'close()' Cannot resolve symbol 'JSONObject' Cannot resolve method 'optString(java.lang.String, java.lang.String)'
最新发布