input: add camera-api backend support 60/309760/4
authorInki Dae <inki.dae@samsung.com>
Tue, 16 Apr 2024 05:31:56 +0000 (14:31 +0900)
committerInki Dae <inki.dae@samsung.com>
Wed, 17 Apr 2024 00:43:07 +0000 (09:43 +0900)
Add camera-api backend and change default input camera backend
from OpenCV to CAMERA-API.

Change-Id: Icc8aab35cbf491923045a2f7f4205ff2124af3a7
Signed-off-by: Inki Dae <inki.dae@samsung.com>
input/CMakeLists.txt
input/backends/camera_api/CMakeLists.txt [new file with mode: 0644]
input/backends/camera_api/include/CameraApiBackend.h [new file with mode: 0644]
input/backends/camera_api/src/CameraApiBackend.cpp [new file with mode: 0644]
input/src/InputCamera.cpp
packaging/singleo.spec

index 87763ad838389fac462e91df52b0faa70545392b..2d85bab1636451f089ef167bd94fc1bb326986da 100644 (file)
@@ -5,7 +5,6 @@ SET(SINGLEO_SERVICE_SOURCE_FILES
     ${INPUT_DIRECTORY}/src/CameraBackendFactory.cpp
     ${INPUT_DIRECTORY}/src/InputCamera.cpp)
 
-# If input backend is OpenCV.
 # Ps. Do not change the order of above SET(SINGOEO_SERVICE_SOURCE_FILES) and below lines
 #     because SINGLEO_SERVICE_SOURCE_FILES should be built prior to specific backends
 #     to use CameraBackendFactory class.
@@ -13,4 +12,9 @@ IF (${USE_INPUT_OPENCV_BACKEND})
     INCLUDE(${INPUT_DIRECTORY}/backends/opencv/CMakeLists.txt)
 ENDIF()
 
+IF (${USE_INPUT_CAMERA_API_BACKEND})
+    INCLUDE(${INPUT_DIRECTORY}/backends/camera_api/CMakeLists.txt)
+    LIST(APPEND SERVICE_LIBRARY_LIST ${SERVICE_LIBRARY_LIST} capi-media-camera)
+ENDIF()
+
 LIST(APPEND INPUT_HEADER_LIST ${INPUT_HEADER_LIST} ${INPUT_DIRECTORY}/include ${INPUT_DIRECTORY}/backends/include)
diff --git a/input/backends/camera_api/CMakeLists.txt b/input/backends/camera_api/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9d2758f
--- /dev/null
@@ -0,0 +1,8 @@
+SET(INPUT_CAMERA_API_BACKEND_DIRECTORY ${INPUT_DIRECTORY}/backends/camera_api)
+
+SET(SINGLEO_SERVICE_SOURCE_FILES
+    ${SINGLEO_SERVICE_SOURCE_FILES}
+    ${INPUT_CAMERA_API_BACKEND_DIRECTORY}/src/CameraApiBackend.cpp
+)
+
+LIST(APPEND INPUT_HEADER_LIST /usr/include/media ${INPUT_HEADER_LIST} ${INPUT_CAMERA_API_BACKEND_DIRECTORY}/include)
diff --git a/input/backends/camera_api/include/CameraApiBackend.h b/input/backends/camera_api/include/CameraApiBackend.h
new file mode 100644 (file)
index 0000000..9add7e3
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __CAMERA_API_BACKEND_H__
+#define __CAMERA_API_BACKEND_H__
+
+#include <thread>
+#include <memory>
+#include <unordered_set>
+#include <camera.h>
+#include <mutex>
+#include <opencv2/opencv.hpp>
+#include <condition_variable>
+
+#include "ICameraBackend.h"
+#include "SingleoInputManager.h"
+#include "InputTypes.h"
+
+namespace singleo
+{
+namespace input
+{
+class CameraApiBackend : public ICameraBackend
+{
+private:
+       camera_h _camera;
+       InputServiceCallbackType _userCb;
+       void *_userData;
+       std::unique_ptr<std::thread> _thread_handle;
+       bool _exit_thread { false };
+       std::mutex _preview_mutex;
+       std::condition_variable _preview_event;
+       camera_pixel_format_e _defaultPixelFormat { CAMERA_PIXEL_FORMAT_I420 };
+       std::vector<camera_pixel_format_e> _validPixelFormat;
+       std::vector<cv::Size> _validResolution;
+
+       void threadLoop();
+       static void previewCb(camera_preview_data_s *data, void *user_data);
+       static bool previewFormatCb(camera_pixel_format_e format, void *user_data);
+       static bool previewResolutionCb(int width, int height, void *user_data);
+       static bool compareSizesDescending(const cv::Size &a, const cv::Size &b);
+
+public:
+       explicit CameraApiBackend(const CameraConfig &config);
+       virtual ~CameraApiBackend();
+
+       static std::unique_ptr<ICameraBackend> create(const CameraConfig &config)
+       {
+               return std::make_unique<CameraApiBackend>(config);
+       }
+
+       void setUserCb(const InputServiceCallbackType &userCb, void *user_data) override;
+       void configure() override;
+       void capture(BaseDataType &out_data) override;
+       void streamOn() override;
+       void streamOff() override;
+};
+
+} // input
+} // singleo
+
+#endif
\ No newline at end of file
diff --git a/input/backends/camera_api/src/CameraApiBackend.cpp b/input/backends/camera_api/src/CameraApiBackend.cpp
new file mode 100644 (file)
index 0000000..bc5a455
--- /dev/null
@@ -0,0 +1,210 @@
+/**
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "SingleoException.h"
+#include "CameraApiBackend.h"
+#include "SingleoLog.h"
+#include "CameraBackendFactory.h"
+
+using namespace std;
+using namespace singleo::exception;
+
+namespace singleo
+{
+namespace input
+{
+REGISTER_CAMERA_BACKEND(CameraApiBackend)
+
+CameraApiBackend::CameraApiBackend(const CameraConfig &config)
+{
+       int ret = camera_create(CAMERA_DEVICE_CAMERA0, &_camera);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_create failed. ret: %d", ret);
+               throw InvalidOperation("CameraApiBackend: camera_create failed.");
+       }
+}
+
+CameraApiBackend::~CameraApiBackend()
+{
+       camera_destroy(_camera);
+}
+
+void CameraApiBackend::setUserCb(const std::function<void(BaseDataType &data, void *user_data)> &userCb,
+                                                                void *user_data)
+{
+       _userCb = userCb;
+       _userData = user_data;
+
+       SINGLEO_LOGD("CameraApiBackend: user callback has been registered.");
+}
+
+void CameraApiBackend::previewCb(camera_preview_data_s *data, void *user_data)
+{
+       CameraApiBackend *context = static_cast<CameraApiBackend *>(user_data);
+
+       if (!context->_userCb)
+               return;
+
+       if (data->num_of_planes != 3 || data->format != CAMERA_PIXEL_FORMAT_I420) {
+               SINGLEO_LOGE("Number of planes or pixel format not supported.");
+               throw InvalidParameter("Number of planes or pixel format not supported.");
+       }
+
+       // TODO. consider for other plane type and pixel format support later.
+
+       cv::Mat cv_src(data->height + data->height / 2, data->width, CV_8UC1, data->data.triple_plane.y);
+       cv::Mat cv_dst;
+
+       cv::cvtColor(cv_src, cv_dst, cv::COLOR_YUV2BGR_I420);
+
+       ImageDataType data_type;
+
+       data_type.height = data->height;
+       data_type.width = data->width;
+       data_type.pixel_format = ImagePixelFormat::RGB888;
+       data_type.byte_per_pixel = cv_dst.channels();
+       data_type.ptr = cv_dst.data;
+
+       context->_userCb(data_type, context->_userData);
+       context->_preview_event.notify_one();
+}
+
+bool CameraApiBackend::previewFormatCb(camera_pixel_format_e format, void *user_data)
+{
+       CameraApiBackend *context = static_cast<CameraApiBackend *>(user_data);
+
+       static map<camera_pixel_format_e, string> pixel_format_table = {
+               { CAMERA_PIXEL_FORMAT_NV12, "NV12" },     { CAMERA_PIXEL_FORMAT_NV12T, "NV12T" },
+               { CAMERA_PIXEL_FORMAT_NV16, "NV16" },     { CAMERA_PIXEL_FORMAT_NV21, "NV21" },
+               { CAMERA_PIXEL_FORMAT_YUYV, "YUYV" },     { CAMERA_PIXEL_FORMAT_UYVY, "UYVY" },
+               { CAMERA_PIXEL_FORMAT_422P, "422P" },     { CAMERA_PIXEL_FORMAT_I420, "I420" },
+               { CAMERA_PIXEL_FORMAT_YV12, "YV12" },     { CAMERA_PIXEL_FORMAT_RGB565, "RGB565" },
+               { CAMERA_PIXEL_FORMAT_RGB888, "RGB888" }, { CAMERA_PIXEL_FORMAT_RGBA, "RGBA" },
+               { CAMERA_PIXEL_FORMAT_ARGB, "ARGB" },     { CAMERA_PIXEL_FORMAT_JPEG, "JPEG" },
+               { CAMERA_PIXEL_FORMAT_H264, "H264" },     { CAMERA_PIXEL_FORMAT_INVZ, "INVZ" }
+       };
+
+       SINGLEO_LOGD("supported pixel format = %s", pixel_format_table[format].c_str());
+       context->_validPixelFormat.push_back(format);
+
+       return true;
+}
+
+bool CameraApiBackend::compareSizesDescending(const cv::Size &a, const cv::Size &b)
+{
+       return a.area() > b.area();
+}
+
+bool CameraApiBackend::previewResolutionCb(int width, int height, void *user_data)
+{
+       auto context = static_cast<CameraApiBackend *>(user_data);
+
+       SINGLEO_LOGD("supported resolution : width = %d, height = %d", width, height);
+
+       context->_validResolution.push_back(cv::Size(width, height));
+       sort(context->_validResolution.begin(), context->_validResolution.end(), context->compareSizesDescending);
+
+       return true;
+}
+
+void CameraApiBackend::configure()
+{
+       int ret = camera_foreach_supported_preview_resolution(_camera, previewResolutionCb, this);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_foreach_supported_preview_resolution failed. ret: %d", ret);
+               throw InvalidOperation("CameraApiBackend: camera_foreach_supported_preview_resolution failed.");
+       }
+
+       SINGLEO_LOGD("Set camera resulition with width(%d) and height(%d) in default.", _validResolution[0].width,
+                                _validResolution[0].height);
+
+       // TODO. set user-given resolution with the option string of singleo API later.
+       ret = camera_set_preview_resolution(_camera, _validResolution[0].width, _validResolution[0].height);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_set_preview_resolution failed. ret: %d", ret);
+               throw InvalidOperation("CameraApiBackend: camera_set_preview_resolution failed.");
+       }
+
+       ret = camera_foreach_supported_preview_format(_camera, previewFormatCb, this);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_foreach_supported_preview_format failed. ret: %d", ret);
+               throw InvalidOperation("CameraApiBackend: camera_foreach_supported_preview_format failed.");
+       }
+
+       // TODO. read default pixel format from json configuration file later.
+       auto it = find(_validPixelFormat.begin(), _validPixelFormat.end(), _defaultPixelFormat);
+       if (it == _validPixelFormat.end()) {
+               SINGLEO_LOGE("Invalid pixel format type.");
+               throw InvalidOperation("CameraApiBackend: Invalid pixel format type.");
+       }
+
+       ret = camera_set_preview_format(_camera, _defaultPixelFormat);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_set_preview_format failed. ret: %d", ret);
+               throw InvalidOperation("CameraApiBackend: camera_set_preview_format failed.");
+       }
+
+       ret = camera_set_preview_cb(_camera, previewCb, this);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_set_preview_cb failed. ret: %d", ret);
+               throw InvalidOperation("CameraApiBackend: camera_set_preview_cb failed.");
+       }
+}
+
+void CameraApiBackend::capture(BaseDataType &out_data)
+{
+       throw InvalidOperation("Not supported yet");
+}
+
+void CameraApiBackend::threadLoop()
+{
+       SINGLEO_LOGD("CameraApiBackend: stream off.");
+
+       int ret = camera_start_preview(_camera);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_start_preview failed. ret: %d", ret);
+               throw InvalidOperation("camera_start_preview: camera_set_preview_format failed.");
+       }
+
+       unique_lock<mutex> lock(_preview_mutex);
+       _preview_event.wait(lock, [this] { return _exit_thread; });
+}
+
+void CameraApiBackend::streamOn()
+{
+       SINGLEO_LOGD("CameraApiBackend: stream on.");
+
+       if (!_thread_handle)
+               _thread_handle = std::make_unique<std::thread>(&CameraApiBackend::threadLoop, this);
+}
+
+void CameraApiBackend::streamOff()
+{
+       SINGLEO_LOGD("CameraApiBackend: stream off.");
+
+       int ret = camera_stop_preview(_camera);
+       if (ret != CAMERA_ERROR_NONE) {
+               SINGLEO_LOGE("CameraApiBackend: camera_stop_preview failed. ret: %d", ret);
+               throw InvalidOperation("camera_start_preview: camera_stop_preview failed.");
+       }
+
+       _exit_thread = true;
+       _preview_event.notify_one();
+       _thread_handle->join();
+}
+
+}
+}
index 3368c41fe10bc16d3bdf74a1153730bba129c413..9dfe802e79730f2e5749f6c48520d741f86658f1 100644 (file)
@@ -31,10 +31,12 @@ namespace input
 InputCamera::InputCamera(CameraConfig &config)
 {
        // TODO. Consider for other input backends such as camera api and vision source later.
-       if (config.backend_type != CameraBackendType::OPENCV)
+       if (config.backend_type != CameraBackendType::OPENCV && config.backend_type != CameraBackendType::CAMERA_API)
                throw InvalidOperation("Camera backend type not supported.");
 
-       _input = CameraBackendFactory::create("OpencvBackend", config);
+       // In default, CameraApiBackend is used.
+       // TODO. introduce json based configuration file later.
+       _input = CameraBackendFactory::create("CameraApiBackend", config);
 }
 
 InputCamera::~InputCamera()
index 545a75afefd8c8a4b23b97ea9cf50acb083690b0..362f138f59921e0e4c224beb85f9544e7cd8e172 100644 (file)
@@ -8,6 +8,7 @@ Source0:     %{name}-%{version}.tar.gz
 BuildRequires: cmake
 BuildRequires: pkgconfig(capi-media-vision)
 BuildRequires: pkgconfig(capi-media-tool)
+BuildRequires: pkgconfig(capi-media-camera)
 BuildRequires: pkgconfig(opencv)
 BuildRequires: pkgconfig(dlog)
 BuildRequires: gtest-devel
@@ -61,7 +62,9 @@ export CFLAGS+=" -DPATH_LIBDIR=\\\"%{_libdir}\\\" -DSYSCONFDIR=\\\"%{_hal_syscon
 export CXXFLAGS+=" -DPATH_LIBDIR=\\\"%{_libdir}\\\" -DSYSCONFDIR=\\\"%{_hal_sysconfdir}\\\""
 
 MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
-%cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} -DTZ_SYS_BIN=%TZ_SYS_BIN %{BUILD_OPTIONS} \
+%cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} -DTZ_SYS_BIN=%TZ_SYS_BIN \
+         -DUSE_INPUT_CAMERA_API_BACKEND=1 \
+         %{BUILD_OPTIONS} \
 
 make %{?jobs:-j%jobs}