[libcamera-devel] [PATCH v5] test: gstreamer: Add test for gstreamer single stream

Laurent Pinchart laurent.pinchart at ideasonboard.com
Wed Aug 11 16:04:15 CEST 2021


Hi Vedant,

On Wed, Aug 11, 2021 at 07:13:40PM +0530, Vedant Paranjape wrote:
> On Wed, Aug 11, 2021 at 6:34 PM Laurent Pinchart wrote:
> > On Wed, Aug 11, 2021 at 01:56:08PM +0100, Kieran Bingham wrote:
> > > On 11/08/2021 13:47, Kieran Bingham wrote:
> > > > On 11/08/2021 08:03, Vedant Paranjape wrote:
> > > >> This patch adds a test to test if single stream using
> > > >> libcamera's gstreamer element works.
> > > >>
> > > >> Signed-off-by: Vedant Paranjape <vedantparanjape160201 at gmail.com>
> > > >> Reviewed-by: Paul Elder <paul.elder at ideasonboard.com>
> > > >
> > > > Is there anything specific needed to run this?
> > > >
> > > > When I ran it here I get:
> > > >
> > > >
> > > > 12/66 libcamera:gstreamer / single_stream_test
> > > >  FAIL            0.01s   (exit status 255 or signal 127 SIGinvalid)
> > > > stderr:
> > > > Not all elements could be created.
> > >
> > > Somehow we need to export the GST_PLUGIN_PATH from the build to the test
> > > when it is run.
> >
> > I've run into the same issue. This needs to be fixed, as otherwise
> > running 'ninja test' will fail.
> 
> I think you need to set the path or install libcamera in root, not sure
> what needs to be fixed.

That's the whole point, it has to run without installing, and without
setting an environment variable. The following has to work without any
test failure:

git clone https://git.libcamera.org/libcamera/libcamera.git/
cd libcamera
mkdir build
cd build
meson setup ..
ninja
ninja test

One option is to set the environment variable (or use a corresponding
GStreamer C API, which would be better) from within the test.

> > Manually running the test, while manually setting the path gives:
> >
> > > > GST_PLUGIN_PATH=./build/src/gstreamer/ ./build/test/gstreamer/single_stream_test
> > > > [322:51:39.768296143] [2459570]  INFO IPAManager ipa_manager.cpp:138 libcamera is not installed. Adding '/home/kbingham/iob/libcamera/chromeos/src/third_party/libcamera/build/src/ipa' to the IPA search path
> > > > [322:51:39.769209245] [2459570]  INFO Camera camera_manager.cpp:294 libcamera v0.0.0+2918-15014c37
> > > > [322:51:39.772516638] [2459575]  INFO V4L2 v4l2_subdevice.cpp:242 'Sensor B': Using /dev/v4l-subdev1
> > > > [322:51:39.773277255] [2459575]  WARN CameraSensorProperties camera_sensor_properties.cpp:123 No static properties available for 'Sensor B'
> > > > [322:51:39.773290289] [2459575]  WARN CameraSensorProperties camera_sensor_properties.cpp:125 Please consider updating the camera sensor properties database
> > > > [322:51:39.773298484] [2459575]  WARN CameraSensor camera_sensor.cpp:403 'Sensor B': Failed to retrieve the camera location
> > > > [322:51:39.773317911] [2459575]  INFO V4L2 v4l2_subdevice.cpp:242 'Debayer B': Using /dev/v4l-subdev3
> > > > [322:51:39.773365811] [2459575]  INFO V4L2 v4l2_subdevice.cpp:242 'Scaler': Using /dev/v4l-subdev5
> > > > [322:51:39.773441643] [2459575]  WARN IPAManager ipa_manager.cpp:280 IPA module /home/kbingham/iob/libcamera/chromeos/src/third_party/libcamera/build/src/ipa/vimc/ipa_vimc.so signature regarded as valid
> > > > [322:51:39.773731977] [2459575]  INFO IPAProxy ipa_proxy.cpp:130 libcamera is not installed. Loading IPA configuration from '/home/kbingham/iob/libcamera/chromeos/src/third_party/libcamera/src/ipa/vimc/data'
> > > > [322:51:39.838754646] [2459576]  INFO Camera camera.cpp:873 configuring streams: (0) 156x120-BGR888
> > > >
> > > > (single_stream_test:2459570): GStreamer-CRITICAL **: 13:51:14.445: gst_mini_object_unref: assertion 'mini_object != NULL' failed

Additionally, it would be nice if the test returned an error gracefully
in this case, instead of crashing with a failed assertion. Error
handling is important.

> > > But even that only showed me a black box that appeared, and then seemed
> > > to disappear again.
> > >
> > > We might also want to run the automated tests on systems without a
> > > display, so pop windows might not work there.
> > >
> > > But I'd be tempted to say we can deal with that when we find an actual
> > > need for that, and keep the visual test, as ... (if It works, and shows
> > > an image) it would be a nice visual confirmation while running the tests.
> > >
> > > Otherwise, perhaps a pipeline that generates an output PNG or encoded
> > > video might be required, or even just a null sink if that's possible?
> >
> > A null sink would be better, as I already run tests on headless systems.
> > It may be as simple as s/autovideosink/fakevideosink/ in the code below,
> > so I think we should already address that.
> 
> I tried this, it works, should I sent a new patch with this ?

Yes, please do this in v6.

While at it, could you please run the test under valgrind, to ensure
there's no memory leak (at least no memory leak caused by the test,
leaks in third party libraries themselves, such as libudev, are not our
fault).

> > But that can be later anyway.
> >
> > > >> ---
> > > >>  .../gstreamer_single_stream_test.cpp          | 116 ++++++++++++++++++
> > > >>  test/gstreamer/meson.build                    |  19 +++
> > > >>  test/meson.build                              |   1 +
> > > >>  3 files changed, 136 insertions(+)
> > > >>  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp
> > > >>  create mode 100644 test/gstreamer/meson.build
> > > >>
> > > >> diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp
> > > >> new file mode 100644
> > > >> index 00000000..c19da3f2
> > > >> --- /dev/null
> > > >> +++ b/test/gstreamer/gstreamer_single_stream_test.cpp
> > > >> @@ -0,0 +1,116 @@

This file needs an SPDX header and a comment block like the other tests.

> > > >> +#include <gst/gst.h>
> > > >> +#include <iostream>
> > > >> +
> > > >> +#include "test.h"
> > > >> +
> > > >> +using namespace std;
> > > >> +
> > > >> +class GstreamerSingleStreamTest : public Test
> > > >> +{
> > > >> +protected:
> > > >> +  int init() override
> > > >> +  {
> > > >> +          /* Initialize GStreamer */
> > > >> +          GError *err_init = nullptr;
> > > >> +          if (!gst_init_check(nullptr, nullptr, &err_init)) {
> > > >> +                  g_printerr("Could not initialize GStreamer: %s\n",
> > > >> +                             err_init ? err_init->message : "unknown error");
> > > >> +                  if (err_init)
> > > >> +                          g_error_free(err_init);
> > > >> +
> > > >> +                  return TestFail;
> > > >> +          }
> > > >> +
> > > >> +          /* Create the elements */
> > > >> +          libcamera_src_ = gst_element_factory_make("libcamerasrc", "libcamera");
> > > >> +          convert0_ = gst_element_factory_make("videoconvert", "convert0");
> > > >> +          sink0_ = gst_element_factory_make("autovideosink", "sink0");
> > > >> +
> > > >> +          /* Create the empty pipeline_ */
> > > >> +          pipeline_ = gst_pipeline_new("test-pipeline");
> > > >> +
> > > >> +          if (!pipeline_ || !convert0_ || !sink0_ || !libcamera_src_) {
> > > >> +                  g_printerr("Not all elements could be created.\n");
> > > >> +                  return TestFail;
> > > >> +          }
> > > >> +
> > > >> +          return TestPass;
> > > >> +  }
> > > >> +
> > > >> +  void cleanup() override
> > > >> +  {
> > > >> +          gst_message_unref(msg_);
> > > >> +          gst_object_unref(bus_);
> > > >> +          gst_element_set_state(pipeline_, GST_STATE_NULL);
> > > >> +          gst_object_unref(pipeline_);
> > > >> +  }
> > > >> +
> > > >> +  int run() override
> > > >> +  {
> > > >> +          /* Build the pipeline */
> > > >> +          gst_bin_add_many(GST_BIN(pipeline_), libcamera_src_, convert0_, sink0_, NULL);
> > > >> +          if (gst_element_link_many(libcamera_src_, convert0_, sink0_, NULL) != TRUE) {
> > > >> +                  g_printerr("Elements could not be linked.\n");
> > > >> +                  gst_object_unref(pipeline_);
> > > >> +                  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");
> > > >> +                  gst_object_unref(pipeline_);
> > > >> +                  return TestFail;
> > > >> +          }
> > > >> +
> > > >> +          /* Wait until error or EOS or timeout after 2 seconds*/

Missing space before */

> > > >> +          GstClockTime timeout = 2000000000;
> > > >> +          bus_ = gst_element_get_bus(pipeline_);
> > > >> +          msg_ = gst_bus_timed_pop_filtered(bus_, timeout,
> > > >> +
> > GstMessageType((uint)GST_MESSAGE_ERROR | (uint)GST_MESSAGE_EOS));
> > > >> +
> > > >> +          /* Parse error message */
> > > >> +          if (msg_ == NULL)
> > > >> +                  return TestPass;
> > > >> +
> > > >> +          switch (GST_MESSAGE_TYPE(msg_)) {
> > > >> +          case GST_MESSAGE_ERROR:
> > > >> +                  GstreamerPrintError(msg_);
> > > >> +                  return TestFail;
> > > >> +                  break;
> > > >> +          case GST_MESSAGE_EOS:
> > > >> +                  g_print("End-Of-Stream reached.\n");
> > > >> +                  return TestFail;
> > > >> +                  break;
> > > >> +          default:
> > > >> +                  g_printerr("Unexpected message received.\n");
> > > >> +                  return TestFail;
> > > >> +                  break;
> > > >> +          }
> > > >> +
> > > >> +          return TestPass;
> > > >> +  }
> > > >> +
> > > >> +private:
> > > >> +  GstElement *pipeline_, *libcamera_src_, *convert0_, *sink0_;
> > > >> +  GstBus *bus_;
> > > >> +  GstMessage *msg_;
> > > >> +  GstStateChangeReturn ret_;
> > > >> +
> > > >> +  void GstreamerPrintError(GstMessage *msg)

Functions start with a lowercase letter in the libcamera coding style.

> > > >> +  {
> > > >> +          GError *err;
> > > >> +          gchar *debug_info;
> > > >> +
> > > >> +          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);
> > > >> +  }
> > > >> +};
> > > >> +
> > > >> +TEST_REGISTER(GstreamerSingleStreamTest)
> > > >> +

Extra blank line.

> > > >> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
> > > >> new file mode 100644
> > > >> index 00000000..b99aa0da
> > > >> --- /dev/null
> > > >> +++ b/test/gstreamer/meson.build
> > > >> @@ -0,0 +1,19 @@
> > > >> +# SPDX-License-Identifier: CC0-1.0
> > > >> +
> > > >> +if not gst_enabled
> > > >> +    subdir_done()
> > > >> +endif
> > > >> +
> > > >> +gstreamer_tests = [
> > > >> +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],
> > > >> +]
> > > >> +gstreamer_dep = dependency('gstreamer-1.0', required: true)
> > > >> +
> > > >> +foreach t : gstreamer_tests
> > > >> +    exe = executable(t[0], t[1],
> > > >> +                     dependencies : [libcamera_private, gstreamer_dep],
> > > >> +                     link_with : test_libraries,
> > > >> +                     include_directories : test_includes_internal)
> > > >> +
> > > >> +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)
> > > >> +endforeach
> > > >> diff --git a/test/meson.build b/test/meson.build
> > > >> index 3bceb5df..d0466f17 100644
> > > >> --- a/test/meson.build
> > > >> +++ b/test/meson.build
> > > >> @@ -11,6 +11,7 @@ subdir('libtest')
> > > >>
> > > >>  subdir('camera')
> > > >>  subdir('controls')
> > > >> +subdir('gstreamer')
> > > >>  subdir('ipa')
> > > >>  subdir('ipc')
> > > >>  subdir('log')

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list