使用opencv和socket和一臺外網IP的服務器,實現攝像頭監控

使用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

發佈了59 篇原創文章 · 獲贊 14 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章