* @pre Create a source handle by calling singleo_service_create()
* @pre Perform a requested service by calling singleo_service_perform()
*/
-int singleo_service_get_result_str(singleo_service_h handle, unsigned int idx, const char *key, char **value);
+int singleo_service_get_result_str(singleo_service_h handle, unsigned int idx, const char *key, const char **value);
/**
* @internal
OdResultType() : BaseResultType(ResultType::OBJECT_DETECTION)
{}
std::vector<Rect> _rects;
+ std::vector<std::string> _labels;
std::shared_ptr<BaseResultType> clone() override
{
if (ret != MEDIA_VISION_ERROR_NONE)
throw runtime_error("Fail to get object detection bound box.");
+ const char *label = nullptr;
+
+ ret = mv_object_detection_get_label(_handle, idx, &label);
+ if (ret != MEDIA_VISION_ERROR_NO_DATA && ret != MEDIA_VISION_ERROR_NONE)
+ throw runtime_error("Fail to get a label to detected object.");
+
+ // If there is no label, it means that the model does not provide a label. In this case, we do not push the label.
+ if (ret != MEDIA_VISION_ERROR_NO_DATA)
+ _output_data._labels.push_back(label);
+
_output_data._rects.push_back(rect);
}
%{_bindir}/test_singleo_airgesture
%endif
%{_bindir}/test_singleo_task_manger
+%{_bindir}/test_singleo_task_api
INCLUDE(air_gesture/CMakeLists.txt)
ENDIF()
+INCLUDE(task_api_test/CMakeLists.txt)
+
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SINGLEO_SERVICE_SOURCE_FILES})
TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PRIVATE include common/include ${ROOT_DIRECTORY}/capi/ ${COMMON_HEADER_LIST} ${INPUT_HEADER_LIST} ${INFERENCE_HEADER_LIST} ${SERVICE_HEADER_LIST} ${LOG_HEADER_LIST})
throw exception::InvalidOperation("Not supported yet.");
}
void getResultFloat(unsigned int idx, std::string key, float *value) override;
+ void getResultStr(unsigned int idx, std::string key, const char **value) override
+ {
+ throw exception::InvalidOperation("Not supported yet.");
+ }
void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
void unregisterUserCallback() override;
{
throw exception::InvalidOperation("Not support yet.");
}
+ void getResultStr(unsigned int idx, std::string key, const char **value) override
+ {
+ throw exception::InvalidOperation("Not supported yet.");
+ }
void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
void unregisterUserCallback() override;
virtual void getResultCnt(unsigned int *cnt) = 0;
virtual void getResultInt(unsigned int idx, std::string key, unsigned int *value) = 0;
virtual void getResultFloat(unsigned int idx, std::string key, float *value) = 0;
+ virtual void getResultStr(unsigned int idx, std::string key, const char **value) = 0;
virtual void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) = 0;
virtual void unregisterUserCallback() = 0;
};
{
// TODO
-enum class ServiceResultType { NONE, AUTO_ZOOM, FOCUS_FINDER, AIR_GESTURE };
+enum class ServiceResultType { NONE, AUTO_ZOOM, FOCUS_FINDER, AIR_GESTURE, TASK_API_TEST };
struct ServiceBaseResultType {
ServiceResultType _result_type { ServiceResultType::NONE };
{
throw exception::InvalidOperation("Not supported yet.");
}
+ void getResultStr(unsigned int idx, std::string key, const char **value) override
+ {
+ throw exception::InvalidOperation("Not supported yet.");
+ }
void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
void unregisterUserCallback() override;
};
return SINGLEO_ERROR_NONE;
}
+int singleo_service_get_result_str(singleo_service_h handle, unsigned int idx, const char *key, const char **value)
+{
+ try {
+ auto context = static_cast<Context *>(handle);
+ context->_service_handle->getResultStr(idx, key, value);
+ } catch (const BaseException &e) {
+ SINGLEO_LOGE("%s", e.what());
+ return e.getError();
+ }
+
+ return SINGLEO_ERROR_NONE;
+}
+
int singleo_service_register_user_callback(singleo_service_h handle, singleo_user_cb_t user_cb, void *user_data)
{
try {
--- /dev/null
+SET(SINGLEO_SERVICE_SOURCE_FILES
+ ${SINGLEO_SERVICE_SOURCE_FILES}
+ task_api_test/src/TaskApiTest.cpp
+)
+
+LIST(APPEND SERVICE_LIBRARY_LIST ${SERVICE_LIBRARY_LIST})
+LIST(APPEND SERVICE_HEADER_LIST ${SERVICE_HEADER_LIST} ${CMAKE_CURRENT_SOURCE_DIR}/task_api_test/include)
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2025 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 __DATA_TYPES_H__
+#define __DATA_TYPES_H__
+
+#include <vector>
+#include "SingleoCommonTypes.h"
+#include "ServiceDataType.h"
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapitest
+{
+enum class TaskApiResultType { MIN_X, MIN_Y, MAX_X, MAX_Y, LABEL };
+
+struct TaskApiResult : public ServiceBaseResultType {
+ TaskApiResult() : ServiceBaseResultType(ServiceResultType::TASK_API_TEST)
+ {}
+ bool is_valid { false };
+ unsigned int frame_number {};
+ unsigned int num_of_objects {};
+ std::vector<Rect> bboxes;
+ std::vector<std::string> labels;
+};
+
+}
+}
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2025 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 __TASK_API_TEST_H__
+#define __TASK_API_TEST_H__
+
+#include <opencv2/opencv.hpp>
+#include <mutex>
+
+#include "IService.h"
+#include "SingleoException.h"
+#include "SingleoCommonTypes.h"
+#include "IInputService.h"
+#include "SingleoInferenceTypes.h"
+#include "InputTypes.h"
+#include "IInputObserver.h"
+#include "ServiceDataType.h"
+#include "DataTypes.h"
+#include "AsyncManager.h"
+#include "IPreprocessor.h"
+#include "TaskManager.h"
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapitest
+{
+class TaskApiTest : public IService, public input::IInputObserver
+{
+private:
+ static bool _registered;
+ std::unique_ptr<TaskManager> _taskManager;
+ std::unique_ptr<singleo::input::IInputService> _input_service;
+ std::queue<std::shared_ptr<BaseDataType> > _inputQ;
+ std::mutex _inputMutex;
+ TaskApiResult _result;
+ std::mutex _result_lock;
+ std::map<std::string, TaskApiResultType> _result_keys = { { "MIN_X", TaskApiResultType::MIN_X },
+ { "MIN_Y", TaskApiResultType::MIN_Y },
+ { "MAX_X", TaskApiResultType::MAX_X },
+ { "MAX_Y", TaskApiResultType::MAX_Y },
+ { "LABEL", TaskApiResultType::LABEL } };
+ bool _async_mode { false };
+
+ bool isKeyValid(std::string key);
+ void updateResult(BaseDataType &input_data);
+
+ std::unique_ptr<AsyncManager<ImageDataType, TaskApiResult> > _async_manager;
+ singleo_user_cb_t _user_cb {};
+ void *_user_data {};
+ std::unique_ptr<IPreprocessor> _preprocessor;
+
+public:
+ TaskApiTest();
+ virtual ~TaskApiTest();
+
+ static IService *create()
+ {
+ return new TaskApiTest();
+ }
+
+ // This function will be called by specific input service internally.
+ // Ps. caller has to provide captured data with concrete class object as data parameter.
+ void inputFeedCb(BaseDataType &data) override;
+ void configure(input::InputConfigBase &config) override;
+ void add_input(BaseDataType &input_data) override;
+ void perform() override;
+ void performAsync() override;
+ void getResultCnt(unsigned int *cnt) override;
+ void getResultInt(unsigned int idx, std::string key, unsigned int *value) override;
+ void getResultFloat(unsigned int idx, std::string key, float *value) override
+ {
+ throw exception::InvalidOperation("Not supported yet.");
+ }
+ void getResultStr(unsigned int idx, std::string key, const char **value) override;
+ void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
+ void unregisterUserCallback() override;
+
+ void runTaskManager(BaseDataType &input_data);
+};
+
+} // taskapitest
+} // service
+} // singleo
+
+#endif
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2025 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 <algorithm>
+#include <map>
+#include "SingleoException.h"
+#include "TaskApiTest.h"
+#include "InferenceTaskFactory.h"
+#include "SingleoLog.h"
+#include "ImagePreprocessor.h"
+#include "ServiceFactory.h"
+#include "InputCamera.h"
+#include "InputTypes.h"
+#include "InferenceNode.h"
+#include "BridgeNode.h"
+#include "EndpointNode.h"
+
+using namespace std;
+using namespace singleo::inference;
+using namespace singleo::input;
+using namespace singleo::exception;
+
+namespace singleo
+{
+namespace services
+{
+namespace taskapitest
+{
+bool TaskApiTest::_registered = registerService<TaskApiTest>("TaskApiTest");
+
+TaskApiTest::TaskApiTest()
+{
+ // In default, we will use Inference service factory for Mediavision to use Mediavision framework
+ // for inference service. TODO. introduce meta config file approach later.
+ auto factory = InferenceTaskFactory::instance().create("MvInferenceTaskFactory");
+
+ _taskManager = make_unique<TaskManager>();
+
+ _taskManager->requestNewNode(NodeType::STARTPOINT, "startpoint");
+
+ auto object_detection_node = _taskManager->requestNewNode(NodeType::INFERENCE, "object_detection");
+ dynamic_cast<InferenceNode *>(object_detection_node)->setInferenceTask(factory->createObjectDetection());
+
+ _taskManager->requestNewNode(NodeType::ENDPOINT, "endpoint");
+
+ _preprocessor = make_unique<ImagePreprocessor>();
+
+ _taskManager->resetPipeline();
+
+ _taskManager->addEdge("startpoint", "object_detection");
+ _taskManager->addEdge("object_detection", "endpoint");
+
+ _taskManager->verify();
+}
+
+void TaskApiTest::configure(InputConfigBase &config)
+{
+ // Create InputCamera service if input service type is CAMERA.
+ if (config._input_feed_type == InputFeedType::CAMERA) {
+ CameraConfig cameraConfig;
+
+ try {
+ cameraConfig = dynamic_cast<CameraConfig &>(config);
+ } catch (bad_cast &e) {
+ SINGLEO_LOGE("Camera input service requires CameraConfig.");
+ throw InvalidOperation("Camera input service requires CameraConfig.");
+ }
+
+ _input_service = make_unique<InputCamera>(cameraConfig.backend_type);
+ _input_service->registerObserver(this);
+ _input_service->configure(cameraConfig);
+
+ SINGLEO_LOGD("Camera input service has been initialized.");
+ }
+}
+
+TaskApiTest::~TaskApiTest()
+{
+ if (_async_mode) {
+ _input_service->streamOff();
+ _async_manager->destroy();
+ }
+
+ _taskManager->clear();
+}
+
+void TaskApiTest::inputFeedCb(BaseDataType &data)
+{
+ _preprocessor->addInput(data);
+
+ auto originalImg = dynamic_pointer_cast<ImageDataType>(_preprocessor->getOutput().clone());
+ auto preprocessedImg = dynamic_cast<ImageDataType &>(_preprocessor->getOutput());
+
+ if (_user_cb) {
+ cv::Mat imgCv(cv::Size(preprocessedImg.width, preprocessedImg.height), CV_MAKETYPE(CV_8U, 3),
+ preprocessedImg.ptr);
+
+ _result_lock.lock();
+
+ if (_result.is_valid) {
+ auto &rects = _result.bboxes;
+ auto &labels = _result.labels;
+
+ _result_lock.unlock();
+
+ for (unsigned int idx = 0; idx < rects.size(); ++idx) {
+ cv::rectangle(imgCv, cv::Point(rects[idx].left, rects[idx].top),
+ cv::Point(rects[idx].right, rects[idx].bottom), cv::Scalar(255, 0, 0), 3);
+ cv::putText(imgCv, labels[idx], cv::Point(rects[idx].left, rects[idx].top - 10), cv::FONT_HERSHEY_SIMPLEX, 1.2, cv::Scalar(255, 0, 0), 2);
+ }
+
+ _result_lock.lock();
+ _result.is_valid = false;
+ _result_lock.unlock();
+ }
+
+ _result_lock.unlock();
+ _user_cb(preprocessedImg.ptr, preprocessedImg.width, preprocessedImg.height, preprocessedImg.byte_per_pixel, _user_data);
+ }
+
+ _preprocessor->invoke();
+
+ // Set is_owned to false to prevent from releasing copied image data
+ // because the owner of copied is AirGesture so the copied will be released
+ // in performAsync function.
+ // Ps. if is_owned is true, copied image data can be released while task manager is being performed.
+ originalImg->is_owned = false;
+
+ // Make sure to release copied buffer if incoming queue isn't empty so skipped pushing the buffer.
+ // If empty then push the buffer to the incoming queue. This buffer will be released at the end of runTaskManager function.
+ if (_async_manager->pushInput(*originalImg) != SINGLEO_ERROR_NONE)
+ delete originalImg->ptr;
+}
+
+bool TaskApiTest::isKeyValid(std::string key)
+{
+ auto it = _result_keys.find(key);
+ if (it == _result_keys.end())
+ return false;
+
+ return true;
+}
+
+void TaskApiTest::add_input(BaseDataType &input_data)
+{
+ if (_inputQ.size() > 0) {
+ SINGLEO_LOGE("Only one input data is allowed.");
+ throw InvalidOperation("Only one input data is allowed.");
+ }
+
+ if (input_data._data_type != DataType::IMAGE && input_data._data_type != DataType::FILE) {
+ SINGLEO_LOGE("Only IMAGE and FILE types are allowed.");
+ throw InvalidOperation("Only IMAGE and FILE types are allowed.");
+ }
+
+ std::lock_guard<std::mutex> lock(_inputMutex);
+
+ _inputQ.push(input_data.clone());
+}
+
+void TaskApiTest::runTaskManager(BaseDataType &input_data)
+{
+ _taskManager->addInput(input_data);
+ _taskManager->run();
+
+ updateResult(input_data);
+}
+
+void TaskApiTest::perform()
+{
+ ImagePreprocessor preprocessor;
+
+ // If input service is not set, input data comes from user.
+ // In this case, get input data from _inputs.
+ if (!_input_service) {
+ std::lock_guard<std::mutex> lock(_inputMutex);
+
+ _preprocessor->addInput(*_inputQ.front());
+ _inputQ.pop();
+ } else {
+ ImageDataType input_data;
+
+ _input_service->capture(input_data);
+ _preprocessor->addInput(input_data);
+ }
+
+ runTaskManager(_preprocessor->getOutput());
+}
+
+void TaskApiTest::performAsync()
+{
+ if (!_input_service) {
+ SINGLEO_LOGE("This API is valid only the case that input feed service is used.");
+ throw InvalidOperation("Invalid API request.");
+ }
+
+ _input_service->streamOn();
+
+ _async_mode = true;
+ _async_manager = make_unique<AsyncManager<ImageDataType, TaskApiResult> >();
+ _async_manager->registerInvokeCb(this, [this](IService *service, BaseDataType &data) {
+ auto task_api_test = static_cast<TaskApiTest *>(service);
+
+ task_api_test->runTaskManager(data);
+
+ auto imageData = dynamic_cast<ImageDataType &>(data);
+
+ // A given imageData - allocated by inputFeedCb function - has been used so release it here.
+ if (!imageData.is_owned)
+ delete imageData.ptr;
+ });
+}
+
+void TaskApiTest::updateResult(BaseDataType &in_data)
+{
+ TaskApiResult taskapi_result;
+
+ for (auto &output : _taskManager->output()) {
+ if (output->_is_empty)
+ continue;
+
+ if (output->_type == ResultType::OBJECT_DETECTION) {
+ taskapi_result.bboxes = dynamic_cast<OdResultType &>(*output)._rects;
+ taskapi_result.labels = dynamic_cast<OdResultType &>(*output)._labels;
+ taskapi_result.is_valid = true;
+ }
+ }
+
+ if (_async_mode) {
+ _async_manager->pushOutput(taskapi_result);
+ } else {
+ _result_lock.lock();
+ _result = taskapi_result;
+ _result_lock.unlock();
+ }
+}
+
+void TaskApiTest::getResultCnt(unsigned int *cnt)
+{
+ if (_async_mode) {
+ auto result = _async_manager->popOutput();
+ std::unique_lock<std::mutex> lock(_result_lock);
+
+ _result = result;
+ *cnt = static_cast<unsigned int>(_result.bboxes.size());
+ return;
+ }
+
+ std::unique_lock<std::mutex> lock(_result_lock);
+
+ *cnt = static_cast<unsigned int>(_result.bboxes.size());
+}
+
+void TaskApiTest::getResultInt(unsigned int idx, std::string key, unsigned int *value)
+{
+ transform(key.begin(), key.end(), key.begin(), ::toupper);
+
+ SINGLEO_LOGD("given key = %s", key.c_str());
+
+ if (!isKeyValid(key))
+ throw InvalidParameter("A given result key is inavlid.");
+
+ std::unique_lock<std::mutex> lock(_result_lock);
+
+ switch (_result_keys[key]) {
+ case TaskApiResultType::MIN_X:
+ *value = _result.bboxes[idx].left;
+ break;
+ case TaskApiResultType::MIN_Y:
+ *value = _result.bboxes[idx].top;
+ break;
+ case TaskApiResultType::MAX_X:
+ *value = _result.bboxes[idx].right;
+ break;
+ case TaskApiResultType::MAX_Y:
+ *value = _result.bboxes[idx].bottom;
+ break;
+ }
+}
+
+void TaskApiTest::getResultStr(unsigned int idx, std::string key, const char **value)
+{
+ transform(key.begin(), key.end(), key.begin(), ::toupper);
+
+ SINGLEO_LOGD("given key = %s", key.c_str());
+
+ if (!isKeyValid(key))
+ throw InvalidParameter("A given result key is inavlid.");
+
+ std::unique_lock<std::mutex> lock(_result_lock);
+
+ switch (_result_keys[key]) {
+ case TaskApiResultType::LABEL:
+ *value = _result.labels[idx].c_str();
+ break;
+ }
+}
+
+void TaskApiTest::registerUserCallback(singleo_user_cb_t user_cb, void *user_data)
+{
+ _user_cb = user_cb;
+ _user_data = user_data;
+}
+
+void TaskApiTest::unregisterUserCallback()
+{
+ _user_cb = nullptr;
+ _user_data = nullptr;
+}
+
+}
+}
+}
gtest gtest_main pthread singleo_service ${OpenCV_LIBS})
INSTALL(TARGETS test_singleo_task_manger DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+SET(TEST_SOURCE_TASK_API_LIST test_task_api_on_screen.cpp)
+
+ADD_EXECUTABLE(test_singleo_task_api ${TEST_SOURCE_TASK_API_LIST})
+TARGET_COMPILE_DEFINITIONS(test_singleo_task_api PRIVATE -DTEST_RES_PATH="${TEST_RES_PATH}")
+TARGET_INCLUDE_DIRECTORIES(test_singleo_task_api PRIVATE ../../capi/ ../../common/include ../../log/include ../../visualizer/include)
+TARGET_LINK_LIBRARIES(test_singleo_task_api
+ gtest gtest_main pthread singleo_service singleo_visualizer opencv_imgcodecs)
+
+INSTALL(TARGETS test_singleo_task_api DESTINATION ${CMAKE_INSTALL_BINDIR})
--- /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 <iostream>
+#include <thread>
+#include <opencv2/opencv.hpp>
+
+#include <mutex>
+#include "gtest/gtest.h"
+#include "singleo_native_capi_internal.h"
+#include "singleo_util_visualizer_2d.h"
+#include "singleo_error.h"
+#include "SingleoCommonTypes.h"
+
+using namespace testing;
+using namespace singleo;
+using namespace std;
+
+struct Context {
+ std::mutex _mutex;
+ singleo_service_h handle {};
+ vector<Point> points;
+};
+
+void taskapi_callback(void *user_data)
+{
+ Context *context = static_cast<Context *>(user_data);
+ singleo_service_h handle = static_cast<singleo_service_h>(context->handle);
+ bool is_loop_exit = false;
+ unsigned long frame_number = 0;
+
+ while (!is_loop_exit) {
+ unsigned int cnt;
+
+ int ret = singleo_service_get_result_cnt(handle, &cnt);
+ if (ret != SINGLEO_ERROR_NONE)
+ break;
+
+ cout << "cnt = " << cnt << " frame number = " << frame_number << endl;
+
+ for (unsigned int idx = 0; idx < cnt; ++idx) {
+ unsigned int min_x {}, min_y {}, max_x {}, max_y {};
+
+ ret = singleo_service_get_result_int(handle, idx, "MIN_X", &min_x);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ ret = singleo_service_get_result_int(handle, idx, "MIN_Y", &min_y);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ ret = singleo_service_get_result_int(handle, idx, "MAX_X", &max_x);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ ret = singleo_service_get_result_int(handle, idx, "MAX_Y", &max_y);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ const char *label {};
+
+ ret = singleo_service_get_result_str(handle, idx, "LABEL", &label);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+
+ cout << "x1 = " << min_x << " y1 = " << min_y << " x2 = " << max_x << " y2 = " << max_y << " label = " << label << endl;
+ }
+
+ if (++frame_number > 5000 && cnt > 0)
+ is_loop_exit = true;
+ }
+}
+
+void user_callback(unsigned char *buffer, unsigned int width, unsigned int height, unsigned int bytes_per_pixel,
+ void *user_data)
+{
+ Context *context = static_cast<Context *>(user_data);
+ cv::Mat result(cv::Size(width, height), CV_MAKETYPE(CV_8U, 3), buffer);
+
+ cv::Mat resized;
+
+ cv::resize(result, resized, cv::Size(1600, 900));
+
+ singleo_util_visualizer_2d(resized, NULL);
+}
+
+TEST(TaskApiAsyncOnScreenTest, ODRequestWithCameraInputFeedShouldBeOk)
+{
+ Context context {};
+
+ int ret = singleo_service_create("service=TaskApiTest, input_feed=camera, camera_id=0, fps=30, async=1",
+ &context.handle);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ ret = singleo_service_register_user_callback(context.handle, user_callback, &context);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ ret = singleo_service_perform(context.handle);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ unique_ptr<thread> thread_handle = make_unique<thread>(&taskapi_callback, static_cast<void *>(&context));
+
+ thread_handle->join();
+
+ ret = singleo_service_unregister_user_callback(context.handle);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+
+ ret = singleo_service_destroy(context.handle);
+ ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
+}