〇、写在前面
首先问个问题,为什么要用UDP传输图像,而不是TCP?
TCP是我们经常使用的通信协议,从认识它的第一天起,就应该知道,它非常稳,丢包率超低。但是一切都有双面性,稳定会影响传输的速度。与TCP不同,UDP没有反复确认这个环节,发送端向一个接收端甩一个数据包,不管接收端有没有接收到,所以相较于TCP,其丢包率比较大,但是它的速度就快多了。针对图像传输这种耗时但是不追求准确性的任务,采用UDP是再合适不过的了。目前许多网络直播都采用UDP来传输图像。
接下来描述一下主要内容,使用C#窗体在两台PC上分别创建一个图像发送端和一个图像接收端,发送端采集摄像头图像,压缩为JPEG格式后使用UDP发送至接收端,接收端接收图像并进行显示。
所以本项目主要有以下两部分:
图像发送端的搭建
图像接收端的搭建
另外这个项目还会用到TCP来确认双方是否都在线,如果接收方还没准备好,发送方就开始发图像了,那就是在做无用功了(虽然对这个项目来说影响不大)。具体的做法就是主机A(发送端)作为TCP服务端,创建一个套接字,绑定一个IP和端口Port,开启监听。主机B作为TCP客户端,连接到主机A创建的服务端。连接后,主机A打开摄像头,并开始向主机B发送图像。
TCP服务端(主机A):
Thread threadWatch = null;
Socket socketWatch = null;
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipaddress = IPAddress.Parse(this.comboBox1.Text.Trim());
IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.comboBox2.Text.Trim()));
socketWatch.Bind(endpoint);
socketWatch.Listen(20);
threadWatch = new Thread(WatchConnecting);
threadWatch.IsBackground = true;
threadWatch.Start();
Socket socConnection = null;
private void WatchConnecting()
{
while (true)
{
socConnection = socketWatch.Accept();
}
}
TCP客户端(主机B):
private void startBtn_Click(object sender, EventArgs e)
{
IPAddress ipaddress = IPAddress.Parse(comboBox1.Text);
EndPoint point = new IPEndPoint(ipaddress, int.Parse(comboBox2.Text));
Thread connect = new Thread(new ParameterizedThreadStart(Connect));
connect.Start(point);
}
private static void Connect(object point)
{
try
{
Socket tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tcpClient.Connect((EndPoint)point);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
最后,两个项目都是用VS2017生成的,.NET框架为4.7.1.
接下来进入正文
一、图像发送端的搭建
首先需要获取摄像头图像,这里使用EmguCV来获取图像以及转码。
点击 工具->NuGet包管理器->管理解决方案的NuGet程序包,在浏览那一栏查找Emgu.cv,选择第一个,在右侧选择要安装EmguCV的项目,点击安装即可。
因为这个项目只用到了EmguCV的读取摄像头功能,就不再赘述了,如果还想深入了解EmguCV,欢迎交流
EmguCV就这样安装好了。然后就可以调用EmguCV中的函数获取摄像头数据了
private VideoCapture capture = new VideoCapture();
Mat currentImage = capture.QueryFrame();
currentImage就是获取的摄像头图像,默认尺寸是640*480,可以通过以下代码更改设置
capture.SetCaptureProperty(CapProp.FrameWidth, 720);
capture.SetCaptureProperty(CapProp.FrameHeight, 1280);
接下来将图像转化成UDP发送的byte[]格式。一个UDP数据包只能发送64k字节数据,也就是65536字节,但是一帧图片就有640x480x3=921600byte=900k字节,所以需要进行图像压缩,这里采用jpeg压缩格式。
Image<Rgb,Byte> img = currentImage.ToImage<Rgb, Byte>();
byte[] bytes = img.ToJpegData(80);
最后使用UDP进行发送。
UdpClient udpClient = new UdpClient();
IPAddress ipaddress = IPAddress.Parse("10.128.14.249");
IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.comboBox2.Text.Trim()));
udpClient.Send(bytes, bytes.Length, endpoint);
udpClient.Close();
二、图像接收端的搭建
在设计面板上添加一个PictureBox控件,用来显示接收到的图像,添加一个Button,用来建立和发送端的连接。
接下来创建一个UDP的套接字,绑定本地IPv4地址,
Socket udpServer = null;
udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpServer.Bind(new IPEndPoint(GetLocalIPv4Address(), 8090));
new Thread(ReceiveMessage)
{
IsBackground = true
}.Start();
如果不知道本地IPv4地址,可以使用以下函数自动获取,这样也可以避免以后IPv4地址改变而报错
public IPAddress GetLocalIPv4Address()
{
IPAddress localIpv4 = null;
IPAddress[] IpList = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress IP in IpList)
{
if (IP.AddressFamily == AddressFamily.InterNetwork)
{
localIpv4 = IP;
}
else
{
continue;
}
}
return localIpv4;
}
最后就可以开始接收图像了
void ReceiveMessage()
{
while (true)
{
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] data = new byte[65536];
int length = udpServer.ReceiveFrom(data, ref remoteEndPoint);
MemoryStream ms = new MemoryStream(data, 0, length);
pictureBox1.Image=Image.FromStream(ms);
}
}
最终效果:
一些注意事项
需要在开头添加以下代码
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Emgu.CV;
using Emgu.CV.Structure;
using System.IO;
项目都已上传至https://download.csdn.net/download/qq_42688495/11872026
----------- 2020.5.14更新 -----------
由于一个UDP数据包只能发送64k字节的数据,所以本文方法能传输的图像大小有限制,更高清晰度图像的传输见UDP实时图像传输进阶篇——1080P视频传输
----------- 2020.8.28更新 -----------
TCP对传输的数据大小没有限制,且能保证传输的可靠性,详见TCP实时图像传输
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)