diff --git a/YUViewUnitTest/YUViewUnitTest.pro b/YUViewUnitTest/YUViewUnitTest.pro index 3b64ec9ec..9bfb6a02e 100644 --- a/YUViewUnitTest/YUViewUnitTest.pro +++ b/YUViewUnitTest/YUViewUnitTest.pro @@ -12,7 +12,8 @@ SOURCES += $$files(*.cpp, true) HEADERS += $$files(*.h, true) INCLUDEPATH += $$top_srcdir/submodules/googletest/googletest/include \ - $$top_srcdir/YUViewLib/src + $$top_srcdir/YUViewLib/src \ + $$top_srcdir/YUViewUnitTest/common LIBS += -L$$top_builddir/submodules/googletest-qmake/gtest -lgtest LIBS += -L$$top_builddir/submodules/googletest-qmake/gtest_main -lgtest_main LIBS += -L$$top_builddir/YUViewLib -lYUViewLib diff --git a/YUViewUnitTest/common/TemporaryFile.cpp b/YUViewUnitTest/common/TemporaryFile.cpp new file mode 100644 index 000000000..ae8d14498 --- /dev/null +++ b/YUViewUnitTest/common/TemporaryFile.cpp @@ -0,0 +1,95 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TemporaryFile.h" + +#include +#include + +namespace yuviewTest +{ + +namespace +{ + +char mapRandomNumberToAlphaChar(const int i) +{ + constexpr auto ASCII_OFFSET_TO_0 = 48; + constexpr auto ASCII_OFFSET_TO_A = 65; + constexpr auto ASCII_OFFSET_TO_a = 97; + + if (i < 10) + return static_cast(i + ASCII_OFFSET_TO_0); + if (i < 36) + return static_cast(i - 10 + ASCII_OFFSET_TO_A); + return static_cast(i - 36 + ASCII_OFFSET_TO_a); +} + +std::string generateRandomFileName() +{ + std::random_device randomDevice; + std::mt19937 generator(randomDevice()); + + std::uniform_int_distribution distribution(0, 61); + + const auto value = distribution(generator); + + std::stringstream s; + constexpr auto NR_CHARACTERS = 40; + for (int i = 0; i < NR_CHARACTERS; ++i) + s << mapRandomNumberToAlphaChar(distribution(generator)); + return s.str(); +} + +} // namespace + +TemporaryFile::TemporaryFile(const ByteVector &data) +{ + this->temporaryFilePath = std::filesystem::temp_directory_path() / generateRandomFileName(); + + std::ofstream tempFileWriter(this->temporaryFilePath, std::iostream::out | std::iostream::binary); + std::for_each( + data.begin(), data.end(), [&tempFileWriter](const unsigned char c) { tempFileWriter << c; }); + tempFileWriter.close(); +} + +TemporaryFile::~TemporaryFile() +{ + std::filesystem::remove(this->temporaryFilePath); +} + +std::filesystem::path TemporaryFile::getFilePath() const +{ + return this->temporaryFilePath; +} + +} // namespace yuviewTest \ No newline at end of file diff --git a/YUViewUnitTest/common/TemporaryFile.h b/YUViewUnitTest/common/TemporaryFile.h new file mode 100644 index 000000000..70518eff6 --- /dev/null +++ b/YUViewUnitTest/common/TemporaryFile.h @@ -0,0 +1,54 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include + +namespace yuviewTest +{ + +class TemporaryFile +{ +public: + TemporaryFile(const ByteVector &data); + ~TemporaryFile(); + + std::filesystem::path getFilePath() const; + + private: + std::filesystem::path temporaryFilePath; +}; + +} // namespace yuviewTest diff --git a/YUViewUnitTest/filesource/FileSourceAnnexBTest.cpp b/YUViewUnitTest/filesource/FileSourceAnnexBTest.cpp new file mode 100644 index 000000000..827151420 --- /dev/null +++ b/YUViewUnitTest/filesource/FileSourceAnnexBTest.cpp @@ -0,0 +1,157 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gtest/gtest.h" + +using ::testing::TestWithParam; +using ::testing::Values; + +#include +#include + +namespace +{ + +struct TestParameters +{ + int startCodeLength{}; + int totalDataLength{}; + std::vector startCodePositions{}; +}; + +using NalSizes = std::vector; +std::pair generateAnnexBStream(const TestParameters &testParameters) +{ + NalSizes nalSizes; + ByteVector data; + + std::optional lastStartPos; + for (const auto pos : testParameters.startCodePositions) + { + unsigned nonStartCodeBytesToAdd; + if (lastStartPos) + { + EXPECT_GT(pos, + *lastStartPos + + testParameters.startCodeLength); // Start codes can not be closer together + nonStartCodeBytesToAdd = pos - *lastStartPos - testParameters.startCodeLength; + nalSizes.push_back(nonStartCodeBytesToAdd + testParameters.startCodeLength); + } + else + nonStartCodeBytesToAdd = pos; + lastStartPos = pos; + + data.insert(data.end(), nonStartCodeBytesToAdd, static_cast(128)); + + static constexpr auto START_CODE_4BYTES = {char(0), char(0), char(0), char(1)}; + static constexpr auto START_CODE_3BYTES = {char(0), char(0), char(1)}; + + if (testParameters.startCodeLength == 4) + data.insert(data.end(), START_CODE_4BYTES.begin(), START_CODE_4BYTES.end()); + else + data.insert(data.end(), START_CODE_3BYTES.begin(), START_CODE_3BYTES.end()); + } + + const auto remainder = + testParameters.totalDataLength - *lastStartPos - testParameters.startCodeLength; + nalSizes.push_back(remainder + testParameters.startCodeLength); + data.insert(data.end(), remainder, char(128)); + + return {nalSizes, data}; +} + +class FileSourceAnnexBTest : public TestWithParam +{ +}; + +std::string getTestName(const testing::TestParamInfo &testParam) +{ + const auto testParameters = testParam.param; + std::stringstream s; + s << "TestAnnexBWithParameters_"; + s << "startCodeLength_" << testParameters.startCodeLength << "_"; + s << "totalDataLength_" << testParameters.totalDataLength << "_"; + s << "startCodePositions_"; + for (const auto &position : testParameters.startCodePositions) + s << position << "_"; + return s.str(); +} + +TEST_P(FileSourceAnnexBTest, TestNalUnitParsing) +{ + const auto testParameters = GetParam(); + + const auto [nalSizes, data] = generateAnnexBStream(testParameters); + yuviewTest::TemporaryFile temporaryFile(data); + + FileSourceAnnexBFile annexBFile(QString::fromStdString(temporaryFile.getFilePath().string())); + EXPECT_EQ(static_cast(annexBFile.getNrBytesBeforeFirstNAL()), + testParameters.startCodePositions.at(0)); + + auto nalData = annexBFile.getNextNALUnit(); + int counter = 0; + while (nalData.size() > 0) + { + EXPECT_EQ(nalSizes.at(counter++), static_cast(nalData.size())); + nalData = annexBFile.getNextNALUnit(); + } +} + +INSTANTIATE_TEST_SUITE_P( + FilesourceTest, + FileSourceAnnexBTest, + Values(TestParameters({3, 10000, {80, 208, 500}}), + TestParameters({3, 10000, {0, 80, 208, 500}}), + TestParameters({3, 10000, {1, 80, 208, 500}}), + TestParameters({3, 10000, {2, 80, 208, 500}}), + TestParameters({3, 10000, {3, 80, 208, 500}}), + TestParameters({3, 10000, {4, 80, 208, 500}}), + TestParameters({3, 10000, {4, 80, 208, 9990}}), + TestParameters({3, 10000, {4, 80, 208, 9997}}), + + // Test cases where a buffer reload is needed (the buffer is 500k) + TestParameters({3, 1000000, {80, 208, 500, 50000, 800000}}), + + // The buffer is 500k in size. Test all variations with a start code around this position + TestParameters({3, 800000, {80, 208, 500, 50000, 499997}}), + TestParameters({3, 800000, {80, 208, 500, 50000, 499998}}), + TestParameters({3, 800000, {80, 208, 500, 50000, 499999}}), + TestParameters({3, 800000, {80, 208, 500, 50000, 500000}}), + TestParameters({3, 800000, {80, 208, 500, 50000, 500001}}), + TestParameters({3, 800000, {80, 208, 500, 50000, 500002}}), + + TestParameters({3, 10000, {80, 208, 500, 9995}}), + TestParameters({3, 10000, {80, 208, 500, 9996}}), + TestParameters({3, 10000, {80, 208, 500, 9997}})), + getTestName); + +} // namespace