/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2008, Nils Hasler, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
// Author: Bill McCord
//
// Intuitive Automata
//
// capture video from a socket connection
//
#include "_highgui.h"
#include <android/log.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#define LOGV(...) __android_log_print(ANDROID_LOG_SILENT, LOG_TAG, __VA_ARGS__)
#define LOG_TAG "CVJNI"
#ifdef NDEBUG
#define CV_WARN(message)
#else
#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
#endif
#define IMAGE( i, x, y, n ) *(( unsigned char * )(( i )->imageData \
+ ( x ) * sizeof( unsigned char ) * 3 \
+ ( y ) * ( i )->widthStep ) + ( n ))
class CVCapture_Socket : public CvCapture
{
public:
CVCapture_Socket()
{
pAddrInfo = 0;
width = 0;
height = 0;
readBufSize = 0;
readBuf = 0;
frame = 0;
}
virtual ~CVCapture_Socket()
{
close();
}
virtual bool open(const char* _address, const char* _port, int _width, int _height);
virtual void close();
virtual double getProperty(int);
virtual bool setProperty(int, double);
virtual bool grabFrame();
virtual IplImage* retrieveFrame();
protected:
struct addrinfo *pAddrInfo;
int width; // the width of the images received over the socket
int height; // the height of the images received over the socket
long readBufSize; // the length of the read buffer
char *readBuf; // the read buffer
IplImage* frame;
};
// The open method simply initializes some variables we will need later.
bool CVCapture_Socket::open(const char* _address, const char* _port, int _width, int _height)
{
// Free the addrinfo if it was allocated.
if (pAddrInfo)
{
freeaddrinfo(pAddrInfo);
pAddrInfo = 0;
}
// Check the easy stuff first.
width = _width;
height = _height;
if (width <= 0 || height <= 0)
{
LOGV("Invalid width or height!");
return false;
}
// Setup a new addrinfo to support a streaming socket at the given address and port.
struct addrinfo hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICHOST;
int error = getaddrinfo(_address, _port, &hints, &pAddrInfo);
if (error)
{
char buffer[100];
sprintf(buffer, "getaddrinfo error: %s", gai_strerror(error));
LOGV("%s", buffer);
freeaddrinfo(pAddrInfo);
pAddrInfo = 0;
return false;
}
readBufSize = width * height * sizeof(int);
readBuf = (char*)malloc(readBufSize);
if (!readBuf)
{
LOGV("out of memory error");
freeaddrinfo(pAddrInfo);
pAddrInfo = 0;
return false;
}
return true;
}
// Close cleans up all of our state and cached data.
void CVCapture_Socket::close()
{
LOGV("Setting simple vars to 0");
width = 0;
height = 0;
readBufSize = 0;
LOGV("Freeing Addr Info");
if (pAddrInfo)
{
freeaddrinfo(pAddrInfo);
pAddrInfo = 0;
}
LOGV("Freeing Buffer");
if (readBuf)
{
free(readBuf);
readBuf = 0;
}
LOGV("Releasing Image");
if (frame)
{
cvReleaseImage( &frame );
frame = 0;
}
LOGV("Done closing Capture Socket");
}
// Helper to load pixels from a byte stream received over a socket.
static IplImage* loadPixels(char* pixels, int width, int height) {
int x, y, pos, int_size = sizeof(int);
IplImage *img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
for ( y = 0; y < height; y++ ) {
pos = y * width * int_size;
for ( x = 0; x < width; x++, pos += int_size ) {
// blue
IMAGE( img, x, y, 0 ) = pixels[pos + 3] & 0xFF;
// green
IMAGE( img, x, y, 1 ) = pixels[pos + 2] & 0xFF;
// red
IMAGE( img, x, y, 2 ) = pixels[pos + 1] & 0xFF;
}
}
return img;
}
// Grabs a frame (image) from a socket.
bool CVCapture_Socket::grabFrame()
{
// First ensure that our addrinfo and read buffer are allocated.
if (pAddrInfo == 0 || readBuf == 0)
{
LOGV("You haven't opened the socket capture yet!");
return false;
}
// Establish the socket.
int sockd = socket(pAddrInfo->ai_family, pAddrInfo->ai_socktype, pAddrInfo->ai_protocol);
if (sockd < 0 || errno != 0)
{
char buffer[100];
sprintf(buffer, "Failed to create socket, errno = %d", errno);
LOGV("%s", buffer);
::close(sockd);
return false;
}
// Now connect to the socket.
if (connect(sockd, pAddrInfo->ai_addr, pAddrInfo->ai_addrlen) < 0 || errno != 0)
{
char buffer[100];
sprintf(buffer, "socket connection errorno = %d", errno);
LOGV("%s", buffer);
::close(sockd);
return false;
}
// Release the image if it hasn't been already because we are going to overwrite it.
if (frame)
{
cvReleaseImage( &frame );
frame = 0;
}
// Read the socket until we have filled the data with the space allocated OR run
// out of data which we treat as an error.
long read_count, total_read = 0;
while (total_read < readBufSize)
{
read_count = read(sockd, &readBuf[total_read], readBufSize);
if (read_count <= 0 || errno != 0)
{
char buffer[100];
sprintf(buffer, "socket read errorno = %d", errno);
LOGV("%s", buffer);
break;
}
total_read += read_count;
}
// If we read all of the data we expected, we will load the frame from the pixels.
if (total_read == readBufSize)
{
frame = loadPixels(readBuf, width, height);
}
else
{
LOGV("full read of pixels failed");
}
// Close the socket and return the frame!
::close(sockd);
return frame != 0;
}
IplImage* CVCapture_Socket::retrieveFrame()
{
return frame;
}
double CVCapture_Socket::getProperty(int id)
{
LOGV("unknown/unhandled property");
return 0;
}
bool CVCapture_Socket::setProperty(int id, double value)
{
LOGV("unknown/unhandled property");
return false;
}
CvCapture* cvCreateCameraCapture_Socket( const char *address, const char *port, int width, int height )
{
CVCapture_Socket* capture = new CVCapture_Socket;
if ( capture-> open(address, port, width, height) )
return capture;
delete capture;
return 0;
}