2011-04-18 16:50:24 +08:00
/*M///////////////////////////////////////////////////////////////////////////////////////
2011-04-06 22:31:03 +08:00
//
// 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) 2000, Intel Corporation, 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*/
# include "precomp.hpp"
# ifdef HAVE_ANDROID_NATIVE_CAMERA
# include <opencv2/imgproc/imgproc.hpp>
# include <pthread.h>
# include <android/log.h>
2011-06-17 02:00:41 +08:00
# include <camera_activity.hpp>
2011-04-06 22:31:03 +08:00
2011-05-19 14:36:44 +08:00
# if !defined(LOGD) && !defined(LOGI) && !defined(LOGE)
2011-04-06 22:31:03 +08:00
# define LOG_TAG "CV_CAP"
# define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
# define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
# define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
2011-05-19 14:36:44 +08:00
# endif
2011-04-06 22:31:03 +08:00
class HighguiAndroidCameraActivity ;
class CvCapture_Android : public CvCapture
{
public :
2011-04-18 16:50:24 +08:00
CvCapture_Android ( int ) ;
2011-04-06 22:31:03 +08:00
virtual ~ CvCapture_Android ( ) ;
virtual double getProperty ( int propIdx ) ;
virtual bool setProperty ( int probIdx , double propVal ) ;
virtual bool grabFrame ( ) ;
virtual IplImage * retrieveFrame ( int outputType ) ;
virtual int getCaptureDomain ( ) { return CV_CAP_ANDROID ; }
bool isOpened ( ) const ;
protected :
struct OutputMap
{
public :
cv : : Mat mat ;
IplImage * getIplImagePtr ( ) ;
private :
IplImage iplHeader ;
} ;
CameraActivity * m_activity ;
2011-04-18 16:50:24 +08:00
//raw from camera
int m_width ;
int m_height ;
unsigned char * m_frameYUV420i ;
unsigned char * m_frameYUV420inext ;
void setFrame ( const void * buffer , int bufferSize ) ;
2011-04-06 22:31:03 +08:00
private :
bool m_isOpened ;
2011-04-18 16:50:24 +08:00
bool m_CameraParamsChanged ;
//frames counter for statistics
int m_framesGrabbed ;
2011-04-06 22:31:03 +08:00
2011-04-18 16:50:24 +08:00
//cached converted frames
2011-04-06 22:31:03 +08:00
OutputMap m_frameGray ;
OutputMap m_frameColor ;
bool m_hasGray ;
bool m_hasColor ;
2011-04-30 03:03:41 +08:00
enum CvCapture_Android_DataState {
CVCAPTURE_ANDROID_STATE_NO_FRAME = 0 ,
CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED ,
CVCAPTURE_ANDROID_STATE_HAS_FRAME_GRABBED
} ;
volatile CvCapture_Android_DataState m_dataState ;
2011-04-18 16:50:24 +08:00
//synchronization
2011-04-06 22:31:03 +08:00
pthread_mutex_t m_nextFrameMutex ;
pthread_cond_t m_nextFrameCond ;
volatile bool m_waitingNextFrame ;
2011-04-30 03:03:41 +08:00
volatile bool m_shouldAutoGrab ;
2011-04-06 22:31:03 +08:00
2011-04-18 16:50:24 +08:00
void prepareCacheForYUV420i ( int width , int height ) ;
static bool convertYUV420i2Grey ( int width , int height , const unsigned char * yuv , cv : : Mat & resmat ) ;
2011-06-06 16:52:51 +08:00
static bool convertYUV420i2BGR ( int width , int height , const unsigned char * yuv , cv : : Mat & resmat , bool inRGBorder , bool withAlpha ) ;
2011-04-06 22:31:03 +08:00
2011-04-18 16:50:24 +08:00
friend class HighguiAndroidCameraActivity ;
2011-04-06 22:31:03 +08:00
} ;
class HighguiAndroidCameraActivity : public CameraActivity
{
2011-04-18 16:50:24 +08:00
public :
2011-04-06 22:31:03 +08:00
HighguiAndroidCameraActivity ( CvCapture_Android * capture )
{
2011-04-18 16:50:24 +08:00
m_capture = capture ;
m_framesReceived = 0 ;
2011-04-06 22:31:03 +08:00
}
virtual bool onFrameBuffer ( void * buffer , int bufferSize )
{
2011-04-18 16:50:24 +08:00
if ( isConnected ( ) & & buffer ! = 0 & & bufferSize > 0 )
{
m_framesReceived + + ;
2011-04-30 03:03:41 +08:00
if ( m_capture - > m_waitingNextFrame | | m_capture - > m_shouldAutoGrab )
2011-04-18 16:50:24 +08:00
{
pthread_mutex_lock ( & m_capture - > m_nextFrameMutex ) ;
2011-04-30 03:03:41 +08:00
m_capture - > setFrame ( buffer , bufferSize ) ;
2011-04-18 16:50:24 +08:00
pthread_cond_broadcast ( & m_capture - > m_nextFrameCond ) ;
pthread_mutex_unlock ( & m_capture - > m_nextFrameMutex ) ;
}
return true ;
}
return false ;
2011-04-06 22:31:03 +08:00
}
void LogFramesRate ( )
{
2011-04-18 16:50:24 +08:00
LOGI ( " FRAMES received: %d grabbed: %d " , m_framesReceived , m_capture - > m_framesGrabbed ) ;
2011-04-06 22:31:03 +08:00
}
2011-04-18 16:50:24 +08:00
private :
2011-04-06 22:31:03 +08:00
CvCapture_Android * m_capture ;
int m_framesReceived ;
} ;
IplImage * CvCapture_Android : : OutputMap : : getIplImagePtr ( )
{
if ( mat . empty ( ) )
return 0 ;
iplHeader = IplImage ( mat ) ;
return & iplHeader ;
}
2011-04-18 16:50:24 +08:00
CvCapture_Android : : CvCapture_Android ( int cameraId )
2011-04-06 22:31:03 +08:00
{
2011-04-18 16:50:24 +08:00
//defaults
m_width = 0 ;
m_height = 0 ;
m_activity = 0 ;
m_isOpened = false ;
m_frameYUV420i = 0 ;
m_frameYUV420inext = 0 ;
m_hasGray = false ;
m_hasColor = false ;
2011-04-30 03:03:41 +08:00
m_dataState = CVCAPTURE_ANDROID_STATE_NO_FRAME ;
2011-04-18 16:50:24 +08:00
m_waitingNextFrame = false ;
2011-04-30 03:03:41 +08:00
m_shouldAutoGrab = false ;
2011-04-18 16:50:24 +08:00
m_framesGrabbed = 0 ;
m_CameraParamsChanged = false ;
//try connect to camera
m_activity = new HighguiAndroidCameraActivity ( this ) ;
if ( m_activity = = 0 ) return ;
pthread_mutex_init ( & m_nextFrameMutex , NULL ) ;
pthread_cond_init ( & m_nextFrameCond , NULL ) ;
CameraActivity : : ErrorCode errcode = m_activity - > connect ( cameraId ) ;
if ( errcode = = CameraActivity : : NO_ERROR )
m_isOpened = true ;
else
{
LOGE ( " Native_camera returned opening error: %d " , errcode ) ;
delete m_activity ;
m_activity = 0 ;
}
2011-04-06 22:31:03 +08:00
}
2011-04-18 16:50:24 +08:00
bool CvCapture_Android : : isOpened ( ) const
2011-04-06 22:31:03 +08:00
{
2011-04-18 16:50:24 +08:00
return m_isOpened ;
2011-04-06 22:31:03 +08:00
}
CvCapture_Android : : ~ CvCapture_Android ( )
{
2011-04-18 16:50:24 +08:00
if ( m_activity )
{
( ( HighguiAndroidCameraActivity * ) m_activity ) - > LogFramesRate ( ) ;
2011-04-30 03:03:41 +08:00
pthread_mutex_lock ( & m_nextFrameMutex ) ;
unsigned char * tmp1 = m_frameYUV420i ;
unsigned char * tmp2 = m_frameYUV420inext ;
2011-04-18 16:50:24 +08:00
m_frameYUV420i = 0 ;
m_frameYUV420inext = 0 ;
2011-04-30 03:03:41 +08:00
delete tmp1 ;
delete tmp2 ;
2011-04-18 16:50:24 +08:00
2011-04-30 03:03:41 +08:00
m_dataState = CVCAPTURE_ANDROID_STATE_NO_FRAME ;
2011-04-29 22:20:24 +08:00
pthread_cond_broadcast ( & m_nextFrameCond ) ;
2011-04-30 03:03:41 +08:00
2011-04-29 22:20:24 +08:00
pthread_mutex_unlock ( & m_nextFrameMutex ) ;
2011-04-30 03:03:41 +08:00
//m_activity->disconnect() will be automatically called inside destructor;
2011-04-29 22:20:24 +08:00
delete m_activity ;
m_activity = 0 ;
2011-04-18 16:50:24 +08:00
pthread_mutex_destroy ( & m_nextFrameMutex ) ;
pthread_cond_destroy ( & m_nextFrameCond ) ;
}
2011-04-06 22:31:03 +08:00
}
double CvCapture_Android : : getProperty ( int propIdx )
{
2011-04-29 22:20:24 +08:00
switch ( propIdx )
2011-04-18 16:50:24 +08:00
{
2011-04-06 22:31:03 +08:00
case CV_CAP_PROP_FRAME_WIDTH :
2011-04-18 16:50:24 +08:00
return ( double ) m_activity - > getFrameWidth ( ) ;
2011-04-06 22:31:03 +08:00
case CV_CAP_PROP_FRAME_HEIGHT :
2011-04-18 16:50:24 +08:00
return ( double ) m_activity - > getFrameHeight ( ) ;
2011-05-10 20:29:43 +08:00
case CV_CAP_PROP_SUPPORTED_PREVIEW_SIZES_STRING :
return ( double ) m_activity - > getProperty ( ANDROID_CAMERA_PROPERTY_SUPPORTED_PREVIEW_SIZES_STRING ) ;
2011-04-06 22:31:03 +08:00
default :
2011-04-18 16:50:24 +08:00
CV_Error ( CV_StsOutOfRange , " Failed attempt to GET unsupported camera property. " ) ;
break ;
}
return - 1.0 ;
2011-04-06 22:31:03 +08:00
}
bool CvCapture_Android : : setProperty ( int propIdx , double propValue )
{
2011-04-18 16:50:24 +08:00
bool res = false ;
if ( isOpened ( ) )
2011-04-06 22:31:03 +08:00
{
2011-04-18 16:50:24 +08:00
switch ( propIdx )
{
case CV_CAP_PROP_FRAME_WIDTH :
m_activity - > setProperty ( ANDROID_CAMERA_PROPERTY_FRAMEWIDTH , propValue ) ;
break ;
case CV_CAP_PROP_FRAME_HEIGHT :
m_activity - > setProperty ( ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT , propValue ) ;
break ;
2011-04-30 03:03:41 +08:00
case CV_CAP_PROP_AUTOGRAB :
m_shouldAutoGrab = ( propValue ! = 0 ) ;
break ;
2011-04-18 16:50:24 +08:00
default :
CV_Error ( CV_StsOutOfRange , " Failed attempt to SET unsupported camera property. " ) ;
2011-04-30 03:03:41 +08:00
return false ;
2011-04-18 16:50:24 +08:00
}
2011-04-30 03:03:41 +08:00
if ( propIdx ! = CV_CAP_PROP_AUTOGRAB ) { // property for highgui class CvCapture_Android only
m_CameraParamsChanged = true ;
}
res = true ;
2011-04-06 22:31:03 +08:00
}
2011-04-18 16:50:24 +08:00
return res ;
2011-04-06 22:31:03 +08:00
}
bool CvCapture_Android : : grabFrame ( )
{
2011-05-10 20:29:43 +08:00
if ( ! isOpened ( ) ) {
LOGE ( " CvCapture_Android::grabFrame(): camera is not opened " ) ;
return false ;
}
2011-04-18 16:50:24 +08:00
2011-04-30 03:03:41 +08:00
bool res = false ;
2011-04-18 16:50:24 +08:00
pthread_mutex_lock ( & m_nextFrameMutex ) ;
if ( m_CameraParamsChanged )
{
m_activity - > applyProperties ( ) ;
m_CameraParamsChanged = false ;
2011-04-30 03:03:41 +08:00
m_dataState = CVCAPTURE_ANDROID_STATE_NO_FRAME ; //we will wait new frame
}
if ( m_dataState ! = CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED ) {
m_waitingNextFrame = true ;
pthread_cond_wait ( & m_nextFrameCond , & m_nextFrameMutex ) ;
2011-04-18 16:50:24 +08:00
}
2011-04-30 03:03:41 +08:00
if ( m_dataState = = CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED ) {
2011-06-09 20:04:10 +08:00
//LOGD("CvCapture_Android::grabFrame: get new frame");
2011-04-30 03:03:41 +08:00
//swap current and new frames
unsigned char * tmp = m_frameYUV420i ;
m_frameYUV420i = m_frameYUV420inext ;
m_frameYUV420inext = tmp ;
//discard cached frames
m_hasGray = false ;
m_hasColor = false ;
m_dataState = CVCAPTURE_ANDROID_STATE_HAS_FRAME_GRABBED ;
m_framesGrabbed + + ;
res = true ;
2011-05-10 20:29:43 +08:00
} else {
LOGE ( " CvCapture_Android::grabFrame: NO new frame " ) ;
2011-04-30 03:03:41 +08:00
}
2011-05-10 20:29:43 +08:00
2011-04-30 03:03:41 +08:00
int res_unlock = pthread_mutex_unlock ( & m_nextFrameMutex ) ;
if ( res_unlock ) {
LOGE ( " Error in CvCapture_Android::grabFrame: pthread_mutex_unlock returned %d --- probably, this object has been destroyed " , res_unlock ) ;
2011-04-29 22:20:24 +08:00
return false ;
}
2011-04-18 16:50:24 +08:00
2011-04-30 03:03:41 +08:00
return res ;
2011-04-18 16:50:24 +08:00
}
IplImage * CvCapture_Android : : retrieveFrame ( int outputType )
{
2011-04-30 03:03:41 +08:00
IplImage * image = NULL ;
unsigned char * current_frameYUV420i = m_frameYUV420i ;
//Attention! all the operations in this function below should occupy less time than the period between two frames from camera
if ( NULL ! = current_frameYUV420i )
2011-04-18 16:50:24 +08:00
{
switch ( outputType )
{
case CV_CAP_ANDROID_GREY_FRAME :
if ( ! m_hasGray )
2011-04-30 03:03:41 +08:00
if ( ! ( m_hasGray = convertYUV420i2Grey ( m_width , m_height , current_frameYUV420i , m_frameGray . mat ) ) )
return NULL ;
2011-04-18 16:50:24 +08:00
image = m_frameGray . getIplImagePtr ( ) ;
break ;
2011-06-06 16:52:51 +08:00
case CV_CAP_ANDROID_COLOR_FRAME_BGR : case CV_CAP_ANDROID_COLOR_FRAME_RGB :
if ( ! m_hasColor )
if ( ! ( m_hasColor = convertYUV420i2BGR ( m_width , m_height , current_frameYUV420i , m_frameColor . mat , outputType = = CV_CAP_ANDROID_COLOR_FRAME_RGB , false ) ) )
return NULL ;
image = m_frameColor . getIplImagePtr ( ) ;
break ;
case CV_CAP_ANDROID_COLOR_FRAME_BGRA : case CV_CAP_ANDROID_COLOR_FRAME_RGBA :
2011-04-29 22:20:24 +08:00
if ( ! m_hasColor )
2011-06-06 16:52:51 +08:00
if ( ! ( m_hasColor = convertYUV420i2BGR ( m_width , m_height , current_frameYUV420i , m_frameColor . mat , outputType = = CV_CAP_ANDROID_COLOR_FRAME_RGBA , true ) ) )
2011-04-30 03:03:41 +08:00
return NULL ;
2011-04-29 22:20:24 +08:00
image = m_frameColor . getIplImagePtr ( ) ;
break ;
2011-04-18 16:50:24 +08:00
default :
LOGE ( " Unsupported frame output format: %d " , outputType ) ;
CV_Error ( CV_StsOutOfRange , " Output frame format is not supported. " ) ;
2011-04-30 03:03:41 +08:00
image = NULL ;
2011-04-18 16:50:24 +08:00
break ;
}
}
return image ;
2011-04-06 22:31:03 +08:00
}
2011-04-30 03:03:41 +08:00
//Attention: this method should be called inside pthread_mutex_lock(m_nextFrameMutex) only
2011-04-18 16:50:24 +08:00
void CvCapture_Android : : setFrame ( const void * buffer , int bufferSize )
2011-04-06 22:31:03 +08:00
{
2011-04-18 16:50:24 +08:00
int width = m_activity - > getFrameWidth ( ) ;
int height = m_activity - > getFrameHeight ( ) ;
int expectedSize = ( width * height * 3 ) > > 1 ;
if ( expectedSize ! = bufferSize )
{
LOGE ( " ERROR reading YUV420i buffer: width=%d, height=%d, size=%d, receivedSize=%d " , width , height , expectedSize , bufferSize ) ;
return ;
}
2011-04-30 03:03:41 +08:00
//allocate memory if needed
2011-04-18 16:50:24 +08:00
prepareCacheForYUV420i ( width , height ) ;
2011-04-06 22:31:03 +08:00
2011-04-18 16:50:24 +08:00
//copy data
memcpy ( m_frameYUV420inext , buffer , bufferSize ) ;
2011-06-09 20:04:10 +08:00
//LOGD("CvCapture_Android::setFrame -- memcpy is done");
2011-04-06 22:31:03 +08:00
2011-04-30 03:03:41 +08:00
#if 0 //moved this part of code into grabFrame
2011-04-18 16:50:24 +08:00
//swap current and new frames
unsigned char * tmp = m_frameYUV420i ;
m_frameYUV420i = m_frameYUV420inext ;
m_frameYUV420inext = tmp ;
2011-04-06 22:31:03 +08:00
2011-04-18 16:50:24 +08:00
//discard cached frames
m_hasGray = false ;
m_hasColor = false ;
2011-04-30 03:03:41 +08:00
# endif
2011-05-10 20:29:43 +08:00
m_dataState = CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED ;
m_waitingNextFrame = false ; //set flag that no more frames required at this moment
2011-04-06 22:31:03 +08:00
}
2011-05-10 20:29:43 +08:00
//Attention: this method should be called inside pthread_mutex_lock(m_nextFrameMutex) only
2011-04-18 16:50:24 +08:00
void CvCapture_Android : : prepareCacheForYUV420i ( int width , int height )
2011-04-06 22:31:03 +08:00
{
2011-04-18 16:50:24 +08:00
if ( width ! = m_width | | height ! = m_height )
2011-04-06 22:31:03 +08:00
{
2011-05-10 20:29:43 +08:00
LOGD ( " CvCapture_Android::prepareCacheForYUV420i: Changing size of buffers: from width=%d height=%d to width=%d height=%d " , m_width , m_height , width , height ) ;
2011-04-18 16:50:24 +08:00
m_width = width ;
m_height = height ;
unsigned char * tmp = m_frameYUV420inext ;
m_frameYUV420inext = new unsigned char [ width * height * 3 / 2 ] ;
2011-05-10 20:29:43 +08:00
if ( tmp ! = NULL ) {
delete [ ] tmp ;
}
2011-04-18 16:50:24 +08:00
tmp = m_frameYUV420i ;
m_frameYUV420i = new unsigned char [ width * height * 3 / 2 ] ;
2011-05-10 20:29:43 +08:00
if ( tmp ! = NULL ) {
delete [ ] tmp ;
}
2011-04-06 22:31:03 +08:00
}
}
2011-04-18 16:50:24 +08:00
bool CvCapture_Android : : convertYUV420i2Grey ( int width , int height , const unsigned char * yuv , cv : : Mat & resmat )
{
if ( yuv = = 0 ) return false ;
2011-06-06 16:52:51 +08:00
# define ALWAYS_COPY_GRAY 0
# if ALWAYS_COPY_GRAY
2011-04-29 22:20:24 +08:00
resmat . create ( height , width , CV_8UC1 ) ;
2011-04-18 16:50:24 +08:00
unsigned char * matBuff = resmat . ptr < unsigned char > ( 0 ) ;
memcpy ( matBuff , yuv , width * height ) ;
2011-06-06 16:52:51 +08:00
# else
resmat = cv : : Mat ( height , width , CV_8UC1 , ( void * ) yuv ) ;
# endif
2011-04-18 16:50:24 +08:00
return ! resmat . empty ( ) ;
2011-04-06 22:31:03 +08:00
}
2011-06-06 16:52:51 +08:00
bool CvCapture_Android : : convertYUV420i2BGR ( int width , int height , const unsigned char * yuv , cv : : Mat & resmat , bool inRGBorder , bool withAlpha )
2011-04-29 22:20:24 +08:00
{
if ( yuv = = 0 ) return false ;
CV_Assert ( width % 2 = = 0 & & height % 2 = = 0 ) ;
2011-06-06 16:52:51 +08:00
cv : : Mat src ( height * 3 / 2 , width , CV_8UC1 , ( void * ) yuv ) ;
2011-04-29 22:20:24 +08:00
2011-06-06 16:52:51 +08:00
cv : : cvtColor ( src , resmat , inRGBorder ? CV_YUV420i2RGB : CV_YUV420i2BGR , withAlpha ? 4 : 3 ) ;
2011-04-06 22:31:03 +08:00
2011-04-18 16:50:24 +08:00
return ! resmat . empty ( ) ;
2011-04-06 22:31:03 +08:00
}
2011-04-18 16:50:24 +08:00
CvCapture * cvCreateCameraCapture_Android ( int cameraId )
2011-04-06 22:31:03 +08:00
{
2011-04-18 16:50:24 +08:00
CvCapture_Android * capture = new CvCapture_Android ( cameraId ) ;
2011-04-06 22:31:03 +08:00
if ( capture - > isOpened ( ) )
return capture ;
delete capture ;
return 0 ;
}
# endif