Changed algorithm of Android camera synchronisation --- added "auto-grab" mode.
authorLeonid Beynenson <no@email>
Fri, 29 Apr 2011 19:03:41 +0000 (19:03 +0000)
committerLeonid Beynenson <no@email>
Fri, 29 Apr 2011 19:03:41 +0000 (19:03 +0000)
modules/highgui/include/opencv2/highgui/highgui_c.h
modules/highgui/src/cap_android.cpp

index d9ed6e5..48d487f 100644 (file)
@@ -353,6 +353,7 @@ enum
     CV_CAP_PROP_TRIGGER_DELAY =25,
     CV_CAP_PROP_WHITE_BALANCE_RED_V =26,
     CV_CAP_PROP_MAX_DC1394    =27,
+    CV_CAP_PROP_AUTOGRAB      =1024, // property for highgui class CvCapture_Android only
        // OpenNI map generators
     CV_CAP_OPENNI_DEPTH_GENERATOR = 0,
     CV_CAP_OPENNI_IMAGE_GENERATOR = 1 << 31,
index aa317bf..5dbfe90 100644 (file)
@@ -102,10 +102,18 @@ private:
     bool m_hasGray;
     bool m_hasColor;
 
+    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;
+
     //synchronization
     pthread_mutex_t m_nextFrameMutex;
     pthread_cond_t m_nextFrameCond;
     volatile bool m_waitingNextFrame;
+    volatile bool m_shouldAutoGrab;
 
     void prepareCacheForYUV420i(int width, int height);
     static bool convertYUV420i2Grey(int width, int height, const unsigned char* yuv, cv::Mat& resmat);
@@ -131,11 +139,15 @@ public:
         if(isConnected() && buffer != 0 && bufferSize > 0)
         {
             m_framesReceived++;
-            if (m_capture->m_waitingNextFrame)
+            if (m_capture->m_waitingNextFrame || m_capture->m_shouldAutoGrab)
             {
-                m_capture->setFrame(buffer, bufferSize);
                 pthread_mutex_lock(&m_capture->m_nextFrameMutex);
+
+                m_capture->setFrame(buffer, bufferSize);
+
+               m_capture->m_dataState = CvCapture_Android::CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED;
                 m_capture->m_waitingNextFrame = false;//set flag that no more frames required at this moment
+
                 pthread_cond_broadcast(&m_capture->m_nextFrameCond);
                 pthread_mutex_unlock(&m_capture->m_nextFrameMutex);
             }
@@ -174,7 +186,9 @@ CvCapture_Android::CvCapture_Android(int cameraId)
     m_frameYUV420inext    = 0;
     m_hasGray             = false;
     m_hasColor            = false;
+    m_dataState           = CVCAPTURE_ANDROID_STATE_NO_FRAME;
     m_waitingNextFrame    = false;
+    m_shouldAutoGrab      = false;
     m_framesGrabbed       = 0;
     m_CameraParamsChanged = false;
 
@@ -209,16 +223,22 @@ CvCapture_Android::~CvCapture_Android()
     {
         ((HighguiAndroidCameraActivity*)m_activity)->LogFramesRate();
 
-        //m_activity->disconnect() will be automatically called inside destructor;
-        delete m_frameYUV420i;
-        delete m_frameYUV420inext;
+
+       pthread_mutex_lock(&m_nextFrameMutex);
+
+       unsigned char *tmp1=m_frameYUV420i;
+       unsigned char *tmp2=m_frameYUV420inext;
         m_frameYUV420i = 0;
         m_frameYUV420inext = 0;
+        delete tmp1;
+        delete tmp2;
 
-       pthread_mutex_lock(&m_nextFrameMutex);
+       m_dataState=CVCAPTURE_ANDROID_STATE_NO_FRAME;
        pthread_cond_broadcast(&m_nextFrameCond);
+
        pthread_mutex_unlock(&m_nextFrameMutex);
 
+        //m_activity->disconnect() will be automatically called inside destructor;
         delete m_activity;
         m_activity = 0;
 
@@ -255,11 +275,20 @@ bool CvCapture_Android::setProperty( int propIdx, double propValue )
         case CV_CAP_PROP_FRAME_HEIGHT:
             m_activity->setProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT, propValue);
             break;
+
+        case CV_CAP_PROP_AUTOGRAB:
+           m_shouldAutoGrab=(propValue != 0);
+            break;
+
         default:
             CV_Error( CV_StsOutOfRange, "Failed attempt to SET unsupported camera property." );
-            break;
+           return false;
         }
-        m_CameraParamsChanged = true;
+
+       if (propIdx != CV_CAP_PROP_AUTOGRAB) {// property for highgui class CvCapture_Android only
+               m_CameraParamsChanged = true;
+       }
+       res = true;
     }
 
     return res;
@@ -270,59 +299,84 @@ bool CvCapture_Android::grabFrame()
     if( !isOpened() )
         return false;
 
+    bool res=false;
     pthread_mutex_lock(&m_nextFrameMutex);
     if (m_CameraParamsChanged)
     {
         m_activity->applyProperties();
         m_CameraParamsChanged = false;
+       m_dataState= CVCAPTURE_ANDROID_STATE_NO_FRAME;//we will wait new frame
     }
-    m_waitingNextFrame = true;
-    pthread_cond_wait(&m_nextFrameCond, &m_nextFrameMutex);
-    int res=pthread_mutex_unlock(&m_nextFrameMutex);
-    if (res) {
-           LOGE("Error in CvCapture_Android::grabFrame: pthread_mutex_unlock returned %d --- probably, this object has been destroyed", res);
+
+    if (m_dataState!=CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED) {
+           m_waitingNextFrame = true;
+           pthread_cond_wait(&m_nextFrameCond, &m_nextFrameMutex);
+    }
+
+    if (m_dataState == CVCAPTURE_ANDROID_STATE_HAS_NEW_FRAME_UNGRABBED) {
+           //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;
+    }
+
+    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);
            return false;
     }
 
-    m_framesGrabbed++;
-    return true;
+    return res;
 }
 
 IplImage* CvCapture_Android::retrieveFrame( int outputType )
 {
-    IplImage* image = 0;
-    if (0 != m_frameYUV420i)
+    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)
     {
         switch(outputType)
         {
         case CV_CAP_ANDROID_COLOR_FRAME:
             if (!m_hasColor)
-                if (!(m_hasColor = convertYUV420i2BGR888(m_width, m_height, m_frameYUV420i, m_frameColor.mat)))
-                    return 0;
+                if (!(m_hasColor = convertYUV420i2BGR888(m_width, m_height, current_frameYUV420i, m_frameColor.mat)))
+                    return NULL;
             image = m_frameColor.getIplImagePtr();
             break;
         case CV_CAP_ANDROID_GREY_FRAME:
             if (!m_hasGray)
-                if (!(m_hasGray = convertYUV420i2Grey(m_width, m_height, m_frameYUV420i, m_frameGray.mat)))
-                    return 0;
+                if (!(m_hasGray = convertYUV420i2Grey(m_width, m_height, current_frameYUV420i, m_frameGray.mat)))
+                    return NULL;
             image = m_frameGray.getIplImagePtr();
             break;
         case CV_CAP_ANDROID_COLOR_FRAME_RGB:
             if (!m_hasColor)
-                if (!(m_hasColor = convertYUV420i2RGB888(m_width, m_height, m_frameYUV420i, m_frameColor.mat)))
-                    return 0;
+                if (!(m_hasColor = convertYUV420i2RGB888(m_width, m_height, current_frameYUV420i, m_frameColor.mat)))
+                    return NULL;
             image = m_frameColor.getIplImagePtr();
             break;
         default:
             LOGE("Unsupported frame output format: %d", outputType);
             CV_Error( CV_StsOutOfRange, "Output frame format is not supported." );
-            image = 0;
+            image = NULL;
             break;
         }
     }
     return image;
 }
 
+//Attention: this method should be called inside pthread_mutex_lock(m_nextFrameMutex) only
 void CvCapture_Android::setFrame(const void* buffer, int bufferSize)
 {
     int width = m_activity->getFrameWidth();
@@ -335,12 +389,13 @@ void CvCapture_Android::setFrame(const void* buffer, int bufferSize)
         return;
     }
 
-    //allocate memery if needed
+    //allocate memory if needed
     prepareCacheForYUV420i(width, height);
 
     //copy data
     memcpy(m_frameYUV420inext, buffer, bufferSize);
 
+#if 0 //moved this part of code into grabFrame
     //swap current and new frames
     unsigned char* tmp = m_frameYUV420i;
     m_frameYUV420i = m_frameYUV420inext;
@@ -349,6 +404,7 @@ void CvCapture_Android::setFrame(const void* buffer, int bufferSize)
     //discard cached frames
     m_hasGray = false;
     m_hasColor = false;
+#endif
 }
 
 void CvCapture_Android::prepareCacheForYUV420i(int width, int height)