diff --git a/YUViewApp/src/yuviewapp.cpp b/YUViewApp/src/yuviewapp.cpp index f63628294..57e367b8c 100644 --- a/YUViewApp/src/yuviewapp.cpp +++ b/YUViewApp/src/yuviewapp.cpp @@ -31,6 +31,9 @@ */ #include +#include + +#include #include #include @@ -46,7 +49,22 @@ int main(int argc, char *argv[]) qRegisterMetaType("recacheIndicator"); - YUViewApplication app(argc, argv); - - return app.returnCode; + try + { + YUViewApplication app(argc, argv); + return app.returnCode; + } + catch (const std::bad_alloc &) + { + try + { + if (QCoreApplication::instance()) + QMessageBox::critical(nullptr, "YUView", "Out of memory."); + } + catch (...) + { + std::cerr << "YUView: Out of memory." << std::endl; + } + return 1; + } } diff --git a/YUViewLib/src/common/Typedef.h b/YUViewLib/src/common/Typedef.h index 155e6f11d..47631f75e 100644 --- a/YUViewLib/src/common/Typedef.h +++ b/YUViewLib/src/common/Typedef.h @@ -184,6 +184,8 @@ struct Ratio struct Size { + static constexpr unsigned MAX_DIMENSION = 16384; + constexpr Size(unsigned width, unsigned height) : width(width), height(height) {} constexpr Size() = default; @@ -196,7 +198,11 @@ struct Size return this->width != other.width || this->height != other.height; } explicit operator bool() const { return this->isValid(); } - constexpr bool isValid() const { return this->width > 0 && this->height > 0; } + constexpr bool isValid() const + { + return this->width > 0 && this->width <= MAX_DIMENSION && + this->height > 0 && this->height <= MAX_DIMENSION; + } unsigned width{}; unsigned height{}; }; diff --git a/YUViewLib/src/filesource/FileSource.cpp b/YUViewLib/src/filesource/FileSource.cpp index e5df613a9..4f8cf0a75 100644 --- a/YUViewLib/src/filesource/FileSource.cpp +++ b/YUViewLib/src/filesource/FileSource.cpp @@ -83,6 +83,9 @@ int64_t FileSource::readBytes(QByteArray &targetBuffer, int64_t startPos, int64_ if (!this->isOk()) return 0; + if (nrBytes <= 0) + return 0; + if (targetBuffer.size() < nrBytes) targetBuffer.resize(nrBytes); diff --git a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp index d3d2054fb..1bcd7afdd 100644 --- a/YUViewLib/src/playlistitem/playlistItemRawFile.cpp +++ b/YUViewLib/src/playlistitem/playlistItemRawFile.cpp @@ -32,6 +32,8 @@ #include "playlistItemRawFile.h" +#include + #include #include #include @@ -134,10 +136,17 @@ playlistItemRawFile::playlistItemRawFile(const QString &rawFilePath, if (!this->video->isFormatValid()) { - // Load 24883200 bytes from the input and try to get the format from the correlation. - QByteArray rawData; - this->dataSource.readBytes(rawData, 0, 24883200); - this->video->setFormatFromCorrelation(rawData, this->dataSource.getFileSize().value_or(-1)); + try + { + QByteArray rawData; + this->dataSource.readBytes(rawData, 0, 24883200); + this->video->setFormatFromCorrelation(rawData, + this->dataSource.getFileSize().value_or(-1)); + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory while guessing format from correlation."; + } } } else @@ -226,6 +235,13 @@ InfoData playlistItemRawFile::getInfo() const else info.items.append(InfoItem("Warning"sv, "Could not obtain file size from input.")); } + else if (this->dataSource.isOk() && !this->video->isFormatValid()) + { + info.items.append(InfoItem( + "Warning"sv, + "Could not determine the video format. Please set the width, height and pixel format " + "manually in the properties panel.")); + } return info; } @@ -542,6 +558,8 @@ void playlistItemRawFile::loadRawData(int frameIdx) return; auto nrBytes = this->video->getBytesPerFrame(); + if (nrBytes <= 0) + return; // Load the raw data for the given frameIdx from file and set it in the video int64_t fileStartPos; @@ -550,10 +568,21 @@ void playlistItemRawFile::loadRawData(int frameIdx) else fileStartPos = frameIdx * nrBytes; + if (fileStartPos < 0) + return; + DEBUG_RAWFILE("playlistItemRawFile::loadRawData Start loading frame " << frameIdx << " bytes " << int(nrBytes)); - if (this->dataSource.readBytes(this->video->rawData, fileStartPos, nrBytes) < nrBytes) - return; // Error + try + { + if (this->dataSource.readBytes(this->video->rawData, fileStartPos, nrBytes) < nrBytes) + return; // Error + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory while loading RAW frame data."; + return; + } this->video->rawData_frameIndex = frameIdx; DEBUG_RAWFILE("playlistItemRawFile::loadRawData Frame " << frameIdx << " loaded"); diff --git a/YUViewLib/src/video/FrameHandler.cpp b/YUViewLib/src/video/FrameHandler.cpp index 2d995ca33..f7a37f0fd 100644 --- a/YUViewLib/src/video/FrameHandler.cpp +++ b/YUViewLib/src/video/FrameHandler.cpp @@ -124,10 +124,10 @@ QLayout *FrameHandler::createFrameHandlerControls(bool isSizeFixed) ui.setupUi(); // Set default values - ui.widthSpinBox->setMaximum(100000); + ui.widthSpinBox->setMaximum(Size::MAX_DIMENSION); ui.widthSpinBox->setValue(frameSize.width); ui.widthSpinBox->setEnabled(!isSizeFixed); - ui.heightSpinBox->setMaximum(100000); + ui.heightSpinBox->setMaximum(Size::MAX_DIMENSION); ui.heightSpinBox->setValue(frameSize.height); ui.heightSpinBox->setEnabled(!isSizeFixed); ui.frameSizeComboBox->addItems(presetFrameSizes.getFormattedNames()); diff --git a/YUViewLib/src/video/rgb/ConversionDifferenceRGB.cpp b/YUViewLib/src/video/rgb/ConversionDifferenceRGB.cpp index 66eeb754c..4863bb178 100644 --- a/YUViewLib/src/video/rgb/ConversionDifferenceRGB.cpp +++ b/YUViewLib/src/video/rgb/ConversionDifferenceRGB.cpp @@ -76,9 +76,12 @@ RenderValue convertDeltaToRenderValue(const rgba_t &delta, } else { - const auto r = functions::clip(128 + delta.r * amplificationFactor, 0, 255); - const auto g = functions::clip(128 + delta.g * amplificationFactor, 0, 255); - const auto b = functions::clip(128 + delta.b * amplificationFactor, 0, 255); + const auto r = static_cast( + functions::clip(128 + static_cast(delta.r) * amplificationFactor, 0LL, 255LL)); + const auto g = static_cast( + functions::clip(128 + static_cast(delta.g) * amplificationFactor, 0LL, 255LL)); + const auto b = static_cast( + functions::clip(128 + static_cast(delta.b) * amplificationFactor, 0LL, 255LL)); return {r, g, b}; } } diff --git a/YUViewLib/src/video/rgb/ConversionDifferenceRGB.h b/YUViewLib/src/video/rgb/ConversionDifferenceRGB.h index 089ee4b62..b447a94cf 100644 --- a/YUViewLib/src/video/rgb/ConversionDifferenceRGB.h +++ b/YUViewLib/src/video/rgb/ConversionDifferenceRGB.h @@ -65,10 +65,10 @@ class SSE public: void addSample(const rgba_t &delta) { - this->r += delta.r * delta.r; - this->g += delta.g * delta.g; - this->b += delta.b * delta.b; - this->a += delta.a * delta.a; + this->r += static_cast(delta.r) * delta.r; + this->g += static_cast(delta.g) * delta.g; + this->b += static_cast(delta.b) * delta.b; + this->a += static_cast(delta.a) * delta.a; ++this->nrSamples; } @@ -83,11 +83,11 @@ class SSE } private: - int64_t r{}; - int64_t g{}; - int64_t b{}; - int64_t a{}; - int64_t nrSamples{}; + uint64_t r{}; + uint64_t g{}; + uint64_t b{}; + uint64_t a{}; + uint64_t nrSamples{}; }; std::pair calculateDifferenceAndMSE(const InputFrameParameters &frame1, diff --git a/YUViewLib/src/video/rgb/videoHandlerRGB.cpp b/YUViewLib/src/video/rgb/videoHandlerRGB.cpp index b72b156e7..3b7499c8d 100644 --- a/YUViewLib/src/video/rgb/videoHandlerRGB.cpp +++ b/YUViewLib/src/video/rgb/videoHandlerRGB.cpp @@ -35,6 +35,8 @@ #include "video/PixelFormat.h" #include "video/rgb/PixelFormatRGB.h" +#include + #include #include #include @@ -482,14 +484,30 @@ void videoHandlerRGB::loadFrame(int frameIndex, bool loadToDoubleBuffer) if (loadToDoubleBuffer) { QImage newImage; - convertRGBToImage(currentFrameRawData, newImage); + try + { + convertRGBToImage(currentFrameRawData, newImage); + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory in loadFrame convertRGBToImage (double buffer)."; + return; + } doubleBufferImage = newImage; doubleBufferImageFrameIndex = frameIndex; } else if (currentImageIndex != frameIndex) { QImage newImage; - convertRGBToImage(currentFrameRawData, newImage); + try + { + convertRGBToImage(currentFrameRawData, newImage); + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory in loadFrame convertRGBToImage."; + return; + } QMutexLocker writeLock(¤tImageSetMutex); currentImage = newImage; currentImageIndex = frameIndex; @@ -551,6 +569,9 @@ void videoHandlerRGB::loadPlaylist(const YUViewDomElement &element) void videoHandlerRGB::loadFrameForCaching(int frameIndex, QImage &frameToCache) { + if (!isFormatValid()) + return; + DEBUG_RGB("videoHandlerRGB::loadFrameForCaching %d", frameIndex); // Lock the mutex for the rgbFormat. The main thread has to wait until caching is done @@ -571,7 +592,15 @@ void videoHandlerRGB::loadFrameForCaching(int frameIndex, QImage &frameToCache) } // Convert RGB to image. This can then be cached. - convertRGBToImage(tmpBufferRawRGBDataCaching, frameToCache); + try + { + convertRGBToImage(tmpBufferRawRGBDataCaching, frameToCache); + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory in loadFrameForCaching convertRGBToImage."; + frameToCache = QImage(); + } rgbFormatMutex.unlock(); } @@ -730,6 +759,8 @@ void videoHandlerRGB::convertSourceToRGBA32Bit(const QByteArray &sourceBuffer, rgba_t videoHandlerRGB::getPixelValue(const QPoint &pixelPos) const { + if (!isFormatValid()) + return {}; return getPixelValueFromBuffer( this->currentFrameRawData, this->srcPixelFormat, this->frameSize, pixelPos); } @@ -753,6 +784,9 @@ void videoHandlerRGB::drawPixelValues(QPainter *painter, const bool markDifference, const int frameIdxItem1) { + if (!isFormatValid()) + return; + // First determine which pixels from this item are actually visible, because we only have to draw // the pixel values of the pixels that are actually visible auto viewport = painter->viewport(); @@ -869,6 +903,9 @@ QImage videoHandlerRGB::calculateDifference(FrameHandler *item2, const int amplificationFactor, const bool markDifference) { + if (!isFormatValid()) + return QImage(); + videoHandlerRGB *rgbItem2 = dynamic_cast(item2); if (rgbItem2 == nullptr) // The given item is not a RGB source. We cannot compare raw RGB values to non raw RGB values. diff --git a/YUViewLib/src/video/yuv/videoHandlerYUV.cpp b/YUViewLib/src/video/yuv/videoHandlerYUV.cpp index 013affa53..7aa5f5a73 100644 --- a/YUViewLib/src/video/yuv/videoHandlerYUV.cpp +++ b/YUViewLib/src/video/yuv/videoHandlerYUV.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -2820,6 +2821,9 @@ void videoHandlerYUV::drawPixelValues(QPainter *painter, const bool markDifference, const int frameIdxItem1) { + if (!isFormatValid()) + return; + // Get the other YUV item (if any) auto yuvItem2 = (item2 == nullptr) ? nullptr : dynamic_cast(item2); if (item2 != nullptr && yuvItem2 == nullptr) @@ -3173,8 +3177,6 @@ bool videoHandlerYUV::setFormatFromString(const std::string_view format) void videoHandlerYUV::loadFrame(int frameIndex, bool loadToDoubleBuffer) { - DEBUG_YUV("videoHandlerYUV::loadFrame " << frameIndex); - if (!isFormatValid()) // We cannot load a frame if the format is not known return; @@ -3189,22 +3191,38 @@ void videoHandlerYUV::loadFrame(int frameIndex, bool loadToDoubleBuffer) if (loadToDoubleBuffer) { QImage newImage; - convertYUVToImage(this->currentFrameRawData, - newImage, - this->srcPixelFormat, - this->frameSize, - this->conversionSettings); + try + { + convertYUVToImage(this->currentFrameRawData, + newImage, + this->srcPixelFormat, + this->frameSize, + this->conversionSettings); + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory in loadFrame convertYUVToImage (double buffer)."; + return; + } doubleBufferImage = newImage; doubleBufferImageFrameIndex = frameIndex; } else if (currentImageIndex != frameIndex) { QImage newImage; - convertYUVToImage(this->currentFrameRawData, - newImage, - this->srcPixelFormat, - this->frameSize, - this->conversionSettings); + try + { + convertYUVToImage(this->currentFrameRawData, + newImage, + this->srcPixelFormat, + this->frameSize, + this->conversionSettings); + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory in loadFrame convertYUVToImage."; + return; + } QMutexLocker setLock(¤tImageSetMutex); currentImage = newImage; currentImageIndex = frameIndex; @@ -3213,7 +3231,8 @@ void videoHandlerYUV::loadFrame(int frameIndex, bool loadToDoubleBuffer) void videoHandlerYUV::loadFrameForCaching(int frameIndex, QImage &frameToCache) { - DEBUG_YUV("videoHandlerYUV::loadFrameForCaching " << frameIndex); + if (!isFormatValid()) + return; // Get the YUV format and the size here, so that the caching process does not crash if this // changes. @@ -3234,8 +3253,16 @@ void videoHandlerYUV::loadFrameForCaching(int frameIndex, QImage &frameToCache) } // Convert YUV to image. This can then be cached. - convertYUVToImage( - tmpBufferRawYUVDataCaching, frameToCache, yuvFormat, curFrameSize, conversionSettings); + try + { + convertYUVToImage( + tmpBufferRawYUVDataCaching, frameToCache, yuvFormat, curFrameSize, conversionSettings); + } + catch (const std::bad_alloc &) + { + qWarning() << "Out of memory in loadFrameForCaching convertYUVToImage."; + frameToCache = QImage(); + } } // Load the raw YUV data for the given frame index into currentFrameRawData. @@ -3270,6 +3297,9 @@ bool videoHandlerYUV::loadRawYUVData(int frameIndex) yuv_t videoHandlerYUV::getPixelValue(const QPoint &pixelPos) const { + if (!isFormatValid()) + return {0, 0, 0}; + const PixelFormatYUV format = srcPixelFormat; const int w = frameSize.width; const int h = frameSize.height; @@ -3535,6 +3565,9 @@ QImage videoHandlerYUV::calculateDifference(FrameHandler *item2, const int amplificationFactor, const bool markDifference) { + if (!isFormatValid()) + return QImage(); + this->diffReady = false; videoHandlerYUV *yuvItem2 = dynamic_cast(item2); diff --git a/YUViewLib/src/video/yuv/videoHandlerYUV.h b/YUViewLib/src/video/yuv/videoHandlerYUV.h index 22b2efee1..b6b6f694b 100644 --- a/YUViewLib/src/video/yuv/videoHandlerYUV.h +++ b/YUViewLib/src/video/yuv/videoHandlerYUV.h @@ -137,6 +137,8 @@ class videoHandlerYUV : public videoHandler virtual std::optional getFormatAsString() const override { const auto frameFormat = FrameHandler::getFormatAsString(); + if (!frameFormat) + return {}; return *frameFormat + ";YUV;" + this->srcPixelFormat.getName(); } virtual bool setFormatFromString(const std::string_view format) override; diff --git a/YUViewUnitTest/video/rgb/ConversionDifferenceRGBTest.cpp b/YUViewUnitTest/video/rgb/ConversionDifferenceRGBTest.cpp index c134d91f9..dcaa6b2df 100644 --- a/YUViewUnitTest/video/rgb/ConversionDifferenceRGBTest.cpp +++ b/YUViewUnitTest/video/rgb/ConversionDifferenceRGBTest.cpp @@ -56,8 +56,8 @@ FrameAandB createTestFrameData(const int bitDepth) std::vector testValuesA; std::vector testValuesB; - const auto maxValue = (1 << bitDepth) - 1; - const auto midValue = (1 << (bitDepth - 1)); + const auto maxValue = bitDepth == 32 ? INT_MAX : (1 << bitDepth) - 1; + const auto midValue = bitDepth == 32 ? (INT_MAX >> 1) : (1 << (bitDepth - 1)); // Add some special values that we definitely want to test testValuesA.push_back(rgba_t({0, maxValue, maxValue, 0})); @@ -168,9 +168,9 @@ ExpectedImageAndMse generateExpectedImageAndMse(const FrameAandB &testFrames, outputPixel = { .r = diff.r != 0 ? 255 : 0, .g = diff.g != 0 ? 255 : 0, .b = diff.b != 0 ? 255 : 0}; else - outputPixel = {.r = functions::clip(128 + diff.r * amplificationFactor, 0, 255), - .g = functions::clip(128 + diff.g * amplificationFactor, 0, 255), - .b = functions::clip(128 + diff.b * amplificationFactor, 0, 255)}; + outputPixel = {.r = static_cast(functions::clip(128 + static_cast(diff.r) * amplificationFactor, 0LL, 255LL)), + .g = static_cast(functions::clip(128 + static_cast(diff.g) * amplificationFactor, 0LL, 255LL)), + .b = static_cast(functions::clip(128 + static_cast(diff.b) * amplificationFactor, 0LL, 255LL))}; const auto x = i % TEST_FRAME_SIZE.width; const auto y = i / TEST_FRAME_SIZE.width;