From a9d70e15b206f2ad443658fa70ae0584961ff86d Mon Sep 17 00:00:00 2001 From: ChristianFeldmann Date: Mon, 15 Apr 2024 22:43:20 +0200 Subject: [PATCH] Add some testing. Can be debugged now. --- src/lib/common/Logging.h | 11 ++ src/lib/libHandling/FFmpegLibraries.cpp | 115 ++++++++++++++---- src/lib/libHandling/FFmpegLibraries.h | 11 +- .../libraryFunctions/AvUtilFunctions.cpp | 5 +- .../libraryFunctions/AvUtilFunctions.h | 3 +- test/ffmpegTest/ffmpegTest.cpp | 99 ++++++++++----- 6 files changed, 180 insertions(+), 64 deletions(-) diff --git a/src/lib/common/Logging.h b/src/lib/common/Logging.h index 9534610..4d1ccbe 100644 --- a/src/lib/common/Logging.h +++ b/src/lib/common/Logging.h @@ -20,6 +20,17 @@ enum class LogLevel Error }; +struct LogEntry +{ + LogLevel logLevel{}; + std::string message{}; + + bool operator==(const LogEntry &other) const + { + return this->logLevel == other.logLevel && this->message == other.message; + } +}; + using LoggingFunction = std::function; } // namespace ffmpeg diff --git a/src/lib/libHandling/FFmpegLibraries.cpp b/src/lib/libHandling/FFmpegLibraries.cpp index 5946b03..a457c7c 100644 --- a/src/lib/libHandling/FFmpegLibraries.cpp +++ b/src/lib/libHandling/FFmpegLibraries.cpp @@ -62,13 +62,82 @@ Path getAbsolutePath(const Path &path) return std::filesystem::absolute(path); } +constexpr auto MAX_NR_REGISTERED_CALLBACKS = 3; +std::array staticCallbackVector; + +void logToLoggingFunction( + LoggingFunction &loggingFunction, void *ptr, int level, const char *fmt, va_list vargs) +{ + if (!loggingFunction) + return; + + std::string message; + va_list vargs_copy; + va_copy(vargs_copy, vargs); + size_t len = vsnprintf(0, 0, fmt, vargs_copy); + message.resize(len); + vsnprintf(&message[0], len + 1, fmt, vargs); + + auto logLevel = LogLevel::Debug; + if (level <= 16) + logLevel = LogLevel::Error; + if (level <= 32) + logLevel = LogLevel::Info; + + loggingFunction(logLevel, message); +} + +void staticLogFunction0(void *ptr, int level, const char *fmt, va_list vargs) +{ + logToLoggingFunction(staticCallbackVector[0], ptr, level, fmt, vargs); +} + +void staticLogFunction1(void *ptr, int level, const char *fmt, va_list vargs) +{ + logToLoggingFunction(staticCallbackVector[1], ptr, level, fmt, vargs); +} + +void staticLogFunction2(void *ptr, int level, const char *fmt, va_list vargs) +{ + logToLoggingFunction(staticCallbackVector[2], ptr, level, fmt, vargs); +} + +auto setStaticLoggingCallback(ffmpeg::internal::functions::AvUtilFunctions &avutilFunctions, + LoggingFunction loggingFunction) +{ + int callbackCounter = 0; + while (callbackCounter < MAX_NR_REGISTERED_CALLBACKS) + { + if (!staticCallbackVector.at(callbackCounter)) + { + staticCallbackVector[callbackCounter] = loggingFunction; + if (callbackCounter == 0) + avutilFunctions.av_log_set_callback(staticLogFunction0); + else if (callbackCounter == 1) + avutilFunctions.av_log_set_callback(staticLogFunction1); + else if (callbackCounter == 2) + avutilFunctions.av_log_set_callback(staticLogFunction2); + } + ++callbackCounter; + } + + loggingFunction(LogLevel::Error, + "Error setting av logging callback. The limit of static callbacks was reached."); +} + } // namespace FFmpegLibraries::FFmpegLibraries() { + std::lock_guard lock(this->loggingMutex); this->loggingFunction = [](const LogLevel, const std::string &) {}; } +FFmpegLibraries::~FFmpegLibraries() +{ + this->disconnectAVLoggingCallback(); +} + bool FFmpegLibraries::tryLoadFFmpegLibrariesInPath(const Path &path) { Path directory; @@ -261,17 +330,22 @@ std::vector FFmpegLibraries::getLibrariesInfo() const void FFmpegLibraries::setLoggingFunction(const LoggingFunction function, const LogLevel minimumLogLevel) { - this->minimumLogLevel = minimumLogLevel; - this->loggingFunction = - [this, function, minimumLogLevel](const LogLevel logLevel, const std::string &message) { - if (function && logLevel >= this->minimumLogLevel) - function(logLevel, message); - }; + std::lock_guard lock(this->loggingMutex); + this->loggingFunction = + [this, function, minimumLogLevel](const LogLevel logLevel, const std::string &message) + { + if (function && logLevel >= minimumLogLevel) + function(logLevel, message); + }; + } + + this->log(LogLevel::Info, "Logging function set successfully"); } void FFmpegLibraries::log(const LogLevel logLevel, const std::string &message) const { + std::lock_guard lock(this->loggingMutex); this->loggingFunction(logLevel, message); } @@ -286,30 +360,19 @@ void FFmpegLibraries::getLibraryVersionsFromLoadedLibraries() void FFmpegLibraries::connectAVLoggingCallback() { - static auto callbackStatic = [this](void *ptr, int level, const char *fmt, va_list vargs) - { this->avLogCallback(ptr, level, fmt, vargs); }; - this->avutil.av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vargs) - { callbackStatic(ptr, level, fmt, vargs); }); + this->log(LogLevel::Info, "Setting up av logging callback"); + std::lock_guard lock(this->loggingMutex); + + setStaticLoggingCallback(this->avutil, this->loggingFunction); } -void FFmpegLibraries::avLogCallback(void *, int level, const char *fmt, va_list vargs) +void FFmpegLibraries::disconnectAVLoggingCallback() { - std::string message; - va_list vargs_copy; - va_copy(vargs_copy, vargs); - size_t len = vsnprintf(0, 0, fmt, vargs_copy); - message.resize(len); - vsnprintf(&message[0], len + 1, fmt, vargs); + this->log(LogLevel::Info, "Disconnectiong av logging callback"); + std::lock_guard lock(this->loggingMutex); - if (this->loggingFunction) - { - auto logLevel = LogLevel::Debug; - if (level <= 16) - logLevel = LogLevel::Error; - if (level <= 32) - logLevel = LogLevel::Info; - this->loggingFunction(logLevel, message); - } + this->avutil.av_log_set_callback( + this->avutil.av_log_default_callback.target()); } } // namespace ffmpeg diff --git a/src/lib/libHandling/FFmpegLibraries.h b/src/lib/libHandling/FFmpegLibraries.h index 092e0c1..2d40ba0 100644 --- a/src/lib/libHandling/FFmpegLibraries.h +++ b/src/lib/libHandling/FFmpegLibraries.h @@ -11,6 +11,8 @@ #include #include +#include + namespace ffmpeg { @@ -18,7 +20,7 @@ class FFmpegLibraries : public IFFmpegLibraries { public: FFmpegLibraries(); - ~FFmpegLibraries() = default; + ~FFmpegLibraries(); std::vector getLibrariesInfo() const override; LibraryVersions getLibrariesVersion() const override { return this->libraryVersions; } @@ -52,10 +54,11 @@ class FFmpegLibraries : public IFFmpegLibraries LibraryVersions libraryVersions{}; void getLibraryVersionsFromLoadedLibraries(); - void connectAVLoggingCallback(); - void avLogCallback(void *ptr, int level, const char *fmt, va_list vargs); + void connectAVLoggingCallback(); + void disconnectAVLoggingCallback(); + mutable std::mutex loggingMutex; + LoggingFunction loggingFunction; - LogLevel minimumLogLevel{LogLevel::Info}; friend class FFmpegLibrariesBuilder; }; diff --git a/src/lib/libHandling/libraryFunctions/AvUtilFunctions.cpp b/src/lib/libHandling/libraryFunctions/AvUtilFunctions.cpp index 3bed60e..1cbc20f 100644 --- a/src/lib/libHandling/libraryFunctions/AvUtilFunctions.cpp +++ b/src/lib/libHandling/libraryFunctions/AvUtilFunctions.cpp @@ -15,7 +15,7 @@ namespace ffmpeg::internal::functions { std::optional tryBindAVUtilFunctionsFromLibrary(const SharedLibraryLoader &lib, - const LoggingFunction & log) + const LoggingFunction &log) { if (!lib) { @@ -35,6 +35,7 @@ std::optional tryBindAVUtilFunctionsFromLibrary(const SharedLib lib.tryResolveFunction(functions.av_frame_get_side_data, "av_frame_get_side_data"); lib.tryResolveFunction(functions.av_frame_get_metadata, "av_frame_get_metadata"); lib.tryResolveFunction(functions.av_log_set_callback, "av_log_set_callback"); + lib.tryResolveFunction(functions.av_log_default_callback, "av_log_default_callback"); lib.tryResolveFunction(functions.av_log_set_level, "av_log_set_level"); lib.tryResolveFunction(functions.av_pix_fmt_desc_get, "av_pix_fmt_desc_get"); lib.tryResolveFunction(functions.av_pix_fmt_desc_next, "av_pix_fmt_desc_next"); @@ -67,6 +68,8 @@ std::optional tryBindAVUtilFunctionsFromLibrary(const SharedLib checkForMissingFunctionAndLog( functions.av_log_set_callback, "av_log_set_callback", missingFunctions, log); + checkForMissingFunctionAndLog( + functions.av_log_default_callback, "av_log_default_callback", missingFunctions, log); checkForMissingFunctionAndLog( functions.av_log_set_level, "av_log_set_level", missingFunctions, log); checkForMissingFunctionAndLog( diff --git a/src/lib/libHandling/libraryFunctions/AvUtilFunctions.h b/src/lib/libHandling/libraryFunctions/AvUtilFunctions.h index ec23012..21011b0 100644 --- a/src/lib/libHandling/libraryFunctions/AvUtilFunctions.h +++ b/src/lib/libHandling/libraryFunctions/AvUtilFunctions.h @@ -29,6 +29,7 @@ struct AvUtilFunctions av_frame_get_side_data; std::function av_frame_get_metadata; std::function av_log_set_callback; + std::function av_log_default_callback; std::function av_log_set_level; std::function av_pix_fmt_desc_get; std::function av_pix_fmt_desc_next; @@ -36,6 +37,6 @@ struct AvUtilFunctions }; std::optional tryBindAVUtilFunctionsFromLibrary(const SharedLibraryLoader &lib, - const LoggingFunction & log); + const LoggingFunction &log); } // namespace ffmpeg::internal::functions diff --git a/test/ffmpegTest/ffmpegTest.cpp b/test/ffmpegTest/ffmpegTest.cpp index 9417f5a..329c0b1 100644 --- a/test/ffmpegTest/ffmpegTest.cpp +++ b/test/ffmpegTest/ffmpegTest.cpp @@ -40,37 +40,54 @@ constexpr std::size_t operator"" _sz(unsigned long long n) // TestFile_h264_aac_1s_320x240.mp4 constexpr auto TEST_FILE_NAME = "TestFile_h264_aac_1s_320x240.mp4"; -void loggingFunction(const LogLevel logLevel, const std::string &message) +class LibrariesWithLogging { - std::cerr << "[ ] [" << to_string(logLevel) << "] " << message << "\n"; -} +public: + LibrariesWithLogging() + { + const auto loadingResult = + FFmpegLibrariesBuilder() + .withAdditionalSearchPaths({".", ""}) + .withLoggingFunction([this](const LogLevel logLevel, const std::string &message) + { this->loggingFunction(logLevel, message); }, + LogLevel::Debug) + .tryLoadingOfLibraries(); + EXPECT_TRUE(loadingResult) << "Error loading libraries"; + this->libraries = loadingResult.ffmpegLibraries; + } -std::shared_ptr openLibraries() -{ - const auto loadingResult = FFmpegLibrariesBuilder() - .withAdditionalSearchPaths({".", ""}) - .withLoggingFunction(&loggingFunction, LogLevel::Info) - .tryLoadingOfLibraries(); - EXPECT_TRUE(loadingResult) << "Error loading libraries"; - return loadingResult.ffmpegLibraries; -} + Demuxer openTestFileInDemuxer() + { + Demuxer demuxer(this->libraries); + EXPECT_TRUE(demuxer.openFile(TEST_FILE_NAME)) + << "Opening test file " << TEST_FILE_NAME << " failed."; -Demuxer openTestFileInDemuxer(std::shared_ptr ffmpegLibraries) -{ - Demuxer demuxer(ffmpegLibraries); - EXPECT_TRUE(demuxer.openFile(TEST_FILE_NAME)) - << "Opening test file " << TEST_FILE_NAME << " failed."; + return demuxer; + } - return demuxer; -} + void loggingFunction(const LogLevel logLevel, const std::string &message) + { + std::cerr << "[ ] [" << to_string(logLevel) << "] " << message << "\n"; + this->logEntries.push_back({logLevel, message}); + } + + bool containsLogEntry(const LogEntry &logEntry) + { + return std::find(this->logEntries.begin(), this->logEntries.end(), logEntry) != + this->logEntries.end(); + } + + std::vector logEntries{}; + std::shared_ptr libraries; +}; } // namespace TEST(FFmpegTest, LoadLibrariesAndLogVersion) { - auto ffmpegLibraries = openLibraries(); + auto libsAndLogs = LibrariesWithLogging(); - const auto librariesInfo = ffmpegLibraries->getLibrariesInfo(); + const auto librariesInfo = libsAndLogs.libraries->getLibrariesInfo(); EXPECT_EQ(librariesInfo.size(), 4); for (const auto &libInfo : librariesInfo) @@ -78,12 +95,28 @@ TEST(FFmpegTest, LoadLibrariesAndLogVersion) << " with version " << libInfo.version << "\n"; } +TEST(FFmpegTest, LoadLibrariesAndCheckLoggingCallback) +{ + auto libsAndLogs = LibrariesWithLogging(); + + EXPECT_FALSE(libsAndLogs.logEntries.empty()); + EXPECT_TRUE(libsAndLogs.containsLogEntry({LogLevel::Info, "Logging function set successfully"})); + EXPECT_TRUE(libsAndLogs.containsLogEntry( + {LogLevel::Debug, "Successfully resolved function avutil_version"})); + EXPECT_TRUE(libsAndLogs.containsLogEntry({LogLevel::Info, "Setting up av logging callback"})); + + // WHy? This makes no sense + EXPECT_FALSE( + libsAndLogs.containsLogEntry({LogLevel::Info, "Disconnectiong av logging callback"})); +} + TEST(FFmpegTest, CheckFormatAndStreamParameters) { - auto ffmpegLibraries = openLibraries(); - auto demuxer = openTestFileInDemuxer(ffmpegLibraries); - const auto formatContext = demuxer.getFormatContext(); - const auto majorVersion = ffmpegLibraries->getLibrariesVersion().avformat.major; + auto libsAndLogs = LibrariesWithLogging(); + + auto demuxer = libsAndLogs.openTestFileInDemuxer(); + const auto formatContext = demuxer.getFormatContext(); + const auto majorVersion = libsAndLogs.libraries->getLibrariesVersion().avformat.major; if (majorVersion <= 56) EXPECT_EQ(formatContext->getStartTime(), -23220); @@ -203,8 +236,9 @@ TEST(FFmpegTest, CheckFormatAndStreamParameters) TEST(FFmpegTest, DemuxPackets) { - auto libraries = openLibraries(); - auto demuxer = openTestFileInDemuxer(libraries); + auto libsAndLogs = LibrariesWithLogging(); + + auto demuxer = libsAndLogs.openTestFileInDemuxer(); const auto formatContext = demuxer.getFormatContext(); constexpr std::array expectedDataSizesVideo = {3827, 499, 90, 46, 29, 439, 82, 36, 40, @@ -222,7 +256,7 @@ TEST(FFmpegTest, DemuxPackets) EXPECT_TRUE(packet->getStreamIndex() == 0 || packet->getStreamIndex() == 1); if (packet->getStreamIndex() == 0) { - if (packetCountAudio == 0 && libraries->getLibrariesVersion().avcodec.major == 57) + if (packetCountAudio == 0 && libsAndLogs.libraries->getLibrariesVersion().avcodec.major == 57) // For some reason, the first packet is 23 bytes larger for this version of FFmpeg. EXPECT_EQ(packet->getDataSize(), 366) << "Audio packet number " << packetCountAudio; else @@ -244,13 +278,14 @@ TEST(FFmpegTest, DemuxPackets) TEST(FFmpegTest, DecodingTest) { - auto ffmpegLibraries = openLibraries(); - auto demuxer = openTestFileInDemuxer(ffmpegLibraries); - auto decoder = Decoder(ffmpegLibraries); + auto libsAndLogs = LibrariesWithLogging(); + + auto demuxer = libsAndLogs.openTestFileInDemuxer(); + auto decoder = Decoder(libsAndLogs.libraries); const auto streamToDecode = 1; const auto stream = demuxer.getFormatContext()->getStream(streamToDecode); - const auto avcodecVersionMajor = ffmpegLibraries->getLibrariesVersion().avcodec.major; + const auto avcodecVersionMajor = libsAndLogs.libraries->getLibrariesVersion().avcodec.major; // The amount of padding that FFmpeg uses depends on how it was compiled. // Here, we use our own compiled versions. But in general this can not be predicted.