From 2c5dd106d3b36a6c334dc07d49c9f0c1adeeb421 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 29 Aug 2022 19:12:56 +0200 Subject: [PATCH 01/13] Refactor dav1d decoder a bit. Move some internal functions to anonymouse namespace --- YUViewLib/src/decoder/decoderDav1d.cpp | 950 ++++++++++++------------- YUViewLib/src/decoder/decoderDav1d.h | 18 +- 2 files changed, 474 insertions(+), 494 deletions(-) diff --git a/YUViewLib/src/decoder/decoderDav1d.cpp b/YUViewLib/src/decoder/decoderDav1d.cpp index 7fd21f7e5..38f64deea 100644 --- a/YUViewLib/src/decoder/decoderDav1d.cpp +++ b/YUViewLib/src/decoder/decoderDav1d.cpp @@ -87,6 +87,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 +477,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()); 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 +506,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 @@ -167,58 +534,59 @@ QStringList decoderDav1d::getSignalNames() const 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(QStringLiteral("Error loading the libde265 library: Can't find function %1.") + .arg(symbol)); return nullptr; } @@ -227,41 +595,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 +639,110 @@ 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(); - int res = this->lib.dav1d_get_picture(decoder, curPicture.getPicture()); + auto res = this->lib.dav1d_get_picture(this->decoder, this->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."); + 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 +750,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 +769,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 +788,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,104 +805,41 @@ 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) + int err = this->lib.dav1d_send_data(decoder, dav1dData.get()); + 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) { decoderDav1d testDecoder; @@ -775,317 +1082,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..2f98a775d 100644 --- a/YUViewLib/src/decoder/decoderDav1d.h +++ b/YUViewLib/src/decoder/decoderDav1d.h @@ -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; }; From 07296c0f8dda809b568465afee6b3f1a74101464 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 29 Aug 2022 19:13:22 +0200 Subject: [PATCH 02/13] Use the OBU parsing function that we already have. The previous one also had a bug. --- YUViewLib/src/ffmpeg/AVPacketWrapper.cpp | 33 +++++------------------- 1 file changed, 7 insertions(+), 26 deletions(-) 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(); } From 4bf97cd288504c4e1d79dd249f15f09c5141d0c1 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 29 Aug 2022 19:13:49 +0200 Subject: [PATCH 03/13] Add a try to extradata parsing. It may fail. --- .../src/filesource/FileSourceFFmpegFile.cpp | 142 +++++++++--------- 1 file changed, 75 insertions(+), 67 deletions(-) diff --git a/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp b/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp index 1d73123bf..fc842e04d 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(); } } @@ -231,88 +231,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; - header.parse(reader); - 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; From fe0d87e2eacb1c5b4f8fc8245accf5200f703e06 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 29 Aug 2022 19:14:05 +0200 Subject: [PATCH 04/13] Formatting --- YUViewLib/src/parser/AV1/AV1OBU.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/YUViewLib/src/parser/AV1/AV1OBU.cpp b/YUViewLib/src/parser/AV1/AV1OBU.cpp index 183f6fdc6..10a0d5a06 100644 --- a/YUViewLib/src/parser/AV1/AV1OBU.cpp +++ b/YUViewLib/src/parser/AV1/AV1OBU.cpp @@ -44,7 +44,7 @@ using namespace av1; ParserAV1OBU::ParserAV1OBU(QObject *parent) : Base(parent) { - decValues.PrevFrameID = -1; + this->decValues.PrevFrameID = -1; } std::pair ParserAV1OBU::parseAndAddOBU(int obuID, @@ -76,7 +76,7 @@ std::pair ParserAV1OBU::parseAndAddOBU(int if (obu.header.obu_type == ObuType::OBU_TEMPORAL_DELIMITER) { decValues.SeenFrameHeader = false; - obuTypeName = "Temporal Delimiter"; + obuTypeName = "Temporal Delimiter"; } else if (obu.header.obu_type == ObuType::OBU_SEQUENCE_HEADER) { @@ -104,7 +104,7 @@ std::pair ParserAV1OBU::parseAndAddOBU(int } catch (const std::exception &e) { - errorText = " ERROR " + std::string(e.what()); + errorText = " ERROR " + std::string(e.what()); obuTypeName = "Error"; } From 6f0c6216ccf5409e9162b8d9c8ef8614e1e21339 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 29 Aug 2022 19:32:09 +0200 Subject: [PATCH 05/13] Debug build fix. --- YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp index e3fa3b9ff..558dbb092 100644 --- a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp +++ b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp @@ -177,7 +177,7 @@ playlistItemCompressedVideo::playlistItemCompressedVideo(const QString &compress << frameSize.width << "x" << frameSize.height); formatYuv = this->inputFileAnnexBParser->getPixelFormat(); DEBUG_COMPRESSED("playlistItemCompressedVideo::playlistItemCompressedVideo YUV format " - << this->formatYuv.getName().c_str()); + << formatYuv.getName().c_str()); this->rawFormat = video::RawFormat::YUV; this->prop.frameRate = this->inputFileAnnexBParser->getFramerate(); DEBUG_COMPRESSED("playlistItemCompressedVideo::playlistItemCompressedVideo framerate " @@ -204,7 +204,7 @@ playlistItemCompressedVideo::playlistItemCompressedVideo(const QString &compress // Is this file RGB or YUV? this->rawFormat = this->inputFileFFmpegLoading->getRawFormat(); DEBUG_COMPRESSED("playlistItemCompressedVideo::playlistItemCompressedVideo Raw format " - << (this->rawFormat == raw_YUV ? "YUV" + << (this->rawFormat == video::RawFormat::YUV ? "YUV" : this->rawFormat == video::RawFormat::RGB ? "RGB" : "Unknown")); if (this->rawFormat == video::RawFormat::YUV) From c2868fbfbae9121083e60f2a4acf80ef0153686b Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 29 Aug 2022 19:52:30 +0200 Subject: [PATCH 06/13] Missing header --- YUViewLib/src/decoder/decoderDav1d.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/YUViewLib/src/decoder/decoderDav1d.cpp b/YUViewLib/src/decoder/decoderDav1d.cpp index 38f64deea..dfe78f139 100644 --- a/YUViewLib/src/decoder/decoderDav1d.cpp +++ b/YUViewLib/src/decoder/decoderDav1d.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include From 10fb44d63f4608e8076db0c2f098b33f12dd7045 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 12 Sep 2022 12:28:27 +0200 Subject: [PATCH 07/13] Debugging why dav1d sometimes stalls --- YUViewLib/src/decoder/decoderDav1d.cpp | 17 ++++++++++++++--- .../playlistItemCompressedVideo.cpp | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/YUViewLib/src/decoder/decoderDav1d.cpp b/YUViewLib/src/decoder/decoderDav1d.cpp index dfe78f139..1574c2833 100644 --- a/YUViewLib/src/decoder/decoderDav1d.cpp +++ b/YUViewLib/src/decoder/decoderDav1d.cpp @@ -48,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 @@ -673,7 +673,10 @@ bool decoderDav1d::decodeFrame() 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"); + if (res >= 0) { // We did get a picture @@ -710,12 +713,18 @@ bool decoderDav1d::decodeFrame() this->currentOutputBuffer.clear(); return true; } - else if (res != -EAGAIN) + 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 (this->decoderState != DecoderState::NeedsMoreData) + { DEBUG_DAV1D("decoderDav1d::decodeFrame No frame available - switching back to data push mode"); - this->decoderState = DecoderState::NeedsMoreData; + this->decoderState = DecoderState::NeedsMoreData; + } return false; } @@ -816,7 +825,9 @@ bool decoderDav1d::pushData(QByteArray &data) auto rawDataPointer = this->lib.dav1d_data_create(dav1dData.get(), data.size()); memcpy(rawDataPointer, data.data(), data.size()); + 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(); diff --git a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp index 558dbb092..6d65c62ce 100644 --- a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp +++ b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp @@ -60,7 +60,7 @@ using namespace functions; using namespace decoder; -#define COMPRESSED_VIDEO_DEBUG_OUTPUT 0 +#define COMPRESSED_VIDEO_DEBUG_OUTPUT 1 #if COMPRESSED_VIDEO_DEBUG_OUTPUT #include #define DEBUG_COMPRESSED(f) qDebug() << f From 6138213b3d2ac75718c4fb1e36bb68071f72a2f3 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Sat, 29 Oct 2022 10:48:36 +0200 Subject: [PATCH 08/13] First work to add parsing of raw OBU files --- YUViewLib/src/filesource/FileSource.cpp | 31 ++-- YUViewLib/src/filesource/FileSource.h | 37 +++-- .../src/filesource/FileSourceAnnexBFile.cpp | 150 ++++++++++-------- .../src/filesource/FileSourceAnnexBFile.h | 15 +- .../src/filesource/FileSourceOBUFile.cpp | 103 ++++++++++++ YUViewLib/src/filesource/FileSourceOBUFile.h | 58 +++++++ YUViewLib/src/parser/AnnexB.cpp | 27 ++-- YUViewLib/src/parser/AnnexB.h | 12 +- .../playlistItemCompressedVideo.cpp | 25 +-- .../playlistItemCompressedVideo.h | 6 +- .../src/playlistitem/playlistItemRawFile.cpp | 13 +- .../src/statistics/StatisticsFileBase.cpp | 8 +- .../src/statistics/StatisticsFileCSV.cpp | 4 +- .../src/statistics/StatisticsFileVTMBMS.cpp | 4 +- 14 files changed, 339 insertions(+), 154 deletions(-) create mode 100644 YUViewLib/src/filesource/FileSourceOBUFile.cpp create mode 100644 YUViewLib/src/filesource/FileSourceOBUFile.h 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..d7347ed46 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,17 @@ const auto InputFormatMapper = EnumMapper({{InputFormat::Invalid, " {InputFormat::AnnexBHEVC, "AnnexBHEVC"}, {InputFormat::AnnexBAVC, "AnnexBAVC"}, {InputFormat::AnnexBVVC, "AnnexBVVC"}, + {InputFormat::OBUAV1, "OBUAV1"}, {InputFormat::Libav, "Libav"}}); +struct FileStartEndPos +{ + int64_t start{}; + int64_t end{}; +}; + +using DataAndStartEndPos = std::pair; + /* 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 +81,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 +128,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..d01f44331 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,7 +88,8 @@ void FileSourceAnnexBFile::seekToFirstNAL() this->nrBytesBeforeFirstNAL = this->bufferStartPosInFile + uint64_t(this->posInBuffer); } -QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, pairUint64 *startEndPosInFile) +QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, + pairUint64 *startEndPosInFile) { if (getLastDataAgain) return this->lastReturnArray; @@ -101,14 +99,15 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, pairUint6 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)); } @@ -117,8 +116,10 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, pairUint6 if (nextStartCodePos < 0 || (uint64_t)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->lastReturnArray += + this->fileBuffer.mid(this->posInBuffer, this->fileBufferSize - this->posInBuffer); + DEBUG_ANNEXBFILE("FileSourceHEVCAnnexBFile::getNextNALUnit no start code found - ret size " + << this->lastReturnArray.size()); if (this->fileBufferSize < BUFFERSIZE) { @@ -129,35 +130,40 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, pairUint6 return this->lastReturnArray; } - // 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(); - + 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); } - 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); } - 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); @@ -179,21 +185,24 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, pairUint6 if (startEndPosInFile) startEndPosInFile->second = this->bufferStartPosInFile + nextStartCodePos; if (nextStartCodePos > int(this->posInBuffer)) - this->lastReturnArray += this->fileBuffer.mid(this->posInBuffer, nextStartCodePos - this->posInBuffer); + this->lastReturnArray += + this->fileBuffer.mid(this->posInBuffer, nextStartCodePos - this->posInBuffer); this->posInBuffer = nextStartCodePos; - DEBUG_ANNEXBFILE("FileSourceAnnexBFile::getNextNALUnit start code found - ret size " << this->lastReturnArray.size()); + DEBUG_ANNEXBFILE("FileSourceAnnexBFile::getNextNALUnit start code found - ret size " + << this->lastReturnArray.size()); return this->lastReturnArray; } QByteArray FileSourceAnnexBFile::getFrameData(pairUint64 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; + auto end = startEndFilePos.second; // Seek the source file to the start position this->seek(start); @@ -201,7 +210,7 @@ QByteArray FileSourceAnnexBFile::getFrameData(pairUint64 startEndFilePos) // Retrieve NAL units (and repackage them) until we reached out end position while (end > this->bufferStartPosInFile + this->posInBuffer) { - auto nalData = getNextNALUnit(); + auto nalData = this->getNextNALUnit(); int headerOffset = 0; if (nalData.at(0) == (char)0 && nalData.at(1) == (char)0) @@ -228,15 +237,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 +257,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..267b4c4b5 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. @@ -71,10 +66,9 @@ class FileSourceAnnexBFile : public FileSource // the payload). QByteArray getFrameData(pairUint64 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; @@ -88,10 +82,7 @@ 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 diff --git a/YUViewLib/src/filesource/FileSourceOBUFile.cpp b/YUViewLib/src/filesource/FileSourceOBUFile.cpp new file mode 100644 index 000000000..3afc80697 --- /dev/null +++ b/YUViewLib/src/filesource/FileSourceOBUFile.cpp @@ -0,0 +1,103 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "FileSourceOBUFile.h" + +#include +#include + +#define OBUFILE_DEBUG_OUTPUT 0 +#if OBUFILE_DEBUG_OUTPUT && !NDEBUG +#include +#define DEBUG_OBUFILE(f) qDebug() << f +#else +#define DEBUG_OBUBFILE(f) ((void)0) +#endif + +const auto BUFFERSIZE = 500'000; + +using SubByteReaderLogging = parser::reader::SubByteReaderLogging; + +bool FileSourceOBUFile::openFile(const QString &fileName) +{ + DEBUG_OBUBFILE("FileSourceOBUFile::openFile fileName " << fileName); + + FileSource::openFile(fileName); + + auto [firstOBUData, startEnd] = this->getNextOBU(); + if (firstOBUData.isEmpty() || firstOBUData.size() > 200) + { + DEBUG_OBUBFILE("Invalid data size (" << firstOBUData.size() ") for first OBU."); + this->srcFile.close(); + return false; + } + + return true; +} + +DataAndStartEndPos FileSourceOBUFile::getNextOBU(bool getLastDataAgain = false) +{ + FileStartEndPos fileStartEndPos; + fileStartEndPos.start = this->pos(); + auto data = this->srcFile.read(10); + + auto obuHeaderAndSize = SubByteReaderLogging::convertToByteVector(data); + if (obuHeaderAndSize.size() < 2) + { + DEBUG_OBUBFILE("Error reading OBU header byte from file"); + return {}; + } + + SubByteReaderLogging reader(obuHeaderAndSize, nullptr); + parser::av1::obu_header header; + try + { + header.parse(reader); + } + catch (...) + { + DEBUG_OBUBFILE("Parsing of OBU header from bytes failed"); + return {}; + } + + if (!header.obu_has_size_field) + { + DEBUG_OBUBFILE("Raw OBU files must have size field"); + return {}; + } + + fileStartEndPos.end = fileStartEndPos.start + header.obu_size; + + // Todo: Read the rest of the needed bytes. + + return {data, fileStartEndPos}; +} diff --git a/YUViewLib/src/filesource/FileSourceOBUFile.h b/YUViewLib/src/filesource/FileSourceOBUFile.h new file mode 100644 index 000000000..251804ebe --- /dev/null +++ b/YUViewLib/src/filesource/FileSourceOBUFile.h @@ -0,0 +1,58 @@ +/* This file is part of YUView - The YUV player with advanced analytics toolset + * + * Copyright (C) 2015 Institut für Nachrichtentechnik, RWTH Aachen University, GERMANY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of portions of this program with the + * OpenSSL library under certain conditions as described in each + * individual source file, and distribute linked combinations including + * the two. + * + * You must obey the GNU General Public License in all respects for all + * of the code used other than OpenSSL. If you modify file(s) with this + * exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do + * so, delete this exception statement from your version. If you delete + * this exception statement from all source files in the program, then + * also delete it here. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +/* + */ +class FileSourceOBUFile : public FileSource +{ + Q_OBJECT + +public: + FileSourceOBUFile() = default; + FileSourceOBUFile(const QString &filePath) : FileSource() { this->openFile(filePath); } + ~FileSourceOBUFile() = default; + + bool openFile(const QString &filePath) override; + + // Get the next OBU. Also return the start and end position of the OBU in the file so + // you can seek to it. + [[nodiscard]] DataAndStartEndPos getNextOBU(bool getLastDataAgain = false); + +protected: + // We will keep the last buffer in case the reader wants to get it again + QByteArray lastReturnArray; +}; diff --git a/YUViewLib/src/parser/AnnexB.cpp b/YUViewLib/src/parser/AnnexB.cpp index 474c05ecb..902d6ea67 100644 --- a/YUViewLib/src/parser/AnnexB.cpp +++ b/YUViewLib/src/parser/AnnexB.cpp @@ -167,8 +167,8 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge progressDialog->setWindowModality(Qt::WindowModal); } - stream_info.file_size = file->getFileSize(); - stream_info.parsing = true; + this->streamInfo.fileSize = file->getFileSize(); + this->streamInfo.parsing = true; emit streamInfoUpdated(); // Just push all NAL units from the annexBFile into the annexBParser @@ -181,8 +181,8 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge { // Update the progress dialog int64_t pos = file->pos(); - if (stream_info.file_size > 0) - progressPercentValue = functions::clip((int)(pos * 100 / stream_info.file_size), 0, 100); + if (this->streamInfo.fileSize > 0) + progressPercentValue = functions::clip((int)(pos * 100 / this->streamInfo.fileSize), 0, 100); try { @@ -265,9 +265,9 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge if (packetModel) emit modelDataUpdated(); - stream_info.parsing = false; - stream_info.nr_nal_units = nalID; - stream_info.nr_frames = unsigned(this->frameListCodingOrder.size()); + this->streamInfo.parsing = false; + this->streamInfo.nrNALUnits = nalID; + this->streamInfo.nrFrames = unsigned(this->frameListCodingOrder.size()); emit streamInfoUpdated(); emit backgroundParsingDone(""); @@ -281,11 +281,12 @@ bool AnnexB::runParsingOfFile(QString compressedFilePath) return this->parseAnnexBFile(file); } -QList AnnexB::stream_info_type::getStreamInfo() +QList AnnexB::StreamInfo::getStreamInfo() { QList infoList; - infoList.append(new QTreeWidgetItem(QStringList() << "File size" << QString::number(file_size))); - if (parsing) + infoList.append( + new QTreeWidgetItem(QStringList() << "File size" << QString::number(this->fileSize))); + if (this->parsing) { infoList.append(new QTreeWidgetItem(QStringList() << "Number NAL units" << "Parsing...")); @@ -294,10 +295,10 @@ QList AnnexB::stream_info_type::getStreamInfo() } else { + infoList.append(new QTreeWidgetItem(QStringList() << "Number NAL units" + << QString::number(this->nrNALUnits))); infoList.append( - new QTreeWidgetItem(QStringList() << "Number NAL units" << QString::number(nr_nal_units))); - infoList.append( - new QTreeWidgetItem(QStringList() << "Number Frames" << QString::number(nr_frames))); + new QTreeWidgetItem(QStringList() << "Number Frames" << QString::number(this->nrFrames))); } return infoList; diff --git a/YUViewLib/src/parser/AnnexB.h b/YUViewLib/src/parser/AnnexB.h index 10e80413b..25b8cafc2 100644 --- a/YUViewLib/src/parser/AnnexB.h +++ b/YUViewLib/src/parser/AnnexB.h @@ -67,7 +67,7 @@ class AnnexB : public Base // Clear all knowledge about the bitstream. void clearData(); - QList getStreamInfo() override { return stream_info.getStreamInfo(); } + QList getStreamInfo() override { return this->streamInfo.getStreamInfo(); } unsigned int getNrStreams() override { return 1; } QString getShortStreamDescription(int streamIndex) const override; @@ -156,16 +156,16 @@ class AnnexB : public Base int pocOfFirstRandomAccessFrame{-1}; // Save general information about the file here - struct stream_info_type + struct StreamInfo { QList getStreamInfo(); - size_t file_size; - unsigned nr_nal_units{0}; - unsigned nr_frames{0}; + size_t fileSize; + unsigned nrNALUnits{0}; + unsigned nrFrames{0}; bool parsing{false}; }; - stream_info_type stream_info; + StreamInfo streamInfo; int getFramePOC(FrameIndexDisplayOrder frameIdx); diff --git a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp index 6d65c62ce..256bd3761 100644 --- a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp +++ b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp @@ -77,11 +77,6 @@ bool isInputFormatTypeAnnexB(InputFormat format) format == InputFormat::AnnexBAVC; } -bool isInputFormatTypeFFmpeg(InputFormat format) -{ - return format == InputFormat::Libav; -} - enum class Codec { AV1, @@ -119,13 +114,15 @@ playlistItemCompressedVideo::playlistItemCompressedVideo(const QString &compress // Open the input file and get some properties (size, bit depth, subsampling) from the file if (input == InputFormat::Invalid) { - QString ext = QFileInfo(compressedFilePath).suffix(); + auto ext = QFileInfo(compressedFilePath).suffix(); if (ext == "hevc" || ext == "h265" || ext == "265") this->inputFormat = InputFormat::AnnexBHEVC; else if (ext == "vvc" || ext == "h266" || ext == "266") this->inputFormat = InputFormat::AnnexBVVC; else if (ext == "avc" || ext == "h264" || ext == "264") this->inputFormat = InputFormat::AnnexBAVC; + else if (ext == "obu") + this->inputFormat = InputFormat::OBUAV1; else this->inputFormat = InputFormat::Libav; } @@ -190,6 +187,12 @@ playlistItemCompressedVideo::playlistItemCompressedVideo(const QString &compress "playlistItemCompressedVideo::playlistItemCompressedVideo sample aspect ratio (" << this->prop.sampleAspectRatio.num << "," << this->prop.sampleAspectRatio.den << ")"); } + else if (this->inputFormat == InputFormat::OBUAV1) + { + DEBUG_COMPRESSED( + "playlistItemCompressedVideo::playlistItemCompressedVideo Opening OBU AV1 file"); + + } else { // Try ffmpeg to open the file @@ -676,8 +679,7 @@ void playlistItemCompressedVideo::loadRawData(int frameIdx, bool caching) while (dec->state() == decoder::DecoderState::NeedsMoreData) { DEBUG_COMPRESSED("playlistItemCompressedVideo::loadRawData decoder needs more data"); - if (isInputFormatTypeFFmpeg(this->inputFormat) && - this->decoderEngine == DecoderEngine::FFMpeg) + if (this->inputFormat == InputFormat::Libav && this->decoderEngine == DecoderEngine::FFMpeg) { // In this scenario, we can read and push AVPackets // from the FFmpeg file and pass them to the FFmpeg decoder directly. @@ -756,7 +758,7 @@ void playlistItemCompressedVideo::loadRawData(int frameIdx, bool caching) << data.size()); this->repushData = !dec->pushData(data); } - else if (isInputFormatTypeFFmpeg(this->inputFormat) && + else if (this->inputFormat == InputFormat::Libav && this->decoderEngine != DecoderEngine::FFMpeg) { // Get the next unit (NAL or OBU) form ffmepg and push it to the decoder @@ -1187,9 +1189,10 @@ void playlistItemCompressedVideo::getSupportedFileExtensions(QStringList &allExt << "webm" << "xmv" << "ts" - << "mxf"; + << "mxf" + << "obu"; QString filtersString = "FFmpeg files ("; - for (QString e : ext) + for (const auto &e : ext) filtersString.append(QString("*.%1").arg(e)); filtersString.append(")"); diff --git a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h index b04913a64..998e54630 100644 --- a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h +++ b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -144,8 +145,11 @@ class playlistItemCompressedVideo : public playlistItemWithVideo // count how many frames we already read. int readAnnexBFrameCounterCodingOrder{-1}; + // The same applies for Raw OBU files. We open the input twice (for caching and loading). The + // parser is only needed once. + // Which type is the input? - InputFormat inputFormat; + InputFormat inputFormat{InputFormat::Invalid}; FFmpeg::AVCodecIDWrapper ffmpegCodec; // For FFMpeg files we don't need a reader to parse them. But if the container contains a diff --git a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp index f2b5874de..10e4fe3b1 100644 --- a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp +++ b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp @@ -60,9 +60,7 @@ playlistItemRawFile::playlistItemRawFile(const QString &rawFilePath, this->prop.isFileSource = true; this->prop.propertiesWidgetTitle = "Raw File Properties"; - this->dataSource.openFile(rawFilePath); - - if (!this->dataSource.isOk()) + if (!this->dataSource.openFile(rawFilePath)) { // Opening the file failed. this->setError("Error opening the input file."); @@ -148,7 +146,7 @@ playlistItemRawFile::playlistItemRawFile(const QString &rawFilePath, void playlistItemRawFile::updateStartEndRange() { - if (!this->dataSource.isOk() || !this->video->isFormatValid()) + if (!this->dataSource.isOpen() || !this->video->isFormatValid()) { this->prop.startEndRange = indexRange(-1, -1); return; @@ -184,7 +182,7 @@ InfoData playlistItemRawFile::getInfo() const info.items.append( InfoItem("Bytes per Frame", QString("%1").arg(this->video->getBytesPerFrame()))); - if (this->dataSource.isOk() && this->video->isFormatValid() && !this->isY4MFile) + if (this->dataSource.isOpen() && this->video->isFormatValid() && !this->isY4MFile) { // Check if the size of the file and the number of bytes per frame can be divided // without any remainder. If not, then there is probably something wrong with the @@ -566,10 +564,7 @@ void playlistItemRawFile::getSupportedFileExtensions(QStringList &allExtensions, void playlistItemRawFile::reloadItemSource() { - // Reopen the file - this->dataSource.openFile(this->properties().name); - if (!this->dataSource.isOk()) - // Opening the file failed. + if (!this->dataSource.openFile(this->properties().name)) return; this->video->invalidateAllBuffers(); diff --git a/YUViewLib/src/statistics/StatisticsFileBase.cpp b/YUViewLib/src/statistics/StatisticsFileBase.cpp index 0c75aad61..996ea7fdb 100644 --- a/YUViewLib/src/statistics/StatisticsFileBase.cpp +++ b/YUViewLib/src/statistics/StatisticsFileBase.cpp @@ -37,15 +37,17 @@ namespace stats StatisticsFileBase::StatisticsFileBase(const QString &filename) { - this->file.openFile(filename); - if (!this->file.isOk()) + if (!this->file.openFile(filename)) { this->errorMessage = "Error opening file " + filename; this->error = true; } } -StatisticsFileBase::~StatisticsFileBase() { this->abortParsingDestroy = true; } +StatisticsFileBase::~StatisticsFileBase() +{ + this->abortParsingDestroy = true; +} InfoData StatisticsFileBase::getInfo() const { diff --git a/YUViewLib/src/statistics/StatisticsFileCSV.cpp b/YUViewLib/src/statistics/StatisticsFileCSV.cpp index 2a95df5ac..1a3f3f229 100644 --- a/YUViewLib/src/statistics/StatisticsFileCSV.cpp +++ b/YUViewLib/src/statistics/StatisticsFileCSV.cpp @@ -231,7 +231,7 @@ void StatisticsFileCSV::readFrameAndTypePositionsFromFile(std::atomic_bool &brea void StatisticsFileCSV::loadStatisticData(StatisticsData &statisticsData, int poc, int typeID) { - if (!this->file.isOk()) + if (!this->file.isOpen()) return; try @@ -348,7 +348,7 @@ void StatisticsFileCSV::readHeaderFromFile(StatisticsData &statisticsData) // We should get rid of this and just set an error and return on failure. try { - if (!this->file.isOk()) + if (!this->file.isOpen()) return; statisticsData.clear(); diff --git a/YUViewLib/src/statistics/StatisticsFileVTMBMS.cpp b/YUViewLib/src/statistics/StatisticsFileVTMBMS.cpp index 644a9b0fe..f0fec2535 100644 --- a/YUViewLib/src/statistics/StatisticsFileVTMBMS.cpp +++ b/YUViewLib/src/statistics/StatisticsFileVTMBMS.cpp @@ -184,7 +184,7 @@ void StatisticsFileVTMBMS::readFrameAndTypePositionsFromFile(std::atomic_bool &b void StatisticsFileVTMBMS::loadStatisticData(StatisticsData &statisticsData, int poc, int typeID) { - if (!this->file.isOk()) + if (!this->file.isOpen()) return; try @@ -417,7 +417,7 @@ void StatisticsFileVTMBMS::readHeaderFromFile(StatisticsData &statisticsData) { try { - if (!this->file.isOk()) + if (!this->file.isOpen()) return; statisticsData.clear(); From 9f2dd1d3497324a0d6c26840f14c9512cf5cac20 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Fri, 11 Nov 2022 20:37:01 +0100 Subject: [PATCH 09/13] More work --- YUViewLib/src/common/Typedef.h | 13 ++ YUViewLib/src/filesource/FileSource.h | 8 +- .../src/filesource/FileSourceAnnexBFile.cpp | 52 +++---- .../src/filesource/FileSourceAnnexBFile.h | 12 +- YUViewLib/src/filesource/FileSourceOBUFile.h | 2 +- YUViewLib/src/parser/AV1/AV1OBU.cpp | 10 +- YUViewLib/src/parser/AV1/AV1OBU.h | 12 +- YUViewLib/src/parser/AV1/OpenBitstreamUnit.h | 6 +- YUViewLib/src/parser/AVC/AnnexBAVC.cpp | 36 ++--- YUViewLib/src/parser/AVC/AnnexBAVC.h | 16 +- YUViewLib/src/parser/AVC/NalUnitAVC.h | 4 +- YUViewLib/src/parser/AVFormat/AVFormat.cpp | 6 +- YUViewLib/src/parser/AnnexB.cpp | 58 +++---- YUViewLib/src/parser/AnnexB.h | 48 +----- YUViewLib/src/parser/Base.cpp | 87 +++++++---- YUViewLib/src/parser/Base.h | 141 +++++++++++------- YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp | 26 ++-- YUViewLib/src/parser/HEVC/AnnexBHEVC.h | 17 +-- YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp | 46 +++--- YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h | 10 +- YUViewLib/src/parser/Mpeg2/NalUnitMpeg2.h | 4 +- YUViewLib/src/parser/VVC/AnnexBVVC.cpp | 12 +- YUViewLib/src/parser/VVC/AnnexBVVC.h | 20 +-- .../playlistItemCompressedVideo.cpp | 8 +- .../playlistItemCompressedVideo.h | 4 + 25 files changed, 337 insertions(+), 321 deletions(-) diff --git a/YUViewLib/src/common/Typedef.h b/YUViewLib/src/common/Typedef.h index 9bbf63f78..963dfa4b5 100644 --- a/YUViewLib/src/common/Typedef.h +++ b/YUViewLib/src/common/Typedef.h @@ -314,6 +314,19 @@ struct Offset int y{}; }; +struct FileStartEndPos +{ + int64_t start{}; + int64_t end{}; +}; + +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/filesource/FileSource.h b/YUViewLib/src/filesource/FileSource.h index d7347ed46..7eef5cf3c 100644 --- a/YUViewLib/src/filesource/FileSource.h +++ b/YUViewLib/src/filesource/FileSource.h @@ -60,14 +60,12 @@ const auto InputFormatMapper = EnumMapper({{InputFormat::Invalid, " {InputFormat::OBUAV1, "OBUAV1"}, {InputFormat::Libav, "Libav"}}); -struct FileStartEndPos +struct DataAndStartEndPos { - int64_t start{}; - int64_t end{}; + QByteArray data{}; + FileStartEndPos startEnd{}; }; -using DataAndStartEndPos = std::pair; - /* 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. diff --git a/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp b/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp index d01f44331..b5889bf0b 100644 --- a/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp +++ b/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp @@ -88,16 +88,13 @@ 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(); - - if (startEndPosInFile) - startEndPosInFile->first = this->bufferStartPosInFile + uint64_t(this->posInBuffer); + this->lastDataAndPos.data.clear(); + this->lastDataAndPos.startEnd.start = this->bufferStartPosInFile + this->posInBuffer; int nextStartCodePos = -1; int searchOffset = 3; @@ -109,25 +106,24 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, // 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) { // No start code found ... append all data in the current buffer. - this->lastReturnArray += + this->lastDataAndPos.data += 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.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 @@ -137,7 +133,7 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, 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) { @@ -148,7 +144,7 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, // 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) @@ -157,7 +153,7 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, // 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) @@ -166,7 +162,7 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, startCodeFound = true; nextStartCodePos = lastByteZero2 ? -1 : 0; if (lastByteZero2) - this->lastReturnArray.chop(1); + this->lastDataAndPos.data.chop(1); } } @@ -182,18 +178,17 @@ QByteArray FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain, } // Position found - if (startEndPosInFile) - startEndPosInFile->second = this->bufferStartPosInFile + nextStartCodePos; + this->lastDataAndPos.startEnd.end = this->bufferStartPosInFile + nextStartCodePos; if (nextStartCodePos > int(this->posInBuffer)) - this->lastReturnArray += + 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; + << 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 @@ -201,16 +196,13 @@ QByteArray FileSourceAnnexBFile::getFrameData(pairUint64 startEndFilePos) // 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 = this->getNextNALUnit(); + auto [nalData, fileStartEndPos] = this->getNextNALUnit(); int headerOffset = 0; if (nalData.at(0) == (char)0 && nalData.at(1) == (char)0) diff --git a/YUViewLib/src/filesource/FileSourceAnnexBFile.h b/YUViewLib/src/filesource/FileSourceAnnexBFile.h index 267b4c4b5..db942c88a 100644 --- a/YUViewLib/src/filesource/FileSourceAnnexBFile.h +++ b/YUViewLib/src/filesource/FileSourceAnnexBFile.h @@ -59,12 +59,12 @@ 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); bool seek(int64_t pos) override; @@ -72,8 +72,8 @@ class FileSourceAnnexBFile : public FileSource 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 @@ -86,7 +86,7 @@ class FileSourceAnnexBFile : public FileSource 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/FileSourceOBUFile.h b/YUViewLib/src/filesource/FileSourceOBUFile.h index 251804ebe..2eaafb0be 100644 --- a/YUViewLib/src/filesource/FileSourceOBUFile.h +++ b/YUViewLib/src/filesource/FileSourceOBUFile.h @@ -50,7 +50,7 @@ class FileSourceOBUFile : public FileSource // Get the next OBU. Also return the start and end position of the OBU in the file so // you can seek to it. - [[nodiscard]] DataAndStartEndPos getNextOBU(bool getLastDataAgain = false); + DataAndStartEndPos getNextOBU(bool getLastDataAgain = false); protected: // We will keep the last buffer in case the reader wants to get it again diff --git a/YUViewLib/src/parser/AV1/AV1OBU.cpp b/YUViewLib/src/parser/AV1/AV1OBU.cpp index 10a0d5a06..dfa58a739 100644 --- a/YUViewLib/src/parser/AV1/AV1OBU.cpp +++ b/YUViewLib/src/parser/AV1/AV1OBU.cpp @@ -47,10 +47,12 @@ ParserAV1OBU::ParserAV1OBU(QObject *parent) : Base(parent) this->decValues.PrevFrameID = -1; } -std::pair ParserAV1OBU::parseAndAddOBU(int obuID, - ByteVector & data, - std::shared_ptr parent, - pairUint64 obuStartEndPosFile) +Base::ParseResult +ParserAV1OBU::parseAndAddUnit(int obuID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional obuStartEndPosFile = {}, + std::shared_ptr parent = nullptr) { // Use the given tree item. If it is not set, use the nalUnitMode (if active). // We don't set data (a name) for this item yet. diff --git a/YUViewLib/src/parser/AV1/AV1OBU.h b/YUViewLib/src/parser/AV1/AV1OBU.h index 5f0b2de85..2fe6b755d 100644 --- a/YUViewLib/src/parser/AV1/AV1OBU.h +++ b/YUViewLib/src/parser/AV1/AV1OBU.h @@ -48,18 +48,18 @@ class ParserAV1OBU : public Base ParserAV1OBU(QObject *parent = nullptr); ~ParserAV1OBU() {} - std::pair parseAndAddOBU(int obuID, - ByteVector & data, - std::shared_ptr parent, - pairUint64 obuStartEndPosFile = pairUint64(-1, -1)); + ParseResult parseAndAddUnit(int obuID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = nullptr); - // So far, we only parse AV1 Obu files from the AVFormat parser so we don't need this (yet). - // When parsing of raw OBU files is added, we will need this. bool runParsingOfFile(QString) override { assert(false); return false; } + QList getStreamInfo() override { return {}; } unsigned int getNrStreams() override { return 1; } QString getShortStreamDescription(int) const override { return "Video"; } diff --git a/YUViewLib/src/parser/AV1/OpenBitstreamUnit.h b/YUViewLib/src/parser/AV1/OpenBitstreamUnit.h index 3b59ee9cc..04fe3539b 100644 --- a/YUViewLib/src/parser/AV1/OpenBitstreamUnit.h +++ b/YUViewLib/src/parser/AV1/OpenBitstreamUnit.h @@ -51,7 +51,7 @@ class ObuPayload class OpenBitstreamUnit { public: - OpenBitstreamUnit(int obu_idx, std::optional filePosStartEnd) : obu_idx(obu_idx) + OpenBitstreamUnit(int obu_idx, std::optional filePosStartEnd) : obu_idx(obu_idx) { if (filePosStartEnd) this->filePosStartEnd = *filePosStartEnd; @@ -61,8 +61,8 @@ class OpenBitstreamUnit std::shared_ptr payload; // Pointer to the first byte of the start code of the NAL unit - pairUint64 filePosStartEnd; - int obu_idx{}; + FileStartEndPos filePosStartEnd; + int obu_idx{}; }; } // namespace parser::av1 diff --git a/YUViewLib/src/parser/AVC/AnnexBAVC.cpp b/YUViewLib/src/parser/AVC/AnnexBAVC.cpp index 4b49f6581..239988500 100644 --- a/YUViewLib/src/parser/AVC/AnnexBAVC.cpp +++ b/YUViewLib/src/parser/AVC/AnnexBAVC.cpp @@ -71,7 +71,7 @@ struct CurrentSliceData std::optional getFrameDataWithUpdatedPosition(std::optional data, - std::optional nalStartEndPosFile, + std::optional nalStartEndPosFile, std::optional currentSliceData) { auto newData = data.value_or(FrameParsingData()); @@ -80,7 +80,7 @@ getFrameDataWithUpdatedPosition(std::optional data, if (!newData.fileStartEndPos) newData.fileStartEndPos = nalStartEndPosFile; else - newData.fileStartEndPos->second = nalStartEndPosFile->second; + newData.fileStartEndPos->end = nalStartEndPosFile->end; } if (currentSliceData) { @@ -178,11 +178,11 @@ video::yuv::PixelFormatYUV AnnexBAVC::getPixelFormat() const } AnnexB::ParseResult -AnnexBAVC::parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBAVC::parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; @@ -269,7 +269,7 @@ AnnexBAVC::parseAndAddNALUnit(int nalI nalAVC->rbsp = newSPS; nalAVC->rawData = data; this->nalUnitsForSeeking.push_back(nalAVC); - parseResult.nalTypeName = + parseResult.unitTypeName = "SPS(" + std::to_string(newSPS->seqParameterSetData.seq_parameter_set_id) + ") "; } else if (nalAVC->header.nal_unit_type == NalType::PPS) @@ -287,7 +287,7 @@ AnnexBAVC::parseAndAddNALUnit(int nalI nalAVC->rbsp = newPPS; nalAVC->rawData = data; this->nalUnitsForSeeking.push_back(nalAVC); - parseResult.nalTypeName = "PPS(" + std::to_string(newPPS->pic_parameter_set_id) + ") "; + parseResult.unitTypeName = "PPS(" + std::to_string(newPPS->pic_parameter_set_id) + ") "; } else if (nalAVC->header.nal_unit_type == NalType::CODED_SLICE_NON_IDR || nalAVC->header.nal_unit_type == NalType::CODED_SLICE_IDR || @@ -364,7 +364,7 @@ AnnexBAVC::parseAndAddNALUnit(int nalI currentSliceType = to_string(newSliceHeader->slice_type); DEBUG_AVC("AnnexBAVC::parseAndAddNALUnit Parsed Slice POC " << newSliceHeader->globalPOC); - parseResult.nalTypeName = "Slice(POC " + std::to_string(newSliceHeader->globalPOC) + ") "; + parseResult.unitTypeName = "Slice(POC " + std::to_string(newSliceHeader->globalPOC) + ") "; } else if (nalAVC->header.nal_unit_type == NalType::CODED_SLICE_DATA_PARTITION_B) { @@ -372,8 +372,8 @@ AnnexBAVC::parseAndAddNALUnit(int nalI throw std::logic_error("No partition A slice header found."); auto slice = std::make_shared(); slice->parse(reader, this->currentAUPartitionASPS); - specificDescription = " Slice Partition B"; - parseResult.nalTypeName = "Slice-PartB "; + specificDescription = " Slice Partition B"; + parseResult.unitTypeName = "Slice-PartB "; } else if (nalAVC->header.nal_unit_type == NalType::CODED_SLICE_DATA_PARTITION_C) { @@ -381,8 +381,8 @@ AnnexBAVC::parseAndAddNALUnit(int nalI throw std::logic_error("No partition A slice header found."); auto slice = std::make_shared(); slice->parse(reader, this->currentAUPartitionASPS); - specificDescription = " Slice Partition C"; - parseResult.nalTypeName = "Slice-PartC "; + specificDescription = " Slice Partition C"; + parseResult.unitTypeName = "Slice-PartC "; } else if (nalAVC->header.nal_unit_type == NalType::SEI) { @@ -420,19 +420,19 @@ AnnexBAVC::parseAndAddNALUnit(int nalI specificDescription += "(x" + std::to_string(newSEI->seis.size()) + ")"; DEBUG_AVC("AnnexBAVC::parseAndAddNALUnit Parsed SEI (" << newSEI->seis.size() << " messages)"); - parseResult.nalTypeName = "SEI(x" + std::to_string(newSEI->seis.size()) + ") "; + parseResult.unitTypeName = "SEI(x" + std::to_string(newSEI->seis.size()) + ") "; } else if (nalAVC->header.nal_unit_type == NalType::FILLER) { specificDescription = " Filler"; DEBUG_AVC("AnnexBAVC::parseAndAddNALUnit Parsed Filler data"); - parseResult.nalTypeName = "Filler "; + parseResult.unitTypeName = "Filler "; } else if (nalAVC->header.nal_unit_type == NalType::AUD) { specificDescription = " AUD"; DEBUG_AVC("AnnexBAVC::parseAndAddNALUnit Parsed AUD"); - parseResult.nalTypeName = "AUD "; + parseResult.unitTypeName = "AUD "; } if (nalAVC->header.nal_unit_type == NalType::CODED_SLICE_IDR || @@ -608,7 +608,7 @@ std::optional AnnexBAVC::getSeekData(int iFrameNr) // Seek here AnnexB::SeekData seekData; if (nal->filePosStartEnd) - seekData.filePos = nal->filePosStartEnd->first; + seekData.filePos = nal->filePosStartEnd->start; // Get the bitstream of all active parameter sets for (const auto &nalMap : {activeSPSNal, activePPSNal}) diff --git a/YUViewLib/src/parser/AVC/AnnexBAVC.h b/YUViewLib/src/parser/AVC/AnnexBAVC.h index b108f300e..b9d5ee835 100644 --- a/YUViewLib/src/parser/AVC/AnnexBAVC.h +++ b/YUViewLib/src/parser/AVC/AnnexBAVC.h @@ -60,9 +60,9 @@ struct FrameParsingData // For every frame (AU), we save the file position where the NAL unit of the first slice starts // and where the NAL of the last slice ends (if known). This is used by getNextFrameNALUnits to // return all information (NAL units) for a specific frame (AU). This includes SPS/PPS. - std::optional fileStartEndPos{}; - std::optional poc{}; - bool isRandomAccess{}; + std::optional fileStartEndPos{}; + std::optional poc{}; + bool isRandomAccess{}; }; } // namespace avc @@ -80,11 +80,11 @@ class AnnexBAVC : public AnnexB Size getSequenceSizeSamples() const override; video::yuv::PixelFormatYUV getPixelFormat() const override; - ParseResult parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = nullptr) override; + ParseResult parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = nullptr) override; std::optional getSeekData(int iFrameNr) override; QByteArray getExtradata() override; diff --git a/YUViewLib/src/parser/AVC/NalUnitAVC.h b/YUViewLib/src/parser/AVC/NalUnitAVC.h index d47100693..8771ab91d 100644 --- a/YUViewLib/src/parser/AVC/NalUnitAVC.h +++ b/YUViewLib/src/parser/AVC/NalUnitAVC.h @@ -51,7 +51,7 @@ class NalRBSP class NalUnitAVC { public: - NalUnitAVC(int nalIdx, std::optional filePosStartEnd) + NalUnitAVC(int nalIdx, std::optional filePosStartEnd) : nalIdx(nalIdx), filePosStartEnd(filePosStartEnd) { } @@ -61,7 +61,7 @@ class NalUnitAVC int nalIdx{}; // Pointer to the first byte of the start code of the NAL unit (if known) - std::optional filePosStartEnd; + std::optional filePosStartEnd; ByteVector rawData; }; diff --git a/YUViewLib/src/parser/AVFormat/AVFormat.cpp b/YUViewLib/src/parser/AVFormat/AVFormat.cpp index c392283b1..a8dc3a8d5 100644 --- a/YUViewLib/src/parser/AVFormat/AVFormat.cpp +++ b/YUViewLib/src/parser/AVFormat/AVFormat.cpp @@ -164,7 +164,7 @@ bool AVFormat::parseExtradata_AVC(ByteVector &extradata) SubByteReaderLoggingSubLevel spsSubLevel(reader, "SPS " + std::to_string(i)); auto sps_size = reader.readBits("sps_size", 16); auto spsData = reader.readBytes("", sps_size, Options().withLoggingDisabled()); - auto parseResult = this->annexBParser->parseAndAddNALUnit( + auto parseResult = this->annexBParser->parseAndAddUnit( nalID++, spsData, {}, {}, reader.getCurrentItemTree()); if (parseResult.success && parseResult.bitrateEntry) this->bitratePlotModel->addBitratePoint(this->videoStreamIndex, *parseResult.bitrateEntry); @@ -176,7 +176,7 @@ bool AVFormat::parseExtradata_AVC(ByteVector &extradata) SubByteReaderLoggingSubLevel ppsSubLevel(reader, "PPS " + std::to_string(i)); auto pps_size = reader.readBits("pps_size", 16); auto pspsData = reader.readBytes("", pps_size, Options().withLoggingDisabled()); - auto parseResult = this->annexBParser->parseAndAddNALUnit( + auto parseResult = this->annexBParser->parseAndAddUnit( nalID++, pspsData, {}, {}, reader.getCurrentItemTree()); if (parseResult.success && parseResult.bitrateEntry) this->bitratePlotModel->addBitratePoint(this->videoStreamIndex, *parseResult.bitrateEntry); @@ -301,7 +301,7 @@ AVFormat::parseByteVectorAnnexBStartCodes(ByteVector & data, auto itNextStartCode = getNextNalStart(itStartCode); auto nalData = ByteVector(itStartCode + sizeStartCode, itNextStartCode); auto parseResult = - this->annexBParser->parseAndAddNALUnit(nalID++, nalData, packetBitrateEntry, {}, item); + this->annexBParser->parseAndAddUnit(nalID++, nalData, packetBitrateEntry, {}, item); if (parseResult.success && parseResult.bitrateEntry) this->bitratePlotModel->addBitratePoint(this->videoStreamIndex, *parseResult.bitrateEntry); if (parseResult.success && parseResult.nalTypeName) diff --git a/YUViewLib/src/parser/AnnexB.cpp b/YUViewLib/src/parser/AnnexB.cpp index 902d6ea67..1226f0e3a 100644 --- a/YUViewLib/src/parser/AnnexB.cpp +++ b/YUViewLib/src/parser/AnnexB.cpp @@ -58,9 +58,9 @@ QString AnnexB::getShortStreamDescription(int) const return info; } -bool AnnexB::addFrameToList(int poc, - std::optional fileStartEndPos, - bool randomAccessPoint) +bool AnnexB::addFrameToList(int poc, + std::optional fileStartEndPos, + bool randomAccessPoint) { for (const auto &f : this->frameListCodingOrder) if (f.poc == poc) @@ -81,9 +81,9 @@ bool AnnexB::addFrameToList(int poc, return true; } -void AnnexB::logNALSize(const ByteVector & data, - std::shared_ptr root, - std::optional nalStartEndPos) +void AnnexB::logNALSize(const ByteVector & data, + std::shared_ptr root, + std::optional nalStartEndPos) { size_t startCodeSize = 0; if (data[0] == char(0) && data[1] == char(0) && data[2] == char(0) && data[3] == char(1)) @@ -138,7 +138,7 @@ auto AnnexB::getClosestSeekPoint(FrameIndexDisplayOrder targetFrame, return seekPointInfo; } -std::optional AnnexB::getFrameStartEndPos(FrameIndexCodingOrder idx) +std::optional AnnexB::getFrameStartEndPos(FrameIndexCodingOrder idx) { if (idx >= this->frameListCodingOrder.size()) return {}; @@ -172,8 +172,7 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge emit streamInfoUpdated(); // Just push all NAL units from the annexBFile into the annexBParser - int nalID = 0; - pairUint64 nalStartEndPosFile; + int nalID = 0; bool abortParsing = false; QElapsedTimer signalEmitTimer; signalEmitTimer.start(); @@ -186,10 +185,14 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge try { - auto nalData = reader::SubByteReaderLogging::convertToByteVector( - file->getNextNALUnit(false, &nalStartEndPosFile)); + auto [nalData, nalStartEndPos] = file->getNextNALUnit(false); + auto parsingResult = - this->parseAndAddNALUnit(nalID, nalData, {}, nalStartEndPosFile, nullptr); + this->parseAndAddUnit(nalID, + reader::SubByteReaderLogging::convertToByteVector(nalData), + {}, + nalStartEndPos, + nullptr); if (!parsingResult.success) { DEBUG_ANNEXB("AnnexB::parseAndAddNALUnit Error parsing NAL " << nalID); @@ -250,7 +253,7 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge try { - auto parseResult = this->parseAndAddNALUnit(-1, {}, {}, {}); + auto parseResult = this->parseAndAddUnit(-1, {}, {}, {}); if (!parseResult.success) DEBUG_ANNEXB("AnnexB::parseAndAddNALUnit Error finalizing parsing. This should not happen."); } @@ -265,9 +268,9 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge if (packetModel) emit modelDataUpdated(); - this->streamInfo.parsing = false; - this->streamInfo.nrNALUnits = nalID; - this->streamInfo.nrFrames = unsigned(this->frameListCodingOrder.size()); + this->streamInfo.parsing = false; + this->streamInfo.nrUnits = nalID; + this->streamInfo.nrFrames = unsigned(this->frameListCodingOrder.size()); emit streamInfoUpdated(); emit backgroundParsingDone(""); @@ -281,29 +284,6 @@ bool AnnexB::runParsingOfFile(QString compressedFilePath) return this->parseAnnexBFile(file); } -QList AnnexB::StreamInfo::getStreamInfo() -{ - QList infoList; - infoList.append( - new QTreeWidgetItem(QStringList() << "File size" << QString::number(this->fileSize))); - if (this->parsing) - { - infoList.append(new QTreeWidgetItem(QStringList() << "Number NAL units" - << "Parsing...")); - infoList.append(new QTreeWidgetItem(QStringList() << "Number Frames" - << "Parsing...")); - } - else - { - infoList.append(new QTreeWidgetItem(QStringList() << "Number NAL units" - << QString::number(this->nrNALUnits))); - infoList.append( - new QTreeWidgetItem(QStringList() << "Number Frames" << QString::number(this->nrFrames))); - } - - return infoList; -} - int AnnexB::getFramePOC(FrameIndexDisplayOrder frameIdx) { this->updateFrameListDisplayOrder(); diff --git a/YUViewLib/src/parser/AnnexB.h b/YUViewLib/src/parser/AnnexB.h index 25b8cafc2..942beb763 100644 --- a/YUViewLib/src/parser/AnnexB.h +++ b/YUViewLib/src/parser/AnnexB.h @@ -71,29 +71,6 @@ class AnnexB : public Base unsigned int getNrStreams() override { return 1; } QString getShortStreamDescription(int streamIndex) const override; - /* Parse the NAL unit and what it contains - * - * It also adds the unit to the nalUnitList (if it is a parameter set or an RA point). - * When there are no more NAL units in the file (the file ends), call this function one last time - * with empty data and a nalID of -1. \nalID A counter (ID) of the nal \data The raw data of the - * NAL. May include the start code or not. \bitrateEntry Pass the bitrate entry data into the - * function that may already be known. E.g. the ffmpeg parser already decodes the DTS/PTS values - * from the container. \parent The tree item of the parent where the items will be appended. - * \nalStartEndPosFile The position of the first and last byte of the NAL. - */ - struct ParseResult - { - ParseResult() = default; - bool success{false}; - std::optional nalTypeName; - std::optional bitrateEntry; - }; - virtual ParseResult parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = nullptr) = 0; - // Get some format properties virtual double getFramerate() const = 0; virtual Size getSequenceSizeSamples() const = 0; @@ -126,7 +103,7 @@ class AnnexB : public Base virtual IntPair getProfileLevel() = 0; virtual Ratio getSampleAspectRatio() = 0; - std::optional getFrameStartEndPos(FrameIndexCodingOrder idx); + std::optional getFrameStartEndPos(FrameIndexCodingOrder idx); bool parseAnnexBFile(std::unique_ptr &file, QWidget *mainWindow = nullptr); @@ -138,7 +115,7 @@ class AnnexB : public Base { AnnexBFrame() = default; int poc{-1}; //< The poc of this frame - std::optional + std::optional fileStartEndPos; //< The start and end position of all slice NAL units (if known) bool randomAccessPoint{false}; //< Can we start decoding here? @@ -147,26 +124,15 @@ class AnnexB : public Base }; // Returns false if the POC was already present int the list - bool addFrameToList(int poc, std::optional fileStartEndPos, bool randomAccessPoint); + bool + addFrameToList(int poc, std::optional fileStartEndPos, bool randomAccessPoint); - static void logNALSize(const ByteVector & data, - std::shared_ptr root, - std::optional nalStartEndPos); + static void logNALSize(const ByteVector & data, + std::shared_ptr root, + std::optional nalStartEndPos); int pocOfFirstRandomAccessFrame{-1}; - // Save general information about the file here - struct StreamInfo - { - QList getStreamInfo(); - - size_t fileSize; - unsigned nrNALUnits{0}; - unsigned nrFrames{0}; - bool parsing{false}; - }; - StreamInfo streamInfo; - int getFramePOC(FrameIndexDisplayOrder frameIdx); private: diff --git a/YUViewLib/src/parser/Base.cpp b/YUViewLib/src/parser/Base.cpp index 652fef5d0..75005fe38 100644 --- a/YUViewLib/src/parser/Base.cpp +++ b/YUViewLib/src/parser/Base.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 "Base.h" @@ -39,7 +39,7 @@ #include #define DEBUG_PARSER qDebug #else -#define DEBUG_PARSER(fmt,...) ((void)0) +#define DEBUG_PARSER(fmt, ...) ((void)0) #endif namespace parser @@ -86,7 +86,7 @@ void Base::enableModel() } void Base::updateNumberModelItems() -{ +{ this->packetModel->updateNumberModelItems(); } @@ -104,4 +104,27 @@ QString Base::convertSliceTypeMapToString(QMap &sliceType return text; } +QList Base::StreamInfo::getStreamInfo() +{ + QList infoList; + infoList.append( + new QTreeWidgetItem(QStringList() << "File size" << QString::number(this->fileSize))); + if (this->parsing) + { + infoList.append(new QTreeWidgetItem(QStringList() << "Number units" + << "Parsing...")); + infoList.append(new QTreeWidgetItem(QStringList() << "Number Frames" + << "Parsing...")); + } + else + { + infoList.append( + new QTreeWidgetItem(QStringList() << "Number units" << QString::number(this->nrUnits))); + infoList.append( + new QTreeWidgetItem(QStringList() << "Number Frames" << QString::number(this->nrFrames))); + } + + return infoList; +} + } // namespace parser \ No newline at end of file diff --git a/YUViewLib/src/parser/Base.h b/YUViewLib/src/parser/Base.h index eca9a3177..4bc86ad39 100644 --- a/YUViewLib/src/parser/Base.h +++ b/YUViewLib/src/parser/Base.h @@ -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 . + */ #pragma once @@ -37,9 +37,9 @@ #include #include -#include "common/PacketItemModel.h" #include "common/BitratePlotModel.h" #include "common/HRDPlotModel.h" +#include "common/PacketItemModel.h" // If the file parsing limit is enabled (setParsingLimitEnabled) parsing will be aborted after // 500 frames have been parsed. This should be enough in most situations and full parsing can be @@ -60,54 +60,95 @@ class Base : public QObject virtual ~Base() = 0; QAbstractItemModel *getPacketItemModel() { return streamIndexFilter.data(); } - BitratePlotModel *getBitratePlotModel() { return bitratePlotModel.data(); } - HRDPlotModel *getHRDPlotModel(); - void setRedirectPlotModel(HRDPlotModel *plotModel); - + BitratePlotModel * getBitratePlotModel() { return bitratePlotModel.data(); } + HRDPlotModel * getHRDPlotModel(); + void setRedirectPlotModel(HRDPlotModel *plotModel); + void updateNumberModelItems(); void enableModel(); // Get info about the stream organized in a tree - virtual QList getStreamInfo() = 0; - virtual unsigned int getNrStreams() = 0; + virtual QList getStreamInfo() = 0; + virtual unsigned int getNrStreams() = 0; + + /* Parse the NAL unit / OBU and what it contains + * + * When there are no more units in the file (the file ends), call this function one last time + * with empty data and a unitID of -1. \unitID A counter (ID) of the unit \data. The raw data of + * the unit. For NALs, this may include the start code or not. \bitrateEntry Pass the bitrate + * entry data into the function that may already be known. E.g. the ffmpeg parser already decodes + * the DTS/PTS values from the container. \parent The tree item of the parent where the items will + * be appended. \nalStartEndPosFile The position of the first and last byte of the unit. + */ + struct ParseResult + { + ParseResult() = default; + bool success{false}; + std::optional unitTypeName; + std::optional bitrateEntry; + }; + virtual ParseResult parseAndAddUnit(int unitID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = nullptr) = 0; // For parsing files in the background (threading) in the bitstream analysis dialog: virtual bool runParsingOfFile(QString fileName) = 0; - int getParsingProgressPercent() { return progressPercentValue; } - void setAbortParsing() { cancelBackgroundParser = true; } + int getParsingProgressPercent() { return progressPercentValue; } + void setAbortParsing() { cancelBackgroundParser = true; } - virtual int getVideoStreamIndex() { return -1; } + virtual int getVideoStreamIndex() { return -1; } virtual QString getShortStreamDescription(int streamIndex) const = 0; void setStreamColorCoding(bool colorCoding) { packetModel->setUseColorCoding(colorCoding); } - void setFilterStreamIndex(int streamIndex) { streamIndexFilter->setFilterStreamIndex(streamIndex); } + void setFilterStreamIndex(int streamIndex) + { + streamIndexFilter->setFilterStreamIndex(streamIndex); + } void setParsingLimitEnabled(bool limitEnabled) { parsingLimitEnabled = limitEnabled; } - void setBitrateSortingIndex(int sortingIndex) { bitratePlotModel->setBitrateSortingIndex(sortingIndex); } + void setBitrateSortingIndex(int sortingIndex) + { + bitratePlotModel->setBitrateSortingIndex(sortingIndex); + } signals: // Some data was updated and the models can be updated to reflec this. This is called regularly // but not for every packet/Nal unit that is parsed. void modelDataUpdated(); void backgroundParsingDone(QString error); - + // Signal that the getStreamInfo() function will now return an updated info void streamInfoUpdated(); protected: - QScopedPointer packetModel; + QScopedPointer packetModel; QScopedPointer streamIndexFilter; - QScopedPointer bitratePlotModel; + QScopedPointer bitratePlotModel; static QString convertSliceTypeMapToString(QMap ¤tAUSliceTypes); - // If this variable is set (from an external thread), the parsing process should cancel immediately - bool cancelBackgroundParser {false}; - int progressPercentValue {0}; - bool parsingLimitEnabled {false}; + // If this variable is set (from an external thread), the parsing process should cancel + // immediately + bool cancelBackgroundParser{false}; + int progressPercentValue{0}; + bool parsingLimitEnabled{false}; + + // Save general information about the file here + struct StreamInfo + { + QList getStreamInfo(); + + size_t fileSize; + unsigned nrUnits{0}; + unsigned nrFrames{0}; + bool parsing{false}; + }; + StreamInfo streamInfo; private: QScopedPointer hrdPlotModel; - HRDPlotModel *redirectPlotModel {nullptr}; + HRDPlotModel * redirectPlotModel{nullptr}; }; } // namespace parser \ No newline at end of file diff --git a/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp b/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp index ae03029fb..d99056c4d 100644 --- a/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp +++ b/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp @@ -270,11 +270,11 @@ Ratio AnnexBHEVC::getSampleAspectRatio() } AnnexB::ParseResult -AnnexBHEVC::parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBHEVC::parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; @@ -337,7 +337,7 @@ AnnexBHEVC::parseAndAddNALUnit(int nal nalHEVC->rbsp = newVPS; nalHEVC->rawData = data; this->nalUnitsForSeeking.push_back(nalHEVC); - parseResult.nalTypeName = "VPS(" + std::to_string(newVPS->vps_video_parameter_set_id) + ") "; + parseResult.unitTypeName = "VPS(" + std::to_string(newVPS->vps_video_parameter_set_id) + ") "; DEBUG_HEVC("AnnexBHEVC::parseAndAddNALUnit VPS ID " << newVPS->vps_video_parameter_set_id); } @@ -356,7 +356,7 @@ AnnexBHEVC::parseAndAddNALUnit(int nal nalHEVC->rbsp = newSPS; nalHEVC->rawData = data; this->nalUnitsForSeeking.push_back(nalHEVC); - parseResult.nalTypeName = "SPS(" + std::to_string(newSPS->sps_seq_parameter_set_id) + ") "; + parseResult.unitTypeName = "SPS(" + std::to_string(newSPS->sps_seq_parameter_set_id) + ") "; } else if (nalHEVC->header.nal_unit_type == hevc::NalType::PPS_NUT) { @@ -373,7 +373,7 @@ AnnexBHEVC::parseAndAddNALUnit(int nal nalHEVC->rbsp = newPPS; nalHEVC->rawData = data; this->nalUnitsForSeeking.push_back(nalHEVC); - parseResult.nalTypeName = "SPS(" + std::to_string(newPPS->pps_pic_parameter_set_id) + ") "; + parseResult.unitTypeName = "SPS(" + std::to_string(newPPS->pps_pic_parameter_set_id) + ") "; } else if (nalHEVC->header.isSlice()) { @@ -480,7 +480,7 @@ AnnexBHEVC::parseAndAddNALUnit(int nal else if (curFrameFileStartEndPos && nalStartEndPosFile) // Another slice NAL which belongs to the last frame // Update the end position - curFrameFileStartEndPos->second = nalStartEndPosFile->second; + curFrameFileStartEndPos->end = nalStartEndPosFile->end; nalHEVC->rbsp = newSlice; if (nalHEVC->header.isIRAP()) @@ -493,7 +493,7 @@ AnnexBHEVC::parseAndAddNALUnit(int nal currentSliceType = to_string(newSlice->sliceSegmentHeader.slice_type); specificDescription += " (POC " + std::to_string(poc) + ")"; - parseResult.nalTypeName = "Slice(POC " + std::to_string(poc) + ")"; + parseResult.unitTypeName = "Slice(POC " + std::to_string(poc) + ")"; DEBUG_HEVC("AnnexBHEVC::parseAndAddNALUnit Slice POC " << poc << " - pocCounterOffset " << this->pocCounterOffset << " maxPOCCount " @@ -540,12 +540,12 @@ AnnexBHEVC::parseAndAddNALUnit(int nal DEBUG_HEVC("AnnexBHEVC::parseAndAddNALUnit Parsed SEI (" << newSEI->seis.size() << " messages)"); - parseResult.nalTypeName = "SEI(x" + std::to_string(newSEI->seis.size()) + ") "; + parseResult.unitTypeName = "SEI(x" + std::to_string(newSEI->seis.size()) + ") "; } else if (nalHEVC->header.nal_unit_type == NalType::FD_NUT) { DEBUG_HEVC("AnnexBHEVC::parseAndAddNALUnit Parsed Fillerdata"); - parseResult.nalTypeName = "Filler "; + parseResult.unitTypeName = "Filler "; } else if (nalHEVC->header.nal_unit_type == NalType::UNSPEC62 || nalHEVC->header.nal_unit_type == NalType::UNSPEC63) @@ -556,7 +556,7 @@ AnnexBHEVC::parseAndAddNALUnit(int nal // https://patents.google.com/patent/US20180278963A1/en specificDescription = " Dolby Vision"; DEBUG_HEVC("AnnexBHEVC::parseAndAddNALUnit Dolby Vision Metadata"); - parseResult.nalTypeName = "Dolby Vision "; + parseResult.unitTypeName = "Dolby Vision "; } if (nalHEVC->header.isSlice()) diff --git a/YUViewLib/src/parser/HEVC/AnnexBHEVC.h b/YUViewLib/src/parser/HEVC/AnnexBHEVC.h index 1cbccd2fd..ddf0c7539 100644 --- a/YUViewLib/src/parser/HEVC/AnnexBHEVC.h +++ b/YUViewLib/src/parser/HEVC/AnnexBHEVC.h @@ -60,10 +60,7 @@ class AnnexBHEVC : public AnnexB Q_OBJECT public: - AnnexBHEVC(QObject *parent = nullptr) : AnnexB(parent) - { - curFrameFileStartEndPos = pairUint64(-1, -1); - } + AnnexBHEVC(QObject *parent = nullptr) : AnnexB(parent) {} ~AnnexBHEVC(){}; // Get some properties @@ -76,11 +73,11 @@ class AnnexBHEVC : public AnnexB IntPair getProfileLevel() override; Ratio getSampleAspectRatio() override; - ParseResult parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = nullptr) override; + ParseResult parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = nullptr) override; protected: // ----- Some nested classes that are only used in the scope of this file handler class @@ -132,7 +129,7 @@ class AnnexBHEVC : public AnnexB // For every frame, we save the file position where the NAL unit of the first slice starts and // where the NAL of the last slice ends. This is used by getNextFrameNALUnits to return all // information (NAL units) for a specific frame. - std::optional + std::optional curFrameFileStartEndPos; //< Save the file start/end position of the current frame (if known) // in case the frame has multiple NAL units // The POC of the current frame. We save this we encounter a NAL from the next POC; then we add diff --git a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp index 87e954f43..ecf5ea771 100644 --- a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp +++ b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp @@ -35,11 +35,11 @@ #include "NalUnitMpeg2.h" #include "group_of_pictures_header.h" #include "nal_extension.h" -#include #include "picture_header.h" #include "sequence_extension.h" #include "sequence_header.h" #include "user_data.h" +#include #include @@ -57,11 +57,11 @@ namespace parser using namespace mpeg2; AnnexB::ParseResult -AnnexBMpeg2::parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBMpeg2::parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; @@ -114,9 +114,9 @@ AnnexBMpeg2::parseAndAddNALUnit(int na if (!this->firstSequenceHeader) this->firstSequenceHeader = newSequenceHeader; - nal_mpeg2.rbsp = newSequenceHeader; - specificDescription = " Sequence Header"; - parseResult.nalTypeName = "SeqHeader"; + nal_mpeg2.rbsp = newSequenceHeader; + specificDescription = " Sequence Header"; + parseResult.unitTypeName = "SeqHeader"; } else if (nal_mpeg2.header.nal_unit_type == NalType::PICTURE) { @@ -135,9 +135,9 @@ AnnexBMpeg2::parseAndAddNALUnit(int na this->lastPictureHeader = newPictureHeader; currentSliceType = newPictureHeader->getPictureTypeString(); - nal_mpeg2.rbsp = newPictureHeader; - specificDescription = " Picture Header POC " + std::to_string(this->curFramePOC); - parseResult.nalTypeName = "PicHeader(POC " + std::to_string(this->curFramePOC) + ")"; + nal_mpeg2.rbsp = newPictureHeader; + specificDescription = " Picture Header POC " + std::to_string(this->curFramePOC); + parseResult.unitTypeName = "PicHeader(POC " + std::to_string(this->curFramePOC) + ")"; } else if (nal_mpeg2.header.nal_unit_type == NalType::GROUP_START) { @@ -146,9 +146,9 @@ AnnexBMpeg2::parseAndAddNALUnit(int na auto newGroupOfPictureHeader = std::make_shared(); newGroupOfPictureHeader->parse(reader); - nal_mpeg2.rbsp = newGroupOfPictureHeader; - specificDescription = " Group of Pictures"; - parseResult.nalTypeName = "GOP"; + nal_mpeg2.rbsp = newGroupOfPictureHeader; + specificDescription = " Group of Pictures"; + parseResult.unitTypeName = "GOP"; } else if (nal_mpeg2.header.nal_unit_type == NalType::USER_DATA) { @@ -157,9 +157,9 @@ AnnexBMpeg2::parseAndAddNALUnit(int na auto newUserData = std::make_shared(); newUserData->parse(reader); - nal_mpeg2.rbsp = newUserData; - specificDescription = " User Data"; - parseResult.nalTypeName = "UserData"; + nal_mpeg2.rbsp = newUserData; + specificDescription = " User Data"; + parseResult.unitTypeName = "UserData"; } else if (nal_mpeg2.header.nal_unit_type == NalType::EXTENSION_START) { @@ -174,14 +174,14 @@ AnnexBMpeg2::parseAndAddNALUnit(int na std::dynamic_pointer_cast(newExtension->payload); } - nal_mpeg2.rbsp = newExtension; - specificDescription = " Extension"; - parseResult.nalTypeName = "Extension"; + nal_mpeg2.rbsp = newExtension; + specificDescription = " Extension"; + parseResult.unitTypeName = "Extension"; } if (nal_mpeg2.header.nal_unit_type == NalType::SLICE) { - specificDescription = " Slice"; - parseResult.nalTypeName = "Slice"; + specificDescription = " Slice"; + parseResult.unitTypeName = "Slice"; } const bool isStartOfNewAU = diff --git a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h index 101222250..b3ae5161d 100644 --- a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h +++ b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h @@ -58,11 +58,11 @@ class AnnexBMpeg2 : public AnnexB Size getSequenceSizeSamples() const override; video::yuv::PixelFormatYUV getPixelFormat() const override; - ParseResult parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = {}) override; + ParseResult parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = {}) override; // TODO: Reading from raw mpeg2 streams not supported (yet? Is this even defined / possible?) virtual std::optional getSeekData(int iFrameNr) override diff --git a/YUViewLib/src/parser/Mpeg2/NalUnitMpeg2.h b/YUViewLib/src/parser/Mpeg2/NalUnitMpeg2.h index 9ee4b0404..96b07312c 100644 --- a/YUViewLib/src/parser/Mpeg2/NalUnitMpeg2.h +++ b/YUViewLib/src/parser/Mpeg2/NalUnitMpeg2.h @@ -47,14 +47,14 @@ class NalRBSP class NalUnitMpeg2 { public: - NalUnitMpeg2(int nalIdx, std::optional filePosStartEnd) + NalUnitMpeg2(int nalIdx, std::optional filePosStartEnd) : nalIdx(nalIdx), filePosStartEnd(filePosStartEnd) { } int nalIdx{}; // Pointer to the first byte of the start code of the NAL unit (if known) - std::optional filePosStartEnd; + std::optional filePosStartEnd; nal_unit_header header; std::shared_ptr rbsp; diff --git a/YUViewLib/src/parser/VVC/AnnexBVVC.cpp b/YUViewLib/src/parser/VVC/AnnexBVVC.cpp index 2ce44420f..8f939edaa 100644 --- a/YUViewLib/src/parser/VVC/AnnexBVVC.cpp +++ b/YUViewLib/src/parser/VVC/AnnexBVVC.cpp @@ -267,11 +267,11 @@ Ratio AnnexBVVC::getSampleAspectRatio() } AnnexB::ParseResult -AnnexBVVC::parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBVVC::parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; parseResult.success = true; @@ -562,7 +562,7 @@ AnnexBVVC::parseAndAddNALUnit(int nalI else if (nalStartEndPosFile) { if (updatedParsingState.currentAU.fileStartEndPos) - updatedParsingState.currentAU.fileStartEndPos->second = nalStartEndPosFile->second; + updatedParsingState.currentAU.fileStartEndPos->end = nalStartEndPosFile->end; else updatedParsingState.currentAU.fileStartEndPos = nalStartEndPosFile; } diff --git a/YUViewLib/src/parser/VVC/AnnexBVVC.h b/YUViewLib/src/parser/VVC/AnnexBVVC.h index 5930b26a4..0bfb30cc5 100644 --- a/YUViewLib/src/parser/VVC/AnnexBVVC.h +++ b/YUViewLib/src/parser/VVC/AnnexBVVC.h @@ -61,11 +61,11 @@ struct ParsingState struct CurrentAU { - size_t counter{}; - size_t sizeBytes{}; - int poc{-1}; - bool isKeyframe{}; - std::optional fileStartEndPos; + size_t counter{}; + size_t sizeBytes{}; + int poc{-1}; + bool isKeyframe{}; + std::optional fileStartEndPos; }; CurrentAU currentAU{}; @@ -94,11 +94,11 @@ class AnnexBVVC : public AnnexB IntPair getProfileLevel() override; Ratio getSampleAspectRatio() override; - ParseResult parseAndAddNALUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = {}) override; + ParseResult parseAndAddUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = {}) override; protected: // The PicOrderCntMsb may be reset to zero for IDR frames. In order to count the global POC, we diff --git a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp index 256bd3761..945d54fb8 100644 --- a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp +++ b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.cpp @@ -191,7 +191,6 @@ playlistItemCompressedVideo::playlistItemCompressedVideo(const QString &compress { DEBUG_COMPRESSED( "playlistItemCompressedVideo::playlistItemCompressedVideo Opening OBU AV1 file"); - } else { @@ -728,7 +727,7 @@ void playlistItemCompressedVideo::loadRawData(int frameIdx, bool caching) DEBUG_COMPRESSED("playlistItemCompressedVideo::loadRawData retrived frame data from file " "- AnnexBCnt " << this->readAnnexBFrameCounterCodingOrder << " startEnd " - << frameStartEndFilePos->first << "-" << frameStartEndFilePos->second + << frameStartEndFilePos->start << "-" << frameStartEndFilePos->end << " - size " << data.size()); } @@ -751,8 +750,9 @@ void playlistItemCompressedVideo::loadRawData(int frameIdx, bool caching) else if (isInputFormatTypeAnnexB(this->inputFormat) && this->decoderEngine != DecoderEngine::FFMpeg) { - auto data = caching ? this->inputFileAnnexBCaching->getNextNALUnit(repushData) - : this->inputFileAnnexBLoading->getNextNALUnit(repushData); + auto [data, fileStartEndPos] = + caching ? this->inputFileAnnexBCaching->getNextNALUnit(repushData) + : this->inputFileAnnexBLoading->getNextNALUnit(repushData); DEBUG_COMPRESSED( "playlistItemCompressedVideo::loadRawData retrived nal unit from file - size " << data.size()); diff --git a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h index 998e54630..96473414e 100644 --- a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h +++ b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -147,6 +148,9 @@ class playlistItemCompressedVideo : public playlistItemWithVideo // The same applies for Raw OBU files. We open the input twice (for caching and loading). The // parser is only needed once. + std::unique_ptr inputFileOBULoading; + std::unique_ptr inputFileOBUCaching; + std::unique_ptr inputFileOBUParser; // Which type is the input? InputFormat inputFormat{InputFormat::Invalid}; From 0e5c539385f28bafd45596f2ee9a4c19aaa853ef Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Sat, 12 Nov 2022 20:38:33 +0100 Subject: [PATCH 10/13] It compiles --- YUViewLib/src/common/Typedef.h | 2 +- .../src/filesource/FileSourceAnnexBFile.cpp | 2 +- .../src/filesource/FileSourceOBUFile.cpp | 2 +- .../AV1/{AV1OBU.cpp => ParserAV1OBU.cpp} | 41 ++++++++-------- .../parser/AV1/{AV1OBU.h => ParserAV1OBU.h} | 9 ++-- YUViewLib/src/parser/AVC/AnnexBAVC.cpp | 14 +++--- YUViewLib/src/parser/AVC/AnnexBAVC.h | 10 ++-- YUViewLib/src/parser/AVC/NalUnitAVC.h | 8 ++-- YUViewLib/src/parser/AVFormat/AVFormat.cpp | 48 ++++++++++++------- YUViewLib/src/parser/AVFormat/AVFormat.h | 10 ++-- YUViewLib/src/parser/AnnexB.cpp | 12 ++--- YUViewLib/src/parser/AnnexB.h | 6 +++ YUViewLib/src/parser/Base.h | 8 +--- YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp | 14 +++--- YUViewLib/src/parser/HEVC/AnnexBHEVC.h | 10 ++-- YUViewLib/src/parser/HEVC/NalUnitHEVC.h | 8 ++-- YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp | 10 ++-- YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h | 10 ++-- YUViewLib/src/parser/VVC/AnnexBVVC.cpp | 20 ++++---- YUViewLib/src/parser/VVC/AnnexBVVC.h | 10 ++-- YUViewLib/src/parser/VVC/NalUnitVVC.h | 10 ++-- .../playlistItemCompressedVideo.h | 8 ++-- 22 files changed, 139 insertions(+), 133 deletions(-) rename YUViewLib/src/parser/AV1/{AV1OBU.cpp => ParserAV1OBU.cpp} (78%) rename YUViewLib/src/parser/AV1/{AV1OBU.h => ParserAV1OBU.h} (84%) diff --git a/YUViewLib/src/common/Typedef.h b/YUViewLib/src/common/Typedef.h index 963dfa4b5..01349f674 100644 --- a/YUViewLib/src/common/Typedef.h +++ b/YUViewLib/src/common/Typedef.h @@ -320,7 +320,7 @@ struct FileStartEndPos int64_t end{}; }; -std::string to_string(const FileStartEndPos fileStartEndPos) +static std::string to_string(const FileStartEndPos &fileStartEndPos) { std::ostringstream ss; ss << "(" << fileStartEndPos.start << ", " << fileStartEndPos.end << ")"; diff --git a/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp b/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp index b5889bf0b..3be213c51 100644 --- a/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp +++ b/YUViewLib/src/filesource/FileSourceAnnexBFile.cpp @@ -110,7 +110,7 @@ DataAndStartEndPos FileSourceAnnexBFile::getNextNALUnit(bool getLastDataAgain) } 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->lastDataAndPos.data += diff --git a/YUViewLib/src/filesource/FileSourceOBUFile.cpp b/YUViewLib/src/filesource/FileSourceOBUFile.cpp index 3afc80697..c5b47bf0f 100644 --- a/YUViewLib/src/filesource/FileSourceOBUFile.cpp +++ b/YUViewLib/src/filesource/FileSourceOBUFile.cpp @@ -64,7 +64,7 @@ bool FileSourceOBUFile::openFile(const QString &fileName) return true; } -DataAndStartEndPos FileSourceOBUFile::getNextOBU(bool getLastDataAgain = false) +DataAndStartEndPos FileSourceOBUFile::getNextOBU(bool getLastDataAgain) { FileStartEndPos fileStartEndPos; fileStartEndPos.start = this->pos(); diff --git a/YUViewLib/src/parser/AV1/AV1OBU.cpp b/YUViewLib/src/parser/AV1/ParserAV1OBU.cpp similarity index 78% rename from YUViewLib/src/parser/AV1/AV1OBU.cpp rename to YUViewLib/src/parser/AV1/ParserAV1OBU.cpp index dfa58a739..c60c1b165 100644 --- a/YUViewLib/src/parser/AV1/AV1OBU.cpp +++ b/YUViewLib/src/parser/AV1/ParserAV1OBU.cpp @@ -30,7 +30,7 @@ * along with this program. If not, see . */ -#include "AV1OBU.h" +#include "ParserAV1OBU.h" #include "OpenBitstreamUnit.h" #include "frame_header_obu.h" @@ -47,12 +47,10 @@ ParserAV1OBU::ParserAV1OBU(QObject *parent) : Base(parent) this->decValues.PrevFrameID = -1; } -Base::ParseResult -ParserAV1OBU::parseAndAddUnit(int obuID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional obuStartEndPosFile = {}, - std::shared_ptr parent = nullptr) +Base::ParseResult ParserAV1OBU::parseAndAddOBU(int obuID, + const ByteVector & data, + std::optional obuStartEndPosFile, + std::shared_ptr parent) { // Use the given tree item. If it is not set, use the nalUnitMode (if active). // We don't set data (a name) for this item yet. @@ -71,14 +69,13 @@ ParserAV1OBU::parseAndAddUnit(int obuI auto nrHeaderBytes = reader.nrBytesRead(); - std::string obuTypeName; - std::string errorText; + ParseResult parseResult; try { if (obu.header.obu_type == ObuType::OBU_TEMPORAL_DELIMITER) { decValues.SeenFrameHeader = false; - obuTypeName = "Temporal Delimiter"; + parseResult.unitTypeName = "Temporal Delimiter"; } else if (obu.header.obu_type == ObuType::OBU_SEQUENCE_HEADER) { @@ -87,8 +84,8 @@ ParserAV1OBU::parseAndAddUnit(int obuI this->active_sequence_header = new_sequence_header; - obuTypeName = "Sequence Header"; - obu.payload = new_sequence_header; + parseResult.unitTypeName = "Sequence Header"; + obu.payload = new_sequence_header; } else if (obu.header.obu_type == ObuType::OBU_FRAME || obu.header.obu_type == ObuType::OBU_FRAME_HEADER) @@ -100,33 +97,33 @@ ParserAV1OBU::parseAndAddUnit(int obuI obu.header.temporal_id, obu.header.spatial_id); - obuTypeName = "Frame"; - obu.payload = new_frame_header; + parseResult.unitTypeName = "Frame"; + obu.payload = new_frame_header; } } catch (const std::exception &e) { - errorText = " ERROR " + std::string(e.what()); - obuTypeName = "Error"; + parseResult.errorMessage = " ERROR " + std::string(e.what()); + parseResult.success = false; } if (obuRoot) { auto name = "OBU " + std::to_string(obu.obu_idx) + ": " + - obuTypeCoding.getMeaning(obu.header.obu_type) + " " + obuTypeName; - if (!errorText.empty()) + obuTypeCoding.getMeaning(obu.header.obu_type) + " " + + parseResult.unitTypeName.value_or(""); + if (!parseResult.success) { obuRoot->setError(); - name += " " + errorText; + name += " " + parseResult.errorMessage; } obuRoot->setProperties(name); } - auto sizeRead = data.size(); if (obu.header.obu_has_size_field) - sizeRead = obu.header.obu_size + nrHeaderBytes; + parseResult.unitSize = obu.header.obu_size + nrHeaderBytes; - return {sizeRead, obuTypeName}; + return parseResult; } } // namespace parser \ No newline at end of file diff --git a/YUViewLib/src/parser/AV1/AV1OBU.h b/YUViewLib/src/parser/AV1/ParserAV1OBU.h similarity index 84% rename from YUViewLib/src/parser/AV1/AV1OBU.h rename to YUViewLib/src/parser/AV1/ParserAV1OBU.h index 2fe6b755d..ac3c15e7c 100644 --- a/YUViewLib/src/parser/AV1/AV1OBU.h +++ b/YUViewLib/src/parser/AV1/ParserAV1OBU.h @@ -48,11 +48,10 @@ class ParserAV1OBU : public Base ParserAV1OBU(QObject *parent = nullptr); ~ParserAV1OBU() {} - ParseResult parseAndAddUnit(int obuID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = nullptr); + ParseResult parseAndAddOBU(int obuID, + const ByteVector & data, + std::optional obuFileStartEndPos = {}, + std::shared_ptr parent = nullptr); bool runParsingOfFile(QString) override { diff --git a/YUViewLib/src/parser/AVC/AnnexBAVC.cpp b/YUViewLib/src/parser/AVC/AnnexBAVC.cpp index 239988500..9e4fbaaf1 100644 --- a/YUViewLib/src/parser/AVC/AnnexBAVC.cpp +++ b/YUViewLib/src/parser/AVC/AnnexBAVC.cpp @@ -178,11 +178,11 @@ video::yuv::PixelFormatYUV AnnexBAVC::getPixelFormat() const } AnnexB::ParseResult -AnnexBAVC::parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBAVC::parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; @@ -607,8 +607,8 @@ std::optional AnnexBAVC::getSeekData(int iFrameNr) { // Seek here AnnexB::SeekData seekData; - if (nal->filePosStartEnd) - seekData.filePos = nal->filePosStartEnd->start; + if (nal->fileStartEndPos) + seekData.filePos = nal->fileStartEndPos->start; // Get the bitstream of all active parameter sets for (const auto &nalMap : {activeSPSNal, activePPSNal}) diff --git a/YUViewLib/src/parser/AVC/AnnexBAVC.h b/YUViewLib/src/parser/AVC/AnnexBAVC.h index b9d5ee835..79cc981bf 100644 --- a/YUViewLib/src/parser/AVC/AnnexBAVC.h +++ b/YUViewLib/src/parser/AVC/AnnexBAVC.h @@ -80,11 +80,11 @@ class AnnexBAVC : public AnnexB Size getSequenceSizeSamples() const override; video::yuv::PixelFormatYUV getPixelFormat() const override; - ParseResult parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = nullptr) override; + ParseResult parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = nullptr) override; std::optional getSeekData(int iFrameNr) override; QByteArray getExtradata() override; diff --git a/YUViewLib/src/parser/AVC/NalUnitAVC.h b/YUViewLib/src/parser/AVC/NalUnitAVC.h index 8771ab91d..32c12f667 100644 --- a/YUViewLib/src/parser/AVC/NalUnitAVC.h +++ b/YUViewLib/src/parser/AVC/NalUnitAVC.h @@ -51,8 +51,8 @@ class NalRBSP class NalUnitAVC { public: - NalUnitAVC(int nalIdx, std::optional filePosStartEnd) - : nalIdx(nalIdx), filePosStartEnd(filePosStartEnd) + NalUnitAVC(int nalIdx, std::optional fileStartEndPos) + : nalIdx(nalIdx), fileStartEndPos(fileStartEndPos) { } @@ -61,11 +61,9 @@ class NalUnitAVC int nalIdx{}; // Pointer to the first byte of the start code of the NAL unit (if known) - std::optional filePosStartEnd; + std::optional fileStartEndPos; ByteVector rawData; }; -using NalMap = std::map>; - } // namespace parser::avc \ No newline at end of file diff --git a/YUViewLib/src/parser/AVFormat/AVFormat.cpp b/YUViewLib/src/parser/AVFormat/AVFormat.cpp index a8dc3a8d5..28645f22b 100644 --- a/YUViewLib/src/parser/AVFormat/AVFormat.cpp +++ b/YUViewLib/src/parser/AVFormat/AVFormat.cpp @@ -164,7 +164,7 @@ bool AVFormat::parseExtradata_AVC(ByteVector &extradata) SubByteReaderLoggingSubLevel spsSubLevel(reader, "SPS " + std::to_string(i)); auto sps_size = reader.readBits("sps_size", 16); auto spsData = reader.readBytes("", sps_size, Options().withLoggingDisabled()); - auto parseResult = this->annexBParser->parseAndAddUnit( + auto parseResult = this->annexBParser->parseAndAddNALUnit( nalID++, spsData, {}, {}, reader.getCurrentItemTree()); if (parseResult.success && parseResult.bitrateEntry) this->bitratePlotModel->addBitratePoint(this->videoStreamIndex, *parseResult.bitrateEntry); @@ -176,7 +176,7 @@ bool AVFormat::parseExtradata_AVC(ByteVector &extradata) SubByteReaderLoggingSubLevel ppsSubLevel(reader, "PPS " + std::to_string(i)); auto pps_size = reader.readBits("pps_size", 16); auto pspsData = reader.readBytes("", pps_size, Options().withLoggingDisabled()); - auto parseResult = this->annexBParser->parseAndAddUnit( + auto parseResult = this->annexBParser->parseAndAddNALUnit( nalID++, pspsData, {}, {}, reader.getCurrentItemTree()); if (parseResult.success && parseResult.bitrateEntry) this->bitratePlotModel->addBitratePoint(this->videoStreamIndex, *parseResult.bitrateEntry); @@ -295,20 +295,20 @@ AVFormat::parseByteVectorAnnexBStartCodes(ByteVector & data, const auto sizeStartCode = (dataFormat == PacketDataFormat::RawNAL ? 3u : 4u); auto nalID = 0u; - std::map naNames; + std::map nalNames; while (itStartCode != data.end()) { auto itNextStartCode = getNextNalStart(itStartCode); auto nalData = ByteVector(itStartCode + sizeStartCode, itNextStartCode); auto parseResult = - this->annexBParser->parseAndAddUnit(nalID++, nalData, packetBitrateEntry, {}, item); + this->annexBParser->parseAndAddNALUnit(nalID++, nalData, packetBitrateEntry, {}, item); if (parseResult.success && parseResult.bitrateEntry) this->bitratePlotModel->addBitratePoint(this->videoStreamIndex, *parseResult.bitrateEntry); - if (parseResult.success && parseResult.nalTypeName) - naNames[*parseResult.nalTypeName]++; + if (parseResult.success && parseResult.unitTypeName) + nalNames[*parseResult.unitTypeName]++; itStartCode = itNextStartCode; } - return naNames; + return nalNames; } bool AVFormat::parseAVPacket(unsigned packetID, unsigned streamPacketID, AVPacketWrapper &packet) @@ -397,32 +397,44 @@ bool AVFormat::parseAVPacket(unsigned packetID, unsigned streamPacketID, AVPacke } else if (this->obuParser) { - auto obuID = 0u; + auto obuID = 0; auto posInData = avpacketData.begin(); while (true) { - pairUint64 obuStartEndPosFile; // Not used try { - auto data = ByteVector(posInData, avpacketData.end()); - auto [nrBytesRead, obuTypeName] = - this->obuParser->parseAndAddOBU(obuID, data, itemTree, obuStartEndPosFile); + auto data = ByteVector(posInData, avpacketData.end()); + auto parseResult = this->obuParser->parseAndAddOBU(obuID, data, {}, itemTree); + + if (!parseResult.success) + { + DEBUG_AVFORMAT("AVFormat::parseAVPacket Failed to parse OBU"); + return false; + } + DEBUG_AVFORMAT( "AVFormat::parseAVPacket parsed OBU %d header %d bytes", obuID, nrBytesRead); - if (!obuTypeName.empty()) - unitNames[obuTypeName]++; + if (parseResult.unitTypeName) + unitNames[*parseResult.unitTypeName]++; + + if (!parseResult.unitSize) + { + DEBUG_AVFORMAT("AVFormat::parseAVPacket OBU size unknown. Assuming all bytes of packet " + "to be one OBU."); + break; + } - constexpr auto minOBUSize = 3u; + constexpr auto minOBUSize = 3; auto remaining = std::distance(posInData, avpacketData.end()); - if (remaining < 0 || nrBytesRead + minOBUSize >= size_t(std::abs(remaining))) + if (remaining < 0 || parseResult.unitSize.value() + minOBUSize >= std::abs(remaining)) break; - posInData += nrBytesRead; + posInData += parseResult.unitSize.value(); } catch (...) { - // Catch exceptions and just return + DEBUG_AVFORMAT("AVFormat::parseAVPacket Failed to parse OBU"); break; } diff --git a/YUViewLib/src/parser/AVFormat/AVFormat.h b/YUViewLib/src/parser/AVFormat/AVFormat.h index e6e291295..1af93e991 100644 --- a/YUViewLib/src/parser/AVFormat/AVFormat.h +++ b/YUViewLib/src/parser/AVFormat/AVFormat.h @@ -32,11 +32,11 @@ #pragma once -#include "../AV1/AV1OBU.h" -#include "../AnnexB.h" -#include "../Base.h" #include #include +#include +#include +#include #include @@ -65,10 +65,10 @@ class AVFormat : public Base // This function can run in a separate thread bool runParsingOfFile(QString compressedFilePath) override; - int getVideoStreamIndex() override { return videoStreamIndex; } + int getVideoStreamIndex() override { return this->videoStreamIndex; } private: - FFmpeg::AVCodecIDWrapper codecID; + FFmpeg::AVCodecIDWrapper codecID{}; bool parseExtradata(ByteVector &extradata); void parseMetadata(const StringPairVec &metadata); diff --git a/YUViewLib/src/parser/AnnexB.cpp b/YUViewLib/src/parser/AnnexB.cpp index 1226f0e3a..2d2ea7050 100644 --- a/YUViewLib/src/parser/AnnexB.cpp +++ b/YUViewLib/src/parser/AnnexB.cpp @@ -188,11 +188,11 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge auto [nalData, nalStartEndPos] = file->getNextNALUnit(false); auto parsingResult = - this->parseAndAddUnit(nalID, - reader::SubByteReaderLogging::convertToByteVector(nalData), - {}, - nalStartEndPos, - nullptr); + this->parseAndAddNALUnit(nalID, + reader::SubByteReaderLogging::convertToByteVector(nalData), + {}, + nalStartEndPos, + nullptr); if (!parsingResult.success) { DEBUG_ANNEXB("AnnexB::parseAndAddNALUnit Error parsing NAL " << nalID); @@ -253,7 +253,7 @@ bool AnnexB::parseAnnexBFile(std::unique_ptr &file, QWidge try { - auto parseResult = this->parseAndAddUnit(-1, {}, {}, {}); + auto parseResult = this->parseAndAddNALUnit(-1, {}, {}, {}); if (!parseResult.success) DEBUG_ANNEXB("AnnexB::parseAndAddNALUnit Error finalizing parsing. This should not happen."); } diff --git a/YUViewLib/src/parser/AnnexB.h b/YUViewLib/src/parser/AnnexB.h index 942beb763..5b35ffa27 100644 --- a/YUViewLib/src/parser/AnnexB.h +++ b/YUViewLib/src/parser/AnnexB.h @@ -110,6 +110,12 @@ class AnnexB : public Base // Called from the bitstream analyzer. This function can run in a background process. bool runParsingOfFile(QString compressedFilePath) override; + virtual ParseResult parseAndAddNALUnit(int unitID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = nullptr) = 0; + protected: struct AnnexBFrame { diff --git a/YUViewLib/src/parser/Base.h b/YUViewLib/src/parser/Base.h index 4bc86ad39..c5105f2e6 100644 --- a/YUViewLib/src/parser/Base.h +++ b/YUViewLib/src/parser/Base.h @@ -82,16 +82,12 @@ class Base : public QObject */ struct ParseResult { - ParseResult() = default; bool success{false}; + std::string errorMessage; std::optional unitTypeName; + std::optional unitSize; std::optional bitrateEntry; }; - virtual ParseResult parseAndAddUnit(int unitID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = nullptr) = 0; // For parsing files in the background (threading) in the bitstream analysis dialog: virtual bool runParsingOfFile(QString fileName) = 0; diff --git a/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp b/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp index d99056c4d..f67b87e1c 100644 --- a/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp +++ b/YUViewLib/src/parser/HEVC/AnnexBHEVC.cpp @@ -165,8 +165,8 @@ std::optional AnnexBHEVC::getSeekData(int iFrameNr) { // Seek here AnnexB::SeekData seekData; - if (nal->filePosStartEnd) - seekData.filePos = nal->filePosStartEnd->first; + if (nal->fileStartEndPos) + seekData.filePos = nal->fileStartEndPos->start; for (const auto &nalMap : {activeVPSNal, activeSPSNal, activePPSNal}) { @@ -270,11 +270,11 @@ Ratio AnnexBHEVC::getSampleAspectRatio() } AnnexB::ParseResult -AnnexBHEVC::parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBHEVC::parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; diff --git a/YUViewLib/src/parser/HEVC/AnnexBHEVC.h b/YUViewLib/src/parser/HEVC/AnnexBHEVC.h index ddf0c7539..d08cdf721 100644 --- a/YUViewLib/src/parser/HEVC/AnnexBHEVC.h +++ b/YUViewLib/src/parser/HEVC/AnnexBHEVC.h @@ -73,11 +73,11 @@ class AnnexBHEVC : public AnnexB IntPair getProfileLevel() override; Ratio getSampleAspectRatio() override; - ParseResult parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = nullptr) override; + ParseResult parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = nullptr) override; protected: // ----- Some nested classes that are only used in the scope of this file handler class diff --git a/YUViewLib/src/parser/HEVC/NalUnitHEVC.h b/YUViewLib/src/parser/HEVC/NalUnitHEVC.h index c744ec6f6..0a0fb03da 100644 --- a/YUViewLib/src/parser/HEVC/NalUnitHEVC.h +++ b/YUViewLib/src/parser/HEVC/NalUnitHEVC.h @@ -49,8 +49,8 @@ class NalRBSP class NalUnitHEVC { public: - NalUnitHEVC(int nalIdx, std::optional filePosStartEnd) - : nalIdx(nalIdx), filePosStartEnd(filePosStartEnd) + NalUnitHEVC(int nalIdx, std::optional fileStartEndPos) + : nalIdx(nalIdx), fileStartEndPos(fileStartEndPos) { } @@ -59,11 +59,9 @@ class NalUnitHEVC int nalIdx{}; // Pointer to the first byte of the start code of the NAL unit (if known) - std::optional filePosStartEnd; + std::optional fileStartEndPos; ByteVector rawData; }; -using NalMap = std::map>; - } // namespace parser::hevc \ No newline at end of file diff --git a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp index ecf5ea771..d97c281a5 100644 --- a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp +++ b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.cpp @@ -57,11 +57,11 @@ namespace parser using namespace mpeg2; AnnexB::ParseResult -AnnexBMpeg2::parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBMpeg2::parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; diff --git a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h index b3ae5161d..acf6c3985 100644 --- a/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h +++ b/YUViewLib/src/parser/Mpeg2/AnnexBMpeg2.h @@ -58,11 +58,11 @@ class AnnexBMpeg2 : public AnnexB Size getSequenceSizeSamples() const override; video::yuv::PixelFormatYUV getPixelFormat() const override; - ParseResult parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = {}) override; + ParseResult parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = {}) override; // TODO: Reading from raw mpeg2 streams not supported (yet? Is this even defined / possible?) virtual std::optional getSeekData(int iFrameNr) override diff --git a/YUViewLib/src/parser/VVC/AnnexBVVC.cpp b/YUViewLib/src/parser/VVC/AnnexBVVC.cpp index 8f939edaa..6366797ae 100644 --- a/YUViewLib/src/parser/VVC/AnnexBVVC.cpp +++ b/YUViewLib/src/parser/VVC/AnnexBVVC.cpp @@ -183,8 +183,8 @@ std::optional AnnexBVVC::getSeekData(int iFrameNr) { // Seek here AnnexB::SeekData seekData; - if (nal->filePosStartEnd) - seekData.filePos = nal->filePosStartEnd->first; + if (nal->fileStartEndPos) + seekData.filePos = nal->fileStartEndPos->start; for (const auto &nalMap : {activeVPSNal, activeSPSNal, activePPSNal}) { @@ -217,7 +217,7 @@ std::optional AnnexBVVC::getSeekData(int iFrameNr) { auto aps = std::dynamic_pointer_cast(nal->rbsp); auto apsType = apsParamTypeMapper.indexOf(aps->aps_params_type); - activeAPSNal[{apsType, aps->aps_adaptation_parameter_set_id}] = nal; + activeAPSNal[{static_cast(apsType), aps->aps_adaptation_parameter_set_id}] = nal; } } @@ -267,11 +267,11 @@ Ratio AnnexBVVC::getSampleAspectRatio() } AnnexB::ParseResult -AnnexBVVC::parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile, - std::shared_ptr parent) +AnnexBVVC::parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile, + std::shared_ptr parent) { AnnexB::ParseResult parseResult; parseResult.success = true; @@ -370,7 +370,9 @@ AnnexBVVC::parseAndAddUnit(int nalID, newAPS->parse(reader); auto apsType = apsParamTypeMapper.indexOf(newAPS->aps_params_type); - this->activeParameterSets.apsMap[{apsType, newAPS->aps_adaptation_parameter_set_id}] = newAPS; + this->activeParameterSets + .apsMap[{static_cast(apsType), newAPS->aps_adaptation_parameter_set_id}] = + newAPS; specificDescription += " ID " + std::to_string(newAPS->aps_adaptation_parameter_set_id); diff --git a/YUViewLib/src/parser/VVC/AnnexBVVC.h b/YUViewLib/src/parser/VVC/AnnexBVVC.h index 0bfb30cc5..fe7bf7340 100644 --- a/YUViewLib/src/parser/VVC/AnnexBVVC.h +++ b/YUViewLib/src/parser/VVC/AnnexBVVC.h @@ -94,11 +94,11 @@ class AnnexBVVC : public AnnexB IntPair getProfileLevel() override; Ratio getSampleAspectRatio() override; - ParseResult parseAndAddUnit(int nalID, - const ByteVector & data, - std::optional bitrateEntry, - std::optional nalStartEndPosFile = {}, - std::shared_ptr parent = {}) override; + ParseResult parseAndAddNALUnit(int nalID, + const ByteVector & data, + std::optional bitrateEntry, + std::optional nalStartEndPosFile = {}, + std::shared_ptr parent = {}) override; protected: // The PicOrderCntMsb may be reset to zero for IDR frames. In order to count the global POC, we diff --git a/YUViewLib/src/parser/VVC/NalUnitVVC.h b/YUViewLib/src/parser/VVC/NalUnitVVC.h index 0321e622c..9370707ef 100644 --- a/YUViewLib/src/parser/VVC/NalUnitVVC.h +++ b/YUViewLib/src/parser/VVC/NalUnitVVC.h @@ -49,14 +49,14 @@ class NalRBSP class NalUnitVVC { public: - NalUnitVVC(int nalIdx, std::optional filePosStartEnd) - : nalIdx(nalIdx), filePosStartEnd(filePosStartEnd) + NalUnitVVC(int nalIdx, std::optional fileStartEndPos) + : nalIdx(nalIdx), fileStartEndPos(fileStartEndPos) { } int nalIdx{}; // Pointer to the first byte of the start code of the NAL unit (if known) - std::optional filePosStartEnd; + std::optional fileStartEndPos; nal_unit_header header; std::shared_ptr rbsp; @@ -64,6 +64,4 @@ class NalUnitVVC ByteVector rawData; }; -using NalMap = std::map>; - -} // namespace parser::vvc \ No newline at end of file +} // namespace parser::vvc diff --git a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h index 96473414e..edb768364 100644 --- a/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h +++ b/YUViewLib/src/playlistitem/playlistItemCompressedVideo.h @@ -36,8 +36,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -148,9 +148,9 @@ class playlistItemCompressedVideo : public playlistItemWithVideo // The same applies for Raw OBU files. We open the input twice (for caching and loading). The // parser is only needed once. - std::unique_ptr inputFileOBULoading; - std::unique_ptr inputFileOBUCaching; - std::unique_ptr inputFileOBUParser; + std::unique_ptr inputFileOBULoading; + std::unique_ptr inputFileOBUCaching; + std::unique_ptr inputFileOBUParser; // Which type is the input? InputFormat inputFormat{InputFormat::Invalid}; From 80a3ccc60746fda531ca1c90dc568198365110e9 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Mon, 21 Nov 2022 13:22:25 +0100 Subject: [PATCH 11/13] More work --- YUViewLib/src/common/Functions.cpp | 25 +++ YUViewLib/src/common/Functions.h | 33 +++ YUViewLib/src/common/Typedef.h | 3 + YUViewLib/src/ffmpeg/AVCodecIDWrapper.h | 15 +- .../src/ffmpeg/AVFormatContextWrapper.cpp | 208 +++++++++++++----- YUViewLib/src/ffmpeg/AVFormatContextWrapper.h | 42 ++-- YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.cpp | 41 ++-- YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.h | 2 +- .../src/ffmpeg/FFmpegLibraryFunctions.cpp | 105 ++++----- YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.h | 22 +- YUViewLib/src/ffmpeg/FFmpegVersionHandler.cpp | 86 ++++---- YUViewLib/src/ffmpeg/FFmpegVersionHandler.h | 42 ++-- .../src/filesource/FileSourceFFmpegFile.cpp | 18 +- .../src/filesource/FileSourceFFmpegFile.h | 9 +- YUViewLib/src/parser/AV1/ParserAV1OBU.h | 6 +- YUViewLib/src/parser/AVFormat/AVFormat.cpp | 17 +- YUViewLib/src/parser/AVFormat/AVFormat.h | 8 +- YUViewLib/src/parser/AnnexB.cpp | 5 + YUViewLib/src/parser/AnnexB.h | 6 +- YUViewLib/src/parser/Base.cpp | 22 +- YUViewLib/src/parser/Base.h | 9 +- YUViewUnitTest/YUViewUnitTest.pro | 1 + .../filesourceAnnexB/FilesourceAnnexBTest.cpp | 38 ++-- .../parser/AnnexBAVC/AnnexBAVCTest.cpp | 27 +++ .../parser/AnnexBAVC/AnnexBAVCTest.pro | 16 ++ YUViewUnitTest/parser/parser.pro | 3 + 26 files changed, 520 insertions(+), 289 deletions(-) create mode 100644 YUViewUnitTest/parser/AnnexBAVC/AnnexBAVCTest.cpp create mode 100644 YUViewUnitTest/parser/AnnexBAVC/AnnexBAVCTest.pro create mode 100644 YUViewUnitTest/parser/parser.pro diff --git a/YUViewLib/src/common/Functions.cpp b/YUViewLib/src/common/Functions.cpp index 07a7b7b0f..2c8813d77 100644 --- a/YUViewLib/src/common/Functions.cpp +++ b/YUViewLib/src/common/Functions.cpp @@ -168,6 +168,31 @@ std::string toLower(std::string str) 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::optional toUnsigned(const std::string &text) { try diff --git a/YUViewLib/src/common/Functions.h b/YUViewLib/src/common/Functions.h index 667c7fe41..f0cb78902 100644 --- a/YUViewLib/src/common/Functions.h +++ b/YUViewLib/src/common/Functions.h @@ -65,6 +65,39 @@ QString formatDataSize(double size, bool isBits = false); QStringList toQStringList(const std::vector &stringVec); std::string toLower(std::string str); +template std::string to_string(std::vector items, 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; +} + +std::string vstring(const char *format, va_list vargs); + +std::string formatString(std::string format, 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; +} + inline std::string boolToString(bool b) { return b ? "True" : "False"; diff --git a/YUViewLib/src/common/Typedef.h b/YUViewLib/src/common/Typedef.h index 01349f674..cf967c6c2 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; @@ -327,6 +328,8 @@ static std::string to_string(const FileStartEndPos &fileStartEndPos) return ss.str(); } +using StreamsInfo = std::map; + // A list of value pair lists, where every list has a string (title) class ValuePairListSets : public QList> { 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/AVFormatContextWrapper.cpp b/YUViewLib/src/ffmpeg/AVFormatContextWrapper.cpp index c7c3410b6..0b1eac27a 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,117 @@ 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: + auto p = reinterpret_cast(this->ctx); + return p->nb_streams; + case 57: + auto p = reinterpret_cast(this->ctx); + return p->nb_streams; + case 58: + auto p = reinterpret_cast(this->ctx); + return p->nb_streams; + case 59: + auto p = reinterpret_cast(this->ctx); + return p->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 >= this->getNbStreams()) + throw std::runtime_error("Invalid stream index"); + + switch (this->libVer.avformat.major) + { + case 56: + auto p = reinterpret_cast(this->ctx); + return AVStreamWrapper(p->streams[idx], this->libVer); + case 57: + auto p = reinterpret_cast(this->ctx); + return AVStreamWrapper(p->streams[idx], this->libVer); + case 58: + auto p = reinterpret_cast(this->ctx); + return AVStreamWrapper(p->streams[idx], this->libVer); + case 59: + auto p = reinterpret_cast(this->ctx); + return AVStreamWrapper(p->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: + auto p = reinterpret_cast(this->ctx); + return AVInputFormatWrapper(p->iformat, libVer); + case 57: + auto p = reinterpret_cast(this->ctx); + return AVInputFormatWrapper(p->iformat, libVer); + case 58: + auto p = reinterpret_cast(this->ctx); + return AVInputFormatWrapper(p->iformat, libVer); + case 59: + auto p = reinterpret_cast(this->ctx); + return AVInputFormatWrapper(p->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: + auto p = reinterpret_cast(this->ctx); + return p->start_time; + case 57: + auto p = reinterpret_cast(this->ctx); + return p->start_time; + case 58: + auto p = reinterpret_cast(this->ctx); + return p->start_time; + case 59: + auto p = reinterpret_cast(this->ctx); + return p->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: + auto p = reinterpret_cast(this->ctx); + return p->duration; + case 57: + auto p = reinterpret_cast(this->ctx); + return p->duration; + case 58: + auto p = reinterpret_cast(this->ctx); + return p->duration; + case 59: + auto p = reinterpret_cast(this->ctx); + return p->duration; + + default: + throw std::runtime_error("Invalid library version"); + } } AVFormatContext *AVFormatContextWrapper::getFormatCtx() const @@ -236,10 +321,26 @@ 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: + auto p = reinterpret_cast(this->ctx); + return AVDictionaryWrapper(p->metadata); + case 57: + auto p = reinterpret_cast(this->ctx); + return AVDictionaryWrapper(p->metadata); + case 58: + auto p = reinterpret_cast(this->ctx); + return AVDictionaryWrapper(p->metadata); + case 59: + auto p = reinterpret_cast(this->ctx); + return AVDictionaryWrapper(p->metadata); + + default: + throw std::runtime_error("Invalid library version"); + } } void AVFormatContextWrapper::update() @@ -256,8 +357,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 +367,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 +385,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 +395,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 +413,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 +423,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 +441,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 +451,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 +467,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; 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/FFMpegLibrariesTypes.cpp b/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.cpp index b6d46af7b..44d7bb764 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, AVRational 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..ed20e2279 100644 --- a/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.h +++ b/YUViewLib/src/ffmpeg/FFMpegLibrariesTypes.h @@ -404,6 +404,6 @@ struct LibraryVersion Version swresample{}; }; -QString timestampToString(int64_t timestamp, AVRational timebase); +std::string formatWithReadableFormat(int64_t timestamp, AVRational 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..b2eaa2070 100644 --- a/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.h +++ b/YUViewLib/src/ffmpeg/FFmpegLibraryFunctions.h @@ -46,14 +46,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 +104,7 @@ class FFmpegLibraryFunctions std::function avutil_version; std::function av_dict_set; - std::function av_dict_get; std::function @@ -124,14 +124,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..187fa5d6f 100644 --- a/YUViewLib/src/ffmpeg/FFmpegVersionHandler.cpp +++ b/YUViewLib/src/ffmpeg/FFmpegVersionHandler.cpp @@ -201,36 +201,29 @@ auto SupportedLibraryVersionCombinations = {LibraryVersion(57, 59, 59, 4), //} // -QString FFmpegVersionHandler::getLibVersionString() 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)); - - return s; -} - -AVCodecIDWrapper FFmpegVersionHandler::getCodecIDWrapper(AVCodecID id) -{ - auto codecName = QString(lib.avcodec.avcodec_get_name(id)); +std::string FFmpegVersionHandler::getLibVersionFormatted() const +{ + 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 stream.str(); +} + +AVCodecIDWrapper FFmpegVersionHandler::getCodecIDWrapper(AVCodecID id) const +{ + 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 +232,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 +256,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 +293,7 @@ void FFmpegVersionHandler::flush_buffers(AVCodecContextWrapper &decCtx) lib.avcodec.avcodec_flush_buffers(decCtx.getCodec()); } -QStringList FFmpegVersionHandler::logListFFmpeg; +StringVec FFmpegVersionHandler::logListFFmpeg; FFmpegVersionHandler::FFmpegVersionHandler() { @@ -312,11 +303,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,18 +322,18 @@ 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(); + 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."); diff --git a/YUViewLib/src/ffmpeg/FFmpegVersionHandler.h b/YUViewLib/src/ffmpeg/FFmpegVersionHandler.h index 3a914e686..d5c62173f 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(std::string avCodecLib, + std::string avFormatLib, + std::string avUtilLib, + 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(std::string path); // Try to load the four specific library files - bool loadFFMpegLibrarySpecific(QString avFormatLib, - QString avCodecLib, - QString avUtilLib, - QString swResampleLib); + bool loadFFMpegLibrarySpecific(std::string avFormatLib, + std::string avCodecLib, + std::string avUtilLib, + 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/FileSourceFFmpegFile.cpp b/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp index f86d0222f..9ae611221 100644 --- a/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp +++ b/YUViewLib/src/filesource/FileSourceFFmpegFile.cpp @@ -653,11 +653,15 @@ indexRange FileSourceFFmpegFile::getDecodableFrameLimits() const return range; } -QList FileSourceFFmpegFile::getFileInfoForAllStreams() +StringPairVec FileSourceFFmpegFile::getGeneralInfo() const { - QList info; + return this->formatCtx.getInfoText(); +} + +StreamsInfo FileSourceFFmpegFile::getStreamsInfo(bool detailedInfo) const +{ + StreamsInfo info; - info += formatCtx.getInfoText(); for (unsigned i = 0; i < this->formatCtx.getNbStreams(); i++) { auto stream = this->formatCtx.getStream(i); @@ -668,14 +672,14 @@ QList FileSourceFFmpegFile::getFileInfoForAllStreams() return info; } -QList FileSourceFFmpegFile::getTimeBaseAllStreams() +std::vector FileSourceFFmpegFile::getTimeBaseAllStreams() { - QList timeBaseList; + std::vector timeBaseList; for (unsigned i = 0; i < this->formatCtx.getNbStreams(); i++) { auto stream = this->formatCtx.getStream(i); - timeBaseList.append(stream.getTimeBase()); + timeBaseList.push_back(stream.getTimeBase()); } return timeBaseList; @@ -685,7 +689,7 @@ QList FileSourceFFmpegFile::getShortStreamDescriptionAllStreams() { QList descriptions; - for (unsigned i = 0; i < formatCtx.getNbStreams(); i++) + for (unsigned i = 0; i < this->formatCtx.getNbStreams(); i++) { QString description; auto stream = this->formatCtx.getStream(i); diff --git a/YUViewLib/src/filesource/FileSourceFFmpegFile.h b/YUViewLib/src/filesource/FileSourceFFmpegFile.h index 551d1e45c..0318867bb 100644 --- a/YUViewLib/src/filesource/FileSourceFFmpegFile.h +++ b/YUViewLib/src/filesource/FileSourceFFmpegFile.h @@ -37,8 +37,8 @@ #include #include #include -#include