昨天写完了实时监控的流程也大概点了下主要的代码,看官觉得不妥的话咱回头修改,欢迎指正。今天主要写下对监控进行回放的基本思路和实现。
先上基本开发思路:上一篇我们获取到的截屏除了作为实时发送外,同时在被监控端做了存储,作回放用,原理简单来说就是把图片按顺序连续发送到监控端,监控端更换图片。
按原理,我们通过io取出一张图片,然后发出去,但是问题来了,io是很慢的,我们看视频是的时候肯定不希望一卡一卡的,我也是写完了test方法才想起来这问题的,看来设计阶段的东西只能提供方向性的指导,内部细节还需不断发现和完善这话还是很对的。我们在网络上观看视频之前,一般都是需要缓冲下,利用这一原理我们就能很好的解决流畅性问题,比如我们的图片资源一共有80张,第一次我们可以取出前40张发过去,监控端我们这样设计,得到这40张图片后,我们把这40张图片又分为两个20张的部分,播放前20张(后面提到的“后20张”指的是这40张中的后面20张图片),播放完毕后后我们就可以向被监控端取后20张过来,在网络相对快的情况下,这20张图传输时间加上io时间一般没有播放后20张图片的耗时长,我们新到的20张图片缓存到位,后20张播完了,接着这后来的20张继续播,如此循环直到最后的缓存张数不足20算播放结束。
现在上代码,被监控端的发送代码:
/// <summary> /// 发送一定量的图片到服务器去做缓存用,当当前时间有值时,选择当前时间的图片开始计算 /// </summary> /// <param name="path"></param> /// <param name="datetime"></param> public void SendPicToServer(string path, string datetime) { if (Imagespath == null) { try { Imagespath = Directory.GetFiles(path); if (Imagespath.Length >= cachnum)//首次播放先判断资源的数量满足缓冲数量否,如果满足就发送 { for (int i = 0; i < cachnum; i++) { Dictionary<string, Image> dic = new Dictionary<string, Image>(); TCPClientManager tcp = new TCPClientManager(1); MemoryStream ms = null; BinaryFormatter formatter = null; try { Bitmap bit = new Bitmap(Imagespath[i]); dic.Add(Imagespath[i], bit); ms = new MemoryStream(); formatter = new BinaryFormatter(); formatter.Serialize(ms, dic); byte[] buf = ms.ToArray(); 发送方法(); } catch (Exception ex) { ex.ToString(); } finally { if (ms != null) ms.Close(); } index++; } } else//如果不满足把全部的数据都发走,并告知已经没有数据可发了,服务器端根据命令做处理 { for (int i = 0; i < Imagespath.Length; i++) { Dictionary<string, Image> dic = new Dictionary<string, Image>(); TCPClientManager tcp = new TCPClientManager(1); MemoryStream ms = null; BinaryFormatter formatter = null; index++; Bitmap bit = new Bitmap(Imagespath[index]); dic.Add(Imagespath[index], bit); ms = new MemoryStream(); formatter = new BinaryFormatter(); formatter.Serialize(ms, dic); byte[] buf = ms.ToArray(); 发送方法(); if (index >= Imagespath.Length - 1) { 发送(没有资源了); } } } } catch (Exception ex) { ex.ToString(); } finally { if (ms != null) ms.Close(); } currentTime = Imagespath[index]; } } }
这是监控端的主要代码:
List<CacheImage> _listimage1 = new List<CacheImage>();//增加的缓存 List<CacheImage> _listimage2 = new List<CacheImage>();//帮助缓存 List<CacheImage> _playListImages = new List<CacheImage>();//总缓存 /// <summary> /// 回放开发思路: /// 先向客户端请求40张图片, /// 播放到第cachenum/2张时,告知客户端再发cachenum/2张过来, /// 得到cachenum/2张图片后,加到播放图片队列中,不断循环 /// </summary> /// <param name="obj"></param> void Instance_DealReplayPicEvent(MsgEventArgs obj) { byte[] buf = (byte[])obj.Data; MemoryStream ms = null; BinaryFormatter formatter = null; try { ms = new MemoryStream(buf); formatter = new BinaryFormatter(); Dictionary<string, Image> dic = (Dictionary<string, Image>)formatter.Deserialize(ms); if (_first) { foreach (string key in dic.Keys) { CacheImage cai = new CacheImage() { PicName = key, Picture = dic[key] }; _listimage1.Add(cai); } if (_listimage1.Count == cachenum) { _playListImages.AddRange(_listimage1); _first = false; } } else { foreach (string key in dic.Keys) { CacheImage cai = new CacheImage() { PicName = key, Picture = dic[key] }; _listimage2.Add(cai); if (_listimage2.Count == cachenum / 2) { _listimage1 = _listimage2; _playListImages.AddRange(_listimage1); _listimage2.Clear(); } } } if (_playListImages.Count >= cachenum) { if (_firstplay) { th = new System.Threading.Timer(new System.Threading.TimerCallback(Play), this, 0, 200); _firstplay = false; } else if (_jumpPlay) { if (th != null) { th.Change(0, 200); } _jumpPlay = false; } } } catch (Exception ex) { } finally { if (ms != null) ms.Close(); } }
播放代码:
//播放 void Play(object obj) { try { object lockThis = new object(); lock (lockThis) { if (_palyover) { if (index < _playListImages.Count) { index++; currprogress++; } if (_playListImages.Count > index) { if (index == 0) { index++; } this.picreplay.Image = _playListImages[index].Picture; } else { th.Change(-1, 0); _palyover = false; } } else { if (index <= (cachenum / 2) - 2) { index++; } if (_playListImages.Count > index) { this.picreplay.Image = _playListImages[index].Picture; if (index >= (cachenum / 2) - 1) { if (_playListImages.Count == cachenum)//当缓冲区的数量饱和时,删除一半的缓冲图片 { index = -1; _listimage2.Clear(); _listimage1.Clear(); _playListImages.RemoveRange(0, cachenum / 2); 请求发图(); } else if (cachenum / 2 < _playListImages.Count && _playListImages.Count < cachenum)//当缓冲区图片小于缓冲区域大小且缓冲区图片数大于缓冲区一半大小时,移除前一半的缓冲图片,不再发送请求 { index = -1; _playListImages.RemoveRange(0, cachenum / 2); } } else//当缓冲区图片数小于缓冲区大小时,判断是否播放到了最后 { if ((_allsourceNum - currprogress) < cachenum / 2) { if ((_allsourceNum - currprogress) <= 0) { th.Change(-1, 0); } } } } } } } catch (Exception ex) { ex.ToString(); index = -1; _listimage2.Clear(); _listimage1.Clear(); } }
以上是两端的主要代码,思路在那了,其他代码就不贴出来了,感觉意义不大,好了看官们有建议可以提出来,大家一起进步,啊怎么还不供暖,冷死了。