diff --git a/YUViewLib/src/common/Functions.cpp b/YUViewLib/src/common/Functions.cpp index 07a7b7b0f..4602be20d 100644 --- a/YUViewLib/src/common/Functions.cpp +++ b/YUViewLib/src/common/Functions.cpp @@ -168,6 +168,67 @@ std::string toLower(std::string str) return str; } +std::string to_string(const Size &size) +{ + return std::to_string(size.width) + "x" + std::to_string(size.height); +} + +std::string to_string(const StringVec &items, const std::string &seperator) +{ + std::string str; + auto it = items.begin(); + while (it != items.end()) + { + if (it != items.begin()) + str += seperator; + str += *it; + ++it; + } + return str; +} + +std::string vstring(const char *format, va_list vargs) +{ + std::string result; + va_list args_copy; + + va_copy(args_copy, vargs); + + int len = vsnprintf(nullptr, 0, format, vargs); + if (len < 0) + { + va_end(args_copy); + throw std::runtime_error("vsnprintf error"); + } + + if (len > 0) + { + result.resize(len); + vsnprintf(result.data(), len + 1, format, args_copy); + } + + va_end(args_copy); + + return result; +} + +std::string formatString(std::string format, const std::initializer_list &arguments) +{ + int counter = 0; + for (auto argument : arguments) + { + const auto toReplace = "%" + std::to_string(counter); + auto replacePos = format.find(toReplace); + if (replacePos == std::string::npos) + return format; + + format.replace(replacePos, 2, argument); + + ++counter; + } + return format; +} + std::optional toUnsigned(const std::string &text) { try diff --git a/YUViewLib/src/common/Functions.h b/YUViewLib/src/common/Functions.h index 667c7fe41..5d3d88745 100644 --- a/YUViewLib/src/common/Functions.h +++ b/YUViewLib/src/common/Functions.h @@ -62,8 +62,29 @@ QStringList getThemeColors(QString themeName); // compatible. QString formatDataSize(double size, bool isBits = false); -QStringList toQStringList(const std::vector &stringVec); -std::string toLower(std::string str); +QStringList toQStringList(const std::vector &stringVec); +[[nodiscard]] std::string toLower(std::string str); +[[nodiscard]] std::string to_string(const Size &size); +[[nodiscard]] std::string to_string(const StringVec &items, const std::string &seperator = ", "); +[[nodiscard]] std::string vstring(const char *format, va_list vargs); +[[nodiscard]] std::string formatString(std::string format, + const std::initializer_list &arguments); + +template +[[nodiscard]] std::string to_string(const std::vector &items, + const std::string & seperator = ", ") +{ + std::string str; + auto it = items.begin(); + while (it != items.end()) + { + if (it != items.begin()) + str += seperator; + str += std::to_string(*it); + ++it; + } + return str; +} inline std::string boolToString(bool b) { diff --git a/YUViewLib/src/common/Typedef.h b/YUViewLib/src/common/Typedef.h index 9bbf63f78..610f4e704 100644 --- a/YUViewLib/src/common/Typedef.h +++ b/YUViewLib/src/common/Typedef.h @@ -207,6 +207,7 @@ typedef std::pair IntPair; typedef std::pair UIntPair; typedef std::pair StringPair; typedef std::vector StringPairVec; +typedef std::vector StringVec; /// ---- Legacy types that will be replaced typedef QPair QStringPair; @@ -314,6 +315,32 @@ struct Offset int y{}; }; +struct Rational +{ + bool operator!=(const Rational &second) const + { + const auto a = int64_t(this->num) * int64_t(second.den); + const auto b = int64_t(this->den) * int64_t(second.num); + return a != b; + } + + int num{}; + int den{}; +}; + +struct FileStartEndPos +{ + int64_t start{}; + int64_t end{}; +}; + +static std::string to_string(const FileStartEndPos &fileStartEndPos) +{ + std::ostringstream ss; + ss << "(" << fileStartEndPos.start << ", " << fileStartEndPos.end << ")"; + return ss.str(); +} + // A list of value pair lists, where every list has a string (title) class ValuePairListSets : public QList> { diff --git a/YUViewLib/src/decoder/decoderBase.cpp b/YUViewLib/src/decoder/decoderBase.cpp index 22688152e..7dcd4ffd6 100644 --- a/YUViewLib/src/decoder/decoderBase.cpp +++ b/YUViewLib/src/decoder/decoderBase.cpp @@ -35,9 +35,43 @@ #include #include +using namespace std::string_literals; + namespace decoder { +namespace +{ + +std::string getLibSearchPathFromSettings() +{ + QSettings settings; + settings.beginGroup("Decoders"); + auto searchPath = settings.value("SearchPath", "").toString().toStdString(); + if (searchPath.back() != '/') + searchPath += "/"; + settings.endGroup(); + return searchPath; +} + +StringVec getPathsToTry(const std::string &libName) +{ + const auto currentPath = std::filesystem::absolute(std::filesystem::current_path()).string(); + const auto currentAppPath = QCoreApplication::applicationDirPath().toStdString(); + + return {getLibSearchPathFromSettings() + libName, + currentPath + "/" + libName, + currentPath + "/decoder/" + libName, + currentPath + "/libde265/" + libName, // for legacy installations before the decoders were + // moved to the "decoders" folder + currentAppPath + "/" + libName, + currentAppPath + "/decoder/" + libName, + currentAppPath + "/libde265/" + libName, + libName}; // Try the system directories. +} + +} // namespace + // Debug the decoder ( 0:off 1:interactive decoder only 2:caching decoder only 3:both) #define DECODERBASE_DEBUG_OUTPUT 0 #if DECODERBASE_DEBUG_OUTPUT && !NDEBUG @@ -97,18 +131,28 @@ stats::FrameTypeData decoderBase::getCurrentFrameStatsForType(int typeId) const return statisticsData->getFrameTypeData(typeId); } -void decoderBaseSingleLib::loadDecoderLibrary(QString specificLibrary) +void decoderBase::setError(const std::string &reason) +{ + this->decoderState = DecoderState::Error; + this->errorString = reason; +} + +bool decoderBase::setErrorB(const std::string &reason) +{ + this->setError(reason); + return false; +} + +void decoderBaseSingleLib::loadDecoderLibrary(const std::string &specificLibrary) { // Try to load the HM library from the current working directory // Unfortunately relative paths like this do not work: (at least on windows) // library.setFileName(".\\libde265"); - bool libLoaded = false; - // Try the specific library first - this->library.setFileName(specificLibrary); + this->library.setFileName(QString::fromStdString(specificLibrary)); this->libraryPath = specificLibrary; - libLoaded = this->library.load(); + auto libLoaded = this->library.load(); if (!libLoaded) { @@ -117,34 +161,13 @@ void decoderBaseSingleLib::loadDecoderLibrary(QString specificLibrary) // the decLibXXX.so file first. Since this has been compiled for linux // it will fail and not even try to open the decLixXXX.dylib. // On windows and linux ommitting the extension works - auto libNames = getLibraryNames(); - - // Get the additional search path from the settings - QSettings settings; - settings.beginGroup("Decoders"); - auto searchPath = settings.value("SearchPath", "").toString(); - if (!searchPath.endsWith("/")) - searchPath.append("/"); - searchPath.append("%1"); - settings.endGroup(); - - auto const libPaths = QStringList() - << searchPath << QDir::currentPath() + "/%1" - << QDir::currentPath() + "/decoder/%1" - << QDir::currentPath() + - "/libde265/%1" // for legacy installations before the decoders were - // moved to the "decoders" folder - << QCoreApplication::applicationDirPath() + "/%1" - << QCoreApplication::applicationDirPath() + "/decoder/%1" - << QCoreApplication::applicationDirPath() + "/libde265/%1" - << "%1"; // Try the system directories. - for (auto &libName : libNames) + for (const auto &libName : this->getLibraryNames()) { - for (auto &libPath : libPaths) + for (auto &libPath : getPathsToTry(libName)) { - this->library.setFileName(libPath.arg(libName)); - this->libraryPath = libPath.arg(libName); + this->library.setFileName(QString::fromStdString(libPath)); + this->libraryPath = libPath; libLoaded = library.load(); if (libLoaded) break; @@ -157,21 +180,15 @@ void decoderBaseSingleLib::loadDecoderLibrary(QString specificLibrary) if (!libLoaded) { this->libraryPath.clear(); - auto error = "Error loading library: " + this->library.errorString() + "\n"; + auto error = "Error loading library: "s + this->library.errorString().toStdString() + "\n"; error += "We could not load one of the supported decoder library ("; auto libNames = this->getLibraryNames(); - for (int i = 0; i < libNames.count(); i++) - { - if (i == 0) - error += libNames[0]; - else - error += ", " + libNames[i]; - } - error += ").\n"; + error += to_string(libNames); error += "\n"; error += "We do not ship all of the decoder libraries.\n"; error += "You can find download links in Help->Downloads."; - return this->setError(error); + this->setError(error); + return; } this->resolveLibraryFunctionPointers(); diff --git a/YUViewLib/src/decoder/decoderBase.h b/YUViewLib/src/decoder/decoderBase.h index cd35ce8ae..fc33d1ca7 100644 --- a/YUViewLib/src/decoder/decoderBase.h +++ b/YUViewLib/src/decoder/decoderBase.h @@ -99,11 +99,11 @@ class decoderBase // Does the loaded library support the extraction of prediction/residual data? // These are the default implementations. Overload if a decoder can support more signals. - virtual int nrSignalsSupported() const { return 1; } - virtual QStringList getSignalNames() const { return QStringList() << "Reconstruction"; } - virtual bool isSignalDifference(int signalID) const; - virtual void setDecodeSignal(int signalID, bool &decoderResetNeeded); - int getDecodeSignal() const { return this->decodeSignal; } + virtual int nrSignalsSupported() const { return 1; } + virtual StringVec getSignalNames() const { return {"Reconstruction"}; } + virtual bool isSignalDifference(int signalID) const; + virtual void setDecodeSignal(int signalID, bool &decoderResetNeeded); + int getDecodeSignal() const { return this->decodeSignal; } // -- The decoding interface // If the current frame is valid, the current frame can be retrieved using getRawFrameData. @@ -124,24 +124,24 @@ class decoderBase // Get the statistics values for the current frame. In order to enable statistics retrievel, // activate it, reset the decoder and decode to the current frame again. - bool statisticsSupported() const { return internalsSupported; } - bool statisticsEnabled() const { return statisticsData != nullptr; } + bool statisticsSupported() const { return this->internalsSupported; } + bool statisticsEnabled() const { return this->statisticsData != nullptr; } void enableStatisticsRetrieval(stats::StatisticsData *s) { this->statisticsData = s; } stats::FrameTypeData getCurrentFrameStatsForType(int typeIdx) const; virtual void fillStatisticList(stats::StatisticsData &) const {}; // Error handling - bool errorInDecoder() const { return decoderState == DecoderState::Error; } - QString decoderErrorString() const { return errorString; } + bool errorInDecoder() const { return this->decoderState == DecoderState::Error; } + std::string decoderErrorString() const { return this->errorString; } // Get the name, filename and full path to the decoder library(s) that is/are being used. // The length of the list must be a multiple of 3 (name, libName, fullPath) - virtual QStringList getLibraryPaths() const = 0; + virtual StringVec getLibraryPaths() const = 0; // Get the decoder name (everyting that is needed to identify the decoder library) and the codec // that is being decoded. If needed, also version information (like HM 16.4) - virtual QString getDecoderName() const = 0; - virtual QString getCodecName() const = 0; + virtual std::string getDecoderName() const = 0; + virtual std::string getCodecName() const = 0; protected: DecoderState decoderState{DecoderState::NeedsMoreData}; @@ -157,18 +157,10 @@ class decoderBase video::yuv::PixelFormatYUV formatYUV{}; video::rgb::PixelFormatRGB formatRGB{}; - // Error handling - void setError(const QString &reason) - { - decoderState = DecoderState::Error; - errorString = reason; - } - bool setErrorB(const QString &reason) - { - setError(reason); - return false; - } - QString errorString{}; + void setError(const std::string &reason); + bool setErrorB(const std::string &reason); + + std::string errorString{}; // If set, fill it (if possible). The playlistItem has ownership of this. stats::StatisticsData *statisticsData{}; @@ -182,20 +174,20 @@ class decoderBaseSingleLib : public decoderBase decoderBaseSingleLib(bool cachingDecoder = false) : decoderBase(cachingDecoder){}; virtual ~decoderBaseSingleLib(){}; - QStringList getLibraryPaths() const override + StringVec getLibraryPaths() const override { - return QStringList() << getDecoderName() << library.fileName() << library.fileName(); + return {this->getDecoderName(), this->library.fileName().toStdString()}; } protected: virtual void resolveLibraryFunctionPointers() = 0; - void loadDecoderLibrary(QString specificLibrary); + void loadDecoderLibrary(const std::string &specificLibrary); // Get all possible names of the library that we will load - virtual QStringList getLibraryNames() const = 0; + virtual StringVec getLibraryNames() const = 0; - QLibrary library; - QString libraryPath{}; + QLibrary library; + std::string libraryPath{}; }; } // namespace decoder diff --git a/YUViewLib/src/decoder/decoderDav1d.cpp b/YUViewLib/src/decoder/decoderDav1d.cpp index 7fd21f7e5..781c2ceff 100644 --- a/YUViewLib/src/decoder/decoderDav1d.cpp +++ b/YUViewLib/src/decoder/decoderDav1d.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -47,7 +48,7 @@ namespace decoder using Subsampling = video::yuv::Subsampling; // Debug the decoder (0:off 1:interactive decoder only 2:caching decoder only 3:both) -#define DECODERDAV1D_DEBUG_OUTPUT 0 +#define DECODERDAV1D_DEBUG_OUTPUT 1 #if DECODERDAV1D_DEBUG_OUTPUT && !NDEBUG #include #if DECODERDAV1D_DEBUG_OUTPUT == 1 @@ -87,6 +88,373 @@ Subsampling convertFromInternalSubsampling(Dav1dPixelLayout layout) return Subsampling::UNKNOWN; } +#if SSE_CONVERSION +void copyImgToByteArray(const Dav1dPictureWrapper &src, + byteArrayAligned & dst, + const Size & frameSize, + const int decodeSignal) +#else +void copyImgToByteArray(const Dav1dPictureWrapper &src, + QByteArray & dst, + const Size & frameSize, + const int decodeSignal) +#endif +{ + auto nrPlanes = (src.getSubsampling() == Subsampling::YUV_400) ? 1 : 3; + + // At first get how many bytes we are going to write + const auto nrBytesPerSample = (src.getBitDepth() > 8) ? 2 : 1; + const auto framSize = src.getFrameSize(); + auto nrBytes = frameSize.width * frameSize.height * nrBytesPerSample; + auto layout = src.getSubsampling(); + if (layout == Subsampling::YUV_420) + nrBytes += (frameSize.width / 2) * (frameSize.height / 2) * 2 * nrBytesPerSample; + else if (layout == Subsampling::YUV_422) + nrBytes += (frameSize.width / 2) * frameSize.height * 2 * nrBytesPerSample; + else if (layout == Subsampling::YUV_444) + nrBytes += frameSize.width * frameSize.height * 2 * nrBytesPerSample; + + // Is the output big enough? + if (dst.capacity() < int(nrBytes)) + dst.resize(int(nrBytes)); + + auto dst_c = reinterpret_cast(dst.data()); + + // We can now copy from src to dst + for (int c = 0; c < nrPlanes; c++) + { + auto width = framSize.width; + auto height = framSize.height; + if (c != 0) + { + if (layout == Subsampling::YUV_420 || layout == Subsampling::YUV_422) + width /= 2; + if (layout == Subsampling::YUV_420) + height /= 2; + } + const size_t widthInBytes = width * nrBytesPerSample; + + uint8_t *img_c = nullptr; + if (decodeSignal == 0) + img_c = src.getData(c); + else if (decodeSignal == 1) + img_c = src.getDataPrediction(c); + else if (decodeSignal == 2) + img_c = src.getDataReconstructionPreFiltering(c); + + if (img_c == nullptr) + return; + + const int stride = (c == 0) ? src.getStride(0) : src.getStride(1); + for (size_t y = 0; y < height; y++) + { + memcpy(dst_c, img_c, widthInBytes); + img_c += stride; + dst_c += widthInBytes; + } + } +} + +IntPair calculateIntraPredDirection(IntraPredMode predMode, int angleDelta) +{ + if (predMode == DC_PRED || predMode > VERT_LEFT_PRED) + return {}; + + // angleDelta should be between -4 and 3 + const int modeIndex = predMode - VERT_PRED; + assert(modeIndex >= 0 && modeIndex < 8); + const int deltaIndex = angleDelta + 4; + assert(deltaIndex >= 0 && deltaIndex < 8); + + /* The python code which was used to create the vectorTable + import math + + def getVector(m, s): + a = m + 3 * s + x = -math.cos(math.radians(a)) + y = -math.sin(math.radians(a)) + return round(x*32), round(y*32) + + modes = [90, 180, 45, 135, 113, 157, 203, 67] + sub_angles = [-4, -3, -2, -1, 0, 1, 2, 3] + for m in modes: + out = "" + for s in sub_angles: + x, y = getVector(m, s) + out += "{{{0}, {1}}}, ".format(x, y) + print(out) + */ + + static const int vectorTable[8][8][2] = { + {{-7, 31}, {-5, 32}, {-3, 32}, {-2, 32}, {0, 32}, {2, 32}, {3, 32}, {5, 32}}, + {{31, 7}, {32, 5}, {32, 3}, {32, 2}, {32, 0}, {32, -2}, {32, -3}, {32, -5}}, + {{-27, 17}, {-26, 19}, {-25, 20}, {-24, 21}, {-23, 23}, {-21, 24}, {-20, 25}, {-19, 26}}, + {{17, 27}, {19, 26}, {20, 25}, {21, 24}, {23, 23}, {24, 21}, {25, 20}, {26, 19}}, + {{6, 31}, {8, 31}, {9, 31}, {11, 30}, {13, 29}, {14, 29}, {16, 28}, {17, 27}}, + {{26, 18}, {27, 17}, {28, 16}, {29, 14}, {29, 13}, {30, 11}, {31, 9}, {31, 8}}, + {{31, -6}, {31, -8}, {31, -9}, {30, -11}, {29, -13}, {29, -14}, {28, -16}, {27, -17}}, + {{-18, 26}, {-17, 27}, {-16, 28}, {-14, 29}, {-13, 29}, {-11, 30}, {-9, 31}, {-8, 31}}}; + + return {vectorTable[modeIndex][deltaIndex][0], vectorTable[modeIndex][deltaIndex][1]}; +} + +void parseBlockPartition(stats::StatisticsData *statisticsData, + Av1Block * blockData, + unsigned x, + unsigned y, + unsigned blockWidth4, + unsigned blockHeight4, + dav1dFrameInfo & frameInfo) +{ + if (y >= frameInfo.sizeInBlocks.height || x >= frameInfo.sizeInBlocks.width) + return; + + auto block = blockData[y * frameInfo.b4_stride + x]; + + const auto cbPosX = x * 4; + const auto cbPosY = y * 4; + const auto cbWidth = blockWidth4 * 4; + const auto cbHeight = blockHeight4 * 4; + + // Set prediction mode + const bool blockIntra = (block.intra != 0); + const auto predMode = blockIntra ? 0 : 1; + statisticsData->at(0).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, predMode); + + const bool frameIsIntra = (frameInfo.frameType == DAV1D_FRAME_TYPE_KEY || + frameInfo.frameType == DAV1D_FRAME_TYPE_INTRA); + if (frameIsIntra) + statisticsData->at(1).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.seg_id); + + statisticsData->at(2).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.skip); + statisticsData->at(3).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.skip_mode); + + if (blockIntra) + { + // Intra pred mode luma/chrmoa + statisticsData->at(4).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.y_mode); + statisticsData->at(5).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.uv_mode); + + // Palette size Y/UV + statisticsData->at(6).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.pal_sz[0]); + statisticsData->at(7).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.pal_sz[1]); + + // Intra angle delta luma/chroma + statisticsData->at(8).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.y_angle); + statisticsData->at(9).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.uv_angle); + + // Intra prediction direction luma/chroma + for (int yc = 0; yc < 2; yc++) + { + auto angleDelta = (yc == 0) ? block.y_angle : block.uv_angle; + auto predMode = (yc == 0) ? static_cast(block.y_mode) + : static_cast(block.uv_mode); + auto vec = calculateIntraPredDirection(predMode, angleDelta); + if (vec.first == 0 && vec.second == 0) + continue; + + auto blockScale = std::min(blockWidth4, blockHeight4); + auto vecX = (float)vec.first * blockScale / 4; + auto vecY = (float)vec.second * blockScale / 4; + + statisticsData->at(10 + yc).addBlockVector(cbPosX, cbPosY, cbWidth, cbHeight, vecX, vecY); + } + + if (block.y_mode == CFL_PRED) + { + // Chroma from luma alpha U/V + statisticsData->at(12).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.cfl_alpha[0]); + statisticsData->at(13).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.cfl_alpha[1]); + } + } + else // inter + { + auto compoundType = static_cast(block.comp_type); + bool isCompound = (compoundType != COMP_INTER_NONE); + + // Reference frame indices 0/1 + statisticsData->at(14).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.ref[0]); + if (isCompound) + statisticsData->at(15).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.ref[1]); + + // Compound prediction type + statisticsData->at(16).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.comp_type); + + if (block.comp_type == COMP_INTER_WEDGE || block.interintra_type == INTER_INTRA_WEDGE) + statisticsData->at(17).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.wedge_idx); + + if (isCompound) // TODO: This might not be correct + statisticsData->at(18).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.mask_sign); + + statisticsData->at(19).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.inter_mode); + + // Dynamic reference list index + if (isCompound) + { + // TODO: This might not be correct + statisticsData->at(20).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.drl_idx); + + statisticsData->at(21).addBlockValue( + cbPosX, cbPosY, cbWidth, cbHeight, block.interintra_type); + statisticsData->at(22).addBlockValue( + cbPosX, cbPosY, cbWidth, cbHeight, block.interintra_mode); + } + + statisticsData->at(23).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, block.motion_mode); + + // Motion vector 0/1 + statisticsData->at(24).addBlockVector( + cbPosX, cbPosY, cbWidth, cbHeight, block.mv[0].x, block.mv[0].y); + if (isCompound) + statisticsData->at(25).addBlockVector( + cbPosX, cbPosY, cbWidth, cbHeight, block.mv[1].x, block.mv[1].y); + } + + static const unsigned TxfmSizeWidthTable[] = { + 4, 8, 16, 32, 64, 4, 8, 8, 16, 16, 32, 32, 64, 4, 16, 8, 32, 16, 64}; + static const unsigned TxfmSizeHeightTable[] = { + 4, 8, 16, 32, 64, 8, 4, 16, 8, 32, 16, 64, 32, 16, 4, 32, 8, 64, 16}; + + const auto tx_val = static_cast(blockIntra ? block.tx : block.max_ytx); + const unsigned tx_w = TxfmSizeWidthTable[tx_val]; + const unsigned tx_h = TxfmSizeHeightTable[tx_val]; + + if (tx_w > cbWidth || tx_h > cbHeight) + // Transform can not be bigger then the coding block + return; + + for (unsigned x = 0; x < cbWidth; x += tx_w) + { + for (unsigned y = 0; y < cbHeight; y += tx_h) + { + // Set the transform size (ID 26) + const auto x_abs = cbPosX + x; + const auto y_abs = cbPosY + y; + if (x_abs < frameInfo.frameSize.width && y_abs < frameInfo.frameSize.height) + statisticsData->at(26).addBlockValue(x_abs, y_abs, tx_w, tx_h, (int)tx_val); + } + } +} + +void parseBlockRecursive(stats::StatisticsData *statisticsData, + Av1Block * blockData, + unsigned x, + unsigned y, + BlockLevel level, + dav1dFrameInfo & frameInfo) +{ + if (y >= frameInfo.sizeInBlocks.height) + return; + + auto block = blockData[y * frameInfo.b4_stride + x]; + const auto blockLevel = static_cast(block.bl); + + // assert(b.bl >= 0 && b.bl <= 4); + if (block.bl > 4) + return; + + const auto blockWidth4 = 1 << (5 - level); + + if (blockLevel > level) + { + // Recurse + const auto nextLevel = static_cast(level + 1); + const auto subw = blockWidth4 / 2; + parseBlockRecursive(statisticsData, blockData, x, y, nextLevel, frameInfo); + parseBlockRecursive(statisticsData, blockData, x + subw, y, nextLevel, frameInfo); + parseBlockRecursive(statisticsData, blockData, x, y + subw, nextLevel, frameInfo); + parseBlockRecursive(statisticsData, blockData, x + subw, y + subw, nextLevel, frameInfo); + } + else + { + // Parse the current partition. How is it split into blocks? + const auto blockPartition = static_cast(block.bp); + + const auto o = blockWidth4 / 2; + const auto oq = blockWidth4 / 2; + const auto bs = blockWidth4; + switch (blockPartition) + { + case PARTITION_NONE: + parseBlockPartition(statisticsData, blockData, x, y, bs, bs, frameInfo); + break; + case PARTITION_H: + parseBlockPartition(statisticsData, blockData, x, y, bs, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + o, bs, bs / 2, frameInfo); + break; + case PARTITION_V: + parseBlockPartition(statisticsData, blockData, x, y, bs / 2, bs, frameInfo); + parseBlockPartition(statisticsData, blockData, x + o, y, bs / 2, bs, frameInfo); + break; + case PARTITION_T_TOP_SPLIT: // PARTITION_HORZ_A + parseBlockPartition(statisticsData, blockData, x, y, bs / 2, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x + o, y, bs / 2, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + o, bs, bs / 2, frameInfo); + break; + case PARTITION_T_BOTTOM_SPLIT: // PARTITION_HORZ_B + parseBlockPartition(statisticsData, blockData, x, y, bs, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + o, bs / 2, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x + o, y + o, bs / 2, bs / 2, frameInfo); + break; + case PARTITION_T_LEFT_SPLIT: // PARTITION_VERT_A + parseBlockPartition(statisticsData, blockData, x, y, bs / 2, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + o, bs / 2, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x + o, y, bs / 2, bs, frameInfo); + break; + case PARTITION_T_RIGHT_SPLIT: // PARTITION_VERT_B + parseBlockPartition(statisticsData, blockData, x, y, bs / 2, bs, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + o, bs / 2, bs / 2, frameInfo); + parseBlockPartition(statisticsData, blockData, x + o, y + o, bs / 2, bs / 2, frameInfo); + break; + case PARTITION_H4: // PARTITION_HORZ_4 + parseBlockPartition(statisticsData, blockData, x, y, bs, bs / 4, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + oq, bs, bs / 4, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + oq * 2, bs, bs / 4, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + oq * 3, bs, bs / 4, frameInfo); + break; + case PARTITION_V4: // PARTITION_VER_4 + parseBlockPartition(statisticsData, blockData, x, y, bs / 4, bs, frameInfo); + parseBlockPartition(statisticsData, blockData, x + oq, y, bs / 4, bs, frameInfo); + parseBlockPartition(statisticsData, blockData, x + oq * 2, y, bs / 4, bs, frameInfo); + parseBlockPartition(statisticsData, blockData, x + oq * 3, y, bs / 4, bs, frameInfo); + break; + case PARTITION_SPLIT: + if (blockLevel == BL_8X8) + { + // 4 square 4x4 blocks. This is allowed. + assert(blockWidth4 == 2); + parseBlockPartition(statisticsData, blockData, x, y, 1, 1, frameInfo); + parseBlockPartition(statisticsData, blockData, x + 1, y, 1, 1, frameInfo); + parseBlockPartition(statisticsData, blockData, x, y + 1, 1, 1, frameInfo); + parseBlockPartition(statisticsData, blockData, x + 1, y + 1, 1, 1, frameInfo); + } + break; + default: + return; + } + } +} + +void cacheStatistics(stats::StatisticsData * statisticsData, + const Dav1dPictureWrapper &img, + int subBlockSize) +{ + + auto blockData = img.getBlockData(); + auto frameHeader = img.getFrameHeader(); + if (frameHeader == nullptr) + return; + + dav1dFrameInfo frameInfo(img.getFrameSize(), frameHeader->frame_type); + frameInfo.frameSize = img.getFrameSize(); + + const auto sb_step = subBlockSize >> 2; + + for (unsigned y = 0; y < frameInfo.frameSizeAligned.height; y += sb_step) + for (unsigned x = 0; x < frameInfo.frameSizeAligned.width; x += sb_step) + parseBlockRecursive(statisticsData, blockData, x, y, BL_128X128, frameInfo); +} + } // namespace Subsampling Dav1dPictureWrapper::getSubsampling() const @@ -110,28 +478,28 @@ dav1dFrameInfo::dav1dFrameInfo(Size frameSize, Dav1dFrameType frameType) decoderDav1d::decoderDav1d(int signalID, bool cachingDecoder) : decoderBaseSingleLib(cachingDecoder) { - currentOutputBuffer.clear(); + this->currentOutputBuffer.clear(); // Libde265 can only decoder HEVC in YUV format this->rawFormat = video::RawFormat::YUV; QSettings settings; settings.beginGroup("Decoders"); - loadDecoderLibrary(settings.value("libDav1dFile", "").toString()); + this->loadDecoderLibrary(settings.value("libDav1dFile", "").toString().toStdString()); settings.endGroup(); bool resetDecoder; - setDecodeSignal(signalID, resetDecoder); - allocateNewDecoder(); + this->setDecodeSignal(signalID, resetDecoder); + this->allocateNewDecoder(); } decoderDav1d::~decoderDav1d() { - if (decoder != nullptr) + if (this->decoder != nullptr) { // Free the decoder - this->lib.dav1d_close(&decoder); - if (decoder != nullptr) + this->lib.dav1d_close(&this->decoder); + if (this->decoder != nullptr) DEBUG_DAV1D( "Error closing the decoder. The close function should set the decoder pointer to NULL"); } @@ -139,17 +507,17 @@ decoderDav1d::~decoderDav1d() void decoderDav1d::resetDecoder() { - if (!decoder) - return setError("Resetting the decoder failed. No decoder allocated."); + if (!this->decoder) + return this->setError("Resetting the decoder failed. No decoder allocated."); - this->lib.dav1d_close(&decoder); - if (decoder != nullptr) + this->lib.dav1d_close(&this->decoder); + if (this->decoder != nullptr) DEBUG_DAV1D( "Error closing the decoder. The close function should set the decoder pointer to NULL"); decoderBase::resetDecoder(); - decoder = nullptr; + this->decoder = nullptr; - allocateNewDecoder(); + this->allocateNewDecoder(); } bool decoderDav1d::isSignalDifference(int signalID) const @@ -157,68 +525,67 @@ bool decoderDav1d::isSignalDifference(int signalID) const return signalID == 2 || signalID == 3; } -QStringList decoderDav1d::getSignalNames() const +StringVec decoderDav1d::getSignalNames() const { - return QStringList() << "Reconstruction" - << "Prediction" - << "Reconstruction pre-filter"; + return {"Reconstruction", "Prediction", "Reconstruction pre-filter"}; } void decoderDav1d::setDecodeSignal(int signalID, bool &decoderResetNeeded) { decoderResetNeeded = false; - if (signalID == decodeSignal) + if (signalID == this->decodeSignal) return; if (signalID >= 0 && signalID < nrSignalsSupported()) - decodeSignal = signalID; + this->decodeSignal = signalID; decoderResetNeeded = true; } void decoderDav1d::resolveLibraryFunctionPointers() { // Get/check function pointers - if (!resolve(this->lib.dav1d_version, "dav1d_version")) + if (!this->resolve(this->lib.dav1d_version, "dav1d_version")) return; - if (!resolve(this->lib.dav1d_default_settings, "dav1d_default_settings")) + if (!this->resolve(this->lib.dav1d_default_settings, "dav1d_default_settings")) return; - if (!resolve(this->lib.dav1d_open, "dav1d_open")) + if (!this->resolve(this->lib.dav1d_open, "dav1d_open")) return; - if (!resolve(this->lib.dav1d_parse_sequence_header, "dav1d_parse_sequence_header")) + if (!this->resolve(this->lib.dav1d_parse_sequence_header, "dav1d_parse_sequence_header")) return; - if (!resolve(this->lib.dav1d_send_data, "dav1d_send_data")) + if (!this->resolve(this->lib.dav1d_send_data, "dav1d_send_data")) return; - if (!resolve(this->lib.dav1d_get_picture, "dav1d_get_picture")) + if (!this->resolve(this->lib.dav1d_get_picture, "dav1d_get_picture")) return; - if (!resolve(this->lib.dav1d_close, "dav1d_close")) + if (!this->resolve(this->lib.dav1d_close, "dav1d_close")) return; - if (!resolve(this->lib.dav1d_flush, "dav1d_flush")) + if (!this->resolve(this->lib.dav1d_flush, "dav1d_flush")) return; - if (!resolve(this->lib.dav1d_data_create, "dav1d_data_create")) + if (!this->resolve(this->lib.dav1d_data_create, "dav1d_data_create")) return; DEBUG_DAV1D("decoderDav1d::resolveLibraryFunctionPointers - decoding functions found"); // This means that - if (!resolve(this->lib.dav1d_default_analyzer_settings, "dav1d_default_analyzer_flags", true)) + if (!this->resolve( + this->lib.dav1d_default_analyzer_settings, "dav1d_default_analyzer_flags", true)) return; - if (!resolve(this->lib.dav1d_set_analyzer_settings, "dav1d_set_analyzer_flags", true)) + if (!this->resolve(this->lib.dav1d_set_analyzer_settings, "dav1d_set_analyzer_flags", true)) return; DEBUG_DAV1D("decoderDav1d::resolveLibraryFunctionPointers - analyzer functions found"); - internalsSupported = true; - nrSignals = 3; // We can also get prediction and reconstruction before filtering - curPicture.setInternalsSupported(); + this->internalsSupported = true; + this->nrSignals = 3; // We can also get prediction and reconstruction before filtering + this->curPicture.setInternalsSupported(); } template T decoderDav1d::resolve(T &fun, const char *symbol, bool optional) { - QFunctionPointer ptr = this->library.resolve(symbol); + auto ptr = this->library.resolve(symbol); if (!ptr) { if (!optional) - setError(QStringLiteral("Error loading the libde265 library: Can't find function %1.") - .arg(symbol)); + this->setError("Error loading the libde265 library: Can't find function " + + std::string(symbol)); return nullptr; } @@ -227,41 +594,41 @@ template T decoderDav1d::resolve(T &fun, const char *symbol, bool o void decoderDav1d::allocateNewDecoder() { - if (decoder != nullptr) + if (this->decoder != nullptr) { DEBUG_DAV1D("decoderDav1d::allocateNewDecoder Error a decoder was already allocated"); return; } - if (decoderState == DecoderState::Error) + if (this->decoderState == DecoderState::Error) return; - DEBUG_DAV1D("decoderDav1d::allocateNewDecoder - decodeSignal %d", decodeSignal); + DEBUG_DAV1D("decoderDav1d::allocateNewDecoder - decodeSignal %d", this->decodeSignal); - this->lib.dav1d_default_settings(&settings); + this->lib.dav1d_default_settings(&this->settings); // Create new decoder object - int err = this->lib.dav1d_open(&decoder, &settings); + int err = this->lib.dav1d_open(&this->decoder, &this->settings); if (err != 0) { - decoderState = DecoderState::Error; - setError("Error opening new decoder (dav1d_open)"); + this->decoderState = DecoderState::Error; + this->setError("Error opening new decoder (dav1d_open)"); return; } - if (internalsSupported) + if (this->internalsSupported) { // Apply the analizer settings - this->lib.dav1d_default_analyzer_settings(&analyzerSettings); + this->lib.dav1d_default_analyzer_settings(&this->analyzerSettings); if (nrSignals > 0) { - if (decodeSignal == 1) + if (this->decodeSignal == 1) { - analyzerSettings.export_prediction = 1; + this->analyzerSettings.export_prediction = 1; DEBUG_DAV1D("decoderDav1d::allocateNewDecoder - Activated export of prediction"); } - else if (decodeSignal == 2) + else if (this->decodeSignal == 2) { - analyzerSettings.export_prefilter = 1; + this->analyzerSettings.export_prefilter = 1; DEBUG_DAV1D( "decoderDav1d::allocateNewDecoder - Activated export of reconstruction pre-filtering"); } @@ -271,108 +638,119 @@ void decoderDav1d::allocateNewDecoder() analyzerSettings.export_blkdata = 1; DEBUG_DAV1D("decoderDav1d::allocateNewDecoder - Activated export of block data"); } - this->lib.dav1d_set_analyzer_settings(decoder, &analyzerSettings); + this->lib.dav1d_set_analyzer_settings(this->decoder, &this->analyzerSettings); } // The decoder is ready to receive data decoderBase::resetDecoder(); - currentOutputBuffer.clear(); - decodedFrameWaiting = false; - flushing = false; + this->currentOutputBuffer.clear(); + this->decodedFrameWaiting = false; + this->flushing = false; } bool decoderDav1d::decodeNextFrame() { - if (decoderState != DecoderState::RetrieveFrames) + if (this->decoderState != DecoderState::RetrieveFrames) { DEBUG_DAV1D("decoderLibde265::decodeNextFrame: Wrong decoder state."); return false; } - if (decodedFrameWaiting) + if (this->decodedFrameWaiting) { - decodedFrameWaiting = false; + this->decodedFrameWaiting = false; return true; } - return decodeFrame(); + return this->decodeFrame(); } bool decoderDav1d::decodeFrame() { - if (decoder == nullptr) + if (this->decoder == nullptr) return false; - curPicture.clear(); + this->curPicture.clear(); + + DEBUG_DAV1D("decoderDav1d::decodeFrame Start dav1d_get_picture"); + auto res = this->lib.dav1d_get_picture(this->decoder, this->curPicture.getPicture()); + DEBUG_DAV1D("decoderDav1d::decodeFrame End dav1d_get_picture"); - int res = this->lib.dav1d_get_picture(decoder, curPicture.getPicture()); if (res >= 0) { // We did get a picture // Get the resolution / yuv format from the frame - auto s = curPicture.getFrameSize(); + auto s = this->curPicture.getFrameSize(); if (!s.isValid()) DEBUG_DAV1D("decoderDav1d::decodeFrame got invalid frame size"); - auto subsampling = curPicture.getSubsampling(); + auto subsampling = this->curPicture.getSubsampling(); if (subsampling == Subsampling::UNKNOWN) DEBUG_DAV1D("decoderDav1d::decodeFrame got invalid subsampling"); - auto bitDepth = functions::clipToUnsigned(curPicture.getBitDepth()); + auto bitDepth = functions::clipToUnsigned(this->curPicture.getBitDepth()); if (bitDepth < 8 || bitDepth > 16) DEBUG_DAV1D("decoderDav1d::decodeFrame got invalid bit depth"); - if (!frameSize.isValid() && !formatYUV.isValid()) + if (!this->frameSize.isValid() && !this->formatYUV.isValid()) { // Set the values - frameSize = s; - formatYUV = video::yuv::PixelFormatYUV(subsampling, bitDepth); + this->frameSize = s; + this->formatYUV = video::yuv::PixelFormatYUV(subsampling, bitDepth); } else { // Check the values against the previously set values - if (frameSize != s) - return setErrorB("Received a frame of different size"); - if (formatYUV.getSubsampling() != subsampling) - return setErrorB("Received a frame with different subsampling"); - if (formatYUV.getBitsPerSample() != bitDepth) - return setErrorB("Received a frame with different bit depth"); + if (this->frameSize != s) + return this->setErrorB("Received a frame of different size"); + if (this->formatYUV.getSubsampling() != subsampling) + return this->setErrorB("Received a frame with different subsampling"); + if (this->formatYUV.getBitsPerSample() != bitDepth) + return this->setErrorB("Received a frame with different bit depth"); } DEBUG_DAV1D("decoderDav1d::decodeFrame Picture decoded - switching to retrieve frame mode"); - decoderState = DecoderState::RetrieveFrames; - currentOutputBuffer.clear(); + this->decoderState = DecoderState::RetrieveFrames; + this->currentOutputBuffer.clear(); return true; } - else if (res != -EAGAIN) - return setErrorB("Error retrieving frame from decoder."); + if (res == -EAGAIN) + { + DEBUG_DAV1D("decoderDav1d::decodeFrame Not enough data to output a frame. Push more data."); + } + else + return this->setErrorB("Error retrieving frame from decoder."); - if (decoderState != DecoderState::NeedsMoreData) + if (this->decoderState != DecoderState::NeedsMoreData) + { DEBUG_DAV1D("decoderDav1d::decodeFrame No frame available - switching back to data push mode"); - decoderState = DecoderState::NeedsMoreData; + this->decoderState = DecoderState::NeedsMoreData; + } return false; } QByteArray decoderDav1d::getRawFrameData() { - auto s = curPicture.getFrameSize(); + auto s = this->curPicture.getFrameSize(); if (s.width <= 0 || s.height <= 0) { DEBUG_DAV1D("decoderDav1d::getRawFrameData: Current picture has invalid size."); - return QByteArray(); + return {}; } if (decoderState != DecoderState::RetrieveFrames) { DEBUG_DAV1D("decoderDav1d::getRawFrameData: Wrong decoder state."); - return QByteArray(); + return {}; } - if (currentOutputBuffer.isEmpty()) + if (this->currentOutputBuffer.isEmpty()) { // Put image data into buffer - copyImgToByteArray(curPicture, currentOutputBuffer); + copyImgToByteArray(curPicture, currentOutputBuffer, this->frameSize, this->decodeSignal); DEBUG_DAV1D("decoderDav1d::getRawFrameData copied frame to buffer"); if (this->statisticsEnabled()) - // Get the statistics from the image and put them into the statistics cache - this->cacheStatistics(curPicture); + { + DEBUG_DAV1D("decoderDav1d::getRawFrameData Getting statistics data from image"); + cacheStatistics(this->statisticsData, curPicture, this->subBlockSize); + } } return currentOutputBuffer; @@ -380,18 +758,18 @@ QByteArray decoderDav1d::getRawFrameData() bool decoderDav1d::pushData(QByteArray &data) { - if (decoderState != DecoderState::NeedsMoreData) + if (this->decoderState != DecoderState::NeedsMoreData) { DEBUG_DAV1D("decoderDav1d::pushData: Wrong decoder state."); return false; } - if (flushing) + if (this->flushing) { DEBUG_DAV1D("decoderDav1d::pushData: Do not push data when flushing!"); return false; } - if (!sequenceHeaderPushed) + if (!this->sequenceHeaderPushed) { // The first packet which is pushed to the decoder should be a sequence header. // Otherwise, the decoder can not decode the data. @@ -399,15 +777,15 @@ bool decoderDav1d::pushData(QByteArray &data) { DEBUG_DAV1D( "decoderDav1d::pushData Error: Sequence header not pushed yet and the data is empty"); - return setErrorB("Error: Sequence header not pushed yet and the data is empty."); + return this->setErrorB("Error: Sequence header not pushed yet and the data is empty."); } Dav1dSequenceHeader seq; - int err = + auto err = this->lib.dav1d_parse_sequence_header(&seq, (const uint8_t *)data.data(), data.size()); if (err == 0) { - sequenceHeaderPushed = true; + this->sequenceHeaderPushed = true; auto s = Size(seq.max_width, seq.max_height); if (!s.isValid()) @@ -418,7 +796,7 @@ bool decoderDav1d::pushData(QByteArray &data) int bitDepth = (seq.hbd == 0 ? 8 : (seq.hbd == 1 ? 10 : (seq.hbd == 2 ? 12 : -1))); if (bitDepth < 8 || bitDepth > 16) DEBUG_DAV1D("decoderDav1d::pushData got invalid bit depth"); - subBlockSize = (seq.sb128 >= 1) ? 128 : 64; + this->subBlockSize = (seq.sb128 >= 1) ? 128 : 64; this->frameSize = s; this->formatYUV = video::yuv::PixelFormatYUV(subsampling, bitDepth); @@ -435,110 +813,49 @@ bool decoderDav1d::pushData(QByteArray &data) { // The input file is at the end. Switch to flushing mode. DEBUG_DAV1D("decoderDav1d::pushData input ended - flushing"); - flushing = true; + this->flushing = true; } else { // Since dav1d consumes the data (takes ownership), we need to copy it to a new buffer from // dav1d - Dav1dData *dav1dData = new Dav1dData; - uint8_t * rawDataPointer = this->lib.dav1d_data_create(dav1dData, data.size()); + auto dav1dData = std::make_unique(); + auto rawDataPointer = this->lib.dav1d_data_create(dav1dData.get(), data.size()); memcpy(rawDataPointer, data.data(), data.size()); - int err = this->lib.dav1d_send_data(decoder, dav1dData); - if (err == -EAGAIN) + DEBUG_DAV1D("decoderDav1d::pushData Start pushing data size %d", data.size()); + int err = this->lib.dav1d_send_data(decoder, dav1dData.get()); + DEBUG_DAV1D("decoderDav1d::pushData Finished pushing data"); + if (err == 0) + { + dav1dData.release(); + DEBUG_DAV1D("decoderDav1d::pushData successfully pushed %d bytes", data.size()); + } + else if (err == -EAGAIN) { - // The data was not consumed and must be pushed again after retrieving some frames - delete dav1dData; - DEBUG_DAV1D("decoderDav1d::pushData need to re-push data"); + DEBUG_DAV1D("decoderDav1d::pushData Need to re-push data after retrieving some frames"); return false; } - else if (err != 0) + else { - delete dav1dData; DEBUG_DAV1D("decoderDav1d::pushData error pushing data"); - return setErrorB("Error pushing data to the decoder."); + return this->setErrorB("Error pushing data to the decoder."); } - DEBUG_DAV1D("decoderDav1d::pushData successfully pushed %d bytes", data.size()); } // Check for an available frame - if (decodeFrame()) - decodedFrameWaiting = true; + if (this->decodeFrame()) + this->decodedFrameWaiting = true; return true; } -#if SSE_CONVERSION -void decoderDav1d::copyImgToByteArray(const Dav1dPictureWrapper &src, byteArrayAligned &dst) -#else -void decoderDav1d::copyImgToByteArray(const Dav1dPictureWrapper &src, QByteArray &dst) -#endif -{ - // How many image planes are there? - int nrPlanes = (src.getSubsampling() == Subsampling::YUV_400) ? 1 : 3; - - // At first get how many bytes we are going to write - const auto nrBytesPerSample = (src.getBitDepth() > 8) ? 2 : 1; - const auto framSize = src.getFrameSize(); - auto nrBytes = frameSize.width * frameSize.height * nrBytesPerSample; - auto layout = src.getSubsampling(); - if (layout == Subsampling::YUV_420) - nrBytes += (frameSize.width / 2) * (frameSize.height / 2) * 2 * nrBytesPerSample; - else if (layout == Subsampling::YUV_422) - nrBytes += (frameSize.width / 2) * frameSize.height * 2 * nrBytesPerSample; - else if (layout == Subsampling::YUV_444) - nrBytes += frameSize.width * frameSize.height * 2 * nrBytesPerSample; - - DEBUG_DAV1D("decoderDav1d::copyImgToByteArray nrBytes %d", nrBytes); - - // Is the output big enough? - if (dst.capacity() < int(nrBytes)) - dst.resize(int(nrBytes)); - - uint8_t *dst_c = (uint8_t *)dst.data(); - - // We can now copy from src to dst - for (int c = 0; c < nrPlanes; c++) - { - auto width = framSize.width; - auto height = framSize.height; - if (c != 0) - { - if (layout == Subsampling::YUV_420 || layout == Subsampling::YUV_422) - width /= 2; - if (layout == Subsampling::YUV_420) - height /= 2; - } - const size_t widthInBytes = width * nrBytesPerSample; - - uint8_t *img_c = nullptr; - if (decodeSignal == 0) - img_c = curPicture.getData(c); - else if (decodeSignal == 1) - img_c = curPicture.getDataPrediction(c); - else if (decodeSignal == 2) - img_c = curPicture.getDataReconstructionPreFiltering(c); - - if (img_c == nullptr) - return; - - const int stride = (c == 0) ? curPicture.getStride(0) : curPicture.getStride(1); - for (size_t y = 0; y < height; y++) - { - memcpy(dst_c, img_c, widthInBytes); - img_c += stride; - dst_c += widthInBytes; - } - } -} - -bool decoderDav1d::checkLibraryFile(QString libFilePath, QString &error) +bool decoderDav1d::checkLibraryFile(const std::string &libFilePath, std::string &error) { decoderDav1d testDecoder; // Try to load the library file - testDecoder.library.setFileName(libFilePath); + testDecoder.library.setFileName(QString::fromStdString(libFilePath)); if (!testDecoder.library.load()) { error = "Error opening QLibrary."; @@ -553,31 +870,28 @@ bool decoderDav1d::checkLibraryFile(QString libFilePath, QString &error) return testDecoder.state() != DecoderState::Error; } -QString decoderDav1d::getDecoderName() const +std::string decoderDav1d::getDecoderName() const { if (decoder) { - QString ver = QString(this->lib.dav1d_version()); + auto ver = std::string(this->lib.dav1d_version()); return "Dav1d deoder Version " + ver; } return "Dav1d decoder"; } -QStringList decoderDav1d::getLibraryNames() const +StringVec decoderDav1d::getLibraryNames() const { // If the file name is not set explicitly, QLibrary will try to open // the libde265.so file first. Since this has been compiled for linux // it will fail and not even try to open the libde265.dylib. // On windows and linux ommitting the extension works if (is_Q_OS_MAC) - return QStringList() << "libdav1d-internals.dylib" - << "libdav1d.dylib"; + return {"libdav1d-internals.dylib", "libdav1d.dylib"}; if (is_Q_OS_WIN) - return QStringList() << "dav1d-internals" - << "dav1d"; + return {"dav1d-internals", "dav1d"}; if (is_Q_OS_LINUX) - return QStringList() << "libdav1d-internals" - << "libdav1d"; + return {"libdav1d-internals", "libdav1d"}; } void decoderDav1d::fillStatisticList(stats::StatisticsData &statisticsData) const @@ -775,317 +1089,4 @@ void decoderDav1d::fillStatisticList(stats::StatisticsData &statisticsData) cons statisticsData.addStatType(transformDepth); } -void decoderDav1d::cacheStatistics(const Dav1dPictureWrapper &img) -{ - if (!internalsSupported || !this->statisticsEnabled()) - return; - - DEBUG_DAV1D("decoderDav1d::cacheStatistics"); - - Av1Block * blockData = img.getBlockData(); - Dav1dFrameHeader *frameHeader = img.getFrameHeader(); - if (frameHeader == nullptr) - return; - - dav1dFrameInfo frameInfo(img.getFrameSize(), frameHeader->frame_type); - frameInfo.frameSize = img.getFrameSize(); - - const int sb_step = subBlockSize >> 2; - - for (unsigned y = 0; y < frameInfo.frameSizeAligned.height; y += sb_step) - for (unsigned x = 0; x < frameInfo.frameSizeAligned.width; x += sb_step) - parseBlockRecursive(blockData, x, y, BL_128X128, frameInfo); -} - -void decoderDav1d::parseBlockRecursive( - Av1Block *blockData, unsigned x, unsigned y, BlockLevel level, dav1dFrameInfo &frameInfo) -{ - if (y >= frameInfo.sizeInBlocks.height) - return; - - Av1Block b = blockData[y * frameInfo.b4_stride + x]; - const BlockLevel blockLevel = (BlockLevel)b.bl; - - // assert(b.bl >= 0 && b.bl <= 4); - if (b.bl > 4) - return; - - const int blockWidth4 = 1 << (5 - level); - - if (blockLevel > level) - { - // Recurse - const BlockLevel nextLevel = (BlockLevel)(level + 1); - const int subw = blockWidth4 / 2; - parseBlockRecursive(blockData, x, y, nextLevel, frameInfo); - parseBlockRecursive(blockData, x + subw, y, nextLevel, frameInfo); - parseBlockRecursive(blockData, x, y + subw, nextLevel, frameInfo); - parseBlockRecursive(blockData, x + subw, y + subw, nextLevel, frameInfo); - } - else - { - // Parse the current partition. How is it split into blocks? - const BlockPartition blockPartition = (BlockPartition)b.bp; - - const int o = blockWidth4 / 2; - const int oq = blockWidth4 / 2; - const int bs = blockWidth4; - switch (blockPartition) - { - case PARTITION_NONE: - parseBlockPartition(blockData, x, y, bs, bs, frameInfo); - break; - case PARTITION_H: - parseBlockPartition(blockData, x, y, bs, bs / 2, frameInfo); - parseBlockPartition(blockData, x, y + o, bs, bs / 2, frameInfo); - break; - case PARTITION_V: - parseBlockPartition(blockData, x, y, bs / 2, bs, frameInfo); - parseBlockPartition(blockData, x + o, y, bs / 2, bs, frameInfo); - break; - case PARTITION_T_TOP_SPLIT: // PARTITION_HORZ_A - parseBlockPartition(blockData, x, y, bs / 2, bs / 2, frameInfo); - parseBlockPartition(blockData, x + o, y, bs / 2, bs / 2, frameInfo); - parseBlockPartition(blockData, x, y + o, bs, bs / 2, frameInfo); - break; - case PARTITION_T_BOTTOM_SPLIT: // PARTITION_HORZ_B - parseBlockPartition(blockData, x, y, bs, bs / 2, frameInfo); - parseBlockPartition(blockData, x, y + o, bs / 2, bs / 2, frameInfo); - parseBlockPartition(blockData, x + o, y + o, bs / 2, bs / 2, frameInfo); - break; - case PARTITION_T_LEFT_SPLIT: // PARTITION_VERT_A - parseBlockPartition(blockData, x, y, bs / 2, bs / 2, frameInfo); - parseBlockPartition(blockData, x, y + o, bs / 2, bs / 2, frameInfo); - parseBlockPartition(blockData, x + o, y, bs / 2, bs, frameInfo); - break; - case PARTITION_T_RIGHT_SPLIT: // PARTITION_VERT_B - parseBlockPartition(blockData, x, y, bs / 2, bs, frameInfo); - parseBlockPartition(blockData, x, y + o, bs / 2, bs / 2, frameInfo); - parseBlockPartition(blockData, x + o, y + o, bs / 2, bs / 2, frameInfo); - break; - case PARTITION_H4: // PARTITION_HORZ_4 - parseBlockPartition(blockData, x, y, bs, bs / 4, frameInfo); - parseBlockPartition(blockData, x, y + oq, bs, bs / 4, frameInfo); - parseBlockPartition(blockData, x, y + oq * 2, bs, bs / 4, frameInfo); - parseBlockPartition(blockData, x, y + oq * 3, bs, bs / 4, frameInfo); - break; - case PARTITION_V4: // PARTITION_VER_4 - parseBlockPartition(blockData, x, y, bs / 4, bs, frameInfo); - parseBlockPartition(blockData, x + oq, y, bs / 4, bs, frameInfo); - parseBlockPartition(blockData, x + oq * 2, y, bs / 4, bs, frameInfo); - parseBlockPartition(blockData, x + oq * 3, y, bs / 4, bs, frameInfo); - break; - case PARTITION_SPLIT: - if (blockLevel == BL_8X8) - { - // 4 square 4x4 blocks. This is allowed. - assert(blockWidth4 == 2); - parseBlockPartition(blockData, x, y, 1, 1, frameInfo); - parseBlockPartition(blockData, x + 1, y, 1, 1, frameInfo); - parseBlockPartition(blockData, x, y + 1, 1, 1, frameInfo); - parseBlockPartition(blockData, x + 1, y + 1, 1, 1, frameInfo); - } - else - { - // This should not happen here - return; - } - break; - default: - return; - } - } -} - -void decoderDav1d::parseBlockPartition(Av1Block * blockData, - unsigned x, - unsigned y, - unsigned blockWidth4, - unsigned blockHeight4, - dav1dFrameInfo &frameInfo) -{ - if (y >= frameInfo.sizeInBlocks.height || x >= frameInfo.sizeInBlocks.width) - return; - - Av1Block b = blockData[y * frameInfo.b4_stride + x]; - - const auto cbPosX = x * 4; - const auto cbPosY = y * 4; - const auto cbWidth = blockWidth4 * 4; - const auto cbHeight = blockHeight4 * 4; - - // Set prediction mode (ID 0) - const bool isIntra = (b.intra != 0); - const int predMode = isIntra ? 0 : 1; - this->statisticsData->at(0).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, predMode); - - bool FrameIsIntra = (frameInfo.frameType == DAV1D_FRAME_TYPE_KEY || - frameInfo.frameType == DAV1D_FRAME_TYPE_INTRA); - if (FrameIsIntra) - { - // Set the segment ID (ID 1) - this->statisticsData->at(1).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.seg_id); - } - - // Set the skip "flag" (ID 2) - this->statisticsData->at(2).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.skip); - - // Set the skip_mode (ID 3) - this->statisticsData->at(3).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.skip_mode); - - if (isIntra) - { - // Set the intra pred mode luma/chrmoa (ID 4, 5) - this->statisticsData->at(4).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.y_mode); - this->statisticsData->at(5).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.uv_mode); - - // Set the palette size Y/UV (ID 6, 7) - this->statisticsData->at(6).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.pal_sz[0]); - this->statisticsData->at(7).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.pal_sz[1]); - - // Set the intra angle delta luma/chroma (ID 8, 9) - this->statisticsData->at(8).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.y_angle); - this->statisticsData->at(9).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.uv_angle); - - // Calculate and set the intra prediction direction luma/chroma (ID 10, 11) - for (int yc = 0; yc < 2; yc++) - { - int angleDelta = (yc == 0) ? b.y_angle : b.uv_angle; - IntraPredMode predMode = (yc == 0) ? (IntraPredMode)b.y_mode : (IntraPredMode)b.uv_mode; - auto vec = calculateIntraPredDirection(predMode, angleDelta); - if (vec.first == 0 && vec.second == 0) - continue; - - int blockScale = std::min(blockWidth4, blockHeight4); - int vecX = (float)vec.first * blockScale / 4; - int vecY = (float)vec.second * blockScale / 4; - - this->statisticsData->at(10 + yc).addBlockVector( - cbPosX, cbPosY, cbWidth, cbHeight, vecX, vecY); - } - - if (b.y_mode == CFL_PRED) - { - // Set the chroma from luma alpha U/V (ID 12, 13) - this->statisticsData->at(12).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.cfl_alpha[0]); - this->statisticsData->at(13).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.cfl_alpha[1]); - } - } - else // inter - { - CompInterType compoundType = (CompInterType)b.comp_type; - bool isCompound = (compoundType != COMP_INTER_NONE); - - // Set the reference frame indices 0/1 (ID 14, 15) - this->statisticsData->at(14).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.ref[0]); - if (isCompound) - this->statisticsData->at(15).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.ref[1]); - - // Set the compound prediction type (ID 16) - this->statisticsData->at(16).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.comp_type); - - // Set the wedge index (ID 17) - if (b.comp_type == COMP_INTER_WEDGE || b.interintra_type == INTER_INTRA_WEDGE) - this->statisticsData->at(17).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.wedge_idx); - - // Set the mask sign (ID 18) - if (isCompound) // TODO: This might not be correct - this->statisticsData->at(18).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.mask_sign); - - // Set the inter mode (ID 19) - this->statisticsData->at(19).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.inter_mode); - - // Set the dynamic reference list index (ID 20) - if (isCompound) // TODO: This might not be correct - this->statisticsData->at(20).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.drl_idx); - - if (isCompound) - { - // Set inter intra type (ID 21) - this->statisticsData->at(21).addBlockValue( - cbPosX, cbPosY, cbWidth, cbHeight, b.interintra_type); - // Set inter intra mode (ID 22) - this->statisticsData->at(22).addBlockValue( - cbPosX, cbPosY, cbWidth, cbHeight, b.interintra_mode); - } - - // Set motion mode (ID 23) - this->statisticsData->at(23).addBlockValue(cbPosX, cbPosY, cbWidth, cbHeight, b.motion_mode); - - // Set motion vector 0/1 (ID 24, 25) - this->statisticsData->at(24).addBlockVector( - cbPosX, cbPosY, cbWidth, cbHeight, b.mv[0].x, b.mv[0].y); - if (isCompound) - this->statisticsData->at(25).addBlockVector( - cbPosX, cbPosY, cbWidth, cbHeight, b.mv[1].x, b.mv[1].y); - } - - const TxfmSize tx_val = TxfmSize(isIntra ? b.tx : b.max_ytx); - static const unsigned TxfmSizeWidthTable[] = { - 4, 8, 16, 32, 64, 4, 8, 8, 16, 16, 32, 32, 64, 4, 16, 8, 32, 16, 64}; - static const unsigned TxfmSizeHeightTable[] = { - 4, 8, 16, 32, 64, 8, 4, 16, 8, 32, 16, 64, 32, 16, 4, 32, 8, 64, 16}; - const unsigned tx_w = TxfmSizeWidthTable[tx_val]; - const unsigned tx_h = TxfmSizeHeightTable[tx_val]; - - if (tx_w > cbWidth || tx_h > cbHeight) - // Transform can not be bigger then the coding block - return; - - for (unsigned x = 0; x < cbWidth; x += tx_w) - { - for (unsigned y = 0; y < cbHeight; y += tx_h) - { - // Set the transform size (ID 26) - const int x_abs = cbPosX + x; - const int y_abs = cbPosY + y; - if (x_abs < int(frameInfo.frameSize.width) && y_abs < int(frameInfo.frameSize.height)) - this->statisticsData->at(26).addBlockValue(x_abs, y_abs, tx_w, tx_h, (int)tx_val); - } - } -} - -IntPair decoderDav1d::calculateIntraPredDirection(IntraPredMode predMode, int angleDelta) -{ - if (predMode == DC_PRED || predMode > VERT_LEFT_PRED) - return {}; - - // angleDelta should be between -4 and 3 - const int modeIndex = predMode - VERT_PRED; - assert(modeIndex >= 0 && modeIndex < 8); - const int deltaIndex = angleDelta + 4; - assert(deltaIndex >= 0 && deltaIndex < 8); - - /* The python code which was used to create the vectorTable - import math - - def getVector(m, s): - a = m + 3 * s - x = -math.cos(math.radians(a)) - y = -math.sin(math.radians(a)) - return round(x*32), round(y*32) - - modes = [90, 180, 45, 135, 113, 157, 203, 67] - sub_angles = [-4, -3, -2, -1, 0, 1, 2, 3] - for m in modes: - out = "" - for s in sub_angles: - x, y = getVector(m, s) - out += "{{{0}, {1}}}, ".format(x, y) - print(out) - */ - - static const int vectorTable[8][8][2] = { - {{-7, 31}, {-5, 32}, {-3, 32}, {-2, 32}, {0, 32}, {2, 32}, {3, 32}, {5, 32}}, - {{31, 7}, {32, 5}, {32, 3}, {32, 2}, {32, 0}, {32, -2}, {32, -3}, {32, -5}}, - {{-27, 17}, {-26, 19}, {-25, 20}, {-24, 21}, {-23, 23}, {-21, 24}, {-20, 25}, {-19, 26}}, - {{17, 27}, {19, 26}, {20, 25}, {21, 24}, {23, 23}, {24, 21}, {25, 20}, {26, 19}}, - {{6, 31}, {8, 31}, {9, 31}, {11, 30}, {13, 29}, {14, 29}, {16, 28}, {17, 27}}, - {{26, 18}, {27, 17}, {28, 16}, {29, 14}, {29, 13}, {30, 11}, {31, 9}, {31, 8}}, - {{31, -6}, {31, -8}, {31, -9}, {30, -11}, {29, -13}, {29, -14}, {28, -16}, {27, -17}}, - {{-18, 26}, {-17, 27}, {-16, 28}, {-14, 29}, {-13, 29}, {-11, 30}, {-9, 31}, {-8, 31}}}; - - return {vectorTable[modeIndex][deltaIndex][0], vectorTable[modeIndex][deltaIndex][1]}; -} - } // namespace decoder diff --git a/YUViewLib/src/decoder/decoderDav1d.h b/YUViewLib/src/decoder/decoderDav1d.h index 071594efd..97b66b86b 100644 --- a/YUViewLib/src/decoder/decoderDav1d.h +++ b/YUViewLib/src/decoder/decoderDav1d.h @@ -117,10 +117,10 @@ class decoderDav1d : public decoderBaseSingleLib void resetDecoder() override; - int nrSignalsSupported() const override { return nrSignals; } - bool isSignalDifference(int signalID) const override; - QStringList getSignalNames() const override; - void setDecodeSignal(int signalID, bool &decoderResetNeeded) override; + int nrSignalsSupported() const override { return nrSignals; } + bool isSignalDifference(int signalID) const override; + StringVec getSignalNames() const override; + void setDecodeSignal(int signalID, bool &decoderResetNeeded) override; // Decoding / pushing data bool decodeNextFrame() override; @@ -128,10 +128,10 @@ class decoderDav1d : public decoderBaseSingleLib bool pushData(QByteArray &data) override; // Check if the given library file is an existing libde265 decoder that we can use. - static bool checkLibraryFile(QString libFilePath, QString &error); + static bool checkLibraryFile(const std::string &libFilePath, std::string &error); - QString getDecoderName() const override; - QString getCodecName() const override { return "AV1"; } + std::string getDecoderName() const override; + std::string getCodecName() const override { return "AV1"; } private: // A private constructor that creates an uninitialized decoder library. @@ -142,7 +142,7 @@ class decoderDav1d : public decoderBaseSingleLib void resolveLibraryFunctionPointers() override; // Return the possible names of the dav1d library - QStringList getLibraryNames() const override; + StringVec getLibraryNames() const override; // The function template for resolving the functions. // This can not go into the base class because then the template @@ -173,27 +173,13 @@ class decoderDav1d : public decoderBaseSingleLib // necessary without invoking the copy operation from the libde265 buffer to the QByteArray again. #if SSE_CONVERSION byteArrayAligned currentOutputBuffer; - void copyImgToByteArray(const Dav1dPictureWrapper &src, byteArrayAligned &dst); #else QByteArray currentOutputBuffer; - void copyImgToByteArray( - const Dav1dPictureWrapper &src, - QByteArray & dst); // Copy the raw data from the Dav1dPicture source *src to the byte array #endif - // Statistics void fillStatisticList(stats::StatisticsData &) const override; - void cacheStatistics(const Dav1dPictureWrapper &img); - void parseBlockRecursive( - Av1Block *blockData, unsigned x, unsigned y, BlockLevel level, dav1dFrameInfo &frameInfo); - void parseBlockPartition(Av1Block * blockData, - unsigned x, - unsigned y, - unsigned blockWidth4, - unsigned blockHeight4, - dav1dFrameInfo &frameInfo); - IntPair calculateIntraPredDirection(IntraPredMode predMode, int angleDelta); - unsigned int subBlockSize{}; + + int subBlockSize{}; LibraryFunctionsDav1d lib; }; diff --git a/YUViewLib/src/decoder/decoderFFmpeg.cpp b/YUViewLib/src/decoder/decoderFFmpeg.cpp index 110a3fb88..e9090e08f 100644 --- a/YUViewLib/src/decoder/decoderFFmpeg.cpp +++ b/YUViewLib/src/decoder/decoderFFmpeg.cpp @@ -364,7 +364,7 @@ bool decoderFFmpeg::pushAVPacket(FFmpeg::AVPacketWrapper &pkt) qDebug() << meaning; } #endif - this->setError(QStringLiteral("Error sending packet (avcodec_send_packet)")); + this->setError("Error sending packet (avcodec_send_packet)"); return false; } else @@ -405,7 +405,7 @@ bool decoderFFmpeg::decodeFrame() { // An error occurred DEBUG_FFMPEG("decoderFFmpeg::decodeFrame Error reading frame."); - return this->setErrorB(QStringLiteral("Error recieving frame (avcodec_receive_frame)")); + return this->setErrorB("Error recieving frame (avcodec_receive_frame)"); } else if (retRecieve == AVERROR(EAGAIN)) { @@ -440,21 +440,20 @@ bool decoderFFmpeg::createDecoder(FFmpeg::AVCodecIDWrapper codecID, { // Allocate the decoder context if (this->videoCodec) - return this->setErrorB(QStringLiteral("Video codec already allocated.")); + return this->setErrorB("Video codec already allocated."); this->videoCodec = this->ff.findDecoder(codecID); if (!this->videoCodec) - return this->setErrorB(QStringLiteral("Could not find a video decoder for the given codec ") + + return this->setErrorB("Could not find a video decoder for the given codec " + codecID.getCodecName()); if (this->decCtx) - return this->setErrorB(QStringLiteral("Decoder context already allocated.")); + return this->setErrorB("Decoder context already allocated."); this->decCtx = this->ff.allocDecoder(this->videoCodec); if (!this->decCtx) - return this->setErrorB( - QStringLiteral("Could not allocate video decoder (avcodec_alloc_context3)")); + return this->setErrorB("Could not allocate video decoder (avcodec_alloc_context3)"); if (codecpar && !this->ff.configureDecoder(decCtx, codecpar)) - return this->setErrorB(QStringLiteral("Unable to configure decoder from codecpar")); + return this->setErrorB("Unable to configure decoder from codecpar"); // Get some parameters from the decoder context this->frameSize = decCtx.getSize(); @@ -470,18 +469,18 @@ bool decoderFFmpeg::createDecoder(FFmpeg::AVCodecIDWrapper codecID, FFmpeg::AVDictionaryWrapper opts; int ret = this->ff.dictSet(opts, "flags2", "+export_mvs", 0); if (ret < 0) - return this->setErrorB( - QStringLiteral("Could not request motion vector retrieval. Return code %1").arg(ret)); + return this->setErrorB("Could not request motion vector retrieval. Return code" + + std::to_string(ret)); // Open codec ret = this->ff.avcodecOpen2(decCtx, videoCodec, opts); if (ret < 0) - return this->setErrorB( - QStringLiteral("Could not open the video codec (avcodec_open2). Return code %1.").arg(ret)); + return this->setErrorB("Could not open the video codec (avcodec_open2). Return code " + + std::to_string(ret)); this->frame = ff.allocateFrame(); if (!this->frame) - return this->setErrorB(QStringLiteral("Could not allocate frame (av_frame_alloc).")); + return this->setErrorB("Could not allocate frame (av_frame_alloc)."); return true; } diff --git a/YUViewLib/src/decoder/decoderFFmpeg.h b/YUViewLib/src/decoder/decoderFFmpeg.h index 6f2a33571..d64f97b1d 100644 --- a/YUViewLib/src/decoder/decoderFFmpeg.h +++ b/YUViewLib/src/decoder/decoderFFmpeg.h @@ -69,11 +69,11 @@ class decoderFFmpeg : public decoderBase // What statistics do we support? void fillStatisticList(stats::StatisticsData &statisticsData) const override; - QStringList getLibraryPaths() const override { return ff.getLibPaths(); } - QString getDecoderName() const override { return "FFmpeg"; } - QString getCodecName() const override { return this->codecName; } + StringVec getLibraryPaths() const override { return ff.getLibPaths(); } + std::string getDecoderName() const override { return "FFmpeg"; } + std::string getCodecName() const override { return this->codecName; } - static QStringList getLogMessages() { return FFmpeg::FFmpegVersionHandler::getFFmpegLog(); } + static StringVec getLogMessages() { return FFmpeg::FFmpegVersionHandler::getFFmpegLog(); } protected: FFmpeg::FFmpegVersionHandler ff; @@ -107,7 +107,7 @@ class decoderFFmpeg : public decoderBase // An array of AV_INPUT_BUFFER_PADDING_SIZE zeros to be added as padding in pushData QByteArray avPacketPaddingData; - QString codecName{}; + std::string codecName{}; }; } // namespace decoder diff --git a/YUViewLib/src/decoder/decoderHM.cpp b/YUViewLib/src/decoder/decoderHM.cpp index 7b4c29535..5a1aaeccd 100644 --- a/YUViewLib/src/decoder/decoderHM.cpp +++ b/YUViewLib/src/decoder/decoderHM.cpp @@ -107,7 +107,7 @@ decoderHM::decoderHM(int, bool cachingDecoder) : decoderBaseSingleLib(cachingDec // Try to load the decoder library (.dll on Windows, .so on Linux, .dylib on Mac) QSettings settings; settings.beginGroup("Decoders"); - loadDecoderLibrary(settings.value("libHMFile", "").toString()); + loadDecoderLibrary(settings.value("libHMFile", "").toString().toStdString()); settings.endGroup(); if (decoderState != DecoderState::Error) @@ -120,15 +120,14 @@ decoderHM::~decoderHM() this->lib.libHMDec_free_decoder(decoder); } -QStringList decoderHM::getLibraryNames() const +StringVec decoderHM::getLibraryNames() const { // If the file name is not set explicitly, QLibrary will try to open the .so file first. // Since this has been compiled for linux it will fail and not even try to open the .dylib. // On windows and linux ommitting the extension works - auto names = - is_Q_OS_MAC ? QStringList() << "libHMDecoder.dylib" : QStringList() << "libHMDecoder"; - - return names; + if (is_Q_OS_MAC) + return {"libHMDecoder.dylib"}; + return {"libHMDecoder"}; } void decoderHM::resolveLibraryFunctionPointers() @@ -199,8 +198,8 @@ template T decoderHM::resolve(T &fun, const char *symbol, bool opti if (!ptr) { if (!optional) - setError(QStringLiteral("Error loading the libde265 library: Can't find function %1.") - .arg(symbol)); + this->setError("Error loading the libde265 library: Can't find function " + + std::string(symbol)); return nullptr; } @@ -328,8 +327,8 @@ bool decoderHM::pushData(QByteArray &data) auto err = this->lib.libHMDec_push_nal_unit( decoder, data, data.length(), endOfFile, bNewPicture, checkOutputPictures); if (err != LIBHMDEC_OK) - return setErrorB(QString("Error pushing data to decoder (libHMDec_push_nal_unit) length %1") - .arg(data.length())); + return setErrorB("Error pushing data to decoder (libHMDec_push_nal_unit) length " + + std::to_string(data.length())); DEBUG_DECHM("decoderHM::pushData pushed NAL length %d%s%s", data.length(), bNewPicture ? " bNewPicture" : "", @@ -584,17 +583,17 @@ void decoderHM::fillStatisticList(stats::StatisticsData &statisticsData) const } } -QString decoderHM::getDecoderName() const +std::string decoderHM::getDecoderName() const { return (decoderState == DecoderState::Error) ? "HM" : this->lib.libHMDec_get_version(); } -bool decoderHM::checkLibraryFile(QString libFilePath, QString &error) +bool decoderHM::checkLibraryFile(const std::string &libFilePath, std::string &error) { decoderHM testDecoder; // Try to load the library file - testDecoder.library.setFileName(libFilePath); + testDecoder.library.setFileName(QString::fromStdString(libFilePath)); if (!testDecoder.library.load()) { error = "Error opening QLibrary."; diff --git a/YUViewLib/src/decoder/decoderHM.h b/YUViewLib/src/decoder/decoderHM.h index cf63b4432..1954f7e83 100644 --- a/YUViewLib/src/decoder/decoderHM.h +++ b/YUViewLib/src/decoder/decoderHM.h @@ -97,10 +97,10 @@ class decoderHM : public decoderBaseSingleLib bool pushData(QByteArray &data) override; // Check if the given library file is an existing libde265 decoder that we can use. - static bool checkLibraryFile(QString libFilePath, QString &error); + static bool checkLibraryFile(const std::string &libFilePath, std::string &error); - QString getDecoderName() const override; - QString getCodecName() const override { return "hevc"; } + std::string getDecoderName() const override; + std::string getCodecName() const override { return "hevc"; } int nrSignalsSupported() const override { return nrSignals; } @@ -110,7 +110,7 @@ class decoderHM : public decoderBaseSingleLib decoderHM(){}; // Return the possible names of the HM library - QStringList getLibraryNames() const override; + StringVec getLibraryNames() const override; // Try to resolve all the required function pointers from the library void resolveLibraryFunctionPointers() override; diff --git a/YUViewLib/src/decoder/decoderLibde265.cpp b/YUViewLib/src/decoder/decoderLibde265.cpp index 9287f7f25..522f3aaf1 100644 --- a/YUViewLib/src/decoder/decoderLibde265.cpp +++ b/YUViewLib/src/decoder/decoderLibde265.cpp @@ -98,7 +98,7 @@ decoderLibde265::decoderLibde265(int signalID, bool cachingDecoder) QSettings settings; settings.beginGroup("Decoders"); - this->loadDecoderLibrary(settings.value("libde265File", "").toString()); + this->loadDecoderLibrary(settings.value("libde265File", "").toString().toStdString()); settings.endGroup(); bool resetDecoder; @@ -236,8 +236,8 @@ template T decoderLibde265::resolve(T &fun, const char *symbol, boo if (!ptr) { if (!optional) - setError(QStringLiteral("Error loading the libde265 library: Can't find function %1.") - .arg(symbol)); + this->setError("Error loading the libde265 library: Can't find function " + + std::string(symbol)); return nullptr; } @@ -445,7 +445,7 @@ bool decoderLibde265::pushData(QByteArray &data) err != DE265_OK ? this->lib.de265_get_error_text(err) : ""); if (err != DE265_OK) return setErrorB("Error pushing data to decoder (de265_push_NAL): " + - QString(this->lib.de265_get_error_text(err))); + std::string(this->lib.de265_get_error_text(err))); } else { @@ -991,12 +991,12 @@ void decoderLibde265::fillStatisticList(stats::StatisticsData &statisticsData) c statisticsData.addStatType(transformDepth); } -bool decoderLibde265::checkLibraryFile(QString libFilePath, QString &error) +bool decoderLibde265::checkLibraryFile(const std::string &libFilePath, std::string &error) { decoderLibde265 testDecoder; // Try to load the library file - testDecoder.library.setFileName(libFilePath); + testDecoder.library.setFileName(QString::fromStdString(libFilePath)); if (!testDecoder.library.load()) { error = "Error opening QLibrary."; @@ -1010,7 +1010,7 @@ bool decoderLibde265::checkLibraryFile(QString libFilePath, QString &error) return testDecoder.state() != DecoderState::Error; } -QStringList decoderLibde265::getLibraryNames() const +StringVec decoderLibde265::getLibraryNames() const { // If the file name is not set explicitly, QLibrary will try to open // the libde265.so file first. Since this has been compiled for linux @@ -1021,7 +1021,9 @@ QStringList decoderLibde265::getLibraryNames() const : QStringList() << "libde265-internals" << "libde265"; - return libNames; + if (is_Q_OS_MAC) + return {"libde265-internals.dylib", "libde265.dylib"}; + return {"libde265-internals", "libde265"}; } } // namespace decoder \ No newline at end of file diff --git a/YUViewLib/src/decoder/decoderLibde265.h b/YUViewLib/src/decoder/decoderLibde265.h index ce1a7ac77..8c37e2ea9 100644 --- a/YUViewLib/src/decoder/decoderLibde265.h +++ b/YUViewLib/src/decoder/decoderLibde265.h @@ -96,14 +96,11 @@ class decoderLibde265 : public decoderBaseSingleLib void resetDecoder() override; - int nrSignalsSupported() const override { return nrSignals; } + int nrSignalsSupported() const override { return this->nrSignals; } bool isSignalDifference(int signalID) const override { return signalID == 2 || signalID == 3; } - QStringList getSignalNames() const override + StringVec getSignalNames() const override { - return QStringList() << "Reconstruction" - << "Prediction" - << "Residual" - << "Transform Coefficients"; + return {"Reconstruction", "Prediction", "Residual", "Transform Coefficients"}; } void setDecodeSignal(int signalID, bool &decoderResetNeeded) override; @@ -116,10 +113,10 @@ class decoderLibde265 : public decoderBaseSingleLib void fillStatisticList(stats::StatisticsData &statisticsData) const override; // Check if the given library file is an existing libde265 decoder that we can use. - static bool checkLibraryFile(QString libFilePath, QString &error); + static bool checkLibraryFile(const std::string &libFilePath, std::string &error); - QString getDecoderName() const override { return "libDe265"; } - QString getCodecName() const override { return "hevc"; } + std::string getDecoderName() const override { return "libDe265"; } + std::string getCodecName() const override { return "hevc"; } private: // A private constructor that creates an uninitialized decoder library. @@ -130,7 +127,7 @@ class decoderLibde265 : public decoderBaseSingleLib void resolveLibraryFunctionPointers() override; // Return the possible names of the libde265 library - QStringList getLibraryNames() const override; + StringVec getLibraryNames() const override; // The function template for resolving the functions. // This can not go into the base class because then the template diff --git a/YUViewLib/src/decoder/decoderVTM.cpp b/YUViewLib/src/decoder/decoderVTM.cpp index 08f39a7de..c842ddaed 100644 --- a/YUViewLib/src/decoder/decoderVTM.cpp +++ b/YUViewLib/src/decoder/decoderVTM.cpp @@ -111,7 +111,7 @@ decoderVTM::decoderVTM(int, bool cachingDecoder) : decoderBaseSingleLib(cachingD // Try to load the decoder library (.dll on Windows, .so on Linux, .dylib on Mac) QSettings settings; settings.beginGroup("Decoders"); - loadDecoderLibrary(settings.value("libVTMFile", "").toString()); + loadDecoderLibrary(settings.value("libVTMFile", "").toString().toStdString()); settings.endGroup(); if (decoderState != DecoderState::Error) @@ -124,15 +124,14 @@ decoderVTM::~decoderVTM() this->lib.libVTMDec_free_decoder(decoder); } -QStringList decoderVTM::getLibraryNames() const +StringVec decoderVTM::getLibraryNames() const { // If the file name is not set explicitly, QLibrary will try to open the .so file first. // Since this has been compiled for linux it will fail and not even try to open the .dylib. // On windows and linux ommitting the extension works - QStringList names = - is_Q_OS_MAC ? QStringList() << "libVTMDecoder.dylib" : QStringList() << "libVTMDecoder"; - - return names; + if (is_Q_OS_MAC) + return {"libVTMDecoder.dylib"}; + return {"libVTMDecoder"}; } void decoderVTM::resolveLibraryFunctionPointers() @@ -190,12 +189,12 @@ void decoderVTM::resolveLibraryFunctionPointers() template T decoderVTM::resolve(T &fun, const char *symbol, bool optional) { - QFunctionPointer ptr = this->library.resolve(symbol); + auto ptr = this->library.resolve(symbol); if (!ptr) { if (!optional) - setError(QStringLiteral("Error loading the VTM decoder library: Can't find function %1.") - .arg(symbol)); + this->setError("Error loading the VTM decoder library: Can't find function " + + std::string(symbol)); return nullptr; } @@ -322,8 +321,8 @@ bool decoderVTM::pushData(QByteArray &data) auto err = this->lib.libVTMDec_push_nal_unit( decoder, data, data.length(), endOfFile, bNewPicture, checkOutputPictures); if (err != LIBVTMDEC_OK) - return setErrorB(QString("Error pushing data to decoder (libVTMDec_push_nal_unit) length %1") - .arg(data.length())); + return this->setErrorB("Error pushing data to decoder (libVTMDec_push_nal_unit) length " + + std::to_string(data.length())); DEBUG_DECVTM("decoderVTM::pushData pushed NAL length %d%s%s", data.length(), bNewPicture ? " bNewPicture" : "", @@ -608,17 +607,17 @@ void decoderVTM::fillStatisticList(stats::StatisticsData &) const //} } -QString decoderVTM::getDecoderName() const +std::string decoderVTM::getDecoderName() const { return (decoderState == DecoderState::Error) ? "VTM" : this->lib.libVTMDec_get_version(); } -bool decoderVTM::checkLibraryFile(QString libFilePath, QString &error) +bool decoderVTM::checkLibraryFile(const std::string &libFilePath, std::string &error) { decoderVTM testDecoder; // Try to load the library file - testDecoder.library.setFileName(libFilePath); + testDecoder.library.setFileName(QString::fromStdString(libFilePath)); if (!testDecoder.library.load()) { error = "Error opening QLibrary."; diff --git a/YUViewLib/src/decoder/decoderVTM.h b/YUViewLib/src/decoder/decoderVTM.h index 30028ddd2..5edb3358f 100644 --- a/YUViewLib/src/decoder/decoderVTM.h +++ b/YUViewLib/src/decoder/decoderVTM.h @@ -84,10 +84,10 @@ class decoderVTM : public decoderBaseSingleLib bool pushData(QByteArray &data) override; // Check if the given library file is an existing libde265 decoder that we can use. - static bool checkLibraryFile(QString libFilePath, QString &error); + static bool checkLibraryFile(const std::string &libFilePath, std::string &error); - QString getDecoderName() const override; - QString getCodecName() const override { return "hevc"; } + std::string getDecoderName() const override; + std::string getCodecName() const override { return "hevc"; } int nrSignalsSupported() const override { return nrSignals; } @@ -97,7 +97,7 @@ class decoderVTM : public decoderBaseSingleLib decoderVTM(){}; // Return the possible names of the HM library - QStringList getLibraryNames() const override; + StringVec getLibraryNames() const override; // Try to resolve all the required function pointers from the library void resolveLibraryFunctionPointers() override; diff --git a/YUViewLib/src/decoder/decoderVVDec.cpp b/YUViewLib/src/decoder/decoderVVDec.cpp index 78b0fd8dd..60e23e93a 100644 --- a/YUViewLib/src/decoder/decoderVVDec.cpp +++ b/YUViewLib/src/decoder/decoderVVDec.cpp @@ -142,7 +142,7 @@ decoderVVDec::decoderVVDec(int signalID, bool cachingDecoder) : decoderBaseSingl // Try to load the decoder library (.dll on Windows, .so on Linux, .dylib on Mac) QSettings settings; settings.beginGroup("Decoders"); - this->loadDecoderLibrary(settings.value("libVVDecFile", "").toString()); + this->loadDecoderLibrary(settings.value("libVVDecFile", "").toString().toStdString()); settings.endGroup(); if (this->decoderState != DecoderState::Error) @@ -165,20 +165,16 @@ decoderVVDec::~decoderVVDec() } } -QStringList decoderVVDec::getLibraryNames() const +StringVec decoderVVDec::getLibraryNames() const { // If the file name is not set explicitly, QLibrary will try to open the .so file first. // Since this has been compiled for linux it will fail and not even try to open the .dylib. // On windows and linux ommitting the extension works - QStringList names; if (is_Q_OS_LINUX) - names << "libvvdecLib"; + return {"libvvdecLib"}; if (is_Q_OS_MAC) - names << "libvvdecLib.dylib"; - if (is_Q_OS_WIN) - names << "vvdecLib"; - - return names; + return {"libvvdecLib.dylib"}; + return {"vvdecLib"}; } void decoderVVDec::resolveLibraryFunctionPointers() @@ -238,8 +234,8 @@ template T decoderVVDec::resolve(T &fun, const char *symbol, bool o if (!ptr) { if (!optional) - this->setError(QStringLiteral("Error loading the libvvDeC library: Can't find function %1.") - .arg(symbol)); + this->setError("Error loading the libvvDeC library: Can't find function " + + std::string(symbol)); return nullptr; } @@ -442,12 +438,10 @@ bool decoderVVDec::pushData(QByteArray &data) endOfFile = true; else if (ret != VVDEC_TRY_AGAIN && ret != VVDEC_OK) { - auto cErr = this->lib.vvdec_get_last_error(this->decoder); - auto cErrAdd = this->lib.vvdec_get_last_additional_error(this->decoder); - return setErrorB(QString("Error pushing data to decoder length %1 - %2 - %3") - .arg(data.length()) - .arg(cErr) - .arg(cErrAdd)); + auto cErr = std::string(this->lib.vvdec_get_last_error(this->decoder)); + auto cErrAdd = std::string(this->lib.vvdec_get_last_additional_error(this->decoder)); + return this->setErrorB("Error pushing data to decoder length " + + std::to_string(data.length()) + " error " + cErr + " " + cErrAdd); } DEBUG_vvdec("decoderVVDec::pushData pushed NAL length %d%s", @@ -533,17 +527,17 @@ void decoderVVDec::copyImgToByteArray(QByteArray &dst) } } -QString decoderVVDec::getDecoderName() const +std::string decoderVVDec::getDecoderName() const { return (decoderState == DecoderState::Error) ? "vvdec" : this->lib.vvdec_get_version(); } -bool decoderVVDec::checkLibraryFile(QString libFilePath, QString &error) +bool decoderVVDec::checkLibraryFile(const std::string &libFilePath, std::string &error) { decoderVVDec testDecoder; // Try to load the library file - testDecoder.library.setFileName(libFilePath); + testDecoder.library.setFileName(QString::fromStdString(libFilePath)); if (!testDecoder.library.load()) { error = "Error opening QLibrary."; diff --git a/YUViewLib/src/decoder/decoderVVDec.h b/YUViewLib/src/decoder/decoderVVDec.h index 2da13838e..9081e77f6 100644 --- a/YUViewLib/src/decoder/decoderVVDec.h +++ b/YUViewLib/src/decoder/decoderVVDec.h @@ -87,10 +87,10 @@ class decoderVVDec : public decoderBaseSingleLib bool pushData(QByteArray &data) override; // Check if the given library file is an existing libde265 decoder that we can use. - static bool checkLibraryFile(QString libFilePath, QString &error); + static bool checkLibraryFile(const std::string &libFilePath, std::string &error); - QString getDecoderName() const override; - QString getCodecName() const override { return "hevc"; } + std::string getDecoderName() const override; + std::string getCodecName() const override { return "hevc"; } int nrSignalsSupported() const override { return nrSignals; } @@ -100,7 +100,7 @@ class decoderVVDec : public decoderBaseSingleLib decoderVVDec(){}; // Return the possible names of the HM library - QStringList getLibraryNames() const override; + StringVec getLibraryNames() const override; // Try to resolve all the required function pointers from the library void resolveLibraryFunctionPointers() override; @@ -112,9 +112,9 @@ class decoderVVDec : public decoderBaseSingleLib void allocateNewDecoder(); - vvdecDecoder *decoder{nullptr}; - vvdecAccessUnit* accessUnit{nullptr}; - vvdecFrame *currentFrame{nullptr}; + vvdecDecoder * decoder{nullptr}; + vvdecAccessUnit *accessUnit{nullptr}; + vvdecFrame * currentFrame{nullptr}; // Try to get the next picture from the decoder and save it in currentHMPic bool getNextFrameFromDecoder(); @@ -132,4 +132,4 @@ class decoderVVDec : public decoderBaseSingleLib LibraryFunctionsVVDec lib{}; }; -} +} // namespace decoder diff --git a/YUViewLib/src/ffmpeg/AVCodecContextWrapper.cpp b/YUViewLib/src/ffmpeg/AVCodecContextWrapper.cpp index d5922b40c..45341e62d 100644 --- a/YUViewLib/src/ffmpeg/AVCodecContextWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVCodecContextWrapper.cpp @@ -61,7 +61,7 @@ typedef struct AVCodecContext_56 int flags2; uint8_t * extradata; int extradata_size; - AVRational time_base; + Rational time_base; int ticks_per_frame; int delay; int width, height; @@ -93,7 +93,7 @@ typedef struct AVCodecContext_56 int slice_count; int prediction_method; int * slice_offset; - AVRational sample_aspect_ratio; + Rational sample_aspect_ratio; int me_cmp; int me_sub_cmp; int mb_cmp; @@ -162,7 +162,7 @@ typedef struct AVCodecContext_57 int flags2; uint8_t * extradata; int extradata_size; - AVRational time_base; + Rational time_base; int ticks_per_frame; int delay; int width, height; @@ -194,7 +194,7 @@ typedef struct AVCodecContext_57 int slice_count; int prediction_method; int * slice_offset; - AVRational sample_aspect_ratio; + Rational sample_aspect_ratio; int me_cmp; int me_sub_cmp; int mb_cmp; @@ -261,7 +261,7 @@ typedef struct AVCodecContext_58 int flags2; uint8_t * extradata; int extradata_size; - AVRational time_base; + Rational time_base; int ticks_per_frame; int delay; int width, height; @@ -291,7 +291,7 @@ typedef struct AVCodecContext_58 int slice_count; int prediction_method; int * slice_offset; - AVRational sample_aspect_ratio; + Rational sample_aspect_ratio; int me_cmp; int me_sub_cmp; int mb_cmp; @@ -351,7 +351,7 @@ typedef struct AVCodecContext_59 int flags2; uint8_t * extradata; int extradata_size; - AVRational time_base; + Rational time_base; int ticks_per_frame; int delay; int width, height; @@ -378,7 +378,7 @@ typedef struct AVCodecContext_59 float dark_masking; int slice_count; int * slice_offset; - AVRational sample_aspect_ratio; + Rational sample_aspect_ratio; int me_cmp; int me_sub_cmp; int mb_cmp; @@ -461,7 +461,7 @@ AVColorSpace AVCodecContextWrapper::getColorspace() return this->colorspace; } -AVRational AVCodecContextWrapper::getTimeBase() +Rational AVCodecContextWrapper::getTimeBase() { this->update(); return this->time_base; diff --git a/YUViewLib/src/ffmpeg/AVCodecContextWrapper.h b/YUViewLib/src/ffmpeg/AVCodecContextWrapper.h index 760f6a171..33c0eedf5 100644 --- a/YUViewLib/src/ffmpeg/AVCodecContextWrapper.h +++ b/YUViewLib/src/ffmpeg/AVCodecContextWrapper.h @@ -52,7 +52,7 @@ class AVCodecContextWrapper AVPixelFormat getPixelFormat(); Size getSize(); AVColorSpace getColorspace(); - AVRational getTimeBase(); + Rational getTimeBase(); QByteArray getExtradata(); private: @@ -71,7 +71,7 @@ class AVCodecContextWrapper int flags{}; int flags2{}; QByteArray extradata{}; - AVRational time_base{}; + Rational time_base{}; int ticks_per_frame{}; int delay{}; int width, height{}; @@ -96,24 +96,24 @@ class AVCodecContextWrapper int slice_count{}; int prediction_method{}; // int *slice_offset; - AVRational sample_aspect_ratio{}; - int me_cmp{}; - int me_sub_cmp{}; - int mb_cmp{}; - int ildct_cmp{}; - int dia_size{}; - int last_predictor_count{}; - int pre_me{}; - int me_pre_cmp{}; - int pre_dia_size{}; - int me_subpel_quality{}; - int dtg_active_format{}; - int me_range{}; - int intra_quant_bias{}; - int inter_quant_bias{}; - int slice_flags{}; - int xvmc_acceleration{}; - int mb_decision{}; + Rational sample_aspect_ratio{}; + int me_cmp{}; + int me_sub_cmp{}; + int mb_cmp{}; + int ildct_cmp{}; + int dia_size{}; + int last_predictor_count{}; + int pre_me{}; + int me_pre_cmp{}; + int pre_dia_size{}; + int me_subpel_quality{}; + int dtg_active_format{}; + int me_range{}; + int intra_quant_bias{}; + int inter_quant_bias{}; + int slice_flags{}; + int xvmc_acceleration{}; + int mb_decision{}; // uint16_t *intra_matrix; // uint16_t *inter_matrix; int scenechange_threshold{}; diff --git a/YUViewLib/src/ffmpeg/AVCodecIDWrapper.h b/YUViewLib/src/ffmpeg/AVCodecIDWrapper.h index 4502c73e8..66c7a1341 100644 --- a/YUViewLib/src/ffmpeg/AVCodecIDWrapper.h +++ b/YUViewLib/src/ffmpeg/AVCodecIDWrapper.h @@ -41,10 +41,13 @@ class AVCodecIDWrapper { public: AVCodecIDWrapper() {} - AVCodecIDWrapper(AVCodecID codecID, QString codecName) : codecID(codecID), codecName(codecName) {} + AVCodecIDWrapper(AVCodecID codecID, std::string codecName) + : codecID(codecID), codecName(codecName) + { + } - QString getCodecName() const { return this->codecName; } - AVCodecID getCodecID() const { return this->codecID; } + std::string getCodecName() const { return this->codecName; } + AVCodecID getCodecID() const { return this->codecID; } void setCodecID(AVCodecID id) { this->codecID = id; } @@ -58,15 +61,15 @@ class AVCodecIDWrapper bool isNone() const { - return this->codecName.isEmpty() || this->codecName == "unknown_codec" || + return this->codecName.empty() || this->codecName == "unknown_codec" || this->codecName == "none"; } bool operator==(const AVCodecIDWrapper &a) const { return codecID == a.codecID; } private: - AVCodecID codecID{AV_CODEC_ID_NONE}; - QString codecName; + AVCodecID codecID{AV_CODEC_ID_NONE}; + std::string codecName; }; } // namespace FFmpeg diff --git a/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.cpp b/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.cpp index b1f8ef794..07ce27e40 100644 --- a/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.cpp @@ -55,7 +55,7 @@ typedef struct AVCodecParameters_57_58_59 int level; int width; int height; - AVRational sample_aspect_ratio; + Rational sample_aspect_ratio; AVFieldOrder field_order; AVColorRange color_range; AVColorPrimaries color_primaries; @@ -118,111 +118,117 @@ Ratio AVCodecParametersWrapper::getSampleAspectRatio() return {this->sample_aspect_ratio.num, this->sample_aspect_ratio.den}; } -QStringPairList AVCodecParametersWrapper::getInfoText() +StringPairVec AVCodecParametersWrapper::getInfoText() const { - QStringPairList info; - if (this->param == nullptr) - { - info.append(QStringPair("Codec parameters are nullptr", "")); - return info; - } - this->update(); + return {{"Codec parameters are nullptr", ""}}; + + AVCodecParametersWrapper wrapper(this->param, this->libVer); - info.append({"Codec Tag", QString::number(this->codec_tag)}); - info.append({"Format", QString::number(this->format)}); - info.append({"Bitrate", QString::number(this->bit_rate)}); - info.append({"Bits per coded sample", QString::number(this->bits_per_coded_sample)}); - info.append({"Bits per Raw sample", QString::number(this->bits_per_raw_sample)}); - info.append({"Profile", QString::number(this->profile)}); - info.append({"Level", QString::number(this->level)}); - info.append({"Width/Height", QString("%1/%2").arg(this->width).arg(this->height)}); - info.append( - {"Sample aspect ratio", - QString("%1:%2").arg(this->sample_aspect_ratio.num).arg(this->sample_aspect_ratio.den)}); - auto fieldOrders = QStringList() << "Unknown" - << "Progressive" - << "Top coded_first, top displayed first" - << "Bottom coded first, bottom displayed first" - << "Top coded first, bottom displayed first" - << "Bottom coded first, top displayed first"; - info.append( - {"Field Order", - fieldOrders.at(functions::clip(int(this->codec_type), 0, int(fieldOrders.count())))}); - auto colorRanges = QStringList() << "Unspecified" - << "The normal 219*2^(n-8) MPEG YUV ranges" - << "The normal 2^n-1 JPEG YUV ranges" - << "Not part of ABI"; - info.append( - {"Color Range", - colorRanges.at(functions::clip(int(this->color_range), 0, int(colorRanges.count())))}); - auto colorPrimaries = - QStringList() - << "Reserved" - << "BT709 / ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B" - << "Unspecified" - << "Reserved" - << "BT470M / FCC Title 47 Code of Federal Regulations 73.682 (a)(20)" - << "BT470BG / ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM" - << "SMPTE170M / also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC" - << "SMPTE240M" - << "FILM - colour filters using Illuminant C" - << "ITU-R BT2020" - << "SMPTE ST 428-1 (CIE 1931 XYZ)" - << "SMPTE ST 431-2 (2011)" - << "SMPTE ST 432-1 D65 (2010)" - << "Not part of ABI"; - info.append(QStringPair("Color Primaries", colorPrimaries.at((int)this->color_primaries))); - auto colorTransfers = - QStringList() - << "Reserved" - << "BT709 / ITU-R BT1361" - << "Unspecified" - << "Reserved" - << "Gamma22 / ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM" - << "Gamma28 / ITU-R BT470BG" - << "SMPTE170M / ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC" - << "SMPTE240M" - << "Linear transfer characteristics" - << "Logarithmic transfer characteristic (100:1 range)" - << "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)" - << "IEC 61966-2-4" - << "ITU-R BT1361 Extended Colour Gamut" - << "IEC 61966-2-1 (sRGB or sYCC)" - << "ITU-R BT2020 for 10-bit system" - << "ITU-R BT2020 for 12-bit system" - << "SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems" - << "SMPTE ST 428-1" - << "ARIB STD-B67, known as Hybrid log-gamma" - << "Not part of ABI"; - info.append({"Color Transfer", colorTransfers.at((int)this->color_trc)}); - auto colorSpaces = QStringList() - << "RGB - order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB)" - << "BT709 / ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B" - << "Unspecified" - << "Reserved" - << "FCC Title 47 Code of Federal Regulations 73.682 (a)(20)" - << "BT470BG / ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & " - "SECAM / IEC 61966-2-4 xvYCC601" - << "SMPTE170M / ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC" - << "SMPTE240M" - << "YCOCG - Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16" - << "ITU-R BT2020 non-constant luminance system" - << "ITU-R BT2020 constant luminance system" - << "SMPTE 2085, Y'D'zD'x" - << "Not part of ABI"; - info.append({"Color Space", colorSpaces.at((int)this->color_space)}); - auto chromaLocations = QStringList() - << "Unspecified" - << "Left / MPEG-2/4 4:2:0, H.264 default for 4:2:0" - << "Center / MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0" - << "Top Left / ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2" - << "Top" - << "Bottom Left" - << "Bottom" - << "Not part of ABI"; - info.append({"Chroma Location", chromaLocations.at((int)this->chroma_location)}); - info.append({"Video Delay", QString::number(this->video_delay)}); + StringPairVec info; + info.push_back({"Codec Tag", std::to_string(wrapper.codec_tag)}); + info.push_back({"Format", std::to_string(wrapper.format)}); + info.push_back({"Bitrate", std::to_string(wrapper.bit_rate)}); + info.push_back({"Bits per coded sample", std::to_string(wrapper.bits_per_coded_sample)}); + info.push_back({"Bits per Raw sample", std::to_string(wrapper.bits_per_raw_sample)}); + info.push_back({"Profile", std::to_string(wrapper.profile)}); + info.push_back({"Level", std::to_string(wrapper.level)}); + info.push_back( + {"Width/Height", std::to_string(this->width) + "/" + std::to_string(this->height)}); + info.push_back({"Sample aspect ratio", + std::to_string(wrapper.sample_aspect_ratio.num) + ":" + + std::to_string(wrapper.sample_aspect_ratio.den)}); + std::array fieldOrders = {"Unknown", + "Progressive", + "Top coded_first, top displayed first", + "Bottom coded first, bottom displayed first", + "Top coded first, bottom displayed first", + "Bottom coded first, top displayed first"}; + info.push_back({"Field Order", + fieldOrders.at(functions::clip( + static_cast(this->codec_type), size_t(0), fieldOrders.size()))}); + std::array colorRanges = {"Unspecified", + "The normal 219*2^(n-8) MPEG YUV ranges", + "The normal 2^n-1 JPEG YUV ranges", + "Not part of ABI"}; + info.push_back({"Color Range", + colorRanges.at(functions::clip( + static_cast(this->color_range), size_t(0), colorRanges.size()))}); + std::array colorPrimaries = { + "Reserved", + "BT709 / ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP177 Annex B", + "Unspecified", + "Reserved", + "BT470M / FCC Title 47 Code of Federal Regulations 73.682 (a)(20)", + "BT470BG / ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM", + "SMPTE170M / also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC", + "SMPTE240M", + "FILM - colour filters using Illuminant C", + "ITU-R BT2020", + "SMPTE ST 428-1 (CIE 1931 XYZ)", + "SMPTE ST 431-2 (2011)", + "SMPTE ST 432-1 D65 (2010)", + "Not part of ABI"}; + info.push_back( + {"Color Primaries", + colorPrimaries.at(functions::clip( + static_cast(wrapper.color_primaries), size_t(0), colorPrimaries.size()))}); + std::array colorTransfers = { + "Reserved", + "BT709 / ITU-R BT1361", + "Unspecified", + "Reserved", + "Gamma22 / ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM", + "Gamma28 / ITU-R BT470BG", + "SMPTE170M / ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC", + "SMPTE240M", + "Linear transfer characteristics", + "Logarithmic transfer characteristic (100:1 range)", + "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)", + "IEC 61966-2-4", + "ITU-R BT1361 Extended Colour Gamut", + "IEC 61966-2-1 (sRGB or sYCC)", + "ITU-R BT2020 for 10-bit system", + "ITU-R BT2020 for 12-bit system", + "SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems", + "SMPTE ST 428-1", + "ARIB STD-B67, known as Hybrid log-gamma", + "Not part of ABI"}; + info.push_back({"Color Transfer", + colorTransfers.at(functions::clip( + static_cast(wrapper.color_trc), size_t(0), colorTransfers.size()))}); + std::array colorSpaces = { + "RGB - order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB)", + "BT709 / ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / SMPTE RP177 Annex B", + "Unspecified", + "Reserved", + "FCC Title 47 Code of Federal Regulations 73.682 (a)(20)", + "BT470BG / ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & " + "SECAM / IEC 61966-2-4 xvYCC601", + "SMPTE170M / ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC", + "SMPTE240M", + "YCOCG - Used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16", + "ITU-R BT2020 non-constant luminance system", + "ITU-R BT2020 constant luminance system", + "SMPTE 2085, Y'D'zD'x", + "Not part of ABI"}; + info.push_back({"Color Space", + colorSpaces.at(functions::clip( + static_cast(wrapper.color_space), size_t(0), colorSpaces.size()))}); + std::array chromaLocations = { + "Unspecified", + "Left / MPEG-2/4 4:2:0, H.264 default for 4:2:0", + "Center / MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0", + "Top Left / ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2", + "Top", + "Bottom Left", + "Bottom", + "Not part of ABI"}; + info.push_back( + {"Chroma Location", + chromaLocations.at(functions::clip( + static_cast(wrapper.chroma_location), size_t(0), chromaLocations.size()))}); + info.push_back({"Video Delay", std::to_string(wrapper.video_delay)}); return info; } @@ -247,7 +253,7 @@ void AVCodecParametersWrapper::setClearValues() p->width = 0; p->height = 0; { - AVRational ratio; + Rational ratio; ratio.num = 1; ratio.den = 1; p->sample_aspect_ratio = ratio; @@ -339,8 +345,8 @@ void AVCodecParametersWrapper::setSampleAspectRatio(int num, int den) if (this->libVer.avformat.major == 57 || this->libVer.avformat.major == 58 || this->libVer.avformat.major == 59) { - auto p = reinterpret_cast(param); - AVRational ratio; + auto p = reinterpret_cast(param); + Rational ratio; ratio.num = num; ratio.den = den; p->sample_aspect_ratio = ratio; diff --git a/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.h b/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.h index 3542e11bb..a6e8ef33a 100644 --- a/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.h +++ b/YUViewLib/src/ffmpeg/AVCodecParametersWrapper.h @@ -42,8 +42,8 @@ class AVCodecParametersWrapper public: AVCodecParametersWrapper() = default; AVCodecParametersWrapper(AVCodecParameters *p, LibraryVersion v); - explicit operator bool() const { return this->param != nullptr; } - QStringPairList getInfoText(); + explicit operator bool() const { return this->param != nullptr; } + StringPairVec getInfoText() const; AVMediaType getCodecType(); AVCodecID getCodecID(); @@ -83,7 +83,7 @@ class AVCodecParametersWrapper int level{}; int width{}; int height{}; - AVRational sample_aspect_ratio{}; + Rational sample_aspect_ratio{}; AVFieldOrder field_order{}; AVColorRange color_range{}; AVColorPrimaries color_primaries{}; diff --git a/YUViewLib/src/ffmpeg/AVCodecWrapper.cpp b/YUViewLib/src/ffmpeg/AVCodecWrapper.cpp index 2d826e43c..271a8b1fe 100644 --- a/YUViewLib/src/ffmpeg/AVCodecWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVCodecWrapper.cpp @@ -88,6 +88,19 @@ template std::vector convertRawListToVec(const T *rawValues, T t return values; } +std::vector convertRawAVRationalList(const AVRational *rawValues) +{ + std::vector values; + int i = 0; + auto val = rawValues[i++]; + while (val.num == 0 && val.den == 0) + { + values.push_back(Rational({val.num, val.den})); + val = rawValues[i++]; + } + return values; +} + } // namespace void AVCodecWrapper::update() @@ -103,7 +116,7 @@ void AVCodecWrapper::update() this->type = p->type; this->id = p->id; this->capabilities = p->capabilities; - this->supported_framerates = convertRawListToVec(p->supported_framerates, AVRational({0, 0})); + this->supported_framerates = convertRawAVRationalList(p->supported_framerates); this->pix_fmts = convertRawListToVec(p->pix_fmts, AVPixelFormat(-1)); this->supported_samplerates = convertRawListToVec(p->supported_samplerates, 0); this->sample_fmts = convertRawListToVec(p->sample_fmts, AVSampleFormat(-1)); @@ -118,7 +131,7 @@ void AVCodecWrapper::update() this->type = p->type; this->id = p->id; this->capabilities = p->capabilities; - this->supported_framerates = convertRawListToVec(p->supported_framerates, AVRational({0, 0})); + this->supported_framerates = convertRawAVRationalList(p->supported_framerates); this->pix_fmts = convertRawListToVec(p->pix_fmts, AVPixelFormat(-1)); this->supported_samplerates = convertRawListToVec(p->supported_samplerates, 0); this->sample_fmts = convertRawListToVec(p->sample_fmts, AVSampleFormat(-1)); diff --git a/YUViewLib/src/ffmpeg/AVCodecWrapper.h b/YUViewLib/src/ffmpeg/AVCodecWrapper.h index 1d8874e76..45cfb513e 100644 --- a/YUViewLib/src/ffmpeg/AVCodecWrapper.h +++ b/YUViewLib/src/ffmpeg/AVCodecWrapper.h @@ -69,7 +69,7 @@ class AVCodecWrapper AVMediaType type; AVCodecID id{AV_CODEC_ID_NONE}; int capabilities{0}; - std::vector supported_framerates; + std::vector supported_framerates; std::vector pix_fmts; std::vector supported_samplerates; std::vector sample_fmts; diff --git a/YUViewLib/src/ffmpeg/AVFormatContextWrapper.cpp b/YUViewLib/src/ffmpeg/AVFormatContextWrapper.cpp index c7c3410b6..03323f473 100644 --- a/YUViewLib/src/ffmpeg/AVFormatContextWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVFormatContextWrapper.cpp @@ -31,7 +31,9 @@ */ #include "AVFormatContextWrapper.h" + #include "AVStreamWrapper.h" +#include namespace FFmpeg { @@ -201,34 +203,102 @@ AVFormatContextWrapper::operator bool() const return this->ctx; }; -unsigned AVFormatContextWrapper::getNbStreams() +unsigned AVFormatContextWrapper::getNbStreams() const { - this->update(); - return this->nb_streams; + + switch (this->libVer.avformat.major) + { + case 56: + return reinterpret_cast(this->ctx)->nb_streams; + case 57: + return reinterpret_cast(this->ctx)->nb_streams; + case 58: + return reinterpret_cast(this->ctx)->nb_streams; + case 59: + return reinterpret_cast(this->ctx)->nb_streams; + + default: + throw std::runtime_error("Invalid library version"); + } } -AVStreamWrapper AVFormatContextWrapper::getStream(int idx) +AVStreamWrapper AVFormatContextWrapper::getStream(int idx) const { - this->update(); - return this->streams[idx]; + if (idx < 0 || static_cast(idx) >= this->getNbStreams()) + throw std::runtime_error("Invalid stream index"); + + switch (this->libVer.avformat.major) + { + case 56: + return AVStreamWrapper(reinterpret_cast(this->ctx)->streams[idx], + this->libVer); + case 57: + return AVStreamWrapper(reinterpret_cast(this->ctx)->streams[idx], + this->libVer); + case 58: + return AVStreamWrapper(reinterpret_cast(this->ctx)->streams[idx], + this->libVer); + case 59: + return AVStreamWrapper(reinterpret_cast(this->ctx)->streams[idx], + this->libVer); + + default: + throw std::runtime_error("Invalid library version"); + } } -AVInputFormatWrapper AVFormatContextWrapper::getInputFormat() +AVInputFormatWrapper AVFormatContextWrapper::getInputFormat() const { - this->update(); - return this->iformat; + switch (this->libVer.avformat.major) + { + case 56: + return AVInputFormatWrapper(reinterpret_cast(this->ctx)->iformat, libVer); + case 57: + return AVInputFormatWrapper(reinterpret_cast(this->ctx)->iformat, libVer); + case 58: + return AVInputFormatWrapper(reinterpret_cast(this->ctx)->iformat, libVer); + case 59: + return AVInputFormatWrapper(reinterpret_cast(this->ctx)->iformat, libVer); + + default: + throw std::runtime_error("Invalid library version"); + } } -int64_t AVFormatContextWrapper::getStartTime() +int64_t AVFormatContextWrapper::getStartTime() const { - this->update(); - return this->start_time; + switch (this->libVer.avformat.major) + { + case 56: + return reinterpret_cast(this->ctx)->start_time; + case 57: + return reinterpret_cast(this->ctx)->start_time; + case 58: + return reinterpret_cast(this->ctx)->start_time; + case 59: + return reinterpret_cast(this->ctx)->start_time; + + default: + throw std::runtime_error("Invalid library version"); + } } -int64_t AVFormatContextWrapper::getDuration() +int64_t AVFormatContextWrapper::getDuration() const { - this->update(); - return this->duration; + switch (this->libVer.avformat.major) + { + case 56: + return reinterpret_cast(this->ctx)->duration; + case 57: + return reinterpret_cast(this->ctx)->duration; + case 58: + return reinterpret_cast(this->ctx)->duration; + case 59: + return reinterpret_cast(this->ctx)->duration; + + default: + throw std::runtime_error("Invalid library version"); + } } AVFormatContext *AVFormatContextWrapper::getFormatCtx() const @@ -236,10 +306,22 @@ AVFormatContext *AVFormatContextWrapper::getFormatCtx() const return this->ctx; } -AVDictionaryWrapper AVFormatContextWrapper::getMetadata() +AVDictionaryWrapper AVFormatContextWrapper::getMetadata() const { - this->update(); - return this->metadata; + switch (this->libVer.avformat.major) + { + case 56: + return AVDictionaryWrapper(reinterpret_cast(this->ctx)->metadata); + case 57: + return AVDictionaryWrapper(reinterpret_cast(this->ctx)->metadata); + case 58: + return AVDictionaryWrapper(reinterpret_cast(this->ctx)->metadata); + case 59: + return AVDictionaryWrapper(reinterpret_cast(this->ctx)->metadata); + + default: + throw std::runtime_error("Invalid library version"); + } } void AVFormatContextWrapper::update() @@ -256,8 +338,8 @@ void AVFormatContextWrapper::update() this->ctx_flags = p->ctx_flags; this->nb_streams = p->nb_streams; for (unsigned i = 0; i < this->nb_streams; i++) - this->streams.append(AVStreamWrapper(p->streams[i], this->libVer)); - this->filename = QString(p->filename); + this->streams.push_back(AVStreamWrapper(p->streams[i], this->libVer)); + this->filename = std::string(p->filename); this->start_time = p->start_time; this->duration = p->duration; this->bit_rate = p->bit_rate; @@ -266,7 +348,7 @@ void AVFormatContextWrapper::update() this->flags = p->flags; this->probesize = p->probesize; this->max_analyze_duration = p->max_analyze_duration; - this->key = QString::fromLatin1((const char *)p->key, p->keylen); + this->key = std::string(reinterpret_cast(p->key), p->keylen); this->nb_programs = p->nb_programs; this->video_codec_id = p->video_codec_id; this->audio_codec_id = p->audio_codec_id; @@ -284,8 +366,8 @@ void AVFormatContextWrapper::update() this->ctx_flags = p->ctx_flags; this->nb_streams = p->nb_streams; for (unsigned i = 0; i < nb_streams; i++) - this->streams.append(AVStreamWrapper(p->streams[i], this->libVer)); - this->filename = QString(p->filename); + this->streams.push_back(AVStreamWrapper(p->streams[i], this->libVer)); + this->filename = std::string(p->filename); this->start_time = p->start_time; this->duration = p->duration; this->bit_rate = p->bit_rate; @@ -294,7 +376,7 @@ void AVFormatContextWrapper::update() this->flags = p->flags; this->probesize = p->probesize; this->max_analyze_duration = p->max_analyze_duration; - this->key = QString::fromLatin1((const char *)p->key, p->keylen); + this->key = std::string(reinterpret_cast(p->key), p->keylen); this->nb_programs = p->nb_programs; this->video_codec_id = p->video_codec_id; this->audio_codec_id = p->audio_codec_id; @@ -312,8 +394,8 @@ void AVFormatContextWrapper::update() this->ctx_flags = p->ctx_flags; this->nb_streams = p->nb_streams; for (unsigned i = 0; i < nb_streams; i++) - this->streams.append(AVStreamWrapper(p->streams[i], this->libVer)); - this->filename = QString(p->filename); + this->streams.push_back(AVStreamWrapper(p->streams[i], this->libVer)); + this->filename = std::string(p->filename); this->start_time = p->start_time; this->duration = p->duration; this->bit_rate = p->bit_rate; @@ -322,7 +404,7 @@ void AVFormatContextWrapper::update() this->flags = p->flags; this->probesize = p->probesize; this->max_analyze_duration = p->max_analyze_duration; - this->key = QString::fromLatin1((const char *)p->key, p->keylen); + this->key = std::string(reinterpret_cast(p->key), p->keylen); this->nb_programs = p->nb_programs; this->video_codec_id = p->video_codec_id; this->audio_codec_id = p->audio_codec_id; @@ -340,8 +422,8 @@ void AVFormatContextWrapper::update() this->ctx_flags = p->ctx_flags; this->nb_streams = p->nb_streams; for (unsigned i = 0; i < nb_streams; i++) - this->streams.append(AVStreamWrapper(p->streams[i], this->libVer)); - this->filename = QString(p->url); + this->streams.push_back(AVStreamWrapper(p->streams[i], this->libVer)); + this->filename = std::string(p->url); this->start_time = p->start_time; this->duration = p->duration; this->bit_rate = p->bit_rate; @@ -350,7 +432,7 @@ void AVFormatContextWrapper::update() this->flags = p->flags; this->probesize = p->probesize; this->max_analyze_duration = p->max_analyze_duration; - this->key = QString::fromLatin1((const char *)p->key, p->keylen); + this->key = std::string(reinterpret_cast(p->key), p->keylen); this->nb_programs = p->nb_programs; this->video_codec_id = p->video_codec_id; this->audio_codec_id = p->audio_codec_id; @@ -366,45 +448,40 @@ void AVFormatContextWrapper::update() throw std::runtime_error("Invalid library version"); } -QStringPairList AVFormatContextWrapper::getInfoText() +StringPairVec AVFormatContextWrapper::getInfoText() const { if (this->ctx == nullptr) - return {QStringPair("Format context not initialized", "")}; + return {}; - this->update(); + AVFormatContextWrapper formatContext(this->ctx, this->libVer); + StringPairVec info; - QStringPairList info; - if (this->ctx_flags != 0) + if (formatContext.ctx_flags != 0) { - QString flags; - if (this->ctx_flags & 1) - flags += QString("No-Header"); - if (this->ctx_flags & 2) - flags += QString("Un-seekable"); - info.append(QStringPair("Flags", flags)); + std::vector flags; + if (formatContext.ctx_flags & 1) + flags.push_back("No-Header"); + if (formatContext.ctx_flags & 2) + flags.push_back("Un-seekable"); + info.push_back({"Flags", functions::to_string(flags)}); } - AVRational time_base; + Rational time_base; time_base.num = 1; time_base.den = AV_TIME_BASE; - info.append(QStringPair("Number streams", QString::number(this->nb_streams))); - info.append(QStringPair("File name", this->filename)); - info.append(QStringPair("Start time", - QString("%1 (%2)") - .arg(this->start_time) - .arg(timestampToString(this->start_time, time_base)))); - info.append(QStringPair( - "Duration", - QString("%1 (%2)").arg(this->duration).arg(timestampToString(duration, time_base)))); + info.push_back({"Number streams", std::to_string(formatContext.nb_streams)}); + info.push_back({"File name", this->filename}); + info.push_back({"Start time", formatWithReadableFormat(formatContext.start_time, time_base)}); + info.push_back({"Duration", formatWithReadableFormat(formatContext.duration, time_base)}); if (bit_rate > 0) - info.append(QStringPair("Bitrate", QString::number(this->bit_rate))); - info.append(QStringPair("Packet size", QString::number(this->packet_size))); - info.append(QStringPair("Max delay", QString::number(this->max_delay))); - info.append(QStringPair("Number programs", QString::number(this->nb_programs))); - info.append(QStringPair("Number chapters", QString::number(this->nb_chapters))); + info.push_back({"Bitrate", std::to_string(formatContext.bit_rate)}); + info.push_back({"Packet size", std::to_string(formatContext.packet_size)}); + info.push_back({"Max delay", std::to_string(formatContext.max_delay)}); + info.push_back({"Number programs", std::to_string(formatContext.nb_programs)}); + info.push_back({"Number chapters", std::to_string(formatContext.nb_chapters)}); return info; } -} // namespace FFmpeg \ No newline at end of file +} // namespace FFmpeg diff --git a/YUViewLib/src/ffmpeg/AVFormatContextWrapper.h b/YUViewLib/src/ffmpeg/AVFormatContextWrapper.h index 8059c51f2..359fde6e8 100644 --- a/YUViewLib/src/ffmpeg/AVFormatContextWrapper.h +++ b/YUViewLib/src/ffmpeg/AVFormatContextWrapper.h @@ -48,17 +48,17 @@ class AVFormatContextWrapper AVFormatContextWrapper() = default; AVFormatContextWrapper(AVFormatContext *c, LibraryVersion v); - void updateFrom(AVFormatContext *c); - explicit operator bool() const; - QStringPairList getInfoText(); + void updateFrom(AVFormatContext *c); + explicit operator bool() const; + [[nodiscard]] StringPairVec getInfoText() const; - unsigned int getNbStreams(); - AVStreamWrapper getStream(int idx); - AVInputFormatWrapper getInputFormat(); - int64_t getStartTime(); - int64_t getDuration(); - AVFormatContext * getFormatCtx() const; - AVDictionaryWrapper getMetadata(); + [[nodiscard]] unsigned int getNbStreams() const; + [[nodiscard]] AVStreamWrapper getStream(int idx) const; + [[nodiscard]] AVInputFormatWrapper getInputFormat() const; + [[nodiscard]] int64_t getStartTime() const; + [[nodiscard]] int64_t getDuration() const; + [[nodiscard]] AVFormatContext * getFormatCtx() const; + [[nodiscard]] AVDictionaryWrapper getMetadata() const; private: // Update all private values from the AVFormatContext @@ -67,20 +67,20 @@ class AVFormatContextWrapper AVInputFormatWrapper iformat{}; // These are private. Use "update" to update them from the AVFormatContext - int ctx_flags{0}; - unsigned int nb_streams{0}; - QList streams; - QString filename{}; - int64_t start_time{-1}; - int64_t duration{-1}; - int bit_rate{0}; - unsigned int packet_size{0}; - int max_delay{0}; - int flags{0}; + int ctx_flags{0}; + unsigned int nb_streams{0}; + std::vector streams; + std::string filename{}; + int64_t start_time{-1}; + int64_t duration{-1}; + int bit_rate{0}; + unsigned int packet_size{0}; + int max_delay{0}; + int flags{0}; unsigned int probesize{0}; int max_analyze_duration{0}; - QString key{}; + std::string key{}; unsigned int nb_programs{0}; AVCodecID video_codec_id{AV_CODEC_ID_NONE}; AVCodecID audio_codec_id{AV_CODEC_ID_NONE}; diff --git a/YUViewLib/src/ffmpeg/AVFrameWrapper.cpp b/YUViewLib/src/ffmpeg/AVFrameWrapper.cpp index a2ef9f64d..e27addb8b 100644 --- a/YUViewLib/src/ffmpeg/AVFrameWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVFrameWrapper.cpp @@ -211,19 +211,20 @@ void AVFrameWrapper::update() this->data[i] = p->data[i]; this->linesize[i] = p->linesize[i]; } - this->width = p->width; - this->height = p->height; - this->nb_samples = p->nb_samples; - this->format = p->format; - this->key_frame = p->key_frame; - this->pict_type = p->pict_type; - this->sample_aspect_ratio = p->sample_aspect_ratio; - this->pts = p->pts; - this->pkt_pts = p->pkt_pts; - this->pkt_dts = p->pkt_dts; - this->coded_picture_number = p->coded_picture_number; - this->display_picture_number = p->display_picture_number; - this->quality = p->quality; + this->width = p->width; + this->height = p->height; + this->nb_samples = p->nb_samples; + this->format = p->format; + this->key_frame = p->key_frame; + this->pict_type = p->pict_type; + this->sample_aspect_ratio.num = p->sample_aspect_ratio.num; + this->sample_aspect_ratio.den = p->sample_aspect_ratio.den; + this->pts = p->pts; + this->pkt_pts = p->pkt_pts; + this->pkt_dts = p->pkt_dts; + this->coded_picture_number = p->coded_picture_number; + this->display_picture_number = p->display_picture_number; + this->quality = p->quality; } else if (this->libVer.avutil.major == 55 || // this->libVer.avutil.major == 56) @@ -234,19 +235,20 @@ void AVFrameWrapper::update() this->data[i] = p->data[i]; this->linesize[i] = p->linesize[i]; } - this->width = p->width; - this->height = p->height; - this->nb_samples = p->nb_samples; - this->format = p->format; - this->key_frame = p->key_frame; - this->pict_type = p->pict_type; - this->sample_aspect_ratio = p->sample_aspect_ratio; - this->pts = p->pts; - this->pkt_pts = p->pkt_pts; - this->pkt_dts = p->pkt_dts; - this->coded_picture_number = p->coded_picture_number; - this->display_picture_number = p->display_picture_number; - this->quality = p->quality; + this->width = p->width; + this->height = p->height; + this->nb_samples = p->nb_samples; + this->format = p->format; + this->key_frame = p->key_frame; + this->pict_type = p->pict_type; + this->sample_aspect_ratio.num = p->sample_aspect_ratio.num; + this->sample_aspect_ratio.den = p->sample_aspect_ratio.den; + this->pts = p->pts; + this->pkt_pts = p->pkt_pts; + this->pkt_dts = p->pkt_dts; + this->coded_picture_number = p->coded_picture_number; + this->display_picture_number = p->display_picture_number; + this->quality = p->quality; } else if (this->libVer.avutil.major == 57) { @@ -256,20 +258,21 @@ void AVFrameWrapper::update() this->data[i] = p->data[i]; this->linesize[i] = p->linesize[i]; } - this->width = p->width; - this->height = p->height; - this->nb_samples = p->nb_samples; - this->format = p->format; - this->key_frame = p->key_frame; - this->pict_type = p->pict_type; - this->sample_aspect_ratio = p->sample_aspect_ratio; - this->pts = p->pts; - this->pkt_pts = -1; - this->pkt_dts = p->pkt_dts; - this->coded_picture_number = p->coded_picture_number; - this->display_picture_number = p->display_picture_number; - this->quality = p->quality; - this->metadata = p->metadata; + this->width = p->width; + this->height = p->height; + this->nb_samples = p->nb_samples; + this->format = p->format; + this->key_frame = p->key_frame; + this->pict_type = p->pict_type; + this->sample_aspect_ratio.num = p->sample_aspect_ratio.num; + this->sample_aspect_ratio.den = p->sample_aspect_ratio.den; + this->pts = p->pts; + this->pkt_pts = -1; + this->pkt_dts = p->pkt_dts; + this->coded_picture_number = p->coded_picture_number; + this->display_picture_number = p->display_picture_number; + this->quality = p->quality; + this->metadata = p->metadata; } else throw std::runtime_error("Invalid library version"); diff --git a/YUViewLib/src/ffmpeg/AVFrameWrapper.h b/YUViewLib/src/ffmpeg/AVFrameWrapper.h index 72b3998fc..b1606f387 100644 --- a/YUViewLib/src/ffmpeg/AVFrameWrapper.h +++ b/YUViewLib/src/ffmpeg/AVFrameWrapper.h @@ -72,7 +72,7 @@ class AVFrameWrapper int format{}; int key_frame{}; AVPictureType pict_type{}; - AVRational sample_aspect_ratio{}; + Rational sample_aspect_ratio{}; int64_t pts{}; int64_t pkt_pts{}; int64_t pkt_dts{}; diff --git a/YUViewLib/src/ffmpeg/AVPacketWrapper.cpp b/YUViewLib/src/ffmpeg/AVPacketWrapper.cpp index 60df6717e..cd728cf8a 100644 --- a/YUViewLib/src/ffmpeg/AVPacketWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVPacketWrapper.cpp @@ -31,6 +31,7 @@ */ #include "AVPacketWrapper.h" +#include #include #include @@ -89,33 +90,13 @@ bool checkForObuFormat(QByteArray &data) parser::reader::SubByteReaderLogging reader( parser::reader::SubByteReaderLogging::convertToByteVector(data), nullptr, "", posInData); - QString bitsRead; - auto forbiddenBit = reader.readFlag("obu_forbidden_bit"); - if (forbiddenBit) - return false; - auto obu_type = reader.readBits("obu_type", 4); - if (obu_type == 0 || (obu_type >= 9 && obu_type <= 14)) - // RESERVED obu types should not occur (highly unlikely) - return false; - auto obu_extension_flag = reader.readFlag("obu_extension_flag"); - auto obu_has_size_field = reader.readFlag("obu_has_size_field"); - reader.readFlag("obu_reserved_1bit", parser::reader::Options().withCheckEqualTo(1)); - - if (obu_extension_flag) - { - reader.readBits("temporal_id", 3); - reader.readBits("spatial_id", 2); - reader.readBits( - "extension_header_reserved_3bits", 3, parser::reader::Options().withCheckEqualTo(0)); - } - size_t obu_size; - if (obu_has_size_field) - { - obu_size = reader.readLEB128("obu_size"); - } - else + parser::av1::obu_header header; + header.parse(reader); + + auto obu_size = header.obu_size; + if (!header.obu_has_size_field) { - obu_size = (size_t(data.size()) - posInData) - 1 - (obu_extension_flag ? 1 : 0); + obu_size = (uint64_t(data.size()) - posInData) - 1 - (header.obu_extension_flag ? 1 : 0); } posInData += obu_size + reader.nrBytesRead(); } diff --git a/YUViewLib/src/ffmpeg/AVStreamWrapper.cpp b/YUViewLib/src/ffmpeg/AVStreamWrapper.cpp index e4f5a8e4e..fe7879db3 100644 --- a/YUViewLib/src/ffmpeg/AVStreamWrapper.cpp +++ b/YUViewLib/src/ffmpeg/AVStreamWrapper.cpp @@ -33,6 +33,9 @@ #include "AVStreamWrapper.h" #include "AVPacketWrapper.h" +#include +#include + namespace FFmpeg { @@ -205,6 +208,17 @@ typedef struct AVStream_59 int pts_wrap_bits; } AVStream_59; +std::string formatFramerate(Rational frameRate) +{ + std::stringstream stream; + auto divFrameRate = 0.0; + if (frameRate.den > 0) + divFrameRate = static_cast(frameRate.num) / static_cast(frameRate.den); + stream << frameRate.num << "/" << frameRate.den << " (" << std::setprecision(2) << divFrameRate + << ")"; + return stream.str(); +} + } // namespace AVStreamWrapper::AVStreamWrapper(AVStream *src_str, LibraryVersion v) @@ -222,19 +236,13 @@ AVMediaType AVStreamWrapper::getCodecType() return this->codecpar.getCodecType(); } -QString AVStreamWrapper::getCodecTypeName() +std::string AVStreamWrapper::getCodecTypeName() { auto type = this->codecpar.getCodecType(); if (type > AVMEDIA_TYPE_NB) return {}; - auto names = QStringList() << "Unknown" - << "Video" - << "Audio" - << "Data" - << "Subtitle" - << "Attachment" - << "NB"; + std::array names = {"Unknown", "Video", "Audio", "Data", "Subtitle", "Attachment", "NB"}; return names[type + 1]; } @@ -256,13 +264,13 @@ AVCodecContextWrapper &AVStreamWrapper::getCodec() return this->codec; }; -AVRational AVStreamWrapper::getAvgFrameRate() +Rational AVStreamWrapper::getAvgFrameRate() { this->update(); return this->avg_frame_rate; } -AVRational AVStreamWrapper::getTimeBase() +Rational AVStreamWrapper::getTimeBase() { this->update(); if (this->time_base.den == 0 || this->time_base.num == 0) @@ -323,156 +331,161 @@ void AVStreamWrapper::update() // Copy values from the source pointer if (libVer.avformat.major == 56) { - auto p = reinterpret_cast(this->stream); - this->index = p->index; - this->id = p->id; - this->codec = AVCodecContextWrapper(p->codec, libVer); - this->time_base = p->time_base; - this->start_time = p->start_time; - this->duration = p->duration; - this->nb_frames = p->nb_frames; - this->disposition = p->nb_frames; - this->discard = p->discard; - this->sample_aspect_ratio = p->sample_aspect_ratio; - this->avg_frame_rate = p->avg_frame_rate; - this->nb_side_data = p->nb_side_data; - this->event_flags = p->event_flags; + auto p = reinterpret_cast(this->stream); + this->index = p->index; + this->id = p->id; + this->codec = AVCodecContextWrapper(p->codec, libVer); + this->time_base.num = p->time_base.num; + this->time_base.den = p->time_base.den; + this->start_time = p->start_time; + this->duration = p->duration; + this->nb_frames = p->nb_frames; + this->disposition = p->nb_frames; + this->discard = p->discard; + this->sample_aspect_ratio.num = p->sample_aspect_ratio.num; + this->sample_aspect_ratio.den = p->sample_aspect_ratio.den; + this->avg_frame_rate.num = p->avg_frame_rate.num; + this->avg_frame_rate.den = p->avg_frame_rate.den; + this->nb_side_data = p->nb_side_data; + this->event_flags = p->event_flags; } else if (libVer.avformat.major == 57) { - auto p = reinterpret_cast(this->stream); - this->index = p->index; - this->id = p->id; - this->codec = AVCodecContextWrapper(p->codec, libVer); - this->time_base = p->time_base; - this->start_time = p->start_time; - this->duration = p->duration; - this->nb_frames = p->nb_frames; - this->disposition = p->nb_frames; - this->discard = p->discard; - this->sample_aspect_ratio = p->sample_aspect_ratio; - this->avg_frame_rate = p->avg_frame_rate; - this->nb_side_data = p->nb_side_data; - this->event_flags = p->event_flags; - this->codecpar = AVCodecParametersWrapper(p->codecpar, libVer); + auto p = reinterpret_cast(this->stream); + this->index = p->index; + this->id = p->id; + this->codec = AVCodecContextWrapper(p->codec, libVer); + this->time_base.num = p->time_base.num; + this->time_base.den = p->time_base.den; + this->start_time = p->start_time; + this->duration = p->duration; + this->nb_frames = p->nb_frames; + this->disposition = p->nb_frames; + this->discard = p->discard; + this->sample_aspect_ratio.num = p->sample_aspect_ratio.num; + this->sample_aspect_ratio.den = p->sample_aspect_ratio.den; + this->avg_frame_rate.num = p->avg_frame_rate.num; + this->avg_frame_rate.den = p->avg_frame_rate.den; + this->nb_side_data = p->nb_side_data; + this->event_flags = p->event_flags; + this->codecpar = AVCodecParametersWrapper(p->codecpar, libVer); } else if (libVer.avformat.major == 58) { - auto p = reinterpret_cast(this->stream); - this->index = p->index; - this->id = p->id; - this->codec = AVCodecContextWrapper(p->codec, libVer); - this->time_base = p->time_base; - this->start_time = p->start_time; - this->duration = p->duration; - this->nb_frames = p->nb_frames; - this->disposition = p->nb_frames; - this->discard = p->discard; - this->sample_aspect_ratio = p->sample_aspect_ratio; - this->avg_frame_rate = p->avg_frame_rate; - this->nb_side_data = p->nb_side_data; - this->event_flags = p->event_flags; - this->codecpar = AVCodecParametersWrapper(p->codecpar, libVer); + auto p = reinterpret_cast(this->stream); + this->index = p->index; + this->id = p->id; + this->codec = AVCodecContextWrapper(p->codec, libVer); + this->time_base.num = p->time_base.num; + this->time_base.den = p->time_base.den; + this->start_time = p->start_time; + this->duration = p->duration; + this->nb_frames = p->nb_frames; + this->disposition = p->nb_frames; + this->discard = p->discard; + this->sample_aspect_ratio.num = p->sample_aspect_ratio.num; + this->sample_aspect_ratio.den = p->sample_aspect_ratio.den; + this->avg_frame_rate.num = p->avg_frame_rate.num; + this->avg_frame_rate.den = p->avg_frame_rate.den; + this->nb_side_data = p->nb_side_data; + this->event_flags = p->event_flags; + this->codecpar = AVCodecParametersWrapper(p->codecpar, libVer); } else if (libVer.avformat.major == 59) { - auto p = reinterpret_cast(this->stream); - this->index = p->index; - this->id = p->id; - this->time_base = p->time_base; - this->start_time = p->start_time; - this->duration = p->duration; - this->nb_frames = p->nb_frames; - this->disposition = p->nb_frames; - this->discard = p->discard; - this->sample_aspect_ratio = p->sample_aspect_ratio; - this->avg_frame_rate = p->avg_frame_rate; - this->nb_side_data = p->nb_side_data; - this->event_flags = p->event_flags; - this->codecpar = AVCodecParametersWrapper(p->codecpar, libVer); + auto p = reinterpret_cast(this->stream); + this->index = p->index; + this->id = p->id; + this->time_base.num = p->time_base.num; + this->time_base.den = p->time_base.den; + this->start_time = p->start_time; + this->duration = p->duration; + this->nb_frames = p->nb_frames; + this->disposition = p->nb_frames; + this->discard = p->discard; + this->sample_aspect_ratio.num = p->sample_aspect_ratio.num; + this->sample_aspect_ratio.den = p->sample_aspect_ratio.den; + this->avg_frame_rate.num = p->avg_frame_rate.num; + this->avg_frame_rate.den = p->avg_frame_rate.den; + this->nb_side_data = p->nb_side_data; + this->event_flags = p->event_flags; + this->codecpar = AVCodecParametersWrapper(p->codecpar, libVer); } else throw std::runtime_error("Invalid library version"); } -QStringPairList AVStreamWrapper::getInfoText(AVCodecIDWrapper &codecIdWrapper) +StringPairVec AVStreamWrapper::getInfoText(const AVCodecIDWrapper &codecIdWrapper) const { if (this->stream == nullptr) - return {QStringPair("Error stream is null", "")}; - - this->update(); - - QStringPairList info; - info.append(QStringPair("Index", QString::number(this->index))); - info.append(QStringPair("ID", QString::number(this->id))); - - info.append(QStringPair("Codec Type", getCodecTypeName())); - info.append(QStringPair("Codec ID", QString::number((int)getCodecID()))); - info.append(QStringPair("Codec Name", codecIdWrapper.getCodecName())); - info.append( - QStringPair("Time base", QString("%1/%2").arg(this->time_base.num).arg(this->time_base.den))); - info.append(QStringPair("Start Time", - QString("%1 (%2)") - .arg(this->start_time) - .arg(timestampToString(this->start_time, this->time_base)))); - info.append(QStringPair("Duration", - QString("%1 (%2)") - .arg(this->duration) - .arg(timestampToString(this->duration, this->time_base)))); - info.append(QStringPair("Number Frames", QString::number(this->nb_frames))); - - if (this->disposition != 0) + return {{"Error stream is null", ""}}; + + AVStreamWrapper wrapper(this->stream, this->libVer); + + StringPairVec info; + info.push_back({"Index", std::to_string(wrapper.index)}); + info.push_back({"ID", std::to_string(wrapper.id)}); + + info.push_back({"Codec Type", wrapper.getCodecTypeName()}); + info.push_back({"Codec ID", std::to_string(wrapper.getCodecID())}); + info.push_back({"Codec Name", codecIdWrapper.getCodecName()}); + info.push_back( + {"Time base", + std::to_string(wrapper.time_base.num) + "/" + std::to_string(wrapper.time_base.den)}); + info.push_back({"Start Time", + std::to_string(wrapper.start_time) + " (" + + formatWithReadableFormat(wrapper.start_time, wrapper.time_base) + ")"}); + info.push_back({"Duration", + std::to_string(wrapper.duration) + " (" + + formatWithReadableFormat(wrapper.duration, wrapper.time_base) + ")"}); + info.push_back({"Number Frames", std::to_string(wrapper.nb_frames)}); + + if (wrapper.disposition != 0) { - QString dispText; - if (this->disposition & 0x0001) - dispText += QString("Default "); - if (this->disposition & 0x0002) - dispText += QString("Dub "); - if (this->disposition & 0x0004) - dispText += QString("Original "); - if (this->disposition & 0x0008) - dispText += QString("Comment "); - if (this->disposition & 0x0010) - dispText += QString("Lyrics "); - if (this->disposition & 0x0020) - dispText += QString("Karaoke "); - if (this->disposition & 0x0040) - dispText += QString("Forced "); - if (this->disposition & 0x0080) - dispText += QString("Hearing_Imparied "); - if (this->disposition & 0x0100) - dispText += QString("Visual_Impaired "); - if (this->disposition & 0x0200) - dispText += QString("Clean_Effects "); - if (this->disposition & 0x0400) - dispText += QString("Attached_Pic "); - if (this->disposition & 0x0800) - dispText += QString("Timed_Thumbnails "); - if (this->disposition & 0x1000) - dispText += QString("Captions "); - if (this->disposition & 0x2000) - dispText += QString("Descriptions "); - if (this->disposition & 0x4000) - dispText += QString("Metadata "); - if (this->disposition & 0x8000) - dispText += QString("Dependent "); - info.append(QStringPair("Disposition", dispText)); + std::string dispText; + if (wrapper.disposition & 0x0001) + dispText += "Default "; + if (wrapper.disposition & 0x0002) + dispText += "Dub "; + if (wrapper.disposition & 0x0004) + dispText += "Original "; + if (wrapper.disposition & 0x0008) + dispText += "Comment "; + if (wrapper.disposition & 0x0010) + dispText += "Lyrics "; + if (wrapper.disposition & 0x0020) + dispText += "Karaoke "; + if (wrapper.disposition & 0x0040) + dispText += "Forced "; + if (wrapper.disposition & 0x0080) + dispText += "Hearing_Imparied "; + if (wrapper.disposition & 0x0100) + dispText += "Visual_Impaired "; + if (wrapper.disposition & 0x0200) + dispText += "Clean_Effects "; + if (wrapper.disposition & 0x0400) + dispText += "Attached_Pic "; + if (wrapper.disposition & 0x0800) + dispText += "Timed_Thumbnails "; + if (wrapper.disposition & 0x1000) + dispText += "Captions "; + if (wrapper.disposition & 0x2000) + dispText += "Descriptions "; + if (wrapper.disposition & 0x4000) + dispText += "Metadata "; + if (wrapper.disposition & 0x8000) + dispText += "Dependent "; + info.push_back({"Disposition", dispText}); } - info.append(QStringPair( - "Sample Aspect Ratio", - QString("%1:%2").arg(this->sample_aspect_ratio.num).arg(this->sample_aspect_ratio.den))); + info.push_back({"Sample Aspect Ratio", + std::to_string(wrapper.sample_aspect_ratio.num) + ":" + + std::to_string(wrapper.sample_aspect_ratio.den)}); - auto divFrameRate = 0.0; - if (this->avg_frame_rate.den > 0) - divFrameRate = double(this->avg_frame_rate.num) / double(this->avg_frame_rate.den); - info.append(QStringPair("Average Frame Rate", - QString("%1/%2 (%3)") - .arg(this->avg_frame_rate.num) - .arg(this->avg_frame_rate.den) - .arg(divFrameRate, 0, 'f', 2))); + info.push_back({"Average Frame Rate", formatFramerate(wrapper.avg_frame_rate)}); - info += this->codecpar.getInfoText(); + const auto codecparInfo = wrapper.codecpar.getInfoText(); + info.insert(info.end(), codecparInfo.begin(), codecparInfo.end()); return info; } diff --git a/YUViewLib/src/ffmpeg/AVStreamWrapper.h b/YUViewLib/src/ffmpeg/AVStreamWrapper.h index daffc288d..a6926a9ed 100644 --- a/YUViewLib/src/ffmpeg/AVStreamWrapper.h +++ b/YUViewLib/src/ffmpeg/AVStreamWrapper.h @@ -47,15 +47,15 @@ class AVStreamWrapper AVStreamWrapper() {} AVStreamWrapper(AVStream *src_str, LibraryVersion v); - explicit operator bool() const { return this->stream != nullptr; }; - QStringPairList getInfoText(AVCodecIDWrapper &codecIdWrapper); + explicit operator bool() const { return this->stream != nullptr; }; + StringPairVec getInfoText(const AVCodecIDWrapper &codecIdWrapper) const; AVMediaType getCodecType(); - QString getCodecTypeName(); + std::string getCodecTypeName(); AVCodecID getCodecID(); AVCodecContextWrapper & getCodec(); - AVRational getAvgFrameRate(); - AVRational getTimeBase(); + Rational getAvgFrameRate(); + Rational getTimeBase(); Size getFrameSize(); AVColorSpace getColorspace(); AVPixelFormat getPixelFormat(); @@ -70,7 +70,7 @@ class AVStreamWrapper int index{}; int id{}; AVCodecContextWrapper codec{}; - AVRational time_base{}; + Rational time_base{}; int64_t start_time{}; int64_t duration{}; int64_t nb_frames{}; @@ -78,10 +78,10 @@ class AVStreamWrapper enum AVDiscard discard { }; - AVRational sample_aspect_ratio{}; - AVRational avg_frame_rate{}; - int nb_side_data{}; - int event_flags{}; + Rational sample_aspect_ratio{}; + Rational avg_frame_rate{}; + int nb_side_data{}; + int event_flags{}; // The AVCodecParameters are present from avformat major version 57 and up. AVCodecParametersWrapper codecpar{}; diff --git a/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.cpp b/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.cpp index b6d46af7b..fa49cff09 100644 --- a/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.cpp +++ b/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.cpp @@ -32,25 +32,36 @@ #include "FFMpegLibrariesTypes.h" +#include +#include + namespace FFmpeg { -QString timestampToString(int64_t timestamp, AVRational timebase) +std::string formatWithReadableFormat(int64_t timestamp, Rational timebase) { - auto d_seconds = (double)timestamp * timebase.num / timebase.den; - auto hours = (int)(d_seconds / 60 / 60); - d_seconds -= hours * 60 * 60; - auto minutes = (int)(d_seconds / 60); - d_seconds -= minutes * 60; - auto seconds = (int)d_seconds; - d_seconds -= seconds; - auto milliseconds = (int)(d_seconds * 1000); - - return QString("%1:%2:%3.%4") - .arg(hours, 2, 10, QChar('0')) - .arg(minutes, 2, 10, QChar('0')) - .arg(seconds, 2, 10, QChar('0')) - .arg(milliseconds, 3, 10, QChar('0')); + std::stringstream stream; + stream << timestamp << " ("; + + auto remainder = static_cast(timestamp) * timebase.num / timebase.den; + const auto hours = static_cast(remainder / 60 / 60); + stream << std::setw(2) << std::setfill('0') << hours << ":"; + + remainder -= hours * 60 * 60; + const auto minutes = static_cast(remainder / 60); + stream << std::setw(2) << std::setfill('0') << minutes << ":"; + + remainder -= minutes * 60; + const auto seconds = static_cast(remainder); + stream << std::setw(2) << std::setfill('0') << seconds << "."; + + remainder -= seconds; + auto milliseconds = static_cast(remainder * 1000); + stream << std::setw(3) << std::setfill('0') << milliseconds; + + stream << ")"; + + return stream.str(); } } // namespace FFmpeg diff --git a/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.h b/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.h index c4941cd89..a55c9a57a 100644 --- a/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.h +++ b/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.h @@ -32,6 +32,8 @@ #pragma once +#include + #include #include #include @@ -122,19 +124,6 @@ struct AVPixFmtDescriptor; #define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000)) -struct AVRational -{ - bool operator!=(const AVRational &second) const - { - const auto a = int64_t(this->num) * int64_t(second.den); - const auto b = int64_t(this->den) * int64_t(second.num); - return a != b; - } - - int num; ///< Numerator - int den; ///< Denominator -}; - // The exact value of the fractional number is: 'val + num / den'. num is assumed to be 0 <= num < // den. struct AVFrac @@ -142,6 +131,12 @@ struct AVFrac int64_t val, num, den; }; +struct AVRational +{ + int num; + int den; +}; + struct AVDictionaryEntry { char *key; @@ -404,6 +399,6 @@ struct LibraryVersion Version swresample{}; }; -QString timestampToString(int64_t timestamp, AVRational timebase); +std::string formatWithReadableFormat(int64_t timestamp, Rational timebase); } // namespace FFmpeg diff --git a/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.cpp b/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.cpp index d4ffa014c..efab16307 100644 --- a/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.cpp +++ b/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.cpp @@ -31,7 +31,12 @@ */ #include "FFmpegLibraryFunctions.h" -#include + +#include + +#include + +using namespace std::string_literals; namespace FFmpeg { @@ -42,14 +47,14 @@ namespace template bool resolveFunction(QLibrary & lib, std::function &function, - const char * symbolName, - QStringList * logList) + std::string symbolName, + StringVec * logList) { - auto ptr = lib.resolve(symbolName); + auto ptr = lib.resolve(symbolName.c_str()); if (!ptr) { if (logList) - logList->append(QString("Function %1 not found.").arg(symbolName)); + logList->push_back("Function " + symbolName + " not found."); return false; } @@ -59,7 +64,7 @@ bool resolveFunction(QLibrary & lib, bool bindLibraryFunctions(QLibrary & lib, FFmpegLibraryFunctions::AvFormatFunctions &functions, - QStringList * log) + StringVec * log) { if (!resolveFunction(lib, functions.avformat_version, "avformat_version", log)) return false; @@ -86,7 +91,7 @@ bool bindLibraryFunctions(QLibrary & lib, bool bindLibraryFunctions(QLibrary & lib, FFmpegLibraryFunctions::AvCodecFunctions &functions, - QStringList * log) + StringVec * log) { if (!resolveFunction(lib, functions.avcodec_find_decoder, "avcodec_find_decoder", log)) return false; @@ -132,7 +137,7 @@ bool bindLibraryFunctions(QLibrary & lib, bool bindLibraryFunctions(QLibrary & lib, FFmpegLibraryFunctions::AvUtilFunctions &functions, - QStringList * log) + StringVec * log) { if (!resolveFunction(lib, functions.avutil_version, "avutil_version", log)) return false; @@ -170,7 +175,7 @@ bool bindLibraryFunctions(QLibrary & lib, bool bindLibraryFunctions(QLibrary & lib, FFmpegLibraryFunctions::SwResampleFunction &functions, - QStringList * log) + StringVec * log) { return resolveFunction(lib, functions.swresample_version, "swresample_version", log); } @@ -182,23 +187,23 @@ FFmpegLibraryFunctions::~FFmpegLibraryFunctions() this->unloadAllLibraries(); } -bool FFmpegLibraryFunctions::loadFFmpegLibraryInPath(QString path, LibraryVersion &libraryVersion) +bool FFmpegLibraryFunctions::loadFFmpegLibraryInPath(std::filesystem::path path, + LibraryVersion & libraryVersion) { // We will load the following libraries (in this order): // avutil, swresample, avcodec, avformat. - if (!path.isEmpty()) + if (!path.empty()) { - QDir givenPath(path); - if (!givenPath.exists()) + if (!std::filesystem::exists(path)) { this->log("The given path is invalid"); return false; } // Get the absolute path - path = givenPath.absolutePath() + "/"; - this->log("Absolute path " + path); + path = std::filesystem::absolute(path); + this->log("Absolute path " + path.string()); } // The ffmpeg libraries are named using a major version number. E.g: avutil-55.dll on windows. @@ -213,7 +218,7 @@ bool FFmpegLibraryFunctions::loadFFmpegLibraryInPath(QString path, LibraryVersio this->unloadAllLibraries(); // This is how we the library name is constructed per platform - QString constructLibName; + std::string constructLibName; if (is_Q_OS_WIN) constructLibName = "%1-%2"; if (is_Q_OS_LINUX && i == 0) @@ -223,14 +228,14 @@ bool FFmpegLibraryFunctions::loadFFmpegLibraryInPath(QString path, LibraryVersio if (is_Q_OS_MAC) constructLibName = "lib%1.%2.dylib"; - auto loadLibrary = - [this, &constructLibName, &path](QLibrary &lib, QString libName, unsigned version) { - auto filename = constructLibName.arg(libName).arg(version); - lib.setFileName(path + filename); - auto success = lib.load(); - this->log("Loading library " + filename + (success ? " succeded" : " failed")); - return success; - }; + auto loadLibrary = [this, &constructLibName, &path]( + QLibrary &lib, std::string libName, unsigned version) { + auto filename = functions::formatString(constructLibName, {libName, std::to_string(version)}); + lib.setFileName(QString::fromStdString(path.string() + filename)); + auto success = lib.load(); + this->log("Loading library " + filename + (success ? " succeded" : " failed")); + return success; + }; if (!loadLibrary(this->libAvutil, "avutil", libraryVersion.avutil.major)) continue; @@ -255,20 +260,20 @@ bool FFmpegLibraryFunctions::loadFFmpegLibraryInPath(QString path, LibraryVersio bindLibraryFunctions(this->libAvcodec, this->avcodec, this->logList) && bindLibraryFunctions(this->libAvutil, this->avutil, this->logList) && bindLibraryFunctions(this->libSwresample, this->swresample, this->logList)); - this->log(QString("Binding functions ") + (success ? "successfull" : "failed")); + this->log("Binding functions "s + (success ? "successfull" : "failed")); return success; } -bool FFmpegLibraryFunctions::loadFFMpegLibrarySpecific(QString avFormatLib, - QString avCodecLib, - QString avUtilLib, - QString swResampleLib) +bool FFmpegLibraryFunctions::loadFFMpegLibrarySpecific(std::string avFormatLib, + std::string avCodecLib, + std::string avUtilLib, + std::string swResampleLib) { this->unloadAllLibraries(); - auto loadLibrary = [this](QLibrary &lib, QString libPath) { - lib.setFileName(libPath); + auto loadLibrary = [this](QLibrary &lib, std::string libPath) { + lib.setFileName(QString::fromStdString(libPath)); auto success = lib.load(); this->log("Loading library " + libPath + (success ? " succeded" : " failed")); return success; @@ -289,25 +294,25 @@ bool FFmpegLibraryFunctions::loadFFMpegLibrarySpecific(QString avFormatLib, bindLibraryFunctions(this->libAvcodec, this->avcodec, this->logList) && bindLibraryFunctions(this->libAvutil, this->avutil, this->logList) && bindLibraryFunctions(this->libSwresample, this->swresample, this->logList)); - this->log(QString("Binding functions ") + (success ? "successfull" : "failed")); + this->log("Binding functions "s + (success ? "successfull" : "failed")); return success; } -void FFmpegLibraryFunctions::addLibNamesToList(QString libName, - QStringList & l, - const QLibrary &lib) const +void FFmpegLibraryFunctions::addLibNamesToList(StringVec & list, + const std::string &libName, + const QLibrary & lib) const { - l.append(libName); + list.push_back(libName); if (lib.isLoaded()) { - l.append(lib.fileName()); - l.append(lib.fileName()); + list.push_back(lib.fileName().toStdString()); + list.push_back(lib.fileName().toStdString()); } else { - l.append("None"); - l.append("None"); + list.push_back("None"); + list.push_back("None"); } } @@ -320,21 +325,21 @@ void FFmpegLibraryFunctions::unloadAllLibraries() this->libAvformat.unload(); } -QStringList FFmpegLibraryFunctions::getLibPaths() const +StringVec FFmpegLibraryFunctions::getLibPaths() const { - QStringList libPaths; + StringVec libPaths; - auto addName = [&libPaths](QString name, const QLibrary &lib) { - libPaths.append(name); + auto addName = [&libPaths](std::string name, const QLibrary &lib) { + libPaths.push_back(name); if (lib.isLoaded()) { - libPaths.append(lib.fileName()); - libPaths.append(lib.fileName()); + libPaths.push_back(lib.fileName().toStdString()); + libPaths.push_back(lib.fileName().toStdString()); } else { - libPaths.append("None"); - libPaths.append("None"); + libPaths.push_back("None"); + libPaths.push_back("None"); } }; @@ -346,10 +351,10 @@ QStringList FFmpegLibraryFunctions::getLibPaths() const return libPaths; } -void FFmpegLibraryFunctions::log(QString message) +void FFmpegLibraryFunctions::log(std::string message) { if (this->logList) - this->logList->append(message); + this->logList->push_back(message); } } // namespace FFmpeg \ No newline at end of file diff --git a/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.h b/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.h index 4f0487a62..27caeca30 100644 --- a/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.h +++ b/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.h @@ -35,6 +35,7 @@ #include "FFMpegLibrariesTypes.h" #include #include +#include namespace FFmpeg { @@ -46,14 +47,14 @@ class FFmpegLibraryFunctions ~FFmpegLibraryFunctions(); // Load the FFmpeg libraries from the given path. - bool loadFFmpegLibraryInPath(QString path, LibraryVersion &libraryVersion); + bool loadFFmpegLibraryInPath(std::filesystem::path path, LibraryVersion &libraryVersion); // Try to load the 4 given specific libraries - bool loadFFMpegLibrarySpecific(QString avFormatLib, - QString avCodecLib, - QString avUtilLib, - QString swResampleLib); + bool loadFFMpegLibrarySpecific(std::string avFormatLib, + std::string avCodecLib, + std::string avUtilLib, + std::string swResampleLib); - QStringList getLibPaths() const; + StringVec getLibPaths() const; struct AvFormatFunctions { @@ -104,7 +105,7 @@ class FFmpegLibraryFunctions std::function avutil_version; std::function av_dict_set; - std::function av_dict_get; std::function @@ -124,14 +125,14 @@ class FFmpegLibraryFunctions }; SwResampleFunction swresample{}; - void setLogList(QStringList *l) { logList = l; } + void setLogList(StringVec *l) { logList = l; } private: - void addLibNamesToList(QString libName, QStringList &l, const QLibrary &lib) const; + void addLibNamesToList(StringVec &l, const std::string &libName, const QLibrary &lib) const; void unloadAllLibraries(); - QStringList *logList{}; - void log(QString message); + StringVec *logList{}; + void log(std::string message); QLibrary libAvutil; QLibrary libSwresample; diff --git a/YUViewLib/src/ffmpeg/FFmpegVersionHandler.cpp b/YUViewLib/src/ffmpeg/FFmpegVersionHandler.cpp index 182f653df..ebbd24c87 100644 --- a/YUViewLib/src/ffmpeg/FFmpegVersionHandler.cpp +++ b/YUViewLib/src/ffmpeg/FFmpegVersionHandler.cpp @@ -40,44 +40,39 @@ namespace FFmpeg namespace { -bool checkVersionWithLib(FFmpegLibraryFunctions &lib, LibraryVersion version, QStringList &logList) +bool checkVersionWithLib(FFmpegLibraryFunctions &lib, LibraryVersion version, StringVec &logList) { if (version.avcodec.major != AV_VERSION_MAJOR(lib.avcodec.avcodec_version())) { - logList.append( - QString("The openend avcodec returned a different major version (%1) than we are " - "looking for (%2).") - .arg(AV_VERSION_MAJOR(lib.avcodec.avcodec_version())) - .arg(version.avcodec.major)); + logList.push_back("The openend avcodec returned a different major version (" + + std::to_string(AV_VERSION_MAJOR(lib.avcodec.avcodec_version())) + + ") than we are looking for (" + std::to_string(version.avcodec.major) + ")."); return false; } if (version.avformat.major != AV_VERSION_MAJOR(lib.avformat.avformat_version())) { - logList.append( - QString("The openend avformat returned a different major version (%1) than we are " - "looking for (%2).") - .arg(AV_VERSION_MAJOR(lib.avformat.avformat_version())) - .arg(version.avformat.major)); + logList.push_back("The openend avformat returned a different major version (" + + std::to_string(AV_VERSION_MAJOR(lib.avformat.avformat_version())) + + ") than we are looking for (" + std::to_string(version.avformat.major) + + ")."); return false; } if (version.avutil.major != AV_VERSION_MAJOR(lib.avutil.avutil_version())) { - logList.append(QString("The openend avutil returned a different major version (%1) than we are " - "looking for (%2).") - .arg(AV_VERSION_MAJOR(lib.avutil.avutil_version())) - .arg(version.avutil.major)); + logList.push_back("The openend avutil returned a different major version (" + + std::to_string(AV_VERSION_MAJOR(lib.avutil.avutil_version())) + + ") than we are looking for (" + std::to_string(version.avutil.major) + ")."); return false; } if (version.swresample.major != AV_VERSION_MAJOR(lib.swresample.swresample_version())) { - logList.append( - QString("The openend swresample returned a different major version (%1) than we are " - "looking for (%2).") - .arg(AV_VERSION_MAJOR(lib.swresample.swresample_version())) - .arg(version.swresample.major)); + logList.push_back("The openend swresample returned a different major version (" + + std::to_string(AV_VERSION_MAJOR(lib.swresample.swresample_version())) + + ") than we are looking for (" + std::to_string(version.swresample.major) + + ")."); return false; } @@ -106,6 +101,30 @@ auto SupportedLibraryVersionCombinations = {LibraryVersion(57, 59, 59, 4), LibraryVersion(55, 57, 57, 2), LibraryVersion(54, 56, 56, 1)}; +StringVec getPathsToTry() +{ + QSettings settings; + settings.beginGroup("Decoders"); + auto decoderSearchPath = settings.value("SearchPath", "").toString().toStdString(); + settings.endGroup(); + + const auto currentPath = std::filesystem::absolute(std::filesystem::current_path()).string(); + const auto currentAppPath = QCoreApplication::applicationDirPath().toStdString(); + + StringVec paths; + + if (!decoderSearchPath.empty()) + paths.push_back(decoderSearchPath); + paths.push_back(currentPath + "/"); // Try the current working directory + paths.push_back(currentPath + "/ffmpeg/"); + paths.push_back(currentAppPath + "/"); // Try the path of the YUView.exe + paths.push_back(currentAppPath + "/ffmpeg/"); + paths.push_back( + ""); // Just try to call QLibrary::load so that the system folder will be searched. + + return paths; +} + } // namespace // bool FFmpegVersionHandler::AVCodecContextCopyParameters(AVCodecContext *srcCtx, AVCodecContext @@ -201,36 +220,29 @@ auto SupportedLibraryVersionCombinations = {LibraryVersion(57, 59, 59, 4), //} // -QString FFmpegVersionHandler::getLibVersionString() const +std::string FFmpegVersionHandler::getLibVersionFormatted() const { - QString s; - s += QString("avUtil %1.%2.%3 ") - .arg(libVersion.avutil.major) - .arg(libVersion.avutil.minor.value_or(0)) - .arg(libVersion.avutil.micro.value_or(0)); - s += QString("avFormat %1.%2.%3 ") - .arg(libVersion.avformat.major) - .arg(libVersion.avformat.minor.value_or(0)) - .arg(libVersion.avformat.micro.value_or(0)); - s += QString("avCodec %1.%2.%3 ") - .arg(libVersion.avcodec.major) - .arg(libVersion.avcodec.minor.value_or(0)) - .arg(libVersion.avcodec.micro.value_or(0)); - s += QString("swresample %1.%2.%3") - .arg(libVersion.swresample.major) - .arg(libVersion.swresample.minor.value_or(0)) - .arg(libVersion.swresample.micro.value_or(0)); + std::stringstream stream; + auto formatVersion = [&stream](std::string name, const FFmpeg::Version &version) { + stream << "avUtil " << version.major << "." << version.minor.value_or(0) << "." + << version.micro.value_or(0) << " "; + }; + + formatVersion("avUtil", this->libVersion.avutil); + formatVersion("avFormat", this->libVersion.avformat); + formatVersion("avCodec", this->libVersion.avcodec); + formatVersion("swresample", this->libVersion.swresample); - return s; + return stream.str(); } -AVCodecIDWrapper FFmpegVersionHandler::getCodecIDWrapper(AVCodecID id) +AVCodecIDWrapper FFmpegVersionHandler::getCodecIDWrapper(AVCodecID id) const { - auto codecName = QString(lib.avcodec.avcodec_get_name(id)); + auto codecName = std::string(lib.avcodec.avcodec_get_name(id)); return AVCodecIDWrapper(id, codecName); } -AVCodecID FFmpegVersionHandler::getCodecIDFromWrapper(AVCodecIDWrapper &wrapper) +AVCodecID FFmpegVersionHandler::getCodecIDFromWrapper(AVCodecIDWrapper &wrapper) const { if (wrapper.getCodecID() != AV_CODEC_ID_NONE) return wrapper.getCodecID(); @@ -239,7 +251,7 @@ AVCodecID FFmpegVersionHandler::getCodecIDFromWrapper(AVCodecIDWrapper &wrapper) QString codecName; do { - auto codecName = QString(this->lib.avcodec.avcodec_get_name(AVCodecID(codecID))); + auto codecName = std::string(this->lib.avcodec.avcodec_get_name(AVCodecID(codecID))); if (codecName == wrapper.getCodecName()) { wrapper.setCodecID(AVCodecID(codecID)); @@ -263,10 +275,8 @@ bool FFmpegVersionHandler::configureDecoder(AVCodecContextWrapper & decCtx, auto ret = this->lib.avcodec.avcodec_parameters_to_context(decCtx.getCodec(), origin_par); if (ret < 0) { - this->log( - QString( - "Could not copy codec parameters (avcodec_parameters_to_context). Return code %1.") - .arg(ret)); + this->log("Could not copy codec parameters (avcodec_parameters_to_context). Return code " + + std::to_string(ret)); return false; } } @@ -302,7 +312,7 @@ void FFmpegVersionHandler::flush_buffers(AVCodecContextWrapper &decCtx) lib.avcodec.avcodec_flush_buffers(decCtx.getCodec()); } -QStringList FFmpegVersionHandler::logListFFmpeg; +StringVec FFmpegVersionHandler::logListFFmpeg; FFmpegVersionHandler::FFmpegVersionHandler() { @@ -312,11 +322,12 @@ FFmpegVersionHandler::FFmpegVersionHandler() void FFmpegVersionHandler::avLogCallback(void *, int level, const char *fmt, va_list vargs) { - QString msg; - msg.vasprintf(fmt, vargs); - auto now = QDateTime::currentDateTime(); - FFmpegVersionHandler::logListFFmpeg.append(now.toString("hh:mm:ss.zzz") + - QString(" - L%1 - ").arg(level) + msg); + std::stringstream stream; + auto currentTime = std::time(NULL); + stream << std::put_time(std::localtime(¤tTime), "%X"); + stream << " - L" << level << " - "; + stream << functions::vstring(fmt, vargs); + FFmpegVersionHandler::logListFFmpeg.push_back(stream.str()); } void FFmpegVersionHandler::loadFFmpegLibraries() @@ -330,40 +341,28 @@ void FFmpegVersionHandler::loadFFmpegLibraries() // First try the specific FFMpeg libraries (if set) QSettings settings; settings.beginGroup("Decoders"); - auto avFormatLib = settings.value("FFmpeg.avformat", "").toString(); - auto avCodecLib = settings.value("FFmpeg.avcodec", "").toString(); - auto avUtilLib = settings.value("FFmpeg.avutil", "").toString(); - auto swResampleLib = settings.value("FFmpeg.swresample", "").toString(); - if (!avFormatLib.isEmpty() && // - !avCodecLib.isEmpty() && // - !avUtilLib.isEmpty() && // - !swResampleLib.isEmpty()) + auto avFormatLib = settings.value("FFmpeg.avformat", "").toString().toStdString(); + auto avCodecLib = settings.value("FFmpeg.avcodec", "").toString().toStdString(); + auto avUtilLib = settings.value("FFmpeg.avutil", "").toString().toStdString(); + auto swResampleLib = settings.value("FFmpeg.swresample", "").toString().toStdString(); + settings.endGroup(); + if (!avFormatLib.empty() && // + !avCodecLib.empty() && // + !avUtilLib.empty() && // + !swResampleLib.empty()) { this->log("Trying to load the libraries specified in the settings."); this->librariesLoaded = - loadFFMpegLibrarySpecific(avFormatLib, avCodecLib, avUtilLib, swResampleLib); + this->loadFFMpegLibrarySpecific(avFormatLib, avCodecLib, avUtilLib, swResampleLib); } else this->log("No ffmpeg libraries were specified in the settings."); if (!this->librariesLoaded) { - // Next, we will try some other paths / options - QStringList possibilites; - auto decoderSearchPath = settings.value("SearchPath", "").toString(); - if (!decoderSearchPath.isEmpty()) - possibilites.append(decoderSearchPath); // Try the specific search path (if one is set) - possibilites.append(QDir::currentPath() + "/"); // Try the current working directory - possibilites.append(QDir::currentPath() + "/ffmpeg/"); - possibilites.append(QCoreApplication::applicationDirPath() + - "/"); // Try the path of the YUView.exe - possibilites.append(QCoreApplication::applicationDirPath() + "/ffmpeg/"); - possibilites.append( - ""); // Just try to call QLibrary::load so that the system folder will be searched. - - for (auto path : possibilites) + for (auto path : getPathsToTry()) { - if (path.isEmpty()) + if (path.empty()) this->log("Trying to load the libraries in the system path"); else this->log("Trying to load the libraries in the path " + path); @@ -390,12 +389,12 @@ bool FFmpegVersionHandler::openInput(AVFormatContextWrapper &fmt, QString url) this->lib.avformat.avformat_open_input(&f_ctx, url.toStdString().c_str(), nullptr, nullptr); if (ret < 0) { - this->log(QString("Error opening file (avformat_open_input). Ret code %1").arg(ret)); + this->log("Error opening file (avformat_open_input). Ret code " + std::to_string(ret)); return false; } if (f_ctx == nullptr) { - this->log(QString("Error opening file (avformat_open_input). No format context returned.")); + this->log("Error opening file (avformat_open_input). No format context returned."); return false; } @@ -405,7 +404,7 @@ bool FFmpegVersionHandler::openInput(AVFormatContextWrapper &fmt, QString url) ret = lib.avformat.avformat_find_stream_info(fmt.getFormatCtx(), nullptr); if (ret < 0) { - this->log(QString("Error opening file (avformat_find_stream_info). Ret code %1").arg(ret)); + this->log("Error opening file (avformat_find_stream_info). Ret code " + std::to_string(ret)); return false; } @@ -498,22 +497,21 @@ int FFmpegVersionHandler::seekBeginning(AVFormatContextWrapper &fmt) { // This is "borrowed" from the ffmpeg sources // (https://ffmpeg.org/doxygen/4.0/ffmpeg_8c_source.html seek_to_start) - this->log(QString("seek_beginning time %1").arg(fmt.getStartTime())); + this->log("seek_beginning time " + std::to_string(fmt.getStartTime())); return lib.avformat.av_seek_frame(fmt.getFormatCtx(), -1, fmt.getStartTime(), 0); } -bool FFmpegVersionHandler::loadFFmpegLibraryInPath(QString path) +bool FFmpegVersionHandler::loadFFmpegLibraryInPath(const std::string &path) { bool success = false; for (auto version : SupportedLibraryVersionCombinations) { if (this->lib.loadFFmpegLibraryInPath(path, version)) { - this->log(QString("Checking versions avutil %1, swresample %2, avcodec %3, avformat %4") - .arg(version.avutil.major) - .arg(version.swresample.major) - .arg(version.avcodec.major) - .arg(version.avformat.major)); + this->log("Checking versions avutil " + std::to_string(version.avutil.major) + + ", swresample " + std::to_string(version.swresample.major) + ", avcodec " + + std::to_string(version.avcodec.major) + ", avformat " + + std::to_string(version.avformat.major)); if ((success = checkVersionWithLib(this->lib, version, this->logList))) { @@ -530,26 +528,25 @@ bool FFmpegVersionHandler::loadFFmpegLibraryInPath(QString path) return success; } -bool FFmpegVersionHandler::loadFFMpegLibrarySpecific(QString avFormatLib, - QString avCodecLib, - QString avUtilLib, - QString swResampleLib) +bool FFmpegVersionHandler::loadFFMpegLibrarySpecific(const std::string &avFormatLib, + const std::string &avCodecLib, + const std::string &avUtilLib, + const std::string &swResampleLib) { bool success = false; for (auto version : SupportedLibraryVersionCombinations) { - this->log(QString("Checking versions avutil %1, swresample %2, avcodec %3, avformat %4") - .arg(version.avutil.major) - .arg(version.swresample.major) - .arg(version.avcodec.major) - .arg(version.avformat.major)); + this->log("Checking versions avutil " + std::to_string(version.avutil.major) + ", swresample " + + std::to_string(version.swresample.major) + ", avcodec " + + std::to_string(version.avcodec.major) + ", avformat " + + std::to_string(version.avformat.major)); if (lib.loadFFMpegLibrarySpecific(avFormatLib, avCodecLib, avUtilLib, swResampleLib)) { this->log("Testing versions of the library. Currently looking for:"); - this->log(QString("avutil: %1.xx.xx").arg(version.avutil.major)); - this->log(QString("swresample: %1.xx.xx").arg(version.swresample.major)); - this->log(QString("avcodec: %1.xx.xx").arg(version.avcodec.major)); - this->log(QString("avformat: %1.xx.xx").arg(version.avformat.major)); + this->log("avutil: " + std::to_string(version.avutil.major) + ".xx.xx"); + this->log("swresample: " + std::to_string(version.swresample.major) + ".xx.xx"); + this->log("avcodec: " + std::to_string(version.avcodec.major) + ".xx.xx"); + this->log("avformat: " + std::to_string(version.avformat.major) + ".xx.xx"); if ((success = checkVersionWithLib(this->lib, version, this->logList))) { @@ -566,11 +563,11 @@ bool FFmpegVersionHandler::loadFFMpegLibrarySpecific(QString avFormatLib, return success; } -bool FFmpegVersionHandler::checkLibraryFiles(QString avCodecLib, - QString avFormatLib, - QString avUtilLib, - QString swResampleLib, - QStringList &logging) +bool FFmpegVersionHandler::checkLibraryFiles(const std::string &avCodecLib, + const std::string &avFormatLib, + const std::string &avUtilLib, + const std::string &swResampleLib, + StringVec & logging) { FFmpegVersionHandler handler; bool success = @@ -609,7 +606,6 @@ FFmpegVersionHandler::getAVPixelFormatFromPixelFormatYUV(video::yuv::PixelFormat if (descWrapper == wrapper) return this->lib.avutil.av_pix_fmt_desc_get_id(desc); - // Get the next descriptor desc = this->lib.avutil.av_pix_fmt_desc_next(desc); } diff --git a/YUViewLib/src/ffmpeg/FFmpegVersionHandler.h b/YUViewLib/src/ffmpeg/FFmpegVersionHandler.h index 3a914e686..f8225b94d 100644 --- a/YUViewLib/src/ffmpeg/FFmpegVersionHandler.h +++ b/YUViewLib/src/ffmpeg/FFmpegVersionHandler.h @@ -57,13 +57,13 @@ class FFmpegVersionHandler void loadFFmpegLibraries(); bool loadingSuccessfull() const; - QStringList getLibPaths() const { return lib.getLibPaths(); } - QString getLibVersionString() const; + StringVec getLibPaths() const { return lib.getLibPaths(); } + std::string getLibVersionFormatted() const; // Only these functions can be used to get valid versions of these wrappers (they have to use // ffmpeg functions to retrieve the needed information) - AVCodecIDWrapper getCodecIDWrapper(AVCodecID id); - AVCodecID getCodecIDFromWrapper(AVCodecIDWrapper &wrapper); + AVCodecIDWrapper getCodecIDWrapper(AVCodecID id) const; + AVCodecID getCodecIDFromWrapper(AVCodecIDWrapper &wrapper) const; AVPixFmtDescriptorWrapper getAvPixFmtDescriptionFromAvPixelFormat(AVPixelFormat pixFmt); AVPixelFormat getAVPixelFormatFromPixelFormatYUV(video::yuv::PixelFormatYUV pixFmt); @@ -112,37 +112,37 @@ class FFmpegVersionHandler static AVPixelFormat convertYUVAVPixelFormat(video::yuv::PixelFormatYUV fmt); // Check if the given four files can be used to open FFmpeg. - static bool checkLibraryFiles(QString avCodecLib, - QString avFormatLib, - QString avUtilLib, - QString swResampleLib, - QStringList &logging); + static bool checkLibraryFiles(const std::string &avCodecLib, + const std::string &avFormatLib, + const std::string &avUtilLib, + const std::string &swResampleLib, + StringVec & logging); // Logging. By default we set the logging level of ffmpeg to AV_LOG_ERROR (Log errors and // everything worse) - static QStringList getFFmpegLog() { return logListFFmpeg; } - void enableLoggingWarning(); + static StringVec getFFmpegLog() { return logListFFmpeg; } + void enableLoggingWarning(); - QStringList getLog() const { return logList; } + StringVec getLog() const { return this->logList; } private: // Try to load the FFmpeg libraries from the given path. // Try the system paths if no path is provided. This function can be called multiple times. - bool loadFFmpegLibraryInPath(QString path); + bool loadFFmpegLibraryInPath(const std::string &path); // Try to load the four specific library files - bool loadFFMpegLibrarySpecific(QString avFormatLib, - QString avCodecLib, - QString avUtilLib, - QString swResampleLib); + bool loadFFMpegLibrarySpecific(const std::string &avFormatLib, + const std::string &avCodecLib, + const std::string &avUtilLib, + const std::string &swResampleLib); bool librariesLoaded{}; // Log what is happening when loading the libraries / opening files. - void log(QString message) { logList.append(message); } - QStringList logList{}; + void log(std::string message) { this->logList.push_back(message); } + StringVec logList{}; // FFmpeg has a callback where it loggs stuff. This log goes here. - static QStringList logListFFmpeg; - static void avLogCallback(void *ptr, int level, const char *fmt, va_list vargs); + static StringVec logListFFmpeg; + static void avLogCallback(void *ptr, int level, const char *fmt, va_list vargs); }; } // namespace FFmpeg diff --git a/YUViewLib/src/filesource/FileSource.cpp b/YUViewLib/src/filesource/FileSource.cpp index 7e5daf3de..19ae8803a 100644 --- a/YUViewLib/src/filesource/FileSource.cpp +++ b/YUViewLib/src/filesource/FileSource.cpp @@ -63,19 +63,16 @@ bool FileSource::openFile(const QString &filePath) if (!this->fileInfo.exists() || !this->fileInfo.isFile()) return false; - if (this->isFileOpened && this->srcFile.isOpen()) + if (this->srcFile.isOpen()) this->srcFile.close(); - // open file for reading this->srcFile.setFileName(filePath); - this->isFileOpened = this->srcFile.open(QIODevice::ReadOnly); - if (!this->isFileOpened) + this->srcFile.open(QIODevice::ReadOnly); + if (!this->srcFile.isOpen()) return false; - // Save the full file path this->fullFilePath = filePath; - // Install a watcher for the file (if file watching is active) this->updateFileWatchSetting(); this->fileChanged = false; @@ -100,7 +97,7 @@ void FileSource::readBytes(byteArrayAligned &targetBuffer, int64_t startPos, int // Resize the target array if necessary and read the given number of bytes to the data array int64_t FileSource::readBytes(QByteArray &targetBuffer, int64_t startPos, int64_t nrBytes) { - if (!this->isOk()) + if (!this->srcFile.isOpen()) return 0; if (targetBuffer.size() < nrBytes) @@ -120,7 +117,7 @@ QList FileSource::getFileInfoList() const { QList infoList; - if (!this->isFileOpened) + if (!this->srcFile.isOpen()) return infoList; infoList.append(InfoItem("File Path", this->fileInfo.absoluteFilePath())); @@ -137,14 +134,19 @@ QList FileSource::getFileInfoList() const return infoList; } +int64_t FileSource::getFileSize() const +{ + return !this->srcFile.isOpen() ? -1 : this->fileInfo.size(); +} + QString FileSource::getAbsoluteFilePath() const { - return this->isFileOpened ? this->fileInfo.absoluteFilePath() : QString(); + return this->srcFile.isOpen() ? this->fileInfo.absoluteFilePath() : QString(); } -FileSource::fileFormat_t FileSource::guessFormatFromFilename() const +FileSource::FileFormat FileSource::guessFormatFromFilename() const { - FileSource::fileFormat_t format; + FileSource::FileFormat format; // We are going to check two strings (one after the other) for indicators on the frames size, fps // and bit depth. 1: The file name, 2: The folder name that the file is contained in. @@ -332,6 +334,11 @@ bool FileSource::getAndResetFileChangedFlag() return b; } +bool FileSource::atEnd() const +{ + return !this->srcFile.isOpen() ? true : this->srcFile.atEnd(); +} + void FileSource::updateFileWatchSetting() { // Install a file watcher if file watching is active in the settings. @@ -345,7 +352,7 @@ void FileSource::updateFileWatchSetting() void FileSource::clearFileCache() { - if (!this->isFileOpened) + if (!this->srcFile.isOpen()) return; #ifdef Q_OS_WIN diff --git a/YUViewLib/src/filesource/FileSource.h b/YUViewLib/src/filesource/FileSource.h index 6aa3f19c0..c4fc61d3d 100644 --- a/YUViewLib/src/filesource/FileSource.h +++ b/YUViewLib/src/filesource/FileSource.h @@ -49,6 +49,7 @@ enum class InputFormat AnnexBHEVC, // Raw HEVC annex B file AnnexBAVC, // Raw AVC annex B file AnnexBVVC, // Raw VVC annex B file + OBUAV1, // Raw OBU file with AV1 Libav // This is some sort of container file which we will read using libavformat }; @@ -56,8 +57,24 @@ const auto InputFormatMapper = EnumMapper({{InputFormat::Invalid, " {InputFormat::AnnexBHEVC, "AnnexBHEVC"}, {InputFormat::AnnexBAVC, "AnnexBAVC"}, {InputFormat::AnnexBVVC, "AnnexBVVC"}, + {InputFormat::OBUAV1, "OBUAV1"}, {InputFormat::Libav, "Libav"}}); +struct StreamInfo +{ + std::string streamName; + Rational timebase{}; + StringPairVec infoItems; + std::string shortInfo; +}; +using StreamsInfo = std::vector; + +struct DataAndStartEndPos +{ + QByteArray data{}; + FileStartEndPos startEnd{}; +}; + /* The FileSource class provides functions for accessing files. Besides the reading of * certain blocks of the file, it also directly provides information on the file for the * fileInfoWidget. It also adds functions for guessing the format from the filename. @@ -71,29 +88,29 @@ class FileSource : public QObject virtual bool openFile(const QString &filePath); - virtual QList getFileInfoList() const; - int64_t getFileSize() const { return !isFileOpened ? -1 : fileInfo.size(); } - QString getAbsoluteFilePath() const; - QFileInfo getFileInfo() const { return this->fileInfo; } - QFile * getQFile() { return &this->srcFile; } - bool getAndResetFileChangedFlag(); + [[nodiscard]] virtual QList getFileInfoList() const; + [[nodiscard]] int64_t getFileSize() const; + [[nodiscard]] QString getAbsoluteFilePath() const; + [[nodiscard]] QFileInfo getFileInfo() const { return this->fileInfo; } + [[nodiscard]] QFile * getQFile() { return &this->srcFile; } + [[nodiscard]] bool getAndResetFileChangedFlag(); // Return true if the file could be opened and is ready for use. - bool isOk() const { return this->isFileOpened; } + [[nodiscard]] bool isOpen() const { return this->srcFile.isOpen(); } + [[nodiscard]] virtual bool atEnd() const; + [[nodiscard]] int64_t pos() const { return this->srcFile.pos(); } - virtual bool atEnd() const { return !this->isFileOpened ? true : this->srcFile.atEnd(); } - QByteArray readLine() { return !this->isFileOpened ? QByteArray() : this->srcFile.readLine(); } - virtual bool seek(int64_t pos) { return !this->isFileOpened ? false : this->srcFile.seek(pos); } - int64_t pos() { return !this->isFileOpened ? 0 : this->srcFile.pos(); } + QByteArray readLine() { return this->srcFile.readLine(); } + virtual bool seek(int64_t pos) { return this->srcFile.seek(pos); } - struct fileFormat_t + struct FileFormat { Size frameSize; int frameRate{-1}; unsigned bitDepth{}; bool packed{false}; }; - fileFormat_t guessFormatFromFilename() const; + FileFormat guessFormatFromFilename() const; // Get the file size in bytes @@ -118,7 +135,6 @@ private slots: QString fullFilePath{}; QFileInfo fileInfo; QFile srcFile; - bool isFileOpened{}; private: QFileSystemWatcher fileWatcher{}; diff --git a/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp b/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp index cc22edf33..3be213c51 100644 --- a/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp +++ b/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp @@ -1,34 +1,34 @@ /* 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 . -*/ + * + * 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 "FileSourceAnnexBFile.h" @@ -40,35 +40,31 @@ #define DEBUG_ANNEXBFILE(f) ((void)0) #endif -const auto BUFFERSIZE = 500000; -const auto STARTCODE = QByteArrayLiteral("\x00\x00\x01"); +const auto BUFFERSIZE = 500'000; +const auto STARTCODE = QByteArrayLiteral("\x00\x00\x01"); FileSourceAnnexBFile::FileSourceAnnexBFile() { this->fileBuffer.resize(BUFFERSIZE); } -// Open the file and fill the read buffer. +// Open the file and fill the read buffer. bool FileSourceAnnexBFile::openFile(const QString &fileName) { DEBUG_ANNEXBFILE("FileSourceAnnexBFile::openFile fileName " << fileName); - // Open the input file (again) FileSource::openFile(fileName); - // Fill the buffer this->fileBufferSize = srcFile.read(this->fileBuffer.data(), BUFFERSIZE); if (this->fileBufferSize == 0) - // The file is empty of there was an error reading from the file. return false; - // Discard all bytes until we find a start code this->seekToFirstNAL(); return true; } bool FileSourceAnnexBFile::atEnd() const -{ +{ return this->fileBufferSize < BUFFERSIZE && this->posInBuffer >= int64_t(this->fileBufferSize); } @@ -76,12 +72,13 @@ void FileSourceAnnexBFile::seekToFirstNAL() { auto nextStartCodePos = this->fileBuffer.indexOf(STARTCODE); if (nextStartCodePos < 0) - // The first buffer does not contain a start code. This is very unusual. Use the normal getNextNALUnit to seek + // The first buffer does not contain a start code. This is very unusual. Use the normal + // getNextNALUnit to seek this->getNextNALUnit(); else { // For 0001 or 001 point to the first 0 byte - if (nextStartCodePos > 0 && this->fileBuffer.at(nextStartCodePos-1) ==(char)0) + if (nextStartCodePos > 0 && this->fileBuffer.at(nextStartCodePos - 1) == (char)0) this->posInBuffer = nextStartCodePos - 1; else this->posInBuffer = nextStartCodePos; @@ -91,76 +88,81 @@ void FileSourceAnnexBFile::seekToFirstNAL() this->nrBytesBeforeFirstNAL = this->bufferStartPosInFile + uint64_t(this->posInBuffer); } -QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, pairUint64 *startEndPosInFile) +DataAndStartEndPos FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain) { if (getLastDataAgain) - return this->lastReturnArray; + return this->lastDataAndPos; - this->lastReturnArray.clear(); + this->lastDataAndPos.data.clear(); + this->lastDataAndPos.startEnd.start = this->bufferStartPosInFile + this->posInBuffer; - if (startEndPosInFile) - startEndPosInFile->first = this->bufferStartPosInFile + uint64_t(this->posInBuffer); - - int nextStartCodePos = -1; - int searchOffset = 3; - bool startCodeFound = false; + int nextStartCodePos = -1; + int searchOffset = 3; + bool startCodeFound = false; while (!startCodeFound) { if (this->posInBuffer < 0) { - // Part of the start code was in the last buffer (see special boundary cases below). Add those parts. + // Part of the start code was in the last buffer (see special boundary cases below). Add those + // parts. const auto nrZeroBytesMissing = std::abs(this->posInBuffer); - this->lastReturnArray.append(nrZeroBytesMissing, char(0)); + this->lastDataAndPos.data.append(nrZeroBytesMissing, char(0)); } nextStartCodePos = this->fileBuffer.indexOf(STARTCODE, this->posInBuffer + searchOffset); - if (nextStartCodePos < 0 || (uint64_t)nextStartCodePos > this->fileBufferSize) + if (nextStartCodePos < 0 || nextStartCodePos > this->fileBufferSize) { // No start code found ... append all data in the current buffer. - this->lastReturnArray += this->fileBuffer.mid(this->posInBuffer, this->fileBufferSize - this->posInBuffer); - DEBUG_ANNEXBFILE("FileSourceHEVCAnnexBFile::getNextNALUnit no start code found - ret size " << this->lastReturnArray.size()); + this->lastDataAndPos.data += + this->fileBuffer.mid(this->posInBuffer, this->fileBufferSize - this->posInBuffer); + DEBUG_ANNEXBFILE("FileSourceHEVCAnnexBFile::getNextNALUnit no start code found - ret size " + << this->lastDataAndPos.data.size()); if (this->fileBufferSize < BUFFERSIZE) { // We are out of file and could not find a next position - this->posInBuffer = BUFFERSIZE; - if (startEndPosInFile) - startEndPosInFile->second = this->bufferStartPosInFile + this->fileBufferSize - 1; - return this->lastReturnArray; + this->posInBuffer = BUFFERSIZE; + this->lastDataAndPos.startEnd.end = this->bufferStartPosInFile + this->fileBufferSize - 1; + return this->lastDataAndPos; } - // Before we load the next bytes: The start code might be located at the boundary to the next buffer + // Before we load the next bytes: The start code might be located at the boundary to the next + // buffer const auto lastByteZero0 = this->fileBuffer.at(this->fileBufferSize - 3) == (char)0; const auto lastByteZero1 = this->fileBuffer.at(this->fileBufferSize - 2) == (char)0; const auto lastByteZero2 = this->fileBuffer.at(this->fileBufferSize - 1) == (char)0; // We have to continue searching - get the next buffer - updateBuffer(); - + this->updateBuffer(); + if (this->fileBufferSize > 2) { // Now look for the special boundary case: if (this->fileBuffer.at(0) == (char)1 && lastByteZero2 && lastByteZero1) { - // Found a start code - the 1 byte is here and the two (or three) 0 bytes were in the last buffer - startCodeFound = true; + // Found a start code - the 1 byte is here and the two (or three) 0 bytes were in the last + // buffer + startCodeFound = true; nextStartCodePos = lastByteZero0 ? -3 : -2; - this->lastReturnArray.chop(lastByteZero0 ? 3 : 2); + this->lastDataAndPos.data.chop(lastByteZero0 ? 3 : 2); } - else if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)1 && lastByteZero2) + else if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)1 && + lastByteZero2) { - // Found a start code - the 01 bytes are here and the one (or two) 0 bytes were in the last buffer - startCodeFound = true; + // Found a start code - the 01 bytes are here and the one (or two) 0 bytes were in the + // last buffer + startCodeFound = true; nextStartCodePos = lastByteZero1 ? -2 : -1; - this->lastReturnArray.chop(lastByteZero0 ? 1 : 1); + this->lastDataAndPos.data.chop(lastByteZero0 ? 1 : 1); } - else if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)0 && this->fileBuffer.at(2) == (char)1) + else if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)0 && + this->fileBuffer.at(2) == (char)1) { // Found a start code - the 001 bytes are here. Check the last byte of the last buffer - startCodeFound = true; + startCodeFound = true; nextStartCodePos = lastByteZero2 ? -1 : 0; if (lastByteZero2) - this->lastReturnArray.chop(1); + this->lastDataAndPos.data.chop(1); } } @@ -176,32 +178,31 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, pairUint6 } // Position found - if (startEndPosInFile) - startEndPosInFile->second = this->bufferStartPosInFile + nextStartCodePos; + this->lastDataAndPos.startEnd.end = this->bufferStartPosInFile + nextStartCodePos; if (nextStartCodePos > int(this->posInBuffer)) - this->lastReturnArray += this->fileBuffer.mid(this->posInBuffer, nextStartCodePos - this->posInBuffer); + this->lastDataAndPos.data += + this->fileBuffer.mid(this->posInBuffer, nextStartCodePos - this->posInBuffer); this->posInBuffer = nextStartCodePos; - DEBUG_ANNEXBFILE("FileSourceAnnexBFile::getNextNALUnit start code found - ret size " << this->lastReturnArray.size()); - return this->lastReturnArray; + DEBUG_ANNEXBFILE("FileSourceAnnexBFile::getNextNALUnit start code found - ret size " + << this->lastDataAndPos.data.size()); + return this->lastDataAndPos; } -QByteArray FileSourceAnnexBFile::getFrameData(pairUint64 startEndFilePos) +QByteArray FileSourceAnnexBFile::getFrameData(const FileStartEndPos &startEndFilePos) { // Get all data for the frame (all NAL units in the raw format with start codes). - // We don't need to convert the format to the mp4 ISO format. The ffmpeg decoder can also accept raw NAL units. - // When the extradata is set as raw NAL units, the AVPackets must also be raw NAL units. + // We don't need to convert the format to the mp4 ISO format. The ffmpeg decoder can also accept + // raw NAL units. When the extradata is set as raw NAL units, the AVPackets must also be raw NAL + // units. QByteArray retArray; - - auto start = startEndFilePos.first; - auto end = startEndFilePos.second; // Seek the source file to the start position - this->seek(start); + this->seek(startEndFilePos.start); // Retrieve NAL units (and repackage them) until we reached out end position - while (end > this->bufferStartPosInFile + this->posInBuffer) + while (startEndFilePos.end > this->bufferStartPosInFile + this->posInBuffer) { - auto nalData = getNextNALUnit(); + auto [nalData, fileStartEndPos] = this->getNextNALUnit(); int headerOffset = 0; if (nalData.at(0) == (char)0 && nalData.at(1) == (char)0) @@ -228,15 +229,16 @@ bool FileSourceAnnexBFile::updateBuffer() this->bufferStartPosInFile += this->fileBufferSize; this->fileBufferSize = srcFile.read(this->fileBuffer.data(), BUFFERSIZE); - this->posInBuffer = 0; + this->posInBuffer = 0; - DEBUG_ANNEXBFILE("FileSourceAnnexBFile::updateBuffer this->fileBufferSize " << this->fileBufferSize); + DEBUG_ANNEXBFILE("FileSourceAnnexBFile::updateBuffer this->fileBufferSize " + << this->fileBufferSize); return (this->fileBufferSize > 0); } bool FileSourceAnnexBFile::seek(int64_t pos) { - if (!isFileOpened) + if (!this->srcFile.isOpen()) return false; DEBUG_ANNEXBFILE("FileSourceAnnexBFile::seek to " << pos); @@ -247,16 +249,18 @@ bool FileSourceAnnexBFile::seek(int64_t pos) // The file is empty of there was an error reading from the file. return false; this->bufferStartPosInFile = pos; - this->posInBuffer = 0; + this->posInBuffer = 0; if (pos == 0) this->seekToFirstNAL(); else { // Check if we are at a start code position (001 or 0001) - if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)0 && this->fileBuffer.at(2) == (char)0 && this->fileBuffer.at(3) == (char)1) + if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)0 && + this->fileBuffer.at(2) == (char)0 && this->fileBuffer.at(3) == (char)1) return true; - if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)0 && this->fileBuffer.at(2) == (char)1) + if (this->fileBuffer.at(0) == (char)0 && this->fileBuffer.at(1) == (char)0 && + this->fileBuffer.at(2) == (char)1) return true; DEBUG_ANNEXBFILE("FileSourceAnnexBFile::seek could not find start code at seek position"); diff --git a/YUViewLib/src/filesource/FileSourceAnnexBFile.h b/YUViewLib/src/filesource/FileSourceAnnexBFile.h index a2d486cb0..db942c88a 100644 --- a/YUViewLib/src/filesource/FileSourceAnnexBFile.h +++ b/YUViewLib/src/filesource/FileSourceAnnexBFile.h @@ -35,11 +35,7 @@ #include #include -/* This class is a normal FileSource for opening of raw AnnexBFiles. - * Basically it understands that this is a binary file where each unit starts with a start code - * (0x0000001) - * TODO: The reading / parsing could be performed in a background thread in order to increase the - * performance +/* Extends the FileSource to read nal units from a raw AnnexB file. */ class FileSourceAnnexBFile : public FileSource { @@ -52,8 +48,7 @@ class FileSourceAnnexBFile : public FileSource bool openFile(const QString &filePath) override; - // Is the file at the end? - bool atEnd() const override; + [[nodiscard]] bool atEnd() const override; // --- Retrieving of data from the file --- // You can either read a file NAL by NAL or frame by frame. Do not mix the two interfaces. @@ -64,22 +59,21 @@ class FileSourceAnnexBFile : public FileSource // Also return the start and end position of the NAL unit in the file so you can seek to it. // startEndPosInFile: The file positions of the first byte in the NAL header and the end position // of the last byte - QByteArray getNextNALUnit(bool getLastDataAgain = false, pairUint64 *startEndPosInFile = nullptr); + DataAndStartEndPos getNextNALUnit(bool getLastDataAgain = false); // Get all bytes that are needed to decode the next frame (from the given start to the given end // position) The data will be returned in the ISO/IEC 14496-15 format (4 bytes size followed by // the payload). - QByteArray getFrameData(pairUint64 startEndFilePos); + QByteArray getFrameData(const FileStartEndPos &startEndFilePos); - // Seek the file to the given byte position. Update the buffer. bool seek(int64_t pos) override; - uint64_t getNrBytesBeforeFirstNAL() const { return this->nrBytesBeforeFirstNAL; } + [[nodiscard]] uint64_t getNrBytesBeforeFirstNAL() const { return this->nrBytesBeforeFirstNAL; } protected: QByteArray fileBuffer; - uint64_t fileBufferSize{0}; ///< How many of the bytes are used? We don't resize the fileBuffer - uint64_t bufferStartPosInFile{ + int64_t fileBufferSize{0}; ///< How many of the bytes are used? We don't resize the fileBuffer + int64_t bufferStartPosInFile{ 0}; ///< The byte position in the file of the start of the currently loaded buffer // The current position in the input buffer in bytes. This always points to the first byte of a @@ -88,14 +82,11 @@ class FileSourceAnnexBFile : public FileSource // update the buffer and the start of the start code was in the previous buffer int64_t posInBuffer{0}; - // load the next buffer bool updateBuffer(); - - // Seek to the first NAL header in the bitstream void seekToFirstNAL(); // We will keep the last buffer in case the reader wants to get it again - QByteArray lastReturnArray; + DataAndStartEndPos lastDataAndPos; - uint64_t nrBytesBeforeFirstNAL{0}; + int64_t nrBytesBeforeFirstNAL{0}; }; diff --git a/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp b/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp index d66c9d431..633cd5361 100644 --- a/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp +++ b/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp @@ -127,7 +127,7 @@ QByteArray FileSourceFFmpegFile::getNextUnit(bool getLastDataAgain) { // Return the remainder of the buffer and clear it so that the next packet is loaded on the // next call - this->lastReturnArray = currentPacketData.mid(this->posInData + offset); + this->lastReturnArray = this->currentPacketData.mid(this->posInData + offset); this->currentPacketData.clear(); } else @@ -167,7 +167,7 @@ QByteArray FileSourceFFmpegFile::getNextUnit(bool getLastDataAgain) else if (this->packetDataFormat == PacketDataFormat::OBU) { SubByteReaderLogging reader( - SubByteReaderLogging::convertToByteVector(currentPacketData), nullptr, "", posInData); + SubByteReaderLogging::convertToByteVector(this->currentPacketData), nullptr, "", posInData); try { @@ -177,16 +177,16 @@ QByteArray FileSourceFFmpegFile::getNextUnit(bool getLastDataAgain) if (header.obu_has_size_field) { auto completeSize = header.obu_size + reader.nrBytesRead(); - this->lastReturnArray = currentPacketData.mid(posInData, completeSize); + this->lastReturnArray = this->currentPacketData.mid(posInData, completeSize); this->posInData += completeSize; - if (this->posInData >= currentPacketData.size()) + if (this->posInData >= this->currentPacketData.size()) this->currentPacketData.clear(); } else { // The OBU is the remainder of the input - this->lastReturnArray = currentPacketData.mid(posInData); - this->posInData = currentPacketData.size(); + this->lastReturnArray = this->currentPacketData.mid(posInData); + this->posInData = this->currentPacketData.size(); this->currentPacketData.clear(); } } @@ -235,98 +235,96 @@ QList FileSourceFFmpegFile::getParameterSets() QList retArray; - // Since the FFmpeg developers don't want to make it too easy, the extradata is organized - // differently depending on the codec. - auto codecID = this->ff.getCodecIDWrapper(video_stream.getCodecID()); - if (codecID.isHEVC()) + try { - if (extradata.at(0) == 1) + // Since the FFmpeg developers don't want to make it too easy, the extradata is organized + // differently depending on the codec. + auto codecID = this->ff.getCodecIDWrapper(video_stream.getCodecID()); + if (codecID.isHEVC()) { - // Internally, ffmpeg uses a custom format for the parameter sets (hvcC). - // The hvcC parameters come first, and afterwards, the "normal" parameter sets are sent. + if (extradata.at(0) == 1) + { + // Internally, ffmpeg uses a custom format for the parameter sets (hvcC). + // The hvcC parameters come first, and afterwards, the "normal" parameter sets are sent. + + // The first 22 bytes are fixed hvcC parameter set (see hvcc_write in libavformat hevc.c) + auto numOfArrays = int(extradata.at(22)); - // The first 22 bytes are fixed hvcC parameter set (see hvcc_write in libavformat hevc.c) - auto numOfArrays = int(extradata.at(22)); + int pos = 23; + for (int i = 0; i < numOfArrays; i++) + { + // The first byte contains the NAL unit type (which we don't use here). + pos++; + // int byte = (unsigned char)(extradata.at(pos++)); + // bool array_completeness = byte & (1 << 7); + // int nalUnitType = byte & 0x3f; + + // Two bytes numNalus + int numNalus = (unsigned char)(extradata.at(pos++)) << 7; + numNalus += (unsigned char)(extradata.at(pos++)); + + for (int j = 0; j < numNalus; j++) + { + // Two bytes nalUnitLength + int nalUnitLength = (unsigned char)(extradata.at(pos++)) << 7; + nalUnitLength += (unsigned char)(extradata.at(pos++)); + + // nalUnitLength bytes payload of the NAL unit + // This payload includes the NAL unit header + auto rawNAL = extradata.mid(pos, nalUnitLength); + retArray.append(rawNAL); + pos += nalUnitLength; + } + } + } + } + else if (codecID.isAVC()) + { + // Note: Actually we would only need this if we would feed the AVC bitstream to a different + // decoder then ffmpeg. + // So this function is so far not called (and not tested). - int pos = 23; - for (int i = 0; i < numOfArrays; i++) + // First byte is 1, length must be at least 7 bytes + if (extradata.at(0) == 1 && extradata.length() >= 7) { - // The first byte contains the NAL unit type (which we don't use here). - pos++; - // int byte = (unsigned char)(extradata.at(pos++)); - // bool array_completeness = byte & (1 << 7); - // int nalUnitType = byte & 0x3f; + int nrSPS = extradata.at(5) & 0x1f; + int pos = 6; + for (int i = 0; i < nrSPS; i++) + { + int nalUnitLength = (unsigned char)(extradata.at(pos++)) << 7; + nalUnitLength += (unsigned char)(extradata.at(pos++)); - // Two bytes numNalus - int numNalus = (unsigned char)(extradata.at(pos++)) << 7; - numNalus += (unsigned char)(extradata.at(pos++)); + auto rawNAL = extradata.mid(pos, nalUnitLength); + retArray.append(rawNAL); + pos += nalUnitLength; + } - for (int j = 0; j < numNalus; j++) + int nrPPS = extradata.at(pos++); + for (int i = 0; i < nrPPS; i++) { - // Two bytes nalUnitLength int nalUnitLength = (unsigned char)(extradata.at(pos++)) << 7; nalUnitLength += (unsigned char)(extradata.at(pos++)); - // nalUnitLength bytes payload of the NAL unit - // This payload includes the NAL unit header auto rawNAL = extradata.mid(pos, nalUnitLength); retArray.append(rawNAL); pos += nalUnitLength; } } } - } - else if (codecID.isAVC()) - { - // Note: Actually we would only need this if we would feed the AVC bitstream to a different - // decoder then ffmpeg. - // So this function is so far not called (and not tested). - - // First byte is 1, length must be at least 7 bytes - if (extradata.at(0) == 1 && extradata.length() >= 7) + else if (codecID.isAV1()) { - int nrSPS = extradata.at(5) & 0x1f; - int pos = 6; - for (int i = 0; i < nrSPS; i++) - { - int nalUnitLength = (unsigned char)(extradata.at(pos++)) << 7; - nalUnitLength += (unsigned char)(extradata.at(pos++)); - - auto rawNAL = extradata.mid(pos, nalUnitLength); - retArray.append(rawNAL); - pos += nalUnitLength; - } - - int nrPPS = extradata.at(pos++); - for (int i = 0; i < nrPPS; i++) - { - int nalUnitLength = (unsigned char)(extradata.at(pos++)) << 7; - nalUnitLength += (unsigned char)(extradata.at(pos++)); - - auto rawNAL = extradata.mid(pos, nalUnitLength); - retArray.append(rawNAL); - pos += nalUnitLength; - } + // This should be a normal OBU for the seuqence header starting with the OBU header + SubByteReaderLogging reader(SubByteReaderLogging::convertToByteVector(extradata), nullptr); + parser::av1::obu_header header; + header.parse(reader); + if (header.obu_type == parser::av1::ObuType::OBU_SEQUENCE_HEADER) + retArray.append(extradata); } } - else if (codecID.isAV1()) + catch (const std::exception &e) { - // This should be a normal OBU for the seuqence header starting with the OBU header - SubByteReaderLogging reader(SubByteReaderLogging::convertToByteVector(extradata), nullptr); - parser::av1::obu_header header; - try - { - header.parse(reader); - } - catch (const std::exception &e) - { - (void)e; - DEBUG_FFMPEG("Error parsing OBU header " + e.what()); - return retArray; - } - - if (header.obu_type == parser::av1::ObuType::OBU_SEQUENCE_HEADER) - retArray.append(extradata); + (void)e; + DEBUG_FFMPEG("Failed to parse extradata: " << e.what()); } return retArray; @@ -483,8 +481,6 @@ void FileSourceFFmpegFile::openFileAndFindVideoStream(QString fileName) if (!this->ff.openInput(this->formatCtx, fileName)) return; - this->formatCtx.getInputFormat(); - for (unsigned idx = 0; idx < this->formatCtx.getNbStreams(); idx++) { auto stream = this->formatCtx.getStream(idx); @@ -655,52 +651,31 @@ indexRange FileSourceFFmpegFile::getDecodableFrameLimits() const return range; } -QList FileSourceFFmpegFile::getFileInfoForAllStreams() +StringPairVec FileSourceFFmpegFile::getGeneralInfo() const { - QList info; - - info += formatCtx.getInfoText(); - for (unsigned i = 0; i < this->formatCtx.getNbStreams(); i++) - { - auto stream = this->formatCtx.getStream(i); - auto codecIdWrapper = this->ff.getCodecIDWrapper(stream.getCodecID()); - info += stream.getInfoText(codecIdWrapper); - } - - return info; + return this->formatCtx.getInfoText(); } -QList FileSourceFFmpegFile::getTimeBaseAllStreams() +StreamsInfo FileSourceFFmpegFile::getStreamsInfo() const { - QList timeBaseList; + StreamsInfo info; for (unsigned i = 0; i < this->formatCtx.getNbStreams(); i++) { - auto stream = this->formatCtx.getStream(i); - timeBaseList.append(stream.getTimeBase()); - } - - return timeBaseList; -} - -QList FileSourceFFmpegFile::getShortStreamDescriptionAllStreams() -{ - QList descriptions; - - for (unsigned i = 0; i < formatCtx.getNbStreams(); i++) - { - QString description; - auto stream = this->formatCtx.getStream(i); - description = stream.getCodecTypeName(); + StreamInfo streamInfo; + auto stream = this->formatCtx.getStream(i); + auto codecID = this->ff.getCodecIDWrapper(stream.getCodecID()); - auto codecID = this->ff.getCodecIDWrapper(stream.getCodecID()); - description += " " + codecID.getCodecName(); + streamInfo.streamName = "Stream " + std::to_string(i); + streamInfo.infoItems = stream.getInfoText(codecID); + streamInfo.timebase = stream.getTimeBase(); - description += - QString(" (%1x%2)").arg(stream.getFrameSize().width).arg(stream.getFrameSize().height); + streamInfo.shortInfo = stream.getCodecTypeName(); + streamInfo.shortInfo += " " + codecID.getCodecName(); - descriptions.append(description); + streamInfo.shortInfo += " (" + std::to_string(stream.getFrameSize().width) + "x" + + std::to_string(stream.getFrameSize().height) + ")"; } - return descriptions; + return info; } diff --git a/YUViewLib/src/filesource/FileSourceFFmpegFile.h b/YUViewLib/src/filesource/FileSourceFFmpegFile.h index 551d1e45c..94b2b7f01 100644 --- a/YUViewLib/src/filesource/FileSourceFFmpegFile.h +++ b/YUViewLib/src/filesource/FileSourceFFmpegFile.h @@ -37,8 +37,8 @@ #include #include #include -#include