MainActivity.java: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;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import org.json.JSONObject;
import android.content.Intent;
import android.widget.Button;
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 DIRECTION_FOCUS_NEAR = 10; // 调整近焦距
private static final int DIRECTION_FOCUS_FAR = 11; // 调整远焦距
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());
Button huifangBtn = findViewById(R.id.huifang);
huifangBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建Intent跳转到FanHui活动
Intent intent = new Intent(MainActivity.this, FanHui.class);
// 传递必要参数(可选)
intent.putExtra("deviceSerial", mDeviceSerial);
intent.putExtra("cameraNo", mCameraNo);
intent.putExtra("accessToken", mAccessToken);
intent.putExtra("appkey", mAppKey);
intent.putExtra("verifyCode", mVerifyCode);
startActivity(intent);
}
});
// 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.focus_add, DIRECTION_FOCUS_NEAR); // 近焦距
setupPTZButton(R.id.foucus_reduce, DIRECTION_FOCUS_FAR); // 远焦距
setupUnsupportedButton(R.id.guangquan_add, "光圈+");
setupUnsupportedButton(R.id.guangquan_reduce, "光圈-");
// 横屏布局的按钮
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);
setupPTZButton(R.id.focus_add2, DIRECTION_FOCUS_NEAR); // 近焦距
setupPTZButton(R.id.foucus_reduce2, DIRECTION_FOCUS_FAR); // 远焦距
}
private void setupUnsupportedButton(int buttonId, String buttonName) {
ImageButton button = findViewById(buttonId);
if (button != null) {
button.setOnClickListener(v -> {
Toast.makeText(MainActivity.this,
"SDK不支持此功能",
Toast.LENGTH_SHORT).show();
});
}
}
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 + ")";
}
}
}
FanHui.java:package com.videogo.ui.login;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import ezviz.ezopensdk.R;
import androidx.appcompat.app.AppCompatActivity;
public class FanHui extends AppCompatActivity {
private static final String TAG = "EZPreview";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ez_playback_list_page);
extractParametersFromIntent();
View fanHui = findViewById(R.id.fanhui);
fanHui.setOnClickListener(v -> finish());
}
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();
}
}
}
依据上述代码解决FanHui.java中的报错:Cannot resolve symbol 'mAppKey' Cannot resolve symbol 'KEY_APPKEY' Cannot resolve symbol 'mDeviceSerial' Cannot resolve symbol 'KEY_SERIAL' Cannot resolve symbol 'mVerifyCode' Cannot resolve symbol 'KEY_VERIFYCODE' Cannot resolve symbol 'mAccessToken' Cannot resolve symbol 'KEY_ACCESSTOKEN' Cannot resolve symbol 'mCameraNo' Cannot resolve symbol 'KEY_CAMERANO' Cannot resolve symbol 'mAppKey' Cannot resolve symbol 'mDeviceSerial' Cannot resolve symbol 'mVerifyCode' Cannot resolve symbol 'mAccessToken' Cannot resolve symbol 'mCameraNo'