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

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

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

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

一.自建流媒体服务器

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

  1. 先客户端软件或设备采集视频流和语音流,或者是摄像头硬件采集的画面流等(如何采集就属于硬件相关的问题了,此处不讨论)
  2. 然后通过推流的方式推到流媒体服务器,推流协议可以使用RTMP RMSP,这2种都是基于tcp的 不会丢包。但是很容易造成高延迟(具体的看服务器 网络 是否做CDN来支撑)。

    1 //可指定h264或h265编码,可以把h265编码看成是h264编码的升级版,在码率 体积 清晰度 移动补偿上更友好些
    2 //大体结构为:rtsp://摄像头用户名:密码@地址:端口 服务器上地址参数...
    3 rtsp://admin:[email protected]:554/h264/ch1/main/av_stream
    4 rtsp://admin:[email protected]:554/Streaming/Channels/101?transportmode=unicast

    以上方式只是实现了流推送到了服务器,并没有指定它播放地址以及播放的转码。因此我们可以考虑使用ffmpeg,这是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。也就是使用ffmpeg不光可以本地采集流还可以指定推送到那一台服务器上和它的播放地址等等;

    1 //ffmpeg -re -i表示使用的协议和协议的参数,具体的参数意义请百度
    2 //接着是和上面一样的推流,这里使用的是rtsp,建议用rtmp,本帅在使用中感觉rtmp兼容性更好 web前端使用rtmp更方便。比如前端用Flash插件。或者Video标签等等。
    3 //然后是基于tcp 转码 播放的地址,比如播放地址是:rtsp://117.250.250.250/Cameratest
    4 ffmpeg -re -i rtsp://admin:[email protected]:554/h264/ch1/main/av_stream -rtsp_transport tcp -vcodec h264 -f rtsp rtsp://localhost/test
    5 ffmpeg -i rtsp://admin:[email protected]:554/h264/ch1/main/av_stream -rtsp_transport tcp -vcodec h264 -f rtsp rtsp://117.250.250.250/Cameratest

    注意播放地址前指定播放协议,比如rtsp rtsp://117.250.250.250/Cameratest。如果是rtmp那么最后就应该是:rtmp rtmp://117........................

  3. 流媒体服务器做一些编码转码处理等将流分发给各个客户端,进而进行拉流播放。那么问题来了  如何实现流媒体服务器呢?如何架设???
  4. 架设上我们可以使用nginx rtmp-module模块来架设,架设好后就可以使用rtmp推流给它。还可以用上面第2点中的ffmpeg命令写一个bat脚本来测试摄像头和架设的流媒体。
  5. PC端播放使用rtmp Flash来进行播放(H5中的Video标签了解一下),移动端播放使用HLS m3u8 rtmp来进行播放(具体播放方式视项目框架情况而定)。看网上有人还使用flv + http stream 进行播放的。
  6. 后期出现了并发 播放量增多的压力可以把nginx做分层(接入层+交换层),或者是转发一下做负载均衡,或者CDN来支撑。前期如果考虑到后期会使用CDN也可以直接跳过nginx 一开始用cdn的直播服务。

二.接入第三方平台

  在之前的项目中是购买了海康威视的摄像头,所以为了方便快捷的开发,是接入了第三方平台,由第三方平台进行管理和转发。大体流程是往第三方平台注册摄像头信息(序列号 验证码),然后流直接走第三方平台,自己服务端只需要获取三方平台的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...

原文地址:https://www.cnblogs.com/CDLinXi/p/12653445.html

时间: 2024-10-23 02:36:29

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

linux设备上的Onvif 实现21:解决大华摄像头无法使用问题

好长时间没有再写该系列文章了,最近刚好摸索着解决了大华摄像头无法使用问题,记录下来,应该对其他博友有所帮助.之前虽然写了一大堆文章说明了如何使用gsoap连接摄像头,但这是针对一台海康的摄像头开发的,一旦使用了同品牌不同型号摄像头或者其他牌子的摄像头就可能出现兼容性问题,导致无法使用.我就是碰到了这个问题,测试过的多个品牌型号摄像头,有的能直接使用,有的不能使用,问题各部相同.本文就是针对大华摄像头的问题解决过程说明. 摄像头型号:DH-IPC-HDW130S-0600B MAC:90:02:A

监控初始化-大华摄像头设置

大华监控摄像头初始化 一.摄像头默认设置 大华摄像头默认IP:192.168.1.108 用户名:admin 二.摄像头初始化设置 1.设置电脑IP地址和摄像头IP地址在同一个网段 2.打开IE浏览器输入摄像头初始IP:192.168.1.108. 3.在弹出的设备初始化界面,设置登陆密码及恢复密码的手机号. 4.设置完成后,进入登录窗口,输入用户名:admin和初始化设置的密码进行登陆. 5.进入相机设置-视频-视频叠加-通道标题,设置摄像头标题. 6.IP地址更改:网络设置-TCP/IP,输

微信扫一扫,手机就能看远程视频监控直播

眼见为实,主要为幼儿园.食品安全.养殖场.交通路况.景点直播等已经安装过监控摄像头领域提供实时视频.微信直播. 目前市场上监控厂商提供手机远程监控太多了,但没有提供微信视频监控直播公司不多,因为监控视频转码成直播流相对比较麻烦,我公司采用自主研发的视频转码网关采集兼容第三方监控摄像头信号,把监控视频推送到云眼微视平台转码生成HTML5进行微信直播,可接入到类似手机.电脑无控件直播,类似云事通"视频直播广场",已经在幼儿园.农场.智慧城市等很多地方有应用,反应还不错的!

同一路摄像头视频流接入RTSP_Onvif网页无插件直播流媒体服务器EasyNVR与其他平台播放视频有差异的原因分析

背景分析 随着平安城市.智慧城市.雪亮工程.智能交通等各项建设的持续开展,安防逐渐得到普及,面对如此广阔的市场,对安防企业来说不仅仅是机遇更多的是挑战.为了解决这些问题,近年来,视频监控行业发展方向主要为:“高清化.网络化.智能化”.视频监控设备技术性极强,系统的创新升级同时也在引导市场需求的变化并创造了新的市场需求. 基于AI视频智能分析云终端 EasyNVR智能云终端是基于视频智能传输技术.视频AI分析技术.智能云组网技术.边缘计算技术.视频大数据技术.窄带通信技术.远程监测技术以及智能语音

【转】网络视频监控P2P解决方案

一.摘要 本文分析了日益增长的民用级别家庭和个人网络视频监控市场的需求特点,并给出了一种经济可行易于大规模部署的P2P解决方案. 由于篇幅有限,本文只给出了方案的思路,未对更深入的技术细节做详细的论述,有兴趣的朋友可以继续深入研究. 二.关键词 IPCAM,  P2P,NAT,  STUN,  TURN,  ICE,  PJSIP,  OPENSIPS,  UDT, TCP,  UDP 三.需求提出 网络视频监控市场持续火爆升温,除了公共安全市场持续高速增长之外,民用市场中家庭和个人视频监控的需

大华摄像头

http://wenku.baidu.com/link?url=OPWfVCUqSjHftEVUvzRKnigEipCFgtI8zHt8RfWVX7AlC811yke8surXyGkA0mqyVSaUf8JSiXUOu8W6EunE4KEmq46DzP3-AYIBjRw3DxK http://wenku.baidu.com/link?url=BqKZq41_SuM_4c1cBg7KWYyxuG_q8sJFp-xYw0kzfgf-f-P6yyLkSOqRdVQm-iPJ0QdLzpcrC6QSaR

海康大华摄像头高起播低延时RTSP网页无插件流媒体播放器EasyPlayer-RTSP-Win录像和抓图实现线程优化方案分享

EasyPlayer-RTSP播放器是一套RTSP专用的播放器,包括有:Windows(支持IE插件,npapi插件).Android.iOS三个平台,是由青犀TSINGSEE开放平台开发和维护的区别于市面上大部分的通用播放器,EasyPlayer-RTSP系列从2014年初发展至今得到了各行各业(尤其是安防行业)的广泛应用,其主要原因是EasyPlayer-RTSP更加精炼.更加专注,具备低延时和高RTSP协议兼容性. ? EasyPlayer-RTSP-Win录像和抓图实现线程优化 测试发现

nginx与nginx-rtmp-module搭建流媒体服务器

转载自my student 克明zhang 现在,一起学习一下如何自己搭建一个流媒体服务器吧! 本次搭建流媒体使用的环境是centos 7.0+nginx: 让我们一起开始奇妙的流媒体之旅吧! 1.下载nginx-rtmp-module: nginx-rtmp-module的官方github地址:https://github.com/arut/nginx-rtmp-module 使用命令: [php] view plain copy git clone https://github.com/ar

海康、大华等网络摄像头RTSP_Onvif网页无插件直播流媒体服务器EasyNVR鉴权出现跨域问题的解决方法

背景分析 随着平安城市.智慧城市.雪亮工程.智能交通等各项建设的持续开展,安防逐渐得到普及,面对如此广阔的市场,对安防企业来说不仅仅是机遇更多的是挑战.现今大多数摄像头一直没能摆脱人工监控的传统监控方式,由此导致了大量视频数据堆积占用存储资源.实时性差.检索困难等问题,海量摄像头带来的海量视频数据检索工作需要耗费大量警力. 为了解决这些问题,近年来,视频监控行业发展方向主要为:“高清化.网络化.智能化”.视频监控设备技术性极强,系统的创新升级同时也在引导市场需求的变化并创造了新的市场需求. Ea