0

Hello
I'm looking to extract jpeg image data from a mjpeg stream, but I'm not sure how to do this in c++ .

The code snippet below does the job of connecting to the remote ip camera server & receives the mjpeg stream via tempBuffer variable.

// Loop and grab mjpeg stream
		while(true)
		{
			int retval;
			retval = recv(hSocket, tempBuffer, sizeof(tempBuffer)-1, 0);
			if (retval==0)
			{
				break; // Connection has been closed
			}
			else if (retval==SOCKET_ERROR)
			{
				throw HRException("socket error while receiving.");
			}
			else
			{
				//***************************************************
				//
				// This is where we receive the incoming data stream
				//
				//***************************************************
				// retval is number of bytes read
				// Terminate buffer with zero				
				tempBuffer[retval] = 0;
				
                
			}
		}

The ip camera server returns the headers followed by jpeg data between --myboundary tags like so

HTTP/1.0 200 OK 
Cache-Control: no-cache 
Pragma: no-cache 
Expires: Thu, 01 Dec 1994 16:00:00 GMT 
Connection: close 
Content-Type: multipart/x-mixed-replace; boundary=--myboundary  

--myboundary 
Content-Type: image/jpeg Content-Length: 19529 

<<jpeg binary image data>
--myboundary
Content-Type: image/jpeg Content-Length: 19789 

<<jpeg binary image data>
--myboundary

Continues...

My question is what would be the best solution to achieve this binary data extraction process in c++ & save to file in binary format, Any coding examples help/advice or links would be greatly appreciated.

Best Regards
Steve

PS
I've searched for days regarding this topic but can't find any examples anywhere, on windows platform using code::blocks+mingw

1
Contributor
1
Reply
2
Views
6 Years
Discussion Span
Last Post by ziggystarman
0

Hello again
I've had a go to try and extract the raw binary image data between the boundary tags with the code below but no great success, I really don't no if this is the right way of getting the raw binary data from the incoming stream and saving it to a file.

string INCOMING_STREAM;
        ofstream dumpFile;
        size_t b_tag;
        string e_tag;
        string boundary_tag;
        string Content_length;
        string raw_data;
        string master_raw_data;
        int flag=0;
        int set_flag=0;


		// Loop and grab mjpeg stream
		while(true)
		{
			int retval;
			retval = recv(hSocket, tempBuffer, sizeof(tempBuffer)-1, 0);
			if (retval==0)
			{
				break; // Connection has been closed
			}
			else if (retval==SOCKET_ERROR)
			{
				throw HRException("socket error while receiving.");
			}
			else
			{
				//***************************************************
				//
				// This is where we recieve the incoming data stream
				//
				//***************************************************
				// retval is number of bytes read
				// Terminate buffer with zero and print as string
				tempBuffer[retval]=0;
				INCOMING_STREAM=tempBuffer;

                //Extract boundary tag ie "--myboundary"
                if(flag == 0){
                        //ID the boundary tag
                        b_tag = INCOMING_STREAM.find("boundary=");
                        if (b_tag!=string::npos){
                            //Extract tag + extra data
                            e_tag = INCOMING_STREAM.substr (b_tag,50);
                            //Get rid of "boundary=" so we left with the tag + trailing garbage
                            e_tag = e_tag.replace(0,9,"");
                        }
                        //Clean up trailing garbage "so let's find pos of newline"
                        //and get rid of every thing after that...
                        b_tag = e_tag.find("\n");
                        if (b_tag!=string::npos){
                            //Cleaned boundary tag
                            boundary_tag = e_tag.replace(int(b_tag),e_tag.length(),"");
                        }
                        cout << "boundary="<<boundary_tag <<endl;
                        //Set flag to 1 so we skip this process in future
                        flag=1;
                }

                //Find boundary tag position of the incoming data stream
                b_tag = INCOMING_STREAM.find(boundary_tag);
                if (b_tag!=string::npos){
                        cout << "Boundary found at: " << b_tag << endl;


                    //First ever boundary tag hit it never comes here again
                    if ( set_flag == 0){

                        cout << "VERY FIRST BOUNDARY TAG HIT" << endl;
                        master_raw_data = INCOMING_STREAM.substr (b_tag,INCOMING_STREAM.length());
                        set_flag = 2;

                    //boundary tag hit
                    }else if (set_flag == 1){

                        cout << "FLAG = 1 LAST BOUNDARY TAG HIT" << endl;
                        cout << "PROCESS RAW DATA" << endl;
                        cout << "LAST BOUNDARY BECOMES NEW FIRST BOUNDARY" << endl;
                        set_flag = 2;

                    //boundary tag hit
                    }else if (set_flag == 2){

                        cout << "FLAG = 2 LAST BOUNDARY TAG HIT" << endl;
                        //Append master_raw_data
                        raw_data = INCOMING_STREAM.substr (0,b_tag);
                        master_raw_data.append(raw_data);

                        //Get content length/position
                        b_tag = master_raw_data.find("Content-Length: ");
                        if (b_tag!=string::npos){
                            //Extract tag + extra data
                            e_tag = master_raw_data.substr (b_tag,50);
                            //Get rid of "Content-Length: " so we left with the value + trailing garbage
                            e_tag = e_tag.replace(0,16,"");
                            master_raw_data = master_raw_data.substr (b_tag,master_raw_data.length());
                            master_raw_data = master_raw_data.replace(0,16,"");
                        }
                        //tidy up remove any leading new lines
                        b_tag = master_raw_data.find("\n");
                        if (b_tag!=string::npos){
                            master_raw_data = master_raw_data.replace(0,int(b_tag),"");
                        }
                        //tidy up remove any leading spaces
                        b_tag = master_raw_data.find(" ");
                        if (b_tag!=string::npos){
                            master_raw_data = master_raw_data.replace(0,int(b_tag),"");
                        }
                        //Clean up trailing garbage "so let's find pos of newline"
                        //and get rid of every thing after that...
                        b_tag = e_tag.find("\n");
                        if (b_tag!=string::npos){
                            //Cleaned content Length
                            Content_length = e_tag.replace(int(b_tag),e_tag.length(),"");
                        }

                        cout << "Length="<<Content_length<<endl;

                        //Write raw binary data to image file .jpg
                        myfile.open ("first_img.jpg", ios::binary);
                        myfile << master_raw_data;
                        myfile.close();

                        //-----------------------------------------------------
                        //Debug, Break process we should have got binary
                        //contain of the very first image data inthe above file
                        //-----------------------------------------------------
                        exit(0);

                        cout << "PROCESS RAW DATA [DONE]" << endl;
                        cout << "LAST BOUNDARY BECOMES NEW FIRST BOUNDARY" << endl;
                        set_flag = 1;


                    }
                //No boundary tag found append incoming to master raw data
                }else{
                        //Append master raw data with new raw data
                        raw_data = INCOMING_STREAM.substr (0,INCOMING_STREAM.length());
                        //cout << "GRABBING RAW DATA TILL WE HIT LAST BOUNDARY TAG" << endl;
                        master_raw_data.append(raw_data);


                }


			}

		}

The whole object is extract & write the binary data between the boundary tag to a file basically as is this will then create an image file .jpg

I've searched the net for a way to do this in c++ but found nothing, I was wondering if some of you more experienced coders could help with this matter so we can get a working example here on these forums here is the full working code I'm trying to achieve this with...

Many Thanks
Steve

#include <iostream>
#include <fstream>
#include <string>
#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>

using namespace std;

ofstream myfile;

class HRException
{
public:
    HRException() :
         m_pMessage("") {}
    virtual ~HRException() {}
    HRException(const char *pMessage) :
         m_pMessage(pMessage) {}
    const char * what() { return m_pMessage; }
private:
    const char *m_pMessage;
};
//-----------------------------------------------------------
//User settings
//-----------------------------------------------------------
const int  REQ_WINSOCK_VER   = 2;	// Minimum winsock version required
const char DEF_SERVER_NAME[] = "192.168.0.3";//ip address of ip camera
const int  SERVER_PORT       = 81;//Port 80
const int  TEMP_BUFFER_SIZE  = 256;

const char HEAD_REQUEST_PART1[] =
{
    //Path to ip camera cgi file
    // "Get /axis-cgi/mjpg/video.cgi?resolution=320x240 HTTP/1.1\r\n"
    "GET  HTTP/1.1\r\n"
	"Host:\r\n"// Specify host name used
	"Accept: text/html;text/plain;"
};
//------------------------------------------------------------
const char HEAD_REQUEST_PART2[] =
{
	"\r\n"							// End hostname header from part1
	"User-agent: Myserver\r\n"      // Specify user agent
	"Connection: close\r\n" 		// Close connection after response
	"\r\n"							// Empty line indicating end of request
};

// IP number typedef for IPv4
typedef unsigned long IPNumber;

IPNumber FindHostIP(const char *pServerName)
{
	HOSTENT *pHostent;

	// Get hostent structure for hostname:
	if (!(pHostent = gethostbyname(pServerName)))
			throw HRException("could not resolve hostname.");

	// Extract primary IP address from hostent structure:
	if (pHostent->h_addr_list && pHostent->h_addr_list[0])
		return *reinterpret_cast<IPNumber*>(pHostent->h_addr_list[0]);

	return 0;
}

void FillSockAddr(sockaddr_in *pSockAddr, const char *pServerName, int portNumber)
{
	// Set family, port and find IP
	pSockAddr->sin_family = AF_INET;
	pSockAddr->sin_port = htons(portNumber);
	pSockAddr->sin_addr.S_un.S_addr = FindHostIP(pServerName);
}

bool RequestHeaders(const char *pServername)
{
	SOCKET 		hSocket = INVALID_SOCKET;
	char		tempBuffer[TEMP_BUFFER_SIZE];
	sockaddr_in	sockAddr = {0};
	bool		bSuccess = true;

	try
	{
		// Lookup hostname and fill sockaddr_in structure:
		cout << "Looking up hostname " << pServername << "... ";
		FillSockAddr(&sockAddr, pServername, SERVER_PORT);
		cout << "found.\n";

		// Create socket
		cout << "Creating socket... ";
		if ((hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
			throw HRException("could not create socket.");
		cout << "created.\n";

		// Connect to server
		cout << "Attempting to connect to " << inet_ntoa(sockAddr.sin_addr)
		     << ":" << SERVER_PORT <<  "... ";
		if (connect(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr))!=0)
		    throw HRException("could not connect.");
		cout << "connected.\n";

		cout << "Sending request... ";

		// send request part 1
		if (send(hSocket, HEAD_REQUEST_PART1, sizeof(HEAD_REQUEST_PART1)-1, 0)==SOCKET_ERROR)
			throw HRException("failed to send data.");

		// send hostname
		if (send(hSocket, pServername, lstrlen(pServername), 0)==SOCKET_ERROR)
			throw HRException("failed to send data.");

		// send request part 2
		if (send(hSocket, HEAD_REQUEST_PART2, sizeof(HEAD_REQUEST_PART2)-1, 0)==SOCKET_ERROR)
			throw HRException("failed to send data.");

		cout << "request sent.\n";

		cout <<	"Dumping received data...\n\n";



        string INCOMING_STREAM;
        ofstream dumpFile;
        size_t b_tag;
        string e_tag;
        string boundary_tag;
        string Content_length;
        string raw_data;
        string master_raw_data;
        int flag=0;
        int set_flag=0;


		// Loop and grab mjpeg stream
		while(true)
		{
			int retval;
			retval = recv(hSocket, tempBuffer, sizeof(tempBuffer)-1, 0);
			if (retval==0)
			{
				break; // Connection has been closed
			}
			else if (retval==SOCKET_ERROR)
			{
				throw HRException("socket error while receiving.");
			}
			else
			{
				//***************************************************
				//
				// This is where we recieve the incoming data stream
				//
				//***************************************************
				// retval is number of bytes read
				// Terminate buffer with zero and print as string
				tempBuffer[retval]=0;
				INCOMING_STREAM=tempBuffer;

                //Extract boundary tag ie "--myboundary"
                if(flag == 0){
                        //ID the boundary tag
                        b_tag = INCOMING_STREAM.find("boundary=");
                        if (b_tag!=string::npos){
                            //Extract tag + extra data
                            e_tag = INCOMING_STREAM.substr (b_tag,50);
                            //Get rid of "boundary=" so we left with the tag + trailing garbage
                            e_tag = e_tag.replace(0,9,"");
                        }
                        //Clean up trailing garbage "so let's find pos of newline"
                        //and get rid of every thing after that...
                        b_tag = e_tag.find("\n");
                        if (b_tag!=string::npos){
                            //Cleaned boundary tag
                            boundary_tag = e_tag.replace(int(b_tag),e_tag.length(),"");
                        }
                        cout << "boundary="<<boundary_tag <<endl;
                        //Set flag to 1 so we skip this process in future
                        flag=1;
                }

                //Find boundary tag position of the incoming data stream
                b_tag = INCOMING_STREAM.find(boundary_tag);
                if (b_tag!=string::npos){
                        cout << "Boundary found at: " << b_tag << endl;


                    //First ever boundary tag hit it never comes here again
                    if ( set_flag == 0){

                        cout << "VERY FIRST BOUNDARY TAG HIT" << endl;
                        master_raw_data = INCOMING_STREAM.substr (b_tag,INCOMING_STREAM.length());
                        set_flag = 2;

                    //boundary tag hit
                    }else if (set_flag == 1){

                        cout << "FLAG = 1 LAST BOUNDARY TAG HIT" << endl;
                        cout << "PROCESS RAW DATA" << endl;
                        cout << "LAST BOUNDARY BECOMES NEW FIRST BOUNDARY" << endl;
                        set_flag = 2;

                    //boundary tag hit
                    }else if (set_flag == 2){

                        cout << "FLAG = 2 LAST BOUNDARY TAG HIT" << endl;
                        //Append master_raw_data
                        raw_data = INCOMING_STREAM.substr (0,b_tag);
                        master_raw_data.append(raw_data);

                        //Get content length/position
                        b_tag = master_raw_data.find("Content-Length: ");
                        if (b_tag!=string::npos){
                            //Extract tag + extra data
                            e_tag = master_raw_data.substr (b_tag,50);
                            //Get rid of "Content-Length: " so we left with the value + trailing garbage
                            e_tag = e_tag.replace(0,16,"");
                            master_raw_data = master_raw_data.substr (b_tag,master_raw_data.length());
                            master_raw_data = master_raw_data.replace(0,16,"");
                        }
                        //tidy up remove any leading new lines
                        b_tag = master_raw_data.find("\n");
                        if (b_tag!=string::npos){
                            master_raw_data = master_raw_data.replace(0,int(b_tag),"");
                        }
                        //tidy up remove any leading spaces
                        b_tag = master_raw_data.find(" ");
                        if (b_tag!=string::npos){
                            master_raw_data = master_raw_data.replace(0,int(b_tag),"");
                        }
                        //Clean up trailing garbage "so let's find pos of newline"
                        //and get rid of every thing after that...
                        b_tag = e_tag.find("\n");
                        if (b_tag!=string::npos){
                            //Cleaned content Length
                            Content_length = e_tag.replace(int(b_tag),e_tag.length(),"");
                        }

                        cout << "Length="<<Content_length<<endl;

                        //Write raw binary data to image file .jpg
                        myfile.open ("first_img.jpg", ios::binary);
                        myfile << master_raw_data;
                        myfile.close();

                        //-----------------------------------------------------
                        //Debug, Break process we should have got binary
                        //contain of the very first image data inthe above file
                        //-----------------------------------------------------
                        exit(0);

                        cout << "PROCESS RAW DATA [DONE]" << endl;
                        cout << "LAST BOUNDARY BECOMES NEW FIRST BOUNDARY" << endl;
                        set_flag = 1;


                    }
                //No boundary tag found append incoming to master raw data
                }else{
                        //Append master raw data with new raw data
                        raw_data = INCOMING_STREAM.substr (0,INCOMING_STREAM.length());
                        //cout << "GRABBING RAW DATA TILL WE HIT LAST BOUNDARY TAG" << endl;
                        master_raw_data.append(raw_data);


                }


			}

		}
	}
	catch(HRException e)
	{
		cerr << "\nError: " << e.what() << endl;
		bSuccess = false;
	}

	if (hSocket!=INVALID_SOCKET)
	{
		closesocket(hSocket);
	}
	return bSuccess;
}

int main(int argc, char* argv[])
{


	int iRet = 1;
	WSADATA wsaData;

	cout << "Initializing winsock... ";

	if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER,0), &wsaData)==0)
	{
		// Check if major version is at least REQ_WINSOCK_VER
		if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER)
		{
			cout << "initialized.\n";

			// Set default hostname:
			const char *pHostname = DEF_SERVER_NAME;

			// Set custom hostname if given on the commandline:
			if (argc > 1)
				pHostname = argv[1];

			iRet = !RequestHeaders(pHostname);
		}
		else
		{
			cerr << "required version not supported!";
		}

		cout << "Cleaning up winsock... ";

		// Cleanup winsock
		if (WSACleanup()!=0)
		{
			cerr << "cleanup failed!\n";
			iRet = 1;
		}
		cout << "done.\n";
	}
	else
	{
		cerr << "startup failed!\n";
	}
	return iRet;
}
This question has already been answered. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.