停止云台控制
接口功能
设备停止云台控制
请求地址
https://open.ys7.com/api/lapp/device/ptz/stop
请求方式
POST
子账户token请求所需最小权限
"Permission":"Ptz" "Resource":"Cam:序列号:通道号"
请求参数
参数名 类型 描述 是否必选
accessToken String 授权过程获取的access_token Y
deviceSerial String 设备序列号,存在英文字母的设备序列号,字母需为大写 Y
channelNo int 通道号 Y
direction int 操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距,16-自动控制 N
提示:建议停止云台接口带方向参数。
HTTP请求报文
POST /api/lapp/device/ptz/stop HTTP/1.1
Host: open.ys7.com
Content-Type: application/x-www-form-urlencoded
accessToken=at.25ne3gkr6fa7coh34ys0fl1h9hryc2kr&deviceSerial=568261888&channelNo=1&direction=1
返回数据
{
"code": "200",
"msg": "操作成功!"
}
返回码
返回码 返回消息 描述
200 操作成功 请求成功
10001 参数错误 参数为空或格式不正确
10002 accessToken异常或过期 重新获取accessToken
10005 appKey异常 appKey被冻结
20002 设备不存在
20006 网络异常 检查设备网络状况,稍后再试
20007 设备不在线 检查设备是否在线
20008 设备响应超时 操作过于频繁,稍后再试
20014 deviceSerial不合法
20032 该用户下通道不存在 该用户下通道不存在
49999 数据异常 接口调用异常
60000 设备不支持云台控制
60001 用户无云台控制权限
60006 云台当前操作失败 稍候再试
60009 正在调用预置点
60020 不支持该命令 确认设备是否支持该操作 开始云台控制
接口功能
对设备进行开始云台控制,开始云台控制之后必须先调用停止云台控制接口才能进行其他操作,包括其他方向的云台转动
请求地址
https://open.ys7.com/api/lapp/device/ptz/start
请求方式
POST
子账户token请求所需最小权限
"Permission":"Ptz" "Resource":"Cam:序列号:通道号"
请求参数
参数名 类型 描述 是否必选
accessToken String 授权过程获取的access_token Y
deviceSerial String 设备序列号,存在英文字母的设备序列号,字母需为大写 Y
channelNo int 通道号 Y
direction int 操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-物理放大,9-物理缩小,10-调整近焦距,11-调整远焦距,16-自动控制 Y
speed int 云台速度:0-慢,1-适中,2-快,海康设备参数不可为0 Y
HTTP请求报文
POST /api/lapp/device/ptz/start HTTP/1.1
Host: open.ys7.com
Content-Type: application/x-www-form-urlencoded
accessToken=at.4g01l53x0w22xbp30ov33q44app1ns9m&deviceSerial=502608888&channelNo=1&direction=2&speed=1
返回数据
{
"code": "200",
"msg": "操作成功!"
}
返回码
返回码 返回消息 描述
200 操作成功 请求成功
10001 参数错误 参数为空或格式不正确
10002 accessToken异常或过期 重新获取accessToken
10005 appKey异常 appKey被冻结
20002 设备不存在
20006 网络异常 检查设备网络状况,稍后再试
20007 设备不在线 检查设备是否在线
20008 设备响应超时 操作过于频繁,稍后再试
20014 deviceSerial不合法
20032 该用户下通道不存在 该用户下通道不存在
49999 数据异常 接口调用异常
60000 设备不支持云台控制
60001 用户无云台控制权限
60002 设备云台旋转达到上限位
60003 设备云台旋转达到下限位
60004 设备云台旋转达到左限位
60005 设备云台旋转达到右限位
60006 云台当前操作失败 稍候再试
60009 正在调用预置点
60020 不支持该命令 确认设备是否支持该操作 MainActivity:package com.hik.netsdk.SimpleDemo.View;
import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.gson.Gson;
import com.hik.netsdk.SimpleDemo.R;
import com.hik.netsdk.SimpleDemo.View.DevMgtUI.AddDevActivity;
import com.videogo.errorlayer.ErrorInfo;
import com.videogo.openapi.EZConstants;
import com.videogo.openapi.EZOpenSDK;
import com.videogo.openapi.EZPlayer;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
// 核心参数
private String mDeviceSerial;
private String mVerifyCode;
private int mCameraNo = 1;
private boolean isEzDevice = false;
// 萤石云相关参数
private String mAppKey = "a794d58c13154caeb7d2fbb5c3420c65";
private String mAccessToken = "";
// UI组件
private SurfaceView mPreviewSurface;
private SurfaceHolder mSurfaceHolder;
private Toolbar m_toolbar;
private ProgressBar mProgressBar;
private RelativeLayout mControlLayout;
private ImageButton mRotateButton;
// 播放器相关
private SimpleExoPlayer mExoPlayer;
private String mPlaybackUrl;
private final OkHttpClient mHttpClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.build();
// 播放器状态监听器
private final Player.Listener mPlayerListener = new Player.Listener() {
@Override
public void onPlaybackStateChanged(int state) {
switch (state) {
case Player.STATE_READY:
mProgressBar.setVisibility(View.GONE);
Log.d("ExoPlayer", "播放准备就绪");
break;
case Player.STATE_BUFFERING:
mProgressBar.setVisibility(View.VISIBLE);
Log.d("ExoPlayer", "缓冲中...");
break;
case Player.STATE_ENDED:
Log.d("ExoPlayer", "播放结束");
break;
case Player.STATE_IDLE:
Log.d("ExoPlayer", "空闲状态");
break;
}
}
@Override
public void onPlayerError(com.google.android.exoplayer2.PlaybackException error) {
Log.e("ExoPlayer", "播放错误: " + error.getMessage());
handleError("播放错误: " + error.getMessage(), -1);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化UI组件
initUIComponents();
// 获取启动参数
parseIntentParams();
// 初始化SDK
initSDK();
// 参数校验
if (mDeviceSerial == null || mDeviceSerial.isEmpty()) {
showErrorAndFinish("设备序列号不能为空");
return;
}
Log.d("MainActivity", "onCreate完成: isEzDevice=" + isEzDevice);
}
private void initUIComponents() {
// 基础UI
m_toolbar = findViewById(R.id.toolbar);
setSupportActionBar(m_toolbar);
// 预览相关UI
mPreviewSurface = findViewById(R.id.realplay_sv);
mSurfaceHolder = mPreviewSurface.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
mProgressBar = findViewById(R.id.liveProgressBar);
mControlLayout = findViewById(R.id.rl_control);
mRotateButton = findViewById(R.id.ib_rotate2);
// 设置旋转按钮点击事件
mRotateButton.setOnClickListener(v -> changeScreen());
// 隐藏管理UI
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer != null) {
drawer.setVisibility(View.GONE);
}
if (m_toolbar != null) {
m_toolbar.setVisibility(View.GONE);
}
// 初始化控制按钮
initControlButtons();
Log.d("UI", "UI组件初始化完成");
}
private void parseIntentParams() {
// 获取Intent参数
Intent intent = getIntent();
isEzDevice = true; // 只支持萤石设备
mAccessToken = intent.getStringExtra("accessToken");
// 优先使用bundle
Bundle bundle = intent.getExtras();
if (bundle != null) {
mDeviceSerial = bundle.getString("devSn");
mVerifyCode = bundle.getString("verifyCode");
mCameraNo = bundle.getInt("cameraNo", 1);
}
// 兼容直接Extra方式
if (mDeviceSerial == null) mDeviceSerial = intent.getStringExtra("devSn");
if (mVerifyCode == null) mVerifyCode = intent.getStringExtra("verifyCode");
if (mCameraNo == 0) mCameraNo = intent.getIntExtra("cameraNo", 1);
Log.d("Params", "设备序列号: " + mDeviceSerial + ", 通道号: " + mCameraNo);
}
private void initSDK() {
try {
// 使用反射检查初始化状态
boolean isInitialized = false;
try {
// 尝试获取实例
EZOpenSDK instance = EZOpenSDK.getInstance();
if (instance != null) {
isInitialized = true;
}
} catch (Exception e) {
// 捕获未初始化的异常
isInitialized = false;
}
// 未初始化时进行初始化
if (!isInitialized) {
EZOpenSDK.initLib(getApplication(), mAppKey);
Log.d("EZSDK", "萤石SDK初始化完成");
}
// 设置AccessToken
if (mAccessToken != null && !mAccessToken.isEmpty()) {
EZOpenSDK.getInstance().setAccessToken(mAccessToken);
Log.d("EZToken", "AccessToken设置成功");
} else {
Log.w("EZToken", "AccessToken缺失!");
}
} catch (Exception e) {
Log.e("EZSDK", "萤石SDK初始化失败", e);
handleError("萤石SDK初始化失败: " + e.getMessage(), -1);
}
}
private void initControlButtons() {
// 云台控制按钮
findViewById(R.id.ptz_top_btn).setOnClickListener(v -> controlPTZ("UP"));
findViewById(R.id.ptz_bottom_btn).setOnClickListener(v -> controlPTZ("DOWN"));
findViewById(R.id.ptz_left_btn).setOnClickListener(v -> controlPTZ("LEFT"));
findViewById(R.id.ptz_right_btn).setOnClickListener(v -> controlPTZ("RIGHT"));
// 变焦控制
findViewById(R.id.focus_add).setOnClickListener(v -> controlZoom("ZOOM_IN"));
findViewById(R.id.foucus_reduce).setOnClickListener(v -> controlZoom("ZOOM_OUT"));
// 水平布局控制按钮
findViewById(R.id.ptz_top_btn2).setOnClickListener(v -> controlPTZ("UP"));
findViewById(R.id.ptz_bottom_btn2).setOnClickListener(v -> controlPTZ("DOWN"));
findViewById(R.id.ptz_left_btn2).setOnClickListener(v -> controlPTZ("LEFT"));
findViewById(R.id.ptz_right_btn2).setOnClickListener(v -> controlPTZ("RIGHT"));
// 添加萤石云特有功能按钮
findViewById(R.id.btn_record).setOnClickListener(v -> startLocalRecord());
findViewById(R.id.btn_stop_record).setOnClickListener(v -> stopLocalRecord());
findViewById(R.id.btn_capture).setOnClickListener(v -> capturePicture());
Log.d("Controls", "控制按钮初始化完成");
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d("Surface", "Surface created");
// 获取播放地址并开始播放
new Thread(this::fetchPlaybackUrl).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d("Surface", "Surface changed: " + width + "x" + height);
if (mExoPlayer != null) {
mExoPlayer.setVideoSurfaceHolder(holder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d("Surface", "Surface destroyed");
stopPreview();
}
// ======================== 播放地址获取与播放 ========================
private void fetchPlaybackUrl() {
try {
// 构建请求参数
JSONObject params = new JSONObject();
params.put("accessToken", mAccessToken);
params.put("deviceSerial", mDeviceSerial);
params.put("channelNo", mCameraNo);
params.put("protocol", 2); // 使用HLS协议
if (mVerifyCode != null && !mVerifyCode.isEmpty()) {
params.put("code", mVerifyCode);
}
params.put("expireTime", 7200); // 2小时有效期
// 创建请求体
RequestBody body = RequestBody.create(
MediaType.parse("application/json"),
params.toString()
);
// 创建请求
Request request = new Request.Builder()
.url("https://open.ys7.com/api/lapp/v2/live/address/get")
.post(body)
.build();
// 执行请求
try (Response response = mHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
handleError("获取播放地址失败: " + response.code(), response.code());
return;
}
// 解析响应
String responseBody = response.body().string();
JSONObject json = new JSONObject(responseBody);
if ("200".equals(json.getString("code"))) {
JSONObject data = json.getJSONObject("data");
mPlaybackUrl = data.getString("url");
Log.d("PlaybackURL", "获取到播放地址: " + mPlaybackUrl);
// 在主线程初始化播放器
runOnUiThread(this::initExoPlayer);
} else {
handleError("API错误: " + json.getString("msg"), json.getInt("code"));
}
}
} catch (JSONException | IOException e) {
Log.e("FetchURL", "获取播放地址异常", e);
handleError("获取播放地址异常: " + e.getMessage(), -1);
}
}
private void initExoPlayer() {
// 释放现有播放器
if (mExoPlayer != null) {
mExoPlayer.release();
}
// 创建新的播放器实例
mExoPlayer = new SimpleExoPlayer.Builder(this).build();
mExoPlayer.addListener(mPlayerListener);
mExoPlayer.setVideoSurfaceHolder(mSurfaceHolder);
// 创建媒体源
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
MediaSource mediaSource = new HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(Uri.parse(mPlaybackUrl)));
// 准备播放
mExoPlayer.setMediaSource(mediaSource);
mExoPlayer.prepare();
mExoPlayer.setPlayWhenReady(true);
mProgressBar.setVisibility(View.VISIBLE);
Log.d("ExoPlayer", "播放器初始化完成,开始播放");
}
// ======================== 萤石云控制方法 ========================
private void controlPTZ(String direction) {
try {
EZConstants.EZPTZAction action;
switch (direction) {
case "UP":
action = EZConstants.EZPTZAction.EZPTZActionUp;
break;
case "DOWN":
action = EZConstants.EZPTZAction.EZPTZActionDown;
break;
case "LEFT":
action = EZConstants.EZPTZAction.EZPTZActionLeft;
break;
case "RIGHT":
action = EZConstants.EZPTZAction.EZPTZActionRight;
break;
default:
return;
}
// 使用EZOpenSDK的API进行控制
EZOpenSDK.getInstance().controlPTZ(
mDeviceSerial,
mCameraNo,
action,
EZConstants.EZPTZCommand.EZPTZCommandStart,
2
);
// 300ms后停止控制
new Handler().postDelayed(() -> {
EZOpenSDK.getInstance().controlPTZ(
mDeviceSerial,
mCameraNo,
EZConstants.EZPTZAction.EZPTZActionStop,
EZConstants.EZPTZCommand.EZPTZCommandStop,
0
);
}, 300);
} catch (Exception e) {
Log.e("EZPTZ", "云台控制异常", e);
}
}
private void controlZoom(String command) {
try {
EZConstants.EZPTZAction action = "ZOOM_IN".equals(command) ?
EZConstants.EZPTZAction.EZPTZActionZoomIn :
EZConstants.EZPTZAction.EZPTZActionZoomOut;
// 使用EZOpenSDK的API进行控制
EZOpenSDK.getInstance().controlPTZ(
mDeviceSerial,
mCameraNo,
action,
EZConstants.EZPTZCommand.EZPTZCommandStart,
2
);
// 300ms后停止控制
new Handler().postDelayed(() -> {
EZOpenSDK.getInstance().controlPTZ(
mDeviceSerial,
mCameraNo,
EZConstants.EZPTZAction.EZPTZActionStop,
EZConstants.EZPTZCommand.EZPTZCommandStop,
0
);
}, 300);
} catch (Exception e) {
Log.e("EZZoom", "变焦控制异常", e);
}
}
// ======================== 萤石云扩展功能 ========================
// 开始本地录像
private void startLocalRecord() {
// 这里需要根据实际需求实现
Toast.makeText(this, "开始录像", Toast.LENGTH_SHORT).show();
}
// 停止本地录像
private void stopLocalRecord() {
// 这里需要根据实际需求实现
Toast.makeText(this, "停止录像", Toast.LENGTH_SHORT).show();
}
// 截图
private void capturePicture() {
// 这里需要根据实际需求实现
Toast.makeText(this, "截图已保存", Toast.LENGTH_SHORT).show();
}
// ======================== 通用功能方法 ========================
public void changeScreen(View view) {
changeScreen();
}
private void changeScreen() {
if (mControlLayout.getVisibility() == View.VISIBLE) {
mControlLayout.setVisibility(View.GONE);
} else {
mControlLayout.setVisibility(View.VISIBLE);
}
}
private void handleError(String message, int errorCode) {
String fullMessage = message + " (错误码: " + errorCode + ")";
// 萤石云特有错误码处理
switch (errorCode) {
case 400001:
fullMessage = "AccessToken无效";
break;
case 400002:
fullMessage = "设备不存在";
break;
case 400007:
fullMessage = "设备不在线";
break;
case 400034:
fullMessage = "验证码错误";
break;
case 400035:
fullMessage = "设备已被自己添加";
break;
case 400036:
fullMessage = "设备已被别人添加";
break;
default:
fullMessage = "萤石云错误: " + errorCode;
}
new AlertDialog.Builder(this)
.setTitle("预览失败")
.setMessage(fullMessage)
.setPositiveButton("确定", (d, w) -> finish())
.setCancelable(false)
.show();
}
private void showErrorAndFinish(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
new Handler().postDelayed(this::finish, 3000);
}
private void stopPreview() {
if (mExoPlayer != null) {
mExoPlayer.release();
mExoPlayer = null;
Log.d("Preview", "预览已停止");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stopPreview();
Log.d("Lifecycle", "onDestroy");
}
@Override
protected void onPause() {
super.onPause();
if (mExoPlayer != null) {
mExoPlayer.setPlayWhenReady(false);
Log.d("Lifecycle", "暂停播放");
}
}
@Override
protected void onResume() {
super.onResume();
if (mExoPlayer != null) {
mExoPlayer.setPlayWhenReady(true);
Log.d("Lifecycle", "恢复播放");
}
}
@Override
public void onBackPressed() {
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer != null && drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main_opt, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_1) {
startActivity(new Intent(this, AddDevActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
依据上述开发文档中停止云台控制和开始云台控制更新MainActivity代码实现完全符合开发文档的功能。