--- /dev/null
+/**
+ * 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
--- /dev/null
+/**
+ * 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();
+}
+
+}
+}