目前已经实现了 UDP+RTP 方式在不同物理机之间的媒体流传输。当然,由于没有基于 .NET 的媒体流压缩实现,所以直接传输的裸图 Bitmap。不过要求不高,帧率低一些,机器性能强一些,看着也很流畅。 能在桌面客户端上看到视频图像的功能已经完成了。下面需要考虑,如何通过浏览器来查看视频。 在不考虑使用 Flash、ActiveX 的条件下,貌似只能选择 MJPEG 方式。目前还没有研究在 HTML5 下视频是如何处理的,以后有时间可以探索。 目录什么是 MJPEG?看这里:
当然,我主要关注 MJPEG over HTTP 这段。
也就是说,建立 HTTP 连接后,服务端在 Response 消息中先发一个数据头 Header 告诉客户端,我后面的都是 JPEG 图片。图片之间使用 boundary-name 来区分,每个图片前都有自己的数据头来描述图片数据长度。
MJPEG数据头定义1 /// <summary> 2 /// 流头部 3 /// </summary> 4 public string StreamHeader 5 { 6 get 7 { 8 return "HTTP/1.1 200 OK" + 9 "\r\n" + 10 "Content-Type: multipart/x-mixed-replace; boundary=" + this.Boundary + 11 "\r\n"; 12 } 13 } 1 /// <summary> 2 /// 图片头部 3 /// </summary> 4 public string PayloadHeader 5 { 6 get 7 { 8 return "\r\n" + 9 this.Boundary + 10 "\r\n" + 11 "Content-Type: image/jpeg" + 12 "\r\n" + 13 "Content-Length: " + _contentLengthString + 14 "\r\n\r\n"; 15 } 16 } 这里的 Boundary 可以是任意字符串,只要你觉得唯一并能区分即可,比如我可以设置为“--dennisgao”。 服务器端实现Http 服务器其实就是个支持 Tcp 连接的服务器。 1 private AsyncTcpServer _server; 2 3 _server = new AsyncTcpServer(Port); 4 _server.Encoding = Encoding.ASCII; 1 public void Start() 2 { 3 _server.Start(10); 4 _server.ClientConnected += new EventHandler<TcpClientConnectedEventArgs>(OnClientConnected); 5 _server.ClientDisconnected += new EventHandler<TcpClientDisconnectedEventArgs>(OnClientDisconnected); 6 } 7 8 public void Stop() 9 { 10 _server.Stop(); 11 _server.ClientConnected -= new EventHandler<TcpClientConnectedEventArgs>(OnClientConnected); 12 _server.ClientDisconnected -= new EventHandler<TcpClientDisconnectedEventArgs>(OnClientDisconnected); 13 } 14 15 private void OnClientConnected(object sender, TcpClientConnectedEventArgs e) 16 { 17 _clients.AddOrUpdate(e.TcpClient.Client.RemoteEndPoint.ToString(), e.TcpClient, (n, o) => { return e.TcpClient; }); 18 } 19 20 private void OnClientDisconnected(object sender, TcpClientDisconnectedEventArgs e) 21 { 22 TcpClient clientToBeThrowAway; 23 _clients.TryRemove(e.TcpClient.Client.RemoteEndPoint.ToString(), out clientToBeThrowAway); 24 } 这里可以参考两篇文章中的实现。 发送图片数据首先要保证,对一个HTTP连接只能发一次流头,因为后面是接连不断的图片数据。当然,发点别的数据客户端也不会解码。 1 private void WriteStreamHeader() 2 { 3 if (_clients.Count > 0) 4 { 5 foreach (var item in _clients) 6 { 7 Logger.Debug(string.Format(CultureInfo.InvariantCulture, 8 "Writing stream header, {0}, {1}{2}", item.Key, Environment.NewLine, StreamHeader)); 9 10 _server.SyncSend(item.Value, StreamHeader); 11 12 TcpClient clientToBeThrowAway; 13 _clients.TryRemove(item.Key, out clientToBeThrowAway); 14 } 15 } 16 } 发送图片数据时,要保证图片的前面是图片头和长度信息,数据尾部要有换行符。 1 private void WritePayload(byte[] payload) 2 { 3 string payloadHeader = this.PayloadHeader.Replace(_contentLengthString, payload.Length.ToString()); 4 string payloadTail = "\r\n"; 5 6 Logger.Debug(string.Format(CultureInfo.InvariantCulture, 7 "Writing payload header, {0}{1}", Environment.NewLine, payloadHeader)); 8 9 byte[] payloadHeaderBytes = _server.Encoding.GetBytes(payloadHeader); 10 byte[] payloadTailBytes = _server.Encoding.GetBytes(payloadTail); 11 byte[] packet = new byte[payloadHeaderBytes.Length + payload.Length + payloadTail.Length]; 12 Buffer.BlockCopy(payloadHeaderBytes, 0, packet, 0, payloadHeaderBytes.Length); 13 Buffer.BlockCopy(payload, 0, packet, payloadHeaderBytes.Length, payload.Length); 14 Buffer.BlockCopy(payloadTailBytes, 0, packet, payloadHeaderBytes.Length + payload.Length, payloadTailBytes.Length); 15 16 _server.SendToAll(packet); 17 } |