add external service support as a plugin 96/312996/5
authorInki Dae <inki.dae@samsung.com>
Tue, 18 Jun 2024 01:40:45 +0000 (10:40 +0900)
committerInki Dae <inki.dae@samsung.com>
Wed, 19 Jun 2024 04:59:25 +0000 (13:59 +0900)
Add external service support as a plugin which can be built as separate git
repository for product teams.

As for this, this patch exposes common header files so that the external
git repository can refer to the header files to develop a new service.
Ps. task manager is allowed only for built-in service so we don't expose
task manager relevant header files yet.

With self-registeration factory pattern, we can *add* a new service and *use*
it in runtime without recompling SingleO framework.

What you have to for adding your own external service are,
   - build your own git repository and create a so library file.
   - install the so library file to user target device.

Now we are ready for the use of the new external service. Just change
the service name like below,
singleo_service_create("service=user-service-name, ...);

Change-Id: I2e04c53820c6bc5602f7b8259d32f17b0411315a
Signed-off-by: Inki Dae <inki.dae@samsung.com>
14 files changed:
CMakeLists.txt
capi/singleo_native_capi_internal.h
common/include/SingleoCommonTypes.h
packaging/singleo.spec
services/auto_zoom/src/AutoZoom.cpp
services/common/include/AsyncManager.h
services/common/include/IService.h
services/common/include/ServiceConfigParser.h
services/common/include/ServiceFactory.h
services/common/src/ServiceConfigParser.cpp
services/singleo_native_capi.cpp
singleo.pc.in
test/services/test_autozoom.cpp
test/services/test_autozoom_on_screen.cpp

index aefb38cc4fd749db8d683ff27f97881d73747133..b297de8c3f66ca736e53e32c2bfe5a3de3b075a1 100644 (file)
@@ -12,11 +12,37 @@ ADD_SUBDIRECTORY(visualizer)
 
 SET(PC_NAME ${PROJECT_NAME})
 SET(PC_LDFLAGS -l${PROJECT_NAME})
-SET(PC_CFLAGS -I\${includedir}/media)
+SET(PC_CFLAGS -I\${includedir}/media/singleo)
 
 CONFIGURE_FILE(
     ${PROJECT_NAME}.pc.in
     ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc
     @ONLY
 )
-INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
\ No newline at end of file
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
+install(
+    FILES ${PROJECT_SOURCE_DIR}/common/include/singleo_error.h
+          ${PROJECT_SOURCE_DIR}/common/include/SingleoCommonTypes.h
+          ${PROJECT_SOURCE_DIR}/common/include/SingleoException.h
+          ${PROJECT_SOURCE_DIR}/common/include/SharedBuffer.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/IService.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/AsyncManager.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/IPreprocessor.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/IPostprocessor.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/ImagePreprocessor.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/ServiceConfigParser.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/ServiceDataType.h
+          ${PROJECT_SOURCE_DIR}/services/common/include/ServiceFactory.h
+          ${PROJECT_SOURCE_DIR}/log/include/SingleoLog.h
+          ${PROJECT_SOURCE_DIR}/log/include/SingleoLogBackend.h
+          ${PROJECT_SOURCE_DIR}/log/include/SingleoLogType.h
+          ${PROJECT_SOURCE_DIR}/inference/include/IInferenceTaskFactory.h
+          ${PROJECT_SOURCE_DIR}/inference/include/InferenceTaskFactory.h
+          ${PROJECT_SOURCE_DIR}/inference/include/IInferenceTaskInterface.h
+          ${PROJECT_SOURCE_DIR}/inference/include/SingleoInferenceTypes.h
+          ${PROJECT_SOURCE_DIR}/input/include/IInputService.h
+          ${PROJECT_SOURCE_DIR}/input/include/InputCamera.h
+          ${PROJECT_SOURCE_DIR}/input/include/IInputObserver.h
+          ${PROJECT_SOURCE_DIR}/input/include/InputTypes.h
+    DESTINATION include/media/singleo
+)
\ No newline at end of file
index a3c350ce69a17759452ad6d7fd8528d69e763a4d..96c72aeb383b19f0a9b88f19c162a5a470d995d6 100644 (file)
@@ -18,9 +18,6 @@
 
 #include "singleo_native_capi.h"
 
-typedef void (*singleo_user_cb_t)(unsigned char *buffer, unsigned int width, unsigned int height,
-                                                                 unsigned int bytes_per_pixel, void *user_data);
-
 /**
  * @internal
  * @brief Registers user-given callback for user to receive result mixed with input data.
index 57c131e8f265ec8fd300176ee2bcf0d4763fa3e3..d3e6ee73a620e518e67296ebed74f6fa506c6c11 100644 (file)
@@ -206,6 +206,9 @@ enum class InputFeedType { NONE, CAMERA, SCREEN_CAPTURE };
 
 enum class CameraBackendType { NONE, OPENCV, CAMERA_API, VISION_SOURCE };
 
+typedef void (*singleo_user_cb_t)(unsigned char *buffer, unsigned int width, unsigned int height,
+                                                                 unsigned int bytes_per_pixel, void *user_data);
+
 } // singleo
 
 #endif
\ No newline at end of file
index adc94e968b7ba2a7edf16bdff2af299bf3eeacf9..0710a63cd09b543fd59a0c5d594ebc1658bc6f78 100644 (file)
@@ -22,17 +22,10 @@ BuildRequires: pkgconfig(re2)
 %description
 SingleO AI Service Framework
 
-%package release
-Summary:    SingleO AI Service Framework
-Group:      Multimedia/Framework
-
-%description release
-SingleO AI Service Framework
-
 %package devel
 Summary:    SingleO AI Service Framework
 Group:      Multimedia/Framework
-Requires:   %{name} = %{version}-%{release}
+Requires:   %{name}
 
 %description devel
 SingleO AI Service Framework (Dev)
@@ -40,7 +33,7 @@ SingleO AI Service Framework (Dev)
 %package test
 Summary:    Test case for SingleO AI Service Framework
 Group:      Multimedia/Framework
-Requires:   %{name} = %{version}-%{release}
+Requires:   %{name}
 
 %description test
 Test case for SingleO AI Service Framework (Dev)
@@ -76,14 +69,37 @@ rm -rf %{buildroot}
 %post -p /sbin/ldconfig
 %postun -p /sbin/ldconfig
 
-%files release
+%files
 %manifest singleo.manifest
 %license LICENSE.APLv2
+%{_libdir}/libsingleo_service.so
+%{_libdir}/libsingleo_visualizer.so
 
 %files devel
 %{_libdir}/pkgconfig/*.pc
-%{_libdir}/libsingleo_service.so
-%{_libdir}/libsingleo_visualizer.so
+%{_includedir}/media/singleo/IInferenceTaskFactory.h
+%{_includedir}/media/singleo/IInferenceTaskInterface.h
+%{_includedir}/media/singleo/InferenceTaskFactory.h
+%{_includedir}/media/singleo/SingleoInferenceTypes.h
+%{_includedir}/media/singleo/SingleoCommonTypes.h
+%{_includedir}/media/singleo/SingleoException.h
+%{_includedir}/media/singleo/singleo_error.h
+%{_includedir}/media/singleo/SharedBuffer.h
+%{_includedir}/media/singleo/IService.h
+%{_includedir}/media/singleo/AsyncManager.h
+%{_includedir}/media/singleo/IPreprocessor.h
+%{_includedir}/media/singleo/IPostprocessor.h
+%{_includedir}/media/singleo/ImagePreprocessor.h
+%{_includedir}/media/singleo/ServiceConfigParser.h
+%{_includedir}/media/singleo/ServiceDataType.h
+%{_includedir}/media/singleo/ServiceFactory.h
+%{_includedir}/media/singleo/SingleoLog.h
+%{_includedir}/media/singleo/SingleoLogBackend.h
+%{_includedir}/media/singleo/SingleoLogType.h
+%{_includedir}/media/singleo/IInputService.h
+%{_includedir}/media/singleo/InputCamera.h
+%{_includedir}/media/singleo/IInputObserver.h
+%{_includedir}/media/singleo/InputTypes.h
 
 %files test
 %{_bindir}/test_singleo
index 13430ad0c6b5dab13f7c099fee6e59c10bbbfef1..466ef4515eb6048e2e1219c0e5bd7c5a1bb886bf 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <algorithm>
+#include <map>
 #include "SingleoException.h"
 #include "AutoZoom.h"
 #include "InferenceTaskFactory.h"
index e45d06650f3db93c02f61868702a3cc295351ec2..6ff9361f5a44ea7a586929697aeab3e44269215e 100644 (file)
@@ -26,7 +26,6 @@
 #include "IService.h"
 #include "SingleoException.h"
 #include "SingleoCommonTypes.h"
-#include "ImagePreprocessor.h"
 
 namespace singleo
 {
index 9c76b3a3aa2cf96dac4589f2cabf553bcf0e12be..78c3510d3ec7b8b6db5b85621e608743e185f1b9 100644 (file)
@@ -17,8 +17,6 @@
 #ifndef __ISERVICE_H__
 #define __ISERVICE_H__
 
-#include "singleo_native_capi_internal.h"
-
 #include "SingleoCommonTypes.h"
 #include "InputTypes.h"
 #include "IPreprocessor.h"
index de2ecb574cb164aee086cf5bec8688a98442c250..3586dc05415439e7268632c85efbf87622132404 100644 (file)
@@ -30,7 +30,7 @@ class ServiceConfigParser
 {
 private:
        std::map<std::string, std::string> _params;
-       ServiceType _service_type { ServiceType::NONE };
+       std::string _service_name;
        InputFeedType _input_feed_type { InputFeedType::NONE };
        ImagePixelFormat _image_pixel_format { ImagePixelFormat::NONE };
        unsigned int _fps {};
@@ -44,7 +44,7 @@ private:
        bool isKeyValid(std::map<std::string, bool> &valid_keys, const std::string &key);
        void parse(std::map<std::string, bool> &valid_keys, std::string option);
        void update();
-       void setServiceType(std::string key);
+       void setServiceName(std::string key);
        void setInputFeedType(std::string key);
        void setCameraDeviceId(std::string key);
        void setFps(std::string key);
@@ -57,7 +57,7 @@ public:
 
        void parseService(std::string option);
        void parseInput(std::string option);
-       ServiceType getServiceType();
+       std::string &getServiceName();
        InputFeedType getInputFeedType();
        ImagePixelFormat getImagePixelFormat();
        CameraBackendType getCameraBackendType();
index 62f868556ea896fc6506f2951fd2af68019649ab..81f60c1fe5fd95027c0be840d0e1b6d0ca7a0200 100644 (file)
@@ -67,4 +67,4 @@ template<typename ServiceType> bool registerService(const std::string &name)
 }
 }
 
-#endif
\ No newline at end of file
+#endif
index 6ae4e89063d64d3b8bb46f980a2dbe488045f7b2..aa794a0b6c083ab166428b797a7b147e105a8dae 100644 (file)
@@ -52,15 +52,9 @@ bool ServiceConfigParser::isKeyValid(map<string, bool> &valid_keys, const string
        return true;
 }
 
-void ServiceConfigParser::setServiceType(string key)
+void ServiceConfigParser::setServiceName(string key)
 {
-       static map<string, ServiceType> valid_services = { { "AUTO_ZOOM", ServiceType::AUTO_ZOOM } };
-
-       auto it = valid_services.find(key);
-       if (it == valid_services.end())
-               throw InvalidOperation("Invalid service type.");
-
-       _service_type = it->second;
+       _service_name = key;
 }
 
 void ServiceConfigParser::setInputFeedType(std::string key)
@@ -112,9 +106,9 @@ void ServiceConfigParser::setInputFormat(string key)
        _image_pixel_format = it->second;
 }
 
-ServiceType ServiceConfigParser::getServiceType()
+string &ServiceConfigParser::getServiceName()
 {
-       return _service_type;
+       return _service_name;
 }
 
 InputFeedType ServiceConfigParser::getInputFeedType()
@@ -155,7 +149,10 @@ void ServiceConfigParser::parse(map<string, bool> &valid_keys, string option)
                                throw InvalidParameter("Invalid option string.");
 
                        transform(key.begin(), key.end(), key.begin(), ::toupper);
-                       transform(value.begin(), value.end(), value.begin(), ::toupper);
+
+                       // Do not transform value to uppercase if key is SERVICE.
+                       if (key != "SERVICE")
+                               transform(value.begin(), value.end(), value.begin(), ::toupper);
 
                        if (!isKeyValid(valid_keys, key))
                                throw InvalidParameter("A given key is invalid.");
@@ -203,7 +200,7 @@ void ServiceConfigParser::update()
                SINGLEO_LOGD("Parsed : key(%s) -> value(%s)", key.c_str(), it->second.c_str());
        };
 
-       setParam("SERVICE", &ServiceConfigParser::setServiceType);
+       setParam("SERVICE", &ServiceConfigParser::setServiceName);
        setParam("INPUT_FEED", &ServiceConfigParser::setInputFeedType);
        setParam("CAMERA_ID", &ServiceConfigParser::setCameraDeviceId);
        setParam("FPS", &ServiceConfigParser::setFps);
index 3c335aa0c74e19afa6147754ce5df96d89340b4b..e83b2bcc590e31788d72fb906f168972e3532f15 100644 (file)
@@ -14,34 +14,52 @@ using namespace singleo::exception;
 using namespace singleo::services;
 using namespace singleo::input;
 
+// TODO. Add json based plugin list file support later
+//       to add new services and use them without recompiling SingleO framework.
+static vector<string> _pluginNames = { "libsingleo-example-plugin.so" };
+static vector<void *> _pluginHandles;
+
+static void singleo_service_open_plugins()
+{
+       for (const auto &pluginName : _pluginNames) {
+               void *plugin_handle = dlopen(pluginName.c_str(), RTLD_LAZY);
+               if (!plugin_handle) {
+                       SINGLEO_LOGW("Fail to load external plugin library(%s).", pluginName.c_str());
+                       continue;
+               }
+
+               SINGLEO_LOGD("Opened new service plugin(%s).", pluginName.c_str());
+               _pluginHandles.push_back(plugin_handle);
+       }
+}
+
+static void singleo_service_close_plugins()
+{
+       for (const auto &plugin_handle : _pluginHandles)
+               dlclose(plugin_handle);
+}
+
 int singleo_service_create(const char *option, singleo_service_h *handle)
 {
        IService *service_handle = NULL;
-       ServiceType service_type {};
        bool async_mode { false };
        Context *context = NULL;
+       ServiceConfigParser parser {};
 
-       try {
-               ServiceConfigParser parser;
+       singleo_service_open_plugins();
 
+       try {
                parser.parseService(option);
                async_mode = parser.getAsyncMode();
 
-               if (ServiceType::AUTO_ZOOM == parser.getServiceType()) {
-                       service_handle = ServiceFactory::instance().create("AutoZoom");
-                       if (!service_handle)
-                               throw InvalidOperation("Failed to create AutoZoom service.");
+               service_handle = ServiceFactory::instance().create(parser.getServiceName());
+               if (!service_handle)
+                       throw InvalidOperation("Failed to create service.");
 
-                       service_handle->configure(parser.getConfig(parser.getInputFeedType()));
-
-                       SINGLEO_LOGD("AutoZoom service has been created.");
-                       service_type = ServiceType::AUTO_ZOOM;
-               } else {
-                       SINGLEO_LOGE("Invalid service type.");
-                       throw InvalidOperation("Invalid service type.");
-               }
+               SINGLEO_LOGD("%s service has been created.", parser.getServiceName().c_str());
 
                context = new Context();
+               service_handle->configure(parser.getConfig(parser.getInputFeedType()));
        } catch (const BaseException &e) {
                if (service_handle)
                        delete service_handle;
@@ -50,7 +68,6 @@ int singleo_service_create(const char *option, singleo_service_h *handle)
        }
 
        context->_service_handle = service_handle;
-       context->_service_type = service_type;
        context->_async_mode = async_mode;
 
        *handle = static_cast<void *>(context);
@@ -62,11 +79,11 @@ int singleo_service_destroy(singleo_service_h handle)
 {
        auto context = static_cast<Context *>(handle);
 
-       if (context->_service_type == ServiceType::AUTO_ZOOM)
-               delete static_cast<IService *>(context->_service_handle);
-
+       delete static_cast<IService *>(context->_service_handle);
        delete context;
 
+       singleo_service_close_plugins();
+
        return SINGLEO_ERROR_NONE;
 }
 
index e7cd18f109b88ba7f44b7a77ad2692d29e41cc54..a21ed26a3d3bb086e27c50d650078964c2e7e1bc 100644 (file)
@@ -4,7 +4,7 @@
 prefix=@PREFIX@
 exec_prefix=/usr
 libdir=@LIB_INSTALL_DIR@
-includedir=/usr/include/media
+includedir=/usr/include/media/singleo
 
 Name: @PC_NAME@
 Description: @PACKAGE_DESCRIPTION@
index 8176d45882a51695a72b012b0879afe3d7a627f3..371e8e780996876cfaa1f12fbe03c7f840f67c73 100644 (file)
@@ -33,7 +33,7 @@ TEST(AutoZoomTest, InferenceRequestShouldBeOk)
 {
        singleo_service_h handle;
 
-       int ret = singleo_service_create("service=auto_zoom", &handle);
+       int ret = singleo_service_create("service=AutoZoom", &handle);
        ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
 
        ret = singleo_service_add_input_image_file(handle, IMG_FACE);
@@ -70,7 +70,7 @@ TEST(AutoZoomTest, InferenceRequestWithCameraInputFeedShouldBeOk)
 {
        singleo_service_h handle;
 
-       int ret = singleo_service_create("service=auto_zoom, input_feed=camera, camera_id=0, fps=30, async=0", &handle);
+       int ret = singleo_service_create("service=AutoZoom, input_feed=camera, camera_id=0, fps=30, async=0", &handle);
        ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
 
        const unsigned int max_iteration = 10;
@@ -143,7 +143,7 @@ TEST(AutoZoomAsyncTest, InferenceRequestWithCameraInputFeedShouldBeOk)
 {
        singleo_service_h handle;
 
-       int ret = singleo_service_create("service=auto_zoom, input_feed=camera, camera_id=0, fps=30, async=1", &handle);
+       int ret = singleo_service_create("service=AutoZoom, input_feed=camera, camera_id=0, fps=30, async=1", &handle);
        ASSERT_EQ(ret, SINGLEO_ERROR_NONE);
 
        ret = singleo_service_perform(handle);
index 83e6bcb7c3a847a32c01e94d25d95d1011d8d39f..cc2e4109df0279fa027aa4d028caf448853453cc 100644 (file)
@@ -65,7 +65,7 @@ TEST(AutoZoomAsyncOnScreenTest, InferenceRequestWithCameraInputFeedShouldBeOk)
 {
        Context context {};
 
-       int ret = singleo_service_create("service=auto_zoom, input_feed=camera, camera_id=0, fps=30, async=1",
+       int ret = singleo_service_create("service=AutoZoom, input_feed=camera, camera_id=0, fps=30, async=1",
                                                                         &context.handle);
        ASSERT_EQ(ret, SINGLEO_ERROR_NONE);