Message ID | 20210831063735.1136615-1-vedantparanjape160201@gmail.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Le mardi 31 août 2021 à 12:07 +0530, Vedant Paranjape a écrit : > Lot of code used in single stream test is boiler plate and common across > every gstreamer test. Factored out this code into a base class called > GstreamerTest. > > Also updated the gstreamer_single_stream_test to use the GstreamerTest > base class > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> > --- > .../gstreamer_single_stream_test.cpp | 145 +++------------ > test/gstreamer/gstreamer_test.cpp | 165 ++++++++++++++++++ > test/gstreamer/gstreamer_test.h | 39 +++++ > test/gstreamer/meson.build | 2 +- > 4 files changed, 229 insertions(+), 122 deletions(-) > create mode 100644 test/gstreamer/gstreamer_test.cpp > create mode 100644 test/gstreamer/gstreamer_test.h > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp > index 4c8d4804..c27e4d36 100644 > --- a/test/gstreamer/gstreamer_single_stream_test.cpp > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp > @@ -14,104 +14,48 @@ > > #include <gst/gst.h> > > +#include "gstreamer_test.h" > #include "test.h" > > using namespace std; > > -extern "C" { > -const char *__asan_default_options() > -{ > - /* > - * Disable leak detection due to a known global variable initialization > - * leak in glib's g_quark_init(). This should ideally be handled by > - * using a suppression file instead of disabling leak detection. > - */ > - return "detect_leaks=false"; > -} > -} > - > -class GstreamerSingleStreamTest : public Test > +class GstreamerSingleStreamTest : public GstreamerTest, public Test > { > +public: > + GstreamerSingleStreamTest() > + : GstreamerTest() > + { > + } > + > protected: > int init() override > { > - /* > - * GStreamer by default spawns a process to run the > - * gst-plugin-scanner helper. If libcamera is compiled with ASan > - * enabled, and as GStreamer is most likely not, this causes the > - * ASan link order check to fail when gst-plugin-scanner > - * dlopen()s the plugin as many libraries will have already been > - * loaded by then. Fix this issue by disabling spawning of a > - * child helper process when scanning the build directory for > - * plugins. > - */ > - gst_registry_fork_set_enabled(false); > - > - /* Initialize GStreamer */ > - g_autoptr(GError) errInit = NULL; > - if (!gst_init_check(nullptr, nullptr, &errInit)) { > - g_printerr("Could not initialize GStreamer: %s\n", > - errInit ? errInit->message : "unknown error"); > + if (status_ != TestPass) > + return status_; > > - return TestFail; > - } > + g_autoptr(GstElement) convert0__ = gst_element_factory_make("videoconvert", "convert0"); > + g_autoptr(GstElement) sink0__ = gst_element_factory_make("fakesink", "sink0"); > + g_object_ref_sink(convert0__); > + g_object_ref_sink(sink0__); > > - /* > - * Remove the system libcamera plugin, if any, and add the > - * plugin from the build directory. > - */ > - GstRegistry *registry = gst_registry_get(); > - GstPlugin *plugin = gst_registry_lookup(registry, "libgstlibcamera.so"); > - if (plugin) { > - gst_registry_remove_plugin(registry, plugin); > - gst_object_unref(plugin); > - } > + if (!convert0__ || !sink0__) { > + g_printerr("Not all elements could be created. %p.%p\n", > + convert0__, sink0__); > > - std::string path = libcamera::utils::libcameraBuildPath() > - + "src/gstreamer"; > - if (!gst_registry_scan_path(registry, path.c_str())) { > - g_printerr("Failed to add plugin to registry\n"); > - gst_deinit(); > return TestFail; > } > > - /* Create the elements */ > - libcameraSrc_ = gst_element_factory_make("libcamerasrc", "libcamera"); > - convert0_ = gst_element_factory_make("videoconvert", "convert0"); > - sink0_ = gst_element_factory_make("fakesink", "sink0"); > - > - /* Create the empty pipeline_ */ > - pipeline_ = gst_pipeline_new("test-pipeline"); > - > - if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) { > - g_printerr("Not all elements could be created. %p.%p.%p.%p\n", > - pipeline_, convert0_, sink0_, libcameraSrc_); > - if (pipeline_) > - gst_object_unref(pipeline_); > - if (convert0_) > - gst_object_unref(convert0_); > - if (sink0_) > - gst_object_unref(sink0_); > - if (libcameraSrc_) > - gst_object_unref(libcameraSrc_); > - gst_deinit(); > + convert0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert0__)); > + sink0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink0__)); > > + if (gstreamer_create_pipeline() != TestPass) > return TestFail; > - } > > return TestPass; > } > > - void cleanup() override > - { > - gst_object_unref(pipeline_); > - gst_deinit(); > - } > - > int run() override > { > - GstStateChangeReturn ret; > - > /* Build the pipeline */ > gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL); > if (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) { > @@ -119,57 +63,16 @@ protected: > return TestFail; > } > > - /* Start playing */ > - ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING); > - if (ret == GST_STATE_CHANGE_FAILURE) { > - g_printerr("Unable to set the pipeline to the playing state.\n"); > + if (gstreamer_start_pipeline() != TestPass) > return TestFail; > - } > > - /* Wait until error or EOS or timeout after 2 seconds */ > - constexpr GstMessageType msgType = > - static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS); > - constexpr GstClockTime timeout = 2 * GST_SECOND; > - > - g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_); > - g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType); > - > - gst_element_set_state(pipeline_, GST_STATE_NULL); > - > - /* Parse error message */ > - if (msg == NULL) > - return TestPass; > - > - switch (GST_MESSAGE_TYPE(msg)) { > - case GST_MESSAGE_ERROR: > - gstreamer_print_error(msg); > - break; > - case GST_MESSAGE_EOS: > - g_print("End-Of-Stream reached.\n"); > - break; > - default: > - g_printerr("Unexpected message received.\n"); > - break; > - } > + if (gstreamer_process_event() != TestPass) > + return TestFail; > > - return TestFail; > + return TestPass; > } > > private: > - void gstreamer_print_error(GstMessage *msg) > - { > - g_autoptr(GError) err = NULL; > - g_autofree gchar *debug_info = NULL; > - > - 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"); > - } > - > - GstElement *pipeline_; > - GstElement *libcameraSrc_; > GstElement *convert0_; > GstElement *sink0_; > }; > diff --git a/test/gstreamer/gstreamer_test.cpp b/test/gstreamer/gstreamer_test.cpp > new file mode 100644 > index 00000000..d9a9618a > --- /dev/null > +++ b/test/gstreamer/gstreamer_test.cpp > @@ -0,0 +1,165 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2021, Vedant Paranjape > + * > + * libcamera Gstreamer element API tests > + */ > + > +#include "gstreamer_test.h" > + > +#include "test.h" > + > +using namespace std; > + > +extern "C" { > +const char *__asan_default_options() > +{ > + /* > + * Disable leak detection due to a known global variable initialization > + * leak in glib's g_quark_init(). This should ideally be handled by > + * using a suppression file instead of disabling leak detection. > + */ > + return "detect_leaks=false"; > +} > +} > + > +GstreamerTest::GstreamerTest() > +{ > + /* > + * GStreamer by default spawns a process to run the > + * gst-plugin-scanner helper. If libcamera is compiled with ASan > + * enabled, and as GStreamer is most likely not, this causes the > + * ASan link order check to fail when gst-plugin-scanner > + * dlopen()s the plugin as many libraries will have already been > + * loaded by then. Fix this issue by disabling spawning of a > + * child helper process when scanning the build directory for > + * plugins. > + */ > + gst_registry_fork_set_enabled(false); > + > + /* Initialize GStreamer */ > + g_autoptr(GError) errInit = NULL; > + if (!gst_init_check(nullptr, nullptr, &errInit)) { > + g_printerr("Could not initialize GStreamer: %s\n", > + errInit ? errInit->message : "unknown error"); > + > + status_ = TestFail; > + return; > + } > + > + /* > + * Remove the system libcamera plugin, if any, and add the > + * plugin from the build directory. > + */ > + GstRegistry *registry = gst_registry_get(); > + g_autoptr(GstPlugin) plugin = gst_registry_lookup(registry, "libgstlibcamera.so"); > + if (plugin) { > + gst_registry_remove_plugin(registry, plugin); > + } > + > + std::string path = libcamera::utils::libcameraBuildPath() + "src/gstreamer"; > + if (!gst_registry_scan_path(registry, path.c_str())) { > + g_printerr("Failed to add plugin to registry\n"); > + > + status_ = TestFail; > + return; > + } > + > + /* Create the element */ > + g_autoptr(GstElement) libcameraSrc__ = gst_element_factory_make("libcamerasrc", "libcamera"); > + g_object_ref_sink(libcameraSrc__); I'm a bit insisting, but the base class should not limit to 1 libcamerasrc. One of the I fixed recently was to driver two cameras concurrently. I was to write a test when this settle, but it would be quite some work with current case class. I would like to suggest a virtual function, create_pipeline(), which delegate completely the creation of the pipeline. > + > + if (!libcameraSrc__) { > + g_printerr("Unable to create libcamera element\n"); > + > + status_ = TestFail; > + return; > + } > + > + libcameraSrc_ = reinterpret_cast<GstElement *>(g_steal_pointer(&libcameraSrc__)); > + > + status_ = TestPass; > +} > + > +GstreamerTest::~GstreamerTest() > +{ > + if (pipeline_) > + gst_object_unref(pipeline_); > + if (status_ == TestFail) { > + gst_object_unref(libcameraSrc_); > + } > + > + gst_deinit(); > +} > + > +int GstreamerTest::gstreamer_create_pipeline() > +{ > + pipeline_ = gst_pipeline_new("test-pipeline"); > + > + if (!pipeline_) { > + g_printerr("Unable to create pipeline\n"); > + > + return TestFail; > + } > + > + return TestPass; > +}:la > + > +int GstreamerTest::gstreamer_start_pipeline() > +{ > + GstStateChangeReturn ret; > + > + /* Start playing */ > + ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING); > + if (ret == GST_STATE_CHANGE_FAILURE) { > + g_printerr("Unable to set the pipeline to the playing state.\n"); > + return TestFail; > + } > + > + return TestPass; > +} > + > +int GstreamerTest::gstreamer_process_event() > +{ > + /* Wait until error or EOS or timeout after 2 seconds */ > + constexpr GstMessageType msgType = > + static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS); > + constexpr GstClockTime timeout = 2 * GST_SECOND; > + > + g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_); > + g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType); > + > + gst_element_set_state(pipeline_, GST_STATE_NULL); > + > + /* Parse error message */ > + if (msg == NULL) { > + return TestPass; > + } > + > + switch (GST_MESSAGE_TYPE(msg)) { > + case GST_MESSAGE_ERROR: > + gstreamer_print_error(msg); > + break; > + case GST_MESSAGE_EOS: > + g_print("End-Of-Stream reached.\n"); > + break; > + default: > + g_printerr("Unexpected message received.\n"); > + break; > + } > + > + return TestFail; > +} > + > +void GstreamerTest::gstreamer_print_error(GstMessage *msg) > +{ > + g_autoptr(GError) err = NULL; > + g_autofree gchar *debug_info = NULL; > + > + 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"); > +} > + > diff --git a/test/gstreamer/gstreamer_test.h b/test/gstreamer/gstreamer_test.h > new file mode 100644 > index 00000000..cdffdea9 > --- /dev/null > +++ b/test/gstreamer/gstreamer_test.h > @@ -0,0 +1,39 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2021, Vedant Paranjape > + * > + * gstreamer_test.cpp - GStreamer test base class > + */ > + > +#ifndef __LIBCAMERA_GSTREAMER_TEST_H__ > +#define __LIBCAMERA_GSTREAMER_TEST_H__ > + > +#include <iostream> > +#include <unistd.h> > + > +#include <libcamera/base/utils.h> > + > +#include "libcamera/internal/source_paths.h" > + > +#include <gst/gst.h> > + > +using namespace std; > + > +class GstreamerTest > +{ > +public: > + GstreamerTest(); > + virtual ~GstreamerTest(); > + > +protected: > + virtual int gstreamer_create_pipeline(); > + int gstreamer_start_pipeline(); > + int gstreamer_process_event(); > + void gstreamer_print_error(GstMessage *msg); > + > + GstElement *pipeline_; > + GstElement *libcameraSrc_; > + int status_; > +}; > + > +#endif /* __LIBCAMERA_GSTREAMER_TEST_H__ */ > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build > index b99aa0da..aca53b92 100644 > --- a/test/gstreamer/meson.build > +++ b/test/gstreamer/meson.build > @@ -10,7 +10,7 @@ gstreamer_tests = [ > gstreamer_dep = dependency('gstreamer-1.0', required: true) > > foreach t : gstreamer_tests > - exe = executable(t[0], t[1], > + exe = executable(t[0], t[1], 'gstreamer_test.cpp', > dependencies : [libcamera_private, gstreamer_dep], > link_with : test_libraries, > include_directories : test_includes_internal)
On Wed, 1 Sep, 2021, 01:12 Nicolas Dufresne, <nicolas@ndufresne.ca> wrote: > Le mardi 31 août 2021 à 12:07 +0530, Vedant Paranjape a écrit : > > Lot of code used in single stream test is boiler plate and common across > > every gstreamer test. Factored out this code into a base class called > > GstreamerTest. > > > > Also updated the gstreamer_single_stream_test to use the GstreamerTest > > base class > > > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> > > --- > > .../gstreamer_single_stream_test.cpp | 145 +++------------ > > test/gstreamer/gstreamer_test.cpp | 165 ++++++++++++++++++ > > test/gstreamer/gstreamer_test.h | 39 +++++ > > test/gstreamer/meson.build | 2 +- > > 4 files changed, 229 insertions(+), 122 deletions(-) > > create mode 100644 test/gstreamer/gstreamer_test.cpp > > create mode 100644 test/gstreamer/gstreamer_test.h > > > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp > b/test/gstreamer/gstreamer_single_stream_test.cpp > > index 4c8d4804..c27e4d36 100644 > > --- a/test/gstreamer/gstreamer_single_stream_test.cpp > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp > > @@ -14,104 +14,48 @@ > > > > #include <gst/gst.h> > > > > +#include "gstreamer_test.h" > > #include "test.h" > > > > using namespace std; > > > > -extern "C" { > > -const char *__asan_default_options() > > -{ > > - /* > > - * Disable leak detection due to a known global variable > initialization > > - * leak in glib's g_quark_init(). This should ideally be handled by > > - * using a suppression file instead of disabling leak detection. > > - */ > > - return "detect_leaks=false"; > > -} > > -} > > - > > -class GstreamerSingleStreamTest : public Test > > +class GstreamerSingleStreamTest : public GstreamerTest, public Test > > { > > +public: > > + GstreamerSingleStreamTest() > > + : GstreamerTest() > > + { > > + } > > + > > protected: > > int init() override > > { > > - /* > > - * GStreamer by default spawns a process to run the > > - * gst-plugin-scanner helper. If libcamera is compiled > with ASan > > - * enabled, and as GStreamer is most likely not, this > causes the > > - * ASan link order check to fail when gst-plugin-scanner > > - * dlopen()s the plugin as many libraries will have > already been > > - * loaded by then. Fix this issue by disabling spawning of > a > > - * child helper process when scanning the build directory > for > > - * plugins. > > - */ > > - gst_registry_fork_set_enabled(false); > > - > > - /* Initialize GStreamer */ > > - g_autoptr(GError) errInit = NULL; > > - if (!gst_init_check(nullptr, nullptr, &errInit)) { > > - g_printerr("Could not initialize GStreamer: %s\n", > > - errInit ? errInit->message : "unknown > error"); > > + if (status_ != TestPass) > > + return status_; > > > > - return TestFail; > > - } > > + g_autoptr(GstElement) convert0__ = > gst_element_factory_make("videoconvert", "convert0"); > > + g_autoptr(GstElement) sink0__ = > gst_element_factory_make("fakesink", "sink0"); > > + g_object_ref_sink(convert0__); > > + g_object_ref_sink(sink0__); > > > > - /* > > - * Remove the system libcamera plugin, if any, and add the > > - * plugin from the build directory. > > - */ > > - GstRegistry *registry = gst_registry_get(); > > - GstPlugin *plugin = gst_registry_lookup(registry, > "libgstlibcamera.so"); > > - if (plugin) { > > - gst_registry_remove_plugin(registry, plugin); > > - gst_object_unref(plugin); > > - } > > + if (!convert0__ || !sink0__) { > > + g_printerr("Not all elements could be created. > %p.%p\n", > > + convert0__, sink0__); > > > > - std::string path = libcamera::utils::libcameraBuildPath() > > - + "src/gstreamer"; > > - if (!gst_registry_scan_path(registry, path.c_str())) { > > - g_printerr("Failed to add plugin to registry\n"); > > - gst_deinit(); > > return TestFail; > > } > > > > - /* Create the elements */ > > - libcameraSrc_ = gst_element_factory_make("libcamerasrc", > "libcamera"); > > - convert0_ = gst_element_factory_make("videoconvert", > "convert0"); > > - sink0_ = gst_element_factory_make("fakesink", "sink0"); > > - > > - /* Create the empty pipeline_ */ > > - pipeline_ = gst_pipeline_new("test-pipeline"); > > - > > - if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) > { > > - g_printerr("Not all elements could be created. > %p.%p.%p.%p\n", > > - pipeline_, convert0_, sink0_, > libcameraSrc_); > > - if (pipeline_) > > - gst_object_unref(pipeline_); > > - if (convert0_) > > - gst_object_unref(convert0_); > > - if (sink0_) > > - gst_object_unref(sink0_); > > - if (libcameraSrc_) > > - gst_object_unref(libcameraSrc_); > > - gst_deinit(); > > + convert0_ = reinterpret_cast<GstElement > *>(g_steal_pointer(&convert0__)); > > + sink0_ = reinterpret_cast<GstElement > *>(g_steal_pointer(&sink0__)); > > > > + if (gstreamer_create_pipeline() != TestPass) > > return TestFail; > > - } > > > > return TestPass; > > } > > > > - void cleanup() override > > - { > > - gst_object_unref(pipeline_); > > - gst_deinit(); > > - } > > - > > int run() override > > { > > - GstStateChangeReturn ret; > > - > > /* Build the pipeline */ > > gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, > convert0_, sink0_, NULL); > > if (gst_element_link_many(libcameraSrc_, convert0_, > sink0_, NULL) != TRUE) { > > @@ -119,57 +63,16 @@ protected: > > return TestFail; > > } > > > > - /* Start playing */ > > - ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING); > > - if (ret == GST_STATE_CHANGE_FAILURE) { > > - g_printerr("Unable to set the pipeline to the > playing state.\n"); > > + if (gstreamer_start_pipeline() != TestPass) > > return TestFail; > > - } > > > > - /* Wait until error or EOS or timeout after 2 seconds */ > > - constexpr GstMessageType msgType = > > - static_cast<GstMessageType>(GST_MESSAGE_ERROR | > GST_MESSAGE_EOS); > > - constexpr GstClockTime timeout = 2 * GST_SECOND; > > - > > - g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_); > > - g_autoptr(GstMessage) msg = > gst_bus_timed_pop_filtered(bus, timeout, msgType); > > - > > - gst_element_set_state(pipeline_, GST_STATE_NULL); > > - > > - /* Parse error message */ > > - if (msg == NULL) > > - return TestPass; > > - > > - switch (GST_MESSAGE_TYPE(msg)) { > > - case GST_MESSAGE_ERROR: > > - gstreamer_print_error(msg); > > - break; > > - case GST_MESSAGE_EOS: > > - g_print("End-Of-Stream reached.\n"); > > - break; > > - default: > > - g_printerr("Unexpected message received.\n"); > > - break; > > - } > > + if (gstreamer_process_event() != TestPass) > > + return TestFail; > > > > - return TestFail; > > + return TestPass; > > } > > > > private: > > - void gstreamer_print_error(GstMessage *msg) > > - { > > - g_autoptr(GError) err = NULL; > > - g_autofree gchar *debug_info = NULL; > > - > > - 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"); > > - } > > - > > - GstElement *pipeline_; > > - GstElement *libcameraSrc_; > > GstElement *convert0_; > > GstElement *sink0_; > > }; > > diff --git a/test/gstreamer/gstreamer_test.cpp > b/test/gstreamer/gstreamer_test.cpp > > new file mode 100644 > > index 00000000..d9a9618a > > --- /dev/null > > +++ b/test/gstreamer/gstreamer_test.cpp > > @@ -0,0 +1,165 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * Copyright (C) 2021, Vedant Paranjape > > + * > > + * libcamera Gstreamer element API tests > > + */ > > + > > +#include "gstreamer_test.h" > > + > > +#include "test.h" > > + > > +using namespace std; > > + > > +extern "C" { > > +const char *__asan_default_options() > > +{ > > + /* > > + * Disable leak detection due to a known global variable > initialization > > + * leak in glib's g_quark_init(). This should ideally be handled by > > + * using a suppression file instead of disabling leak detection. > > + */ > > + return "detect_leaks=false"; > > +} > > +} > > + > > +GstreamerTest::GstreamerTest() > > +{ > > + /* > > + * GStreamer by default spawns a process to run the > > + * gst-plugin-scanner helper. If libcamera is compiled with ASan > > + * enabled, and as GStreamer is most likely not, this causes the > > + * ASan link order check to fail when gst-plugin-scanner > > + * dlopen()s the plugin as many libraries will have already been > > + * loaded by then. Fix this issue by disabling spawning of a > > + * child helper process when scanning the build directory for > > + * plugins. > > + */ > > + gst_registry_fork_set_enabled(false); > > + > > + /* Initialize GStreamer */ > > + g_autoptr(GError) errInit = NULL; > > + if (!gst_init_check(nullptr, nullptr, &errInit)) { > > + g_printerr("Could not initialize GStreamer: %s\n", > > + errInit ? errInit->message : "unknown error"); > > + > > + status_ = TestFail; > > + return; > > + } > > + > > + /* > > + * Remove the system libcamera plugin, if any, and add the > > + * plugin from the build directory. > > + */ > > + GstRegistry *registry = gst_registry_get(); > > + g_autoptr(GstPlugin) plugin = gst_registry_lookup(registry, > "libgstlibcamera.so"); > > + if (plugin) { > > + gst_registry_remove_plugin(registry, plugin); > > + } > > + > > + std::string path = libcamera::utils::libcameraBuildPath() + > "src/gstreamer"; > > + if (!gst_registry_scan_path(registry, path.c_str())) { > > + g_printerr("Failed to add plugin to registry\n"); > > + > > + status_ = TestFail; > > + return; > > + } > > + > > + /* Create the element */ > > + g_autoptr(GstElement) libcameraSrc__ = > gst_element_factory_make("libcamerasrc", "libcamera"); > > + g_object_ref_sink(libcameraSrc__); > > I'm a bit insisting, but the base class should not limit to 1 > libcamerasrc. One > of the I fixed recently was to driver two cameras concurrently. I was to > write a > test when this settle, but it would be quite some work with current case > class. > > I would like to suggest a virtual function, create_pipeline(), which > delegate > completely the creation of the pipeline. > Okay so you mean, libcamera element and pipeline making all in one function create_pipeline, as I have actually defined the function below. > > > > + > > + if (!libcameraSrc__) { > > + g_printerr("Unable to create libcamera element\n"); > > + > > + status_ = TestFail; > > + return; > > + } > > + > > + libcameraSrc_ = reinterpret_cast<GstElement > *>(g_steal_pointer(&libcameraSrc__)); > > + > > + status_ = TestPass; > > +} > > + > > +GstreamerTest::~GstreamerTest() > > +{ > > + if (pipeline_) > > + gst_object_unref(pipeline_); > > + if (status_ == TestFail) { > > + gst_object_unref(libcameraSrc_); > > + } > > + > > + gst_deinit(); > > +} > > + > > +int GstreamerTest::gstreamer_create_pipeline() > > +{ > > + pipeline_ = gst_pipeline_new("test-pipeline"); > > + > > + if (!pipeline_) { > > + g_printerr("Unable to create pipeline\n"); > > + > > + return TestFail; > > + } > > + > > + return TestPass; > > +}:la > > + > > +int GstreamerTest::gstreamer_start_pipeline() > > +{ > > + GstStateChangeReturn ret; > > + > > + /* Start playing */ > > + ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING); > > + if (ret == GST_STATE_CHANGE_FAILURE) { > > + g_printerr("Unable to set the pipeline to the playing > state.\n"); > > + return TestFail; > > + } > > + > > + return TestPass; > > +} > > + > > +int GstreamerTest::gstreamer_process_event() > > +{ > > + /* Wait until error or EOS or timeout after 2 seconds */ > > + constexpr GstMessageType msgType = > > + static_cast<GstMessageType>(GST_MESSAGE_ERROR | > GST_MESSAGE_EOS); > > + constexpr GstClockTime timeout = 2 * GST_SECOND; > > + > > + g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_); > > + g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, > timeout, msgType); > > + > > + gst_element_set_state(pipeline_, GST_STATE_NULL); > > + > > + /* Parse error message */ > > + if (msg == NULL) { > > + return TestPass; > > + } > > + > > + switch (GST_MESSAGE_TYPE(msg)) { > > + case GST_MESSAGE_ERROR: > > + gstreamer_print_error(msg); > > + break; > > + case GST_MESSAGE_EOS: > > + g_print("End-Of-Stream reached.\n"); > > + break; > > + default: > > + g_printerr("Unexpected message received.\n"); > > + break; > > + } > > + > > + return TestFail; > > +} > > + > > +void GstreamerTest::gstreamer_print_error(GstMessage *msg) > > +{ > > + g_autoptr(GError) err = NULL; > > + g_autofree gchar *debug_info = NULL; > > + > > + 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"); > > +} > > + > > diff --git a/test/gstreamer/gstreamer_test.h > b/test/gstreamer/gstreamer_test.h > > new file mode 100644 > > index 00000000..cdffdea9 > > --- /dev/null > > +++ b/test/gstreamer/gstreamer_test.h > > @@ -0,0 +1,39 @@ > > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > > +/* > > + * Copyright (C) 2021, Vedant Paranjape > > + * > > + * gstreamer_test.cpp - GStreamer test base class > > + */ > > + > > +#ifndef __LIBCAMERA_GSTREAMER_TEST_H__ > > +#define __LIBCAMERA_GSTREAMER_TEST_H__ > > + > > +#include <iostream> > > +#include <unistd.h> > > + > > +#include <libcamera/base/utils.h> > > + > > +#include "libcamera/internal/source_paths.h" > > + > > +#include <gst/gst.h> > > + > > +using namespace std; > > + > > +class GstreamerTest > > +{ > > +public: > > + GstreamerTest(); > > + virtual ~GstreamerTest(); > > + > > +protected: > > + virtual int gstreamer_create_pipeline(); > > + int gstreamer_start_pipeline(); > > + int gstreamer_process_event(); > > + void gstreamer_print_error(GstMessage *msg); > > + > > + GstElement *pipeline_; > > + GstElement *libcameraSrc_; > > + int status_; > > +}; > > + > > +#endif /* __LIBCAMERA_GSTREAMER_TEST_H__ */ > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build > > index b99aa0da..aca53b92 100644 > > --- a/test/gstreamer/meson.build > > +++ b/test/gstreamer/meson.build > > @@ -10,7 +10,7 @@ gstreamer_tests = [ > > gstreamer_dep = dependency('gstreamer-1.0', required: true) > > > > foreach t : gstreamer_tests > > - exe = executable(t[0], t[1], > > + exe = executable(t[0], t[1], 'gstreamer_test.cpp', > > dependencies : [libcamera_private, gstreamer_dep], > > link_with : test_libraries, > > include_directories : test_includes_internal) > > >
diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp index 4c8d4804..c27e4d36 100644 --- a/test/gstreamer/gstreamer_single_stream_test.cpp +++ b/test/gstreamer/gstreamer_single_stream_test.cpp @@ -14,104 +14,48 @@ #include <gst/gst.h> +#include "gstreamer_test.h" #include "test.h" using namespace std; -extern "C" { -const char *__asan_default_options() -{ - /* - * Disable leak detection due to a known global variable initialization - * leak in glib's g_quark_init(). This should ideally be handled by - * using a suppression file instead of disabling leak detection. - */ - return "detect_leaks=false"; -} -} - -class GstreamerSingleStreamTest : public Test +class GstreamerSingleStreamTest : public GstreamerTest, public Test { +public: + GstreamerSingleStreamTest() + : GstreamerTest() + { + } + protected: int init() override { - /* - * GStreamer by default spawns a process to run the - * gst-plugin-scanner helper. If libcamera is compiled with ASan - * enabled, and as GStreamer is most likely not, this causes the - * ASan link order check to fail when gst-plugin-scanner - * dlopen()s the plugin as many libraries will have already been - * loaded by then. Fix this issue by disabling spawning of a - * child helper process when scanning the build directory for - * plugins. - */ - gst_registry_fork_set_enabled(false); - - /* Initialize GStreamer */ - g_autoptr(GError) errInit = NULL; - if (!gst_init_check(nullptr, nullptr, &errInit)) { - g_printerr("Could not initialize GStreamer: %s\n", - errInit ? errInit->message : "unknown error"); + if (status_ != TestPass) + return status_; - return TestFail; - } + g_autoptr(GstElement) convert0__ = gst_element_factory_make("videoconvert", "convert0"); + g_autoptr(GstElement) sink0__ = gst_element_factory_make("fakesink", "sink0"); + g_object_ref_sink(convert0__); + g_object_ref_sink(sink0__); - /* - * Remove the system libcamera plugin, if any, and add the - * plugin from the build directory. - */ - GstRegistry *registry = gst_registry_get(); - GstPlugin *plugin = gst_registry_lookup(registry, "libgstlibcamera.so"); - if (plugin) { - gst_registry_remove_plugin(registry, plugin); - gst_object_unref(plugin); - } + if (!convert0__ || !sink0__) { + g_printerr("Not all elements could be created. %p.%p\n", + convert0__, sink0__); - std::string path = libcamera::utils::libcameraBuildPath() - + "src/gstreamer"; - if (!gst_registry_scan_path(registry, path.c_str())) { - g_printerr("Failed to add plugin to registry\n"); - gst_deinit(); return TestFail; } - /* Create the elements */ - libcameraSrc_ = gst_element_factory_make("libcamerasrc", "libcamera"); - convert0_ = gst_element_factory_make("videoconvert", "convert0"); - sink0_ = gst_element_factory_make("fakesink", "sink0"); - - /* Create the empty pipeline_ */ - pipeline_ = gst_pipeline_new("test-pipeline"); - - if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) { - g_printerr("Not all elements could be created. %p.%p.%p.%p\n", - pipeline_, convert0_, sink0_, libcameraSrc_); - if (pipeline_) - gst_object_unref(pipeline_); - if (convert0_) - gst_object_unref(convert0_); - if (sink0_) - gst_object_unref(sink0_); - if (libcameraSrc_) - gst_object_unref(libcameraSrc_); - gst_deinit(); + convert0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert0__)); + sink0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink0__)); + if (gstreamer_create_pipeline() != TestPass) return TestFail; - } return TestPass; } - void cleanup() override - { - gst_object_unref(pipeline_); - gst_deinit(); - } - int run() override { - GstStateChangeReturn ret; - /* Build the pipeline */ gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL); if (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) { @@ -119,57 +63,16 @@ protected: return TestFail; } - /* Start playing */ - ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING); - if (ret == GST_STATE_CHANGE_FAILURE) { - g_printerr("Unable to set the pipeline to the playing state.\n"); + if (gstreamer_start_pipeline() != TestPass) return TestFail; - } - /* Wait until error or EOS or timeout after 2 seconds */ - constexpr GstMessageType msgType = - static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS); - constexpr GstClockTime timeout = 2 * GST_SECOND; - - g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_); - g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType); - - gst_element_set_state(pipeline_, GST_STATE_NULL); - - /* Parse error message */ - if (msg == NULL) - return TestPass; - - switch (GST_MESSAGE_TYPE(msg)) { - case GST_MESSAGE_ERROR: - gstreamer_print_error(msg); - break; - case GST_MESSAGE_EOS: - g_print("End-Of-Stream reached.\n"); - break; - default: - g_printerr("Unexpected message received.\n"); - break; - } + if (gstreamer_process_event() != TestPass) + return TestFail; - return TestFail; + return TestPass; } private: - void gstreamer_print_error(GstMessage *msg) - { - g_autoptr(GError) err = NULL; - g_autofree gchar *debug_info = NULL; - - 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"); - } - - GstElement *pipeline_; - GstElement *libcameraSrc_; GstElement *convert0_; GstElement *sink0_; }; diff --git a/test/gstreamer/gstreamer_test.cpp b/test/gstreamer/gstreamer_test.cpp new file mode 100644 index 00000000..d9a9618a --- /dev/null +++ b/test/gstreamer/gstreamer_test.cpp @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Vedant Paranjape + * + * libcamera Gstreamer element API tests + */ + +#include "gstreamer_test.h" + +#include "test.h" + +using namespace std; + +extern "C" { +const char *__asan_default_options() +{ + /* + * Disable leak detection due to a known global variable initialization + * leak in glib's g_quark_init(). This should ideally be handled by + * using a suppression file instead of disabling leak detection. + */ + return "detect_leaks=false"; +} +} + +GstreamerTest::GstreamerTest() +{ + /* + * GStreamer by default spawns a process to run the + * gst-plugin-scanner helper. If libcamera is compiled with ASan + * enabled, and as GStreamer is most likely not, this causes the + * ASan link order check to fail when gst-plugin-scanner + * dlopen()s the plugin as many libraries will have already been + * loaded by then. Fix this issue by disabling spawning of a + * child helper process when scanning the build directory for + * plugins. + */ + gst_registry_fork_set_enabled(false); + + /* Initialize GStreamer */ + g_autoptr(GError) errInit = NULL; + if (!gst_init_check(nullptr, nullptr, &errInit)) { + g_printerr("Could not initialize GStreamer: %s\n", + errInit ? errInit->message : "unknown error"); + + status_ = TestFail; + return; + } + + /* + * Remove the system libcamera plugin, if any, and add the + * plugin from the build directory. + */ + GstRegistry *registry = gst_registry_get(); + g_autoptr(GstPlugin) plugin = gst_registry_lookup(registry, "libgstlibcamera.so"); + if (plugin) { + gst_registry_remove_plugin(registry, plugin); + } + + std::string path = libcamera::utils::libcameraBuildPath() + "src/gstreamer"; + if (!gst_registry_scan_path(registry, path.c_str())) { + g_printerr("Failed to add plugin to registry\n"); + + status_ = TestFail; + return; + } + + /* Create the element */ + g_autoptr(GstElement) libcameraSrc__ = gst_element_factory_make("libcamerasrc", "libcamera"); + g_object_ref_sink(libcameraSrc__); + + if (!libcameraSrc__) { + g_printerr("Unable to create libcamera element\n"); + + status_ = TestFail; + return; + } + + libcameraSrc_ = reinterpret_cast<GstElement *>(g_steal_pointer(&libcameraSrc__)); + + status_ = TestPass; +} + +GstreamerTest::~GstreamerTest() +{ + if (pipeline_) + gst_object_unref(pipeline_); + if (status_ == TestFail) { + gst_object_unref(libcameraSrc_); + } + + gst_deinit(); +} + +int GstreamerTest::gstreamer_create_pipeline() +{ + pipeline_ = gst_pipeline_new("test-pipeline"); + + if (!pipeline_) { + g_printerr("Unable to create pipeline\n"); + + return TestFail; + } + + return TestPass; +}:la + +int GstreamerTest::gstreamer_start_pipeline() +{ + GstStateChangeReturn ret; + + /* Start playing */ + ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) { + g_printerr("Unable to set the pipeline to the playing state.\n"); + return TestFail; + } + + return TestPass; +} + +int GstreamerTest::gstreamer_process_event() +{ + /* Wait until error or EOS or timeout after 2 seconds */ + constexpr GstMessageType msgType = + static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS); + constexpr GstClockTime timeout = 2 * GST_SECOND; + + g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_); + g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType); + + gst_element_set_state(pipeline_, GST_STATE_NULL); + + /* Parse error message */ + if (msg == NULL) { + return TestPass; + } + + switch (GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_ERROR: + gstreamer_print_error(msg); + break; + case GST_MESSAGE_EOS: + g_print("End-Of-Stream reached.\n"); + break; + default: + g_printerr("Unexpected message received.\n"); + break; + } + + return TestFail; +} + +void GstreamerTest::gstreamer_print_error(GstMessage *msg) +{ + g_autoptr(GError) err = NULL; + g_autofree gchar *debug_info = NULL; + + 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"); +} + diff --git a/test/gstreamer/gstreamer_test.h b/test/gstreamer/gstreamer_test.h new file mode 100644 index 00000000..cdffdea9 --- /dev/null +++ b/test/gstreamer/gstreamer_test.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021, Vedant Paranjape + * + * gstreamer_test.cpp - GStreamer test base class + */ + +#ifndef __LIBCAMERA_GSTREAMER_TEST_H__ +#define __LIBCAMERA_GSTREAMER_TEST_H__ + +#include <iostream> +#include <unistd.h> + +#include <libcamera/base/utils.h> + +#include "libcamera/internal/source_paths.h" + +#include <gst/gst.h> + +using namespace std; + +class GstreamerTest +{ +public: + GstreamerTest(); + virtual ~GstreamerTest(); + +protected: + virtual int gstreamer_create_pipeline(); + int gstreamer_start_pipeline(); + int gstreamer_process_event(); + void gstreamer_print_error(GstMessage *msg); + + GstElement *pipeline_; + GstElement *libcameraSrc_; + int status_; +}; + +#endif /* __LIBCAMERA_GSTREAMER_TEST_H__ */ diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build index b99aa0da..aca53b92 100644 --- a/test/gstreamer/meson.build +++ b/test/gstreamer/meson.build @@ -10,7 +10,7 @@ gstreamer_tests = [ gstreamer_dep = dependency('gstreamer-1.0', required: true) foreach t : gstreamer_tests - exe = executable(t[0], t[1], + exe = executable(t[0], t[1], 'gstreamer_test.cpp', dependencies : [libcamera_private, gstreamer_dep], link_with : test_libraries, include_directories : test_includes_internal)
Lot of code used in single stream test is boiler plate and common across every gstreamer test. Factored out this code into a base class called GstreamerTest. Also updated the gstreamer_single_stream_test to use the GstreamerTest base class Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> --- .../gstreamer_single_stream_test.cpp | 145 +++------------ test/gstreamer/gstreamer_test.cpp | 165 ++++++++++++++++++ test/gstreamer/gstreamer_test.h | 39 +++++ test/gstreamer/meson.build | 2 +- 4 files changed, 229 insertions(+), 122 deletions(-) create mode 100644 test/gstreamer/gstreamer_test.cpp create mode 100644 test/gstreamer/gstreamer_test.h