成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C#開源實(shí)現(xiàn)MJPEG流傳輸

開發(fā) 后端
目前已經(jīng)實(shí)現(xiàn)了UDP+RTP 方式在不同物理機(jī)之間的媒體流傳輸。當(dāng)然,由于沒有基于 .NET 的媒體流壓縮實(shí)現(xiàn),所以直接傳輸?shù)穆銏DBitmap。不過(guò)要求不高,幀率低一些,機(jī)器性能強(qiáng)一些,看著也很流暢。

許久以前寫了篇文章《基于.NET打造IP智能網(wǎng)絡(luò)視頻監(jiān)控系統(tǒng)》,記錄和介紹了自己幾年來(lái)積累和演練的一個(gè)系統(tǒng)。發(fā)現(xiàn)幾個(gè)月過(guò)去了,沒有任何進(jìn)展。

目前已經(jīng)實(shí)現(xiàn)了UDP+RTP 方式在不同物理機(jī)之間的媒體流傳輸。當(dāng)然,由于沒有基于 .NET 的媒體流壓縮實(shí)現(xiàn),所以直接傳輸?shù)穆銏DBitmap。不過(guò)要求不高,幀率低一些,機(jī)器性能強(qiáng)一些,看著也很流暢。

能在桌面客戶端上看到視頻圖像的功能已經(jīng)完成了。下面需要考慮,如何通過(guò)瀏覽器來(lái)查看視頻。

在不考慮使用 Flash、ActiveX 的條件下,貌似只能選擇 MJPEG 方式。目前還沒有研究在 HTML5 下視頻是如何處理的,以后有時(shí)間可以探索。

什么是 MJPEG?

看這里:

當(dāng)然,我主要關(guān)注 MJPEG over HTTP 這段。

M-JPEG over HTTP
HTTP streaming separates each image into individual HTTP replies on a specified marker. RTP streaming creates packets of a sequence of JPEG images that can be received by clients such as QuickTime or VLC.
In response to a GET request for a MJPEG file or stream, the server streams the sequence of JPEG frames over HTTP. A special mime-type content type multipart/x-mixed-replace;boundary=<boundary-name> informs the client to expect several parts (frames) as an answer delimited by <boundary-name>. This boundary name is expressly disclosed within the MIME-type declaration itself. The TCP connection is not closed as long as the client wants to receive new frames and the server wants to provide new frames. Two basic implementations of a M-JPEG streaming server are cambozola and MJPG-Streamer. The more robust ffmpeg-server also provides M-JPEG streaming support.

也就是說(shuō),建立 HTTP 連接后,服務(wù)端在 Response 消息中先發(fā)一個(gè)數(shù)據(jù)頭 Header 告訴客戶端,我后面的都是 JPEG 圖片。圖片之間使用 boundary-name 來(lái)區(qū)分,每個(gè)圖片前都有自己的數(shù)據(jù)頭來(lái)描述圖片數(shù)據(jù)長(zhǎng)度。

MJPEG數(shù)據(jù)頭定義

  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 可以是任意字符串,只要你覺得唯一并能區(qū)分即可,比如我可以設(shè)置為“--dennisgao”。

#p#

服務(wù)器端實(shí)現(xiàn)

Http 服務(wù)器其實(shí)就是個(gè)支持 Tcp 連接的服務(wù)器。

  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.     } 

這里可以參考兩篇文章中的實(shí)現(xiàn)。

發(fā)送圖片數(shù)據(jù)

首先要保證,對(duì)一個(gè)HTTP連接只能發(fā)一次流頭,因?yàn)楹竺媸墙舆B不斷的圖片數(shù)據(jù)。當(dāng)然,發(fā)點(diǎn)別的數(shù)據(jù)客戶端也不會(huì)解碼。

  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.     } 

發(fā)送圖片數(shù)據(jù)時(shí),要保證圖片的前面是圖片頭和長(zhǎng)度信息,數(shù)據(jù)尾部要有換行符。

  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.     } 

結(jié)果演示

在可以成功發(fā)送流信息和圖片信息后,就可以在瀏覽器上查看視頻了。當(dāng)然,我用的 Google Chrome 。IE10 好奇葩,它會(huì)把流當(dāng)成文件不停的下載,搞不懂。

遠(yuǎn)程訪問(wèn)

局域網(wǎng)內(nèi)的無(wú)線設(shè)備,只要瀏覽器支持 MJPEG ,均可以查看視頻。我測(cè)試了 iPad 上的 Safari 是可以的,但 Chrome 卻直接解析成亂碼。

當(dāng)然,如果在路由器上配置轉(zhuǎn)發(fā)規(guī)則,就可以在外網(wǎng)訪問(wèn)了。

#p#

完整代碼

  1. public class MJpegStreamingServer  
  2.   {  
  3.     private static string _contentLengthString = "__PayloadHeaderContentLength__";  
  4.     private AsyncTcpServer _server;  
  5.     private ConcurrentDictionary<string, TcpClient> _clients;  
  6.  
  7.     public MJpegStreamingServer(int listenPort)  
  8.       : this(listenPort, "--dennisgao")  
  9.     {  
  10.     }  
  11.  
  12.     public MJpegStreamingServer(int listenPort, string boundary)  
  13.     {  
  14.       Port = listenPort;  
  15.       Boundary = boundary;  
  16.  
  17.       _server = new AsyncTcpServer(Port);  
  18.       _server.Encoding = Encoding.ASCII;  
  19.       _clients = new ConcurrentDictionary<string, TcpClient>();  
  20.     }  
  21.  
  22.     /// <summary>  
  23.     /// 監(jiān)聽的端口  
  24.     /// </summary>  
  25.     public int Port { getprivate set; }  
  26.  
  27.     /// <summary>  
  28.     /// 分隔符  
  29.     /// </summary>  
  30.     public string Boundary { getprivate set; }  
  31.  
  32.     /// <summary>  
  33.     /// 流頭部  
  34.     /// </summary>  
  35.     public string StreamHeader  
  36.     {  
  37.       get 
  38.       {  
  39.         return "HTTP/1.1 200 OK" +  
  40.                "\r\n" +  
  41.                "Content-Type: multipart/x-mixed-replace; boundary=" + this.Boundary +  
  42.                "\r\n";  
  43.       }  
  44.     }  
  45.  
  46.     /// <summary>  
  47.     /// 圖片頭部  
  48.     /// </summary>  
  49.     public string PayloadHeader  
  50.     {  
  51.       get 
  52.       {  
  53.         return "\r\n" +  
  54.                this.Boundary +  
  55.                "\r\n" +  
  56.                "Content-Type: image/jpeg" +  
  57.                "\r\n" +  
  58.                "Content-Length: " + _contentLengthString +  
  59.                "\r\n\r\n";  
  60.       }  
  61.     }  
  62.  
  63.     public void Start()  
  64.     {  
  65.       _server.Start(10);  
  66.       _server.ClientConnected += new EventHandler<TcpClientConnectedEventArgs>(OnClientConnected);  
  67.       _server.ClientDisconnected += new EventHandler<TcpClientDisconnectedEventArgs>(OnClientDisconnected);  
  68.     }  
  69.  
  70.     public void Stop()  
  71.     {  
  72.       _server.Stop();  
  73.       _server.ClientConnected -= new EventHandler<TcpClientConnectedEventArgs>(OnClientConnected);  
  74.       _server.ClientDisconnected -= new EventHandler<TcpClientDisconnectedEventArgs>(OnClientDisconnected);  
  75.     }  
  76.  
  77.     private void OnClientConnected(object sender, TcpClientConnectedEventArgs e)  
  78.     {  
  79.       _clients.AddOrUpdate(e.TcpClient.Client.RemoteEndPoint.ToString(), e.TcpClient, (n, o) => { return e.TcpClient; });  
  80.     }  
  81.  
  82.     private void OnClientDisconnected(object sender, TcpClientDisconnectedEventArgs e)  
  83.     {  
  84.       TcpClient clientToBeThrowAway;  
  85.       _clients.TryRemove(e.TcpClient.Client.RemoteEndPoint.ToString(), out clientToBeThrowAway);  
  86.     }  
  87.  
  88.     public void Write(Image image)  
  89.     {  
  90.       if (_server.IsRunning)  
  91.       {  
  92.         byte[] payload = BytesOf(image);  
  93.  
  94.         WriteStreamHeader();  
  95.         WritePayload(payload);  
  96.       }  
  97.     }  
  98.  
  99.     private void WriteStreamHeader()  
  100.     {  
  101.       if (_clients.Count > 0)  
  102.       {  
  103.         foreach (var item in _clients)  
  104.         {  
  105.           Logger.Debug(string.Format(CultureInfo.InvariantCulture,  
  106.             "Writing stream header, {0}, {1}{2}", item.Key, Environment.NewLine, StreamHeader));  
  107.  
  108.           _server.SyncSend(item.Value, StreamHeader);  
  109.  
  110.           TcpClient clientToBeThrowAway;  
  111.           _clients.TryRemove(item.Key, out clientToBeThrowAway);  
  112.         }  
  113.       }  
  114.     }  
  115.  
  116.     private void WritePayload(byte[] payload)  
  117.     {  
  118.       string payloadHeader = this.PayloadHeader.Replace(_contentLengthString, payload.Length.ToString());  
  119.       string payloadTail = "\r\n";  
  120.  
  121.       Logger.Debug(string.Format(CultureInfo.InvariantCulture,  
  122.         "Writing payload header, {0}{1}", Environment.NewLine, payloadHeader));  
  123.  
  124.       byte[] payloadHeaderBytes = _server.Encoding.GetBytes(payloadHeader);  
  125.       byte[] payloadTailBytes = _server.Encoding.GetBytes(payloadTail);  
  126.       byte[] packet = new byte[payloadHeaderBytes.Length + payload.Length + payloadTail.Length];  
  127.       Buffer.BlockCopy(payloadHeaderBytes, 0, packet, 0, payloadHeaderBytes.Length);  
  128.       Buffer.BlockCopy(payload, 0, packet, payloadHeaderBytes.Length, payload.Length);  
  129.       Buffer.BlockCopy(payloadTailBytes, 0, packet, payloadHeaderBytes.Length + payload.Length, payloadTailBytes.Length);  
  130.  
  131.       _server.SendToAll(packet);  
  132.     }  
  133.  
  134.     private byte[] BytesOf(Image image)  
  135.     {  
  136.       MemoryStream ms = new MemoryStream();  
  137.       image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);  
  138.  
  139.       byte[] payload = ms.ToArray();  
  140.  
  141.       return payload;  
  142.     }  
  143.   } 

原文鏈接:http://www.cnblogs.com/gaochundong/p/csharp_mjpeg_streaming.html

責(zé)任編輯:林師授 來(lái)源: 博客園
相關(guān)推薦

2009-08-26 14:35:00

用C#實(shí)現(xiàn)HTTP協(xié)議

2009-08-21 14:33:15

C#異步傳輸字符串

2009-08-21 14:25:23

C#異步傳輸字符串

2009-08-21 16:37:54

C#客戶端程序

2011-05-19 11:20:08

2009-08-21 16:27:44

C#服務(wù)端程序

2016-12-28 17:45:30

Hadoop大數(shù)據(jù)Kafka

2009-08-19 17:00:07

C#實(shí)現(xiàn)PrintPa

2009-08-20 14:22:17

C#實(shí)現(xiàn) Contro

2009-08-25 17:55:52

C#實(shí)現(xiàn)Strateg

2009-08-31 15:55:17

C#實(shí)現(xiàn)Strateg

2009-09-01 18:29:10

C#繼承C#多態(tài)

2009-08-26 09:54:45

C#打印預(yù)覽C#打印

2021-04-19 12:31:04

太坊數(shù)據(jù)QuestDB

2009-08-24 10:06:31

C#接口成員

2009-08-19 14:29:33

C#代理

2009-09-04 13:22:31

C#實(shí)現(xiàn)多個(gè)接口

2009-09-01 18:29:24

C#實(shí)現(xiàn)多個(gè)接口

2009-08-18 17:51:17

C#實(shí)現(xiàn)Interne

2009-08-12 13:11:24

C#實(shí)現(xiàn)遠(yuǎn)程線程插入
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 免费黄色片视频 | 亚洲精品免费在线 | www.久久久.com| 在线观看视频91 | 一区二区三区视频在线 | 人人看人人干 | 中文字幕一区二区三区四区五区 | 91国自产 | 午夜小视频免费观看 | 日韩欧美在线视频 | 国产高清视频在线观看播放 | 久久精品一二三影院 | 成人影院网站ww555久久精品 | 亚洲一区二区三区四区五区午夜 | 日韩在线观看一区二区三区 | 一级片免费视频 | 成人二区| 无码国模国产在线观看 | 久久国产精品一区二区 | 亚洲精品大片 | 91精品中文字幕一区二区三区 | 亚洲黄色在线免费观看 | 亚洲人成人一区二区在线观看 | 亚洲欧美在线一区 | 亚洲精品一区二区网址 | 亚洲精品久久久 | 国产精品成人69xxx免费视频 | 毛片免费观看 | 亚洲精品永久免费 | 久草网址 | 久久精品超碰 | 日韩欧美在线视频 | 久久精品99久久 | 国产免费一区二区三区免费视频 | 黄色一级大片在线免费看产 | 亚洲欧美激情精品一区二区 | 国产午夜精品久久久久 | 日韩在线观看中文字幕 | 日韩精品av一区二区三区 | 欧美日韩在线观看一区 | 99久久久99久久国产片鸭王 |