Streaming UDP Video

This is a small test consisting of a UDP client and server. The server captures video from a webcam using OpenCV, and sends the frames to the client via UDP. This test was done as a part of the process of creating the Cyclops video transmission system.


NOTE: Some people have asked about UDPClient.h - I will post updated source code when I get a chance. In the meantime, you can download the old code here.


Here is a screenshot of the video being transmitted from the server to the client:

The client (left) is receiving video frames from the server (right).



All of this code was written in C++ using the OpenCV library for video capture, and the Windows Socket 2 API for networking. As of now, the code only works on Windows (built and tested with Windows 7).



This code allows the server to capture video from a webcam:

#include "highgui.h"
#include "cv.h"
#define CV_H
#include <iostream>
#include <conio.h>
#include "UDPClient.h"
using namespace std;
using namespace cv;

char *addr = "127.0.0.1";
int port = 9999;

int main(){
	cout<<"=== VIDEO SENDER ==="<<endl;

	//setup UDP client:
	UDPClient *client = new UDPClient(addr, port);

	//setup openCV
	cvNamedWindow("UDP Video Sender", CV_WINDOW_AUTOSIZE);
	CvCapture* capture = cvCreateCameraCapture(0);
	if(!capture){
		cout<<"No camera found."<<endl;
		goto DONE;
	}
	IplImage *frame;
	frame = cvQueryFrame(capture);
	IplImage *small = cvCreateImage(cvSize(frame->width / 2, frame->height / 2),
		frame->depth, 3);

	while(1){
		//capture frame and resize
		frame = cvQueryFrame(capture);
		cvResize(frame, small, CV_INTER_LINEAR);
		cvShowImage("UDP Video Sender", small);


		//send
		int result = client->sendData((char*)(&buff[0]), buff.size());
		if(result < 0)
			cout<<"Failed to send."<<endl;
		else
			cout<<"Sent a frame of size "<<result<<endl;

		cvWaitKey(15);
	}

	goto DONE;

DONE:
	cout<<"Press any key to continue."<<endl;
	getch();
}


This is the client code that displays received video data on the screen:

#include "highgui.h"
#include "cv.h"
#define CV_H
#include <iostream>
#include <conio.h>
#include "UDPClient.h"
using namespace std;
using namespace cv;

int UDPMAX = 65507; // max buffer size
int port = 9999; // listen port number

int main(){
	cout<<"=== VIDEO RECEIVER ==="<<endl;

	//setup UDP client
	UDPClient *client = new UDPClient(port);
	char *buff = (char*)malloc(UDPMAX);

	//setup openCV
	cvNamedWindow("UDP Video Receiver", CV_WINDOW_AUTOSIZE);
	vector<uchar> videoBuffer;
	
	while(1){
		//read data
		int result = client->receiveData(buff, UDPMAX);
		if(result < 0){
			cout<<"Failed to receive frame."<<endl;
			continue;
		}
		cout<<"Got a frame of size "<<result<<endl;

		videoBuffer.resize(result);
		memcpy((char*)(&videoBuffer[0]), buff, result);

		//reconstruct jpeg and display it
		Mat jpegimage = imdecode(Mat(videoBuffer),CV_LOAD_IMAGE_COLOR);
		IplImage img = jpegimage;
		cvShowImage("UDP Video Receiver", &img);

		cvWaitKey(5);
	}
}




The UDPClient class was used by both the server and client side. It contains functions to send and receive data across the network via UDP.


This is the UDPClient constructor and sendData function used by the server:

//CONSTRUCTOR: Create a new socket, establishing a default destination to the
//	given host and port.
//THROWS: Exception upon encountering an error.
UDPClient::UDPClient(char* host_addr, int host_port){
	int result = WSAStartup(MAKEWORD(2, 2), (LPWSADATA) &wsaData);
	if(result < 0){
		throw "WSAStartup ERROR.";
		return;
	}

	clientSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(clientSock == INVALID_SOCKET){
		throw "Invalid Socket ERROR.";
		return;
	}
	
	//bind to (local) address - default properties:
	//setup local address info
	sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = inet_addr("localhost");
	local.sin_port = 0; //randomly selected port
	bind(clientSock, (sockaddr*)&local, sizeof(local));

	//set up destination variables:
	default_destination.sin_family = AF_INET;
	default_destination.sin_addr.s_addr = inet_addr(host_addr);
	default_destination.sin_port = htons(host_port);
	default_destination_size = sizeof(default_destination);

	default_destination_set = true;
}


//SEND_DATA: attempts to send data to the default host/port.
//	RETURNS: results of (private) sendData(), or
//	-1 if failed or if default destination is not set.
int UDPClient::sendData(char* buffer, int len){
	if(!default_destination_set){
		printf("No default set.\n");
		return -1;
	}

	return sendto(clientSock, buffer, len, 0,
		(sockaddr*)&default_destination, default_destination_size);
}

The default_destination variable keeps track of the client's IP and Port numbers, sending data directly there. However, the UDP socket can also be used to send data to any address provided.


Here is the UDPClient constructor and receiveData function used by the client:

//CONSTRUCTOR: Create new socket, bound locally to the given port:
//THROWS: Exception upon ecountering an error.
UDPClient::UDPClient(int local_port){
	int result = WSAStartup(MAKEWORD(2, 2), (LPWSADATA) &wsaData);
	if(result < 0){
		throw "WSAStartup ERROR.";
		return;
	}

	clientSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(clientSock == INVALID_SOCKET){
		throw "Invalid Socket ERROR.";
		return;
	}
	
	//bind to (local) address - given port:
	sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = INADDR_ANY;//inet_addr("127.0.0.1");
	local.sin_port = htons(local_port); //assign given port
	//bind: //NOTE: always returns -1?
	result = bind(clientSock, (sockaddr*)&local, sizeof(local));
	if(result < 0){
		throw "Socket Bind ERROR";
		return;
	}

	default_destination_set = false;
}


//RECEIVE_DATA: attempts to receive data on the bound PORT.
//		RETURNS -1 if failed to receive.
//Read socket data up to "len" size from socket into
// ELSE RETURNS length of data that was received (in bytes).
int UDPClient::receiveData(char* buffer, int len){
	int received = 0;

	sockaddr_in source;
	int source_size = sizeof(source);

	if((received = recvfrom(clientSock, buffer, len, 0,
		(sockaddr*) &source, &source_size)) == SOCKET_ERROR){
		printf("ERROR #%d\n", WSAGetLastError());
		return SOCKET_ERROR;
	}

	last_address = source;
	return received;
}




After we designed this experiment and got it working, we were able to successfully implement this code into the Cyclops client and robot software to enable UDP video transmission.




Back to Top