// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #pragma once #include "frameBuffer.hpp" #include "stitchOutput.hpp" #include "stereoOutput.hpp" #include "libvideostitch/controller.hpp" #include #include #include #include #include namespace VideoStitch { namespace Output { class VideoWriter; class StereoWriter; } // namespace Output namespace Core { /** * A set of host panoramic buffers that can be filled asynchronously. * * It buffers N frames and waits when no frame is available. * Warning: on destruction, waits for all pending frames to be written. * This can effectively block forever if there are missing frames. * * Frames should start at 0. * * Past frames will be ignored, current frame undergoes a * special treatment if refilled (for restitch). * Future frames are buffered when possible, if not fill() * becomes blocking. * * 3 threads are manipulating the panoramic buffers. * - "register callbacks" thread : read all buffers, don't change their state (stitched / blank / in use), * modify them * - "stitcher" thread : pop one from blanks, put one in stitched * - "consumer" thread : pop one from stitched, put one in blank */ template class AsyncBuffer { public: typedef FrameBuffer* Frame; typedef FrameBuffer FB; Status initialize(const std::vector>&, const std::vector>& writers); virtual ~AsyncBuffer(); Status pushVideo(mtime_t date); Status registerWriters(const std::vector>&); Status registerWriter(std::shared_ptr); protected: void synchronize(FrameBuffer* frame) { frame->streamSynchronize(); } FrameBuffer* getUsedFrame(mtime_t date); FrameBuffer* getCurrentFrame(mtime_t date); // Frames std::mutex bkMu; std::condition_variable bkCond; std::deque blankFrames; std::mutex stMu; std::condition_variable stCond; std::deque> stitchedFrames; bool shutDown = false; // Hold the frames while the stitcher schedules everything // The stitcher asks first for the device buffer in which // to schedule the stitching, then, once everything is scheduled, // it asks for the host buffer where to render the results. // // Since multiple frames can be scheduled at the same time, if more // than double buffering is used, we ask // to store the correspondence in between. // Unlocked, since only the "stitcher" thread // accesses it. std::map inUse; // used by the "register" thread only std::vector allFrames; }; class AsyncSourceBuffer : public AsyncBuffer { public: static Potential create(const std::vector>&, const std::vector>&); GPU::Surface& acquireFrame(mtime_t date, GPU::Stream& stream); }; class AsyncPanoBuffer : public AsyncBuffer { public: static Potential create(const std::vector>&, const std::vector>&); PanoSurface& acquireFrame(mtime_t date); }; /** * A set of host stereoscopic buffers that can be filled asynchronously. * * It buffers N frames and waits when no frame is available. * Warning: on destruction, waits for all pending frames to be written. * This can effectively block forever if there are missing frames. * * Frames should start at 0. * * Past frames will be ignored, current frame undergoes a * special treatment if refilled (for restitch). * Future frames are buffered when possible, if not fill() * becomes blocking. * 3 types of threads are manipulating the panoramic buffers. * - "register callbacks" thread : read all buffers, don't change their state (stitched / blank / in use), * modify them * - 2 "stitcher" threads : pop one from blanks, put one in stitched, collaborate (one pop - one push) * - "consumer" thread : pop one from stitched, put one in blank */ class AsyncStereoBuffer { public: typedef std::pair Frame; typedef PanoFrameBuffer FB; static Potential create(const std::vector>&, const std::vector>& writers); Status initialize(const std::vector>&, const std::vector>& writers); virtual ~AsyncStereoBuffer(); PanoSurface& acquireLeftFrame(mtime_t); PanoSurface& acquireRightFrame(mtime_t); Status pushVideo(mtime_t date, Eye eye); Status registerWriters(const std::vector>&); Status registerWriter(std::shared_ptr); protected: AsyncStereoBuffer() {} PanoSurface& acquireFrame(mtime_t date, Eye eye); void synchronize(Frame frame); Frame getUsedFrame(mtime_t date); Frame getCurrentFrame(mtime_t date); // Frames std::mutex bkMu; std::condition_variable bkCond; std::deque blankFrames; std::mutex stMu; std::condition_variable stCond; std::deque> stitchedFrames; bool shutDown = false; // Hold the frames while the stitcher schedules everything // The stitcher asks first for the device buffer in which // to schedule the stitching, then, once everything is scheduled, // it asks for the host buffer where to render the results. // // Since multiple frames can be scheduled at the same time, if more // than double buffering is used, we ask // to store the correspondence in between. std::map inUse; // used by the "register" thread only std::vector allFrames; }; template class AsyncBufferedOutput : public Pimpl, protected AsyncBuffer, protected Pusher { public: typedef typename Pimpl::Writer Writer; AsyncBufferedOutput(const std::vector>&, const std::vector>& writers); virtual ~AsyncBufferedOutput(); virtual bool setRenderers(const std::vector>&) override; virtual bool addRenderer(std::shared_ptr renderer) override { return Pusher::addRenderer(renderer); } virtual bool removeRenderer(const std::string& name) override { return Pusher::removeRenderer(name); } virtual bool setWriters(const std::vector>&) override; virtual bool addWriter(std::shared_ptr) override; virtual bool removeWriter(const std::string&) override; virtual bool updateWriter(const std::string&, const Ptv::Value&) override; protected: Status initialize(const std::vector>&, const std::vector>& writers); private: static void consumerThread(AsyncBufferedOutput* that); std::thread* worker; std::atomic shutdown; }; class AsyncSourceOutput : public AsyncBufferedOutput, PanoDeviceDefinition> { public: typedef AsyncBufferedOutput, PanoDeviceDefinition> Base; static Potential create(const std::vector>& surfs, const std::vector>& renderers, const std::vector>& writers, int source) { AsyncSourceOutput* aso = new AsyncSourceOutput(surfs, renderers, writers, source); FAIL_RETURN(aso->initialize(surfs, writers)); return aso; } Status pushVideo(mtime_t date) override { return AsyncBuffer::pushVideo(date); } GPU::Surface& acquireFrame(mtime_t date, GPU::Stream& stream) override { return AsyncSourceBuffer::acquireFrame(date, stream); } private: AsyncSourceOutput(const std::vector>& surfs, const std::vector>& renderers, const std::vector>& writers, int source) : Base(surfs, writers) { sourceIdx = source; setRenderers(renderers); } }; class AsyncStitchOutput : public AsyncBufferedOutput, PanoDeviceDefinition> { public: static Potential create(const std::vector>& surfs, const std::vector>& renderers, const std::vector>& writers) { AsyncStitchOutput* aso = new AsyncStitchOutput(surfs, renderers, writers); FAIL_RETURN(aso->initialize(surfs, writers)); return aso; } Status pushVideo(mtime_t date) override { return AsyncBuffer::pushVideo(date); } virtual void setCompositor(const std::shared_ptr& c) override { WriterPusher::setCompositor(c); } PanoSurface& acquireFrame(mtime_t date) override { return AsyncPanoBuffer::acquireFrame(date); } protected: typedef AsyncBufferedOutput, PanoDeviceDefinition> Base; AsyncStitchOutput(const std::vector>& surfs, const std::vector>& renderers, const std::vector>& writers) : Base(surfs, writers) { setRenderers(renderers); } }; class AsyncStereoOutput : public AsyncBufferedOutput { public: static Potential create(const std::vector>& surfs, const std::vector>& renderers, const std::vector>& writers) { AsyncStereoOutput* ret = new AsyncStereoOutput(surfs, renderers, writers); FAIL_RETURN(ret->initialize(surfs, writers)); return ret; } virtual ~AsyncStereoOutput() {} Status pushVideo(mtime_t date, Eye eye) override { return AsyncStereoBuffer::pushVideo(date, eye); } virtual PanoSurface& acquireLeftFrame(mtime_t date) override { return AsyncStereoBuffer::acquireLeftFrame(date); } virtual PanoSurface& acquireRightFrame(mtime_t date) override { return AsyncStereoBuffer::acquireRightFrame(date); } protected: AsyncStereoOutput(const std::vector>& surfs, const std::vector>& renderers, const std::vector>& writers) : AsyncBufferedOutput(surfs, writers) { setRenderers(renderers); } }; } // namespace Core } // namespace VideoStitch