近期因项目需要,使用了卡菲(官网在此)相机控制产品,总体来说,该产品还是非常优秀的,支持第三方调用,并有较详细的文档说明(在线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 { }
}
经过测试,局域网内实时显示图片,还是比较流畅的,对于本次项目来说,已能满足需求。