流媒体服务器、海康威视 大华摄像头实现视频监控、直播解决方案,


  随着互联网+物联网进程的加快,视频监控应用领域变得越来越广泛,其中海康威视 大华等品牌的摄像头频繁出现在视野中。由于去年也实现过智慧工地项目上的视频监控方案,加上当今直播趋势不减。现在总结一下:

缘由:是1对N 点对多的直播方式, 一般都是采用服务器转发,所以此处不考虑WebRTC这种端对端的方式,WebRTC将在下一篇文章中讲解下实现思路。

前提:需要海康威视或大华的摄像头,大华摄像头清晰度 品质较好,但相对于海康的摄像头较贵,所以海康威视的摄像头更受口袋欢迎。

一.自建流媒体服务器

  第一种方式就是自建流媒体服务器,然后自己实现采集推流 到服务器 拉流到客户端播放。先看一张图:

二.接入第三方平台

  在之前的项目中是购买了海康威视的摄像头,所以为了方便快捷的开发,是接入了第三方平台,由第三方平台进行管理和转发。大体流程是往第三方平台注册摄像头信息(序列号 验证码),然后流直接走第三方平台,自己服务端只需要获取三方平台的API接口便能得知播放地址 直接客户端播放。使用的是萤石云。

 

 

 

   我们可以在自己的项目中往萤石云注册摄像头信息(也就是调用萤石云接口 往萤石云写一条数据),然后在需要用的地方获取萤石云API接口播放地址。完全不用管流的处理(得充值)。

   提供一份对萤石云请求封装的类(C#代码):  

1 using Newtonsoft.Json; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Net.Http; 6 using System.Net.Http.Headers; 7 using System.Text; 8 using System.Threading.Tasks; 9 using YJT.Common; 10 11 /*20190819 by suzong */ 12 namespace YJT.Wisdom.Api.lib 13 { 14 /// <summary> 15 /// 萤石云请求封装 16 /// </summary> 17 public class YsClient 18 { 19 private static readonly string requestUrl = "https://open.ys7.com/"; 20 private static readonly string appKey = "";//官网注册获得 21 private static readonly string appSecret = "";//官网注册获得 22 23 /// <summary> 24 /// 获得token 25 /// </summary> 26 /// <returns>{code:200,data:{accessToken:"",expireTime:精确到毫秒}}</returns> 27 public static async Task<string> GetToken() 28 { 29 string key = ConfigHelper.GetSetting("CacheKey:YsToken") ?? "YsAccessToken"; 30 string tokenStr = MemoryCacheHelper.Get(key)?.ToString(); 31 if (string.IsNullOrEmpty(tokenStr)) 32 { 33 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/token/get?appKey={appKey}&appSecret={appSecret}"); 34 YsResult result = JsonConvert.DeserializeObject<YsResult>(str); 35 //缓存token 缓存时间为5天 36 tokenStr = result?.data?.accessToken; 37 MemoryCacheHelper.Set(key, tokenStr, TimeSpan.FromDays(5)); 38 } 39 return tokenStr; 40 } 41 42 /// <summary> 43 /// 添加设备 44 /// </summary> 45 /// <param name="deviceSerial">设备序列号</param> 46 /// <param name="validateCode">设备验证码</param> 47 /// <returns></returns> 48 public static async Task<YsResult> SaveDevice(string deviceSerial, string validateCode) 49 { 50 if (string.IsNullOrEmpty(deviceSerial) || string.IsNullOrEmpty(validateCode)) 51 return new YsResult() { code = "-1", msg = "缺少验证码或序列号" }; 52 53 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/device/add?accessToken={GetToken().Result}&deviceSerial={deviceSerial.ToUpper()}&validateCode={validateCode.ToUpper()}"); 54 return JsonConvert.DeserializeObject<YsResult>(str); 55 } 56 57 /// <summary> 58 /// 关闭视频加密 59 /// </summary> 60 /// <param name="deviceSerial">设备序列号</param> 61 /// <param name="validateCode">设备验证码</param> 62 /// <returns>{code:200}</returns> 63 public static async Task<YsResult> OffEncryption(string deviceSerial, string validateCode) 64 { 65 if (string.IsNullOrEmpty(deviceSerial) || string.IsNullOrEmpty(validateCode)) 66 return new YsResult() { code = "-1", msg = "缺少验证码或序列号" }; 67 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/device/encrypt/off?accessToken={GetToken().Result}&deviceSerial={deviceSerial.ToUpper()}&validateCode={validateCode.ToUpper()}"); 68 return JsonConvert.DeserializeObject<YsResult>(str); 69 } 70 71 /// <summary> 72 /// 删除设备 73 /// </summary> 74 /// <param name="token"></param> 75 /// <param name="deviceSerial">设备序列号</param> 76 /// <returns>{code:200}</returns> 77 public static async Task<YsResult> DeleteDevice(string deviceSerial) 78 { 79 if (string.IsNullOrEmpty(deviceSerial)) 80 return new YsResult() { code = "-1", msg = "缺少序列号" }; 81 82 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/device/delete?accessToken={GetToken().Result}&deviceSerial={deviceSerial.ToUpper()}"); 83 return JsonConvert.DeserializeObject<YsResult>(str); 84 } 85 86 /// <summary> 87 /// 获取直播地址 WSS地址 #get请求 每次获取 88 /// </summary> 89 /// <param name="token"></param> 90 /// <param name="deviceSerial">设备序列号</param> 91 /// <param name="validateCode">设备验证码</param> 92 /// <returns></returns> 93 public static async Task<string> GetPlayWss(string deviceSerial, string validateCode) 94 { 95 if (string.IsNullOrEmpty(deviceSerial) || string.IsNullOrEmpty(validateCode)) 96 return null; 97 //{"retcode":0,"msg":"成功","data":{"tokens":["ot.cadfwa3t0dkdn62x5qf257es7dbq1cie-1vwkltfwtz-1w1jc79-9eabx2bbz"],"params":"&auth=1&biz=4&cln=100"}} 98 string str = await HttpHelper.HttpGetAsync($"{requestUrl}jssdk/ezopen/getStreamToken?accessToken={GetToken().Result}&num=1&type=live"); 99 YsResult result = JsonConvert.DeserializeObject<YsResult>(str); 100 if (result.retcode == 0) 101 { 102 string tokensStr = result?.data?.tokens[0]; 103 string paramStr = result?.data["params"]; 104 //wss://jsdecoder.ys7.com:20006/live?dev=设备序列号&chn=1&stream=2&ssn=刚才获取的tokens[0]+刚才获取的params的字符串。作为wssUrl,此地址可以加上checkCode=验证码作为视频加密传输。 105 return $"wss://jsdecoder.ys7.com:20006/live?dev={deviceSerial}&chn=1&stream=2&ssn={tokensStr}{paramStr}&checkCode={validateCode}"; 106 } 107 return null; 108 } 109 110 /// <summary> 111 /// 获取直播地址 #返回RTMP地址 112 /// </summary> 113 /// <param name="token"></param> 114 /// <param name="deviceSerial">设备序列号</param> 115 /// <returns>返回rtmp</returns> 116 public static async Task<string> GetPlayRtmp(string deviceSerial) 117 { 118 if (string.IsNullOrEmpty(deviceSerial)) 119 return null; 120 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/live/address/get?accessToken={GetToken().Result}&source={deviceSerial}:1"); 121 YsResult result = JsonConvert.DeserializeObject<YsResult>(str); 122 if (result.code.Equals("200")) 123 return result?.data[0]?.rtmp; 124 return null; 125 } 126 127 /// <summary> 128 /// 获取设备可有的权限 129 /// </summary> 130 /// <param name="token"></param> 131 /// <param name="deviceSerial">设备序列号</param> 132 /// <returns>data: 133 ///{ 134 /// supprot_encrypt 是否支持视频图像加密 0 - 不支持, 1 - 支持 135 /// support_modify_pwd 是否支持修改设备加密密码: 0 - 不支持, 1 - 支持 136 /// ptz_top_bottom 是否支持云台上下转动 0 - 不支持, 1 - 支持 137 /// ptz_left_right 是否支持云台左右转动 0 - 不支持, 1 - 支持 138 /// ptz_45 是否支持云台45度方向转动 0 - 不支持, 1 - 支持 139 /// ptz_zoom 是否支持云台缩放控制 0 - 不支持, 1 - 支持 140 /// ptz_focus 是否支持焦距模式 0 - 不支持, 1 - 支持 141 ///}code: 200 142 /// </returns> 143 public static async Task<YsResult<YsRoles>> GetDeviceRole(string deviceSerial) 144 { 145 if (string.IsNullOrEmpty(deviceSerial)) 146 return new YsResult<YsRoles>() { code = "-1", msg = "缺少序列号" }; 147 148 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/device/capacity?accessToken={GetToken().Result}&deviceSerial={deviceSerial.ToUpper()}"); 149 return JsonConvert.DeserializeObject<YsResult<YsRoles>>(str); 150 } 151 152 /// <summary> 153 /// 云台控制开始 154 /// </summary> 155 /// <param name="token"></param> 156 /// <param name="deviceSerial">设备序列号</param> 157 /// <param name="direction">方向 (操作命令:0 - 上,1 - 下,2 - 左,3 - 右,4 - 左上,5 - 左下,6 - 右上,7 - 右下,8 - 放大,9 - 缩小,10 - 近焦距,11 - 远焦距)</param> 158 /// <param name="speed">速度 (云台速度:0 - 慢,1 - 适中,2 - 快)</param> 159 /// <returns>{code:200}</returns> 160 public static async Task<YsResult> CradleControlStarts(string token, string deviceSerial, int direction, int speed) 161 { 162 if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(deviceSerial)) 163 return null; 164 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/device/ptz/start?accessToken={token}&deviceSerial={deviceSerial}&channelNo=1&direction={direction}&speed={speed}"); 165 return JsonConvert.DeserializeObject<YsResult>(str); 166 } 167 168 /// <summary> 169 /// 云台控制结束 170 /// </summary> 171 /// <param name="token"></param> 172 /// <param name="deviceSerial">设备序列号</param> 173 /// <param name="direction">方向 (操作命令:0-上,1-下,2-左,3-右,4-左上,5-左下,6-右上,7-右下,8-放大,9-缩小,10-近焦距,11-远焦距)</param> 174 /// <returns>{code:200}</returns> 175 public static async Task<YsResult> CradleControlEnd(string token, string deviceSerial, int direction) 176 { 177 if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(deviceSerial)) 178 return null; 179 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/device/ptz/stop?accessToken={token}&deviceSerial={deviceSerial}&channelNo=1&direction={direction}"); 180 return JsonConvert.DeserializeObject<YsResult>(str); 181 } 182 183 /// <summary> 184 /// 获取单个设备信息 185 /// </summary> 186 /// <param name="token"></param> 187 /// <param name="deviceSerial">设备序列号</param> 188 /// <returns></returns> 189 public static async Task<YsResult> GetDeviceInfo(string deviceSerial) 190 { 191 if (string.IsNullOrEmpty(deviceSerial)) 192 return null; 193 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/device/info?accessToken={GetToken().Result}&deviceSerial={deviceSerial}"); 194 YsResult result = JsonConvert.DeserializeObject<YsResult>(str); 195 if (result.code.Equals("200")) 196 return result; 197 return null; 198 } 199 200 /// <summary> 201 /// 开通直播功能 202 /// </summary> 203 /// <param name="deviceSerial">设备序列号</param> 204 /// <returns></returns> 205 public static async Task<YsResult> LiveOpen(string deviceSerial) 206 { 207 if (string.IsNullOrEmpty(deviceSerial)) 208 return null; 209 string str = await HttpHelper.HttpPostAsync($"{requestUrl}api/lapp/live/video/open?accessToken={GetToken().Result}&source={deviceSerial}:1"); 210 return JsonConvert.DeserializeObject<YsResult>(str); 211 } 212 213 214 } 215 216 /// <summary> 217 /// 萤石云返回对象 218 /// </summary> 219 public class YsResult<T> 220 { 221 public string code { get; set; } 222 public T data { get; set; } 223 public string msg { get; set; } 224 public int retcode { get; set; } 225 } 226 public class YsResult : YsResult<dynamic> 227 { 228 } 229 230 /// <summary> 231 /// 萤石云设备能力集 232 /// </summary> 233 public class YsRoles 234 { 235 /// <summary> 236 /// 是否支持视频图像加密 0 - 不支持, 1 - 支持 237 /// </summary> 238 public int supprot_encrypt { get; set; } = 0; 239 /// <summary> 240 /// 是否支持修改设备加密密码: 0 - 不支持, 1 - 支持 241 /// </summary> 242 public int support_modify_pwd { get; set; } = 0; 243 /// <summary> 244 /// 是否支持云台上下转动 0 - 不支持, 1 - 支持 245 /// </summary> 246 public int ptz_top_bottom { get; set; } = 0; 247 /// <summary> 248 /// 是否支持云台左右转动 0 - 不支持, 1 - 支持 249 /// </summary> 250 public int ptz_left_right { get; set; } = 0; 251 /// <summary> 252 /// 是否支持云台45度方向转动 0 - 不支持, 1 - 支持 253 /// </summary> 254 public int ptz_45 { get; set; } = 0; 255 /// <summary> 256 /// 是否支持云台缩放控制 0 - 不支持, 1 - 支持 257 /// </summary> 258 public int ptz_zoom { get; set; } = 0; 259 /// <summary> 260 /// 是否支持焦距模式 0 - 不支持, 1 - 支持 261 /// </summary> 262 public int ptz_focus { get; set; } = 0; 263 } 264 265 } 萤石云请求封装

 

三.使用开源流媒体框架

   开源流媒体框架就很多了,常见的SRS国产的。安装 推流 拉流。可用于直播/录播/视频客服等多种场景,其定位是运营级的互联网直播服务器集群。传送门:http://www.ossrs.net/srs.release/releases/ 喜欢的可以自己去了解了解。


提醒:你所购买的摄像头硬件上都会有摄像头的名称 序列号 验证码信息,摄像头厂商比如海康会有搜索局域网内摄像头的一个工具(官网去找)。一个Web界面的后台用于设置摄像头通道 配置信息等,在局域网内连接上摄像头 浏览器地址栏输入对应的地址就可以登录当前摄像头后台。

附赠几个rtsp rtmp免费测试地址(可以先让前端用这些地址先实现播放功能):

1 rtsp://184.72.239.149/vod/mp4:BigBuckBunny_175k.mov
2 rtsp://195.200.199.8/mpeg4/media.amp
3 rtmp://media3.sinovision.net:1935/live/livestream

 End...

 

相关内容

    暂无相关文章