// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #include "stereoPipeline.hpp" #include "buffer.hpp" #include "libvideostitch/imageMergerFactory.hpp" #include "libvideostitch/imageFlowFactory.hpp" #include "libvideostitch/imageWarperFactory.hpp" #include <iostream> #include <future> namespace VideoStitch { namespace Core { StereoPipeline::StereoPipeline(PanoStitcherImplBase<StereoOutput>* left, PanoStitcherImplBase<StereoOutput>* right, const std::vector<Input::VideoReader*>& readers, const std::vector<PreProcessor*>& preprocs, PostProcessor* postproc) : VideoPipeline(readers, preprocs, postproc), leftStitcher(left), rightStitcher(right) {} StereoPipeline::~StereoPipeline() { delete leftStitcher; delete rightStitcher; } Potential<StereoPipeline> StereoPipeline::createStereoPipeline(PanoStitcherImplBase<StereoOutput>* left, PanoStitcherImplBase<StereoOutput>* right, const std::vector<Input::VideoReader*>& readers, const std::vector<PreProcessor*>& preprocs, PostProcessor* postproc) { StereoPipeline* ret = new StereoPipeline(left, right, readers, preprocs, postproc); Status initStatus = ret->init(); if (!initStatus.ok()) { delete ret; ret = nullptr; return initStatus; } return ret; } /** * * - do not load concurrently (avoid concurrent accesses to disk) * - do not write while loading * - loading, transmitting and mapping are independant for two different images * - merging requires the previous and current images to be mapped. * - keep frames independant for the moment, i.e. do not start images for next frames during current frame. * * Image1 | load | map | merge | | load | * Image2 | | load | map | merge | * Image3 | | load | map |XXXXX| merge | * Image4 | | load | map |XXXXXXXXX| merge | * Image5 | | load | map |XXX| merge | * * | readback | write | * * The main thread orchestrates the loading, delegating asynchronous logic to cuda streams. */ Status StereoPipeline::stitch(mtime_t date, frameid_t frame, std::map<readerid_t, Input::PotentialFrame>& inputBuffers, StereoOutput* output) { std::vector<ExtractOutput*> ext; return stitchAndExtract(date, frame, inputBuffers, output, ext, nullptr); } Status StereoPipeline::stitchAndExtract(mtime_t date, FrameRate frameRate, std::map<readerid_t, Input::PotentialFrame>& inputBuffers, StereoOutput* output, std::vector<ExtractOutput*> extracts, AlgorithmOutput* algo) { Status leftStatus; Status rightStatus; FAIL_RETURN(extract(date, frameRate, inputBuffers, extracts, algo)); auto frame = frameRate.timestampToFrame(date); // stereo in single gpu // can't fix the race condition, and it's not slower sequentially leftStatus = leftStitcher->stitch(date, frame, postproc, inputBuffers, readers, preprocs, output); rightStatus = rightStitcher->stitch(date, frame, postproc, inputBuffers, readers, preprocs, output); if (!leftStatus.ok() && !rightStatus.ok()) { return {Origin::Stitcher, ErrType::RuntimeError, "Stitching of both eyes failed", leftStatus}; } if (!leftStatus.ok()) { return {Origin::Stitcher, ErrType::RuntimeError, "Stitching of left eye failed", leftStatus}; } if (!rightStatus.ok()) { return {Origin::Stitcher, ErrType::RuntimeError, "Stitching of right eye failed", rightStatus}; } return Status::OK(); } Status StereoPipeline::setup(const ImageMergerFactory& mergerFactory, const ImageWarperFactory& warperFactory, const ImageFlowFactory& flowFactory, const StereoRigDefinition* rig) { auto leftHandle = std::async(std::launch::async, &PanoStitcherImplBase<StereoOutput>::setup, leftStitcher, std::cref(mergerFactory), std::cref(warperFactory), std::cref(flowFactory), readers, rig); auto rightHandle = std::async(std::launch::async, &PanoStitcherImplBase<StereoOutput>::setup, rightStitcher, std::cref(mergerFactory), std::cref(warperFactory), std::cref(flowFactory), readers, rig); // wait for both tasks to finish auto leftStatus = leftHandle.get(); auto rightStatus = rightHandle.get(); FAIL_RETURN(leftStatus); return rightStatus; } } // namespace Core } // namespace VideoStitch