基于superSocket——AForge的UDP传输摄像案例——服务端
2021/4/12 10:25:53
本文主要是介绍基于superSocket——AForge的UDP传输摄像案例——服务端,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
服务端
业务逻辑:
这里打个比方,有个陌生人(摄像客户端)一直在敲你家的门,但是你没开,等到你爸(Web客户端)给你一个命令开门,你就把门打开,让陌生人进来,并进行核对信息(信息匹配),原来是隔壁老王,给你带了好吃的(UDP数据),你于是就把吃的留下来了。
准备操作:
用superSocket接受客户端发来的UDP数据,superSocket开源框架网址:https://github.com/kerryjiang/SuperSocket。
在NuGet中添加:SuperSocket,SuperSocket.Engine,SuperSocket.ProtoBase
用FFMPEG、VFW进行录入视频。
①.新建UdpTestSession继承AppSession,用于建立会话;
using SuperSocket.SocketBase; using System; using System.Collections.Generic; using System.Linq; using System.Text; using SuperSocket.SocketBase.Protocol; namespace Receive.udp { public class UdpTestSession : AppSession<UdpTestSession, MyUdpRequestInfo> { protected override void OnSessionClosed(CloseReason reason) { base.OnSessionClosed(reason); } protected override void OnSessionStarted() { Console.WriteLine("start Session"); base.OnSessionStarted(); } protected override void HandleUnknownRequest(MyUdpRequestInfo requestInfo) { Console.WriteLine("unKnownRequest"); base.HandleUnknownRequest(requestInfo); } } }
②.新建MyUdpRequestInfo,用于封装会话信息与传输的数据信息。
using Receive.udp; using SuperSocket.ProtoBase; using SuperSocket.SocketBase.Protocol; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Receive.udp { public class MyUdpRequestInfo : UdpRequestInfo, IPackageInfo { public MyUdpRequestInfo(string key, string sessionID) : base(key, sessionID) { } public byte[] Body { get; set; } } }
③.新建MyReceiveFilter,用于处理(过滤)传输的数据,封装成MyUdpRequestInfo。
其中这步中,我将客户端分为web客户端与摄像设备客户端,web 发送的数据类型 密钥+设备号+录像时间+结束标志,录像设备发送的数据 密钥+设备号+照片+结束标志。其中密钥与设备号可以设置固定长度,不足补休止符。(见下辅助类)
using System.Linq; namespace Receive.udp { public class MyReceiveFilter : SuperSocket.SocketBase.Protocol.IReceiveFilter<MyUdpRequestInfo> { public int LeftBufferSize { get { return 0; } } public SuperSocket.SocketBase.Protocol.IReceiveFilter<MyUdpRequestInfo> NextReceiveFilter { get { return this; } } public SuperSocket.SocketBase.Protocol.FilterState State { get; private set; } /// <summary> /// 过滤器接受信息 解析byte[] /// </summary> /// <param name="readBuffer"></param> /// <param name="offset"></param> /// <param name="length"></param> /// <param name="toBeCopied"></param> /// <param name="rest"></param> /// <returns></returns> public MyUdpRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest) { var endFlag = HandleUdpUtils.getEndFlagStr(readBuffer, length); if (!endFlag.Equals(HandleUdpUtils.IMG_END_FLAG)||!endFlag.Equals(HandleUdpUtils.WEB_END_FLAG)) //判断结束标志 { int endFlagSize = 2; string privateKey = HandleUdpUtils.getPrivateKey(readBuffer); string sesssionId = HandleUdpUtils.getMachineCode(readBuffer); //machineCode 当成sessionId rest = 0; byte[] body = readBuffer.Skip(HandleUdpUtils.MACHINE_CODE_LENGTH).Take(length - HandleUdpUtils.MACHINE_CODE_LENGTH - endFlagSize).ToArray(); return new MyUdpRequestInfo(privateKey, sesssionId) { Body = body }; }else { rest = 0; return null; } } public void Reset() { } } }
④.新建UdpAppServer
集中处理传递的数据,根据不同的客户端请求,进行不一样的操作。web设备的请求则开启摄像,摄像设备则进行视频的录入。
using SuperSocket.SocketBase; using System; using System.Collections.Generic; using System.Linq; using System.Text; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Protocol; using SuperSocket.ProtoBase; using SuperSocket.SocketBase.Logging; using System.Collections.Concurrent; using log4net; using System.IO; using System.Drawing; namespace Receive.udp { class UdpAppServer : AppServer<UdpTestSession, MyUdpRequestInfo> { public static readonly log4net.ILog LOG = log4net.LogManager.GetLogger("VideoSocket"); public static readonly string PATH = "D:\\ReadVersionD\\video"; public static Dictionary<string, string> PRIVATE_KEY = new Dictionary<string, string>(); //deviceCode->key public static readonly int MIN_IMG_SIZE = 100; public UdpAppServer() : base(new DefaultReceiveFilterFactory<MyReceiveFilter, MyUdpRequestInfo>()) { } public static ConcurrentDictionary<string, VideoPackageModel> FILE_WRITERS = new ConcurrentDictionary<string, VideoPackageModel>(); //读取文件 protected override void OnSystemMessageReceived(string messageType, object messageData) { Console.WriteLine(messageType); base.OnSystemMessageReceived(messageType, messageData); } /// <summary> /// 过滤器后 进入该方法 /// </summary> /// <param name="session"></param> /// <param name="requestInfo"></param> protected override void ExecuteCommand(UdpTestSession session, MyUdpRequestInfo requestInfo) { if (requestInfo.Key.Equals(HandleUdpUtils.WEB_SESSION_KEY)) { handleWebUdpSocket(requestInfo, session); return; } string privateKey = ""; if (PRIVATE_KEY.TryGetValue(requestInfo.SessionID, out privateKey) && privateKey.Equals(requestInfo.Key)) { handleDeviceInfo(requestInfo); session.Send("receive success"); } else { LOG.Error(requestInfo.SessionID + "|不存在该设备"); } } #region WEB /// <summary> /// 发送socket 到web客户端 /// </summary> /// <param name="requestInfo"></param> /// <param name="session"></param> private void handleWebUdpSocket(MyUdpRequestInfo requestInfo, UdpTestSession session) { switch (handleWebCommand(requestInfo)) { case 1: session.Send("addOK"); break; case 0: session.Send("dataError"); break; case -1: session.Send("alreadyExit"); break; } } /// <summary> /// body的组成 : 5|DateType /// </summary> /// <param name="requestInfo"></param> private int handleWebCommand(MyUdpRequestInfo requestInfo) { var startTime = DateTime.Now; DateTime endTime; int seconds = HandleUdpUtils.getLasingTime(requestInfo.Body,out startTime,out endTime); if (seconds == 0) { LOG.Error(requestInfo.SessionID + "|获取录像持续时间失败!"); return 0; } string privateKey = ""; PRIVATE_KEY.TryGetValue(requestInfo.SessionID,out privateKey); if (!addVideoPackageModel(requestInfo.SessionID,privateKey,startTime,endTime,seconds)) { Console.WriteLine(requestInfo.SessionID + "|添加失败!"); LOG.Error(requestInfo.SessionID + "|添加失败!");//TODO 存在更新的操作 return -1; } else { Console.WriteLine(requestInfo.SessionID + "|添加成功!"); LOG.Info(requestInfo.SessionID + "|添加成功!"); return 1; } } /// <summary> /// 添加VideoPackageModel /// </summary> /// <param name="sessionId"></param> /// <param name="model"></param> /// <returns></returns> private bool addVideoPackageModel(string sessionId, string privateKey,DateTime startTime,DateTime endTime ,int seconds) { VideoPackageModel oldModel; if(FILE_WRITERS.TryGetValue(sessionId,out oldModel)) { if (oldModel.Disposed) //可能没删除 { FILE_WRITERS.TryRemove(sessionId,out oldModel); VideoPackageModel model = new VideoPackageModel(privateKey, sessionId, startTime, endTime, seconds, PATH); //避免计时器出错 return FILE_WRITERS.TryAdd(sessionId, model); } return false; }else { VideoPackageModel model = new VideoPackageModel(privateKey, sessionId, startTime, endTime, seconds, PATH); return FILE_WRITERS.TryAdd(sessionId, model); } } #endregion #region 录像设备 /// <summary> /// 处理设备发来的视频 /// </summary> /// <param name="requestInfo"></param> private void handleDeviceInfo(MyUdpRequestInfo requestInfo) { VideoPackageModel videoModel = getVideoPackage(requestInfo.SessionID); if (videoModel == null || requestInfo.Body.Length< MIN_IMG_SIZE) { return; } MemoryStream stream = null; stream = new MemoryStream(requestInfo.Body); Bitmap img = null; try { img = HandleUdpUtils .BytesToBitmap(requestInfo.Body) ; // img.Save("D:\\JGHPCXReadVersionD\\video\\001\\test.jpg"); videoModel.wirteVideo(img); } catch (Exception e) { LOG.Error(requestInfo.SessionID+"|"+e.ToString()); Console.WriteLine(requestInfo.SessionID + "|" + e.ToString()); }finally { if(stream!=null) stream.Close(); if(img!=null) img.Dispose(); } } /// <summary> /// getVideoPackage /// </summary> /// <param name="machineCode"></param> /// <returns></returns> private VideoPackageModel getVideoPackage(string sessionId) { VideoPackageModel video; if(FILE_WRITERS.TryGetValue(sessionId, out video)) { LOG.Error(sessionId + "|设备未开启录像"); } return video; } #endregion } }
辅助类——VideoPackageModel:用于封装Video数据与操作。
using Accord.Video.FFMPEG; using Accord.Video.VFW; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Timers; namespace Receive.udp { public class VideoPackageModel: IDisposable { //校验码 public string PrivateKey { get; } //机器码 public string SessionId { get;} //录制开始时间 public DateTime StartTime { get; } //录制结束时间 public DateTime EndTime { get; } //视频路径 public string VideoPath { get; } //计时器 private System.Timers.Timer Timer = new System.Timers.Timer(); //视频写入(32位的FFMPEG) // private VideoFileWriter Writer = new VideoFileWriter(); //是否释放资源 public bool Disposed = false; //avi读取 private AVIWriter aviWriter = new AVIWriter("wmv3"); //VideoFileWriter 所需的时间戳 DateTime _firstFrameTime; public VideoPackageModel(string privateKey,string sessionId,DateTime startTime,DateTime endTime,int second,string path) { SessionId = sessionId; PrivateKey = privateKey; StartTime = startTime; EndTime = endTime; _firstFrameTime = DateTime.Now; int width = 848; //录制视频的宽度 int height = 480; //录制视频的高度 int fps = 20; path = getTimeStamp(path); aviWriter.Open(path, Convert.ToInt32( width), Convert.ToInt32( height)); // Writer.Open(path, width, height, fps, VideoCodec.Default); int lastTime = second * 1000; Console.WriteLine("持续时间"+lastTime); Timer.Elapsed += new System.Timers.ElapsedEventHandler(endTimeEvent); //到达时间的时候执行事件; Timer.AutoReset = false; //设置是执行一次(false)还是一直执行(true); Timer.Interval = lastTime;//设置定时间隔(毫秒为单位) Timer.Enabled = true; } /// <summary> /// video 存储路径 /// </summary> /// <param name="path"></param> /// <returns></returns> private string getTimeStamp(string path) { System.DateTime originTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); long timeStamp = (long)(StartTime - originTime).TotalMilliseconds; string filePath = path + "\\" + SessionId; if (!Directory.Exists(filePath)) { Directory.CreateDirectory(filePath); } return filePath + "\\" + timeStamp + ".avi"; } /// <summary> /// 结束资源 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void endTimeEvent(object sender, ElapsedEventArgs e) { using (this) { VideoPackageModel videoModel; UdpAppServer.FILE_WRITERS.TryRemove(SessionId, out videoModel); Console.WriteLine("endWriters"); } } /// <summary> /// 写入视频 /// </summary> /// <param name="img"></param> public void wirteVideo(Bitmap img) { try { lock (aviWriter) { if (aviWriter != null) aviWriter.AddFrame(img); } // if(Writer != null && Writer.IsOpen) // { // lock (Writer) // { // if (_firstFrameTime != null) // { // Writer.WriteVideoFrame(img, TimeSpan.FromMilliseconds(DateTime.Now.ToUniversalTime().Subtract( //new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) //).TotalMilliseconds - _firstFrameTime.ToUniversalTime().Subtract( //new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) //).TotalMilliseconds)); // } // else // { // Writer.WriteVideoFrame(img); // _firstFrameTime = DateTime.Now; // } // } // } } catch (Exception e) { _firstFrameTime = DateTime.Now; Console.WriteLine(e.ToString()); } } #region Dispose 操作 public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!Disposed) { if (disposing) { if (aviWriter != null) { try { aviWriter.Close(); aviWriter.Dispose(); } catch (Exception e) { Console.WriteLine(e); } finally { aviWriter = null; } } if(Timer!=null) { Timer.Dispose(); Timer = null; } } //处理非托管 Disposed = true; } } ~VideoPackageModel() { Dispose(false); } #endregion } }
辅助类——字节数组操作类。
using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Text; namespace Receive.udp { //handle byte public class HandleUdpUtils { public static readonly string IMG_END_FLAG = "##"; //设备传来的结束标志 public static readonly string WEB_END_FLAG = "**"; //WEB传来的结束标志 public static readonly string WEB_SESSION_KEY = "f21c2a0689443179082e02f8f44079"; public static readonly int KEY_LENGTH = 40; public static readonly int MACHINE_CODE_LENGTH = 80; public enum TimeTypeEnum { Day,Hour, Minute,Second } /// <summary> /// 读取byte[]并转化为图片 /// </summary> /// <param name="bytes">byte[]</param> /// <returns>Image</returns> public static Bitmap BytesToBitmap(byte[] Bytes) { MemoryStream stream = null; try { stream = new MemoryStream(Bytes); return new Bitmap((Image)new Bitmap(stream)); } catch (ArgumentNullException ex) { throw ex; } catch (ArgumentException ex) { throw ex; } finally { stream.Close(); } } /// <summary> /// 得到传输来的结束标志 /// </summary> /// <param name="bytes"></param> /// <param name="length"></param> /// <returns></returns> public static string getEndFlagStr(byte[] bytes, int length) { int begin = length - 2; int size = 2; string endFlag = Encoding.ASCII.GetString(bytes, begin, size); return String.IsNullOrEmpty(endFlag)?"":endFlag; } /// <summary> /// 根据标志判断客户端类型 /// </summary> /// <param name="bytes"></param> /// <param name="length"></param> /// <returns></returns> public static int getClientType(byte[] bytes, int length) { var endFlagStr= getEndFlagStr(bytes ,length); if (endFlagStr.Equals(WEB_END_FLAG)) //web return 1; else if (endFlagStr.Equals(IMG_END_FLAG)) //device return 2; else return 0; } /// <summary> /// 长度为40的密钥 /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static string getPrivateKey(byte[] bytes) { string privateKey = ""; if (bytes.Length <= KEY_LENGTH) return privateKey; privateKey = Encoding.ASCII.GetString(bytes, 0, KEY_LENGTH).TrimEnd('\0'); //取掉休止符 return privateKey; } /// <summary> /// 得到机器码 /// </summary> /// <param name="bytes"></param> /// <returns></returns> public static string getMachineCode(byte[] bytes) { string machineCode = ""; if (bytes.Length <= MACHINE_CODE_LENGTH) return machineCode; machineCode = Encoding.ASCII.GetString(bytes, KEY_LENGTH, MACHINE_CODE_LENGTH-KEY_LENGTH).TrimEnd ('\0'); return machineCode; } /// <summary> /// 密钥构造成字节数组 (客户端) /// </summary> /// <param name="privateKey"></param> /// <returns></returns> public static byte[] InitPrivateKeyBytes(string privateKey) { byte[] fixedBytes = new byte[40]; if (String.IsNullOrEmpty(privateKey)) return fixedBytes; var bytes = Encoding.Default.GetBytes(privateKey); for (int i = 0; i < bytes.Length; i++) fixedBytes[i] = bytes[i]; return fixedBytes; } /// <summary> /// web取截止时间 /// </summary> /// <param name="body"></param> /// <param name="endTime"></param> /// <returns></returns> public static bool StrToTime(byte[] body, out DateTime endTime) { string str = Encoding.Default.GetString(body); endTime = DateTime.Now; try { DateTime dateTime = Convert.ToDateTime(str); endTime = dateTime; } catch (Exception) { return false; } return true; } /// <summary> /// 得到计时时间 /// </summary> /// <param name="body"></param> /// <param name="startTime"></param> /// <param name="endTime"></param> /// <returns></returns> public static int getLasingTime(byte[] body,out DateTime startTime,out DateTime endTime) { string str = Encoding.Default.GetString(body); int index = str.IndexOf("|"); startTime = DateTime.Now; endTime = DateTime.Now; if (index == -1) return 0; var timeType = str.Substring(index+1); //5|Second var num = 0; try { num = Convert.ToInt32(str.Substring(0, index)); } catch (Exception) { return 0; } switch (timeType) { case nameof(TimeTypeEnum.Day) : num *= 12 * 60 * 60; break; case nameof(TimeTypeEnum.Hour): num *= 60 * 60; break; case nameof(TimeTypeEnum.Minute): num *= 60; break; case nameof(TimeTypeEnum.Second): break; default: num = 0; break; } endTime = startTime.AddSeconds(num); return num; } } }
主函数:
using Receive.udp; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Config; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UdpSocketServer { class Program { private static IServerConfig SERVER_CONFIG; private static IRootConfig ROOT_CONFIG; public static IServerConfig DefaultServerConfig { get { return new ServerConfig { Ip = "127.0.0.1", LogCommand = true, MaxConnectionNumber = 1000, Mode = SocketMode.Udp, Name = "Udp Test Socket Server", Port = 6602, ClearIdleSession = true, ClearIdleSessionInterval = 1, IdleSessionTimeOut = 5, SendingQueueSize = 100, ReceiveBufferSize = 50000 }; } } static void Main(string[] args) { initData(); SERVER_CONFIG = DefaultServerConfig; ROOT_CONFIG = new RootConfig(); var testServer = new UdpAppServer(); testServer.Setup(ROOT_CONFIG, SERVER_CONFIG); testServer.Start(); while (true) { } } private static void initData() { UdpAppServer.PRIVATE_KEY.Add("webSession", "f21c2a0689443179082e02f8f44079"); UdpAppServer.PRIVATE_KEY.Add("001", "key001"); UdpAppServer.PRIVATE_KEY.Add("002", "key002"); UdpAppServer.PRIVATE_KEY.Add("003", "key003"); } } }
下载Demo地址:
https://download.csdn.net/download/a748448660/10559647
————————————————
版权声明:本文为CSDN博主「追风筝的摆渡人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a748448660/article/details/81175712
https://blog.csdn.net/a748448660/article/details/81175712
这篇关于基于superSocket——AForge的UDP传输摄像案例——服务端的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-07fastcgi 是什么-icode9专业技术文章分享
- 2024-10-07fastcgi 的详细使用教程介绍-icode9专业技术文章分享
- 2024-10-07git如何更新单个文件到本地-icode9专业技术文章分享
- 2024-10-07如何使用ASM(Abstract Syntax Tree Manipulation)技术来修改第三方AAR依赖中的函数-icode9专业技术文章分享
- 2024-10-07Activity 跳转时间耗时很长怎么优化解决-icode9专业技术文章分享
- 2024-10-07Androud Toast 有哪些常用的第三方组件-icode9专业技术文章分享
- 2024-10-07在viewmodel中怎么使用 mmkv?-icode9专业技术文章分享
- 2024-10-07MMKV.defaultMMKV() 是单例模式吗?-icode9专业技术文章分享
- 2024-10-04el-table 开启定时器下,表格的选中状态会消失是什么原因-icode9专业技术文章分享
- 2024-10-03如何安装和初始化飞牛私有云 fnOS?-icode9专业技术文章分享