From 0990cd827a5d523bc745bd887cdcfab2e294a73e Mon Sep 17 00:00:00 2001 From: Vinayak Y B <89510107+Vinayak-YB@users.noreply.github.com> Date: Wed, 2 Aug 2023 01:11:53 +0530 Subject: [PATCH] TestSignalGenerator Module (#270) * TestSignalGenerator code commit * saveOrCompare arguments changed * changed the tolerance of saveOrCompare * Removing test images from data * Adding fresh samples * Updating tolerance to 50 * Increasing tolerance to 100 * added download cap for saveorcompare made verbose * small fix in testcase * path fix for testcase * savefile implementation update * deleted old files * added new data * Fixed test offset mismatch issue * saveorcompare enhanced --------- Co-authored-by: Mradul Dubey Co-authored-by: Vinayak Bhustali Co-authored-by: zaki --- .../workflows/build-test-lin-container.yml | 1 + .github/workflows/build-test-lin-wsl.yml | 1 + .github/workflows/build-test-lin.yml | 1 + .github/workflows/build-test-win.yml | 1 + base/CMakeLists.txt | 3 + base/include/TestSignalGeneratorSrc.h | 52 +++++++ base/src/TestSignalGeneratorSrc.cpp | 131 ++++++++++++++++++ base/test/testSignalGeneratorSrc_tests.cpp | 86 ++++++++++++ base/test/test_utils.cpp | 27 +++- data/TestSample.raw | Bin 0 -> 240000 bytes data/TestSample1.raw | Bin 0 -> 345600 bytes data/TestSample2.raw | Bin 0 -> 240000 bytes 12 files changed, 302 insertions(+), 1 deletion(-) create mode 100644 base/include/TestSignalGeneratorSrc.h create mode 100644 base/src/TestSignalGeneratorSrc.cpp create mode 100644 base/test/testSignalGeneratorSrc_tests.cpp create mode 100644 data/TestSample.raw create mode 100644 data/TestSample1.raw create mode 100644 data/TestSample2.raw diff --git a/.github/workflows/build-test-lin-container.yml b/.github/workflows/build-test-lin-container.yml index 4d3df0ea7..3bd29c054 100644 --- a/.github/workflows/build-test-lin-container.yml +++ b/.github/workflows/build-test-lin-container.yml @@ -178,6 +178,7 @@ jobs: name: TestResults_${{ inputs.flav }} path: | CI_test_result_${{inputs.flav}}.xml + ${{ github.workspace }}/data/SaveOrCompareFail/** - name: Upload build logs diff --git a/.github/workflows/build-test-lin-wsl.yml b/.github/workflows/build-test-lin-wsl.yml index 4c036c248..a193b781a 100644 --- a/.github/workflows/build-test-lin-wsl.yml +++ b/.github/workflows/build-test-lin-wsl.yml @@ -190,6 +190,7 @@ jobs: name: TestResults_${{ inputs.flav }} path: | CI_test_result_${{inputs.flav}}.xml + ${{ github.workspace }}/data/SaveOrCompareFail/** - name: Upload build logs diff --git a/.github/workflows/build-test-lin.yml b/.github/workflows/build-test-lin.yml index d45e77064..44c9bf4ab 100644 --- a/.github/workflows/build-test-lin.yml +++ b/.github/workflows/build-test-lin.yml @@ -174,6 +174,7 @@ jobs: name: TestResults_${{ inputs.flav }} path: | CI_test_result_${{inputs.flav}}.xml + ${{ github.workspace }}/data/SaveOrCompareFail/** - name: Upload build logs diff --git a/.github/workflows/build-test-win.yml b/.github/workflows/build-test-win.yml index f2fee82ae..d330c3829 100644 --- a/.github/workflows/build-test-win.yml +++ b/.github/workflows/build-test-win.yml @@ -182,6 +182,7 @@ jobs: name: TestResults_${{ inputs.flav }} path: | CI_test_result_${{inputs.flav}}.xml + ${{ github.workspace }}/data/SaveOrCompareFail/** - name: Upload build logs diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index 771c2a3cb..4e461fb91 100755 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -221,6 +221,7 @@ SET(CORE_FILES_H include/MotionVectorExtractor.h include/OverlayModule.h include/OrderedCacheOfFiles.h + include/TestSignalGeneratorSrc.h ) IF(ENABLE_WINDOWS) @@ -278,6 +279,7 @@ SET(IP_FILES src/Overlay.cpp src/OverlayFactory.h src/OverlayFactory.cpp + src/TestSignalGeneratorSrc.cpp ) @@ -555,6 +557,7 @@ SET(UT_FILES test/mp4_getlivevideots_tests.cpp test/mp4_dts_strategy_tests.cpp test/overlaymodule_tests.cpp + test/testSignalGeneratorSrc_tests.cpp ${ARM64_UT_FILES} ${CUDA_UT_FILES} ) diff --git a/base/include/TestSignalGeneratorSrc.h b/base/include/TestSignalGeneratorSrc.h new file mode 100644 index 000000000..fcda4e943 --- /dev/null +++ b/base/include/TestSignalGeneratorSrc.h @@ -0,0 +1,52 @@ +#pragma once +#include "Module.h" + +class TestSignalGeneratorProps : public ModuleProps +{ +public: + TestSignalGeneratorProps() {} + TestSignalGeneratorProps(int _width, int _height) + : width(_width), height(_height) {} + + ~TestSignalGeneratorProps() {} + + int width = 0; + int height = 0; + +private: + friend class boost::serialization::access; + + template + void serialize(Archive &ar, const unsigned int version) + { + ar &boost::serialization::base_object(*this); + ar &width; + ar &height; + } +}; + +class TestSignalGenerator : public Module +{ +public: + TestSignalGenerator(TestSignalGeneratorProps _props); + ~TestSignalGenerator(); + + bool init(); + bool term(); + void setProps(TestSignalGeneratorProps &props); + TestSignalGeneratorProps getProps(); + +protected: + bool produce(); + bool validateOutputPins(); + void setMetadata(framemetadata_sp &metadata); + bool handlePropsChange(frame_sp &frame); + + +private: + class Detail; + boost::shared_ptr mDetail; + size_t outputFrameSize; + framemetadata_sp mOutputMetadata; + std::string mOutputPinId; +}; diff --git a/base/src/TestSignalGeneratorSrc.cpp b/base/src/TestSignalGeneratorSrc.cpp new file mode 100644 index 000000000..c4640309b --- /dev/null +++ b/base/src/TestSignalGeneratorSrc.cpp @@ -0,0 +1,131 @@ +#include "TestSignalGeneratorSrc.h" +#include "Module.h" +#include +#include + +class TestSignalGenerator::Detail +{ +public: + Detail(TestSignalGeneratorProps &_props) + : mProps(_props), start_shade(0), end_shade(255), current_shade(start_shade) {} + + ~Detail() {} + + bool generate(frame_sp &frame) + { + auto frame_ptr = frame->data(); + uint8_t* x = static_cast(frame_ptr); + + for (int height = 0; height < mProps.height * 1.5; height++) + { + memset(x, current_shade, mProps.width); + x += mProps.width; + current_shade += 1; + if (current_shade > end_shade) + { + current_shade = start_shade; + } + } + return true; + } + + void setProps(const TestSignalGeneratorProps &_props) + { + mProps = _props; + reset(); + } + void reset() + { + current_shade = start_shade; + } + + TestSignalGeneratorProps mProps; + uint8_t start_shade = 0; + uint8_t end_shade = 255; + uint8_t current_shade = 0; +}; + +TestSignalGenerator::TestSignalGenerator(TestSignalGeneratorProps _props) + : Module(SOURCE, "TestSignalGenerator", _props), outputFrameSize(0) +{ + mDetail.reset(new Detail(_props)); + mOutputMetadata = framemetadata_sp(new RawImagePlanarMetadata(_props.width, _props.height, ImageMetadata::ImageType::YUV420, size_t(0), CV_8U)); + mOutputPinId = addOutputPin(mOutputMetadata); +} + +TestSignalGenerator::~TestSignalGenerator() +{ + mDetail->~Detail(); +} + +bool TestSignalGenerator::validateOutputPins() +{ + if (getNumberOfOutputPins() != 1) + { + LOG_ERROR << "<" << getId() << ">::validateOutputPins size is expected to be 1. Actual<" << getNumberOfOutputPins() << ">"; + return false; + } + framemetadata_sp metadata = getFirstOutputMetadata(); + auto frameType = metadata->getFrameType(); + if (frameType != FrameMetadata::RAW_IMAGE_PLANAR) + { + LOG_ERROR << "<" << getId() << ">::validateOutputPins output frameType should be RAW_IMAGE_PLANAR. Actual<" << frameType << ">"; + return false; + } + + return true; +} + +bool TestSignalGenerator::init() +{ + if (!Module::init()) + { + return false; + } + outputFrameSize = (getProps().width * getProps().height * 3) >> 1; + + return true; +} + +bool TestSignalGenerator::produce() +{ + auto mPinId = getOutputPinIdByType(FrameMetadata::RAW_IMAGE_PLANAR); + frame_container frames; + frame_sp frame = makeFrame(outputFrameSize); + mDetail->generate(frame); + frames.insert(make_pair(mPinId, frame)); + send(frames); + return true; +} + +bool TestSignalGenerator::term() +{ + return Module::term(); +} + +void TestSignalGenerator::setMetadata(framemetadata_sp &metadata) +{ + if (!metadata->isSet()) + { + return; + } +} + +bool TestSignalGenerator::handlePropsChange(frame_sp &frame) +{ + TestSignalGeneratorProps props; + bool ret = Module::handlePropsChange(frame, props); + mDetail->setProps(props); + outputFrameSize = (props.width * props.height * 3) >> 1; + return ret; +} + +void TestSignalGenerator::setProps(TestSignalGeneratorProps &props) +{ + Module::addPropsToQueue(props); +} + +TestSignalGeneratorProps TestSignalGenerator::getProps() +{ + return mDetail->mProps; +} diff --git a/base/test/testSignalGeneratorSrc_tests.cpp b/base/test/testSignalGeneratorSrc_tests.cpp new file mode 100644 index 000000000..de503bef6 --- /dev/null +++ b/base/test/testSignalGeneratorSrc_tests.cpp @@ -0,0 +1,86 @@ +#include "TestSignalGeneratorSrc.h" +#include "Module.h" +#include "RawImageMetadata.h" +#include +#include +#include "PipeLine.h" +#include "test_utils.h" +#include "ExternalSinkModule.h" +#include "FrameContainerQueue.h" +#include"FileWriterModule.h" + +BOOST_AUTO_TEST_SUITE(TestSignalGenerator_tests) + +class SinkModuleProps : public ModuleProps +{ +public: + SinkModuleProps() : ModuleProps(){}; +}; + +class SinkModule : public Module +{ +public: + SinkModule(SinkModuleProps props) : Module(SINK, "sinkModule", props){}; + boost::shared_ptr getQue() { return Module::getQue(); } + +protected: + bool validateOutputPins() + { + return true; + } + bool validateInputPins() + { + return true; + } +}; +BOOST_AUTO_TEST_CASE(Basic) +{ + auto source = boost::shared_ptr(new TestSignalGenerator(TestSignalGeneratorProps(400, 400))); + auto sink = boost::shared_ptr(new ExternalSinkModule()); + source->setNext(sink); + BOOST_TEST(source->init()); + BOOST_TEST(sink->init()); + source->step(); + auto frames = sink->try_pop(); + BOOST_TEST(frames.size() == 1); + auto outputFrame = frames.cbegin()->second; + BOOST_TEST(outputFrame->getMetadata()->getFrameType() == FrameMetadata::RAW_IMAGE_PLANAR); + const uint8_t* pReadDataTest = const_cast(static_cast(outputFrame->data())); + unsigned int readDataSizeTest = outputFrame->size(); + Test_Utils::saveOrCompare("./data/TestSample.raw", pReadDataTest, readDataSizeTest,0); +} + +BOOST_AUTO_TEST_CASE(getSetProps) +{ + auto source = boost::shared_ptr(new TestSignalGenerator(TestSignalGeneratorProps(640, 360))); + auto sink = boost::shared_ptr(new SinkModule(SinkModuleProps())); + source->setNext(sink); + source->init(); + sink->init(); + source->step(); + auto sinkQue = sink->getQue(); + frame_container frames; + frames = sinkQue->pop(); + auto frameMetadata = frames.begin()->second->getMetadata(); + auto currentProps = source->getProps(); + BOOST_TEST(frames.size() == 1); + auto outputFrame = frames.cbegin()->second; + BOOST_TEST(outputFrame->getMetadata()->getFrameType() == FrameMetadata::RAW_IMAGE_PLANAR); + const uint8_t* pReadDataTest = const_cast(static_cast(outputFrame->data())); + unsigned int readDataSizeTest = outputFrame->size(); + Test_Utils::saveOrCompare("./data/TestSample1.raw", pReadDataTest,readDataSizeTest, 0); + TestSignalGeneratorProps newProps(400, 400); + source->setProps(newProps); + source->step(); + sinkQue = sink->getQue(); + frames = sinkQue->pop(); + frameMetadata = frames.begin()->second->getMetadata(); + BOOST_TEST(frames.size() == 1); + outputFrame = frames.cbegin()->second; + BOOST_TEST(outputFrame->getMetadata()->getFrameType() == FrameMetadata::RAW_IMAGE_PLANAR); + pReadDataTest = const_cast(static_cast(outputFrame->data())); + readDataSizeTest = outputFrame->size(); + Test_Utils::saveOrCompare("./data/TestSample2.raw",pReadDataTest,readDataSizeTest, 0); +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/base/test/test_utils.cpp b/base/test/test_utils.cpp index ace2fce0a..1487d2b20 100755 --- a/base/test/test_utils.cpp +++ b/base/test/test_utils.cpp @@ -2,6 +2,7 @@ #include "test_utils.h" #include "Logger.h" #include +#include #include "iostream" #include #include @@ -76,9 +77,11 @@ bool CompareData(const uint8_t* data01, const uint8_t* data02, unsigned int data else { mismatch += 1; + LOG_ERROR<<"The mismatch occured at data element"< tolerance) { + LOG_ERROR<<"Mismatch has crossed tolerance. Mismatch "< tolerance "<R;Yn=LJg!BY9ND90~v)H$RyN2 zW}ybM2sMyZsDW%k4P+N;Acs%`IfWX?CDcG}p$75@HIP@RfqX&@)E8=?flvbt zg&Jri)IejQ2AT*p&{U{_WH84`Bfl)#Yj23EOj8Fq(g&G(q z)WCS51||qKFj1(1NkR=w7HVLMPy3z;vMoW(YMfQ>cMiLJiCoYG95~19OEM zm?zZ0e4z#w2sN-!sDVX74J;OFV2MxzONAO(Ce*-kp$1k6HLy~sfmK2ctQKlujZgz? zg&J5V)WCY71~v#auu-UiO+pQ97HVLNPy<_q8rUY(z;>Yqb_g}FQ>cMmLJjN|YG990 z1AB!U*eBG$exU{q2sLm}sDVR54ICC~;D}HIM}-d>fm1>aoEB=} zj8Fq-g&H^~)WCV61}+FSa8am%OF|7?7HZ&%Py<(m8n`CZz;&SpZU{ASQ>cMkLJiy& zYT%Ag19ycQxF^)WeW3;(2sQ9fsDVd94LlZV;E7NJPlXzICe*-lp$1+EHSkiXfmcEe zycTNUjZgz`g&KG#)WCb820jQi@KLCNPeKiR7HZ&&Py=6u8u%vEz;~ereh4-2Q>cMo zLJj;DYT%Dh1Am1Y_$Smr0HFo~3N;W&sDZ#j4FnNtAgE9S!GszJF4RB>p$0+n4I~k2AgNFT$%Gn6F4RB@p$1Y4 zHIPcEfz(0`q!DT$txyB$gc?XM)IbKI1~LjYkV&Y4%t8%h5o#c-Py^Y78ptlxKn|e> zatbw&OQ?a|LJi~*Y9Oyr1Nnp+$S>4D0igy83N=tjsDZ*l4HOY-pr}v-#e^CtF4RB? zp$19{HBd^Zfzm<^lo4v6tWX2xgc>L>)IbHH1}X|QP)VqP%0dlP5o(~SPy^M38mKPR zKn+_OQ?a`LJiarYM`!A1NDR&s4vt&1EB^Q3N_G3sDZ{p4KxvIps7#;&4e0g zF4RB^p$1wCHPA|^f!0C|v=M5ctxyB)gc@is)IbNJ2098g&`GF)&O!}z5o(~TPy^kB z8t5+6Ko6k?dI~krOQ?a~LJjm0YM`%B1OEv%&`+p={z4555NcqcPy>U68W=3pz!0GZ zh6*(>OsIk3LJf=%YG9;L1EYi*7%kMm7@-En3NsE8dxmU zz!ISbmI^hnOsIk7LJh1CYG9>M1FM7@SS{4R8leW(3N^4!sDbrD4Qvo=0^Tr%(gCgc{f_)W9C02KEXyuurIg{Xz{K5NhC{Py>gA z8aOP}z!9MajtVt!OsIk5LJgb{YT%?$1E+)&}sDblB4O|dv;G$3i zmxLO)EY!dip$4uBHE>O+f$KsI+z@KurceX7gc`Ul)W98~2JQ+qa8Ia#`$7#o5NhC| zPy>&I8h9+!z!RYco(eVaOsIk9LJhnSYT%_%1FwV{crDbx8=(f?3N`RfsDbxF4SW!4 z;G<9jpM)CtEY!dkp$5JRHSkTSf$u^M{19s3r%(gGgc|rQ)W9F12L1{)@K30L074A} z6lx%lPy>O58VDlPKv1Cuf(bPcT&RH%LJfozY9N$Q1EGZ)2qV-$SfK{O2{jO2sDTJV z4MY@bAd*l6k%bzFBGf=sp$4J}H4t5>ffzy!#1v{EmQVw+g&K$>)IeOJ2I2`d5MQW) z1VRlY6lx%mPy>mD8b~74KvJOwk_k1CT&RH*LJg!8Y9N(R1F3}?NF&rhTA>Eg2{n*j zsDTVZ4P+E*Ad^r7nS~n2BGf=up$4)EHIQAXfgC~&

Tkmrw(_g&N2s)IeUL2J#6t zkYA{Q0zwTG6l$Q5Py>a98Ym*vKvAIviU~DPT&RH(LJgD@YM_)*1Eqx;C?nKBS)m5X m2{lk&sDTPX4OA3rppsAnm4zCpBGf=tp$4i6HBeosf&T#lQswLb literal 0 HcmV?d00001 diff --git a/data/TestSample1.raw b/data/TestSample1.raw new file mode 100644 index 0000000000000000000000000000000000000000..0401a86cad9c9e4f9329845876be1f054bb6a752 GIT binary patch literal 345600 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 Lgacz^ARPEVT~Ah! literal 0 HcmV?d00001 diff --git a/data/TestSample2.raw b/data/TestSample2.raw new file mode 100644 index 0000000000000000000000000000000000000000..78aee8ca7f8cbfd9b843609aac098d6ef8e19b33 GIT binary patch literal 240000 zcmeIuLjo8E006<*wr$(CZQC{*+qP}nwr$&X(r@fx=Lr)afJg%Yg&GJX)IeaN27(AR z5LBpvU_uQ97iu7cPy-=_8VDuSKxm-`!U#1GR;Yn+LJfo$Y9NA80}+K9h$PfNWT6J4 z2sIE@sDWrg4MZ1eAcjx_F@+k4CDcG{p$6gzH4s;*fp|g<#20EHflvbpg&IgC)IegP z29gLhkW{FFWI_!j7iu7dPy;E28b~G7Kx&}|(g-z>R;Yn=LJg!BY9ND90~v)H$RyN2 zW}ybM2sMyZsDW%k4P+N;Acs%`IfWX?CDcG}p$75@HIP@RfqX&@)E8=?flvbt zg&Jri)IejQ2AT*p&{U{_WH84`Bfl)#Yj23EOj8Fq(g&G(q z)WCS51||qKFj1(1NkR=w7HVLMPy3z;vMoW(YMfQ>cMiLJiCoYG95~19OEM zm?zZ0e4z#w2sN-!sDVX74J;OFV2MxzONAO(Ce*-kp$1k6HLy~sfmK2ctQKlujZgz? zg&J5V)WCY71~v#auu-UiO+pQ97HVLNPy<_q8rUY(z;>Yqb_g}FQ>cMmLJjN|YG990 z1AB!U*eBG$exU{q2sLm}sDVR54ICC~;D}HIM}-d>fm1>aoEB=} zj8Fq-g&H^~)WCV61}+FSa8am%OF|7?7HZ&%Py<(m8n`CZz;&SpZU{ASQ>cMkLJiy& zYT%Ag19ycQxF^)WeW3;(2sQ9fsDVd94LlZV;E7NJPlXzICe*-lp$1+EHSkiXfmcEe zycTNUjZgz`g&KG#)WCb820jQi@KLCNPeKiR7HZ&&Py=6u8u%vEz;~ereh4-2Q>cMo zLJj;DYT%Dh1Am1Y_$Smr0HFo~3N;W&sDZ#j4FnNtAgE9S!GszJF4RB>p$0+n4I~k2AgNFT$%Gn6F4RB@p$1Y4 zHIPcEfz(0`q!DT$txyB$gc?XM)IbKI1~LjYkV&Y4%t8%h5o#c-Py^Y78ptlxKn|e> zatbw&OQ?a|LJi~*Y9Oyr1Nnp+$S>4D0igy83N=tjsDZ*l4HOY-pr}v-#e^CtF4RB? zp$19{HBd^Zfzm<^lo4v6tWX2xgc>L>)IbHH1}X|QP)VqP%0dlP5o(~SPy^M38mKPR zKn+_OQ?a`LJiarYM`!A1NDR&s4vt&1EB^Q3N_G3sDZ{p4KxvIps7#;&4e0g zF4RB^p$1wCHPA|^f!0C|v=M5ctxyB)gc@is)IbNJ2098g&`GF)&O!}z5o(~TPy^kB z8t5+6Ko6k?dI~krOQ?a~LJjm0YM`%B1OEv%&`+p={z4555NcqcPy>U68W=3pz!0GZ zh6*(>OsIk3LJf=%YG9;L1EYi*7%kMm7@-En3NsE8dxmU zz!ISbmI^hnOsIk7LJh1CYG9>M1FM7@SS{4R8leW(3N^4!sDbrD4Qvo=0^Tr%(gCgc{f_)W9C02KEXyuurIg{Xz{K5NhC{Py>gA z8aOP}z!9MajtVt!OsIk5LJgb{YT%?$1E+)&}sDblB4O|dv;G$3i zmxLO)EY!dip$4uBHE>O+f$KsI+z@KurceX7gc`Ul)W98~2JQ+qa8Ia#`$7#o5NhC| zPy>&I8h9+!z!RYco(eVaOsIk9LJhnSYT%_%1FwV{crDbx8=(f?3N`RfsDbxF4SW!4 z;G<9jpM)CtEY!dkp$5JRHSkTSf$u^M{19s3r%(gGgc|rQ)W9F12L1{)@K30L074A} z6lx%lPy>O58VDlPKv1Cuf(bPcT&RH%LJfozY9N$Q1EGZ)2qV-$SfK{O2{jO2sDTJV z4MY@bAd*l6k%bzFBGf=sp$4J}H4t5>ffzy!#1v{EmQVw+g&K$>)IeOJ2I2`d5MQW) z1VRlY6lx%mPy>mD8b~74KvJOwk_k1CT&RH*LJg!8Y9N(R1F3}?NF&rhTA>Eg2{n*j zsDTVZ4P+E*Ad^r7nS~n2BGf=up$4)EHIQAXfgC~&

Tkmrw(_g&N2s)IeUL2J#6t zkYA{Q0zwTG6l$Q5Py>a98Ym*vKvAIviU~DPT&RH(LJgD@YM_)*1Eqx;C?nKBS)m5X m2{lk&sDTPX4OA3rppsAnm4zCpBGf=tp$4i6HBeosf&T#lQswLb literal 0 HcmV?d00001