From a2bbbab3d22af7ce684931cf321d07819ccbdfd3 Mon Sep 17 00:00:00 2001 From: for13to1 Date: Sat, 27 Jun 2026 21:47:07 +0800 Subject: [PATCH 1/3] fix: default to YUV_400 format for .raw files - Initialize YUV video handler to YUV_400 for .raw extension files - Guard against empty frameSize optional in checkSpecificFileExtensions --- YUViewLib/src/playlistitem/playlistItemRawFile.cpp | 4 ++++ YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp index d3d2054fb..0ae98f5fb 100644 --- a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp +++ b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp @@ -104,6 +104,10 @@ playlistItemRawFile::playlistItemRawFile(const QString &rawFilePath, { this->video = std::make_unique(); this->rawFormat = video::RawFormat::YUV; + if (isInExtensions(ext, RAW_BAYER_EXTENSIONS)) + { + this->getYUVVideo()->setPixelFormatYUV(video::yuv::PixelFormatYUV(video::yuv::Subsampling::YUV_400, 8, video::yuv::PlaneOrder::YUV)); + } } else if (isInExtensions(ext, RGB_EXTENSIONS) || isInExtensions(ext, RGBA_EXTENSIONS) || isInExtensions(ext, CMYK_EXTENSIONS) || fmt.toLower() == "rgb") diff --git a/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp b/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp index 69a8d43ea..94300d437 100644 --- a/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp +++ b/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp @@ -243,6 +243,8 @@ checkSpecificFileExtensions(const GuessedFrameFormat &guessedFrameFormat, { const auto rawBayerFormat = PixelFormatYUV(Subsampling::YUV_400, guessedFrameFormat.bitDepth.value_or(8)); + if (!guessedFrameFormat.frameSize) + return rawBayerFormat; if (doesPixelFormatMatchFileSize( rawBayerFormat, *guessedFrameFormat.frameSize, fileInfo.fileSize)) return rawBayerFormat; From 90b985071f40a8f9dbf6350b5e31a08ef5da3701 Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Sun, 9 Aug 2026 17:59:51 +0200 Subject: [PATCH 2/3] Set raw/v12 format also if the framesize is unknown. --- .../src/playlistitem/playlistItemRawFile.cpp | 4 --- .../src/video/yuv/PixelFormatYUVGuess.cpp | 36 ++++++++++--------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp index 0ae98f5fb..d3d2054fb 100644 --- a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp +++ b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp @@ -104,10 +104,6 @@ playlistItemRawFile::playlistItemRawFile(const QString &rawFilePath, { this->video = std::make_unique(); this->rawFormat = video::RawFormat::YUV; - if (isInExtensions(ext, RAW_BAYER_EXTENSIONS)) - { - this->getYUVVideo()->setPixelFormatYUV(video::yuv::PixelFormatYUV(video::yuv::Subsampling::YUV_400, 8, video::yuv::PlaneOrder::YUV)); - } } else if (isInExtensions(ext, RGB_EXTENSIONS) || isInExtensions(ext, RGBA_EXTENSIONS) || isInExtensions(ext, CMYK_EXTENSIONS) || fmt.toLower() == "rgb") diff --git a/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp b/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp index 94300d437..b8309be1c 100644 --- a/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp +++ b/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp @@ -143,7 +143,7 @@ PixelFormatYUV testFormatFromSizeAndNamePlanar(const std::string &nam formatName << bitDepth << endianness; formatName << interlacedString; auto fmt = PixelFormatYUV( - subsampling, bitDepth, entry.second, endianness == "be", {}, interlaced); + subsampling, bitDepth, entry.second, endianness == "be", {}, interlaced); if (name.find(formatName.str()) != std::string::npos && doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileSize)) return fmt; @@ -158,7 +158,7 @@ PixelFormatYUV testFormatFromSizeAndNamePlanar(const std::string &nam formatName << bitDepth << endianness; formatName << interlacedString; auto fmt = PixelFormatYUV( - subsampling, bitDepth, entry.second, endianness == "be", {}, interlaced); + subsampling, bitDepth, entry.second, endianness == "be", {}, interlaced); if (name.find(formatName.str()) != std::string::npos && doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileSize)) return fmt; @@ -242,17 +242,19 @@ checkSpecificFileExtensions(const GuessedFrameFormat &guessedFrameFormat, if (fileExtension == ".raw") { const auto rawBayerFormat = - PixelFormatYUV(Subsampling::YUV_400, guessedFrameFormat.bitDepth.value_or(8)); + PixelFormatYUV(Subsampling::YUV_400, guessedFrameFormat.bitDepth.value_or(8)); if (!guessedFrameFormat.frameSize) return rawBayerFormat; if (doesPixelFormatMatchFileSize( - rawBayerFormat, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + rawBayerFormat, *guessedFrameFormat.frameSize, fileInfo.fileSize)) return rawBayerFormat; } if (fileExtension == ".v210" || fileExtension == ".V210") { const auto v210Format = PixelFormatYUV(PredefinedPixelFormat::V210); + if (!guessedFrameFormat.frameSize) + return v210Format; if (doesPixelFormatMatchFileSize(v210Format, *guessedFrameFormat.frameSize, fileInfo.fileSize)) return v210Format; } @@ -300,20 +302,20 @@ checkFFmpegPixelFormatNames(const std::string &name, const auto checkPackedFormatsFirst = (guessedFrameFormat.dataLayout == DataLayout::Packed); if (checkPackedFormatsFirst) { - if (const auto fmt = testFormatFromSizeAndNamePacked( - name, guessedFrameFormat, subsampling, fileInfo.fileSize)) + if (const auto fmt = + testFormatFromSizeAndNamePacked(name, guessedFrameFormat, subsampling, fileInfo.fileSize)) return fmt; - if (const auto fmt = testFormatFromSizeAndNamePlanar( - name, guessedFrameFormat, subsampling, fileInfo.fileSize)) + if (const auto fmt = + testFormatFromSizeAndNamePlanar(name, guessedFrameFormat, subsampling, fileInfo.fileSize)) return fmt; } else { - if (const auto fmt = testFormatFromSizeAndNamePlanar( - name, guessedFrameFormat, subsampling, fileInfo.fileSize)) + if (const auto fmt = + testFormatFromSizeAndNamePlanar(name, guessedFrameFormat, subsampling, fileInfo.fileSize)) return fmt; - if (const auto fmt = testFormatFromSizeAndNamePacked( - name, guessedFrameFormat, subsampling, fileInfo.fileSize)) + if (const auto fmt = + testFormatFromSizeAndNamePacked(name, guessedFrameFormat, subsampling, fileInfo.fileSize)) return fmt; } @@ -375,10 +377,10 @@ checkForSubsamplingIndiatorInName(const std::string &name, } std::optional ignoreNameAndJustCheckIfSomeBasicFormatsMatchTheFileSize( - const GuessedFrameFormat &guessedFrameFormat, const FileInfoForGuess &fileInfo) + const GuessedFrameFormat &guessedFrameFormat, const FileInfoForGuess &fileInfo) { const auto testSubsamplings = - std::vector({Subsampling::YUV_420, Subsampling::YUV_444, Subsampling::YUV_422}); + std::vector({Subsampling::YUV_420, Subsampling::YUV_444, Subsampling::YUV_422}); std::vector testBitDepths; if (guessedFrameFormat.bitDepth) @@ -412,19 +414,19 @@ PixelFormatYUV guessPixelFormatFromSizeAndName(const GuessedFrameFormat &guessed {functions::toLower(fileInfo.filename), functions::toLower(fileInfo.parentFolderName)}) { if (const auto pixelFormat = - checForNVIndicator(name, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + checForNVIndicator(name, *guessedFrameFormat.frameSize, fileInfo.fileSize)) return *pixelFormat; if (const auto pixelFormat = checkFFmpegPixelFormatNames(name, guessedFrameFormat, fileInfo)) return *pixelFormat; if (const auto pixelFormat = - checkForSubsamplingIndiatorInName(name, guessedFrameFormat, fileInfo)) + checkForSubsamplingIndiatorInName(name, guessedFrameFormat, fileInfo)) return *pixelFormat; } if (const auto pixelFormat = - ignoreNameAndJustCheckIfSomeBasicFormatsMatchTheFileSize(guessedFrameFormat, fileInfo)) + ignoreNameAndJustCheckIfSomeBasicFormatsMatchTheFileSize(guessedFrameFormat, fileInfo)) return *pixelFormat; return {}; From ae048509665cb823aada3ef211a39e7d48654d0b Mon Sep 17 00:00:00 2001 From: Christian Feldmann Date: Sun, 9 Aug 2026 21:09:24 +0200 Subject: [PATCH 3/3] Also try to guess the pixel format if we don't know the frame size. --- .../src/playlistitem/playlistItemRawFile.cpp | 12 +++--- .../src/video/yuv/PixelFormatYUVGuess.cpp | 43 ++++++++++--------- .../video/yuv/PixelFormatYUVGuessTest.cpp | 17 +++++++- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp index 6bd169ef6..3d3147a48 100644 --- a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp +++ b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp @@ -457,15 +457,13 @@ void playlistItemRawFile::setFormatFromFileName() const auto frameFormat = filesource::frameFormatGuess::guessFrameFormat(fileInfoForGuess); if (frameFormat.frameSize) - { this->video->setFrameSize(*frameFormat.frameSize); - // We were able to extract width and height from the file name using - // regular expressions. Try to get the pixel format by checking with the file size. - this->video->guessAndSetPixelFormat(frameFormat, fileInfoForGuess); - if (frameFormat.frameRate) - this->prop.frameRate = *frameFormat.frameRate; - } + // Try to get the pixel format. If we know a frame size, we will check the format + // against the file size. + this->video->guessAndSetPixelFormat(frameFormat, fileInfoForGuess); + if (frameFormat.frameRate) + this->prop.frameRate = *frameFormat.frameRate; } void playlistItemRawFile::createPropertiesWidget() diff --git a/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp b/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp index b8309be1c..3bb972f89 100644 --- a/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp +++ b/YUViewLib/src/video/yuv/PixelFormatYUVGuess.cpp @@ -42,6 +42,9 @@ using filesource::frameFormatGuess::GuessedFrameFormat; namespace video::yuv { +namespace +{ + Subsampling findSubsamplingTypeIndicatorInName(const std::string &name) { std::string matcher = "(?:_|\\.|-)("; @@ -94,13 +97,13 @@ std::vector getDetectionSubsamplingList(Subsampling subsamplingToFo } bool doesPixelFormatMatchFileSize(const PixelFormatYUV &pixelFormat, - const Size &frameSize, + const std::optional &frameSize, const std::optional &fileSize) { - if (!fileSize) + if (!fileSize || !frameSize) return true; - const auto bytesPerFrame = pixelFormat.bytesPerFrame(frameSize); + const auto bytesPerFrame = pixelFormat.bytesPerFrame(*frameSize); if (bytesPerFrame <= 0) return false; @@ -145,7 +148,7 @@ PixelFormatYUV testFormatFromSizeAndNamePlanar(const std::string &nam auto fmt = PixelFormatYUV( subsampling, bitDepth, entry.second, endianness == "be", {}, interlaced); if (name.find(formatName.str()) != std::string::npos && - doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileSize)) + doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileSize)) return fmt; } @@ -160,7 +163,7 @@ PixelFormatYUV testFormatFromSizeAndNamePlanar(const std::string &nam auto fmt = PixelFormatYUV( subsampling, bitDepth, entry.second, endianness == "be", {}, interlaced); if (name.find(formatName.str()) != std::string::npos && - doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileSize)) + doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileSize)) return fmt; } } @@ -182,7 +185,7 @@ PixelFormatYUV testFormatFromSizeAndNamePacked(const std::string &nam if (std::regex_search(name, sm, strExpr)) { const auto fmt = PixelFormatYUV(PredefinedPixelFormat::V210); - if (doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileSize)) + if (doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileSize)) return fmt; } @@ -209,7 +212,7 @@ PixelFormatYUV testFormatFromSizeAndNamePacked(const std::string &nam formatName << std::to_string(bitDepth) + endianness; auto fmt = PixelFormatYUV(subsampling, bitDepth, packing, false, endianness == "be"); if (name.find(formatName.str()) != std::string::npos && - doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileSize)) + doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileSize)) return fmt; } @@ -222,7 +225,7 @@ PixelFormatYUV testFormatFromSizeAndNamePacked(const std::string &nam formatName << bitDepth << endianness; auto fmt = PixelFormatYUV(subsampling, bitDepth, packing, false, endianness == "be"); if (name.find(formatName.str()) != std::string::npos && - doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileSize)) + doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileSize)) return fmt; } } @@ -243,19 +246,15 @@ checkSpecificFileExtensions(const GuessedFrameFormat &guessedFrameFormat, { const auto rawBayerFormat = PixelFormatYUV(Subsampling::YUV_400, guessedFrameFormat.bitDepth.value_or(8)); - if (!guessedFrameFormat.frameSize) - return rawBayerFormat; if (doesPixelFormatMatchFileSize( - rawBayerFormat, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + rawBayerFormat, guessedFrameFormat.frameSize, fileInfo.fileSize)) return rawBayerFormat; } if (fileExtension == ".v210" || fileExtension == ".V210") { const auto v210Format = PixelFormatYUV(PredefinedPixelFormat::V210); - if (!guessedFrameFormat.frameSize) - return v210Format; - if (doesPixelFormatMatchFileSize(v210Format, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + if (doesPixelFormatMatchFileSize(v210Format, guessedFrameFormat.frameSize, fileInfo.fileSize)) return v210Format; } @@ -263,7 +262,7 @@ checkSpecificFileExtensions(const GuessedFrameFormat &guessedFrameFormat, } std::optional checForNVIndicator(const std::string_view name, - const Size &frameSize, + const std::optional &frameSize, const std::optional &fileSize) { if (name.find("nv12") != std::string::npos) @@ -324,7 +323,7 @@ checkFFmpegPixelFormatNames(const std::string &name, { // Check if the format and the file size match auto fmt = PixelFormatYUV(Subsampling::YUV_444, 16, PackingOrder::AYUV, false, false); - if (doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + if (doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileInfo.fileSize)) return fmt; } @@ -334,7 +333,7 @@ checkFFmpegPixelFormatNames(const std::string &name, if (name.find("gray" + std::to_string(bitDepth) + "le") != std::string::npos) { auto fmt = PixelFormatYUV(Subsampling::YUV_400, bitDepth); - if (doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + if (doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileInfo.fileSize)) return fmt; } } @@ -367,7 +366,7 @@ checkForSubsamplingIndiatorInName(const std::string &name, fmt = PixelFormatYUV(subsampling, bitDepth, PackingOrder::YUV); else fmt = PixelFormatYUV(subsampling, bitDepth, PlaneOrder::YUV); - if (doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + if (doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileInfo.fileSize)) return fmt; } } @@ -393,7 +392,7 @@ std::optional ignoreNameAndJustCheckIfSomeBasicFormatsMatchTheFi for (const auto bd : testBitDepths) { auto fmt = PixelFormatYUV(subsampling, bd, PlaneOrder::YUV); - if (doesPixelFormatMatchFileSize(fmt, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + if (doesPixelFormatMatchFileSize(fmt, guessedFrameFormat.frameSize, fileInfo.fileSize)) return fmt; } } @@ -401,10 +400,12 @@ std::optional ignoreNameAndJustCheckIfSomeBasicFormatsMatchTheFi return {}; } +} // namespace + PixelFormatYUV guessPixelFormatFromSizeAndName(const GuessedFrameFormat &guessedFrameFormat, const FileInfoForGuess &fileInfo) { - if (!guessedFrameFormat.frameSize || fileInfo.filename.empty()) + if (fileInfo.filename.empty()) return {}; if (const auto pixelFormat = checkSpecificFileExtensions(guessedFrameFormat, fileInfo)) @@ -414,7 +415,7 @@ PixelFormatYUV guessPixelFormatFromSizeAndName(const GuessedFrameFormat &guessed {functions::toLower(fileInfo.filename), functions::toLower(fileInfo.parentFolderName)}) { if (const auto pixelFormat = - checForNVIndicator(name, *guessedFrameFormat.frameSize, fileInfo.fileSize)) + checForNVIndicator(name, guessedFrameFormat.frameSize, fileInfo.fileSize)) return *pixelFormat; if (const auto pixelFormat = checkFFmpegPixelFormatNames(name, guessedFrameFormat, fileInfo)) diff --git a/YUViewUnitTest/video/yuv/PixelFormatYUVGuessTest.cpp b/YUViewUnitTest/video/yuv/PixelFormatYUVGuessTest.cpp index 4f54f1370..907cd54a8 100644 --- a/YUViewUnitTest/video/yuv/PixelFormatYUVGuessTest.cpp +++ b/YUViewUnitTest/video/yuv/PixelFormatYUVGuessTest.cpp @@ -86,6 +86,7 @@ TEST_P(GuessYUVFormatFromFilenameFrameSizeFileSizeDataLayoutAndBitDepth, TestGue << parameters.fileInfoForGuess.fileSize.value_or(-1); } +constexpr auto BYTES_IRRELEVANT = 123; constexpr auto BYTES_1080P = 1920 * 1080 * 3 * 6; // 12 frames 420 constexpr auto BYTES_720P = 1280 * 720 * 3 * 6; // 6 frames 444 constexpr auto BYTES_720P_V210 = 1296u * 720 / 6 * 16 * 3; // 3 frames @@ -192,7 +193,21 @@ INSTANTIATE_TEST_SUITE_P( TestParameters({FileInfoForGuess({"VisualAcuityLandolt_3840x2160_60fps_10bit_420p_BT709.yuv", "", BYTES_2160p_10bit_420}), - PixelFormatYUV(Subsampling::YUV_420, 10)}) + PixelFormatYUV(Subsampling::YUV_420, 10)}), + + // Files that do not indicate a frame size. Issue 663. + TestParameters({FileInfoForGuess({"sample_noResolution_something.raw", "", BYTES_IRRELEVANT}), + PixelFormatYUV(Subsampling::YUV_400, 8)}), + TestParameters({FileInfoForGuess({"sample_noResolution_something.v210", "", BYTES_IRRELEVANT}), + PixelFormatYUV(PredefinedPixelFormat::V210)}), + TestParameters({FileInfoForGuess({"sample_noResolution_something.V210", "", BYTES_IRRELEVANT}), + PixelFormatYUV(PredefinedPixelFormat::V210)}), + TestParameters( + {FileInfoForGuess({"sample_noResolution_nv12_something.yuv", "", BYTES_IRRELEVANT}), + PixelFormatYUV(Subsampling::YUV_420, 8, PlaneOrder::YUV, false, {}, true)}), + TestParameters( + {FileInfoForGuess({"sample_noResolution_nv21_something.yuv", "", BYTES_IRRELEVANT}), + PixelFormatYUV(Subsampling::YUV_420, 8, PlaneOrder::YVU, false, {}, true)}) // More tests please :)