近期因项目需要,使用了卡菲官网在此相机控制产品,总体来说,该产品还是非常优秀的,支持第三方调用,并有较详细的文档说明(在线SDK在此),可实现照相、读取照片列表、下载原图、下载缩略图、下载预览图、实时取景、设置相机配置等等一系列功能,还支持市面上大部分主流的单反相机。

在本次项目中,我们主要使用到了拍照、下载原图、实时取景等功能,其中在开发“实时取景”功能时,碰到了一个小问题,在此分享一下。官方文档中关于“实时取景”的调用方式是如下描述的:


实时取景

GET /capturemovie 
启动实时取景视频流。调用这个API,会创建一个TCP服务器,端口为890.客户端可以通过SOCKET端口,读取视频流。视频流格式为MJPEG. 
返回: 成功返回 状态代码 200 OK,否则返回状态代码500.

GET /stopcapturemovie 
关闭视频流 返回: 成功返回 状态代码 200 OK,否则返回状态代码500.


客户端调用capturemovice后,服务器会以TCP的方式返回MJPEG流。关于MJPEG流,可参考一下这篇文章(戳这里)。于是,我写代码接收TCP的流,发现有的数据包比较大,有点数据包比较小,仔细分析每个数据包,发现其实就是一张张的JPG图片,JPG图片的一个特征是:图像以FFD8开头,图像以FFD9结束。为此,每收到一个数据包,都先判断该包是否为完整的图像包,如果是,则显示出来;如果不是,先存储在内存,再继续接收下一个包,当收到结束包后,再把拼接后的图像显示出来。以此类推,具体代码如下:

private void RevImgData()
   {
            RestClient client = new RestClient(URL);
            RestRequest req = new RestRequest("capturemovie");

            req.Timeout = 5 * 1000; //超时时间
            req.Credentials = new NetworkCredential("CamFi", "");
            IRestResponse res = client.Get(req);

            if (res.StatusCode != HttpStatusCode.OK)
            {
                MessageBox.Show("CamFi 未连接,请检查网络!", "警告", 0, MessageBoxIcon.Warning);
                return;
            }

            //初始化UDP参数
            string localIP = ConfigurationManager.AppSettings["localIP"];
            string camIP = ConfigurationManager.AppSettings["camIP"];
            int localPort = Convert.ToInt32(ConfigurationManager.AppSettings["localPort"]);
            IPEndPoint localIp = new IPEndPoint(IPAddress.Parse(localIP), localPort);
            IPEndPoint remoteIp = new IPEndPoint(IPAddress.Parse(camIP), 890);

            System.Net.Sockets.Socket tcpclient = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork,
            System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);

            try
            {
                byte[] data = new byte[5 * 1024 * 1024];
                byte[] bufdata = new byte[5 * 1024 * 1024];

                int recvSize = 0, imgSize = 0;
                bool bOnePack = true; //图片是否就一个数据包

                byte[] realdata = null;
                Image img = null;

                //TCP连接
                tcpclient.Connect(remoteIp);

                while (bRevImgData)
                {
                    recvSize = tcpclient.Receive(data);

                    if (data[0] == 255 && data[1] == 216) //图片头
                    {
                        bOnePack = true;
                        imgSize = 0;
                     //   Console.WriteLine("头:" + data[0] + "," + data[1]);

                    }
                    if(data[recvSize - 2] == 255 && data[recvSize - 1] == 217) //图片尾
                    {
                        if(bOnePack)
                        {
                            Array.Copy(data, bufdata, recvSize);
                            imgSize = recvSize;
                        }
                        else
                        {
                            Array.Copy(data, 0, bufdata, imgSize, recvSize);
                            imgSize += recvSize;
                         }

                     //   Console.WriteLine("大小:" + imgSize);
                     //   Console.WriteLine("尾:" + data[recv - 2] + "," + data[recv - 1]);           
                        lock (m_lock)
                        {
                            realdata = new byte[imgSize];
                            Array.Copy(bufdata, realdata, imgSize);
                            img = BytesToImage(realdata);

                            pictureBox1.Image = img;
                        }
                        imgSize = 0;
                    }
                    else
                    {
                        Array.Copy(data, 0, bufdata, imgSize, recvSize);
                        imgSize += recvSize;
                        bOnePack = false;
                    }
                    Thread.Sleep(10);
                }
            }
         catch { }
  }

经过测试,局域网内实时显示图片,还是比较流畅的,对于本次项目来说,已能满足需求。