C# wpf客户端接收解析mjpg-streamer视频流

最近用wpf做一个树莓派机器人的综合控制端,需要解析机器人摄像头的视频流,树莓派是用mjpg-streamer调用并搭建了视频流服务。

客户端解析mjpg-streamer视频帧的原理是:建立Http长连接,每次接收1024长度的数据,数据流中包含数据头信息和紧跟在数据头信息后的图像帧数据,所以需要先定位数据头信息,头信息中包含图像帧数据的长度,然后从头信息的结尾开始解析图像帧数据,第一次读取的数据中不一定包含完整的图像帧数据,需要从第二次读取的数据中继续解析未完整的数据。

原理参考:https://blog.csdn.net/qingkongyeyue/article/details/52824165

以下为具体代码:

    private static readonly string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
   private byte[] HeaderStartBuffer = new byte[20]
        {
            45, 45, 98, 111, 117, 110, 100, 97, 114, 121,
          100, 111, 110, 111, 116, 99, 114, 111, 115, 115
        };
    private byte[] HeaderEndBuffer = new byte[4] { 13, 10, 13, 10 };
    public void GetLivingVedio(string requestUrl, Action<byte[]> setFrame)
        {
            Task.Run(delegate
            {
                if (string.IsNullOrEmpty(requestUrl))
                {
                    MessageBox.Show("GetLivingVedio url 为空!");
                    return;
                }

                try
                {
                    HttpWebRequest request = WebRequest.Create(requestUrl) as HttpWebRequest; ;

                    //在使用curl做POST的时候, 当要POST的数据大于1024字节的时候, curl并不会直接就发起POST请求, 而是会分为俩步,
                    //1.发送一个请求, 包含一个Expect: 100 -continue, 询问Server使用愿意接受数据
                    //2.接收到Server返回的100 - continue应答以后, 才把数据POST给Server
                    //并不是所有的Server都会正确应答100 -continue, 比如lighttpd, 就会返回417 “Expectation Failed”, 则会造成逻辑出错,,
                    request.ServicePoint.Expect100Continue = false;
                    //默认代理
                    request.UserAgent = DefaultUserAgent;
                    //ContentType
                    //request.ContentType = "application/json;charset=utf-8";
                    request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8";
                    request.KeepAlive = true;
                    request.Method = "Get";

                    request.Timeout = (int)(30 * 1000f); // to ensure  no timeout

                    using (Stream stream = request.GetResponse().GetResponseStream())
                    {
                        BinaryReader reader = new BinaryReader(new BufferedStream(stream), new System.Text.ASCIIEncoding());

                        int headerStart = 0;
                        int frameStart = 0;
                        int frameLength = 0;

                        byte[] frameBuffer = null;
                        int remainDataInNext = 0;

                        while (!StopLivingVedio)
                        {
                            try
                            {
                                byte[] buffer = reader.ReadBytes(1024);

                                if (remainDataInNext > 0)
                                {
                                    int locationBuffStart = frameBuffer.Length - remainDataInNext;
                                    if (buffer.Length > remainDataInNext)
                                    {
                                        Array.Copy(buffer, 0, frameBuffer, locationBuffStart, remainDataInNext);
                                    }
                                    else
                                    {
                                        Array.Copy(buffer, 0, frameBuffer, locationBuffStart, buffer.Length);
                                    }

                                    remainDataInNext = remainDataInNext - buffer.Length < 0 ? 0 : remainDataInNext - buffer.Length;

                                    if (remainDataInNext == 0)
                                    {
                                        //Console.WriteLine("frame is ok! {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
                                        if (setFrame != null)
                                            setFrame(frameBuffer);
                                    }
                                }
                                else if (LocateHeader(buffer, ref headerStart, ref frameStart, ref frameLength))
                                {
                                    frameBuffer = new byte[frameLength];
                                    Array.Copy(buffer, frameStart, frameBuffer, 0, buffer.Length - frameStart);
                                    remainDataInNext = frameLength - (buffer.Length - frameStart);
                                }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine("GetLivingVedio's error:{0}", ex);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("GetLivingVedio's error:{0}", ex.ToString());
                }

                Console.WriteLine("exit real time preview");
            });
        }

        /// <summary>
        /// 定位buffer中的头信息
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="headerstart"></param>
        /// <param name="framestart"></param>
        /// <param name="framelength"></param>
        /// <returns></returns>
        public bool LocateHeader(byte[] buffer, ref int headerstart, ref int framestart, ref int framelength)
        {
            bool searchHeaderStartResult = false;
            bool searchHeaderEndResult = false;
            if (buffer != null && buffer.Length > 0)
            {
                for (int i = 0; i < buffer.Length; i++)
                {
                    //比较消息头
                    for (int j = 0; j < this.HeaderStartBuffer.Length; j++)
                    {
                        //相等时继续比较,不跳出循环
                        if ((i + j) < buffer.Length && buffer[i + j] == this.HeaderStartBuffer[j])
                        {
                            if (j == 19)
                            {
                                headerstart = i;
                                searchHeaderStartResult = true;
                                i += 20;
                            }
                        }
                        else
                        {
                            break;
                        }
                    }

                    //比较消息结尾
                    for (int j = 0; j < this.HeaderEndBuffer.Length; j++)
                    {
                        //相等时继续比较,不跳出循环
                        if ((i + j) < buffer.Length && buffer[i + j] == this.HeaderEndBuffer[j])
                        {
                            if (j == 3)
                            {
                                framestart = i + 4;
                                searchHeaderEndResult = true;
                            }
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (searchHeaderStartResult && searchHeaderEndResult)
                    {
                        if (headerstart < framestart)
                        {
                            byte[] headerBuffer = new byte[framestart - headerstart];
                            Array.Copy(buffer, headerstart, headerBuffer, 0, headerBuffer.Length);
                            string headerString = Encoding.UTF8.GetString(headerBuffer);
                            int lengthStartIndex = headerString.IndexOf("Content-Length:") + 15;
                            int lengthEndIndex = headerString.IndexOf("X-Timestamp");
                            string lengthString = headerString.Substring(lengthStartIndex, lengthEndIndex - lengthStartIndex);
                            framelength = int.Parse(lengthString.Trim());
                        }
                        break;
                    }
                }
            }
            return searchHeaderStartResult && searchHeaderEndResult;
        }

wpf调用:

     private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            string videoUrl = "http://192.168.1.1:8080/?action=stream";
            this.ViewModel.GetLivingVedio(videoUrl, displayVedioFrame);
        }

        private void displayVedioFrame(byte[] frameBuffer)
        {
            this.Dispatcher.Invoke(delegate
            {
                BitmapImage bmp = null;
                try
                {
                    bmp = new BitmapImage();
                    bmp.BeginInit();
                    bmp.StreamSource = new MemoryStream(frameBuffer);
                    bmp.EndInit();
                    imgLiving.Source = bmp;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("displayVedioFrame's error:{0}", ex);
                }
            });
        }


 

猜你喜欢

转载自blog.csdn.net/jkffklejk/article/details/82950316