use observer pattern instead of callback approach 59/310959/7
authorInki Dae <inki.dae@samsung.com>
Fri, 10 May 2024 06:11:53 +0000 (15:11 +0900)
committerInki Dae <inki.dae@samsung.com>
Mon, 13 May 2024 07:38:17 +0000 (16:38 +0900)
Use observer pattern instead of callback approach to get input feeded
data in runtime.

Until now, a specific service such as AutoZoom should have registered its own
callback function to Input service to receive captured input data
from a given input service such as camera. However, this approach made
it some limit because input relevant class called a callback of specific
service's class so some wrapper functions was needed to access its own members.

For cleanup, this patch introduces observer pattern by making each specific
service class to inherit IInputObserver class. with this patch, each specific
service class can register itself as input observer to input service like below,

// override inputFeedCb interface and implement it.
class AutoZoom::public IService, public IInputObserver
{
...
void inputFeedCb(BaseDataType &data) override;
...
}

// register itself as input observer to input service.
void AutoZoom::configure(...)
{
....
    _input_service->registerObserver(this);
....
}

In addition, this patch separates input configuration from ctor
and drops redundant code.

Change-Id: I83b3cadb031b68212fcfdd783054397d09a6b36a
Signed-off-by: Inki Dae <inki.dae@samsung.com>
16 files changed:
input/backends/camera_api/include/CameraApiBackend.h
input/backends/camera_api/src/CameraApiBackend.cpp
input/backends/include/ICameraBackend.h
input/backends/opencv/include/OpencvBackend.h
input/backends/opencv/src/OpencvBackend.cpp
input/include/ICameraService.h [deleted file]
input/include/IInputObserver.h [new file with mode: 0644]
input/include/IInputService.h
input/include/InputCamera.h
input/include/InputTypes.h
input/src/InputCamera.cpp
services/auto_zoom/include/AutoZoom.h
services/auto_zoom/src/AutoZoom.cpp
services/common/include/IService.h
services/common/include/ServiceFactory.h
services/singleo_native_capi.cpp

index 8e4e33a1f333ff7480d2f7dd43432452ac547918..017e34f372485fe65dba356307e076f3c65277fd 100644 (file)
@@ -25,6 +25,7 @@
 #include <opencv2/opencv.hpp>
 #include <condition_variable>
 
+#include "IInputObserver.h"
 #include "ICameraBackend.h"
 #include "SingleoInputManager.h"
 #include "InputTypes.h"
@@ -37,11 +38,10 @@ class CameraApiBackend : public ICameraBackend
 {
 private:
        camera_h _camera;
-       InputServiceCallbackType _userCb;
-       void *_userData;
        std::mutex _capture_mutex;
        std::condition_variable _capture_event;
        bool _isCaptured { false };
+       IInputObserver *_observer;
        cv::Mat _cvCaptureImage;
        camera_pixel_format_e _defaultPreviewPixelFormat { CAMERA_PIXEL_FORMAT_I420 };
        camera_pixel_format_e _defaultCapturePixelFormat { CAMERA_PIXEL_FORMAT_I420 };
@@ -86,7 +86,7 @@ public:
        CameraApiBackend();
        virtual ~CameraApiBackend();
 
-       void setUserCb(const InputServiceCallbackType &userCb, void *user_data) override;
+       void registerObserver(IInputObserver *observer) override;
        void configure(const CameraConfig &config) override;
        void capture(BaseDataType &out_data) override;
        void streamOn() override;
index 7def9b1bb0cf822b4c972960d93e6df364919844..f86e2b6a747a4f03d632cf696061bf683f811ea0 100644 (file)
@@ -73,19 +73,18 @@ void CameraApiBackend::setActivateCameraDevice(unsigned int id)
        SINGLEO_LOGD("Camera device id(%d) has been activated.", id);
 }
 
-void CameraApiBackend::setUserCb(const InputServiceCallbackType &userCb, void *user_data)
+void CameraApiBackend::registerObserver(IInputObserver *observer)
 {
-       _userCb = userCb;
-       _userData = user_data;
+       _observer = observer;
 
-       SINGLEO_LOGD("CameraApiBackend: user callback has been registered.");
+       SINGLEO_LOGD("CameraApiBackend: service has been registered.");
 }
 
 void CameraApiBackend::previewCb(camera_preview_data_s *data, void *user_data)
 {
        CameraApiBackend *context = static_cast<CameraApiBackend *>(user_data);
 
-       if (!context->_userCb)
+       if (!context->_observer)
                return;
 
        if (data->num_of_planes != 3 || data->format != CAMERA_PIXEL_FORMAT_I420) {
@@ -108,7 +107,7 @@ void CameraApiBackend::previewCb(camera_preview_data_s *data, void *user_data)
        data_type.byte_per_pixel = cv_dst.channels();
        data_type.ptr = cv_dst.data;
 
-       context->_userCb(data_type, context->_userData);
+       context->_observer->inputFeedCb(data_type);
 }
 
 bool CameraApiBackend::previewFormatCb(camera_pixel_format_e format, void *user_data)
index a1b7e59575fd896347704c46b03f4513513305f4..6dbc1908254c6d6cde145ad93b7c52cb53b5b3b4 100644 (file)
@@ -17,8 +17,9 @@
 #ifndef __ICAMERA_BACKEND_H__
 #define __ICAMERA_BACKEND_H__
 
-#include <functional>
+#include "IService.h"
 #include "InputTypes.h"
+#include "IInputObserver.h"
 #include "SingleoInputManager.h"
 
 namespace singleo
@@ -30,7 +31,7 @@ class ICameraBackend
 public:
        virtual ~ICameraBackend() {};
 
-       virtual void setUserCb(const InputServiceCallbackType &userCb, void *user_data) = 0;
+       virtual void registerObserver(IInputObserver *observer) = 0;
        virtual void configure(const CameraConfig &config) = 0;
        virtual void capture(BaseDataType &out_data) = 0;
        virtual void streamOn() = 0;
index c70b821ddb7f31cade3e918d91c1b4d7d79bb82b..855149e73f8ae27b5c9d72fa722bb6588688f9a5 100644 (file)
@@ -35,8 +35,7 @@ class OpencvBackend : public ICameraBackend
 private:
        std::unique_ptr<cv::VideoCapture> _video_capture;
        cv::Mat _captured_image;
-       InputServiceCallbackType _userCb;
-       void *_userData;
+       IInputObserver *_observer;
        std::unique_ptr<std::thread> _thread_handle;
        bool _exit_thread { false };
        std::unordered_set<unsigned int> _camera_ids;
@@ -51,7 +50,7 @@ public:
        OpencvBackend();
        virtual ~OpencvBackend();
 
-       void setUserCb(const InputServiceCallbackType &userCb, void *user_data) override;
+       void registerObserver(IInputObserver *observer) override;
        void configure(const CameraConfig &config) override;
        void capture(BaseDataType &out_data) override;
        void streamOn() override;
index 634cb933f534a368b6fcfa36cd361c8f3d4239a1..bb8572e6cfeeae67ed1c41274d1d60a71fbf67a3 100644 (file)
@@ -54,12 +54,11 @@ void OpencvBackend::updateAvailableCameraDevices()
        }
 }
 
-void OpencvBackend::setUserCb(const InputServiceCallbackType &userCb, void *user_data)
+void OpencvBackend::registerObserver(IInputObserver *observer)
 {
-       _userCb = userCb;
-       _userData = user_data;
+       _observer = observer;
 
-       SINGLEO_LOGD("OpencvBackend: user callback has been registered.");
+       SINGLEO_LOGD("OpencvBackend: service has been registered.");
 }
 
 void OpencvBackend::setActivateCameraDevice(unsigned int id)
@@ -117,8 +116,8 @@ void OpencvBackend::threadLoop()
        while (!_exit_thread) {
                capture(data);
 
-               if (_userCb)
-                       _userCb(data, _userData);
+               if (_observer)
+                       _observer->inputFeedCb(data);
        }
 }
 
diff --git a/input/include/ICameraService.h b/input/include/ICameraService.h
deleted file mode 100644 (file)
index eea9f4a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * 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 __ICAMERA_SERVICE_H__
-#define __ICAMERA_SERVICE_H__
-
-#include "InputTypes.h"
-
-namespace singleo
-{
-namespace input
-{
-class ICameraService
-{
-public:
-       virtual ~ICameraService() {};
-
-       virtual void setUserCb(const InputServiceCallbackType &userCb, void *user_data) = 0;
-       virtual void configure() = 0;
-       virtual void capture(BaseDataType &data) = 0;
-       virtual void streamOn() = 0;
-       virtual void streamOff() = 0;
-};
-
-} // input
-} // singleo
-
-#endif
\ No newline at end of file
diff --git a/input/include/IInputObserver.h b/input/include/IInputObserver.h
new file mode 100644 (file)
index 0000000..d25a14d
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ * 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 __IINPUT_OBSERVER_H__
+#define __IINPUT_OBSERVER_H__
+
+#include "SingleoCommonTypes.h"
+
+namespace singleo
+{
+namespace input
+{
+class IInputObserver
+{
+public:
+       virtual ~IInputObserver() {};
+
+       virtual void inputFeedCb(BaseDataType &data) = 0;
+};
+
+} // input
+} // singleo
+
+#endif
index ae2c3b848c078c84b736164470083747bea7c590..8ab3f4c05e4bc4f409b6b702b2fcd44d50359d5d 100644 (file)
@@ -17,7 +17,7 @@
 #ifndef __IINPUT_SERVICE_H__
 #define __IINPUT_SERVICE_H__
 
-#include <functional>
+#include "IInputObserver.h"
 #include "InputTypes.h"
 
 namespace singleo
@@ -29,7 +29,7 @@ class IInputService
 public:
        virtual ~IInputService() {};
 
-       virtual void setUserCb(const InputServiceCallbackType &userCb, void *user_data) = 0;
+       virtual void registerObserver(IInputObserver *observer) = 0;
        virtual void configure(CameraConfig &config) = 0;
        virtual void capture(BaseDataType &data) = 0;
        virtual void streamOn() = 0;
index ee8910629254187b904b5c928c3526ac59696556..a57fbe43cf7a450062f950e27014bd62d42f89df 100644 (file)
@@ -35,7 +35,7 @@ public:
        explicit InputCamera(CameraBackendType backend_type);
        virtual ~InputCamera();
 
-       void setUserCb(const InputServiceCallbackType &userCb, void *user_data) override;
+       void registerObserver(IInputObserver *observer) override;
        void configure(CameraConfig &config) override;
        void capture(BaseDataType &data) override;
        void streamOn() override;
index 43c4603e04a7ef6ef95da22aa5ea6f703ba9ddba..1681fdb16ba5a88d28720365b6cd1791665c319e 100644 (file)
@@ -54,8 +54,6 @@ struct ScreenCaptureConfig : public InputConfigBase {
        unsigned int y_resolution {};
 };
 
-using InputServiceCallbackType = std::function<void(BaseDataType &data, void *user_data)>;
-
 } // input
 } // singleo
 
index d8d1bd159f844e8d32db632234126ec127497bf6..0b556ef2284f3dc09a6fd3d72247977ab8926d3b 100644 (file)
@@ -42,9 +42,9 @@ InputCamera::InputCamera(CameraBackendType backend_type)
 InputCamera::~InputCamera()
 {}
 
-void InputCamera::setUserCb(const std::function<void(BaseDataType &data, void *user_data)> &userCb, void *user_data)
+void InputCamera::registerObserver(IInputObserver *observer)
 {
-       _input->setUserCb(userCb, user_data);
+       _input->registerObserver(observer);
 }
 
 void InputCamera::configure(CameraConfig &config)
index c75d83b58c3778b10e258874e20898cba1cdb404..d0e5901ebc5b173d1552e2149e947747cbc76aa2 100644 (file)
@@ -23,6 +23,7 @@
 #include "IInputService.h"
 #include "SingleoInferenceTypes.h"
 #include "InputTypes.h"
+#include "IInputObserver.h"
 #include "ServiceDataType.h"
 #include "DataTypes.h"
 #include "AsyncManager.h"
@@ -35,7 +36,7 @@ namespace services
 {
 namespace autozoom
 {
-class AutoZoom : public IService
+class AutoZoom : public IService, public input::IInputObserver
 {
 private:
        std::unique_ptr<TaskManager> _taskManager;
@@ -58,17 +59,18 @@ private:
        void *_user_data {};
 
 public:
-       explicit AutoZoom(input::InputConfigBase &config);
+       AutoZoom();
        virtual ~AutoZoom();
 
-       static IService *create(input::InputConfigBase &config)
+       static IService *create()
        {
-               return new AutoZoom(config);
+               return new AutoZoom();
        }
 
        // This function will be called by specific input service internally.
        // Ps. caller has to provide captured data with concrete class object as data parameter.
-       static void inputServiceCb(BaseDataType &data, void *user_data);
+       void inputFeedCb(BaseDataType &data) override;
+       void configure(input::InputConfigBase &config) override;
        void add_input(BaseDataType &input_data) override;
        void perform() override;
        void performAsync() override;
@@ -77,20 +79,7 @@ public:
        void registerUserCallback(singleo_user_cb_t user_cb, void *user_data) override;
        void unregisterUserCallback() override;
 
-       std::unique_ptr<AsyncManager<ImageDataType, AutoZoomResult> > &getAsyncManager()
-       {
-               return _async_manager;
-       }
-
-       std::unique_ptr<IPostprocessor> &getPostprocessor()
-       {
-               return _postprocessor;
-       }
-
-       std::unique_ptr<TaskManager> &getTaskManager()
-       {
-               return _taskManager;
-       }
+       void runTaskManager(BaseDataType &input_data);
 };
 
 } // autozoom
index 8e0e7d354aa0c68830ea7384503a77695251333a..d6a0556c6f7080793e34eed83e5c189c397518db 100644 (file)
@@ -39,7 +39,7 @@ namespace autozoom
 {
 REGISTER_SERVICE(AutoZoom)
 
-AutoZoom::AutoZoom(InputConfigBase &config)
+AutoZoom::AutoZoom()
 {
        // In default, we will use Inference service factory for Mediavision to use Mediavision framework
        // for inference service. TODO. introduce meta config file approach later.
@@ -50,7 +50,11 @@ AutoZoom::AutoZoom(InputConfigBase &config)
        face_detection_node->setInferenceService(factory->createFaceDetection());
        _taskManager = make_unique<TaskManager>();
        _taskManager->addNode(face_detection_node);
+       _postprocessor = make_unique<Postprocessor>();
+}
 
+void AutoZoom::configure(InputConfigBase &config)
+{
        // Create InputCamera service if input service type is CAMERA.
        if (config._input_feed_type == InputFeedType::CAMERA) {
                CameraConfig cameraConfig;
@@ -63,13 +67,11 @@ AutoZoom::AutoZoom(InputConfigBase &config)
                }
 
                _input_service = make_unique<InputCamera>(cameraConfig.backend_type);
-               _input_service->setUserCb(inputServiceCb, static_cast<void *>(this));
+               _input_service->registerObserver(this);
                _input_service->configure(cameraConfig);
 
                SINGLEO_LOGD("Camera input service has been initialized.");
        }
-
-       _postprocessor = make_unique<Postprocessor>();
 }
 
 AutoZoom::~AutoZoom()
@@ -78,10 +80,8 @@ AutoZoom::~AutoZoom()
                _input_service->streamOff();
 }
 
-void AutoZoom::inputServiceCb(BaseDataType &data, void *user_data)
+void AutoZoom::inputFeedCb(BaseDataType &data)
 {
-       auto auto_zoom = static_cast<AutoZoom *>(user_data);
-
        ImagePreprocessor preprocessor(data);
        ImageDataType preprocessed = dynamic_cast<ImageDataType &>(preprocessor.getData());
        ImageDataType copied = preprocessed;
@@ -92,20 +92,19 @@ void AutoZoom::inputServiceCb(BaseDataType &data, void *user_data)
        copied.ptr = new unsigned char[buffer_size];
        memcpy(copied.ptr, preprocessed.ptr, buffer_size);
 
-       if (auto_zoom->_user_cb) {
+       if (_user_cb) {
                // if postprocessor isn't in progress, postprocess current camera preview image.
-               if (!auto_zoom->getPostprocessor()->isWorking()) {
-                       ImageDataType zoom_data = dynamic_cast<ImageDataType &>(auto_zoom->getPostprocessor()->getOutput(copied));
+               if (!_postprocessor->isWorking()) {
+                       ImageDataType zoom_data = dynamic_cast<ImageDataType &>(_postprocessor->getOutput(copied));
 
-                       auto_zoom->_user_cb(zoom_data.ptr, zoom_data.width, zoom_data.height, zoom_data.byte_per_pixel,
-                                                               auto_zoom->_user_data);
+                       _user_cb(zoom_data.ptr, zoom_data.width, zoom_data.height, zoom_data.byte_per_pixel, _user_data);
                } else {
-                       auto_zoom->_user_cb(copied.ptr, copied.width, copied.height, copied.byte_per_pixel, auto_zoom->_user_data);
+                       _user_cb(copied.ptr, copied.width, copied.height, copied.byte_per_pixel, _user_data);
                }
        }
 
        // Make sure to release copied buffer if incoming queue isn't empty so skipped pushing the buffer.
-       if (auto_zoom->getAsyncManager()->pushInput(copied) != SINGLEO_ERROR_NONE)
+       if (_async_manager->pushInput(copied) != SINGLEO_ERROR_NONE)
                delete copied.ptr;
 }
 
@@ -137,6 +136,14 @@ void AutoZoom::add_input(BaseDataType &input_data)
        }
 }
 
+void AutoZoom::runTaskManager(BaseDataType &input_data)
+{
+       _taskManager->addInput(input_data);
+       _taskManager->run();
+
+       updateResult(input_data);
+}
+
 void AutoZoom::perform()
 {
        ImagePreprocessor preprocessor;
@@ -148,9 +155,7 @@ void AutoZoom::perform()
                auto data = dynamic_pointer_cast<FileDataType>(result);
 
                preprocessor.update(*data);
-               _taskManager->addInput(preprocessor.getData());
-               _taskManager->run();
-               updateResult(preprocessor.getData());
+               runTaskManager(preprocessor.getData());
                return;
        } else {
                ImageDataType input_data;
@@ -159,9 +164,7 @@ void AutoZoom::perform()
                preprocessor.update(input_data);
        }
 
-       _taskManager->addInput(preprocessor.getData());
-       _taskManager->run();
-       updateResult(preprocessor.getData());
+       runTaskManager(preprocessor.getData());
 }
 
 void AutoZoom::performAsync()
@@ -178,9 +181,7 @@ void AutoZoom::performAsync()
        _async_manager->registerInvokeCb(this, [this](IService *service, BaseDataType &data) {
                auto auto_zoom = static_cast<AutoZoom *>(service);
 
-               auto_zoom->getTaskManager()->addInput(data);
-               auto_zoom->getTaskManager()->run();
-               auto_zoom->updateResult(data);
+               auto_zoom->runTaskManager(data);
 
                // This buffer was allocated and copied in inputServiceCb callback.
                // So make sure to release this buffer here.
index de774ac8f823cdc779aa9b73c349b9ef1cc44a25..9c76b3a3aa2cf96dac4589f2cabf553bcf0e12be 100644 (file)
@@ -20,6 +20,7 @@
 #include "singleo_native_capi_internal.h"
 
 #include "SingleoCommonTypes.h"
+#include "InputTypes.h"
 #include "IPreprocessor.h"
 
 namespace singleo
@@ -32,6 +33,7 @@ public:
        virtual ~IService() {};
 
        virtual void add_input(BaseDataType &input_data) = 0;
+       virtual void configure(input::InputConfigBase &config) = 0;
        virtual void perform() = 0;
        virtual void performAsync() = 0;
        virtual void getResultCnt(unsigned int *cnt) = 0;
index 75101405f6821855440a24168497fa5189aea45a..77216ae1df5a369c84d2a3f62ab15fe23b8ed81e 100644 (file)
@@ -30,14 +30,14 @@ namespace services
 class ServiceFactory
 {
 public:
-       using createFunc = IService *(*) (input::InputConfigBase &config);
+       using createFunc = IService *(*) ();
        static std::map<std::string, createFunc> __service_table;
 
-       static IService *create(const std::string &className, input::InputConfigBase &config)
+       static IService *create(const std::string &className)
        {
                auto it = __service_table.find(className);
                if (it != __service_table.end())
-                       return it->second(config);
+                       return it->second();
 
                return nullptr;
        }
index d2d26a1137156fc81d877bcb5dcea232cad2fc5d..d353842353cd1cb4174af433e50fcb06276f81c8 100644 (file)
@@ -28,10 +28,12 @@ int singleo_service_create(const char *option, singleo_service_h *handle)
                async_mode = parser.getAsyncMode();
 
                if (ServiceType::AUTO_ZOOM == parser.getServiceType()) {
-                       service_handle = ServiceFactory::create("AutoZoom", parser.getConfig(parser.getInputFeedType()));
+                       service_handle = ServiceFactory::create("AutoZoom");
                        if (!service_handle)
                                throw InvalidOperation("Failed to create AutoZoom service.");
 
+                       service_handle->configure(parser.getConfig(parser.getInputFeedType()));
+
                        SINGLEO_LOGD("AutoZoom service has been created.");
                        service_type = ServiceType::AUTO_ZOOM;
                } else {