// Copyright (C) 2018-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 // #pragma once #include #include #include #include #include #include #include class InputChannel; class IInputSource { public: virtual bool read(cv::Mat& mat, const std::shared_ptr& caller) = 0; virtual void addSubscriber(const std::weak_ptr& inputChannel) = 0; virtual cv::Size getSize() = 0; virtual void lock() { sourceLock.lock(); } virtual void unlock() { sourceLock.unlock(); } virtual ~IInputSource() = default; private: std::mutex sourceLock; }; class InputChannel: public std::enable_shared_from_this { // note: public inheritance public: InputChannel(const InputChannel&) = delete; InputChannel& operator=(const InputChannel&) = delete; static std::shared_ptr create(const std::shared_ptr& source) { auto tmp = std::shared_ptr(new InputChannel(source)); source->addSubscriber(tmp); return tmp; } bool read(cv::Mat& mat) { readQueueMutex.lock(); if (readQueue.empty()) { readQueueMutex.unlock(); source->lock(); readQueueMutex.lock(); if (readQueue.empty()) { bool res = source->read(mat, shared_from_this()); readQueueMutex.unlock(); source->unlock(); return res; } else { source->unlock(); } } mat = readQueue.front().clone(); readQueue.pop(); readQueueMutex.unlock(); return true; } void push(const cv::Mat& mat) { readQueueMutex.lock(); readQueue.push(mat); readQueueMutex.unlock(); } cv::Size getSize() { return source->getSize(); } private: explicit InputChannel(const std::shared_ptr& source): source{source} {} std::shared_ptr source; std::queue> readQueue; std::mutex readQueueMutex; }; class VideoCaptureSource: public IInputSource { public: VideoCaptureSource(const cv::VideoCapture& videoCapture, bool loop): videoCapture{videoCapture}, loop{loop}, imSize{static_cast(videoCapture.get(cv::CAP_PROP_FRAME_WIDTH)), static_cast(videoCapture.get(cv::CAP_PROP_FRAME_HEIGHT))} {} bool read(cv::Mat& mat, const std::shared_ptr& caller) override { if (!videoCapture.read(mat)) { if (loop) { videoCapture.set(cv::CAP_PROP_POS_FRAMES, 0); videoCapture.read(mat); } else { return false; } } if (1 != subscribedInputChannels.size()) { cv::Mat shared = mat.clone(); for (const std::weak_ptr& weakInputChannel : subscribedInputChannels) { try { std::shared_ptr sharedInputChannel = std::shared_ptr(weakInputChannel); if (caller != sharedInputChannel) { sharedInputChannel->push(shared); } } catch (const std::bad_weak_ptr&) {} } } return true; } void addSubscriber(const std::weak_ptr& inputChannel) override { subscribedInputChannels.push_back(inputChannel); } cv::Size getSize() override { return imSize; } private: std::vector> subscribedInputChannels; cv::VideoCapture videoCapture; bool loop; cv::Size imSize; }; class ImageSource: public IInputSource { public: ImageSource(const cv::Mat& im, bool loop): im{im.clone()}, loop{loop} {} // clone to avoid image changing bool read(cv::Mat& mat, const std::shared_ptr& caller) override { if (!loop) { auto subscribedInputChannelsIt = subscribedInputChannels.find(caller); if (subscribedInputChannels.end() == subscribedInputChannelsIt) { return false; } else { subscribedInputChannels.erase(subscribedInputChannelsIt); mat = im; return true; } } else { mat = im; return true; } } void addSubscriber(const std::weak_ptr& inputChannel) override { if (false == subscribedInputChannels.insert(inputChannel).second) throw std::invalid_argument("The insertion did not take place"); } cv::Size getSize() override { return im.size(); } private: std::set, std::owner_less>> subscribedInputChannels; cv::Mat im; bool loop; };