相关的核心代码我上传会上传到github,以下文字可以理清实现思路,

git地址:https://github.com/blackMilk123/workspace/tree/master/rtmp

  • 一个开通了RTMP协议的流地址,萤石云之类的监控提供商都有的,格式为rtmp://rtmp01open.ys7.com/openlive/xxxxxxxxx.hd
  • 向萤石云申请开发者资质之后会得到一个appKey,appSecret,accessToken,既然能搜到这篇文章相信你肯定不会缺少这些东西。

以下第一个表结构基本上是固定的,存放你开通的那个萤石云帐号的key 密钥 等等的信息,第二个表是根据你具体的业务来设计的,跟你的设备关联,一般你有一个监控设备就会有对应的信息,红框圈起来是一些比较重要的参数,这些都可以在萤石云中文档中看到

表一:video_appinfo 基本上都是一些固定参数,结构基本上可以不动

表二:video_deviceinfo表,圈起来的是一些比较重要的参数,都是由相应的视频监控设备提供的

根据以上两张表的设计创建相应的实体类,因为字段太多,并且表二根据实际业务的不同,加上实体类代码放上来也没有什么意义,所以这里就不再贴代码了,可以自行创建,以及service层,dao层的基础映射文件也是一样的,简单的可以自己创建一下

SDK客户端主要是用于模拟各种Http请求,带着申请的密钥,去向萤石云的授权服务器获取视频信息等等,

  1. public class SdkClient {
  2. /** 获取accessToken */
  3. private static final String API_TOKEN_GET_URL = "https://open.ys7.com/api/lapp/token/get";
  4. /** 添加设备 */
  5. private static final String API_DEVICE_ADD_URL = "https://open.ys7.com/api/lapp/device/add";
  6. /** 查询账号下流量消耗汇总 */
  7. private static final String API_TRAFFIC_TOTAL = "https://open.ys7.com/api/lapp/traffic/user/total";
  8. /** 获取设备列表 */
  9. private static final String API_DEVICE_LIST = "https://open.ys7.com/api/lapp/device/list";
  10. /** 获取摄像头列表 */
  11. private static final String API_CAMERA_LIST = "https://open.ys7.com/api/lapp/camera/list";
  12. /** 添加设备 */
  13. private static final String API_DEVICE_ADD = "https://open.ys7.com/api/lapp/device/add";
  14. /** 获取单个设备信息 */
  15. private static final String API_DEVICE_INFO = "https://open.ys7.com/api/lapp/device/info";
  16. /** 开始云台控制 */
  17. private static final String API_DEVICE_PTZ_START = "https://open.ys7.com/api/lapp/device/ptz/start";
  18. /** 停止云台控制 */
  19. private static final String API_DEVICE_PTZ_STOP = "https://open.ys7.com/api/lapp/device/ptz/stop";
  20. /** 关闭设备视频加密 */
  21. private static final String API_DEVICE_ENCRYPT_OFF = "https://open.ys7.com/api/lapp/device/encrypt/off";
  22. /** 开启设备视频加密 */
  23. private static final String API_DEVICE_ENCRYPT_ON = "https://open.ys7.com/api/lapp/device/encrypt/on";
  24. /** 开通直播功能 */
  25. private static final String API_LIVE_VIDEO_OPEN = "https://open.ys7.com/api/lapp/live/video/open";
  26. /** 关闭直播功能 */
  27. private static final String API_LIVE_VIDEO_CLOSE = "https://open.ys7.com/api/lapp/live/video/close";
  28. /** 获取指定有效期的直播地址 */
  29. private static final String API_LIVE_ADDRESS_LIMITED = "https://open.ys7.com/api/lapp/live/address/limited";
  30. /** 获取直播地址*/
  31. private static final String API_LIVE_ADDRESS_GET = "https://open.ys7.com/api/lapp/live/address/get";
  32. /**
  33. * app 标识
  34. */
  35. private String appKey;
  36. public String getAppKey() {
  37. return appKey;
  38. }
  39. public void setAppKey(String appKey) {
  40. this.appKey = appKey;
  41. }
  42. /**
  43. * app 密钥
  44. */
  45. private String appSecret;
  46. public String getAppSecret() {
  47. return appSecret;
  48. }
  49. public void setAppSecret(String appSecret) {
  50. this.appSecret = appSecret;
  51. }
  52. /**
  53. * token 凭据
  54. */
  55. private String accessToken;
  56. public String getAccessToken() {
  57. return accessToken;
  58. }
  59. public void setAccessToken(String accessToken) {
  60. this.accessToken = accessToken;
  61. }
  62. /**
  63. * token 有效时间
  64. */
  65. private Date tokenExpire;
  66. public Date getTokenExpire() {
  67. return tokenExpire;
  68. }
  69. public void setTokenExpire(Date tokenExpire) {
  70. this.tokenExpire = tokenExpire;
  71. }
  72. public SdkToken getToken() {
  73. return getToken(false);
  74. }
  75. /**
  76. * 获取 accessToken
  77. * @param force 是否强制获取
  78. * @return
  79. */
  80. public SdkToken getToken(boolean force) {
  81. if(!force && this.getAccessToken() != null) {
  82. //准备缓存 accessToken
  83. SdkToken existsSdkToken = new SdkToken("200", "使用 accessToken 缓存", this.getAccessToken(), this.getTokenExpire() != null ? String.valueOf(this.getTokenExpire().getTime() ): "0");
  84. //判断 token是否过期
  85. if(this.getTokenExpire() != null && this.getTokenExpire().getTime() < new Date().getTime()) {
  86. return existsSdkToken;
  87. }
  88. //调用接口,验证 accessToken有效性
  89. SdkReturn<SdkMap> sdkReturn = this.trafficTotal();
  90. if(sdkReturn.isSuccess()) {
  91. return existsSdkToken;
  92. }
  93. }
  94. Map<String, Object> paramMap = new HashMap<>();
  95. paramMap.put("appKey", this.appKey);
  96. paramMap.put("appSecret", this.appSecret);
  97. SdkToken sdkToken = getMap(API_TOKEN_GET_URL, paramMap, SdkToken.class);
  98. if(sdkToken.isSuccess()) {
  99. this.setAccessToken(sdkToken.getAccessToken());
  100. this.setTokenExpire(new Date(sdkToken.getExpireTime()));
  101. }
  102. return sdkToken;
  103. }
  104. /**
  105. * 添加设备
  106. * @param deviceSerial 序列号
  107. * @param validateCode 验证码
  108. * @return
  109. */
  110. public SdkReturnMap deviceAdd(String deviceSerial, String validateCode) {
  111. Map<String, Object> paramMap = new HashMap<>();
  112. paramMap.put("accessToken", this.getAccessToken());
  113. paramMap.put("deviceSerial", deviceSerial);
  114. paramMap.put("validateCode", validateCode);
  115. SdkReturnMap returnMap = getMap(API_DEVICE_ADD, paramMap);
  116. //添加容错
  117. if(!returnMap.isSuccess()) {
  118. if(returnMap.getCode().equals("20017")) {
  119. returnMap.setCode("200");
  120. }else if(returnMap.getCode().equals("20002")) {
  121. returnMap.setMsg(returnMap.getMsg() + ",设备未注册至萤石云");
  122. }
  123. }
  124. return returnMap;
  125. }
  126. /**
  127. * 获取单个设备信息
  128. * @param deviceSerial
  129. * @return
  130. */
  131. public SdkReturnMap deviceInfo(String deviceSerial) {
  132. Map<String, Object> paramMap = new HashMap<>();
  133. paramMap.put("accessToken", this.getAccessToken());
  134. paramMap.put("deviceSerial", deviceSerial);
  135. return getMap(API_DEVICE_INFO, paramMap);
  136. }
  137. /**
  138. * 开启设备视频加密
  139. * @param deviceSerial 设备序列号
  140. * @return
  141. */
  142. public SdkReturnMap deviceEncryptOn(String deviceSerial) {
  143. Map<String, Object> paramMap = new HashMap<>();
  144. paramMap.put("accessToken", this.getAccessToken());
  145. paramMap.put("deviceSerial", deviceSerial);
  146. return getMap(API_DEVICE_ENCRYPT_ON, paramMap);
  147. }
  148. /**
  149. * 开通直播功能
  150. * @param channelNo 通道号
  151. * @param deviceSerials 设备序列号集合
  152. * @return
  153. */
  154. public SdkReturnMapList liveVideoOpen(int channelNo, String... deviceSerials) {
  155. if(deviceSerials == null || deviceSerials.length == 0) {
  156. return new SdkReturnMapList();
  157. }
  158. String source = "";
  159. for(int i = 0; i < deviceSerials.length; i++) {
  160. if(i > 0) {
  161. source += ",";
  162. }
  163. source += (deviceSerials[i] + ":" + channelNo);
  164. }
  165. Map<String, Object> paramMap = new HashMap<>();
  166. paramMap.put("accessToken", this.getAccessToken());
  167. paramMap.put("source", source);
  168. return getMapList(API_LIVE_VIDEO_OPEN, paramMap);
  169. }
  170. /**
  171. * 获取直播地址
  172. * @param channelNo 通道号 默认 1
  173. * @param deviceSerials 设备序列号集合
  174. * @return
  175. */
  176. public SdkReturnMapList liveVideoGetAddress(int channelNo, String... deviceSerials) {
  177. if(deviceSerials == null || deviceSerials.length == 0) {
  178. return new SdkReturnMapList();
  179. }
  180. String source = "";
  181. for(int i = 0; i < deviceSerials.length; i++) {
  182. if(i > 0) {
  183. source += ",";
  184. }
  185. source += (deviceSerials[i] + ":" + channelNo);
  186. }
  187. Map<String, Object> paramMap = new HashMap<>();
  188. paramMap.put("accessToken", this.getAccessToken());
  189. paramMap.put("source", source);
  190. return getMapList(API_LIVE_ADDRESS_GET, paramMap);
  191. }
  192. /**
  193. * 获取指定有效期的直播地址
  194. * @param deviceSerial 设备序列号
  195. * @param channelNo 通道号
  196. * @param expireTime 地址过期时间:单位秒数,最大默认62208000(即720天),最小默认300(即5分钟)。
  197. * @return
  198. */
  199. public SdkReturnMap liveVideoLimitedAddress(String deviceSerial, int channelNo, Integer expireTime) {
  200. Map<String, Object> paramMap = new HashMap<>();
  201. paramMap.put("accessToken", this.getAccessToken());
  202. paramMap.put("deviceSerial", deviceSerial);
  203. paramMap.put("channelNo", channelNo);
  204. if(expireTime != null) {
  205. paramMap.put("expireTime", expireTime);
  206. }
  207. return getMap(API_LIVE_ADDRESS_LIMITED, paramMap);
  208. }
  209. /**
  210. * 开始云台控制
  211. * @param deviceSerial deviceSerial 设备序列号
  212. * @param channelNo channelNo 通道号
  213. * @param direction direction 操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距
  214. * @return
  215. */
  216. public SdkReturnMap startDevicePtz(String deviceSerial, int channelNo, int direction) {
  217. return this.startDevicePtz(deviceSerial, channelNo, direction, 0);
  218. }
  219. /**
  220. * 开始云台控制
  221. * @param deviceSerial 设备序列号
  222. * @param channelNo 通道号
  223. * @param direction 操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距
  224. * @param speed 云台速度:0-慢,1-适中,2-快
  225. * @return
  226. */
  227. public SdkReturnMap startDevicePtz(String deviceSerial, int channelNo, int direction, int speed) {
  228. Map<String, Object> paramMap = new HashMap<>();
  229. paramMap.put("accessToken", this.getAccessToken());
  230. paramMap.put("deviceSerial", deviceSerial);
  231. paramMap.put("channelNo", channelNo);
  232. paramMap.put("direction", String.valueOf(direction));
  233. paramMap.put("speed", String.valueOf(speed));
  234. SdkReturnMap sdkReturn = getMap(API_DEVICE_PTZ_START, paramMap);
  235. return sdkReturn;
  236. }
  237. /**
  238. * 停止云台控制
  239. * @param deviceSerial 设备序列号
  240. * @param channelNo 通道号
  241. * @param direction 操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距
  242. * @return
  243. */
  244. public SdkReturnMap stopDevicePtz(String deviceSerial, int channelNo, int direction) {
  245. Map<String, Object> paramMap = new HashMap<>();
  246. paramMap.put("accessToken", this.getAccessToken());
  247. paramMap.put("deviceSerial", deviceSerial);
  248. paramMap.put("channelNo", channelNo);
  249. paramMap.put("direction", String.valueOf(direction));
  250. SdkReturnMap sdkReturn = getMap(API_DEVICE_PTZ_STOP, paramMap);
  251. return sdkReturn;
  252. }
  253. /*
  254. public static void main(String[] args) {
  255. SdkClient sdkClient = new SdkClient();
  256. sdkClient.setAppKey("e82e92e47b1542d1a7ba2003199f5cf2");
  257. sdkClient.setAppSecret("58ddf728e5e58c321bb18c3e7c8d0aa8");
  258. sdkClient.setAccessToken("at.3h35yneydi4i9tz75alqgoe90jq8c9yx-6t55llnnkg-17ys1z5-6lttvhphk-");
  259. sdkClient.getToken();
  260. }
  261. */
  262. /**
  263. * 获取 map
  264. * @param url
  265. * @param paramMap
  266. * @return
  267. */
  268. private SdkReturnMap getMap(String url, Map<String, Object> paramMap) {
  269. return getMap(url, paramMap, SdkReturnMap.class);
  270. }
  271. /**
  272. * 获取 map
  273. * @param url
  274. * @param paramMap
  275. * @param classz
  276. * @return
  277. */
  278. private <T extends SdkReturn<SdkMap>> T getMap(String url, Map<String, Object> paramMap, Class<T> classz) {
  279. System.out.println(url);
  280. try {
  281. String result = HttpUtil.post(url, paramMap);
  282. System.out.println(result);
  283. T sdkReturn = JSONObject.parseObject(result, classz);
  284. return sdkReturn;
  285. } catch (Exception e) {
  286. T sdkReturn = null;
  287. try {
  288. sdkReturn = classz.newInstance();
  289. sdkReturn.setCode("-1");
  290. sdkReturn.setMsg(e.getMessage());
  291. } catch (Exception ex) {
  292. ex.printStackTrace();
  293. }
  294. return sdkReturn;
  295. }
  296. }
  297. /**
  298. * 获取 map 集合
  299. * @param url
  300. * @param paramMap
  301. * @return
  302. */
  303. private SdkReturnMapList getMapList(String url, Map<String, Object> paramMap) {
  304. return getMapList(url, paramMap, SdkReturnMapList.class);
  305. }
  306. /**
  307. * 获取 map 集合
  308. * @param url
  309. * @param paramMap
  310. * @param classz
  311. * @return
  312. */
  313. private <T extends SdkReturn<SdkMapList>> T getMapList(String url, Map<String, Object> paramMap, Class<T> classz) {
  314. System.out.println(url);
  315. try {
  316. String result = HttpUtil.post(url, paramMap);
  317. System.out.println(result);
  318. T sdkReturn = JSONObject.parseObject(result, classz);
  319. return sdkReturn;
  320. } catch (Exception e) {
  321. T sdkReturn = null;
  322. try {
  323. sdkReturn = classz.newInstance();
  324. sdkReturn.setCode("-1");
  325. sdkReturn.setMsg(e.getMessage());
  326. } catch (Exception ex) {
  327. ex.printStackTrace();
  328. }
  329. return sdkReturn;
  330. }
  331. }
  332. }
  • VideoAppInfoService
    • 这里的代码主要用于更新设备的过期时间以及accessToken刷新,在直播播放之前需要先更新一下设备信息等等。
  1. @Service("videoAppInfo")
  2. public class VideoAppInfoServiceImpl extends com.sunrise.common.BaseServiceImpl<VideoAppInfo> implements VideoAppInfoService {
  3. @Autowired
  4. private VideoAppInfoMapper videoAppInfoMapper;
  5. public VideoAppInfoServiceImpl() {
  6. }
  7. @Override
  8. public com.sunrise.common.BaseMapper<VideoAppInfo> getMapper() {
  9. return this.videoAppInfoMapper;
  10. }
  11. @Override
  12. public Return<VideoAppInfo> updateVideoAppInfoToken(Long appId) {
  13. //查询视频监控应用信息
  14. VideoAppInfo videoAppInfo = this.selectByPrimaryKey(appId);
  15. if(videoAppInfo == null) {
  16. return new Return<>(false, "获取视频监控app信息失败!");
  17. }
  18. //accessToken刷新
  19. SdkClient sdkClient = new SdkClient();
  20. sdkClient.setAppKey(videoAppInfo.getAppKey());
  21. sdkClient.setAppSecret(videoAppInfo.getAppSecret());
  22. sdkClient.setAccessToken(videoAppInfo.getAccessToken());
  23. sdkClient.setTokenExpire(videoAppInfo.getTokenExpire());
  24. SdkToken sdkToken = sdkClient.getToken();
  25. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  26. Calendar calendar = Calendar.getInstance();
  27. //设置过期时间为当前时间+7天
  28. calendar.add(Calendar.DATE,7);
  29. String format = dateFormat.format(calendar.getTime());
  30. //更新过期时间
  31. videoAppInfo.setAccessToken(sdkToken.getAccessToken());
  32. videoAppInfo.setTokenExpire(calendar.getTime());
  33. videoAppInfo.setRemark(sdkToken.getMsg());
  34. this.updateSelective(videoAppInfo);
  35. if(!sdkToken.isSuccess()) {
  36. videoAppInfo.setRemark(sdkToken.getMsg());
  37. this.updateSelective(videoAppInfo);
  38. return new Return<>(false, sdkToken.getMsg());
  39. }
  40. //如果accessToken一致,则数据不变
  41. if(videoAppInfo.getAccessToken() != null && videoAppInfo.getAccessToken().equalsIgnoreCase(sdkToken.getAccessToken())) {
  42. return new Return<VideoAppInfo>(true, "accessToken 无变化").setData(videoAppInfo);
  43. }
  44. //更新 accessToken
  45. videoAppInfo.setAccessToken(sdkToken.getAccessToken());
  46. videoAppInfo.setTokenExpire(new Date(sdkToken.getExpireTime()));
  47. videoAppInfo.setRemark(sdkToken.getMsg());
  48. this.updateSelective(videoAppInfo);
  49. //返回结果
  50. return new Return<VideoAppInfo>(true, "已更新 accessToken").setData(videoAppInfo);
  51. }
  52. }
  1. /**
  2. * 跳转水质视频监控页面
  3. * @return
  4. */
  5. @RequestMapping("/toAppVideoList")
  6. public String toAppVideoList(HttpServletRequest requestLong id){
  7. //更新视频应用信息
  8. Long appId = videoDeviceInfo != null ? videoDeviceInfo.getAppId() : 1L;
  9. Return<VideoAppInfo> retApp = videoAppInfoService.updateVideoAppInfoToken(appId);
  10. request.setAttribute("accessToken", retApp.isSuccess() ? retApp.getData().getAccessToken() : null);
  11. //通过主键查询视频设备信息
  12. VideoDeviceInfo videoDeviceInfo = this.videoDeviceInfoService.selectByPrimaryKey(id);
  13. //传到前端
  14. request.setAttribute("videoDevice",deviceInfos);
  15. return "szapp/appWaterVideoList";
  16. }

添加前端播放插件:

  1. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
  2. <html lang="en">
  3. <head>
  4. <!doctype html>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  8. <link rel="stylesheet" href="${staticPath }/css/screen/swiper.min.css"/>
  9. </head>
  10. <body>
  11. <div>
  12. //初始化一个视频播放框
  13. <div class="media-box">
  14. <div class="media" id="player" style="width: 100%;height:40%;">
  15. </div>
  16. </div>
  17. </div>
  18. <script src="${staticPath }/js/lib/jquery/jquery-3.3.1.min.js"></script>
  19. <script type="text/javascript" src="${staticPath}/js/sewise-player/sewise.player.min.js"></script>
  20. <script>
  21. //进页面时加载
  22. $(function () {
  23. hospital04();
  24. });
  25. function hospital04() {
  26. var title = "${videoDeviceInfo3.videoName != null ? videoDeviceInfo3.videoName : videoDeviceInfo3.deviceName}";
  27. var videoUrl = "${videoDeviceInfo3.videoUrl}";
  28. var poster = \'${staticPath}/images/index/bg.jpg\';
  29. if (videoUrl.indexOf(\'rtmp://\') == 0) {
  30. SewisePlayer.setup({
  31. server: "live",
  32. type: "rtmp",
  33. autostart: "true",
  34. streamurl: videoUrl,
  35. skin: "",
  36. title: title,
  37. claritybutton: "disable",//[可选]是否显示多码率按钮 "enable"、"disable",缺省默认值为:"enable"
  38. timedisplay: "disable",//[可选]是否显示播放控制栏上的时间 "enable"、"disable",缺省默认值为:"enable"
  39. controlbardisplay: "disable",//[可选]是否显示播放控制栏 "enable"、"disable",缺省默认值为:"enable"
  40. topbardisplay: "disable",//[可选]是否显示顶部标题 "enable"、"disable",缺省默认值为:"enable"
  41. draggable: false,
  42. buffer: 0,
  43. primary: "html5",
  44. lang: "zh_CN",
  45. poster: poster,//[可选]视频播放前显示的图像
  46. logo: " ",//[可选]播放器角落logo
  47. }, "player");
  48. } else if (videoUrl.indexOf(\'.m3u8\') > -1) {
  49. SewisePlayer.setup({
  50. server: "vod",
  51. type: "m3u8",
  52. autostart: "true",
  53. videourl: videoUrl,
  54. skin: "vodFlowPlayer",
  55. title: "",
  56. claritybutton: "disable",
  57. lang: "zh_CN",
  58. logo: "",
  59. }, "player");
  60. } else if (videoUrl.indexOf(\'.flv\') > -1) {
  61. SewisePlayer.setup({
  62. server: "vod",
  63. type: "flv",
  64. autostart: "true",
  65. videourl: videoUrl,
  66. skin: "vodWhite",
  67. title: "",
  68. claritybutton: "disable",
  69. lang: "zh_CN",
  70. logo: "",
  71. }, "player");
  72. }
  73. }
  74. </script>
  75. </body>
  76. </html>

这样一个视频直播就做好了 效果图

版权声明:本文为blackmlik原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/blackmlik/p/12767853.html