package com.videogo.ui.login;
import android.app.AlertDialog;
import android.content.Intent;
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 android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.DatePicker;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.videogo.exception.BaseException;
import com.videogo.exception.ErrorCode;
import com.videogo.openapi.EZConstants;
import com.videogo.openapi.EZPlayer;
import androidx.appcompat.app.AppCompatActivity;
import com.videogo.openapi.EZOpenSDK;
import ezviz.ezopensdk.R;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FanHui extends AppCompatActivity implements SurfaceHolder.Callback {
private static final String TAG = "EZPlayback";
private String mAppKey;
private String mDeviceSerial;
private String mVerifyCode;
private String mAccessToken;
private int mCameraNo;
private TextView mDateTextView;
private int mSelectedYear, mSelectedMonth, mSelectedDay;
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 static final String VIDEO_BY_TIME_URL = "https://open.ys7.com/api/lapp/video/by/time";
private static final String PLAYBACK_URL_API = "https://open.ys7.com/api/lapp/v2/live/address/get";
private static final int PROTOCOL_EZOPEN = 1; // EZOpen协议
private ExecutorService mExecutorService;
private ListView mListView;
private PlaybackAdapter mAdapter;
private List<VideoInfo> mVideoList = new ArrayList<>();
// 播放器相关
private EZPlayer mEZPlayer;
private SurfaceView mPlaybackSurfaceView;
private SurfaceHolder mSurfaceHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ez_playback_list_page);
// 创建线程池
mExecutorService = Executors.newFixedThreadPool(2);
extractParametersFromIntent();
final Calendar calendar = Calendar.getInstance();
mSelectedYear = calendar.get(Calendar.YEAR);
mSelectedMonth = calendar.get(Calendar.MONTH);
mSelectedDay = calendar.get(Calendar.DAY_OF_MONTH);
// 初始化视图
initViews();
// 设置日期显示模块
setupDatePicker();
// 默认加载当天的录像
loadVideosForSelectedDate();
// 初始化SDK
initSDK();
}
private void initSDK() {
try {
// 初始化萤石云SDK
EZOpenSDK.initLib(getApplication(), mAppKey);
EZOpenSDK.getInstance().setAccessToken(mAccessToken);
} catch (Exception e) {
Log.e(TAG, "SDK初始化失败", e);
Toast.makeText(this, "SDK初始化失败", Toast.LENGTH_SHORT).show();
}
}
private void initViews() {
// 查找ListView
mListView = findViewById(R.id.listView);
if (mListView == null) {
Log.e(TAG, "ListView not found with ID listView");
return;
}
// 初始化适配器
mAdapter = new PlaybackAdapter();
mListView.setAdapter(mAdapter);
// 设置点击事件
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
VideoInfo video = mVideoList.get(position);
playVideo(video);
}
});
// 获取播放器视图 - 使用SurfaceView
mPlaybackSurfaceView = findViewById(R.id.remote_playback_wnd_sv);
if (mPlaybackSurfaceView != null) {
// 设置SurfaceHolder回调
mSurfaceHolder = mPlaybackSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
} else {
Log.e(TAG, "SurfaceView not found with ID remote_playback_wnd_sv");
}
}
// SurfaceHolder回调方法
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "Surface created");
// 当Surface创建时,如果有播放器实例,设置SurfaceHolder
if (mEZPlayer != null) {
mEZPlayer.setSurfaceHold(holder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "Surface changed: " + width + "x" + height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface destroyed");
// 当Surface销毁时,释放播放器资源
stopPlayback();
}
private void setupDatePicker() {
mDateTextView = findViewById(R.id.date_text);
ImageButton datePickerButton = findViewById(R.id.date_picker_button);
updateDateDisplay();
datePickerButton.setOnClickListener(v -> showDatePickerDialog());
}
private void updateDateDisplay() {
String formattedDate = String.format(Locale.getDefault(),
"%d年%02d月%02d日",
mSelectedYear,
mSelectedMonth + 1, // 月份需要+1
mSelectedDay);
mDateTextView.setText(formattedDate);
}
private void showDatePickerDialog() {
final AlertDialog dlg = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog).create();
dlg.show();
Window window = dlg.getWindow();
window.setContentView(R.layout.datepicker_layout);
// 设置对话框宽度
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(lp);
// 获取并初始化 DatePicker
DatePicker dpPicker = window.findViewById(R.id.dpPicker);
// 隐藏不需要的视图
ViewGroup rootView = (ViewGroup) dpPicker.getChildAt(0);
if (rootView != null) {
ViewGroup childView = (ViewGroup) rootView.getChildAt(0);
if (childView != null) {
childView.getChildAt(2).setVisibility(View.VISIBLE);
// 确保月选择器可见
childView.getChildAt(1).setVisibility(View.VISIBLE);
}
}
dpPicker.init(mSelectedYear, mSelectedMonth, mSelectedDay, null);
// 设置按钮事件
RelativeLayout yesButton = window.findViewById(R.id.YES);
RelativeLayout noButton = window.findViewById(R.id.NO);
yesButton.setOnClickListener(v -> {
mSelectedYear = dpPicker.getYear();
mSelectedMonth = dpPicker.getMonth();
mSelectedDay = dpPicker.getDayOfMonth();
updateDateDisplay();
dlg.dismiss();
// 加载新选择的日期的录像
loadVideosForSelectedDate();
});
noButton.setOnClickListener(v -> dlg.dismiss());
}
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");
}
}
private void loadVideosForSelectedDate() {
// 计算开始和结束时间戳
Calendar cal = Calendar.getInstance();
cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 0, 0, 0);
long startTime = cal.getTimeInMillis();
cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 23, 59, 59);
long endTime = cal.getTimeInMillis();
// 发起网络请求获取录像
fetchVideosByTime(startTime, endTime);
}
private void fetchVideosByTime(long startTime, long endTime) {
mExecutorService.execute(() -> {
try {
URL url = new URL(VIDEO_BY_TIME_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
// 构建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);
postData.append("&startTime=").append(startTime);
postData.append("&endTime=").append(endTime);
postData.append("&recType=").append(0); // 系统自动选择
postData.append("&version=2.0"); // 添加分页版本
postData.append("&pageSize=100"); // 添加分页大小
// 发送请求
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");
if ("200".equals(code)) {
JSONArray data = json.getJSONArray("data");
List<VideoInfo> videos = parseVideoData(data);
runOnUiThread(() -> {
mVideoList.clear();
mVideoList.addAll(videos);
mAdapter.notifyDataSetChanged();
});
} else {
String msg = json.optString("msg", "未知错误");
Log.e(TAG, "获取录像失败: " + msg);
runOnUiThread(() -> Toast.makeText(FanHui.this,
"获取录像失败: " + msg, Toast.LENGTH_SHORT).show());
}
} else {
Log.e(TAG, "HTTP错误: " + responseCode);
runOnUiThread(() -> Toast.makeText(FanHui.this,
"网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show());
}
conn.disconnect();
} catch (Exception e) {
Log.e(TAG, "获取录像异常", e);
runOnUiThread(() -> Toast.makeText(FanHui.this,
"获取录像出错: " + e.getMessage(), Toast.LENGTH_SHORT).show());
}
});
}
private List<VideoInfo> parseVideoData(JSONArray data) throws JSONException {
List<VideoInfo> videos = new ArrayList<>();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
for (int i = 0; i < data.length(); i++) {
JSONObject videoObj = data.getJSONObject(i);
VideoInfo video = new VideoInfo();
video.id = videoObj.optString("id");
video.startTime = videoObj.optLong("startTime");
video.endTime = videoObj.optLong("endTime");
video.recType = videoObj.optInt("recType");
// 格式化时间显示
Date startDate = new Date(video.startTime);
Date endDate = new Date(video.endTime);
video.timeRange = sdf.format(startDate) + " - " + sdf.format(endDate);
videos.add(video);
}
return videos;
}
private void playVideo(VideoInfo video) {
// 停止当前播放
stopPlayback();
// 获取播放地址
fetchPlaybackUrl(video);
}
/**
* 获取回放地址并播放
*/
private void fetchPlaybackUrl(VideoInfo video) {
mExecutorService.execute(() -> {
try {
// 构建开始和结束时间字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
String startTimeStr = sdf.format(new Date(video.startTime));
String stopTimeStr = sdf.format(new Date(video.endTime));
// 构建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);
postData.append("&protocol=").append(PROTOCOL_EZOPEN);
postData.append("&type=2"); // 2表示本地录像回放
postData.append("&startTime=").append(URLEncoder.encode(startTimeStr, "UTF-8"));
postData.append("&stopTime=").append(URLEncoder.encode(stopTimeStr, "UTF-8"));
postData.append("&quality=1"); // 1-高清
URL url = new URL(PLAYBACK_URL_API);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
// 发送请求
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");
if ("200".equals(code)) {
JSONObject data = json.getJSONObject("data");
String playbackUrl = data.getString("url");
// 回到主线程播放视频
runOnUiThread(() -> playWithUrl(playbackUrl, video));
} else {
String msg = json.optString("msg", "未知错误");
Log.e(TAG, "获取播放地址失败: " + msg);
runOnUiThread(() ->
Toast.makeText(FanHui.this, "获取播放地址失败: " + msg, Toast.LENGTH_SHORT).show());
}
} else {
Log.e(TAG, "HTTP错误: " + responseCode);
runOnUiThread(() ->
Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show());
}
conn.disconnect();
} catch (Exception e) {
Log.e(TAG, "获取播放地址异常", e);
runOnUiThread(() ->
Toast.makeText(FanHui.this, "获取播放地址出错: " + e.getMessage(), Toast.LENGTH_SHORT).show());
}
});
}
/**
* 使用URL播放视频
*/
private void playWithUrl(String url, VideoInfo video) {
try {
// 创建播放器实例
mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo);
// 设置回调处理器
mEZPlayer.setHandler(mHandler);
// 设置验证码
mEZPlayer.setPlayVerifyCode(mVerifyCode);
// 关联播放视图
if (mSurfaceHolder != null) {
mEZPlayer.setSurfaceHold(mSurfaceHolder);
} else {
Log.e(TAG, "无法关联播放视图");
Toast.makeText(this, "播放视图未初始化", Toast.LENGTH_SHORT).show();
return;
}
// 开始播放
mEZPlayer.playUrl(url);
Toast.makeText(this, "开始播放录像: " + video.timeRange, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Log.e(TAG, "播放录像失败", e);
Toast.makeText(this, "播放录像失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
private void stopPlayback() {
if (mEZPlayer != null) {
mEZPlayer.stopPlayback();
mEZPlayer.release();
mEZPlayer = null;
}
}
// 播放器回调处理器
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EZConstants.EZRealPlayConstants.MSG_REALPLAY_PLAY_SUCCESS:
Log.i(TAG, "回放播放成功");
break;
case EZConstants.EZPlayBackConstants.MSG_PLAYBACK_PLAY_SUCCESS:
Log.i(TAG, "URL回放播放成功");
break;
case EZConstants.EZRealPlayConstants.MSG_REALPLAY_PLAY_FAIL:
case EZConstants.EZPlayBackConstants.MSG_PLAYBACK_PLAY_FAIL:
Log.e(TAG, "回放播放失败");
BaseException error = (BaseException) msg.obj;
int errorCode = error.getErrorCode();
String errorMsg = "播放失败: " + errorCode;
if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_NEED) {
errorMsg = "需要验证码";
} else if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_ERROR) {
errorMsg = "验证码错误";
} else if (errorCode == ErrorCode.ERROR_TRANSF_ACCESSTOKEN_ERROR) {
errorMsg = "accessToken无效";
}
Toast.makeText(FanHui.this, errorMsg, Toast.LENGTH_LONG).show();
break;
}
}
};
@Override
protected void onPause() {
super.onPause();
if (mEZPlayer != null) {
mEZPlayer.stopRealPlay();
mEZPlayer.release();
mEZPlayer = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stopPlayback();
if (mExecutorService != null) {
mExecutorService.shutdown();
}
}
// 录像信息数据结构
private static class VideoInfo {
String id;
long startTime;
long endTime;
int recType;
String timeRange;
}
// 列表适配器
private class PlaybackAdapter extends BaseAdapter {
@Override
public int getCount() {
return mVideoList.size();
}
@Override
public Object getItem(int position) {
return mVideoList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = getLayoutInflater().inflate(R.layout.video_item_layout, parent, false);
holder = new ViewHolder();
holder.timeTextView = convertView.findViewById(R.id.time_text);
holder.durationTextView = convertView.findViewById(R.id.duration_text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
VideoInfo video = mVideoList.get(position);
// 计算持续时间(分钟)
long durationMinutes = (video.endTime - video.startTime) / (1000 * 60);
holder.timeTextView.setText(video.timeRange);
holder.durationTextView.setText(durationMinutes + "分钟");
return convertView;
}
class ViewHolder {
TextView timeTextView;
TextView durationTextView;
}
}
}
依据上述代码解决报错:Cannot resolve method 'playUrl' in 'EZPlayer' Cannot resolve symbol 'EZPlayBackConstants'(498行)Cannot resolve symbol 'EZPlayBackConstants'(503行)。
最新发布