Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4ef776ee authored by PythonLimited's avatar PythonLimited
Browse files

camera: add wrapper and build camera2 instead of snap

camera2 fixes the flash bugging when taking a photo

the wrapper isnt needed but ill keep it to debug cam later on R
parent e85c0404
Loading
Loading
Loading
Loading

camera/Android.bp

0 → 100644
+43 −0
Original line number Diff line number Diff line
cc_library_shared {
    name: "camera.universal8895",
    vendor: true,
    relative_install_path: "hw",

    srcs: [
        "CameraWrapper.cpp",
        "Camera2Wrapper.cpp",
        "Camera3Wrapper.cpp",
        "CallbackWorkerThread.cpp",
    ],

    export_shared_lib_headers: [
        "android.hardware.graphics.bufferqueue@1.0",
        "android.hardware.graphics.bufferqueue@2.0",
    ],

    generated_headers: [
        "android.hardware.graphics.bufferqueue@1.0_genc++_headers",
        "android.hardware.graphics.bufferqueue@2.0_genc++_headers",
    ],

    shared_libs: [
        "libhardware",
        "liblog",
        "libcamera_client",
        "libutils",
        "libcutils",
        "android.hidl.token@1.0-utils",
        "android.hardware.graphics.bufferqueue@1.0",
        "android.hardware.graphics.bufferqueue@2.0",
    ],

    include_dirs: [
        "frameworks/native/libs/nativewindow/include",
        "frameworks/native/libs/arect/include",
        "frameworks/av/media/ndk/include",
    ],

    header_libs: [
        "libnativebase_headers",
    ],
}
+259 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019, The LineageOS Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Camera HAL "CallbackWorkerThread" workaround description
 *
 * The camera HAL of the Exynos7580 (A3, A5, ... 2016) reguralry deadlocks when using
 * most camera apps, this happens a lot when using Google Camera but does occur
 * occasionally in Snap.
 *
 * The issue was tracked down to the way that Samsung operates their HAL, all operations
 * are run in multiple threads and the different callbacks are executed from one of these
 * threads.
 *
 * The deadlocks occur when the camera client executes a function like cancel_auto_focus
 * or stop_preview and a callback from the HAL occurs at around the same time. The HAL
 * waits for the callback functions to return before they finish processing the client
 * calls and the client stops processing callbacks until their calling function completes.
 *
 * You end up in a state when both the HAL and the client are waiting for the other
 * process to finish and so end up with a deadlock of the HAL/Client which only a reboot
 * can cure.
 *
 * This worker thread offloads the actual callbacks to another thread which allows the
 * HAL to finish the client calls and avoid the deadlock scenario.
 *
 */

#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 */
    ALOG_ASSERT(m_thread != NULL);

    /* 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 */
    ALOG_ASSERT(m_thread != NULL);

    /* 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 */
    ALOG_ASSERT(m_thread != NULL);

    /* 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 */
                ALOG_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 */
                ALOG_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 */
	            ALOG_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;
}
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019, The LineageOS Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#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
+633 −0

File added.

Preview size limit exceeded, changes collapsed.

+20 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017, The LineageOS Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <dlfcn.h>
#include <hardware/camera2.h>

int camera2_device_open(const hw_module_t* module, const char* name, hw_device_t** device);
Loading