前言:上一篇博文已经分享了如何获取直播列表,本篇博文是在上一篇博文的基础上做了完善,实现了实时预览和云台控制。因此,部分代码会有重复。
步骤一:在 萤石云开发平台 注册开发者并添加球机设备
步骤二:获取accessToken和直播列表请参考上一篇博文 海康威视4G球机对接萤石云平台获取直播视频列表
步骤三:云台控制
后端代码:action类
package com.jk.action; import com.jk.comm.action.BaseAction; import com.jiankong.util.HttpRequest; import com.jiankong.util.TokenThread; public class JianKongAction extends BaseAction{<!-- --> private String device; //设备序列号,存在英文字母的设备序列号,字母需为大写 private Integer channelNo; //通道号 private Integer direction; //操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距 private Integer speed; //云台速度:0-慢,1-适中,2-快,海康设备参数不可为0 public String getDevice() {<!-- --> return device; } public void setDevice(String device) {<!-- --> this.device = device; } public Integer getChannelNo() {<!-- --> return channelNo; } public void setChannelNo(Integer channelNo) {<!-- --> this.channelNo = channelNo; } public Integer getDirection() {<!-- --> return direction; } public void setDirection(Integer direction) {<!-- --> this.direction = direction; } public Integer getSpeed() {<!-- --> return speed; } public void setSpeed(Integer speed) {<!-- --> this.speed = speed; } /** * */ private static final long serialVersionUID = 1L; /** * 获取直播列表 */ public void getList(){<!-- --> String accessToken = MyHttpRequest.getAccessToken("https://open.ys7.com/api/lapp/token/get?appKey=xxxxxxxxxxxxxxxx&appSecret=xxxxxxxxxxxxxxxxx"); //获取直播列表 String list = HttpRequest.getList("https://open.ys7.com/api/lapp/live/video/list?accessToken="+accessToken+"&pageStart=0&pageSize=3"); printJsonString(list); } /** * 云台控制 * 开始云台控制之后必须先调用停止云台控制接口才能进行其他操作,包括其他方向的云台转动 */ public void setCloudControl(){<!-- --> String accessToken = MyHttpRequest.getAccessToken("https://open.ys7.com/api/lapp/token/get?appKey=xxxxxxxxxxxxxxxx&appSecret=xxxxxxxxxxxxxxxxx"); String start = "accessToken="+accessToken+"&deviceSerial="+device+"&channelNo="+channelNo+"&direction="+direction+"&speed="+speed; String stop = "accessToken="+accessToken+"&deviceSerial="+device+"&channelNo="+channelNo+"&direction="+direction; jsonMsg = HttpRequest.setStartCloudControl("https://open.ys7.com/api/lapp/device/ptz/start?"+start); if(jsonMsg.equals("开始云台控制成功")){<!-- --> HttpRequest.setStopCloudControl("https://open.ys7.com/api/lapp/device/ptz/stop?"+stop); } printJsonString(jsonMsg); } }
action父类:
public class BaseAction extends ActionSupport {<!-- --> protected int page = 1; //当前第几页 protected int rows = 10; // 每页数量 protected String jsonMsg = ""; /** * 打印Json字符串返回给客户 * @param jsonString * @Description: */ protected void printJsonString ( String jsonString ) {<!-- --> PrintWriter out = null; try {<!-- --> HttpServletResponse response = getResponse(); response.setCharacterEncoding ( "UTF-8" ); response.setContentType("text/html;charset=UTF-8"); out = response.getWriter (); out.print ( jsonString ); } catch (IOException e) {<!-- --> e.printStackTrace (); } finally {<!-- --> out.close (); } } }
util类
package com.jiankong.util; import java.util.HashMap; import java.util.Map; import net.sf.json.JSONObject; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.params.ClientPNames; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; public class HttpRequest {<!-- --> /** * 获取监控应用的 accessToken * @param url * @return */ public static String getAccessToken(String url) {<!-- --> DefaultHttpClient client = new DefaultHttpClient(); client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true); HttpPost httpost = getPostMethod(url); String accessToken = ""; try {<!-- --> try {<!-- --> HttpResponse response = client.execute(httpost); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println("jsonStr:"+jsonStr); JSONObject jsStr = JSONObject.fromObject(jsonStr); System.out.println("jsStr:"+jsStr); String code = String.valueOf(jsStr.get("code")); if(code.equals("200")){<!-- --> String data = jsStr.getString("data"); JSONObject dataJson = JSONObject.fromObject(data); accessToken = dataJson.getString("accessToken"); return accessToken; } } catch (Exception e) {<!-- --> e.printStackTrace(); } } catch (Exception e) {<!-- --> e.printStackTrace(); } finally {<!-- --> if(!httpost.isAborted()){<!-- --> httpost.abort(); } client.getConnectionManager().shutdown(); } return accessToken; } /** * 获取直播视频列表 * @param url * @return */ public static String getList(String url) {<!-- --> DefaultHttpClient client = new DefaultHttpClient(); client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true); HttpPost httpost = getPostMethod(url); String list = ""; try {<!-- --> try {<!-- --> HttpResponse response = client.execute(httpost); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println("jsonStr:"+jsonStr); JSONObject jsStr = JSONObject.fromObject(jsonStr); String code = jsStr.getString("code"); if(code.equals("200")){<!-- --> list = jsStr.getString("data"); return list; }else if(code.equals("10002")){<!-- --> //accessToken过期或异常 } } catch (Exception e) {<!-- --> e.printStackTrace(); } } catch (Exception e) {<!-- --> e.printStackTrace(); } finally {<!-- --> if(!httpost.isAborted()){<!-- --> httpost.abort(); } client.getConnectionManager().shutdown(); } return list; } public static String setStartCloudControl(String url){<!-- --> DefaultHttpClient client = new DefaultHttpClient(); client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true); HttpPost httpost = getPostMethod(url); String msg = ""; try {<!-- --> try {<!-- --> HttpResponse response = client.execute(httpost); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println("jsonStr:"+jsonStr); JSONObject jsStr = JSONObject.fromObject(jsonStr); String code = jsStr.getString("code"); if(code.equals("200")){<!-- --> msg = "开始云台控制成功"; }else{<!-- --> msg = "开始云台控制失败"; } } catch (Exception e) {<!-- --> e.printStackTrace(); } } catch (Exception e) {<!-- --> e.printStackTrace(); } finally {<!-- --> if(!httpost.isAborted()){<!-- --> httpost.abort(); } client.getConnectionManager().shutdown(); } return msg; } public static String setStopCloudControl(String url){<!-- --> DefaultHttpClient client = new DefaultHttpClient(); client.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true); HttpPost httpost = getPostMethod(url); String msg = ""; try {<!-- --> try {<!-- --> HttpResponse response = client.execute(httpost); String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8"); System.out.println("jsonStr:"+jsonStr); JSONObject jsStr = JSONObject.fromObject(jsonStr); String code = jsStr.getString("code"); if(code.equals("200")){<!-- --> msg = "停止云台控制成功"; }else{<!-- --> msg = "停止云台控制失败"; } } catch (Exception e) {<!-- --> e.printStackTrace(); } } catch (Exception e) {<!-- --> e.printStackTrace(); } finally {<!-- --> if(!httpost.isAborted()){<!-- --> httpost.abort(); } client.getConnectionManager().shutdown(); } return msg; } /** * 模拟浏览器post提交 * * @param url * @return */ public static HttpPost getPostMethod(String url) {<!-- --> HttpPost pmethod = new HttpPost(url); // 设置响应头信息 pmethod.addHeader("Connection", "keep-alive"); pmethod.addHeader("Accept", "*/*"); pmethod.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); pmethod.addHeader("Host", "open.ys7.com"); pmethod.addHeader("X-Requested-With", "XMLHttpRequest"); pmethod.addHeader("Cache-Control", "max-age=0"); pmethod.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); return pmethod; } }
前端页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>监控网点</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <link href="${basePath}static/css/jiankong.css" rel="stylesheet" type="text/css"/> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> <style type="text/css"> body{<!-- -->margin:0;} #myPlayer{max-width: 600px;width: 100%;display: inline;} #myPlayer1{max-width: 600px;width: 100%;display: inline;} #myPlayer2{max-width: 600px;width: 100%;display: inline;} .num{<!-- --> max-width: 600px; display: inline; } .head{<!-- --> display: inline-grid; width: 600px; } </style> <script src="${basePath}static/js/hontek/jiankong/ezuikit.js" type="text/javascript"></script> <script src="${basePath}static/js/jquery/jquery-1.8.0.min.js" type="text/javascript"></script> <script src="${basePath}static/js/layer/layer.js" type="text/javascript"></script> </head> <body> <div class="head"> <div class="num">1号监控点</div> <video id="myPlayer" controls playsInline webkit-playsinline autoplay> <source id="playVideo0" src="" type="rtmp/flv" /> </video> </div> <div class="head"> <div class="num">2号监控点</div> <video id="myPlayer1" controls playsInline webkit-playsinline autoplay> <source id="playVideo1" src="" type="rtmp/flv" /> </video> </div> <div class="head"> <div class="num">3号监控点</div> <video id="myPlayer2" controls playsInline webkit-playsinline autoplay> <source id="playVideo2" src="" type="rtmp/flv" /> </video> </div> <div class="head"> <fieldset class="ptz" style="position: absolute;margin-top: 80px;margin-left: 60px;"> <legend>云台控制</legend> <table cellpadding="0" cellspacing="3" border="0" class="left"> <tr> <td> <input type="button" class="btn" value="左上" onclick="cloudControl(4)" /> <input type="button" class="btn" value="上" onclick="cloudControl(0)"/> <input type="button" class="btn" value="右上" onclick="cloudControl(6)" /> </td> </tr> <tr> <td> <input type="button" class="btn" value="左" onclick="cloudControl(2)"/> <input type="button" class="btn" value="自动" onclick="" style="display:none"/> <input type="button" class="btn" value="右" onclick="cloudControl(3)" style="position: unset;margin-left: 49px;"/> </td> </tr> <tr> <td> <input type="button" class="btn" value="左下" onclick="cloudControl(5)" /> <input type="button" class="btn" value="下" onclick="cloudControl(1)"/> <input type="button" class="btn" value="右下" onclick="cloudControl(7)"/> </td> </tr> </table> <table cellpadding="0" cellspacing="3" border="0" class="left"> <tr> <td class="tt">云台速度</td> <td> <select id="ptzspeed" class="sel"> <option value="1" >适中</option> <option value="2">快</option> </select> </td> </tr> <tr> <td class="tt">监控点</td> <td id="jkd"> </td> </tr> </table> <table cellpadding="0" cellspacing="3" border="0" class="left"> <tr> <td class="tt"><input type="button" class="btn2" value="变倍+" onclick="cloudControl(8)"></td> <td><input type="button" class="btn2" value="变倍-" onclick="cloudControl(9)"></td> </tr> <tr style="position: absolute;margin-top: 20px;margin-left: -2px;"> <td class="tt"><input type="button" class="btn2" value="变焦+" onclick="cloudControl(10)"></td> <td><input type="button" class="btn2" value="变焦-" onclick="cloudControl(11)"></td> </tr> </table> </fieldset> </div> <script> $.post('jiankong_getList.action','',function(res){<!-- --> if(res.length>0){<!-- --> var myJkd = "<select id='direction' class='sel'>"; for(var i=0;i<res.length;i++){<!-- --> var rtmp = res[i].rtmp; $("#playVideo"+i).attr('src',rtmp); myJkd+="<option value='"+res[i].deviceSerial+"'>"+(i+1)+"号监控点</option>"; } myJkd+=" </select>"; $("#jkd").html(myJkd); var player = new EZuikit.EZUIPlayer('myPlayer'); var player1 = new EZuikit.EZUIPlayer('myPlayer1'); }else{<!-- --> alert("获取失败"); } },'json'); function cloudControl(direction){<!-- --> layer.load(2); var jkd = $("#jkd option:selected").val();//选中的值 var speed = $("#ptzspeed option:selected").val();//选中的值 var data ={<!-- --> "device":jkd, "channelNo":1, "direction":direction, "speed":speed }; $.post('jiankong_setCloudControl.action',data,function(res){<!-- --> layer.closeAll('loading'); },'text'); } </script> </body> </html>
jiankong.css文件
@charset "utf-8"; * {<!-- --> margin:0; padding:0; } html {<!-- --> width:100%; height:100%; font-size:12px; font-family:Arial, Helvetica, sans-serif; -webkit-text-size-adjust:none; background:#FFFFFF; } body {<!-- --> padding:5px; } select {<!-- --> height:20px; line-height:20px; } .left {<!-- --> float:left; } .freeze {<!-- --> position:absolute; text-align:center; background:#343434; color:#FFFFFF; font-size:26px; font-weight:bold; filter:alpha(opacity=60); opacity:0.6; } .vtop {<!-- --> vertical-align:middle; margin-top:-1px; } /*插件*/ .plugin {<!-- --> width:800px; height:600px; top:10px; } object {<!-- --> width:800px !important ; height:600px !important; } fieldset {<!-- --> display:block; } /*本地配置*/ .localconfig {<!-- --> width:480px; padding:10px; border:1px solid #7F9DB9; } .localconfig .tt {<!-- --> width:125px; } .localconfig .txt {<!-- --> width:310px; } .localconfig .txt2 {<!-- --> width:300px; } .localconfig .btn {<!-- --> width:45px; height:22px; line-height:18px; } .localconfig .sel {<!-- --> width:120px; } /*登录*/ .login {<!-- --> width:480px; padding:10px; border:1px solid #7F9DB9; } .login .tt {<!-- --> width:100px; } .login .txt {<!-- --> width:130px; } .login .btn {<!-- --> width:45px; height:22px; line-height:18px; } .login .btn2 {<!-- --> width:100px; height:22px; line-height:18px; } .login .sel {<!-- --> width:130px; } .login .sel2 {<!-- --> width:65px; } /*数字通道*/ .ipchannel {<!-- --> width:480px; padding:10px; border:1px solid #7F9DB9; } .ipchannel .btn {<!-- --> width:130px; height:22px; line-height:18px; } .ipchannel .digitaltdiv {<!-- --> height:100px; overflow:hidden; overflow-y:auto; border:1px solid #7F9DB9; font-size:11px; } .ipchannel .digitalchannellist th, .ipchannel .digitalchannellist td {<!-- --> padding:2px; border:1px solid #7F9DB9; border-collapse:collapse; white-space:nowrap; } /*预览*/ .preview {<!-- --> width:450px; padding:10px; padding-top:0; margin-left:10px; border:1px solid #7F9DB9; } .preview .tt {<!-- --> width:60px; } .preview .txt {<!-- --> width:30px; } .preview .btn {<!-- --> width:70px; height:22px; line-height:18px; } .preview .btn2 {<!-- --> width:90px; height:22px; line-height:18px; } .preview .sel {<!-- --> width:105px; } /*云台*/ .ptz {<!-- --> width:450px; padding:10px; margin-left:10px; border:1px solid #7F9DB9; } .ptz .tt {<!-- --> width:60px; } .ptz .txt {<!-- --> width:60px; } .ptz .btn {<!-- --> width:45px; height:22px; line-height:18px; } .ptz .btn2 {<!-- --> width:60px; height:22px; line-height:18px; } .ptz .sel {<!-- --> width:100px; } /*视频参数*/ .videoparam {<!-- --> width:450px; padding:10px; margin-left:10px; border:1px solid #7F9DB9; } .videoparam .tt {<!-- --> width:60px; } .videoparam .txt {<!-- --> width:60px; } .videoparam .btn {<!-- --> width:45px; height:22px; line-height:18px; } .videoparam .sel {<!-- --> width:65px; } /*回放*/ .playback {<!-- --> width:450px; padding:10px; margin-left:10px; border:1px solid #7F9DB9; } .playback .tt {<!-- --> width:60px; } .playback .txt {<!-- --> width:140px; } .playback .btn {<!-- --> width:45px; height:22px; line-height:18px; } .playback .btn2 {<!-- --> width:70px; height:22px; line-height:18px; } .playback .sel {<!-- --> width:142px; } .playback .searchdiv {<!-- --> height:100px; overflow:hidden; overflow-y:auto; border:1px solid #7F9DB9; font-size:11px; } .playback .searchlist th, .playback .searchlist td {<!-- --> padding:2px; border:1px solid #7F9DB9; border-collapse:collapse; white-space:nowrap; } /*系统维护*/ .maintain {<!-- --> width:450px; padding:10px; margin-left:10px; border:1px solid #7F9DB9; } .maintain .tt {<!-- --> width:60px; } .maintain .txt {<!-- --> width:280px; } .maintain .btn {<!-- --> width:45px; height:22px; line-height:18px; } .maintain .btn2 {<!-- --> width:100px; height:22px; line-height:18px; } .maintain .sel {<!-- --> width:65px; } /*操作信息*/ .operate {<!-- --> width:450px; padding:10px; margin-left:10px; border:1px solid #7F9DB9; } .operate .opinfo {<!-- --> height:150px; border:1px solid #7F9DB9; overflow:auto; } /*事件回调*/ .callback {<!-- --> width:450px; padding:10px; margin-left:10px; border:1px solid #7F9DB9; } .callback .cbinfo {<!-- --> height:114px; border:1px solid #7F9DB9; overflow:auto; } /*IP解析*/ .ipparse {<!-- --> width:450px; padding:10px; margin-left:10px; border:1px solid #7F9DB9; } .ipparse .tt {<!-- --> width:85px; } .ipparse .txt {<!-- --> width:130px; } .ipparse .btn {<!-- --> width:90px; height:22px; line-height:18px; } .ipparse .sel {<!-- --> width:130px; } /*绘图*/ .draw {<!-- --> width:450px; padding:10px; padding-top:0; margin-left:10px; border:1px solid #7F9DB9; } .draw .tt {<!-- --> width:60px; } .draw .txt {<!-- --> width:140px; } .draw .btn {<!-- --> width:70px; height:22px; line-height:18px; } .draw .btn2 {<!-- --> width:100px; height:22px; line-height:18px; } .draw .sel {<!-- --> width:105px; }
注:ezuikit.js文件可在萤石云开发平台下载
最终效果如下:
有两台设备,不过此时只一台设备接通了电源。