HappyOnvif - ONVIF协议摄像机接入平台
平台功能:在平台上录入相机后台设置的onvif信息,对外提供webapi接口,如获取播放地址,ptz控制,预置点控制等功能
开发环境:NetCore 6.0 + Sqlite
一、相机管理
1. 添加相机
2. 控制面板
2.1 获取播放地址,并播放rtsp流
2.2 PTZ控制
2.3 预置点管理(获取,前往,添加,删除)
2.4 语音对讲(语音广播,开始对讲,结束对讲)需要支持onvif backchannel
rtsp转webrtc程序,用于html页面播放rtsp地址流
二、日志信息
三、 Swagger
四、程序结构
五、部分接口代码
using HappyOnvif.Utility; using HappyOnvif.Utilty; using HappyOnvif.Utilty.db; using HappyOnvif.Utilty.db.model; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace HappyOnvif.Controllers { [Route("api/onvif/[action]")] [ApiController] public class OnvifController : ControllerBase { /// <summary> /// 获取webrtc服务ip /// </summary> /// <returns></returns> [HttpGet] public ResponseExtend getwebrtcip() { var result = new ResponseExtend<string>(); try { result.data = Program.Rtsp2Webrtc_ServiceIp; } catch (Exception ex) { result.code = 10000; result.msg = ex.Message; Logger.Error($"系统调用接口/onvif/getwebrtcip 发生内部错误:{ex.Message}"); } FreeSqlHelper.InsterApiLog(HttpContext.Request.GetDisplayUrl(), JsonConvert.SerializeObject(result)); return result; } /// <summary> /// 设备初始化 /// </summary> /// <param name="model"></param> /// <returns></returns> [HttpPost] public ResponseExtend init([FromBody] Camera model) { string ip = model.Ip; var result = new ResponseExtend<bool>(); try { var onvif = DataMgr.GetOnvif(ip); if (null == onvif) { onvif = new OnvifWsdl(); bool status = onvif.Init(ip, model.HttpPort, model.RtspPort, model.Name, model.Pwd); if (status) { DataMgr.AddOnvif(ip, onvif); } result.data = status; } else { result.data = true; } } catch (Exception ex) { result.code = 10000; result.msg = ex.Message; Logger.Error($"系统调用接口/onvif/init 发生内部错误:{ex.Message}"); } FreeSqlHelper.InsterApiLog(HttpContext.Request.GetDisplayUrl(), JsonConvert.SerializeObject(result)); return result; } /// <summary> /// 获取播放地址 /// </summary> /// <param name="ip"></param> /// <returns></returns> [HttpGet] public ResponseExtend geturi(string ip = "192.168.0.13") { var result = new ResponseExtend<string>(); try { var onvif = DataMgr.GetOnvif(ip); if (null != onvif) { result.data = JsonConvert.SerializeObject(onvif.GetUri()); } else { result.code = 10001; result.msg = "设备尚未初始化"; } } catch (Exception ex) { result.code = 10000; result.msg = ex.Message; Logger.Error($"系统调用接口/onvif/geturi 发生内部错误:{ex.Message}"); } FreeSqlHelper.InsterApiLog(HttpContext.Request.GetDisplayUrl(), JsonConvert.SerializeObject(result)); return result; } /// <summary> /// 获取预置点 /// </summary> /// <param name="ip"></param> /// <returns></returns> [HttpGet] public ResponseExtend getpresets(string ip = "192.168.0.13") { var result = new ResponseExtend<string>(); try { var onvif = DataMgr.GetOnvif(ip); if (null != onvif) { // 获取预置点 onvif.PTZControl(ECommandType.GetPreset); result.data = JsonConvert.SerializeObject(onvif.Presets); } else { result.code = 10001; result.msg = "设备尚未初始化"; } } catch (Exception ex) { result.code = 10000; result.msg = ex.Message; Logger.Error($"系统调用接口/onvif/gettpresets 发生内部错误:{ex.Message}"); } FreeSqlHelper.InsterApiLog(HttpContext.Request.GetDisplayUrl(), JsonConvert.SerializeObject(result)); return result; } /// <summary> /// ptz控制 /// </summary> /// <param name="ip"></param> /// <param name="cmd">0:停止;1:上;2:左上;3:右上;4:下;5:左下;6:右下;7:左;8:右;9:聚焦+;10:聚焦-;11:变倍+;12:变倍-;13:光圈开;14:光圈关;15:调用预置位;16:前往预置点;17:设置预置位;18:删除预置位</param> /// <param name="presetname"></param> /// <param name="presettoken">通过gettpresets获取到value</param> /// <param name="speed">步长 0-10</param> /// <returns></returns> [HttpGet] public ResponseExtend ptz(string ip = "192.168.0.13", int cmd = 0, int speed = 5, string presetname = "", string presettoken = "") { var result = new ResponseExtend<string>(); try { var onvif = DataMgr.GetOnvif(ip); if (null != onvif) { result.data = onvif.PTZControl((ECommandType)cmd, speed, presetname, presettoken); } else { result.code = 10001; result.msg = "设备尚未初始化"; } } catch (Exception ex) { result.code = 10000; result.msg = ex.Message; Logger.Error($"系统调用接口/onvif/ptz 发生内部错误:{ex.Message}"); } FreeSqlHelper.InsterApiLog(HttpContext.Request.GetDisplayUrl(), JsonConvert.SerializeObject(result)); return result; } /// <summary> /// 获取websocket信息 /// </summary> /// <returns></returns> [HttpGet] public string wsinfo() { Dictionary<string, string> dicResult = new Dictionary<string, string>(); dicResult["ServerIp"] = Program.Rtsp2Webrtc_ServiceIp; dicResult["WebSocketPort"] = Program.WebSocketPort.ToString(); return JsonConvert.SerializeObject(dicResult); } /// <summary> /// 播放音频文件 /// </summary> /// <param name="ip"></param> /// <param name="type">音频类型(1:1.g711a;2:2.g711a;3:3.g711a)</param> /// <returns></returns> [HttpGet] public async Task<ResponseExtend> playmediafile(string ip, int type) { var result = new ResponseExtend<string>(); try { var onvif = DataMgr.GetOnvif(ip); if (null != onvif) { string filePath = AppDomain.CurrentDomain.BaseDirectory + "g77a//"; switch (type) { case 1: filePath += "1.g711a"; break; case 2: filePath += "2.g711a"; break; case 3: filePath += "3.g711a"; break; default: break; } string rtspUrl = $"rtsp://{ip}:{onvif.RtspPort}"; string rtpmap = await onvif.Session.InitVoice(rtspUrl, onvif.Name, onvif.Pwd); result.data = onvif.Session.Status ? "初始化成功,rtpmap=" + rtpmap : "初始化失败:" + onvif.Session.Errmsg; if (!onvif.Session.Status || 0 != onvif.Session.VoiceStatus) { result.code = 10001; result.msg = "状态错误"; } else { onvif.Session.VoiceStatus = 1; byte[] audio = System.IO.File.ReadAllBytes(filePath);// out1_8k.g711a css.pcm left.g711a ThreadPool.QueueUserWorkItem(async p => { string note = ""; try { await onvif.Session.PlayAudio_G711A(audio, 25, 8000, onvif.Session.Ssrc2, onvif.Session.Channel); note = "播放结束"; onvif.Session.VoiceStatus = 0; } catch (Exception ex) { note = "播放失败,errmsg:" + ex.Message; } finally { onvif.Session.Close(); } }); } } else { result.code = 10001; result.msg = "设备尚未初始化"; } } catch (Exception ex) { result.code = 10000; result.msg = ex.Message; Logger.Error($"系统调用接口/onvif/media 发生内部错误:{ex.Message}"); } FreeSqlHelper.InsterApiLog(HttpContext.Request.GetDisplayUrl(), JsonConvert.SerializeObject(result)); return result; } /// <summary> /// 语音对讲申请 /// </summary> /// <param name="ip"></param> /// <returns></returns> [HttpGet] public async Task<ResponseExtend> talk(string ip) { var result = new ResponseExtend<bool>(); try { var onvif = DataMgr.GetOnvif(ip); if (null != onvif) { string rtspUrl = $"rtsp://{ip}:{onvif.RtspPort}"; string rtpmap = await onvif.Session.InitVoice(rtspUrl, onvif.Name, onvif.Pwd); result.data = onvif.Session.Status; } else { result.code = 10001; result.msg = "设备尚未初始化"; } } catch (Exception ex) { result.code = 10000; result.msg = ex.Message; Logger.Error($"系统调用接口/onvif/talk 发生内部错误:{ex.Message}"); } FreeSqlHelper.InsterApiLog(HttpContext.Request.GetDisplayUrl(), JsonConvert.SerializeObject(result)); return result; } } }
qq:505645074
分类:
流媒体服务器
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具