1/*M///////////////////////////////////////////////////////////////////////////////////////
2//
3//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4//
5//  By downloading, copying, installing or using the software you agree to this license.
6//  If you do not agree to this license, do not download, install,
7//  copy or use the software.
8//
9//
10//                        Intel License Agreement
11//                For Open Source Computer Vision Library
12//
13// Copyright (C) 2008, Nils Hasler, all rights reserved.
14// Third party copyrights are property of their respective owners.
15//
16// Redistribution and use in source and binary forms, with or without modification,
17// are permitted provided that the following conditions are met:
18//
19//   * Redistribution's of source code must retain the above copyright notice,
20//     this list of conditions and the following disclaimer.
21//
22//   * Redistribution's in binary form must reproduce the above copyright notice,
23//     this list of conditions and the following disclaimer in the documentation
24//     and/or other materials provided with the distribution.
25//
26//   * The name of Intel Corporation may not be used to endorse or promote products
27//     derived from this software without specific prior written permission.
28//
29// This software is provided by the copyright holders and contributors "as is" and
30// any express or implied warranties, including, but not limited to, the implied
31// warranties of merchantability and fitness for a particular purpose are disclaimed.
32// In no event shall the Intel Corporation or contributors be liable for any direct,
33// indirect, incidental, special, exemplary, or consequential damages
34// (including, but not limited to, procurement of substitute goods or services;
35// loss of use, data, or profits; or business interruption) however caused
36// and on any theory of liability, whether in contract, strict liability,
37// or tort (including negligence or otherwise) arising in any way out of
38// the use of this software, even if advised of the possibility of such damage.
39//
40//M*/
41
42// Author: Bill McCord
43//
44//         Intuitive Automata
45
46//
47// capture video from a socket connection
48//
49
50#include "_highgui.h"
51#include <android/log.h>
52#include <errno.h>
53#include <netdb.h>
54#include <unistd.h>
55
56#define LOGV(...) __android_log_print(ANDROID_LOG_SILENT, LOG_TAG, __VA_ARGS__)
57#define LOG_TAG "CVJNI"
58
59#ifdef NDEBUG
60#define CV_WARN(message)
61#else
62#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
63#endif
64
65#define IMAGE( i, x, y, n )   *(( unsigned char * )(( i )->imageData      \
66                                    + ( x ) * sizeof( unsigned char ) * 3 \
67                                    + ( y ) * ( i )->widthStep ) + ( n ))
68
69class CVCapture_Socket : public CvCapture
70{
71public:
72    CVCapture_Socket()
73    {
74		pAddrInfo = 0;
75		width = 0;
76		height = 0;
77		readBufSize = 0;
78		readBuf = 0;
79        frame = 0;
80    }
81
82    virtual ~CVCapture_Socket()
83    {
84        close();
85    }
86
87    virtual bool open(const char* _address, const char* _port, int _width, int _height);
88    virtual void close();
89    virtual double getProperty(int);
90    virtual bool setProperty(int, double);
91    virtual bool grabFrame();
92    virtual IplImage* retrieveFrame();
93
94protected:
95	struct addrinfo *pAddrInfo;
96	int width; // the width of the images received over the socket
97	int height; // the height of the images received over the socket
98	long readBufSize; // the length of the read buffer
99	char *readBuf; // the read buffer
100
101    IplImage* frame;
102};
103
104// The open method simply initializes some variables we will need later.
105bool CVCapture_Socket::open(const char* _address, const char* _port, int _width, int _height)
106{
107	// Free the addrinfo if it was allocated.
108	if (pAddrInfo)
109	{
110		freeaddrinfo(pAddrInfo);
111		pAddrInfo = 0;
112	}
113
114	// Check the easy stuff first.
115	width = _width;
116	height = _height;
117	if (width <= 0 || height <= 0)
118	{
119		LOGV("Invalid width or height!");
120		return false;
121	}
122
123	// Setup a new addrinfo to support a streaming socket at the given address and port.
124	struct addrinfo hints;
125	memset(&hints, 0, sizeof hints);
126	hints.ai_family = AF_UNSPEC;  // use IPv4 or IPv6, whichever
127	hints.ai_socktype = SOCK_STREAM;
128	hints.ai_flags = AI_NUMERICHOST;
129
130	int error = getaddrinfo(_address, _port, &hints, &pAddrInfo);
131	if (error)
132	{
133		char buffer[100];
134		sprintf(buffer, "getaddrinfo error: %s", gai_strerror(error));
135		LOGV("%s", buffer);
136		freeaddrinfo(pAddrInfo);
137		pAddrInfo = 0;
138		return false;
139	}
140
141	readBufSize = width * height * sizeof(int);
142	readBuf = (char*)malloc(readBufSize);
143	if (!readBuf)
144	{
145		LOGV("out of memory error");
146		freeaddrinfo(pAddrInfo);
147		pAddrInfo = 0;
148		return false;
149	}
150
151	return true;
152}
153
154// Close cleans up all of our state and cached data.
155void CVCapture_Socket::close()
156{
157	LOGV("Setting simple vars to 0");
158	width = 0;
159	height = 0;
160	readBufSize = 0;
161
162	LOGV("Freeing Addr Info");
163	if (pAddrInfo)
164	{
165		freeaddrinfo(pAddrInfo);
166		pAddrInfo = 0;
167	}
168
169	LOGV("Freeing Buffer");
170	if (readBuf)
171	{
172		free(readBuf);
173		readBuf = 0;
174	}
175
176	LOGV("Releasing Image");
177	if (frame)
178	{
179		cvReleaseImage( &frame );
180		frame = 0;
181	}
182
183	LOGV("Done closing Capture Socket");
184}
185
186// Helper to load pixels from a byte stream received over a socket.
187static IplImage* loadPixels(char* pixels, int width, int height) {
188
189	int x, y, pos, int_size = sizeof(int);
190	IplImage *img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
191
192	for ( y = 0; y < height; y++ ) {
193		pos = y * width * int_size;
194        for ( x = 0; x < width; x++, pos += int_size ) {
195            // blue
196            IMAGE( img, x, y, 0 ) = pixels[pos + 3] & 0xFF;
197            // green
198            IMAGE( img, x, y, 1 ) = pixels[pos + 2] & 0xFF;
199            // red
200            IMAGE( img, x, y, 2 ) = pixels[pos + 1] & 0xFF;
201        }
202    }
203
204	return img;
205}
206
207// Grabs a frame (image) from a socket.
208bool CVCapture_Socket::grabFrame()
209{
210	// First ensure that our addrinfo and read buffer are allocated.
211	if (pAddrInfo == 0 || readBuf == 0)
212	{
213		LOGV("You haven't opened the socket capture yet!");
214		return false;
215	}
216
217	// Establish the socket.
218	int sockd = socket(pAddrInfo->ai_family, pAddrInfo->ai_socktype, pAddrInfo->ai_protocol);
219	if (sockd < 0 || errno != 0)
220	{
221		char buffer[100];
222		sprintf(buffer, "Failed to create socket, errno = %d", errno);
223		LOGV("%s", buffer);
224		::close(sockd);
225		return false;
226	}
227
228	// Now connect to the socket.
229	if (connect(sockd, pAddrInfo->ai_addr, pAddrInfo->ai_addrlen) < 0 || errno != 0)
230	{
231		char buffer[100];
232		sprintf(buffer, "socket connection errorno = %d", errno);
233		LOGV("%s", buffer);
234		::close(sockd);
235		return false;
236	}
237
238	// Release the image if it hasn't been already because we are going to overwrite it.
239	if (frame)
240	{
241		cvReleaseImage( &frame );
242		frame = 0;
243	}
244
245	// Read the socket until we have filled the data with the space allocated OR run
246	// out of data which we treat as an error.
247	long read_count, total_read = 0;
248	while (total_read < readBufSize)
249	{
250		read_count = read(sockd, &readBuf[total_read], readBufSize);
251		if (read_count <= 0 || errno != 0)
252		{
253			char buffer[100];
254			sprintf(buffer, "socket read errorno = %d", errno);
255			LOGV("%s", buffer);
256			break;
257		}
258		total_read += read_count;
259	}
260
261	// If we read all of the data we expected, we will load the frame from the pixels.
262	if (total_read == readBufSize)
263	{
264		frame = loadPixels(readBuf, width, height);
265	}
266	else
267	{
268		LOGV("full read of pixels failed");
269	}
270
271	// Close the socket and return the frame!
272	::close(sockd);
273
274    return frame != 0;
275}
276
277IplImage* CVCapture_Socket::retrieveFrame()
278{
279    return frame;
280}
281
282double CVCapture_Socket::getProperty(int id)
283{
284	LOGV("unknown/unhandled property");
285    return 0;
286}
287
288bool CVCapture_Socket::setProperty(int id, double value)
289{
290    LOGV("unknown/unhandled property");
291    return false;
292}
293
294CvCapture* cvCreateCameraCapture_Socket( const char *address, const char *port, int width, int height )
295{
296	CVCapture_Socket* capture = new CVCapture_Socket;
297	if ( capture-> open(address, port, width, height) )
298		return capture;
299
300	delete capture;
301	return 0;
302}
303