// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #pragma once #include "libvideostitch/input.hpp" #include "libvideostitch/inputDef.hpp" #include "libvideostitch/inputFactory.hpp" #include "libvideostitch/ptv.hpp" #include "../gpu/testing.hpp" #include <atomic> #include <thread> namespace VideoStitch { namespace Testing { enum class AudioMockTest { EOSInput = 0, TryAgainInput, NormalInput }; enum class ReaderClockResolution { Millisecond, Microsecond, }; /** * A fake reader that * - sleeps for readFrameTime when reading frames and seeking * - tracks number of calls to readFrame (readFrameCalls, readFrameExits) * - simulates a date computed through frame rate and given first frame * - stores the date in the first 8 Bytes of the frame */ class FakeVideoReader : public Input::VideoReader { public: FakeVideoReader(readerid_t id, int64_t width, int64_t height, FrameRate frameRate, ReaderClockResolution clockResolution, frameid_t firstFrame, frameid_t lastFrame, const unsigned char *maskHostBuffer, int readFrameTime, bool isProcedural, std::atomic<int> *readFrameCalls = nullptr, std::atomic<int> *readFrameExits = nullptr) : Reader(id), VideoReader(width, height, width * height * 4, PixelFormat::RGBA, Host, frameRate, firstFrame, lastFrame, isProcedural, maskHostBuffer), frameRate(frameRate), clockResolution(clockResolution), frameID(firstFrame), readFrameCalls(readFrameCalls), readFrameExits(readFrameExits), readFrameTime(readFrameTime) { // storing the date in the video buffer on readFrame assert(width * height * 4 >= (int64_t)sizeof(mtime_t)); } virtual ~FakeVideoReader() {} virtual Input::ReadStatus readFrame(mtime_t &date, unsigned char *dst) { if (readFrameCalls != nullptr) { (*readFrameCalls)++; } std::this_thread::sleep_for(std::chrono::milliseconds(readFrameTime)); switch (clockResolution) { case ReaderClockResolution::Microsecond: date = frameID * 1000000 * frameRate.den / frameRate.num; break; case ReaderClockResolution::Millisecond: date = frameID * 1000 * frameRate.den / frameRate.num; date *= 1000; break; default: assert(false); break; } mtime_t *dstlong = (mtime_t *)dst; *dstlong = date; ++frameID; if (readFrameExits != nullptr) { (*readFrameExits)++; } return Input::ReadStatus::OK(); } virtual Status seekFrame(frameid_t targetFrame) { std::this_thread::sleep_for(std::chrono::milliseconds(readFrameTime)); frameID = targetFrame; return Status::OK(); } private: FrameRate frameRate; ReaderClockResolution clockResolution; frameid_t frameID; std::atomic<int> *readFrameCalls; std::atomic<int> *readFrameExits; int readFrameTime; }; class FakeAudioReader : public Input::AudioReader { public: FakeAudioReader(readerid_t id, FrameRate frameRate, frameid_t firstFrame, AudioMockTest testCase) : Reader(id), AudioReader(Audio::ChannelLayout::STEREO, Audio::SamplingRate::SR_44100, Audio::SamplingDepth::DBL_P), timestamp(0), testCase(testCase) { timestamp = static_cast<mtime_t>(firstFrame) * static_cast<mtime_t>(frameRate.den) * 1000000 / static_cast<mtime_t>(frameRate.num); } virtual ~FakeAudioReader() {} virtual Input::ReadStatus readSamples(size_t nbSamples, Audio::Samples &audioSamples) { Audio::audioSample_t *data[MAX_AUDIO_CHANNELS]; // Manage only format stereo planar double data[Audio::getChannelIndexFromChannelMap(Audio::SPEAKER_FRONT_LEFT)] = new Audio::audioSample_t[nbSamples]; data[Audio::getChannelIndexFromChannelMap(Audio::SPEAKER_FRONT_RIGHT)] = new Audio::audioSample_t[nbSamples]; audioSamples = Audio::Samples(getSpec().sampleRate, getSpec().sampleDepth, getSpec().layout, static_cast<mtime_t>(timestamp), (uint8_t **)data, nbSamples); mtime_t a = static_cast<mtime_t>(std::round(nbSamples * 1000000. / Audio::getIntFromSamplingRate(getSpec().sampleRate))); timestamp += a; return Input::ReadStatus::OK(); } virtual Status seekFrame(mtime_t date) { return Status::OK(); } virtual size_t available() { if (testCase == AudioMockTest::TryAgainInput || testCase == AudioMockTest::EOSInput) { return 0; } return 1024; } virtual bool eos() { if (testCase == AudioMockTest::EOSInput) { return true; } return false; } private: mtime_t timestamp; AudioMockTest testCase; }; class FakeAVReader : public FakeVideoReader, public FakeAudioReader { public: FakeAVReader(readerid_t id, int64_t width, int64_t height, FrameRate frameRate, frameid_t firstFrame, frameid_t lastFrame, const unsigned char *maskHostBuffer, int readFrameTime, bool isProcedural, AudioMockTest testCase, std::atomic<int> *readFrameCalls = nullptr, std::atomic<int> *readFrameExits = nullptr, ReaderClockResolution clockResolution = ReaderClockResolution::Microsecond) : Reader(id), FakeVideoReader(id, width, height, frameRate, clockResolution, firstFrame, lastFrame, maskHostBuffer, readFrameTime, isProcedural, readFrameCalls, readFrameExits), FakeAudioReader(id, frameRate, firstFrame, testCase) {} virtual ~FakeAVReader() {} }; /** * A fake reader factory that ignores the given config and creates configurable readers * from arguments at factory creation time */ class FakeReaderFactory : public Input::ReaderFactory { public: FakeReaderFactory(int readFrameTime, std::atomic<int> *readFrameCalls = nullptr, std::atomic<int> *readFrameExits = nullptr) : frameRate({10, 0}), firstFrame(0), lastFrame(10), maskHostBuffer(NULL), readFrameCalls(readFrameCalls), readFrameExits(readFrameExits), readFrameTime(readFrameTime) {} /** * Reader option setters. * @{ */ void setFrameRate(FrameRate value) { frameRate = value; } void setFirstFrame(frameid_t value) { firstFrame = value; } void setLastFrame(frameid_t value) { lastFrame = value; } void setMaskHostBuffer(const unsigned char *value) { maskHostBuffer = value; } /** * @} */ virtual ~FakeReaderFactory() {} virtual Potential<Input::Reader> create(readerid_t id, const Core::ReaderInputDefinition &def) const { return Potential<Input::Reader>( new FakeVideoReader(id, def.getWidth(), def.getHeight(), frameRate, ReaderClockResolution::Microsecond, firstFrame + def.getFrameOffset(), lastFrame + def.getFrameOffset(), maskHostBuffer, readFrameTime, true, readFrameCalls, readFrameExits)); } virtual Input::ProbeResult probe(const Ptv::Value & /*config*/) const { ENSURE(false, "not supported"); return Input::ProbeResult({false, false, -1, -1, -1, -1}); } virtual int getFirstFrame() const { return firstFrame; } virtual int getNumFrames() const { return lastFrame; } private: FrameRate frameRate; frameid_t firstFrame; frameid_t lastFrame; const unsigned char *maskHostBuffer; std::atomic<int> *readFrameCalls; std::atomic<int> *readFrameExits; int readFrameTime; }; /** * A fake reader that notifies on deallocation */ class ResourceCheckReader : public Input::VideoReader { public: explicit ResourceCheckReader(std::atomic<int> *dealloc) : Reader(0), VideoReader(1, 1, 1, PixelFormat::RGBA, Host, {1, 1}, 0, 1, false, nullptr), dealloc(dealloc) {} virtual ~ResourceCheckReader() { (*dealloc)++; } virtual Input::ReadStatus readFrame(mtime_t &, unsigned char *) { return Status{Origin::Input, ErrType::UnsupportedAction, "ResourceCheckReader doesn't implement readFrame"}; } virtual Status seekFrame(frameid_t) { return {Origin::Input, ErrType::UnsupportedAction, "ResourceCheckReader doesn't implement seekFrame"}; } private: std::atomic<int> *dealloc; }; /** * A reader factory that provides the ability to check * that all of its created readers are destroyed once */ class ResourceCheckReaderFactory : public Input::ReaderFactory { public: ResourceCheckReaderFactory(std::vector<std::atomic<int> *> &readerChecks, std::atomic<int> &factoryCheck) : readerChecks(readerChecks), factoryCheck(factoryCheck) {} virtual ~ResourceCheckReaderFactory() { factoryCheck++; } virtual Potential<Input::Reader> create(readerid_t, const Core::ReaderInputDefinition &) const { auto counter = new std::atomic<int>(0); readerChecks.push_back(counter); return Potential<Input::Reader>(new ResourceCheckReader(counter)); } virtual Input::ProbeResult probe(const Ptv::Value & /*config*/) const { ENSURE(false, "not supported"); return Input::ProbeResult({false, false, -1, -1, -1, -1}); } virtual int getFirstFrame() const { return 0; } virtual int getNumFrames() const { return -1; } private: std::vector<std::atomic<int> *> &readerChecks; std::atomic<int> &factoryCheck; }; } // namespace Testing } // namespace VideoStitch