UDP/TCP和OpenCV的实时视频传输(On Linux)
程序员文章站
2022-07-14 18:48:05
...
1. UDP 传输视频
client.cpp
#include <unistd.h>//Linux系统下网络通讯的头文件集合
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
enum
{
PORT = 8888
};
int main(int argc, char** argv)
{
int m_sockClient;
if ((m_sockClient = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //创建socket句柄,采用UDP协议
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
sockaddr_in m_servaddr;
memset(&m_servaddr, 0, sizeof(m_servaddr)); //初始化结构体
m_servaddr.sin_family = AF_INET; //设置通信方式
m_servaddr.sin_port = htons(PORT); //设置端口号
m_servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//m_servaddr.sin_port = htons(8888);//设置需要发送的IP和端口号
bind(m_sockClient, (sockaddr*)&m_servaddr, sizeof(m_servaddr));//绑定端口号
VideoCapture capture(cv::String("/home/zhy/Documents/Perception/camera_data/tmp.mp4"));//打开摄像头
Mat image;
while (true)
{
capture >> image;//读入图片
if (image.empty()) //如果照片为空则退出
{
printf("empty image\n\n");
return -1;
}
std::vector<uchar> data_encode;
std::vector<int> quality;
quality.push_back(CV_IMWRITE_JPEG_QUALITY);
quality.push_back(30);//进行50%的压缩
imencode(".jpg", image, data_encode,quality);//将图像编码
//char encodeImg[426672];
int nSize = data_encode.size();
unsigned char *encodeImg = new unsigned char[nSize];
printf("%d\n", nSize);
for (int i = 0; i < nSize; i++)
{
encodeImg[i] = data_encode[i];
}
sendto(m_sockClient, encodeImg, nSize, 0, (const sockaddr*)& m_servaddr, sizeof(m_servaddr));
memset(&encodeImg, 0, sizeof(encodeImg)); //初始化结构体
}
return 0;
}
server.cpp
#include <unistd.h>//Linux系统下网络通讯的头文件集合
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
enum
{
PORT = 8888
};
int main(int argc, char** argv)
{
int m_sockClient;
if ((m_sockClient = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //创建socket句柄,采用UDP协议
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
sockaddr_in m_servaddr;
memset(&m_servaddr, 0, sizeof(m_servaddr)); //初始化结构体
m_servaddr.sin_family = AF_INET; //设置通信方式
m_servaddr.sin_port = htons(PORT); //设置端口号
m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
int len = sizeof(m_servaddr);
bind(m_sockClient, (sockaddr*)&m_servaddr, sizeof(m_servaddr));//绑定套接字
Mat image;
unsigned char buf[426672];
struct sockaddr_in addr_client;
while (true)
{
std::vector<uchar> decode;
int n = recvfrom(m_sockClient, buf, sizeof(buf), 0,(struct sockaddr *)&addr_client, (socklen_t *)&len);//接受缓存
int pos = 0;
while (pos < n)
{
decode.push_back(buf[pos++]);//存入vector
}
buf[n] = 0;
image = imdecode(decode, CV_LOAD_IMAGE_COLOR);//图像解码
imshow("image", image);
waitKey(30);
}
return 0;
}
2. TCP传输视频
TCP协议通信的一般步骤:
客户端:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
服务器端:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen();
5、接收客户端上来的连接,用函数accept();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
8、关闭监听;
采集与发送:
SocketMatTransmissionClient.h
#ifndef SOCKETMATTRANSMISSIONCLIENT_H
#define SOCKETMATTRANSMISSIONCLIENT_H
#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace cv;
//待传输图像默认大小为 640*480,可修改
#define IMG_WIDTH 1920 // 需传输图像的宽
#define IMG_HEIGHT 1200 // 需传输图像的高
#define PACKAGE_NUM 2
//默认格式为CV_8UC3
#define BUFFER_SIZE IMG_WIDTH*IMG_HEIGHT*3/PACKAGE_NUM
struct sentbuf
{
char buf[BUFFER_SIZE];
int flag;
};
class SocketMatTransmissionClient
{
public:
SocketMatTransmissionClient(void);
~SocketMatTransmissionClient(void);
private:
int sockClient;
struct sentbuf data;
public:
// 打开socket连接
// params : IP 服务器的ip地址
// PORT 传输端口
// return : -1 连接失败
// 1 连接成功
int socketConnect(const char* IP, int PORT);
// 传输图像
// params : image 待传输图像
// return : -1 传输失败
// 1 传输成功
int transmit(cv::Mat image);
// 断开socket连接
void socketDisconnect(void);
};
#endif // SOCKETMATTRANSMISSIONCLIENT_H
socketmattransmissionclient.cpp
#include "socketmattransmissionclient.h"
SocketMatTransmissionClient::SocketMatTransmissionClient(void)
{
}
SocketMatTransmissionClient::~SocketMatTransmissionClient(void)
{
}
int SocketMatTransmissionClient::socketConnect(const char* IP, int PORT)
{
struct sockaddr_in servaddr;
if ((sockClient = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
/*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
if (inet_pton(AF_INET, IP, &servaddr.sin_addr) <= 0)
{
printf("inet_pton error for %s\n", IP);
return -1;
}
/*将套接字绑定到服务器的网络地址上*/
if (connect(sockClient, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("connect error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
else
{
printf("connect successful!\n");
}
}
void SocketMatTransmissionClient::socketDisconnect(void)
{
close(sockClient);
}
int SocketMatTransmissionClient::transmit(cv::Mat image)
{
if (image.empty())
{
printf("empty image\n\n");
return -1;
}
if(image.cols != IMG_WIDTH || image.rows != IMG_HEIGHT || image.type() != CV_8UC3)
{
printf("the image must satisfy : cols == IMG_WIDTH(%d) rows == IMG_HEIGHT(%d) type == CV_8UC3\n\n", IMG_WIDTH, IMG_HEIGHT);
return -1;
}
for(int k = 0; k < PACKAGE_NUM; k++)
{
int num1 = IMG_HEIGHT / PACKAGE_NUM * k;
for (int i = 0; i < IMG_HEIGHT / PACKAGE_NUM; i++)
{
int num2 = i * IMG_WIDTH * 3;
uchar* ucdata = image.ptr<uchar>(i + num1);
for (int j = 0; j < IMG_WIDTH * 3; j++)
{
data.buf[num2 + j] = ucdata[j];
}
}
if(k == PACKAGE_NUM - 1)
data.flag = 2;
else
data.flag = 1;
if (send(sockClient, (char *)(&data), sizeof(data), 0) < 0)
{
printf("send image error: %s(errno: %d)\n", strerror(errno), errno);
return -1;
}
}
}
main.cpp
#include "socketmattransmissionclient.h"
#include <opencv2/opencv.hpp>
int main()
{
SocketMatTransmissionClient socketMat;
if (socketMat.socketConnect("127.0.0.1", 6666) < 0)
{
return 0;
}
cv::VideoCapture capture(cv::String("/home/zhy/Documents/Perception/camera_data/tmp.mp4"));
cv::Mat image;
while (1)
{
if (!capture.isOpened())
return 0;
capture >> image;
if (image.empty())
return 0;
socketMat.transmit(image);
}
socketMat.socketDisconnect();
return 0;
}
CMakeLists.txt
# cmake needs this line
cmake_minimum_required(VERSION 2.8)
# Define project name
project(image_client_TCP)
# Find OpenCV, you may need to set OpenCV_DIR variable
# to the absolute path to the directory containing OpenCVConfig.cmake file
# via the command line or GUI
find_package(OpenCV REQUIRED)
# If the package has been found, several variables will
# be set, you can find the full list with descriptions
# in the OpenCVConfig.cmake file.
# Print some message showing some of them
message(STATUS "OpenCV library status:")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
if(CMAKE_VERSION VERSION_LESS "2.8.11")
# Add OpenCV headers location to your include paths
include_directories(${OpenCV_INCLUDE_DIRS})
endif()
# Declare the executable target built from your sources
add_executable(main main.cpp socketmattransmissionclient.cpp)
# Link your application with OpenCV libraries
target_link_libraries(main ${OpenCV_LIBS})
接收与显示:
SocketMatTransmissionServer.h
#ifndef SOCKETMATTRANSMISSIONSERVER_H
#define SOCKETMATTRANSMISSIONSERVER_H
#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace cv;
#define PACKAGE_NUM 2
#define IMG_WIDTH 1920
#define IMG_HEIGHT 1200
#define BLOCKSIZE IMG_WIDTH*IMG_HEIGHT*3/PACKAGE_NUM
struct recvBuf
{
char buf[BLOCKSIZE];
int flag;
};
class SocketMatTransmissionServer
{
public:
SocketMatTransmissionServer(void);
~SocketMatTransmissionServer(void);
int sockConn;
private:
struct recvBuf data;
int needRecv;
int count;
public:
// 打开socket连接
// params : PORT 传输端口
// return : -1 连接失败
// 1 连接成功
int socketConnect(int PORT);
// 传输图像
// params : image 待接收图像
// image 待接收图像
// return : -1 接收失败
// 1 接收成功
int receive(cv::Mat& image);
// 断开socket连接
void socketDisconnect(void);
};
#endif // SOCKETMATTRANSMISSIONSERVER_H
socketmattransmissionserver.cpp
#include "socketmattransmissionserver.h"
SocketMatTransmissionServer::SocketMatTransmissionServer(void)
{
}
SocketMatTransmissionServer::~SocketMatTransmissionServer(void)
{
}
int SocketMatTransmissionServer::socketConnect(int PORT)
{
int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)
{
perror("bind");
return -1;
}
if(listen(server_sockfd,5) == -1)
{
perror("listen");
return -1;
}
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
sockConn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);
if(sockConn<0)
{
perror("connect");
return -1;
}
else
{
printf("connect successful!\n");
return 1;
}
close(server_sockfd);
}
void SocketMatTransmissionServer::socketDisconnect(void)
{
close(sockConn);
}
int SocketMatTransmissionServer::receive(cv::Mat& image)
{
int returnflag = 0;
cv::Mat img(IMG_HEIGHT, IMG_WIDTH, CV_8UC3, cv::Scalar(0));
needRecv = sizeof(recvBuf);
count = 0;
memset(&data,0,sizeof(data));
for (int i = 0; i < PACKAGE_NUM; i++)
{
int pos = 0;
int len0 = 0;
while (pos < needRecv)
{
len0 = recv(sockConn, (char*)(&data) + pos, needRecv - pos, 0);
if (len0 < 0)
{
printf("Server Recieve Data Failed!\n");
break;
}
pos += len0;
}
count = count + data.flag;
int num1 = IMG_HEIGHT / PACKAGE_NUM * i;
for (int j = 0; j < IMG_HEIGHT / PACKAGE_NUM; j++)
{
int num2 = j * IMG_WIDTH * 3;
uchar* ucdata = img.ptr<uchar>(j + num1);
for (int k = 0; k < IMG_WIDTH * 3; k++)
{
ucdata[k] = data.buf[num2 + k];
}
}
if (data.flag == 2)
{
if (count == PACKAGE_NUM + 1)
{
image = img;
returnflag = 1;
count = 0;
}
else
{
count = 0;
i = 0;
}
}
}
if(returnflag == 1)
return 1;
else
return -1;
}
main.cpp
#include "socketmattransmissionserver.h"
int main()
{
SocketMatTransmissionServer socketMat;
if (socketMat.socketConnect(6666) < 0)
{
return 0;
}
cv::Mat image;
while (1)
{
if(socketMat.receive(image) > 0)
{
cv::imshow("",image);
cv::waitKey(30);
}
}
socketMat.socketDisconnect();
return 0;
}
实验结果: