使用opencv和socket和一臺外網IP的服務器,實現攝像頭監控
如何編譯opencv我就不說了
1.vs新建一個空白的c++項目,取名VideoClientDemo,啓動一個獲取視頻的socket client,client.cpp文件內容:
#include <opencv2/opencv.hpp>
#include <WinSock2.h>
#include <Windows.h>
#pragma comment (lib, "ws2_32.lib")
#pragma warning(disable : 4996)
using namespace cv;
//待傳輸圖像默認大小爲 640*480,可修改
#define IMG_WIDTH 640 // 需傳輸圖像的寬
#define IMG_HEIGHT 480 // 需傳輸圖像的高
//默認格式爲CV_8UC3
#define BUFFER_SIZE IMG_WIDTH*IMG_HEIGHT*3/32
struct recvbuf
{
char buf[BUFFER_SIZE];
int flag;
};
recvbuf data_recv;
Mat recieveMat(SOCKET sockServer);
int main() {
//初始化 DLL
WSADATA data;
WORD w = MAKEWORD(2, 0);
::WSAStartup(w, &data);
// 創建套接字
SOCKET s;
s = ::socket(AF_INET, SOCK_STREAM, 0);
// 構造ip地址
sockaddr_in addr;
addr.sin_family = AF_INET;
//addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.100");
addr.sin_addr.S_un.S_addr = inet_addr("120.24.60.68");
addr.sin_port = htons(2345);
std::cout << "connecting\n";
::connect(s, (sockaddr*)&addr, sizeof(addr));
std::cout << "linked\n";
while (true) {
Mat frame = recieveMat(s);
if (frame.data) imshow("Camera", frame);
if (waitKey(1) >= 0)break;
}
::closesocket(s);
::WSACleanup();
}
Mat recieveMat(SOCKET sockServer) {
Mat img(IMG_HEIGHT, IMG_WIDTH, CV_8UC3, cv::Scalar(0));
int needRecv = sizeof(recvbuf);
int count = 0;
for (int i = 0; i < 32; i++) {
int pos = 0;
int len0 = 0;
while (pos < needRecv) {
len0 = recv(sockServer, (char*)(&data_recv) + pos, needRecv - pos, 0);
pos += len0;
}
count = count + data_recv.flag;
int num1 = IMG_HEIGHT / 32 * i;
for (int j = 0; j < IMG_HEIGHT / 32; 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_recv.buf[num2 + k];
}
}
if (data_recv.flag == 2) {
if (count == 33) {
return img;
}
else {
count = 0;
i = 0;
}
}
}
return img;
}
2.新建一個vs c++項目,取名VideoServerDemo,啓動一個視頻源socket client,src.cpp內容如下:
#include <opencv2/opencv.hpp>
#include <WinSock2.h>
#include <Windows.h>
#pragma comment (lib, "ws2_32.lib")
#pragma warning(disable : 4996)
using namespace cv;
//待傳輸圖像默認大小爲 640*480,可修改
#define IMG_WIDTH 640 // 需傳輸圖像的寬
#define IMG_HEIGHT 480 // 需傳輸圖像的高
//默認格式爲CV_8UC3
#define BUFFER_SIZE IMG_WIDTH*IMG_HEIGHT*3/32
struct sentbuf {
char buf[BUFFER_SIZE];
int flag;
};
sentbuf data;
void sendMat(SOCKET sockClient, Mat image);
int main() {
VideoCapture cap(0); // open the default camera
if (!cap.isOpened()) // check if we succeeded
return -1;
Mat frame;
//初始化 DLL
WSADATA data;
WORD w = MAKEWORD(2, 0);
::WSAStartup(w, &data);
// 創建套接字
SOCKET s;
s = ::socket(AF_INET, SOCK_STREAM, 0);
// 構造ip地址
sockaddr_in addr;
addr.sin_family = AF_INET;
//addr.sin_addr.S_un.S_addr = inet_addr("192.168.1.100");
addr.sin_addr.S_un.S_addr = inet_addr("120.24.60.68");
addr.sin_port = htons(2345);
std::cout << "connecting\n";
::connect(s, (sockaddr*)&addr, sizeof(addr));
std::cout << "linked\n";
char cs[1];
int len = recv(s, (char*)(&cs), 1, 0);
for (;;)
{
cap >> frame; // get a new frame from camera
sendMat(s, frame);
}
::closesocket(s);
::WSACleanup();
}
void sendMat(SOCKET sockClient, Mat image) {
for (int k = 0; k < 32; k++) {
int num1 = IMG_HEIGHT / 32 * k;
for (int i = 0; i < IMG_HEIGHT / 32; 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 == 31)
data.flag = 2;
else
data.flag = 1;
send(sockClient, (char *)(&data), sizeof(data), 0);
}
}
3.兩個socket client同時連接外網IP服務器,服務器負責轉發視頻源發送的數據到接收視頻數據的client,由於服務器系統是linux,所有用c編寫了一個socket server,video_proxy.c內容如下:
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<netdb.h>
#include<errno.h>
#define PORT 2345
#define MAXSIZE 28804
int main(int argc, char *argv[])
{
int sockfd, srcsockfd, tarsockfd;
//定義服務端套接口數據結構
struct sockaddr_in server_addr;
struct sockaddr_in src_client_addr;
struct sockaddr_in tar_client_addr;
int sin_zise, portnumber;
//發送數據緩衝區
char buf[MAXSIZE];
//定義客戶端套接口數據結構
int addr_len = sizeof(struct sockaddr_in);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "create socket failed\n");
exit(EXIT_FAILURE);
}
puts("create socket success");
//清空表示地址的結構體變量
bzero(&server_addr, sizeof(struct sockaddr_in));
//設置addr的成員變量信息
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
//設置ip爲本機IP
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr)) < 0)
{
fprintf(stderr, "bind failed \n");
exit(EXIT_FAILURE);
}
if (listen(sockfd, 10) < 0)
{
perror("listen fail\n");
exit(EXIT_FAILURE);
}
int sin_size = sizeof(struct sockaddr_in);
if ((srcsockfd = accept(sockfd, (struct sockaddr *)(&src_client_addr), &sin_size)) < 0)
{
perror("accept error");
exit(EXIT_FAILURE);
}
if ((tarsockfd = accept(sockfd, (struct sockaddr *)(&tar_client_addr), &sin_size)) < 0)
{
perror("accept error");
exit(EXIT_FAILURE);
}
char cs[1];
cs[0] = 's';
send(srcsockfd, cs, 1, 0);
while (1)
{
int pos = 0;
int len0 = 0;
while (pos < 28804) {
len0 = recv(srcsockfd, buf + pos, MAXSIZE - pos,0);//接收服務器端信息
pos += len0;
}
//printf("send message %d\n", pos);
send(tarsockfd, buf, MAXSIZE, 0);
}
close(srcsockfd);
close(tarsockfd);
close(sockfd);
puts("exit success");
exit(EXIT_SUCCESS);
return 0;
}
以上就是用到的所有代碼,有不懂的可以在評論區留言。
如果你的視頻源已經有了外網IP和端口,可以直接啓動socket server,這樣想要獲取視頻的socket client直接連接socket server就行了,視頻源的socket server的Server.cpp內容如下:
#include "opencv2/opencv.hpp"
#include <WinSock2.h>
#include <Windows.h>
#pragma comment (lib, "ws2_32.lib") //加載 ws2_32.dll
#pragma warning(disable : 4996)
//待傳輸圖像默認大小爲 640*480,可修改
#define IMG_WIDTH 640 // 需傳輸圖像的寬
#define IMG_HEIGHT 480 // 需傳輸圖像的高
//默認格式爲CV_8UC3
#define BUFFER_SIZE IMG_WIDTH*IMG_HEIGHT*3/32
using namespace cv;
struct sentbuf {
char buf[BUFFER_SIZE];
int flag;
};
sentbuf data;
void sendMat(SOCKET sockClient, Mat image);
int main(int, char**)
{
VideoCapture cap(0); // open the default camera
if (!cap.isOpened()) // check if we succeeded
return -1;
Mat frame;
//初始化 DLL
WSADATA wsaData;
::WSAStartup(MAKEWORD(2, 0), &wsaData);
//創建套接字
SOCKET servSock = ::socket(AF_INET, SOCK_STREAM, 0);
//綁定套接字
sockaddr_in sockAddr;
sockAddr.sin_family = AF_INET;
sockAddr.sin_addr.S_un.S_addr = inet_addr("192.168.1.25");
sockAddr.sin_port = htons(1234);
::bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//進入監聽狀態
listen(servSock, 5);
//接收客戶端請求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
std::cout << "linked\n";
for (;;)
{
cap >> frame; // get a new frame from camera
sendMat(clntSock, frame);
}
// the camera will be deinitialized automatically in VideoCapture destructor
//關閉套接字
closesocket(clntSock);
closesocket(servSock);
//終止 DLL 的使用
WSACleanup();
return 0;
}
void sendMat(SOCKET sockClient, Mat image) {
for (int k = 0; k < 32; k++) {
int num1 = IMG_HEIGHT / 32 * k;
for (int i = 0; i < IMG_HEIGHT / 32; 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 == 31)
data.flag = 2;
else
data.flag = 1;
int c = sizeof(data);
send(sockClient, (char *)(&data), c, 0);
}
}
源碼下載:https://download.csdn.net/download/lwy572039941/12094550