universal7580: camera: create a worker thread to handle all callbacks to avoid hard...
authorDanny Wood <danwood76@gmail.com>
Fri, 21 Jun 2019 09:46:36 +0000 (10:46 +0100)
committerJan Altensen <info@stricted.net>
Fri, 16 Aug 2019 21:18:47 +0000 (23:18 +0200)
Change-Id: I31e5b4e874d32ffdceb04aaf5cdd4b805a34f26e

camera/Android.mk
camera/CallbackWorkerThread.cpp [new file with mode: 0644]
camera/CallbackWorkerThread.h [new file with mode: 0644]
camera/Camera2Wrapper.cpp

index 95d1ee24a4c30a8edd81006f4d88a4e2cf783677..4d9cf0b40c12cd941ce17206c39754742a53fc3b 100644 (file)
@@ -3,7 +3,8 @@ include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
     CameraWrapper.cpp \
-    Camera2Wrapper.cpp
+    Camera2Wrapper.cpp \
+    CallbackWorkerThread.cpp
 
 LOCAL_STATIC_LIBRARIES := libbase libarect
 LOCAL_SHARED_LIBRARIES := \
diff --git a/camera/CallbackWorkerThread.cpp b/camera/CallbackWorkerThread.cpp
new file mode 100644 (file)
index 0000000..676e6bd
--- /dev/null
@@ -0,0 +1,219 @@
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "Camera2WrapperCbThread"
+
+#include "CallbackWorkerThread.h"
+#include <iostream>
+#include <cutils/log.h>
+
+using namespace std;
+
+#define MSG_EXIT_THREAD         1
+#define MSG_EXECUTE_CALLBACK      2
+#define MSG_UPDATE_CALLBACKS    3
+
+struct ThreadMsg
+{
+    ThreadMsg(int i, const void* m, long long ts) { id = i; msg = m; CallerTS = ts; }
+    int id;
+    const void* msg;
+    long long CallerTS;
+};
+
+CallbackWorkerThread::CallbackWorkerThread() : m_thread(0) {
+}
+
+CallbackWorkerThread::~CallbackWorkerThread() {
+    ExitThread();
+}
+
+bool CallbackWorkerThread::CreateThread() {
+    if (!m_thread)
+        m_thread = new thread(&CallbackWorkerThread::Process, this);
+    return true;
+}
+
+void CallbackWorkerThread::ExitThread() {
+    if (!m_thread)
+        return;
+
+    /* Create the exit thread worker message */
+    ThreadMsg* threadMsg = new ThreadMsg(MSG_EXIT_THREAD, 0, GetTimestamp());
+
+    /* Add it to the message queue */
+    {
+        lock_guard<mutex> lock(m_mutex);
+        m_queue.push(threadMsg);
+        m_cv.notify_one();
+    }
+
+    /* Join the thread and then cleanup */
+    m_thread->join();
+    delete m_thread;
+    m_thread = 0;
+}
+
+void CallbackWorkerThread::AddCallback(const WorkerMessage* data) {
+    /* Assert that the thread exists */
+    assert(!m_thread);
+
+    /* Create a new worker thread message from the data */
+    ThreadMsg* threadMsg = new ThreadMsg(MSG_EXECUTE_CALLBACK, data, GetTimestamp());
+
+    /* Add it to our worker queue and notify the worker */
+    std::unique_lock<std::mutex> lk(m_mutex);
+    m_queue.push(threadMsg);
+    m_cv.notify_one();
+}
+
+void CallbackWorkerThread::SetCallbacks(const CallbackData* data) {
+    /* Assert that the thread exists */
+    assert(!m_thread);
+
+    /* Create a new worker thread message from the callback data */
+    ThreadMsg* threadMsg = new ThreadMsg(MSG_UPDATE_CALLBACKS, data, GetTimestamp());
+
+    /* Add it to our worker queue and notify the worker */
+    std::unique_lock<std::mutex> lk(m_mutex);
+    m_queue.push(threadMsg);
+    m_cv.notify_one();
+}
+
+void CallbackWorkerThread::ClearCallbacks() {
+    /* Assert that the thread exists */
+    assert(!m_thread);
+
+    /* Lock the mutex and clear the message queue */
+    std::unique_lock<std::mutex> lk(m_mutex);
+
+    ALOGV("%s: Clearing %i messages", __FUNCTION__, m_queue.size());
+
+    /* Whilst the queue is not empty */
+    while (!m_queue.empty()) {
+        /* Pop the message from the queue and delete the allocated data */
+        ThreadMsg* msg = m_queue.front();
+        m_queue.pop();
+        delete msg;
+    }
+
+    m_cv.notify_one();
+}
+
+void CallbackWorkerThread::Process() {
+    camera_notify_callback UserNotifyCb = NULL;
+    camera_data_callback UserDataCb = NULL;
+
+    while (1) {
+        ThreadMsg* msg = 0;
+        {
+            /* Wait for a message to be added to the queue */
+            std::unique_lock<std::mutex> lk(m_mutex);
+            while (m_queue.empty())
+                m_cv.wait(lk);
+
+            if (m_queue.empty())
+                continue;
+
+            msg = m_queue.front();
+            m_queue.pop();
+        }
+
+        switch (msg->id) {
+            case MSG_EXECUTE_CALLBACK:
+            {
+                /* Assert that we have a valid message */
+                assert(msg->msg != NULL);
+
+                /* Cast the the ThreadMsg void* data back to a WorkerMessage* */
+                const WorkerMessage* userData = static_cast<const WorkerMessage*>(msg->msg);
+
+                /* If the callback is not stale (newer than 5mS) */
+                if(GetTimestamp() - msg->CallerTS < 5) {
+                    /* If the callback type is set to notifycb */
+                    if(userData->CbType == CB_TYPE_NOTIFY) {
+                        /* Execute the users notify callback if it is valid */
+                        if(UserNotifyCb != NULL) {
+                            ALOGV("%s: UserNotifyCb: %i %i %i %p", __FUNCTION__, userData->msg_type, userData->ext1, userData->ext2, userData->user);
+                            UserNotifyCb(userData->msg_type, userData->ext1, userData->ext2, userData->user);
+                        }
+                    } /* If the callback type is set to notifycb */
+                    else if(userData->CbType == CB_TYPE_DATA) {
+                        /* Execute the users data callback if it is valid */
+                        if(UserDataCb != NULL) {
+                            ALOGV("%s: UserDataCb: %i %p %i %p %p", __FUNCTION__, userData->msg_type, userData->data, userData->index, userData->metadata, userData->user);
+                            UserDataCb(userData->msg_type, userData->data, userData->index, userData->metadata, userData->user);
+                        }
+                    }
+                } else {
+                    /* If the callback type is set to notifycb */
+                    if(userData->CbType == CB_TYPE_NOTIFY) {
+                            ALOGV("%s: UserNotifyCb Stale: %llimS old", __FUNCTION__, GetTimestamp() - msg->CallerTS);
+                    } /* If the callback type is set to notifycb */
+                    else if(userData->CbType == CB_TYPE_DATA) {
+                            ALOGV("%s: UserDataCb Stale: %llimS old", __FUNCTION__, GetTimestamp() - msg->CallerTS);
+                    }
+                }
+
+                /* Cleanup allocated data */
+                delete userData;
+                delete msg;
+                break;
+            }
+
+            case MSG_UPDATE_CALLBACKS:
+            {
+                /* Assert that we have a valid message */
+                assert(msg->msg != NULL);
+
+                /* Cast the the ThreadMsg void* data back to a CallbackData* */
+                const CallbackData* callbackData = static_cast<const CallbackData*>(msg->msg);
+
+                ALOGV("%s: UpdateCallbacks", __FUNCTION__);
+
+                /* Copy the new callback pointers */
+                UserNotifyCb = callbackData->NewUserNotifyCb;
+                UserDataCb = callbackData->NewUserDataCb;
+
+                /* Cleanup allocated data */
+                delete callbackData;
+                delete msg;
+                break;
+            }
+
+            case MSG_EXIT_THREAD:
+            {
+                /* Delete current message */
+                delete msg;
+                /* Then delete all pending messages in the queue */
+                std::unique_lock<std::mutex> lk(m_mutex);
+                /* Whilst the queue is not empty */
+                while (!m_queue.empty()) {
+                    /* Pop the message from the queue and delete the allocated data */
+                    msg = m_queue.front();
+                    m_queue.pop();
+                    delete msg;
+                }
+
+                ALOGV("%s: Exit Thread", __FUNCTION__);
+                return;
+            }
+
+            default:
+                /* Error if we get here */
+                   assert(0);
+        }
+    }
+}
+
+
+/* based on current_timestamp() function from stack overflow:
+ * https://stackoverflow.com/questions/3756323/how-to-get-the-current-time-in-milliseconds-from-c-in-linux/17083824
+ */
+
+long long CallbackWorkerThread::GetTimestamp() {
+    struct timeval te;
+    gettimeofday(&te, NULL); // get current time
+    long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds
+    return milliseconds;
+}
+
diff --git a/camera/CallbackWorkerThread.h b/camera/CallbackWorkerThread.h
new file mode 100644 (file)
index 0000000..a304692
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef _THREAD_STD_H
+#define _THREAD_STD_H
+
+#include <thread>
+#include <queue>
+#include <mutex>
+#include <atomic>
+#include <condition_variable>
+
+#include <sys/time.h>
+
+#include <hardware/camera.h>
+#include <hardware/camera2.h>
+
+#define CB_TYPE_NONE    0
+#define CB_TYPE_NOTIFY  1
+#define CB_TYPE_DATA    2
+
+struct WorkerMessage {
+    /* Worker callback type */
+    int32_t CbType;
+
+    /* Callback data */
+    int32_t msg_type;
+    const camera_memory_t *data;
+    unsigned int index;
+    camera_frame_metadata_t *metadata;
+    void *user;
+    int32_t ext1;
+    int32_t ext2;
+};
+
+struct CallbackData {
+    camera_notify_callback NewUserNotifyCb;
+    camera_data_callback NewUserDataCb;
+};
+
+struct ThreadMsg;
+
+class CallbackWorkerThread {
+public:
+    CallbackWorkerThread();
+    ~CallbackWorkerThread();
+
+    /* Creates our worker, returns true on success */
+    bool CreateThread();
+
+    /* Exits the worker thread */
+    void ExitThread();
+
+    /* Sends a new callback to our worker thread */
+    void AddCallback(const WorkerMessage* data);
+
+    /* Sets the callback function pointers for our worker to call */
+    void SetCallbacks(const CallbackData* data);
+
+    /* Clears the worker message queue */
+    void ClearCallbacks(void);
+
+private:
+    CallbackWorkerThread(const CallbackWorkerThread&);
+    CallbackWorkerThread& operator=(const CallbackWorkerThread&);
+
+    long long GetTimestamp();
+
+    /* Entry point for the worker thread */
+    void Process();
+
+    std::thread* m_thread;
+    std::queue<ThreadMsg*> m_queue;
+    std::mutex m_mutex;
+    std::condition_variable m_cv;
+    const char* m_name;
+};
+
+#endif
+
index 1eead7ce2be0539190b776cb7d12c34abb1be94d..d4d857300dc08d3d5d0d5e76c7caa77dfa585321 100644 (file)
 #include <cutils/log.h>
 
 #include <unistd.h>
+#include <stdatomic.h>
 
 #include "CameraWrapper.h"
 #include "Camera2Wrapper.h"
+#include "CallbackWorkerThread.h"
+
+CallbackWorkerThread cbThread;
 
 #include <sys/time.h>
 
@@ -142,17 +146,62 @@ static int camera2_set_preview_window(struct camera_device * device,
     return rc;
 }
 
-uint8_t BlockNotifyCb = 0;
-camera_notify_callback UserNotifyCb = NULL;
+atomic_int BlockCbs;
 
 void WrappedNotifyCb (int32_t msg_type, int32_t ext1, int32_t ext2, void *user) {
     ALOGV("%s->In", __FUNCTION__);
 
-    /* If the notify callback is valid and we are not blocking callbacks */
-    if((UserNotifyCb != NULL) && (BlockNotifyCb == 0)) {
-        ALOGV("%s->Calling UserNotifyCb", __FUNCTION__);
-        UserNotifyCb(msg_type, ext1, ext2, user);
+    /* Print a log message and return if we currently blocking adding callbacks */
+    if(BlockCbs == 1) {
+        ALOGV("%s->BlockCbs == 1", __FUNCTION__);
+        return;
     }
+
+    /* Create message to send to the callback worker */
+    WorkerMessage* newWorkerMessage = new WorkerMessage();
+    newWorkerMessage->CbType = CB_TYPE_NOTIFY;
+
+    /* Copy the callback data to our worker message */
+    newWorkerMessage->msg_type = msg_type;
+    newWorkerMessage->ext1 = ext1;
+    newWorkerMessage->ext2 = ext2;
+    newWorkerMessage->user = user;
+
+    /* Post the message to the callback worker */
+    cbThread.AddCallback(newWorkerMessage);
+
+    /* 5mS delay to slow down the camera hal thread */
+    usleep(5000);
+    ALOGV("%s->Out", __FUNCTION__);
+}
+
+void WrappedDataCb (int32_t msg_type, const camera_memory_t *data, unsigned int index,
+        camera_frame_metadata_t *metadata, void *user) {
+    ALOGV("%s->In, %i, %u", __FUNCTION__, msg_type, index);
+
+    /* Print a log message and return if we currently blocking adding callbacks */
+    if(BlockCbs == 1) {
+        ALOGV("%s->BlockCbs == 1", __FUNCTION__);
+        return;
+    }
+
+    /* Create message to send to the callback worker */
+    WorkerMessage* newWorkerMessage = new WorkerMessage();
+    newWorkerMessage->CbType = CB_TYPE_DATA;
+
+    /* Copy the callback data to our worker message */
+    newWorkerMessage->msg_type = msg_type;
+    newWorkerMessage->data = data;
+    newWorkerMessage->index= index;
+    newWorkerMessage->metadata = metadata;
+    newWorkerMessage->user = user;
+
+    /* Post the message to the callback worker */
+    cbThread.AddCallback(newWorkerMessage);
+
+    /* 20mS delay to slow down the camera hal thread */
+    usleep(20000);
+    ALOGV("%s->Out", __FUNCTION__);
 }
 
 static void camera2_set_callbacks(struct camera_device * device,
@@ -167,11 +216,16 @@ static void camera2_set_callbacks(struct camera_device * device,
     if(!device)
         return;
 
-    /* Copy the notify_cb to our user pointer */
-    UserNotifyCb = notify_cb;
+    /* Create and populate a new callback data structure */
+    CallbackData* newCallbackData = new CallbackData();
+    newCallbackData->NewUserNotifyCb = notify_cb;
+    newCallbackData->NewUserDataCb = data_cb;
+
+    /* Send it to our worker thread */
+    cbThread.SetCallbacks(newCallbackData);
 
     /* Call the set_callbacks function substituting the notify callback with our wrapper */
-    VENDOR_CALL(device, set_callbacks, WrappedNotifyCb, data_cb, data_cb_timestamp, get_memory, user);
+    VENDOR_CALL(device, set_callbacks, WrappedNotifyCb, WrappedDataCb, data_cb_timestamp, get_memory, user);
 }
 
 static void camera2_enable_msg_type(struct camera_device * device, int32_t msg_type)
@@ -229,7 +283,16 @@ static void camera2_stop_preview(struct camera_device * device)
     if(!device)
         return;
 
+    /* Block queueing more callbacks */
+    BlockCbs = 1;
+
+    /* Clear the callback queue */
+    cbThread.ClearCallbacks();
+    /* Execute stop_preview */
     VENDOR_CALL(device, stop_preview);
+
+    /* Unblock queueing more callbacks */
+    BlockCbs = 0;
 }
 
 static int camera2_preview_enabled(struct camera_device * device)
@@ -309,6 +372,9 @@ static int camera2_auto_focus(struct camera_device * device)
     if(!device)
         return -EINVAL;
 
+    /* Clear the callback queue */
+    cbThread.ClearCallbacks();
+
     /* Call the auto_focus function */
     Ret = VENDOR_CALL(device, auto_focus);
 
@@ -326,6 +392,12 @@ static int camera2_cancel_auto_focus(struct camera_device * device)
     if(!device)
         return -EINVAL;
 
+    /* Block queueing more callbacks */
+    BlockCbs = 1;
+
+    /* Clear the callback queue */
+    cbThread.ClearCallbacks();
+
     /* Calculate the difference between our guard time and now */
     long long TimeDiff = CancelAFTimeGuard - current_timestamp();
     /* Post a log message and return success (skipping the call) if the diff is greater than 0 */
@@ -334,14 +406,11 @@ static int camera2_cancel_auto_focus(struct camera_device * device)
         return 0;
     }
 
-    /* Block notify callbacks whilst we are in cancel_auto_focus */
-    BlockNotifyCb = 1;
-
     /* No active time guard so call the vendor function */
     Ret = VENDOR_CALL(device, cancel_auto_focus);
 
-    /* Clear the block flag */
-    BlockNotifyCb = 0;
+    /* Unblock queueing more callbacks */
+    BlockCbs = 0;
 
     return Ret;
 }
@@ -459,7 +528,10 @@ static int camera2_device_close(hw_device_t* device)
 done:
     gPreviewWindow = 0;
     gPreviewStartDeferred = false;
-    UserNotifyCb = NULL;
+
+    /* Exit our callback dispatch thread */
+    cbThread.ExitThread();
+
     return ret;
 }
 
@@ -484,6 +556,10 @@ int camera2_device_open(const hw_module_t* module, const char* name,
 
     android::Mutex::Autolock lock(gCameraWrapperLock);
 
+    /* Create our callback dispatch thread */
+    cbThread.CreateThread();
+    BlockCbs = 0;
+
     ALOGV("%s", __FUNCTION__);
 
     if (name != NULL) {