-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tests cross-validate agains cmafmux
- Loading branch information
Showing
8 changed files
with
542 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Configure test project | ||
project(gstgpacplugin_test LANGUAGES CXX) | ||
enable_testing() | ||
|
||
# GoogleTest requires at least C++14 | ||
set(CMAKE_CXX_STANDARD 14) | ||
set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
|
||
# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: | ||
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") | ||
cmake_policy(SET CMP0135 NEW) | ||
endif() | ||
|
||
# Fetch GoogleTest | ||
include(FetchContent) | ||
FetchContent_Declare( | ||
googletest | ||
URL https://github.com/google/googletest/archive/2b6b042a77446ff322cd7522ca068d9f2a21c1d1.zip | ||
DOWNLOAD_EXTRACT_TIMESTAMP | ||
) | ||
|
||
# For Windows: Prevent overriding the parent project's compiler/linker settings | ||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) | ||
FetchContent_MakeAvailable(googletest) | ||
|
||
# Sources | ||
file(GLOB_RECURSE SOURCES | ||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc | ||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp | ||
) | ||
|
||
# Test executable | ||
add_executable(${PROJECT_NAME} ${SOURCES}) | ||
target_compile_options(${PROJECT_NAME} PRIVATE | ||
-g | ||
-Wall -Wextra -Werror | ||
-Wcast-align | ||
-Wno-unused-parameter | ||
-Wno-unused-variable | ||
-Wno-missing-field-initializers | ||
-Wno-cast-function-type | ||
) | ||
|
||
# Link libraries | ||
include_directories(${GSTREAMER_INCLUDE_DIRS} ${GSTREAMER_BASE_INCLUDE_DIRS}) | ||
target_link_libraries(${PROJECT_NAME} GTest::gtest_main ${GSTREAMER_LIBRARIES} ${GSTREAMER_BASE_LIBRARIES}) | ||
|
||
# Discover tests | ||
include(GoogleTest) | ||
gtest_discover_tests(${PROJECT_NAME}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Tests | ||
|
||
## Setup | ||
|
||
From the root of the repository, configure the project to build the tests: | ||
|
||
```bash | ||
cmake -S . -B build -DENABLE_TESTS=ON | ||
cmake --build build | ||
``` | ||
|
||
Some tests require [`gst-plugin-fmp4`](https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs) to be built and installed on your system. Follow the instructions in the repository to build and install the plugin. Specifically, the `cmafmux` element is used to cross-validate the output of the `gpacmp4mx` element. | ||
|
||
> [!NOTE] | ||
> GStreamer documentation describes the plugin search path in detail. You can refer to the [GStreamer documentation](https://gstreamer.freedesktop.org/documentation/gstreamer/gstregistry.html?gi-language=c) to understand how GStreamer searches for plugins. | ||
## Running tests | ||
|
||
To run the tests, execute the following command: | ||
|
||
```bash | ||
./build/tests/gstgpacplugin_test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
#pragma once | ||
|
||
#include <gst/gst.h> | ||
#include <gtest/gtest.h> | ||
#include <thread> | ||
|
||
class GstAppSink | ||
{ | ||
private: | ||
std::mutex buffer_queue_mutex; | ||
std::condition_variable buffer_queue_cv; | ||
GQueue* buffer_queue; | ||
GstElement* appsink; | ||
|
||
protected: | ||
static GstFlowReturn NewSample(GstElement* sink, GstAppSink* self) | ||
{ | ||
GstSample* sample; | ||
g_signal_emit_by_name(sink, "pull-sample", &sample); | ||
if (sample == NULL) | ||
return GST_FLOW_ERROR; | ||
|
||
// Push the sample into the buffer queue | ||
GstBufferList* buffer = gst_sample_get_buffer_list(sample); | ||
gst_buffer_list_ref(buffer); | ||
g_queue_push_tail(self->buffer_queue, buffer); | ||
gst_sample_unref(sample); | ||
self->buffer_queue_cv.notify_one(); | ||
return GST_FLOW_OK; | ||
} | ||
|
||
public: | ||
GstAppSink(GstElement* test_element, GstElement* tee, GstElement* pipeline) | ||
{ | ||
// Create the elements | ||
GstElement* queue = gst_element_factory_make("queue", NULL); | ||
appsink = gst_element_factory_make("appsink", NULL); | ||
if (!queue || !appsink) { | ||
g_error("Failed to create elements"); | ||
return; | ||
} | ||
|
||
// Set the appsink properties | ||
g_object_set( | ||
appsink, "emit-signals", TRUE, "sync", FALSE, "buffer-list", TRUE, NULL); | ||
g_signal_connect(appsink, "new-sample", G_CALLBACK(NewSample), this); | ||
buffer_queue = g_queue_new(); | ||
|
||
// Add the appsink to the test element | ||
gst_bin_add_many(GST_BIN(pipeline), queue, test_element, appsink, NULL); | ||
|
||
// Link the elements | ||
if (!gst_element_link(tee, queue) || | ||
!gst_element_link(queue, test_element) || | ||
!gst_element_link(test_element, appsink)) { | ||
g_error("Failed to link elements"); | ||
return; | ||
} | ||
} | ||
|
||
GstBufferList* PopBuffer() | ||
{ | ||
// Wait until we have at least one buffer in the queue | ||
std::unique_lock<std::mutex> lock(buffer_queue_mutex); | ||
buffer_queue_cv.wait(lock, [&]() { | ||
gboolean eos = false; | ||
g_object_get(G_OBJECT(this->appsink), "eos", &eos, NULL); | ||
if (eos) | ||
return true; | ||
return !g_queue_is_empty(this->buffer_queue); | ||
}); | ||
|
||
return (GstBufferList*)g_queue_pop_head(buffer_queue); | ||
} | ||
|
||
~GstAppSink() | ||
{ | ||
// Flush the buffer queue | ||
while (!g_queue_is_empty(buffer_queue)) { | ||
GstBufferList* buffer = (GstBufferList*)g_queue_pop_head(buffer_queue); | ||
gst_buffer_list_unref(buffer); | ||
} | ||
|
||
// Free resources | ||
gst_object_unref(appsink); | ||
g_queue_free(buffer_queue); | ||
} | ||
|
||
int GetBufferCount() { return g_queue_get_length(buffer_queue); } | ||
}; | ||
|
||
class GstTestFixture : public ::testing::Test | ||
{ | ||
protected: | ||
GstElement* tee; | ||
GstElement* pipeline; | ||
|
||
static void SetUpTestSuite() { gst_init(NULL, NULL); } | ||
static void TearDownTestSuite() { gst_deinit(); } | ||
|
||
void SetUp() override | ||
{ | ||
GstElement* source = gst_element_factory_make_full( | ||
"videotestsrc", "num-buffers", 60, "do-timestamp", TRUE, NULL); | ||
GstElement* capsfilter = gst_element_factory_make_full( | ||
"capsfilter", | ||
"caps", | ||
gst_caps_from_string("video/x-raw, framerate=30/1"), | ||
NULL); | ||
GstElement* encoder = | ||
gst_element_factory_make_full("x264enc", "key-int-max", 30, NULL); | ||
tee = gst_element_factory_make("tee", NULL); | ||
|
||
// Create the pipeline | ||
pipeline = gst_pipeline_new("test-pipeline"); | ||
if (!pipeline || !source || !capsfilter || !encoder || !tee) { | ||
g_error("Failed to create elements"); | ||
return; | ||
} | ||
|
||
// Build the pipeline | ||
gst_bin_add_many(GST_BIN(pipeline), source, capsfilter, encoder, tee, NULL); | ||
if (!gst_element_link(source, capsfilter) || | ||
!gst_element_link(capsfilter, encoder) || | ||
!gst_element_link(encoder, tee)) { | ||
g_error("Failed to link elements"); | ||
return; | ||
} | ||
} | ||
|
||
void TearDown() override | ||
{ | ||
// Wait until error or EOS | ||
GstBus* bus = gst_element_get_bus(pipeline); | ||
GstMessage* msg = gst_bus_timed_pop_filtered( | ||
bus, | ||
GST_CLOCK_TIME_NONE, | ||
(GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); | ||
|
||
// Parse message | ||
if (msg != NULL) { | ||
GError* err; | ||
gchar* debug_info; | ||
|
||
switch (GST_MESSAGE_TYPE(msg)) { | ||
case GST_MESSAGE_ERROR: | ||
gst_message_parse_error(msg, &err, &debug_info); | ||
g_printerr("Error received from element %s: %s\n", | ||
GST_OBJECT_NAME(msg->src), | ||
err->message); | ||
g_printerr("Debugging information: %s\n", | ||
debug_info ? debug_info : "none"); | ||
g_clear_error(&err); | ||
g_free(debug_info); | ||
break; | ||
case GST_MESSAGE_EOS: | ||
break; | ||
default: | ||
// We should not reach here because we only asked for ERRORs and EOS | ||
g_printerr("Unexpected message received.\n"); | ||
break; | ||
} | ||
gst_message_unref(msg); | ||
} | ||
|
||
// Free resources | ||
gst_object_unref(bus); | ||
gst_element_set_state(pipeline, GST_STATE_NULL); | ||
gst_object_unref(pipeline); | ||
} | ||
|
||
bool StartPipeline() | ||
{ | ||
GstStateChangeReturn ret = | ||
gst_element_set_state(pipeline, GST_STATE_PLAYING); | ||
if (ret == GST_STATE_CHANGE_FAILURE) { | ||
g_error("Failed to start pipeline"); | ||
return false; | ||
} | ||
return true; | ||
} | ||
}; |
Oops, something went wrong.