Bootstrap

(七)WPFC# 视频监控画面的传输:使用Aforge类库打开USB摄像头循环采集每一帧图像给Socket传输,并解决粘包、拆包问题

一、问题描述

     我用Aforge类库打开了本地的摄像头,可以在PC看到实时画面。但是,我想把该实时画面通过局域网络,传输给另一台PC。Aforge类库或Socket的Demo,请参考我的博客。

Aforge类库 Demo(建议你用Aforge类库,因为它是功能最强大的,包含了人工智能算法、图像处理算法等等功能):

(六)WPF/C# 使用Aforge类库打开USB摄像头:实现拍照/重拍功能。

Socket Demo:

https://blog.csdn.net/xpj8888/article/category/8321616等系列。

二、别人的实时视频监控画面方案

1、OpenCV结合socket进行实时视频传输(OpenCV + TCP协议 + Socket +  C++实现)

https://blog.csdn.net/neal1991/article/details/45009229

https://blog.csdn.net/u012736685/article/details/77131633(实时图片传输、再现)

https://www.cnblogs.com/ryhan/p/4470107.html

https://blog.csdn.net/pengz0807/article/details/52204134

2、android 手机采集摄像头视频 socket 视频传输 实时传播

以下两个参考方法都是传输视频画面(图片帧的方式),没有传输语音。

https://blog.csdn.net/a289973483/article/details/52790217

https://www.cnblogs.com/lijiongquan/p/4729445.html

3、Socket+视频流

(重点)https://blog.csdn.net/ilipan/article/details/44982807(图片帧的形式)

https://blog.csdn.net/u011426247/article/details/80551749

https://blog.csdn.net/theArcticOcean/article/details/52658748

https://download.csdn.net/download/xmcy001122/4914401(图片帧)

基于superSocket——AForge的UDP摄像传输视频案例-CSDN下载

MyCamMonitorServer AForge 摄像头监控,开启Socket监听发送 到的...(联合开发网)

三、我的实现方法

1、技术点    

1.1、肉眼分辨率

        所谓的帧就是一幅完整的图象,一般人眼看视频时有24帧/秒就是流畅的了;所以,我们在一秒内,传输24章图像即可,然后在接收端把图片重现即可。

     https://bbs.csdn.net/topics/392357720?list=lz

1.2、视频压缩

       当然,你可以把图片编码后解码(即压缩),从而满足在一定的宽度速率下传输。请参考:https://zhidao.baidu.com/question/473219109.html

2、发送端(是在Aforge类库上修改的,该类库已经存放到GitHub上)

MainWindow.xaml:

<Window x:Class="AForge.Wpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:AForge.Wpf"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="422.669"
        Height="350"
        mc:Ignorable="d">
    <Grid>
        <Image x:Name="videoPlayer" Margin="10,0,10,70" />
        <ComboBox x:Name="comboBox"
                  Width="167"
                  Height="21"
                  Margin="10,0,0,25"
                  HorizontalAlignment="Left"
                  VerticalAlignment="Bottom"
                  DisplayMemberPath="Name"
                  ItemsSource="{Binding VideoDevices}"
                  SelectedItem="{Binding CurrentDevice}" />
        <Label Width="167"
               Height="24"
               Margin="10,0,0,46"
               HorizontalAlignment="Left"
               VerticalAlignment="Bottom"
               Content="Select video source" />
        <Button Width="67"
                Height="21"
                Margin="213,0,0,25"
                HorizontalAlignment="Left"
                VerticalAlignment="Bottom"
                Click="btnStart_Click"
                Content="Start" Background="Red" />
        <Button Width="67"
                Height="21"
                Margin="296,0,0,25"
                HorizontalAlignment="Left"
                VerticalAlignment="Bottom"
                Click="btnStop_Click"
                Content="Stop" />
    </Grid>
</Window>

MainWindow.xaml.cs: 

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using AForge.Video;
using AForge.Video.DirectShow;
using System.Net.Sockets;
using System.Net;
using System.IO;

namespace AForge.Wpf
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {

        #region Public properties

        public ObservableCollection<FilterInfo> VideoDevices { get; set; }

        public FilterInfo CurrentDevice
        {
            get { return _currentDevice; }
            set { _currentDevice = value; this.OnPropertyChanged("CurrentDevice"); }
        }
        private FilterInfo _currentDevice;

        #endregion


        #region Private fields

        private IVideoSource _videoSource;
        

        #endregion
        Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        Socket socketWatch2;
        public MainWindow()
        {
           
            string strSocketStartFlag = "1";
           
            string strSocketIP = "192.168.xxx.xx";//无线网
            int point = 50000;
            if (strSocketStartFlag == "1")
            {

                IPAddress ip = IPAddress.Parse(strSocketIP);
                IPEndPoint iPEndPoint = new IPEndPoint(ip, point);
                socketWatch.Bind(iPEndPoint);
                socketWatch.Listen(10);
                MessageBox.Show("监听成功");
                //开启线程监听
                Thread th = new Thread(Listen);
                th.IsBackground = true;
                th.Start(socketWatch);
            }

            //InitializeComponent();
            this.DataContext = this;
            GetVideoDevices();
            this.Closing += MainWindow_Closing;
        }

        Socket Server;
        //将远程连接的客户端的IP地址和Socket存入集合中
        Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();
        string Receive_IPandPort = null;
        void Listen(object o)   
        {
            socketWatch2 = o as Socket;
            //等待客户端的连接,并且创建一个负责通信的Socket
            while (true)
            {
                try
                {
                    //负责跟客户端通信的Socket
                    Server = socketWatch2.Accept();
                    //将远程连接的客户端的IP地址和Socket存入集合中
                    dicSocket.Add(Server.RemoteEndPoint.ToString(), Server);
                    //将远程连接的客户端的IP地址和端口号存储下拉框中
                    //cboUsers.Items.Add(Server.RemoteEndPoint.ToString());
                    //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate2(cboUsers.Items.Add), Server.RemoteEndPoint.ToString());
                    Receive_IPandPort = Server.RemoteEndPoint.ToString();
                    //192.168.0.100:连接成功
                    //Dispatcher.BeginInvoke(DispatcherPriority.Normal, new delegate1(ShowMsg), Server.RemoteEndPoint.ToString() + ":" + "连接成功");
                    //开启一个新的线程,不停地接收客户端发送过来的消息
                    //Thread th = new Thread(Receive);
                    //th.IsBackground = true;
                    //th.Start(Server);
                }
                catch { }
            }
        }

        public void receiveFunc(object obj)
        {
            Socket Server = obj as Socket;
            int ithread1 = Thread.CurrentThread.ManagedThreadId;
            while (true)
            {
                try
                {
                    byte[] buffer = new byte[1024 * 1024 * 3];
                    //实际接收到的有效字节数
                    //int r = mainWindow.Server.Receive(buffer);
                    int r = Server.Receive(buffer);
                    if (r == 0)//未收到任何东西
                    {
                        break;
                    }
                    //第一个字节表示接收的是文字消息
                    else
                    {
                   
                    }
                }
                catch
                {
                    //MessageBox.Show("你接收的是非法文件或非法消息");
                }
            }
            //return server1;
        }





        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            StopCamera();
        }

        /// <summary>
        /// 打开摄像头
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            StartCamera();
        }

        private void video_NewFrame(object sender, Video.NewFrameEventArgs eventArgs)
        {
            try
            {
                BitmapImage bi;
                using (var bitmap = (Bitmap)eventArgs.Frame.Clone())
                {
                    bi = bitmap.ToBitmapImage();
                    //将BitmapImage转成字节
                    Stream stream = bi.StreamSource;
                    if (stream != null && stream.Length > 0)
                    {
                        stream.Position = 0;//必须要。
                        BinaryReader br = new BinaryReader(stream);

                        //内容
                        byte[] bytVideoContext = br.ReadBytes((int)stream.Length);
                        //标志位
                        byte[] bytVideoSurveillanceSendFlag = new byte[2] { 0x03, 0 };
                        byte[] bytSum = new byte[2 + 921654];
                        Array.ConstrainedCopy(bytVideoSurveillanceSendFlag, 0, bytSum, 0, bytVideoSurveillanceSendFlag.Length);
                        Array.ConstrainedCopy(bytVideoContext, 0, bytSum, bytVideoSurveillanceSendFlag.Length, bytVideoContext.Length);

                        dicSocket[Receive_IPandPort].Send(bytSum);
                
                    }
                }     
                bi.Freeze(); // avoid cross thread operations and prevents leaks
                Dispatcher.BeginInvoke(new ThreadStart(delegate { videoPlayer.Source = bi; }));
            }
            catch (Exception exc)
            {
                MessageBox.Show("Error on _videoSource_NewFrame:\n" + exc.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                StopCamera();
            }
        }

        /// <summary>
        /// 关闭摄像头
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, RoutedEventArgs e)
        {
            StopCamera();
        }

        private void GetVideoDevices()
        {
            VideoDevices = new ObservableCollection<FilterInfo>();
            foreach (FilterInfo filterInfo in new FilterInfoCollection(FilterCategory.VideoInputDevice))
            {
                VideoDevices.Add(filterInfo);
            }
            if (VideoDevices.Any())
            {
                CurrentDevice = VideoDevices[0];
            }
            else
            {
                MessageBox.Show("No video sources found", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void StartCamera()
        {
            if (CurrentDevice != null)
            {
                _videoSource = new VideoCaptureDevice(CurrentDevice.MonikerString);
                _videoSource.NewFrame += video_NewFrame;
                _videoSource.Start();
            }
        }

        private void StopCamera()
        {
            if (_videoSource != null && _videoSource.IsRunning)
            {
                _videoSource.SignalToStop();
                _videoSource.NewFrame -= new NewFrameEventHandler(video_NewFrame);
            }
        }

        #region INotifyPropertyChanged members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        } 

        #endregion
    }
}

BitmapHelpers.cs:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace AForge.Wpf
{
    static class BitmapHelpers
    {
        public static BitmapImage ToBitmapImage(this Bitmap bitmap)
        {
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, ImageFormat.Bmp);
            ms.Seek(0, SeekOrigin.Begin);
            bi.StreamSource = ms;
            bi.EndInit();
            return bi;
        }
    }
}

3、接收端(解决了粘包和拆包问题)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Threading;
using KeenRayLargePC.Views;
using KeenRayLargePC.PublicFunc;
using System.Windows.Interop;
using System.Xml.Linq;
using System.Drawing.Imaging;
using System.Drawing;
using KeenRayLargePC.Common;

namespace KeenRayLargePC
{
    public partial class MainWindow : Window 
    {
        #region 标志位

        //标志位长度
        const int FLAG_LENGTH               = 2;
        //加载页面标志
        string pageTypeFlag                 = null;       
        //启动页面标志
        static byte[] btStudyPageRecFlag    = new byte[FLAG_LENGTH] { 0x00, 0 };
        static byte[] btReviewPageRecFlag   = new byte[FLAG_LENGTH] { 0x01, 0 };
        static byte[] btHistoryPageRecFlag  = new byte[FLAG_LENGTH] { 0x02, 0 };
        //启动图片接收、字符接收标志      
        static byte[] btImageRecFlag        = new byte[FLAG_LENGTH] { 0x04, 0 };
        static byte[] btStringRecFlag       = new byte[FLAG_LENGTH] { 0x00, 0 };
        //启动用户、列表、曝光标志
        static byte[] btUserRecFlag         = new byte[FLAG_LENGTH] { 0x00, 0 };//这里出现的0x00标志与前面的0x00没有联系。
        static byte[] btListViewRecFlag     = new byte[FLAG_LENGTH] { 0x08, 0 };
        static byte[] btExposeRecFlag       = new byte[FLAG_LENGTH] { 0x10, 0 };
        //启动KV、mA、ms、mAs标志
        static byte[] btExposeKVRecFlag     = new byte[FLAG_LENGTH] { 0x00, 0 };
        static byte[] btExposemARecFlag     = new byte[FLAG_LENGTH] { 0x20, 0 };
        static byte[] btExposemsRecFlag     = new byte[FLAG_LENGTH] { 0x40, 0 };
        static byte[] btExposemAsRecFlag    = new byte[FLAG_LENGTH] { 0x60, 0 };
        //启动 Plus/Reduce标志
        static byte[] btExposePlusRecFlag   = new byte[FLAG_LENGTH] { 0x00, 0 };
        static byte[] btExposeReduceRecFlag = new byte[FLAG_LENGTH] { 0x80, 0 };

        //将字节转整数
        Int16 iStudyPageRecFlag             = System.BitConverter.ToInt16(btStudyPageRecFlag, 0);
        Int16 iReviewPageRecFlag            = System.BitConverter.ToInt16(btReviewPageRecFlag, 0);
        Int16 iHistoryPageRecFlag           = System.BitConverter.ToInt16(btHistoryPageRecFlag, 0);
        Int16 iStringRecFlag                = System.BitConverter.ToInt16(btStringRecFlag, 0);
        Int16 iImageRecFlag                 = System.BitConverter.ToInt16(btImageRecFlag, 0);
        Int16 iUserRecFlag                  = System.BitConverter.ToInt16(btUserRecFlag, 0);
        Int16 iListViewRecFlag              = System.BitConverter.ToInt16(btListViewRecFlag, 0);
        Int16 iExposeRecFlag                = System.BitConverter.ToInt16(btExposeRecFlag, 0);
        Int16 iExposeKVRecFlag              = System.BitConverter.ToInt16(btExposeKVRecFlag, 0);
        Int16 iExposemARecFlag              = System.BitConverter.ToInt16(btExposemARecFlag, 0);
        Int16 iExposemsRecFlag              = System.BitConverter.ToInt16(btExposemsRecFlag, 0);
        Int16 iExposemAsRecFlag             = System.BitConverter.ToInt16(btExposemAsRecFlag, 0);
        Int16 iExposePlusRecFlag            = System.BitConverter.ToInt16(btExposePlusRecFlag, 0);
        Int16 iExposeReduceRecFlag          = System.BitConverter.ToInt16(btExposeReduceRecFlag, 0);

        #endregion

        #region 字段

        StudyPage studyPage = new StudyPage();
        ReviewPage reviewPage = new ReviewPage();
        HistoryPage historyPage = new HistoryPage();
        ExposeValueMsgFunc exposeValueMsgFunc = new ExposeValueMsgFunc();
        ImageFunc imageFunc = new ImageFunc();
        ListViewMsgFunc listViewMsgFunc = new ListViewMsgFunc();
        UserMsgFunc userMsgFunc = new UserMsgFunc();
        Log log = new Log();//日志类
        Config config = new Config();

        #endregion

        #region 视频监控

        int iCameraFlag = 1;                                        //视频监控开关状态。0:关闭。1:打开。
        List<byte> VideoList = new List<byte>();                    //存放视频帧
        const int BITMAP_IMAGE_LENGTH = 921654;                     //帧字节长度
        byte[] btCurrentBitmapImage = new byte[BITMAP_IMAGE_LENGTH];//当前帧
        byte[] btLastBitmapImage    = new byte[BITMAP_IMAGE_LENGTH];//上一帧

        #endregion

        #region socket 初始化 

        byte[] btRecBuffer = new byte[1000 * 1001];//socket接收缓存区
        int iRecBuffeActualByteCount = 0;          //socket实际接收到的有效字节数
        Socket Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        public Socket ClientVar
        {
            get { return Client; }
        }

        #endregion

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            //清空日志
            log.ClearLogFunc();                               
            //配置IP
            string strIPconfigFilePath = Environment.CurrentDirectory + "\\IPconfig.xml";            
            IPAddress serverIP = IPAddress.Parse(config.IpConfigFunc(strIPconfigFilePath));
           
            IPEndPoint serverIPEndPoint = new IPEndPoint(serverIP, 50000);
            获得要连接的远程服务器应用程序的IP地址和端口号
            //Client.Connect(serverIPEndPoint);
            //MessageBox.Show("连接服务器状态:成功");
            //Thread threadReceive = new Thread(Receive);
            将threadReceive设置为后台线程
            //threadReceive.IsBackground = true;
            启动线程
            //threadReceive.Start();
        }
     
        /// <summary>
       /// 接收函数
       /// </summary>
        private void Receive()
        {
            while (true)
            {
                try
                {
                    iRecBuffeActualByteCount = Client.Receive(btRecBuffer);
                    if (iRecBuffeActualByteCount == 0)//未收到任何东西
                    {
                        break;
                    }
                    else //有收到信息
                    {
                        //拷贝,找出butter的标志位。
                        byte[] btRecFlag = new byte[2];
                        Array.ConstrainedCopy(btRecBuffer, 0, btRecFlag, 0, btRecFlag.Length);
                        //转成整数,以便进行与操作。
                        Int16 iRecFlag = System.BitConverter.ToInt16(btRecFlag, 0);
                        //因为视频监控是不连续的帧,所以每次接收的btRecBuffer数据不一定有标志位。因此,不能根据每组btRecBuffer数据来判断是否启动视频监控
                        if (iCameraFlag == 1)
                        {
                            //默认打开视频监控画面
                            App.Current.Dispatcher.Invoke((Action)delegate ()
                            {
                                Panel.SetZIndex(imgVideoSurveillance, 2);
                            });
                            LoadVideoSurveillanceFunc(iRecBuffeActualByteCount, btRecBuffer);
                        }
                        //关闭视频监控,打开其他页面。
                        else if (iCameraFlag == 0)
                        {
                            //前两位为00,则启动StudyPage。iRecFlag & 0x03,表示提取字节的前两位。
                            if ((iRecFlag & 0x03) == iStudyPageRecFlag)
                            {
                                LoadStudyPageFunc(iRecFlag);
                            }
                            //前两位为01,则启动ReviewPage
                            else if ((iRecFlag & 0x03) == iReviewPageRecFlag)
                            {
                                LoadReviewPageFunc(iRecFlag);
                            }
                            //前两位为10,则启动HistoryPage
                            else if ((iRecFlag & 0x03) == iHistoryPageRecFlag)
                            {
                                LoadHistoryPageFunc(iRecFlag);
                            }
                        }
                    }
                }
                catch(Exception ex)
                {
                    MessageBox.Show("连接不上");
                    log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::Receive()——" + "Socket接收发送错误:" + ex.ToString() + "\n\n");
                }
            }
        }
    
        /// <summary>
        /// 加载检查页
        /// </summary>
        /// <param name="iRecFlag"></param>
        private void LoadStudyPageFunc(int iRecFlag)
        {
            try
            {
                pageTypeFlag = "studyPage";
                //加载页面
                App.Current.Dispatcher.Invoke((Action)delegate ()
                {
                    Frame.Content = studyPage;
                });  
                   
                #region 加载非图片
                //iRecFlag & 0x04,表示提取字节的第三位。
                if ((iRecFlag & 0x04) == iStringRecFlag)//启动文字信息。
                {
                    if ((iRecFlag & 0x18) == iUserRecFlag)//启动用户信息
                    {
                        userMsgFunc.GetUserMsgFunc(iRecBuffeActualByteCount, btRecBuffer, studyPage, reviewPage, pageTypeFlag);
                    }
                    else if ((iRecFlag & 0x18) == iListViewRecFlag)//启动列表信息
                    {
                        listViewMsgFunc.GetListViewMsgFunc(iRecBuffeActualByteCount, btRecBuffer, studyPage, historyPage, pageTypeFlag);
                    }
                    else if ((iRecFlag & 0x18) == iExposeRecFlag)//启动曝光信息
                    {
                        string strVar = exposeValueMsgFunc.GetExposeValueMsgFunc(iRecBuffeActualByteCount, btRecBuffer);
                        if ((iRecFlag & 0x60) == iExposeKVRecFlag)//启动KV
                        {
                            if ((iRecFlag & 0x80) == iExposePlusRecFlag)//加
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    studyPage.lbl_KVValue.Content = strVar;
                                });
                            }
                            else if ((iRecFlag & 0x80) == iExposeReduceRecFlag)//减
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    studyPage.lbl_KVValue.Content = strVar;
                                });
                            }
                        }
                        else if ((iRecFlag & 0x60) == iExposemARecFlag)//启动mA
                        {
                            if ((iRecFlag & 0x80) == iExposePlusRecFlag)//加
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    //studyPage.lbl_mAValue.Content = exposeValueMsgFunc.GetExposeValueMsgFunc(iRecBuffeActualByteCount, btRecBuffer);
                                    studyPage.lbl_mAValue.Content = strVar;
                                });
                            }
                            else if ((iRecFlag & 0x80) == iExposeReduceRecFlag)//减
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    //studyPage.lbl_mAValue.Content = exposeValueMsgFunc.GetExposeValueMsgFunc(iRecBuffeActualByteCount, btRecBuffer);
                                    studyPage.lbl_mAValue.Content = strVar;
                                });
                            }
                        }
                        else if ((iRecFlag & 0x60) == iExposemsRecFlag)//启动ms
                        {
                            if ((iRecFlag & 0x80) == iExposePlusRecFlag)//加
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    //studyPage.lbl_msValue.Content = exposeValueMsgFunc.GetExposeValueMsgFunc(iRecBuffeActualByteCount, btRecBuffer);
                                    studyPage.lbl_msValue.Content = strVar;
                                });
                            }
                            else if ((iRecFlag & 0x80) == iExposeReduceRecFlag)//减
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    //studyPage.lbl_msValue.Content = exposeValueMsgFunc.GetExposeValueMsgFunc(iRecBuffeActualByteCount, btRecBuffer);
                                    studyPage.lbl_msValue.Content = strVar;
                                });
                            }
                        }
                        else if ((iRecFlag & 0x60) == iExposemAsRecFlag)//启动mAs
                        {
                            if ((iRecFlag & 0x80) == iExposePlusRecFlag)//加
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    //studyPage.lbl_mAsValue.Content = exposeValueMsgFunc.GetExposeValueMsgFunc(iRecBuffeActualByteCount, btRecBuffer);
                                    studyPage.lbl_mAsValue.Content = strVar;

                                });
                            }
                            else if ((iRecFlag & 0x80) == iExposeReduceRecFlag)//减
                            {
                                App.Current.Dispatcher.Invoke((Action)delegate ()
                                {
                                    //studyPage.lbl_mAsValue.Content = exposeValueMsgFunc.GetExposeValueMsgFunc(iRecBuffeActualByteCount, btRecBuffer);
                                    studyPage.lbl_mAsValue.Content = strVar;
                                });
                            }
                        }

                    }
                }
                #endregion

                #region 加载图片
                else if ((iRecFlag & 0x04) == iImageRecFlag)//启动图片信息
                {
                    App.Current.Dispatcher.Invoke((Action)delegate ()
                    {
                        imageFunc.GetImageFunc(iRecBuffeActualByteCount, btRecBuffer, studyPage, reviewPage, pageTypeFlag);
                    });
                }
                #endregion
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::LoadStudyPageFunc()——" + "加载检查页发送错误:" + ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 加载阅片页
        /// </summary>
        /// <param name="iRecFlag"></param>
        private void LoadReviewPageFunc(int iRecFlag)
        {
            try
            {
                pageTypeFlag = "reviewPage";
                App.Current.Dispatcher.Invoke((Action)delegate ()
                {
                    Frame.Content = reviewPage;
                });
                //iRecFlag & 0x04,表示提取字节的第三位。
                if ((iRecFlag & 0x04) == iStringRecFlag)//启动文字-用户信息。
                {
                    userMsgFunc.GetUserMsgFunc(iRecBuffeActualByteCount, btRecBuffer, studyPage, reviewPage, pageTypeFlag);
                }
                else if ((iRecFlag & 0x04) == iImageRecFlag)//启动图片信息
                {
                    imageFunc.GetImageFunc(iRecBuffeActualByteCount, btRecBuffer, studyPage, reviewPage, pageTypeFlag);
                }
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::LoadReviewPageFunc()——" + "加载阅片页发送错误:" + ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 加载历史页
        /// </summary>
        /// <param name="iRecFlag"></param>
        private void LoadHistoryPageFunc(int iRecFlag)
        {
            try
            {
     
                pageTypeFlag = "HistoryPage";
                App.Current.Dispatcher.Invoke((Action)delegate ()
                {
                    Frame.Content = historyPage;
                });
                //iRecFlag & 0x04,表示提取字节的第三位。
                if ((iRecFlag & 0x04) == iStringRecFlag)//启动文字信息。
                {
                    if ((iRecFlag & 0x18) == iListViewRecFlag)//启动列表信息
                    {
                        //listViewMsgFunc.GetListViewMsgFunc(iRecBuffeActualByteCount, btRecBuffer, studyPage, historyPage, pageTypeFlag);

                    }
                }
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::LoadHistoryPageFunc()——" + "加载历史页发送错误:" + ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 加载视频监控画面
        /// </summary>
        /// <param name="iRecFlag"></param>
        private void LoadVideoSurveillanceFunc(int iRecBuffeActualByteCount, byte[] btRecBuffer)
        {
            try
            {
                //将缓存器的有效字节拿出来
                byte[] btRecBufferVideoContent = new byte[iRecBuffeActualByteCount];
                Array.ConstrainedCopy(btRecBuffer, 0, btRecBufferVideoContent, 0, iRecBuffeActualByteCount);
                VideoQueneFunc(btRecBufferVideoContent);
                //log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::LoadVideoSurveillanceFunc——" + "帧长度 = " + iRecBuffeActualByteCount.ToString() + "\n\n");
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::LoadVideoSurveillanceFunc——" + "视频监控发送错误:"+ ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 视频缓存队列
        /// </summary>
        /// <param name="btVedioRecByte"></param>
        /// <returns></returns>
        private void VideoQueneFunc(byte[] btVedioRecByte)
        {
            try
            {
                //插入:将字节数组插入到末尾
                VideoList.AddRange(btVedioRecByte);

                //网络慢状态:收到少于1帧,画面停留在上一时刻。同时解决拆包问题。
                if (VideoList.Count < (BITMAP_IMAGE_LENGTH + FLAG_LENGTH))
                {
                    btCurrentBitmapImage = btLastBitmapImage;
                    return;
                }

                //正常网络状态下:来一帧,则处理一帧
                else if ((BITMAP_IMAGE_LENGTH + FLAG_LENGTH) * FLAG_LENGTH > VideoList.Count && VideoList.Count >= (BITMAP_IMAGE_LENGTH + FLAG_LENGTH))
                {
                    //拷贝
                    Array.ConstrainedCopy(VideoList.ToArray(), FLAG_LENGTH, btCurrentBitmapImage, 0, BITMAP_IMAGE_LENGTH);
                    btLastBitmapImage = btCurrentBitmapImage;

                    //删除
                    VideoList.RemoveRange(0, (BITMAP_IMAGE_LENGTH + FLAG_LENGTH));
                }

                //网络较好,接收端未能及时处理所有帧:解决粘包问题,并将画面更新到最新帧。
                else if (VideoList.Count >= (BITMAP_IMAGE_LENGTH + FLAG_LENGTH) * FLAG_LENGTH)
                {
                    int iCurrentImageFrameCount;
                    iCurrentImageFrameCount = VideoList.Count / (BITMAP_IMAGE_LENGTH + FLAG_LENGTH);
                    //删除多余帧
                    VideoList.RemoveRange(0, (BITMAP_IMAGE_LENGTH + FLAG_LENGTH) * (iCurrentImageFrameCount - 1));

                    //拷贝当前帧
                    Array.ConstrainedCopy(VideoList.ToArray(), FLAG_LENGTH, btCurrentBitmapImage, 0, BITMAP_IMAGE_LENGTH);
                    btLastBitmapImage = btCurrentBitmapImage;

                    //删除当前帧
                    VideoList.RemoveRange(0, (BITMAP_IMAGE_LENGTH + FLAG_LENGTH));
                }

                //字节转BitmapImage
                BitmapImage bitmapImage = new BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.CacheOption = BitmapCacheOption.OnDemand;
                MemoryStream ms = new MemoryStream(btCurrentBitmapImage);
                bitmapImage.StreamSource = ms;
                bitmapImage.EndInit();
                bitmapImage.Freeze();
                //更新UI
                App.Current.Dispatcher.Invoke((Action)delegate ()
                {
                    imgVideoSurveillance.Source = bitmapImage;
                });
            }
            catch (Exception ex)
            {
                log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::VideoQueneFunc()——" + "视频缓存队列发送错误:" + ex.ToString() + "\n\n");
            }
        }

        /// <summary>
        /// 关闭系统
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnSysShutDown_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        /// <summary>
        /// 视频监控开关状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Camera_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (iCameraFlag == 0)//如果在关闭状态下,那就打开它。
                {
                    iCameraFlag = 1;
                    //显示窗口
                    Panel.SetZIndex(imgVideoSurveillance, 2);//imgVideoSurveillance是图片控件的名字;

                    //Socket给主程序,启动USB视频监控,并将监控画面传输到当前窗口。


                }
                else if (iCameraFlag == 1)// 如果在打开状态下,那就关闭它。
                {
                    iCameraFlag = 0;
                    //关闭窗口
                    Panel.SetZIndex(imgVideoSurveillance, 0);
                    //Socket给主程序,关闭USB视频监控

                }
            }
            catch(Exception ex)
            {
                log.WriteErrorLogFunc(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "\n" + "MainWindow.xaml.cs::Camera_Click()——" + "视频监控开关状态发送错误:" + ex.ToString() + "\n\n");
            }
        }
    }
}

 

;