Skip to content

Commit

Permalink
Add some testing. Can be debugged now.
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristianFeldmann committed Apr 15, 2024
1 parent 2714c05 commit a9d70e1
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 64 deletions.
11 changes: 11 additions & 0 deletions src/lib/common/Logging.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void(const LogLevel, const std::string &)>;

} // namespace ffmpeg
115 changes: 89 additions & 26 deletions src/lib/libHandling/FFmpegLibraries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,82 @@ Path getAbsolutePath(const Path &path)
return std::filesystem::absolute(path);
}

constexpr auto MAX_NR_REGISTERED_CALLBACKS = 3;
std::array<LoggingFunction, MAX_NR_REGISTERED_CALLBACKS> 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<std::mutex> lock(this->loggingMutex);
this->loggingFunction = [](const LogLevel, const std::string &) {};
}

FFmpegLibraries::~FFmpegLibraries()
{
this->disconnectAVLoggingCallback();
}

bool FFmpegLibraries::tryLoadFFmpegLibrariesInPath(const Path &path)
{
Path directory;
Expand Down Expand Up @@ -261,17 +330,22 @@ std::vector<LibraryInfo> 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<std::mutex> 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<std::mutex> lock(this->loggingMutex);
this->loggingFunction(logLevel, message);
}

Expand All @@ -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<std::mutex> 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<std::mutex> 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<void(void *, int, const char *, va_list)>());
}

} // namespace ffmpeg
11 changes: 7 additions & 4 deletions src/lib/libHandling/FFmpegLibraries.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@
#include <common/Logging.h>
#include <libHandling/SharedLibraryLoader.h>

#include <mutex>

namespace ffmpeg
{

class FFmpegLibraries : public IFFmpegLibraries
{
public:
FFmpegLibraries();
~FFmpegLibraries() = default;
~FFmpegLibraries();

std::vector<LibraryInfo> getLibrariesInfo() const override;
LibraryVersions getLibrariesVersion() const override { return this->libraryVersions; }
Expand Down Expand Up @@ -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;
};
Expand Down
5 changes: 4 additions & 1 deletion src/lib/libHandling/libraryFunctions/AvUtilFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace ffmpeg::internal::functions
{

std::optional<AvUtilFunctions> tryBindAVUtilFunctionsFromLibrary(const SharedLibraryLoader &lib,
const LoggingFunction & log)
const LoggingFunction &log)
{
if (!lib)
{
Expand All @@ -35,6 +35,7 @@ std::optional<AvUtilFunctions> 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");
Expand Down Expand Up @@ -67,6 +68,8 @@ std::optional<AvUtilFunctions> 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(
Expand Down
3 changes: 2 additions & 1 deletion src/lib/libHandling/libraryFunctions/AvUtilFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ struct AvUtilFunctions
av_frame_get_side_data;
std::function<AVDictionary *(const AVFrame *frame)> av_frame_get_metadata;
std::function<void(void (*callback)(void *, int, const char *, va_list))> av_log_set_callback;
std::function<void(void(void *, int, const char *, va_list))> av_log_default_callback;
std::function<void(int level)> av_log_set_level;
std::function<const AVPixFmtDescriptor *(AVPixelFormat pix_fmt)> av_pix_fmt_desc_get;
std::function<const AVPixFmtDescriptor *(const AVPixFmtDescriptor *prev)> av_pix_fmt_desc_next;
std::function<AVPixelFormat(const AVPixFmtDescriptor *desc)> av_pix_fmt_desc_get_id;
};

std::optional<AvUtilFunctions> tryBindAVUtilFunctionsFromLibrary(const SharedLibraryLoader &lib,
const LoggingFunction & log);
const LoggingFunction &log);

} // namespace ffmpeg::internal::functions
99 changes: 67 additions & 32 deletions test/ffmpegTest/ffmpegTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,50 +40,83 @@ 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<IFFmpegLibraries> 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<IFFmpegLibraries> 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<LogEntry> logEntries{};
std::shared_ptr<IFFmpegLibraries> 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)
std::cerr << "[ ] [INFO] Lib " << libInfo.name << " loaded from path " << libInfo.path
<< " 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);
Expand Down Expand Up @@ -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<int, 25> expectedDataSizesVideo = {3827, 499, 90, 46, 29, 439, 82, 36, 40,
Expand All @@ -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
Expand All @@ -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.
Expand Down

0 comments on commit a9d70e1

Please sign in to comment.