本文章讲解了,基于GStreamer、RTP/JPEG实现对USB摄像头的控制和显示。实现客户端主动打开摄像头进行视频传输,以及客户端主动关闭摄像头停止视频传输的过程。该方案可以帮助用户快速搭建自己的网络监控系统,满足基本监控需要。如果给server分配公网IP的话,就可以实现远程监控的功能。注意Server端要进行安全验证,保证合法用户才能使用,这方面可以参考RTSP协议进行设计。
系统架构如下所示:
在该系统中ARM端的软件作为server,其IP地址和端口是固定的。PC的C#上位机软件作为client,其IP地址和端口不固定。
首先,Client端向Server发送打开、关闭摄像头的请求。
private void buttonPlay_Click(object sender, EventArgs e)
{
if (Cbx_Protocol.Text.Trim() == "UDP")
{
byte[] data = {0};
if (udpClient != null)
{
udpClient.Send(data, data.Length, new IPEndPoint(IPAddress.Parse("192.168.18.10"), 13456));
}
else
{
MessageBox.Show("网络通信未连接!", "Warning");
}
}
else
{
MessageBox.Show("UDP通信未设置!", "Warning");
}
}
private void buttonStop_Click(object sender, EventArgs e)
{
if (Cbx_Protocol.Text.Trim() == "UDP")
{
byte[] data = {1};
if (udpClient != null)
{
udpClient.Send(data, data.Length, new IPEndPoint(IPAddress.Parse("192.168.18.10"), 13456));
}
else
{
MessageBox.Show("网络通信未连接!", "Warning");
}
}
else
{
MessageBox.Show("UDP通信未设置!", "Warning");
}
}
Server端在收到播放请求以后,将client的IP地址和端口解析出来。使用system函数调用gst-launch,将视频流发送到对应的IP地址和端口。
Server端在收到停止请求以后,使用system函数调用pkill杀掉gst-launch进程。
int Connection::receiveUdp(void)
{
std::vector<uint8_t> data(mMaxRcvSize, 0);
struct sockaddr fromAddr;
socklen_t addrLen = sizeof(fromAddr);
struct sockaddr_in sendAddr;
socklen_t sdAddrLen = sizeof(sendAddr);
errno = 0;
int dataLen = recvfrom(mSd, static_cast<void*>(data.data()), data.size(), 0,
&fromAddr, &addrLen);
pid_t sys_status;
if (0 > dataLen)
{
if (errno != EWOULDBLOCK)
{
// Unexpected error - cleanup for retry
cleanup();
}
}
else if (0 == dataLen)
{
// No action - Empty input buffer (EOF)
}
else if (mRcvCallback != nullptr)
{
// bytes received - forward data via callback
//mRcvCallback(data.data(), dataLen, mName);
if(data[0] == 0)
{
char ip[INET_ADDRSTRLEN] = {0}; // Note: INET6 not supported
sockaddr_in* fromAddr_in = reinterpret_cast<sockaddr_in*>(&fromAddr);
inet_ntop(AF_INET, &fromAddr_in->sin_addr, ip, INET_ADDRSTRLEN);
cout << "UDP from " << ip << ":" << ntohs(fromAddr_in->sin_port) << endl;
std::string video_play = "gst-launch-1.0 -v v4l2src device=/dev/video0 ! 'image/jpeg,width=176, height=144, framerate=(fraction)30/1' ! rtpjpegpay ! udpsink host=";
std::string ip_str(ip);
string str_port = std::to_string(ntohs(fromAddr_in->sin_port));
//gst-launch-1.0 -v v4l2src device=/dev/video0 ! "image/jpeg,width=176, height=144, framerate=(fraction)30/1" ! rtpjpegpay ! udpsink host=192.168.18.136 port=49152
video_play += ip_str;
video_play += " port=";
video_play += str_port;
video_play += "&";
cout << "video_play command: " << video_play.c_str() << endl;
sys_status=system(video_play.c_str());
cout << "sys_status: " << int(sys_status) << endl;
}
else if(data[0] == 1)
{
sys_status=system("pkill gst-launch-1.0");
}
}
return dataLen;
}
最后显示的效果如下: