[{"id":18792,"web_url":"https://patchwork.libcamera.org/comment/18792/","msgid":"<20210814051353.GA1513067@pyrite.rasen.tech>","date":"2021-08-14T05:13:53","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"Hello,\n\nOn Sat, Aug 14, 2021 at 02:46:52AM +0300, Laurent Pinchart wrote:\n> From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> \n> This patch adds a test to test if single stream using libcamera's\n> gstreamer element works.\n> \n> We need to work around two distinct issues with ASan when enabled in the\n> build:\n> \n> - glib has a known leak at initialization time. This is covered by the\n>   suppression file shipped with glib, but it's not clear how to use it\n>   automatically. For now, disable leak detection to avoid test failures.\n> \n> - GStreamer spawns a child process to scan plugins. If GStreamer is\n>   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n>   the libcamera plugin will cause an ASan link order verification\n>   failure. Disable the verification child processes to work around the\n>   problem. This requires gcc 8 or newer.\n> \n> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> This version incorporates changes coming from my review of v10, and\n> fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> independent reasons as explained in the commit message. I'd like to find\n> a way to use leak suppression files to handle the glib initialization\n> leak, but that's a big rabbit hole. The workaround for the second issue\n> is acceptable in my opinion.\n> \n> The biggest trouble with these workarounds is that they don't work with\n> gcc version older than 8. As the stable version of the most common Linux\n> distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> is the oldest version that libcamera supports) is also acceptable in my\n> opinion.\n> \n> Changes since v10:\n> \n> - Disable ASan leak detection\n> - Disable ASan link order verification for child processes\n> - Include source_path.h\n> - Remove unneeded explicit std::string construction\n> - Declare variables at usage site\n> - Make constants constexpr\n> - Add a variable for the message type\n> - Blank space fixes\n> ---\n>  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n>  test/gstreamer/meson.build                    |  19 ++\n>  test/meson.build                              |   1 +\n>  3 files changed, 208 insertions(+)\n>  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n>  create mode 100644 test/gstreamer/meson.build\n> \n> diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> new file mode 100644\n> index 000000000000..e26673b3471a\n> --- /dev/null\n> +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> @@ -0,0 +1,188 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2021, Vedant Paranjape\n> + *\n> + * ipa_interface_test.cpp - Test the IPA interface\n\nMaybe we should change the description.\n\n\nPaul\n\n> + */\n> +\n> +#include <iostream>\n> +#include <unistd.h>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +#include \"libcamera/internal/source_paths.h\"\n> +\n> +#include <gst/gst.h>\n> +\n> +#include \"test.h\"\n> +\n> +using namespace std;\n> +\n> +extern \"C\" {\n> +const char *__asan_default_options()\n> +{\n> +\t/*\n> +\t * Disable leak detection due to a known global variable initialization\n> +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> +\t * using a suppression file instead of disabling leak detection.\n> +\t */\n> +\treturn \"detect_leaks=false\";\n> +}\n> +}\n> +\n> +class GstreamerSingleStreamTest : public Test\n> +{\n> +protected:\n> +\tint init() override\n> +\t{\n> +\t\t/*\n> +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> +\t\t * GStreamer is most likely not, this will cause the ASan link\n> +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> +\t\t * plugin as many libraries will have already been loaded by\n> +\t\t * then. Work around this issue by disabling the link order\n> +\t\t * check. This will only affect child processes, as ASan is\n> +\t\t * already loaded for this process by the time this code is\n> +\t\t * executed, and should thus hopefully be safe.\n> +\t\t *\n> +\t\t * This option is not available in gcc older than 8, the only\n> +\t\t * option in that case is to skip the test.\n> +\t\t */\n> +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> +\t\treturn TestSkip;\n> +#endif\n> +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> +\n> +\t\t/* Initialize GStreamer */\n> +\t\tGError *errInit = nullptr;\n> +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> +\t\t\tif (errInit)\n> +\t\t\t\tg_error_free(errInit);\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Remove the system libcamera plugin, if any, and add the\n> +\t\t * plugin from the build directory.\n> +\t\t */\n> +\t\tGstRegistry *registry = gst_registry_get();\n> +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> +\t\tif (plugin) {\n> +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> +\t\t\tgst_object_unref(plugin);\n> +\t\t}\n> +\n> +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> +\t\t\t\t + \"src/gstreamer\";\n> +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> +\t\t\tgst_deinit();\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Create the elements */\n> +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> +\n> +\t\t/* Create the empty pipeline_ */\n> +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> +\n> +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> +\t\t\tif (pipeline_)\n> +\t\t\t\tgst_object_unref(pipeline_);\n> +\t\t\tif (convert0_)\n> +\t\t\t\tgst_object_unref(convert0_);\n> +\t\t\tif (sink0_)\n> +\t\t\t\tgst_object_unref(sink0_);\n> +\t\t\tif (libcameraSrc_)\n> +\t\t\t\tgst_object_unref(libcameraSrc_);\n> +\t\t\tgst_deinit();\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +\n> +\tvoid cleanup() override\n> +\t{\n> +\t\tgst_object_unref(pipeline_);\n> +\t\tgst_deinit();\n> +\t}\n> +\n> +\tint run() override\n> +\t{\n> +\t\tGstStateChangeReturn ret;\n> +\n> +\t\t/* Build the pipeline */\n> +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Start playing */\n> +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> +\t\tconstexpr GstMessageType msgType =\n> +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> +\t\tconstexpr GstClockTime timeout = 2000000000;\n> +\n> +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> +\n> +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> +\n> +\t\t/* Parse error message */\n> +\t\tif (msg == NULL)\n> +\t\t\treturn TestPass;\n> +\n> +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> +\t\tcase GST_MESSAGE_ERROR:\n> +\t\t\tgstreamer_print_error(msg);\n> +\t\t\tbreak;\n> +\t\tcase GST_MESSAGE_EOS:\n> +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> +\t\t\tbreak;\n> +\t\t}\n> +\n> +\t\treturn TestFail;\n> +\t}\n> +\n> +private:\n> +\tvoid gstreamer_print_error(GstMessage *msg)\n> +\t{\n> +\t\tGError *err;\n> +\t\tgchar *debug_info;\n> +\n> +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> +\t\tg_printerr(\"Debugging information: %s\\n\",\n> +\t\t\t   debug_info ? debug_info : \"none\");\n> +\t\tg_clear_error(&err);\n> +\t\tg_free(debug_info);\n> +\t}\n> +\n> +\tGstElement *pipeline_;\n> +\tGstElement *libcameraSrc_;\n> +\tGstElement *convert0_;\n> +\tGstElement *sink0_;\n> +};\n> +\n> +TEST_REGISTER(GstreamerSingleStreamTest)\n> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> new file mode 100644\n> index 000000000000..b99aa0da0ba3\n> --- /dev/null\n> +++ b/test/gstreamer/meson.build\n> @@ -0,0 +1,19 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +if not gst_enabled\n> +    subdir_done()\n> +endif\n> +\n> +gstreamer_tests = [\n> +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> +]\n> +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> +\n> +foreach t : gstreamer_tests\n> +    exe = executable(t[0], t[1],\n> +                     dependencies : [libcamera_private, gstreamer_dep],\n> +                     link_with : test_libraries,\n> +                     include_directories : test_includes_internal)\n> +\n> +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> +endforeach\n> diff --git a/test/meson.build b/test/meson.build\n> index 3bceb5df586f..d0466f17d7b6 100644\n> --- a/test/meson.build\n> +++ b/test/meson.build\n> @@ -11,6 +11,7 @@ subdir('libtest')\n>  \n>  subdir('camera')\n>  subdir('controls')\n> +subdir('gstreamer')\n>  subdir('ipa')\n>  subdir('ipc')\n>  subdir('log')\n> -- \n> Regards,\n> \n> Laurent Pinchart\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 1EF69C3240\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 14 Aug 2021 05:14:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 89A266888D;\n\tSat, 14 Aug 2021 07:14:04 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A50E6025E\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Aug 2021 07:14:02 +0200 (CEST)","from pyrite.rasen.tech (unknown\n\t[IPv6:2400:4051:61:600:2c71:1b79:d06d:5032])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B14623E5;\n\tSat, 14 Aug 2021 07:14:00 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"dndYZR3E\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1628918042;\n\tbh=nYuwG7yjKy9CuzdaHIZctT3hcYIPnXDcr+eFy76Y6uk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=dndYZR3Et+tS+skbsR4rpR5fXEirhtiMZ/0Rf92cqKjs1rwiJE5sy7b4Pg198L0Cu\n\t7Nwx5qaxVCbf3iqdi3Scyc8c/kcqPaHn+CXgbkPspnbrRugIZsLvrRnRG5Cb23de26\n\t6a7o7mc51AGQeQYwqRT/IiEMrbKNPfW1nxIKV2GQ=","Date":"Sat, 14 Aug 2021 14:13:53 +0900","From":"paul.elder@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20210814051353.GA1513067@pyrite.rasen.tech>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org,\n\tVedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18801,"web_url":"https://patchwork.libcamera.org/comment/18801/","msgid":"<YRgfgJLUfxFrp66x@pendragon.ideasonboard.com>","date":"2021-08-14T19:54:40","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nOn Sat, Aug 14, 2021 at 02:13:53PM +0900, paul.elder@ideasonboard.com wrote:\n> On Sat, Aug 14, 2021 at 02:46:52AM +0300, Laurent Pinchart wrote:\n> > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > \n> > This patch adds a test to test if single stream using libcamera's\n> > gstreamer element works.\n> > \n> > We need to work around two distinct issues with ASan when enabled in the\n> > build:\n> > \n> > - glib has a known leak at initialization time. This is covered by the\n> >   suppression file shipped with glib, but it's not clear how to use it\n> >   automatically. For now, disable leak detection to avoid test failures.\n> > \n> > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> >   the libcamera plugin will cause an ASan link order verification\n> >   failure. Disable the verification child processes to work around the\n> >   problem. This requires gcc 8 or newer.\n> > \n> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > This version incorporates changes coming from my review of v10, and\n> > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > independent reasons as explained in the commit message. I'd like to find\n> > a way to use leak suppression files to handle the glib initialization\n> > leak, but that's a big rabbit hole. The workaround for the second issue\n> > is acceptable in my opinion.\n> > \n> > The biggest trouble with these workarounds is that they don't work with\n> > gcc version older than 8. As the stable version of the most common Linux\n> > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > is the oldest version that libcamera supports) is also acceptable in my\n> > opinion.\n> > \n> > Changes since v10:\n> > \n> > - Disable ASan leak detection\n> > - Disable ASan link order verification for child processes\n> > - Include source_path.h\n> > - Remove unneeded explicit std::string construction\n> > - Declare variables at usage site\n> > - Make constants constexpr\n> > - Add a variable for the message type\n> > - Blank space fixes\n> > ---\n> >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> >  test/gstreamer/meson.build                    |  19 ++\n> >  test/meson.build                              |   1 +\n> >  3 files changed, 208 insertions(+)\n> >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> >  create mode 100644 test/gstreamer/meson.build\n> > \n> > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > new file mode 100644\n> > index 000000000000..e26673b3471a\n> > --- /dev/null\n> > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > @@ -0,0 +1,188 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2021, Vedant Paranjape\n> > + *\n> > + * ipa_interface_test.cpp - Test the IPA interface\n> \n> Maybe we should change the description.\n\nAs good point. Funny how this can stay unnoticed for 11 versions.\n\nI'll update it to\n\n * gstreamer_single_stream_test.cpp - GStreamer single stream capture test\n\n> > + */\n> > +\n> > +#include <iostream>\n> > +#include <unistd.h>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +#include \"libcamera/internal/source_paths.h\"\n> > +\n> > +#include <gst/gst.h>\n> > +\n> > +#include \"test.h\"\n> > +\n> > +using namespace std;\n> > +\n> > +extern \"C\" {\n> > +const char *__asan_default_options()\n> > +{\n> > +\t/*\n> > +\t * Disable leak detection due to a known global variable initialization\n> > +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> > +\t * using a suppression file instead of disabling leak detection.\n> > +\t */\n> > +\treturn \"detect_leaks=false\";\n> > +}\n> > +}\n> > +\n> > +class GstreamerSingleStreamTest : public Test\n> > +{\n> > +protected:\n> > +\tint init() override\n> > +\t{\n> > +\t\t/*\n> > +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> > +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> > +\t\t * GStreamer is most likely not, this will cause the ASan link\n> > +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> > +\t\t * plugin as many libraries will have already been loaded by\n> > +\t\t * then. Work around this issue by disabling the link order\n> > +\t\t * check. This will only affect child processes, as ASan is\n> > +\t\t * already loaded for this process by the time this code is\n> > +\t\t * executed, and should thus hopefully be safe.\n> > +\t\t *\n> > +\t\t * This option is not available in gcc older than 8, the only\n> > +\t\t * option in that case is to skip the test.\n> > +\t\t */\n> > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> > +\t\treturn TestSkip;\n> > +#endif\n> > +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > +\n> > +\t\t/* Initialize GStreamer */\n> > +\t\tGError *errInit = nullptr;\n> > +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> > +\t\t\tif (errInit)\n> > +\t\t\t\tg_error_free(errInit);\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/*\n> > +\t\t * Remove the system libcamera plugin, if any, and add the\n> > +\t\t * plugin from the build directory.\n> > +\t\t */\n> > +\t\tGstRegistry *registry = gst_registry_get();\n> > +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > +\t\tif (plugin) {\n> > +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> > +\t\t\tgst_object_unref(plugin);\n> > +\t\t}\n> > +\n> > +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> > +\t\t\t\t + \"src/gstreamer\";\n> > +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> > +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> > +\t\t\tgst_deinit();\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Create the elements */\n> > +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > +\n> > +\t\t/* Create the empty pipeline_ */\n> > +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > +\n> > +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> > +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> > +\t\t\tif (pipeline_)\n> > +\t\t\t\tgst_object_unref(pipeline_);\n> > +\t\t\tif (convert0_)\n> > +\t\t\t\tgst_object_unref(convert0_);\n> > +\t\t\tif (sink0_)\n> > +\t\t\t\tgst_object_unref(sink0_);\n> > +\t\t\tif (libcameraSrc_)\n> > +\t\t\t\tgst_object_unref(libcameraSrc_);\n> > +\t\t\tgst_deinit();\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\treturn TestPass;\n> > +\t}\n> > +\n> > +\tvoid cleanup() override\n> > +\t{\n> > +\t\tgst_object_unref(pipeline_);\n> > +\t\tgst_deinit();\n> > +\t}\n> > +\n> > +\tint run() override\n> > +\t{\n> > +\t\tGstStateChangeReturn ret;\n> > +\n> > +\t\t/* Build the pipeline */\n> > +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> > +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Start playing */\n> > +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> > +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> > +\t\tconstexpr GstMessageType msgType =\n> > +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > +\t\tconstexpr GstClockTime timeout = 2000000000;\n> > +\n> > +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > +\n> > +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> > +\n> > +\t\t/* Parse error message */\n> > +\t\tif (msg == NULL)\n> > +\t\t\treturn TestPass;\n> > +\n> > +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> > +\t\tcase GST_MESSAGE_ERROR:\n> > +\t\t\tgstreamer_print_error(msg);\n> > +\t\t\tbreak;\n> > +\t\tcase GST_MESSAGE_EOS:\n> > +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\n> > +\t\t\tbreak;\n> > +\t\tdefault:\n> > +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> > +\t\t\tbreak;\n> > +\t\t}\n> > +\n> > +\t\treturn TestFail;\n> > +\t}\n> > +\n> > +private:\n> > +\tvoid gstreamer_print_error(GstMessage *msg)\n> > +\t{\n> > +\t\tGError *err;\n> > +\t\tgchar *debug_info;\n> > +\n> > +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> > +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> > +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> > +\t\tg_printerr(\"Debugging information: %s\\n\",\n> > +\t\t\t   debug_info ? debug_info : \"none\");\n> > +\t\tg_clear_error(&err);\n> > +\t\tg_free(debug_info);\n> > +\t}\n> > +\n> > +\tGstElement *pipeline_;\n> > +\tGstElement *libcameraSrc_;\n> > +\tGstElement *convert0_;\n> > +\tGstElement *sink0_;\n> > +};\n> > +\n> > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > new file mode 100644\n> > index 000000000000..b99aa0da0ba3\n> > --- /dev/null\n> > +++ b/test/gstreamer/meson.build\n> > @@ -0,0 +1,19 @@\n> > +# SPDX-License-Identifier: CC0-1.0\n> > +\n> > +if not gst_enabled\n> > +    subdir_done()\n> > +endif\n> > +\n> > +gstreamer_tests = [\n> > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > +]\n> > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > +\n> > +foreach t : gstreamer_tests\n> > +    exe = executable(t[0], t[1],\n> > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > +                     link_with : test_libraries,\n> > +                     include_directories : test_includes_internal)\n> > +\n> > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > +endforeach\n> > diff --git a/test/meson.build b/test/meson.build\n> > index 3bceb5df586f..d0466f17d7b6 100644\n> > --- a/test/meson.build\n> > +++ b/test/meson.build\n> > @@ -11,6 +11,7 @@ subdir('libtest')\n> >  \n> >  subdir('camera')\n> >  subdir('controls')\n> > +subdir('gstreamer')\n> >  subdir('ipa')\n> >  subdir('ipc')\n> >  subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id F1437BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 14 Aug 2021 19:54:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 6E59360263;\n\tSat, 14 Aug 2021 21:54:47 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BD7D160262\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Aug 2021 21:54:45 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3E2953F0;\n\tSat, 14 Aug 2021 21:54:45 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"JCGKD/mR\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1628970885;\n\tbh=GAnYfDR+2SQ+v7NhgbP7/IICC66PgapejRXJJ2Oi6Qk=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=JCGKD/mRu8yn7jItSgGS1O0lw/Nw7WJb+rmwOt1JADq0ccNW8mKRxpimPWZ+v1+Q6\n\tP/14CvR8tVxPvQc85DmrbZMie3bdQxJHYwHKExr1GdY69Xz+TGrtta3ah6UDYGP6xl\n\tejJUWo2n02R0vDRnbX15BTmygQgTV74MHDdBe5UE=","Date":"Sat, 14 Aug 2021 22:54:40 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"paul.elder@ideasonboard.com","Message-ID":"<YRgfgJLUfxFrp66x@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<20210814051353.GA1513067@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20210814051353.GA1513067@pyrite.rasen.tech>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org,\n\tVedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18815,"web_url":"https://patchwork.libcamera.org/comment/18815/","msgid":"<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>","date":"2021-08-16T05:47:27","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":86,"url":"https://patchwork.libcamera.org/api/people/86/","name":"Umang Jain","email":"umang.jain@ideasonboard.com"},"content":"Hi Vedant and Laurent,\n\nThanks for the patch. Overall looks good to me,\n\na few comments below for cleanup paths :)\n\nOn 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n>\n> This patch adds a test to test if single stream using libcamera's\n> gstreamer element works.\n>\n> We need to work around two distinct issues with ASan when enabled in the\n> build:\n>\n> - glib has a known leak at initialization time. This is covered by the\n>    suppression file shipped with glib, but it's not clear how to use it\n>    automatically. For now, disable leak detection to avoid test failures.\n>\n> - GStreamer spawns a child process to scan plugins. If GStreamer is\n>    compiled without ASan (which is likely) but libcamera is, dlopen()ing\n>    the libcamera plugin will cause an ASan link order verification\n>    failure. Disable the verification child processes to work around the\n>    problem. This requires gcc 8 or newer.\n>\n> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> This version incorporates changes coming from my review of v10, and\n> fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> independent reasons as explained in the commit message. I'd like to find\n> a way to use leak suppression files to handle the glib initialization\n> leak, but that's a big rabbit hole. The workaround for the second issue\n> is acceptable in my opinion.\n>\n> The biggest trouble with these workarounds is that they don't work with\n> gcc version older than 8. As the stable version of the most common Linux\n> distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> is the oldest version that libcamera supports) is also acceptable in my\n> opinion.\n>\n> Changes since v10:\n>\n> - Disable ASan leak detection\n> - Disable ASan link order verification for child processes\n> - Include source_path.h\n> - Remove unneeded explicit std::string construction\n> - Declare variables at usage site\n> - Make constants constexpr\n> - Add a variable for the message type\n> - Blank space fixes\n> ---\n>   .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n>   test/gstreamer/meson.build                    |  19 ++\n>   test/meson.build                              |   1 +\n>   3 files changed, 208 insertions(+)\n>   create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n>   create mode 100644 test/gstreamer/meson.build\n>\n> diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> new file mode 100644\n> index 000000000000..e26673b3471a\n> --- /dev/null\n> +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> @@ -0,0 +1,188 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2021, Vedant Paranjape\n> + *\n> + * ipa_interface_test.cpp - Test the IPA interface\n> + */\n> +\n> +#include <iostream>\n> +#include <unistd.h>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +#include \"libcamera/internal/source_paths.h\"\n> +\n> +#include <gst/gst.h>\n> +\n> +#include \"test.h\"\n> +\n> +using namespace std;\n> +\n> +extern \"C\" {\n> +const char *__asan_default_options()\n> +{\n> +\t/*\n> +\t * Disable leak detection due to a known global variable initialization\n> +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> +\t * using a suppression file instead of disabling leak detection.\n> +\t */\n> +\treturn \"detect_leaks=false\";\n> +}\n> +}\n> +\n> +class GstreamerSingleStreamTest : public Test\n> +{\n> +protected:\n> +\tint init() override\n> +\t{\n> +\t\t/*\n> +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> +\t\t * GStreamer is most likely not, this will cause the ASan link\n> +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> +\t\t * plugin as many libraries will have already been loaded by\n> +\t\t * then. Work around this issue by disabling the link order\n> +\t\t * check. This will only affect child processes, as ASan is\n> +\t\t * already loaded for this process by the time this code is\n> +\t\t * executed, and should thus hopefully be safe.\n> +\t\t *\n> +\t\t * This option is not available in gcc older than 8, the only\n> +\t\t * option in that case is to skip the test.\n> +\t\t */\n> +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> +\t\treturn TestSkip;\n> +#endif\n> +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> +\n> +\t\t/* Initialize GStreamer */\n> +\t\tGError *errInit = nullptr;\nOne can use:\n\n     g_autoptr(GError) errInit = NULL;\n\n...\n\n> +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> +\t\t\tif (errInit)\n> +\t\t\t\tg_error_free(errInit);\nAnd remove manual free here.\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Remove the system libcamera plugin, if any, and add the\n> +\t\t * plugin from the build directory.\n> +\t\t */\n> +\t\tGstRegistry *registry = gst_registry_get();\n> +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> +\t\tif (plugin) {\n> +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> +\t\t\tgst_object_unref(plugin);\n> +\t\t}\n> +\n> +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> +\t\t\t\t + \"src/gstreamer\";\n> +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> +\t\t\tgst_deinit();\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Create the elements */\n> +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> +\n> +\t\t/* Create the empty pipeline_ */\n> +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> +\n> +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> +\t\t\tif (pipeline_)\n> +\t\t\t\tgst_object_unref(pipeline_);\n> +\t\t\tif (convert0_)\n> +\t\t\t\tgst_object_unref(convert0_);\n> +\t\t\tif (sink0_)\n> +\t\t\t\tgst_object_unref(sink0_);\n> +\t\t\tif (libcameraSrc_)\n> +\t\t\t\tgst_object_unref(libcameraSrc_);\n> +\t\t\tgst_deinit();\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +\n> +\tvoid cleanup() override\n> +\t{\n> +\t\tgst_object_unref(pipeline_);\n> +\t\tgst_deinit();\n> +\t}\n> +\n> +\tint run() override\n> +\t{\n> +\t\tGstStateChangeReturn ret;\n> +\n> +\t\t/* Build the pipeline */\n> +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Start playing */\n> +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> +\t\tconstexpr GstMessageType msgType =\n> +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> +\t\tconstexpr GstClockTime timeout = 2000000000;\n> +\n> +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> +\n> +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> +\n> +\t\t/* Parse error message */\n> +\t\tif (msg == NULL)\n> +\t\t\treturn TestPass;\n> +\n> +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> +\t\tcase GST_MESSAGE_ERROR:\n> +\t\t\tgstreamer_print_error(msg);\n> +\t\t\tbreak;\n> +\t\tcase GST_MESSAGE_EOS:\n> +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\nEOS doesn't sound like a error/Test-fail condition to me. If there is a \npossibility that EOS will be reached before timeout, what's ensures(in \nthe first place) that the pipeline will be in playing state for entire \ntimeout, before we request masked EOS/Error messages from the bus? What \nam I missing?\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> +\t\t\tbreak;\n> +\t\t}\n> +\n> +\t\treturn TestFail;\n> +\t}\n> +\n> +private:\n> +\tvoid gstreamer_print_error(GstMessage *msg)\n> +\t{\n> +\t\tGError *err;\n\nThis can use:\n\n     g_autoptr(GError) err = NULL;\n\n> +\t\tgchar *debug_info;\n\nand\n\ng_autofree char *debug_info = NULL;\n\n> +\n> +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> +\t\tg_printerr(\"Debugging information: %s\\n\",\n> +\t\t\t   debug_info ? debug_info : \"none\");\n> +\t\tg_clear_error(&err);\n> +\t\tg_free(debug_info);\n\nSo, that will lead us to dropping manual free of err and debug info\n\n\nMinor things, so:\n\nReviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n\n> +\t}\n> +\n> +\tGstElement *pipeline_;\n> +\tGstElement *libcameraSrc_;\n> +\tGstElement *convert0_;\n> +\tGstElement *sink0_;\n> +};\n> +\n> +TEST_REGISTER(GstreamerSingleStreamTest)\n> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> new file mode 100644\n> index 000000000000..b99aa0da0ba3\n> --- /dev/null\n> +++ b/test/gstreamer/meson.build\n> @@ -0,0 +1,19 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +if not gst_enabled\n> +    subdir_done()\n> +endif\n> +\n> +gstreamer_tests = [\n> +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> +]\n> +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> +\n> +foreach t : gstreamer_tests\n> +    exe = executable(t[0], t[1],\n> +                     dependencies : [libcamera_private, gstreamer_dep],\n> +                     link_with : test_libraries,\n> +                     include_directories : test_includes_internal)\n> +\n> +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> +endforeach\n> diff --git a/test/meson.build b/test/meson.build\n> index 3bceb5df586f..d0466f17d7b6 100644\n> --- a/test/meson.build\n> +++ b/test/meson.build\n> @@ -11,6 +11,7 @@ subdir('libtest')\n>   \n>   subdir('camera')\n>   subdir('controls')\n> +subdir('gstreamer')\n>   subdir('ipa')\n>   subdir('ipc')\n>   subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 2A17ABD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 05:47:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5927A68890;\n\tMon, 16 Aug 2021 07:47:33 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B298C6025C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 07:47:32 +0200 (CEST)","from [192.168.1.104] (unknown [103.238.109.15])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8095A3E5;\n\tMon, 16 Aug 2021 07:47:31 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"jrtEeDod\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629092852;\n\tbh=qXRre2T6CzvXL5cFUo9A3tuczkI8YkgSgQFbp2O780A=;\n\th=Subject:To:Cc:References:From:Date:In-Reply-To:From;\n\tb=jrtEeDoduIqsFSV5pd/OlwrtKBYQNB31Mt/p30OHUUWzeGXRKemMylP6Pecnc7tyc\n\tVLyf5MBd2vARI6Nq2lDRUG4GWg3w/HBuRzRH0xtmVFMtgbpJ372OTNWbbGCFhN3bBg\n\tgAkjp/a06kV4IAOjOcGsxPr/Nc+a8+F4xGseKCio=","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","From":"Umang Jain <umang.jain@ideasonboard.com>","Message-ID":"<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>","Date":"Mon, 16 Aug 2021 11:17:27 +0530","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.10.2","MIME-Version":"1.0","In-Reply-To":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","Content-Type":"multipart/alternative;\n\tboundary=\"------------C329B805D9AE56D4762B5959\"","Content-Language":"en-US","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18822,"web_url":"https://patchwork.libcamera.org/comment/18822/","msgid":"<YRow9fW+6OXNFcyS@pendragon.ideasonboard.com>","date":"2021-08-16T09:33:41","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Umang,\n\nOn Mon, Aug 16, 2021 at 11:17:27AM +0530, Umang Jain wrote:\n> Hi Vedant and Laurent,\n> \n> Thanks for the patch. Overall looks good to me,\n> \n> a few comments below for cleanup paths :)\n\nThe patch has already been merged I'm afraid.\n\nVedant, could you read these comments, and either submit follow-up\npatches, or reply if you disagree ?\n\n> On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> >\n> > This patch adds a test to test if single stream using libcamera's\n> > gstreamer element works.\n> >\n> > We need to work around two distinct issues with ASan when enabled in the\n> > build:\n> >\n> > - glib has a known leak at initialization time. This is covered by the\n> >    suppression file shipped with glib, but it's not clear how to use it\n> >    automatically. For now, disable leak detection to avoid test failures.\n> >\n> > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> >    compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> >    the libcamera plugin will cause an ASan link order verification\n> >    failure. Disable the verification child processes to work around the\n> >    problem. This requires gcc 8 or newer.\n> >\n> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > This version incorporates changes coming from my review of v10, and\n> > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > independent reasons as explained in the commit message. I'd like to find\n> > a way to use leak suppression files to handle the glib initialization\n> > leak, but that's a big rabbit hole. The workaround for the second issue\n> > is acceptable in my opinion.\n> >\n> > The biggest trouble with these workarounds is that they don't work with\n> > gcc version older than 8. As the stable version of the most common Linux\n> > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > is the oldest version that libcamera supports) is also acceptable in my\n> > opinion.\n> >\n> > Changes since v10:\n> >\n> > - Disable ASan leak detection\n> > - Disable ASan link order verification for child processes\n> > - Include source_path.h\n> > - Remove unneeded explicit std::string construction\n> > - Declare variables at usage site\n> > - Make constants constexpr\n> > - Add a variable for the message type\n> > - Blank space fixes\n> > ---\n> >   .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> >   test/gstreamer/meson.build                    |  19 ++\n> >   test/meson.build                              |   1 +\n> >   3 files changed, 208 insertions(+)\n> >   create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> >   create mode 100644 test/gstreamer/meson.build\n> >\n> > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > new file mode 100644\n> > index 000000000000..e26673b3471a\n> > --- /dev/null\n> > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > @@ -0,0 +1,188 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2021, Vedant Paranjape\n> > + *\n> > + * ipa_interface_test.cpp - Test the IPA interface\n> > + */\n> > +\n> > +#include <iostream>\n> > +#include <unistd.h>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +#include \"libcamera/internal/source_paths.h\"\n> > +\n> > +#include <gst/gst.h>\n> > +\n> > +#include \"test.h\"\n> > +\n> > +using namespace std;\n> > +\n> > +extern \"C\" {\n> > +const char *__asan_default_options()\n> > +{\n> > +\t/*\n> > +\t * Disable leak detection due to a known global variable initialization\n> > +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> > +\t * using a suppression file instead of disabling leak detection.\n> > +\t */\n> > +\treturn \"detect_leaks=false\";\n> > +}\n> > +}\n> > +\n> > +class GstreamerSingleStreamTest : public Test\n> > +{\n> > +protected:\n> > +\tint init() override\n> > +\t{\n> > +\t\t/*\n> > +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> > +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> > +\t\t * GStreamer is most likely not, this will cause the ASan link\n> > +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> > +\t\t * plugin as many libraries will have already been loaded by\n> > +\t\t * then. Work around this issue by disabling the link order\n> > +\t\t * check. This will only affect child processes, as ASan is\n> > +\t\t * already loaded for this process by the time this code is\n> > +\t\t * executed, and should thus hopefully be safe.\n> > +\t\t *\n> > +\t\t * This option is not available in gcc older than 8, the only\n> > +\t\t * option in that case is to skip the test.\n> > +\t\t */\n> > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> > +\t\treturn TestSkip;\n> > +#endif\n> > +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > +\n> > +\t\t/* Initialize GStreamer */\n> > +\t\tGError *errInit = nullptr;\n>\n> One can use:\n> \n>      g_autoptr(GError) errInit = NULL;\n> \n> ...\n> \n> > +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> > +\t\t\tif (errInit)\n> > +\t\t\t\tg_error_free(errInit);\n>\n> And remove manual free here.\n>\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/*\n> > +\t\t * Remove the system libcamera plugin, if any, and add the\n> > +\t\t * plugin from the build directory.\n> > +\t\t */\n> > +\t\tGstRegistry *registry = gst_registry_get();\n> > +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > +\t\tif (plugin) {\n> > +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> > +\t\t\tgst_object_unref(plugin);\n> > +\t\t}\n> > +\n> > +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> > +\t\t\t\t + \"src/gstreamer\";\n> > +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> > +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> > +\t\t\tgst_deinit();\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Create the elements */\n> > +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > +\n> > +\t\t/* Create the empty pipeline_ */\n> > +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > +\n> > +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> > +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> > +\t\t\tif (pipeline_)\n> > +\t\t\t\tgst_object_unref(pipeline_);\n> > +\t\t\tif (convert0_)\n> > +\t\t\t\tgst_object_unref(convert0_);\n> > +\t\t\tif (sink0_)\n> > +\t\t\t\tgst_object_unref(sink0_);\n> > +\t\t\tif (libcameraSrc_)\n> > +\t\t\t\tgst_object_unref(libcameraSrc_);\n> > +\t\t\tgst_deinit();\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\treturn TestPass;\n> > +\t}\n> > +\n> > +\tvoid cleanup() override\n> > +\t{\n> > +\t\tgst_object_unref(pipeline_);\n> > +\t\tgst_deinit();\n> > +\t}\n> > +\n> > +\tint run() override\n> > +\t{\n> > +\t\tGstStateChangeReturn ret;\n> > +\n> > +\t\t/* Build the pipeline */\n> > +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> > +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Start playing */\n> > +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> > +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> > +\t\tconstexpr GstMessageType msgType =\n> > +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > +\t\tconstexpr GstClockTime timeout = 2000000000;\n> > +\n> > +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > +\n> > +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> > +\n> > +\t\t/* Parse error message */\n> > +\t\tif (msg == NULL)\n> > +\t\t\treturn TestPass;\n> > +\n> > +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> > +\t\tcase GST_MESSAGE_ERROR:\n> > +\t\t\tgstreamer_print_error(msg);\n> > +\t\t\tbreak;\n> > +\t\tcase GST_MESSAGE_EOS:\n> > +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\n>\n> EOS doesn't sound like a error/Test-fail condition to me. If there is a \n> possibility that EOS will be reached before timeout, what's ensures(in \n> the first place) that the pipeline will be in playing state for entire \n> timeout, before we request masked EOS/Error messages from the bus? What \n> am I missing?\n>\n> > +\t\t\tbreak;\n> > +\t\tdefault:\n> > +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> > +\t\t\tbreak;\n> > +\t\t}\n> > +\n> > +\t\treturn TestFail;\n> > +\t}\n> > +\n> > +private:\n> > +\tvoid gstreamer_print_error(GstMessage *msg)\n> > +\t{\n> > +\t\tGError *err;\n> \n> This can use:\n> \n>      g_autoptr(GError) err = NULL;\n> \n> > +\t\tgchar *debug_info;\n> \n> and\n> \n> g_autofree char *debug_info = NULL;\n> \n> > +\n> > +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> > +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> > +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> > +\t\tg_printerr(\"Debugging information: %s\\n\",\n> > +\t\t\t   debug_info ? debug_info : \"none\");\n> > +\t\tg_clear_error(&err);\n> > +\t\tg_free(debug_info);\n> \n> So, that will lead us to dropping manual free of err and debug info\n> \n> \n> Minor things, so:\n> \n> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> \n> > +\t}\n> > +\n> > +\tGstElement *pipeline_;\n> > +\tGstElement *libcameraSrc_;\n> > +\tGstElement *convert0_;\n> > +\tGstElement *sink0_;\n> > +};\n> > +\n> > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > new file mode 100644\n> > index 000000000000..b99aa0da0ba3\n> > --- /dev/null\n> > +++ b/test/gstreamer/meson.build\n> > @@ -0,0 +1,19 @@\n> > +# SPDX-License-Identifier: CC0-1.0\n> > +\n> > +if not gst_enabled\n> > +    subdir_done()\n> > +endif\n> > +\n> > +gstreamer_tests = [\n> > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > +]\n> > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > +\n> > +foreach t : gstreamer_tests\n> > +    exe = executable(t[0], t[1],\n> > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > +                     link_with : test_libraries,\n> > +                     include_directories : test_includes_internal)\n> > +\n> > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > +endforeach\n> > diff --git a/test/meson.build b/test/meson.build\n> > index 3bceb5df586f..d0466f17d7b6 100644\n> > --- a/test/meson.build\n> > +++ b/test/meson.build\n> > @@ -11,6 +11,7 @@ subdir('libtest')\n> >   \n> >   subdir('camera')\n> >   subdir('controls')\n> > +subdir('gstreamer')\n> >   subdir('ipa')\n> >   subdir('ipc')\n> >   subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B7B8FBD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 09:33:49 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2F21268890;\n\tMon, 16 Aug 2021 11:33:49 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5311260260\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 11:33:47 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B7FC53E5;\n\tMon, 16 Aug 2021 11:33:46 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"joR3t9dz\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629106426;\n\tbh=nIMVUOWitFOZ55SqKgGOtSWumYtyl/JxcJsShzbkZNY=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=joR3t9dzfh1AM22Nw2MGneLbItS4G4j2+M17F5BYhyAU8sqmQoc6qnoPaYYr19fSj\n\tzmYMomq4wEHt62ZJyIyly2J0/QWK3SM89VoXDSk4EI0Zg4N3QzD2NPJzN7suQZLNym\n\tE1gKArmKESJxXCdqW/naJUX0eNDRGU/13VJZ0lCw=","Date":"Mon, 16 Aug 2021 12:33:41 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Umang Jain <umang.jain@ideasonboard.com>","Message-ID":"<YRow9fW+6OXNFcyS@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org,\n\tVedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18828,"web_url":"https://patchwork.libcamera.org/comment/18828/","msgid":"<CACGrz-N83p001eNDaGf0ftCQXXZcmCrpaLb=+3a7nAHR+=P=Bg@mail.gmail.com>","date":"2021-08-16T10:31:23","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"Hi Laurent and Umang,\nYes, I'll take a look soon, just finishing with classes.\n\nRegards,\nVedant Paranjape\n\n\nOn Mon, 16 Aug, 2021, 15:03 Laurent Pinchart, <\nlaurent.pinchart@ideasonboard.com> wrote:\n\n> Hi Umang,\n>\n> On Mon, Aug 16, 2021 at 11:17:27AM +0530, Umang Jain wrote:\n> > Hi Vedant and Laurent,\n> >\n> > Thanks for the patch. Overall looks good to me,\n> >\n> > a few comments below for cleanup paths :)\n>\n> The patch has already been merged I'm afraid.\n>\n> Vedant, could you read these comments, and either submit follow-up\n> patches, or reply if you disagree ?\n>\n> > On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > >\n> > > This patch adds a test to test if single stream using libcamera's\n> > > gstreamer element works.\n> > >\n> > > We need to work around two distinct issues with ASan when enabled in\n> the\n> > > build:\n> > >\n> > > - glib has a known leak at initialization time. This is covered by the\n> > >    suppression file shipped with glib, but it's not clear how to use it\n> > >    automatically. For now, disable leak detection to avoid test\n> failures.\n> > >\n> > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > >    compiled without ASan (which is likely) but libcamera is,\n> dlopen()ing\n> > >    the libcamera plugin will cause an ASan link order verification\n> > >    failure. Disable the verification child processes to work around the\n> > >    problem. This requires gcc 8 or newer.\n> > >\n> > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > > This version incorporates changes coming from my review of v10, and\n> > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > > independent reasons as explained in the commit message. I'd like to\n> find\n> > > a way to use leak suppression files to handle the glib initialization\n> > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > is acceptable in my opinion.\n> > >\n> > > The biggest trouble with these workarounds is that they don't work with\n> > > gcc version older than 8. As the stable version of the most common\n> Linux\n> > > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > > is the oldest version that libcamera supports) is also acceptable in my\n> > > opinion.\n> > >\n> > > Changes since v10:\n> > >\n> > > - Disable ASan leak detection\n> > > - Disable ASan link order verification for child processes\n> > > - Include source_path.h\n> > > - Remove unneeded explicit std::string construction\n> > > - Declare variables at usage site\n> > > - Make constants constexpr\n> > > - Add a variable for the message type\n> > > - Blank space fixes\n> > > ---\n> > >   .../gstreamer_single_stream_test.cpp          | 188\n> ++++++++++++++++++\n> > >   test/gstreamer/meson.build                    |  19 ++\n> > >   test/meson.build                              |   1 +\n> > >   3 files changed, 208 insertions(+)\n> > >   create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > >   create mode 100644 test/gstreamer/meson.build\n> > >\n> > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > new file mode 100644\n> > > index 000000000000..e26673b3471a\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > @@ -0,0 +1,188 @@\n> > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > +/*\n> > > + * Copyright (C) 2021, Vedant Paranjape\n> > > + *\n> > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > + */\n> > > +\n> > > +#include <iostream>\n> > > +#include <unistd.h>\n> > > +\n> > > +#include <libcamera/base/utils.h>\n> > > +\n> > > +#include \"libcamera/internal/source_paths.h\"\n> > > +\n> > > +#include <gst/gst.h>\n> > > +\n> > > +#include \"test.h\"\n> > > +\n> > > +using namespace std;\n> > > +\n> > > +extern \"C\" {\n> > > +const char *__asan_default_options()\n> > > +{\n> > > +   /*\n> > > +    * Disable leak detection due to a known global variable\n> initialization\n> > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > +    * using a suppression file instead of disabling leak detection.\n> > > +    */\n> > > +   return \"detect_leaks=false\";\n> > > +}\n> > > +}\n> > > +\n> > > +class GstreamerSingleStreamTest : public Test\n> > > +{\n> > > +protected:\n> > > +   int init() override\n> > > +   {\n> > > +           /*\n> > > +            * GStreamer spawns a process to run the gst-plugin-scanner\n> > > +            * helper. If libcamera is compiled with ASan enabled, and\n> as\n> > > +            * GStreamer is most likely not, this will cause the ASan\n> link\n> > > +            * order check to fail when gst-plugin-scanner dlopen()s\n> the\n> > > +            * plugin as many libraries will have already been loaded\n> by\n> > > +            * then. Work around this issue by disabling the link order\n> > > +            * check. This will only affect child processes, as ASan is\n> > > +            * already loaded for this process by the time this code is\n> > > +            * executed, and should thus hopefully be safe.\n> > > +            *\n> > > +            * This option is not available in gcc older than 8, the\n> only\n> > > +            * option in that case is to skip the test.\n> > > +            */\n> > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__\n> < 8\n> > > +           return TestSkip;\n> > > +#endif\n> > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > > +\n> > > +           /* Initialize GStreamer */\n> > > +           GError *errInit = nullptr;\n> >\n> > One can use:\n> >\n> >      g_autoptr(GError) errInit = NULL;\n> >\n> > ...\n> >\n> > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > > +                              errInit ? errInit->message : \"unknown\n> error\");\n> > > +                   if (errInit)\n> > > +                           g_error_free(errInit);\n> >\n> > And remove manual free here.\n> >\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /*\n> > > +            * Remove the system libcamera plugin, if any, and add the\n> > > +            * plugin from the build directory.\n> > > +            */\n> > > +           GstRegistry *registry = gst_registry_get();\n> > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n> \"libgstlibcamera.so\");\n> > > +           if (plugin) {\n> > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > +                   gst_object_unref(plugin);\n> > > +           }\n> > > +\n> > > +           std::string path = libcamera::utils::libcameraBuildPath()\n> > > +                            + \"src/gstreamer\";\n> > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > +                   g_printerr(\"Failed to add plugin to registry\\n\");\n> > > +                   gst_deinit();\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Create the elements */\n> > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\",\n> \"libcamera\");\n> > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n> \"convert0\");\n> > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > > +\n> > > +           /* Create the empty pipeline_ */\n> > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > +\n> > > +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_)\n> {\n> > > +                   g_printerr(\"Not all elements could be created.\n> %p.%p.%p.%p\\n\",\n> > > +                              pipeline_, convert0_, sink0_,\n> libcameraSrc_);\n> > > +                   if (pipeline_)\n> > > +                           gst_object_unref(pipeline_);\n> > > +                   if (convert0_)\n> > > +                           gst_object_unref(convert0_);\n> > > +                   if (sink0_)\n> > > +                           gst_object_unref(sink0_);\n> > > +                   if (libcameraSrc_)\n> > > +                           gst_object_unref(libcameraSrc_);\n> > > +                   gst_deinit();\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           return TestPass;\n> > > +   }\n> > > +\n> > > +   void cleanup() override\n> > > +   {\n> > > +           gst_object_unref(pipeline_);\n> > > +           gst_deinit();\n> > > +   }\n> > > +\n> > > +   int run() override\n> > > +   {\n> > > +           GstStateChangeReturn ret;\n> > > +\n> > > +           /* Build the pipeline */\n> > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n> convert0_, sink0_, NULL);\n> > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n> sink0_, NULL) != TRUE) {\n> > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Start playing */\n> > > +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > +                   g_printerr(\"Unable to set the pipeline to the\n> playing state.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > +           constexpr GstMessageType msgType =\n> > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR |\n> GST_MESSAGE_EOS);\n> > > +           constexpr GstClockTime timeout = 2000000000;\n> > > +\n> > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > +           g_autoptr(GstMessage) msg =\n> gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > +\n> > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > +\n> > > +           /* Parse error message */\n> > > +           if (msg == NULL)\n> > > +                   return TestPass;\n> > > +\n> > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > +           case GST_MESSAGE_ERROR:\n> > > +                   gstreamer_print_error(msg);\n> > > +                   break;\n> > > +           case GST_MESSAGE_EOS:\n> > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> >\n> > EOS doesn't sound like a error/Test-fail condition to me. If there is a\n> > possibility that EOS will be reached before timeout, what's ensures(in\n> > the first place) that the pipeline will be in playing state for entire\n> > timeout, before we request masked EOS/Error messages from the bus? What\n> > am I missing?\n> >\n> > > +                   break;\n> > > +           default:\n> > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > +                   break;\n> > > +           }\n> > > +\n> > > +           return TestFail;\n> > > +   }\n> > > +\n> > > +private:\n> > > +   void gstreamer_print_error(GstMessage *msg)\n> > > +   {\n> > > +           GError *err;\n> >\n> > This can use:\n> >\n> >      g_autoptr(GError) err = NULL;\n> >\n> > > +           gchar *debug_info;\n> >\n> > and\n> >\n> > g_autofree char *debug_info = NULL;\n> >\n> > > +\n> > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > +                      debug_info ? debug_info : \"none\");\n> > > +           g_clear_error(&err);\n> > > +           g_free(debug_info);\n> >\n> > So, that will lead us to dropping manual free of err and debug info\n> >\n> >\n> > Minor things, so:\n> >\n> > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> >\n> > > +   }\n> > > +\n> > > +   GstElement *pipeline_;\n> > > +   GstElement *libcameraSrc_;\n> > > +   GstElement *convert0_;\n> > > +   GstElement *sink0_;\n> > > +};\n> > > +\n> > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > new file mode 100644\n> > > index 000000000000..b99aa0da0ba3\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/meson.build\n> > > @@ -0,0 +1,19 @@\n> > > +# SPDX-License-Identifier: CC0-1.0\n> > > +\n> > > +if not gst_enabled\n> > > +    subdir_done()\n> > > +endif\n> > > +\n> > > +gstreamer_tests = [\n> > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > +]\n> > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > +\n> > > +foreach t : gstreamer_tests\n> > > +    exe = executable(t[0], t[1],\n> > > +                     dependencies : [libcamera_private,\n> gstreamer_dep],\n> > > +                     link_with : test_libraries,\n> > > +                     include_directories : test_includes_internal)\n> > > +\n> > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > +endforeach\n> > > diff --git a/test/meson.build b/test/meson.build\n> > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > --- a/test/meson.build\n> > > +++ b/test/meson.build\n> > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > >\n> > >   subdir('camera')\n> > >   subdir('controls')\n> > > +subdir('gstreamer')\n> > >   subdir('ipa')\n> > >   subdir('ipc')\n> > >   subdir('log')\n>\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 5C149BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 10:31:38 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C2C5468894;\n\tMon, 16 Aug 2021 12:31:37 +0200 (CEST)","from mail-yb1-xb2a.google.com (mail-yb1-xb2a.google.com\n\t[IPv6:2607:f8b0:4864:20::b2a])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A7A56025D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 12:31:36 +0200 (CEST)","by mail-yb1-xb2a.google.com with SMTP id l144so6294187ybl.12\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 03:31:36 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"Kt9G5Ppw\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=Vnm58AUJwuc6mDj+oxOOMa9GJERqKO7pM2y8BeaTMPs=;\n\tb=Kt9G5PpwCYosfEpbTiimFzTP6/NCkX6cFWh0Awl9q7OfV0yqKSP9HI1XCVKesDtTg3\n\te/sf8AmspG98HPHUYzVtQCl3b7W59dFpGQeMg27TfvODmJDTdessProe7yu+TxAgPfPt\n\tjGk/XUEmeIgYYOW6ac9awm/2P6GIor05Egzfji0ZnIxmIXXLVr/zcph+72uRG5jqlwCe\n\tIFQkOATVvT9YqqVgj7P57578Wz4Rn+f/sxRSNSG5SNOEr2N+vgWDfVlq3MLIHAMLfnLV\n\tLP1w/2KbOEDITfyMsmlpuv0lPkShcKepU0SZ4UDeAfSBNbu5mIr5N7I5VKwHJCLD758L\n\tEQHQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=Vnm58AUJwuc6mDj+oxOOMa9GJERqKO7pM2y8BeaTMPs=;\n\tb=sdWItCj9H5t/yfHcqU/EokY43i6aDSTPsGwr0R0u6pCg9rZr1hFynbBmPuwTH0DWBO\n\tKj6rPp8ZO9Eh6nfHV3bPLdUplX/Lpb4YFWkzR5L7BHkl1BScSzWt/iFeZ7WgSLz6odX6\n\tjBaEe74Xnp7jzGrlY8SuaDPRNcbL+9QCpWC0QPT2P1S78wmt0doZjCBpQgTKjvOIXrgG\n\tkjPqnkc68mgtXzZOCi3yWCjXzrjJzyaYRAimB+BFRgKrwHewBEoNocIc4iwmX/y8FzaE\n\tIVPIOo7eIuIBzMT9n74mtugtS44Eh8rm1KQGQHQ/pW3Re2SqG9I+P9IQRW6p2/2+7wrA\n\tPpkg==","X-Gm-Message-State":"AOAM530a4J4J+kY5r6cJOzpLeuEfbSudwbsqkU0sS8srX+TmOyC1ovov\n\taAAMbdE6gIatslwX0agbCLH5pohjFLzfmrZIgMM=","X-Google-Smtp-Source":"ABdhPJzivjBwSPTAmCmLhkb+xktKWHmr+uaN8P5KSNniuwJzuVvBkOdoG7308wt+9lrZEkbSgwI8dknMRZ9BWd01yls=","X-Received":"by 2002:a25:3f85:: with SMTP id\n\tm127mr20299628yba.21.1629109894869; \n\tMon, 16 Aug 2021 03:31:34 -0700 (PDT)","MIME-Version":"1.0","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<YRow9fW+6OXNFcyS@pendragon.ideasonboard.com>","In-Reply-To":"<YRow9fW+6OXNFcyS@pendragon.ideasonboard.com>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Mon, 16 Aug 2021 16:01:23 +0530","Message-ID":"<CACGrz-N83p001eNDaGf0ftCQXXZcmCrpaLb=+3a7nAHR+=P=Bg@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"0000000000009574e105c9aab464\"","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18837,"web_url":"https://patchwork.libcamera.org/comment/18837/","msgid":"<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>","date":"2021-08-16T13:34:00","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"Hi Umang,\nThanks for the Review. I feel the changes are appropriate. I will send a\npatch with the changes.\n\nOn Mon, Aug 16, 2021 at 11:17 AM Umang Jain <umang.jain@ideasonboard.com>\nwrote:\n\n> Hi Vedant and Laurent,\n>\n> Thanks for the patch. Overall looks good to me,\n>\n> a few comments below for cleanup paths :)\n> On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n>\n> From: Vedant Paranjape <vedantparanjape160201@gmail.com> <vedantparanjape160201@gmail.com>\n>\n> This patch adds a test to test if single stream using libcamera's\n> gstreamer element works.\n>\n> We need to work around two distinct issues with ASan when enabled in the\n> build:\n>\n> - glib has a known leak at initialization time. This is covered by the\n>   suppression file shipped with glib, but it's not clear how to use it\n>   automatically. For now, disable leak detection to avoid test failures.\n>\n> - GStreamer spawns a child process to scan plugins. If GStreamer is\n>   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n>   the libcamera plugin will cause an ASan link order verification\n>   failure. Disable the verification child processes to work around the\n>   problem. This requires gcc 8 or newer.\n>\n> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> <vedantparanjape160201@gmail.com>\n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> <paul.elder@ideasonboard.com>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> <kieran.bingham@ideasonboard.com>\n> Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com> <kieran.bingham@@ideasonboard.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> <laurent.pinchart@ideasonboard.com>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> <laurent.pinchart@ideasonboard.com>\n> ---\n> This version incorporates changes coming from my review of v10, and\n> fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> independent reasons as explained in the commit message. I'd like to find\n> a way to use leak suppression files to handle the glib initialization\n> leak, but that's a big rabbit hole. The workaround for the second issue\n> is acceptable in my opinion.\n>\n> The biggest trouble with these workarounds is that they don't work with\n> gcc version older than 8. As the stable version of the most common Linux\n> distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> is the oldest version that libcamera supports) is also acceptable in my\n> opinion.\n>\n> Changes since v10:\n>\n> - Disable ASan leak detection\n> - Disable ASan link order verification for child processes\n> - Include source_path.h\n> - Remove unneeded explicit std::string construction\n> - Declare variables at usage site\n> - Make constants constexpr\n> - Add a variable for the message type\n> - Blank space fixes\n> ---\n>  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n>  test/gstreamer/meson.build                    |  19 ++\n>  test/meson.build                              |   1 +\n>  3 files changed, 208 insertions(+)\n>  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n>  create mode 100644 test/gstreamer/meson.build\n>\n> diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> new file mode 100644\n> index 000000000000..e26673b3471a\n> --- /dev/null\n> +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> @@ -0,0 +1,188 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2021, Vedant Paranjape\n> + *\n> + * ipa_interface_test.cpp - Test the IPA interface\n> + */\n> +\n> +#include <iostream>\n> +#include <unistd.h>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +#include \"libcamera/internal/source_paths.h\"\n> +\n> +#include <gst/gst.h>\n> +\n> +#include \"test.h\"\n> +\n> +using namespace std;\n> +\n> +extern \"C\" {\n> +const char *__asan_default_options()\n> +{\n> +\t/*\n> +\t * Disable leak detection due to a known global variable initialization\n> +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> +\t * using a suppression file instead of disabling leak detection.\n> +\t */\n> +\treturn \"detect_leaks=false\";\n> +}\n> +}\n> +\n> +class GstreamerSingleStreamTest : public Test\n> +{\n> +protected:\n> +\tint init() override\n> +\t{\n> +\t\t/*\n> +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> +\t\t * GStreamer is most likely not, this will cause the ASan link\n> +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> +\t\t * plugin as many libraries will have already been loaded by\n> +\t\t * then. Work around this issue by disabling the link order\n> +\t\t * check. This will only affect child processes, as ASan is\n> +\t\t * already loaded for this process by the time this code is\n> +\t\t * executed, and should thus hopefully be safe.\n> +\t\t *\n> +\t\t * This option is not available in gcc older than 8, the only\n> +\t\t * option in that case is to skip the test.\n> +\t\t */\n> +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> +\t\treturn TestSkip;\n> +#endif\n> +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> +\n> +\t\t/* Initialize GStreamer */\n> +\t\tGError *errInit = nullptr;\n>\n> One can use:\n>\n>     g_autoptr(GError) errInit = NULL;\n>\n> ...\n>\n> +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> +\t\t\tif (errInit)\n> +\t\t\t\tg_error_free(errInit);\n>\n> And remove manual free here.\n>\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Remove the system libcamera plugin, if any, and add the\n> +\t\t * plugin from the build directory.\n> +\t\t */\n> +\t\tGstRegistry *registry = gst_registry_get();\n> +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> +\t\tif (plugin) {\n> +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> +\t\t\tgst_object_unref(plugin);\n> +\t\t}\n> +\n> +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> +\t\t\t\t + \"src/gstreamer\";\n> +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> +\t\t\tgst_deinit();\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Create the elements */\n> +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> +\n> +\t\t/* Create the empty pipeline_ */\n> +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> +\n> +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> +\t\t\tif (pipeline_)\n> +\t\t\t\tgst_object_unref(pipeline_);\n> +\t\t\tif (convert0_)\n> +\t\t\t\tgst_object_unref(convert0_);\n> +\t\t\tif (sink0_)\n> +\t\t\t\tgst_object_unref(sink0_);\n> +\t\t\tif (libcameraSrc_)\n> +\t\t\t\tgst_object_unref(libcameraSrc_);\n> +\t\t\tgst_deinit();\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +\n> +\tvoid cleanup() override\n> +\t{\n> +\t\tgst_object_unref(pipeline_);\n> +\t\tgst_deinit();\n> +\t}\n> +\n> +\tint run() override\n> +\t{\n> +\t\tGstStateChangeReturn ret;\n> +\n> +\t\t/* Build the pipeline */\n> +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Start playing */\n> +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> +\t\tconstexpr GstMessageType msgType =\n> +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> +\t\tconstexpr GstClockTime timeout = 2000000000;\n> +\n> +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> +\n> +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> +\n> +\t\t/* Parse error message */\n> +\t\tif (msg == NULL)\n> +\t\t\treturn TestPass;\n> +\n> +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> +\t\tcase GST_MESSAGE_ERROR:\n> +\t\t\tgstreamer_print_error(msg);\n> +\t\t\tbreak;\n> +\t\tcase GST_MESSAGE_EOS:\n> +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\n>\n> EOS doesn't sound like a error/Test-fail condition to me. If there is a\n> possibility that EOS will be reached before timeout, what's ensures(in the\n> first place) that the pipeline will be in playing state for entire timeout,\n> before we request masked EOS/Error messages from the bus? What am I missing?\n>\n\nI am not a gstreamer expert, to the best of my knowledge, EOS won't be ever\nsent by libcamerasrc, as it is the element which drives the pipeline. I\nthink this event can be dropped, I just want to confirm it with Nicolas\nonce he's back.\n\n> It is important to note that *only elements driving the pipeline should\never send an EOS event*. If your element is chain-based, it is not driving\nthe pipeline. Chain-based elements should just return GST_FLOW_EOS from\ntheir chain function at the end of the stream (or the configured segment)\n\nhttps://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n\n+\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> +\t\t\tbreak;\n> +\t\t}\n> +\n> +\t\treturn TestFail;\n> +\t}\n> +\n> +private:\n> +\tvoid gstreamer_print_error(GstMessage *msg)\n> +\t{\n> +\t\tGError *err;\n>\n> This can use:\n>\n>     g_autoptr(GError) err = NULL;\n>\n> +\t\tgchar *debug_info;\n>\n> and\n>\n>      g_autofree char *debug_info = NULL;\n>\n> +\n> +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> +\t\tg_printerr(\"Debugging information: %s\\n\",\n> +\t\t\t   debug_info ? debug_info : \"none\");\n> +\t\tg_clear_error(&err);\n> +\t\tg_free(debug_info);\n>\n> So, that will lead us to dropping manual free of err and debug info\n>\n>\n> Minor things, so:\n>\n> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> <umang.jain@ideasonboard.com>\n>\n> +\t}\n> +\n> +\tGstElement *pipeline_;\n> +\tGstElement *libcameraSrc_;\n> +\tGstElement *convert0_;\n> +\tGstElement *sink0_;\n> +};\n> +\n> +TEST_REGISTER(GstreamerSingleStreamTest)\n> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> new file mode 100644\n> index 000000000000..b99aa0da0ba3\n> --- /dev/null\n> +++ b/test/gstreamer/meson.build\n> @@ -0,0 +1,19 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +if not gst_enabled\n> +    subdir_done()\n> +endif\n> +\n> +gstreamer_tests = [\n> +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> +]\n> +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> +\n> +foreach t : gstreamer_tests\n> +    exe = executable(t[0], t[1],\n> +                     dependencies : [libcamera_private, gstreamer_dep],\n> +                     link_with : test_libraries,\n> +                     include_directories : test_includes_internal)\n> +\n> +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> +endforeach\n> diff --git a/test/meson.build b/test/meson.build\n> index 3bceb5df586f..d0466f17d7b6 100644\n> --- a/test/meson.build\n> +++ b/test/meson.build\n> @@ -11,6 +11,7 @@ subdir('libtest')\n>\n>  subdir('camera')\n>  subdir('controls')\n> +subdir('gstreamer')\n>  subdir('ipa')\n>  subdir('ipc')\n>  subdir('log')\n>\n>\nRegards,\n*Vedant Paranjape*","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 82CE1BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 13:34:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id F12A068895;\n\tMon, 16 Aug 2021 15:34:13 +0200 (CEST)","from mail-yb1-xb36.google.com (mail-yb1-xb36.google.com\n\t[IPv6:2607:f8b0:4864:20::b36])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 23AC16025D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 15:34:13 +0200 (CEST)","by mail-yb1-xb36.google.com with SMTP id m193so32833925ybf.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 06:34:13 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"LL/Kvg/v\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=xypGH25tar8kyjLN3hgbyVNeoBNWfL59WqwyMkqnnPE=;\n\tb=LL/Kvg/vg7GelFTEOgbTSh/BFIy9PXtk8gpSUS+O/tStucSt5zZM4DP8kIaGaETw3Q\n\tnd3+qle8TFOGHWO+nZBFK434jaa/StRnh5SZ/M50XraBIZBxStxHN0KDAcZ9veFQ/nEa\n\tBTcOryrDhmtuZ8WejyxP2rtxr+OTGxOEkV0dGLrZYfsznvg44ENwWlFc0T/jwheJ8yaF\n\tqrEDGNyzcmWgj1jGmeaPsUj11wYWWt6AxB8biteODGjMD4tsPBGf1XevSOUgaZ36Yqsu\n\tgLwkJ5d1ScN+tkui8O/AMiWYoal66oTH24jOPBdAt5z243jBPp8LG4aeYFlJuqFz8BCQ\n\t2cMQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=xypGH25tar8kyjLN3hgbyVNeoBNWfL59WqwyMkqnnPE=;\n\tb=kCCmjrKNTdAKSMY64Hx0JazSx+K565cqEsjbHbIzC8J3SGsTvJt9eO/EzFdLyHJUoI\n\tR+qB2lTP7mr1mwyX6KYF9sN2gfEH1eq13u8MwYGBk5vzhdo0jDbUEA6Yzb/4RZsCcBqV\n\tHzrGn7QmzJxXiyavSDLG+c4OKuMsYUSP0/zw6+FIMPWievGTMTdXYMboulHrvIxE5UOt\n\tsgDNfD7leGnEywVl0ZtBUYW6lSnrhN5ziZNJpdLH1NpT2pW9vWORECsb//TQgwhE8a6I\n\tY9nojffO2TkiDxNF1gHNnkKOzj/obc/ZKT8zU/TY15O+bpHnbkCzRmCdnINzXdB0452s\n\tNUog==","X-Gm-Message-State":"AOAM5332BsNL6vgVlurWAy2zQttkVezErR7vcdX7qmhWO9gi7RKB28XQ\n\tbY8eS1UU3v/8pmDe0elDzDY2NsMwqq6tmhIf1vpNIV17M105BbTY","X-Google-Smtp-Source":"ABdhPJwjEp7e/cwbmBSVSc8bKhj1LIOrKQVCgZlS+FnKx8SUDLcXkmcu58JZHGIHwp+IQX11EisCVtGDHyabNnh2wFg=","X-Received":"by 2002:a25:c9c5:: with SMTP id\n\tz188mr10078965ybf.223.1629120851767; \n\tMon, 16 Aug 2021 06:34:11 -0700 (PDT)","MIME-Version":"1.0","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>","In-Reply-To":"<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Mon, 16 Aug 2021 19:04:00 +0530","Message-ID":"<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>","To":"Umang Jain <umang.jain@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000aa771305c9ad4166\"","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18839,"web_url":"https://patchwork.libcamera.org/comment/18839/","msgid":"<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>","date":"2021-08-16T13:58:54","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Vedant,\n\nOn Mon, Aug 16, 2021 at 07:04:00PM +0530, Vedant Paranjape wrote:\n> Hi Umang,\n> Thanks for the Review. I feel the changes are appropriate. I will send a\n> patch with the changes.\n\nWhile at it, I'm wondering if it would make sense to select the vimc\ncamera explicitly from the libcamerasrc element instead of using\nwhatever camera happens to be the first. It would make tests more\nreproducible.\n\n> On Mon, Aug 16, 2021 at 11:17 AM Umang Jain wrote:\n> \n> > Hi Vedant and Laurent,\n> >\n> > Thanks for the patch. Overall looks good to me,\n> >\n> > a few comments below for cleanup paths :)\n> > On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> >\n> > From: Vedant Paranjape <vedantparanjape160201@gmail.com> <vedantparanjape160201@gmail.com>\n> >\n> > This patch adds a test to test if single stream using libcamera's\n> > gstreamer element works.\n> >\n> > We need to work around two distinct issues with ASan when enabled in the\n> > build:\n> >\n> > - glib has a known leak at initialization time. This is covered by the\n> >   suppression file shipped with glib, but it's not clear how to use it\n> >   automatically. For now, disable leak detection to avoid test failures.\n> >\n> > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> >   the libcamera plugin will cause an ASan link order verification\n> >   failure. Disable the verification child processes to work around the\n> >   problem. This requires gcc 8 or newer.\n> >\n> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> <vedantparanjape160201@gmail.com>\n> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> <paul.elder@ideasonboard.com>\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> <kieran.bingham@ideasonboard.com>\n> > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com> <kieran.bingham@@ideasonboard.com>\n> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> <laurent.pinchart@ideasonboard.com>\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> <laurent.pinchart@ideasonboard.com>\n> > ---\n> > This version incorporates changes coming from my review of v10, and\n> > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > independent reasons as explained in the commit message. I'd like to find\n> > a way to use leak suppression files to handle the glib initialization\n> > leak, but that's a big rabbit hole. The workaround for the second issue\n> > is acceptable in my opinion.\n> >\n> > The biggest trouble with these workarounds is that they don't work with\n> > gcc version older than 8. As the stable version of the most common Linux\n> > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > is the oldest version that libcamera supports) is also acceptable in my\n> > opinion.\n> >\n> > Changes since v10:\n> >\n> > - Disable ASan leak detection\n> > - Disable ASan link order verification for child processes\n> > - Include source_path.h\n> > - Remove unneeded explicit std::string construction\n> > - Declare variables at usage site\n> > - Make constants constexpr\n> > - Add a variable for the message type\n> > - Blank space fixes\n> > ---\n> >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> >  test/gstreamer/meson.build                    |  19 ++\n> >  test/meson.build                              |   1 +\n> >  3 files changed, 208 insertions(+)\n> >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> >  create mode 100644 test/gstreamer/meson.build\n> >\n> > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > new file mode 100644\n> > index 000000000000..e26673b3471a\n> > --- /dev/null\n> > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > @@ -0,0 +1,188 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2021, Vedant Paranjape\n> > + *\n> > + * ipa_interface_test.cpp - Test the IPA interface\n> > + */\n> > +\n> > +#include <iostream>\n> > +#include <unistd.h>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +#include \"libcamera/internal/source_paths.h\"\n> > +\n> > +#include <gst/gst.h>\n> > +\n> > +#include \"test.h\"\n> > +\n> > +using namespace std;\n> > +\n> > +extern \"C\" {\n> > +const char *__asan_default_options()\n> > +{\n> > +\t/*\n> > +\t * Disable leak detection due to a known global variable initialization\n> > +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> > +\t * using a suppression file instead of disabling leak detection.\n> > +\t */\n> > +\treturn \"detect_leaks=false\";\n> > +}\n> > +}\n> > +\n> > +class GstreamerSingleStreamTest : public Test\n> > +{\n> > +protected:\n> > +\tint init() override\n> > +\t{\n> > +\t\t/*\n> > +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> > +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> > +\t\t * GStreamer is most likely not, this will cause the ASan link\n> > +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> > +\t\t * plugin as many libraries will have already been loaded by\n> > +\t\t * then. Work around this issue by disabling the link order\n> > +\t\t * check. This will only affect child processes, as ASan is\n> > +\t\t * already loaded for this process by the time this code is\n> > +\t\t * executed, and should thus hopefully be safe.\n> > +\t\t *\n> > +\t\t * This option is not available in gcc older than 8, the only\n> > +\t\t * option in that case is to skip the test.\n> > +\t\t */\n> > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> > +\t\treturn TestSkip;\n> > +#endif\n> > +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > +\n> > +\t\t/* Initialize GStreamer */\n> > +\t\tGError *errInit = nullptr;\n> >\n> > One can use:\n> >\n> >     g_autoptr(GError) errInit = NULL;\n> >\n> > ...\n> >\n> > +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> > +\t\t\tif (errInit)\n> > +\t\t\t\tg_error_free(errInit);\n> >\n> > And remove manual free here.\n> >\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/*\n> > +\t\t * Remove the system libcamera plugin, if any, and add the\n> > +\t\t * plugin from the build directory.\n> > +\t\t */\n> > +\t\tGstRegistry *registry = gst_registry_get();\n> > +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > +\t\tif (plugin) {\n> > +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> > +\t\t\tgst_object_unref(plugin);\n> > +\t\t}\n> > +\n> > +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> > +\t\t\t\t + \"src/gstreamer\";\n> > +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> > +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> > +\t\t\tgst_deinit();\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Create the elements */\n> > +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > +\n> > +\t\t/* Create the empty pipeline_ */\n> > +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > +\n> > +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> > +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> > +\t\t\tif (pipeline_)\n> > +\t\t\t\tgst_object_unref(pipeline_);\n> > +\t\t\tif (convert0_)\n> > +\t\t\t\tgst_object_unref(convert0_);\n> > +\t\t\tif (sink0_)\n> > +\t\t\t\tgst_object_unref(sink0_);\n> > +\t\t\tif (libcameraSrc_)\n> > +\t\t\t\tgst_object_unref(libcameraSrc_);\n> > +\t\t\tgst_deinit();\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\treturn TestPass;\n> > +\t}\n> > +\n> > +\tvoid cleanup() override\n> > +\t{\n> > +\t\tgst_object_unref(pipeline_);\n> > +\t\tgst_deinit();\n> > +\t}\n> > +\n> > +\tint run() override\n> > +\t{\n> > +\t\tGstStateChangeReturn ret;\n> > +\n> > +\t\t/* Build the pipeline */\n> > +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> > +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Start playing */\n> > +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> > +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> > +\t\tconstexpr GstMessageType msgType =\n> > +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > +\t\tconstexpr GstClockTime timeout = 2000000000;\n> > +\n> > +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > +\n> > +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> > +\n> > +\t\t/* Parse error message */\n> > +\t\tif (msg == NULL)\n> > +\t\t\treturn TestPass;\n> > +\n> > +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> > +\t\tcase GST_MESSAGE_ERROR:\n> > +\t\t\tgstreamer_print_error(msg);\n> > +\t\t\tbreak;\n> > +\t\tcase GST_MESSAGE_EOS:\n> > +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\n> >\n> > EOS doesn't sound like a error/Test-fail condition to me. If there is a\n> > possibility that EOS will be reached before timeout, what's ensures(in the\n> > first place) that the pipeline will be in playing state for entire timeout,\n> > before we request masked EOS/Error messages from the bus? What am I missing?\n> \n> I am not a gstreamer expert, to the best of my knowledge, EOS won't be ever\n> sent by libcamerasrc, as it is the element which drives the pipeline. I\n> think this event can be dropped, I just want to confirm it with Nicolas\n> once he's back.\n> \n> > It is important to note that *only elements driving the pipeline should\n> ever send an EOS event*. If your element is chain-based, it is not driving\n> the pipeline. Chain-based elements should just return GST_FLOW_EOS from\n> their chain function at the end of the stream (or the configured segment)\n> \n> https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n> \n> +\t\t\tbreak;\n> > +\t\tdefault:\n> > +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> > +\t\t\tbreak;\n> > +\t\t}\n> > +\n> > +\t\treturn TestFail;\n> > +\t}\n> > +\n> > +private:\n> > +\tvoid gstreamer_print_error(GstMessage *msg)\n> > +\t{\n> > +\t\tGError *err;\n> >\n> > This can use:\n> >\n> >     g_autoptr(GError) err = NULL;\n> >\n> > +\t\tgchar *debug_info;\n> >\n> > and\n> >\n> >      g_autofree char *debug_info = NULL;\n> >\n> > +\n> > +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> > +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> > +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> > +\t\tg_printerr(\"Debugging information: %s\\n\",\n> > +\t\t\t   debug_info ? debug_info : \"none\");\n> > +\t\tg_clear_error(&err);\n> > +\t\tg_free(debug_info);\n> >\n> > So, that will lead us to dropping manual free of err and debug info\n> >\n> >\n> > Minor things, so:\n> >\n> > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> >\n> > +\t}\n> > +\n> > +\tGstElement *pipeline_;\n> > +\tGstElement *libcameraSrc_;\n> > +\tGstElement *convert0_;\n> > +\tGstElement *sink0_;\n> > +};\n> > +\n> > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > new file mode 100644\n> > index 000000000000..b99aa0da0ba3\n> > --- /dev/null\n> > +++ b/test/gstreamer/meson.build\n> > @@ -0,0 +1,19 @@\n> > +# SPDX-License-Identifier: CC0-1.0\n> > +\n> > +if not gst_enabled\n> > +    subdir_done()\n> > +endif\n> > +\n> > +gstreamer_tests = [\n> > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > +]\n> > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > +\n> > +foreach t : gstreamer_tests\n> > +    exe = executable(t[0], t[1],\n> > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > +                     link_with : test_libraries,\n> > +                     include_directories : test_includes_internal)\n> > +\n> > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > +endforeach\n> > diff --git a/test/meson.build b/test/meson.build\n> > index 3bceb5df586f..d0466f17d7b6 100644\n> > --- a/test/meson.build\n> > +++ b/test/meson.build\n> > @@ -11,6 +11,7 @@ subdir('libtest')\n> >\n> >  subdir('camera')\n> >  subdir('controls')\n> > +subdir('gstreamer')\n> >  subdir('ipa')\n> >  subdir('ipc')\n> >  subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 906A2BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 13:59:04 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B1B7E68893;\n\tMon, 16 Aug 2021 15:59:01 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E26EC6025D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 15:59:00 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5AB163E5;\n\tMon, 16 Aug 2021 15:59:00 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"FY4UDGe6\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629122340;\n\tbh=wYKSML2vXweLk1Gs/ZVkUUQdshnprdi7KPRQgvTq+HY=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=FY4UDGe6YhIYDfvdBD6PtaV2uNo7K8I8v35o9UnZy/EUMRjyrvo6za+n9TD+j8wBA\n\ti8jvlbbr078kI/osTkHEUlLSchDkYhiENwqEjz9Omjs9OEYYLZXw1LAdghriwG0rWr\n\tPM3bfG/rr19ERizBj9lB+r8eYoYmElL8qlh2BySg=","Date":"Mon, 16 Aug 2021 16:58:54 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Message-ID":"<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18843,"web_url":"https://patchwork.libcamera.org/comment/18843/","msgid":"<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>","date":"2021-08-16T14:14:34","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"Hi Laurent,\nYes, what's the camera-name with which vimc camera can be selected ?\nI could simply set the object property to use it. I think it would be nice\nto have two separate tests, one which runs on real camera and other one on\nvimc camera.\n\nRegards,\n*Vedant Paranjape*\n\nOn Mon, Aug 16, 2021 at 7:29 PM Laurent Pinchart <\nlaurent.pinchart@ideasonboard.com> wrote:\n\n> Hi Vedant,\n>\n> On Mon, Aug 16, 2021 at 07:04:00PM +0530, Vedant Paranjape wrote:\n> > Hi Umang,\n> > Thanks for the Review. I feel the changes are appropriate. I will send a\n> > patch with the changes.\n>\n> While at it, I'm wondering if it would make sense to select the vimc\n> camera explicitly from the libcamerasrc element instead of using\n> whatever camera happens to be the first. It would make tests more\n> reproducible.\n>\n> > On Mon, Aug 16, 2021 at 11:17 AM Umang Jain wrote:\n> >\n> > > Hi Vedant and Laurent,\n> > >\n> > > Thanks for the patch. Overall looks good to me,\n> > >\n> > > a few comments below for cleanup paths :)\n> > > On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> > >\n> > > From: Vedant Paranjape <vedantparanjape160201@gmail.com> <\n> vedantparanjape160201@gmail.com>\n> > >\n> > > This patch adds a test to test if single stream using libcamera's\n> > > gstreamer element works.\n> > >\n> > > We need to work around two distinct issues with ASan when enabled in\n> the\n> > > build:\n> > >\n> > > - glib has a known leak at initialization time. This is covered by the\n> > >   suppression file shipped with glib, but it's not clear how to use it\n> > >   automatically. For now, disable leak detection to avoid test\n> failures.\n> > >\n> > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> > >   the libcamera plugin will cause an ASan link order verification\n> > >   failure. Disable the verification child processes to work around the\n> > >   problem. This requires gcc 8 or newer.\n> > >\n> > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> <\n> vedantparanjape160201@gmail.com>\n> > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> <\n> paul.elder@ideasonboard.com>\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> <\n> kieran.bingham@ideasonboard.com>\n> > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> <kieran.bingham@@ideasonboard.com>\n> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> <\n> laurent.pinchart@ideasonboard.com>\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> <\n> laurent.pinchart@ideasonboard.com>\n> > > ---\n> > > This version incorporates changes coming from my review of v10, and\n> > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > > independent reasons as explained in the commit message. I'd like to\n> find\n> > > a way to use leak suppression files to handle the glib initialization\n> > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > is acceptable in my opinion.\n> > >\n> > > The biggest trouble with these workarounds is that they don't work with\n> > > gcc version older than 8. As the stable version of the most common\n> Linux\n> > > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > > is the oldest version that libcamera supports) is also acceptable in my\n> > > opinion.\n> > >\n> > > Changes since v10:\n> > >\n> > > - Disable ASan leak detection\n> > > - Disable ASan link order verification for child processes\n> > > - Include source_path.h\n> > > - Remove unneeded explicit std::string construction\n> > > - Declare variables at usage site\n> > > - Make constants constexpr\n> > > - Add a variable for the message type\n> > > - Blank space fixes\n> > > ---\n> > >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> > >  test/gstreamer/meson.build                    |  19 ++\n> > >  test/meson.build                              |   1 +\n> > >  3 files changed, 208 insertions(+)\n> > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > >  create mode 100644 test/gstreamer/meson.build\n> > >\n> > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > new file mode 100644\n> > > index 000000000000..e26673b3471a\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > @@ -0,0 +1,188 @@\n> > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > +/*\n> > > + * Copyright (C) 2021, Vedant Paranjape\n> > > + *\n> > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > + */\n> > > +\n> > > +#include <iostream>\n> > > +#include <unistd.h>\n> > > +\n> > > +#include <libcamera/base/utils.h>\n> > > +\n> > > +#include \"libcamera/internal/source_paths.h\"\n> > > +\n> > > +#include <gst/gst.h>\n> > > +\n> > > +#include \"test.h\"\n> > > +\n> > > +using namespace std;\n> > > +\n> > > +extern \"C\" {\n> > > +const char *__asan_default_options()\n> > > +{\n> > > +   /*\n> > > +    * Disable leak detection due to a known global variable\n> initialization\n> > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > +    * using a suppression file instead of disabling leak detection.\n> > > +    */\n> > > +   return \"detect_leaks=false\";\n> > > +}\n> > > +}\n> > > +\n> > > +class GstreamerSingleStreamTest : public Test\n> > > +{\n> > > +protected:\n> > > +   int init() override\n> > > +   {\n> > > +           /*\n> > > +            * GStreamer spawns a process to run the gst-plugin-scanner\n> > > +            * helper. If libcamera is compiled with ASan enabled, and\n> as\n> > > +            * GStreamer is most likely not, this will cause the ASan\n> link\n> > > +            * order check to fail when gst-plugin-scanner dlopen()s\n> the\n> > > +            * plugin as many libraries will have already been loaded\n> by\n> > > +            * then. Work around this issue by disabling the link order\n> > > +            * check. This will only affect child processes, as ASan is\n> > > +            * already loaded for this process by the time this code is\n> > > +            * executed, and should thus hopefully be safe.\n> > > +            *\n> > > +            * This option is not available in gcc older than 8, the\n> only\n> > > +            * option in that case is to skip the test.\n> > > +            */\n> > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__\n> < 8\n> > > +           return TestSkip;\n> > > +#endif\n> > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > > +\n> > > +           /* Initialize GStreamer */\n> > > +           GError *errInit = nullptr;\n> > >\n> > > One can use:\n> > >\n> > >     g_autoptr(GError) errInit = NULL;\n> > >\n> > > ...\n> > >\n> > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > > +                              errInit ? errInit->message : \"unknown\n> error\");\n> > > +                   if (errInit)\n> > > +                           g_error_free(errInit);\n> > >\n> > > And remove manual free here.\n> > >\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /*\n> > > +            * Remove the system libcamera plugin, if any, and add the\n> > > +            * plugin from the build directory.\n> > > +            */\n> > > +           GstRegistry *registry = gst_registry_get();\n> > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n> \"libgstlibcamera.so\");\n> > > +           if (plugin) {\n> > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > +                   gst_object_unref(plugin);\n> > > +           }\n> > > +\n> > > +           std::string path = libcamera::utils::libcameraBuildPath()\n> > > +                            + \"src/gstreamer\";\n> > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > +                   g_printerr(\"Failed to add plugin to registry\\n\");\n> > > +                   gst_deinit();\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Create the elements */\n> > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\",\n> \"libcamera\");\n> > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n> \"convert0\");\n> > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > > +\n> > > +           /* Create the empty pipeline_ */\n> > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > +\n> > > +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_)\n> {\n> > > +                   g_printerr(\"Not all elements could be created.\n> %p.%p.%p.%p\\n\",\n> > > +                              pipeline_, convert0_, sink0_,\n> libcameraSrc_);\n> > > +                   if (pipeline_)\n> > > +                           gst_object_unref(pipeline_);\n> > > +                   if (convert0_)\n> > > +                           gst_object_unref(convert0_);\n> > > +                   if (sink0_)\n> > > +                           gst_object_unref(sink0_);\n> > > +                   if (libcameraSrc_)\n> > > +                           gst_object_unref(libcameraSrc_);\n> > > +                   gst_deinit();\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           return TestPass;\n> > > +   }\n> > > +\n> > > +   void cleanup() override\n> > > +   {\n> > > +           gst_object_unref(pipeline_);\n> > > +           gst_deinit();\n> > > +   }\n> > > +\n> > > +   int run() override\n> > > +   {\n> > > +           GstStateChangeReturn ret;\n> > > +\n> > > +           /* Build the pipeline */\n> > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n> convert0_, sink0_, NULL);\n> > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n> sink0_, NULL) != TRUE) {\n> > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Start playing */\n> > > +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > +                   g_printerr(\"Unable to set the pipeline to the\n> playing state.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > +           constexpr GstMessageType msgType =\n> > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR |\n> GST_MESSAGE_EOS);\n> > > +           constexpr GstClockTime timeout = 2000000000;\n> > > +\n> > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > +           g_autoptr(GstMessage) msg =\n> gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > +\n> > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > +\n> > > +           /* Parse error message */\n> > > +           if (msg == NULL)\n> > > +                   return TestPass;\n> > > +\n> > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > +           case GST_MESSAGE_ERROR:\n> > > +                   gstreamer_print_error(msg);\n> > > +                   break;\n> > > +           case GST_MESSAGE_EOS:\n> > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > >\n> > > EOS doesn't sound like a error/Test-fail condition to me. If there is a\n> > > possibility that EOS will be reached before timeout, what's ensures(in\n> the\n> > > first place) that the pipeline will be in playing state for entire\n> timeout,\n> > > before we request masked EOS/Error messages from the bus? What am I\n> missing?\n> >\n> > I am not a gstreamer expert, to the best of my knowledge, EOS won't be\n> ever\n> > sent by libcamerasrc, as it is the element which drives the pipeline. I\n> > think this event can be dropped, I just want to confirm it with Nicolas\n> > once he's back.\n> >\n> > > It is important to note that *only elements driving the pipeline should\n> > ever send an EOS event*. If your element is chain-based, it is not\n> driving\n> > the pipeline. Chain-based elements should just return GST_FLOW_EOS from\n> > their chain function at the end of the stream (or the configured segment)\n> >\n> >\n> https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n> >\n> > +                     break;\n> > > +           default:\n> > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > +                   break;\n> > > +           }\n> > > +\n> > > +           return TestFail;\n> > > +   }\n> > > +\n> > > +private:\n> > > +   void gstreamer_print_error(GstMessage *msg)\n> > > +   {\n> > > +           GError *err;\n> > >\n> > > This can use:\n> > >\n> > >     g_autoptr(GError) err = NULL;\n> > >\n> > > +           gchar *debug_info;\n> > >\n> > > and\n> > >\n> > >      g_autofree char *debug_info = NULL;\n> > >\n> > > +\n> > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > +                      debug_info ? debug_info : \"none\");\n> > > +           g_clear_error(&err);\n> > > +           g_free(debug_info);\n> > >\n> > > So, that will lead us to dropping manual free of err and debug info\n> > >\n> > >\n> > > Minor things, so:\n> > >\n> > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> > >\n> > > +   }\n> > > +\n> > > +   GstElement *pipeline_;\n> > > +   GstElement *libcameraSrc_;\n> > > +   GstElement *convert0_;\n> > > +   GstElement *sink0_;\n> > > +};\n> > > +\n> > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > new file mode 100644\n> > > index 000000000000..b99aa0da0ba3\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/meson.build\n> > > @@ -0,0 +1,19 @@\n> > > +# SPDX-License-Identifier: CC0-1.0\n> > > +\n> > > +if not gst_enabled\n> > > +    subdir_done()\n> > > +endif\n> > > +\n> > > +gstreamer_tests = [\n> > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > +]\n> > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > +\n> > > +foreach t : gstreamer_tests\n> > > +    exe = executable(t[0], t[1],\n> > > +                     dependencies : [libcamera_private,\n> gstreamer_dep],\n> > > +                     link_with : test_libraries,\n> > > +                     include_directories : test_includes_internal)\n> > > +\n> > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > +endforeach\n> > > diff --git a/test/meson.build b/test/meson.build\n> > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > --- a/test/meson.build\n> > > +++ b/test/meson.build\n> > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > >\n> > >  subdir('camera')\n> > >  subdir('controls')\n> > > +subdir('gstreamer')\n> > >  subdir('ipa')\n> > >  subdir('ipc')\n> > >  subdir('log')\n>\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 592A8BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 14:14:49 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C30D068893;\n\tMon, 16 Aug 2021 16:14:48 +0200 (CEST)","from mail-yb1-xb2b.google.com (mail-yb1-xb2b.google.com\n\t[IPv6:2607:f8b0:4864:20::b2b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A07AD6025D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 16:14:46 +0200 (CEST)","by mail-yb1-xb2b.google.com with SMTP id r4so10206878ybp.4\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 07:14:46 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"GaUYmiQi\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=MOEeik0X8nhv/iEYKIRB/E+fpgnaHP+9vWxbz7rcw3M=;\n\tb=GaUYmiQiXaXdpcC0blm3bsTA3eJv6jxBp5CYMAC8IQqaV3lcYTSmNsBHGcT8Q6q8jO\n\tROq0IhU1V3WQhAx/Ehq55DW2eVR+JEALCEe4pJOacthne/3+ffl8Nd6bsO/uehGKBqh/\n\tPMZbzZvm04TedD1a1Xqwre5z2ALTvYvDRTcHqCpEy8LT8p7QVzH8Ak4G+U1jUNFj2MUe\n\t+FzNUysRbDn9S9bTbhouss1MCcmSg0sUZbMHrqvNESIaT8eRAHpe41YMLlG7G1skS1yL\n\tVlLDfUrXWxVxTPmxCYrAg9H4SCnGmpfqSMndI5p4FVUw5/HRrgOb8F0Fj9NgabOr/1DX\n\to/HQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=MOEeik0X8nhv/iEYKIRB/E+fpgnaHP+9vWxbz7rcw3M=;\n\tb=VKp9+0YZ2PJCB4xBhRemLJCK58sQTEM4Ez9SMwmLwSy50YkDgSMdwLY/nvnRvGe5WM\n\tvDAlHmvLZ6DFhqK0mrEdKToYLWPwPGwXlQ87t0he2F7b4b4u/VstKPLNflh1o2EFxnxK\n\tP3uRGK86esA4BHFOUzbvLFBlU8Dztjh32U1ngDajkr9WbVapT56W1AjQFCvjZFAmnV/A\n\tIkTChzN9wxWlVMrENd7/WR8WAfkYcUhS/SZDkpN8oz18okWKy5TiXXHNJp+V/+MIMFB0\n\tADXRXcIZNOy4hzH7wq5GVprc8d7QbxYpLVxEot6dMu1BbMPu3d90GJYZModawZLSIR0S\n\t1f5g==","X-Gm-Message-State":"AOAM531GamRN2i1YG9r/UVFsCyJ1qCZIlBPSsroq9Pa4+/7TLaxcs1zl\n\tgy74cFiQzwoM+93jwK9B8HfXeQsjgm7xN0m19Dg=","X-Google-Smtp-Source":"ABdhPJwSGrVkAeUgjmCDLtJst0QgF8Oj4T8v9Y1R+G5vEtp/ZC3RLBjTmYz8iLF9TAVV5pxhfv8Qqij0PeKIoIcME0Y=","X-Received":"by 2002:a25:3f85:: with SMTP id\n\tm127mr21438966yba.21.1629123285372; \n\tMon, 16 Aug 2021 07:14:45 -0700 (PDT)","MIME-Version":"1.0","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>\n\t<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>","In-Reply-To":"<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Mon, 16 Aug 2021 19:44:34 +0530","Message-ID":"<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000b8542705c9add2cb\"","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18845,"web_url":"https://patchwork.libcamera.org/comment/18845/","msgid":"<b69307d5-d4b8-8d96-4169-cffff78ac349@ideasonboard.com>","date":"2021-08-16T14:22:29","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 16/08/2021 15:14, Vedant Paranjape wrote:\n> Hi Laurent,\n> Yes, what's the camera-name with which vimc camera can be selected ?\n> I could simply set the object property to use it. I think it would be\n> nice to have two separate tests, one which runs on real camera and other\n> one on vimc camera.\n\n\nOr runs on all available cameras?\n\n--\nKieran\n\n\n> Regards,\n> /*Vedant Paranjape*/\n> \n> On Mon, Aug 16, 2021 at 7:29 PM Laurent Pinchart\n> <laurent.pinchart@ideasonboard.com\n> <mailto:laurent.pinchart@ideasonboard.com>> wrote:\n> \n>     Hi Vedant,\n> \n>     On Mon, Aug 16, 2021 at 07:04:00PM +0530, Vedant Paranjape wrote:\n>     > Hi Umang,\n>     > Thanks for the Review. I feel the changes are appropriate. I will\n>     send a\n>     > patch with the changes.\n> \n>     While at it, I'm wondering if it would make sense to select the vimc\n>     camera explicitly from the libcamerasrc element instead of using\n>     whatever camera happens to be the first. It would make tests more\n>     reproducible.\n> \n>     > On Mon, Aug 16, 2021 at 11:17 AM Umang Jain wrote:\n>     >\n>     > > Hi Vedant and Laurent,\n>     > >\n>     > > Thanks for the patch. Overall looks good to me,\n>     > >\n>     > > a few comments below for cleanup paths :)\n>     > > On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n>     > >\n>     > > From: Vedant Paranjape <vedantparanjape160201@gmail.com\n>     <mailto:vedantparanjape160201@gmail.com>>\n>     <vedantparanjape160201@gmail.com\n>     <mailto:vedantparanjape160201@gmail.com>>\n>     > >\n>     > > This patch adds a test to test if single stream using libcamera's\n>     > > gstreamer element works.\n>     > >\n>     > > We need to work around two distinct issues with ASan when\n>     enabled in the\n>     > > build:\n>     > >\n>     > > - glib has a known leak at initialization time. This is covered\n>     by the\n>     > >   suppression file shipped with glib, but it's not clear how to\n>     use it\n>     > >   automatically. For now, disable leak detection to avoid test\n>     failures.\n>     > >\n>     > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n>     > >   compiled without ASan (which is likely) but libcamera is,\n>     dlopen()ing\n>     > >   the libcamera plugin will cause an ASan link order verification\n>     > >   failure. Disable the verification child processes to work\n>     around the\n>     > >   problem. This requires gcc 8 or newer.\n>     > >\n>     > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com\n>     <mailto:vedantparanjape160201@gmail.com>>\n>     <vedantparanjape160201@gmail.com\n>     <mailto:vedantparanjape160201@gmail.com>>\n>     > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com\n>     <mailto:paul.elder@ideasonboard.com>> <paul.elder@ideasonboard.com\n>     <mailto:paul.elder@ideasonboard.com>>\n>     > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com\n>     <mailto:kieran.bingham@ideasonboard.com>>\n>     <kieran.bingham@ideasonboard.com\n>     <mailto:kieran.bingham@ideasonboard.com>>\n>     > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com\n>     <http://ideasonboard.com>> <kieran.bingham@@ideasonboard.com\n>     <http://ideasonboard.com>>\n>     > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com\n>     <mailto:laurent.pinchart@ideasonboard.com>>\n>     <laurent.pinchart@ideasonboard.com\n>     <mailto:laurent.pinchart@ideasonboard.com>>\n>     > > Signed-off-by: Laurent Pinchart\n>     <laurent.pinchart@ideasonboard.com\n>     <mailto:laurent.pinchart@ideasonboard.com>>\n>     <laurent.pinchart@ideasonboard.com\n>     <mailto:laurent.pinchart@ideasonboard.com>>\n>     > > ---\n>     > > This version incorporates changes coming from my review of v10, and\n>     > > fixes for ASan issues. ASan is a bit of a pain with GStreamer,\n>     for two\n>     > > independent reasons as explained in the commit message. I'd like\n>     to find\n>     > > a way to use leak suppression files to handle the glib\n>     initialization\n>     > > leak, but that's a big rabbit hole. The workaround for the\n>     second issue\n>     > > is acceptable in my opinion.\n>     > >\n>     > > The biggest trouble with these workarounds is that they don't\n>     work with\n>     > > gcc version older than 8. As the stable version of the most\n>     common Linux\n>     > > distributions ship gcc 8 or newer, skipping this test for gcc 7\n>     (which\n>     > > is the oldest version that libcamera supports) is also\n>     acceptable in my\n>     > > opinion.\n>     > >\n>     > > Changes since v10:\n>     > >\n>     > > - Disable ASan leak detection\n>     > > - Disable ASan link order verification for child processes\n>     > > - Include source_path.h\n>     > > - Remove unneeded explicit std::string construction\n>     > > - Declare variables at usage site\n>     > > - Make constants constexpr\n>     > > - Add a variable for the message type\n>     > > - Blank space fixes\n>     > > ---\n>     > >  .../gstreamer_single_stream_test.cpp          | 188\n>     ++++++++++++++++++\n>     > >  test/gstreamer/meson.build                    |  19 ++\n>     > >  test/meson.build                              |   1 +\n>     > >  3 files changed, 208 insertions(+)\n>     > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n>     > >  create mode 100644 test/gstreamer/meson.build\n>     > >\n>     > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n>     b/test/gstreamer/gstreamer_single_stream_test.cpp\n>     > > new file mode 100644\n>     > > index 000000000000..e26673b3471a\n>     > > --- /dev/null\n>     > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n>     > > @@ -0,0 +1,188 @@\n>     > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n>     > > +/*\n>     > > + * Copyright (C) 2021, Vedant Paranjape\n>     > > + *\n>     > > + * ipa_interface_test.cpp - Test the IPA interface\n>     > > + */\n>     > > +\n>     > > +#include <iostream>\n>     > > +#include <unistd.h>\n>     > > +\n>     > > +#include <libcamera/base/utils.h>\n>     > > +\n>     > > +#include \"libcamera/internal/source_paths.h\"\n>     > > +\n>     > > +#include <gst/gst.h>\n>     > > +\n>     > > +#include \"test.h\"\n>     > > +\n>     > > +using namespace std;\n>     > > +\n>     > > +extern \"C\" {\n>     > > +const char *__asan_default_options()\n>     > > +{\n>     > > +   /*\n>     > > +    * Disable leak detection due to a known global variable\n>     initialization\n>     > > +    * leak in glib's g_quark_init(). This should ideally be\n>     handled by\n>     > > +    * using a suppression file instead of disabling leak detection.\n>     > > +    */\n>     > > +   return \"detect_leaks=false\";\n>     > > +}\n>     > > +}\n>     > > +\n>     > > +class GstreamerSingleStreamTest : public Test\n>     > > +{\n>     > > +protected:\n>     > > +   int init() override\n>     > > +   {\n>     > > +           /*\n>     > > +            * GStreamer spawns a process to run the\n>     gst-plugin-scanner\n>     > > +            * helper. If libcamera is compiled with ASan\n>     enabled, and as\n>     > > +            * GStreamer is most likely not, this will cause the\n>     ASan link\n>     > > +            * order check to fail when gst-plugin-scanner\n>     dlopen()s the\n>     > > +            * plugin as many libraries will have already been\n>     loaded by\n>     > > +            * then. Work around this issue by disabling the\n>     link order\n>     > > +            * check. This will only affect child processes, as\n>     ASan is\n>     > > +            * already loaded for this process by the time this\n>     code is\n>     > > +            * executed, and should thus hopefully be safe.\n>     > > +            *\n>     > > +            * This option is not available in gcc older than 8,\n>     the only\n>     > > +            * option in that case is to skip the test.\n>     > > +            */\n>     > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) &&\n>     __GNUC__ < 8\n>     > > +           return TestSkip;\n>     > > +#endif\n>     > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n>     > > +\n>     > > +           /* Initialize GStreamer */\n>     > > +           GError *errInit = nullptr;\n>     > >\n>     > > One can use:\n>     > >\n>     > >     g_autoptr(GError) errInit = NULL;\n>     > >\n>     > > ...\n>     > >\n>     > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n>     > > +                   g_printerr(\"Could not initialize GStreamer:\n>     %s\\n\",\n>     > > +                              errInit ? errInit->message :\n>     \"unknown error\");\n>     > > +                   if (errInit)\n>     > > +                           g_error_free(errInit);\n>     > >\n>     > > And remove manual free here.\n>     > >\n>     > > +\n>     > > +                   return TestFail;\n>     > > +           }\n>     > > +\n>     > > +           /*\n>     > > +            * Remove the system libcamera plugin, if any, and\n>     add the\n>     > > +            * plugin from the build directory.\n>     > > +            */\n>     > > +           GstRegistry *registry = gst_registry_get();\n>     > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n>     \"libgstlibcamera.so\");\n>     > > +           if (plugin) {\n>     > > +                   gst_registry_remove_plugin(registry, plugin);\n>     > > +                   gst_object_unref(plugin);\n>     > > +           }\n>     > > +\n>     > > +           std::string path =\n>     libcamera::utils::libcameraBuildPath()\n>     > > +                            + \"src/gstreamer\";\n>     > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n>     > > +                   g_printerr(\"Failed to add plugin to\n>     registry\\n\");\n>     > > +                   gst_deinit();\n>     > > +                   return TestFail;\n>     > > +           }\n>     > > +\n>     > > +           /* Create the elements */\n>     > > +           libcameraSrc_ =\n>     gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n>     > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n>     \"convert0\");\n>     > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n>     > > +\n>     > > +           /* Create the empty pipeline_ */\n>     > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n>     > > +\n>     > > +           if (!pipeline_ || !convert0_ || !sink0_ ||\n>     !libcameraSrc_) {\n>     > > +                   g_printerr(\"Not all elements could be\n>     created. %p.%p.%p.%p\\n\",\n>     > > +                              pipeline_, convert0_, sink0_,\n>     libcameraSrc_);\n>     > > +                   if (pipeline_)\n>     > > +                           gst_object_unref(pipeline_);\n>     > > +                   if (convert0_)\n>     > > +                           gst_object_unref(convert0_);\n>     > > +                   if (sink0_)\n>     > > +                           gst_object_unref(sink0_);\n>     > > +                   if (libcameraSrc_)\n>     > > +                           gst_object_unref(libcameraSrc_);\n>     > > +                   gst_deinit();\n>     > > +\n>     > > +                   return TestFail;\n>     > > +           }\n>     > > +\n>     > > +           return TestPass;\n>     > > +   }\n>     > > +\n>     > > +   void cleanup() override\n>     > > +   {\n>     > > +           gst_object_unref(pipeline_);\n>     > > +           gst_deinit();\n>     > > +   }\n>     > > +\n>     > > +   int run() override\n>     > > +   {\n>     > > +           GstStateChangeReturn ret;\n>     > > +\n>     > > +           /* Build the pipeline */\n>     > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n>     convert0_, sink0_, NULL);\n>     > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n>     sink0_, NULL) != TRUE) {\n>     > > +                   g_printerr(\"Elements could not be linked.\\n\");\n>     > > +                   return TestFail;\n>     > > +           }\n>     > > +\n>     > > +           /* Start playing */\n>     > > +           ret = gst_element_set_state(pipeline_,\n>     GST_STATE_PLAYING);\n>     > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n>     > > +                   g_printerr(\"Unable to set the pipeline to\n>     the playing state.\\n\");\n>     > > +                   return TestFail;\n>     > > +           }\n>     > > +\n>     > > +           /* Wait until error or EOS or timeout after 2 seconds */\n>     > > +           constexpr GstMessageType msgType =\n>     > > +                 \n>      static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n>     > > +           constexpr GstClockTime timeout = 2000000000;\n>     > > +\n>     > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n>     > > +           g_autoptr(GstMessage) msg =\n>     gst_bus_timed_pop_filtered(bus, timeout, msgType);\n>     > > +\n>     > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n>     > > +\n>     > > +           /* Parse error message */\n>     > > +           if (msg == NULL)\n>     > > +                   return TestPass;\n>     > > +\n>     > > +           switch (GST_MESSAGE_TYPE(msg)) {\n>     > > +           case GST_MESSAGE_ERROR:\n>     > > +                   gstreamer_print_error(msg);\n>     > > +                   break;\n>     > > +           case GST_MESSAGE_EOS:\n>     > > +                   g_print(\"End-Of-Stream reached.\\n\");\n>     > >\n>     > > EOS doesn't sound like a error/Test-fail condition to me. If\n>     there is a\n>     > > possibility that EOS will be reached before timeout, what's\n>     ensures(in the\n>     > > first place) that the pipeline will be in playing state for\n>     entire timeout,\n>     > > before we request masked EOS/Error messages from the bus? What\n>     am I missing?\n>     >\n>     > I am not a gstreamer expert, to the best of my knowledge, EOS\n>     won't be ever\n>     > sent by libcamerasrc, as it is the element which drives the\n>     pipeline. I\n>     > think this event can be dropped, I just want to confirm it with\n>     Nicolas\n>     > once he's back.\n>     >\n>     > > It is important to note that *only elements driving the pipeline\n>     should\n>     > ever send an EOS event*. If your element is chain-based, it is not\n>     driving\n>     > the pipeline. Chain-based elements should just return GST_FLOW_EOS\n>     from\n>     > their chain function at the end of the stream (or the configured\n>     segment)\n>     >\n>     >\n>     https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n>     <https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos>\n>     >\n>     > +                     break;\n>     > > +           default:\n>     > > +                   g_printerr(\"Unexpected message received.\\n\");\n>     > > +                   break;\n>     > > +           }\n>     > > +\n>     > > +           return TestFail;\n>     > > +   }\n>     > > +\n>     > > +private:\n>     > > +   void gstreamer_print_error(GstMessage *msg)\n>     > > +   {\n>     > > +           GError *err;\n>     > >\n>     > > This can use:\n>     > >\n>     > >     g_autoptr(GError) err = NULL;\n>     > >\n>     > > +           gchar *debug_info;\n>     > >\n>     > > and\n>     > >\n>     > >      g_autofree char *debug_info = NULL;\n>     > >\n>     > > +\n>     > > +           gst_message_parse_error(msg, &err, &debug_info);\n>     > > +           g_printerr(\"Error received from element %s: %s\\n\",\n>     > > +                      GST_OBJECT_NAME(msg->src), err->message);\n>     > > +           g_printerr(\"Debugging information: %s\\n\",\n>     > > +                      debug_info ? debug_info : \"none\");\n>     > > +           g_clear_error(&err);\n>     > > +           g_free(debug_info);\n>     > >\n>     > > So, that will lead us to dropping manual free of err and debug info\n>     > >\n>     > >\n>     > > Minor things, so:\n>     > >\n>     > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com\n>     <mailto:umang.jain@ideasonboard.com>>\n>     > >\n>     > > +   }\n>     > > +\n>     > > +   GstElement *pipeline_;\n>     > > +   GstElement *libcameraSrc_;\n>     > > +   GstElement *convert0_;\n>     > > +   GstElement *sink0_;\n>     > > +};\n>     > > +\n>     > > +TEST_REGISTER(GstreamerSingleStreamTest)\n>     > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n>     > > new file mode 100644\n>     > > index 000000000000..b99aa0da0ba3\n>     > > --- /dev/null\n>     > > +++ b/test/gstreamer/meson.build\n>     > > @@ -0,0 +1,19 @@\n>     > > +# SPDX-License-Identifier: CC0-1.0\n>     > > +\n>     > > +if not gst_enabled\n>     > > +    subdir_done()\n>     > > +endif\n>     > > +\n>     > > +gstreamer_tests = [\n>     > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n>     > > +]\n>     > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n>     > > +\n>     > > +foreach t : gstreamer_tests\n>     > > +    exe = executable(t[0], t[1],\n>     > > +                     dependencies : [libcamera_private,\n>     gstreamer_dep],\n>     > > +                     link_with : test_libraries,\n>     > > +                     include_directories : test_includes_internal)\n>     > > +\n>     > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n>     > > +endforeach\n>     > > diff --git a/test/meson.build b/test/meson.build\n>     > > index 3bceb5df586f..d0466f17d7b6 100644\n>     > > --- a/test/meson.build\n>     > > +++ b/test/meson.build\n>     > > @@ -11,6 +11,7 @@ subdir('libtest')\n>     > >\n>     > >  subdir('camera')\n>     > >  subdir('controls')\n>     > > +subdir('gstreamer')\n>     > >  subdir('ipa')\n>     > >  subdir('ipc')\n>     > >  subdir('log')\n> \n>     -- \n>     Regards,\n> \n>     Laurent Pinchart\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E839BBD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 14:22:33 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id A415868894;\n\tMon, 16 Aug 2021 16:22:33 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BA86E68891\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 16:22:32 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2CE113E5;\n\tMon, 16 Aug 2021 16:22:32 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"RoQoKvKW\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629123752;\n\tbh=SVUPIIHQQyZPqa9sGFpaKGljI2TEqW96ie7ZLBmO+Xc=;\n\th=Subject:To:Cc:References:From:Date:In-Reply-To:From;\n\tb=RoQoKvKW3yY0YoLKzEOTwXAOf9/KLTPT9LPhAOeOMXyMRWvUxyuiNX4MQxOSvaony\n\ttTFYnhbJYJ+Fr5kHk6UB4JLkzjVAfencUn1wWMxX2gyINIOrOo0ds6dEU0mJ1w8MLg\n\tGA+nceShQG+dnf0SOQZxyCKedIXAQiWEqhOkUaHs=","To":"Vedant Paranjape <vedantparanjape160201@gmail.com>,\n\tLaurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>\n\t<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>\n\t<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<b69307d5-d4b8-8d96-4169-cffff78ac349@ideasonboard.com>","Date":"Mon, 16 Aug 2021 15:22:29 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18846,"web_url":"https://patchwork.libcamera.org/comment/18846/","msgid":"<CACGrz-Ow3rOp8_R_nWs1dfdJmC4bADh0So9JqCMXphLV7d0jiw@mail.gmail.com>","date":"2021-08-16T14:27:11","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"Hi Kieran,\nSounds like a good idea. Once I figure how to use GstDeviceProvider, I'll\ntry to do this.\n\nRegards,\nVedant Paranjape\n\nOn Mon, 16 Aug, 2021, 19:52 Kieran Bingham, <kieran.bingham@ideasonboard.com>\nwrote:\n\n> On 16/08/2021 15:14, Vedant Paranjape wrote:\n> > Hi Laurent,\n> > Yes, what's the camera-name with which vimc camera can be selected ?\n> > I could simply set the object property to use it. I think it would be\n> > nice to have two separate tests, one which runs on real camera and other\n> > one on vimc camera.\n>\n>\n> Or runs on all available cameras?\n>\n> --\n> Kieran\n>\n>\n> > Regards,\n> > /*Vedant Paranjape*/\n> >\n> > On Mon, Aug 16, 2021 at 7:29 PM Laurent Pinchart\n> > <laurent.pinchart@ideasonboard.com\n> > <mailto:laurent.pinchart@ideasonboard.com>> wrote:\n> >\n> >     Hi Vedant,\n> >\n> >     On Mon, Aug 16, 2021 at 07:04:00PM +0530, Vedant Paranjape wrote:\n> >     > Hi Umang,\n> >     > Thanks for the Review. I feel the changes are appropriate. I will\n> >     send a\n> >     > patch with the changes.\n> >\n> >     While at it, I'm wondering if it would make sense to select the vimc\n> >     camera explicitly from the libcamerasrc element instead of using\n> >     whatever camera happens to be the first. It would make tests more\n> >     reproducible.\n> >\n> >     > On Mon, Aug 16, 2021 at 11:17 AM Umang Jain wrote:\n> >     >\n> >     > > Hi Vedant and Laurent,\n> >     > >\n> >     > > Thanks for the patch. Overall looks good to me,\n> >     > >\n> >     > > a few comments below for cleanup paths :)\n> >     > > On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> >     > >\n> >     > > From: Vedant Paranjape <vedantparanjape160201@gmail.com\n> >     <mailto:vedantparanjape160201@gmail.com>>\n> >     <vedantparanjape160201@gmail.com\n> >     <mailto:vedantparanjape160201@gmail.com>>\n> >     > >\n> >     > > This patch adds a test to test if single stream using libcamera's\n> >     > > gstreamer element works.\n> >     > >\n> >     > > We need to work around two distinct issues with ASan when\n> >     enabled in the\n> >     > > build:\n> >     > >\n> >     > > - glib has a known leak at initialization time. This is covered\n> >     by the\n> >     > >   suppression file shipped with glib, but it's not clear how to\n> >     use it\n> >     > >   automatically. For now, disable leak detection to avoid test\n> >     failures.\n> >     > >\n> >     > > - GStreamer spawns a child process to scan plugins. If GStreamer\n> is\n> >     > >   compiled without ASan (which is likely) but libcamera is,\n> >     dlopen()ing\n> >     > >   the libcamera plugin will cause an ASan link order verification\n> >     > >   failure. Disable the verification child processes to work\n> >     around the\n> >     > >   problem. This requires gcc 8 or newer.\n> >     > >\n> >     > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com\n> >     <mailto:vedantparanjape160201@gmail.com>>\n> >     <vedantparanjape160201@gmail.com\n> >     <mailto:vedantparanjape160201@gmail.com>>\n> >     > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com\n> >     <mailto:paul.elder@ideasonboard.com>> <paul.elder@ideasonboard.com\n> >     <mailto:paul.elder@ideasonboard.com>>\n> >     > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com\n> >     <mailto:kieran.bingham@ideasonboard.com>>\n> >     <kieran.bingham@ideasonboard.com\n> >     <mailto:kieran.bingham@ideasonboard.com>>\n> >     > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com\n> >     <http://ideasonboard.com>> <kieran.bingham@@ideasonboard.com\n> >     <http://ideasonboard.com>>\n> >     > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com\n> >     <mailto:laurent.pinchart@ideasonboard.com>>\n> >     <laurent.pinchart@ideasonboard.com\n> >     <mailto:laurent.pinchart@ideasonboard.com>>\n> >     > > Signed-off-by: Laurent Pinchart\n> >     <laurent.pinchart@ideasonboard.com\n> >     <mailto:laurent.pinchart@ideasonboard.com>>\n> >     <laurent.pinchart@ideasonboard.com\n> >     <mailto:laurent.pinchart@ideasonboard.com>>\n> >     > > ---\n> >     > > This version incorporates changes coming from my review of v10,\n> and\n> >     > > fixes for ASan issues. ASan is a bit of a pain with GStreamer,\n> >     for two\n> >     > > independent reasons as explained in the commit message. I'd like\n> >     to find\n> >     > > a way to use leak suppression files to handle the glib\n> >     initialization\n> >     > > leak, but that's a big rabbit hole. The workaround for the\n> >     second issue\n> >     > > is acceptable in my opinion.\n> >     > >\n> >     > > The biggest trouble with these workarounds is that they don't\n> >     work with\n> >     > > gcc version older than 8. As the stable version of the most\n> >     common Linux\n> >     > > distributions ship gcc 8 or newer, skipping this test for gcc 7\n> >     (which\n> >     > > is the oldest version that libcamera supports) is also\n> >     acceptable in my\n> >     > > opinion.\n> >     > >\n> >     > > Changes since v10:\n> >     > >\n> >     > > - Disable ASan leak detection\n> >     > > - Disable ASan link order verification for child processes\n> >     > > - Include source_path.h\n> >     > > - Remove unneeded explicit std::string construction\n> >     > > - Declare variables at usage site\n> >     > > - Make constants constexpr\n> >     > > - Add a variable for the message type\n> >     > > - Blank space fixes\n> >     > > ---\n> >     > >  .../gstreamer_single_stream_test.cpp          | 188\n> >     ++++++++++++++++++\n> >     > >  test/gstreamer/meson.build                    |  19 ++\n> >     > >  test/meson.build                              |   1 +\n> >     > >  3 files changed, 208 insertions(+)\n> >     > >  create mode 100644\n> test/gstreamer/gstreamer_single_stream_test.cpp\n> >     > >  create mode 100644 test/gstreamer/meson.build\n> >     > >\n> >     > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> >     b/test/gstreamer/gstreamer_single_stream_test.cpp\n> >     > > new file mode 100644\n> >     > > index 000000000000..e26673b3471a\n> >     > > --- /dev/null\n> >     > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> >     > > @@ -0,0 +1,188 @@\n> >     > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> >     > > +/*\n> >     > > + * Copyright (C) 2021, Vedant Paranjape\n> >     > > + *\n> >     > > + * ipa_interface_test.cpp - Test the IPA interface\n> >     > > + */\n> >     > > +\n> >     > > +#include <iostream>\n> >     > > +#include <unistd.h>\n> >     > > +\n> >     > > +#include <libcamera/base/utils.h>\n> >     > > +\n> >     > > +#include \"libcamera/internal/source_paths.h\"\n> >     > > +\n> >     > > +#include <gst/gst.h>\n> >     > > +\n> >     > > +#include \"test.h\"\n> >     > > +\n> >     > > +using namespace std;\n> >     > > +\n> >     > > +extern \"C\" {\n> >     > > +const char *__asan_default_options()\n> >     > > +{\n> >     > > +   /*\n> >     > > +    * Disable leak detection due to a known global variable\n> >     initialization\n> >     > > +    * leak in glib's g_quark_init(). This should ideally be\n> >     handled by\n> >     > > +    * using a suppression file instead of disabling leak\n> detection.\n> >     > > +    */\n> >     > > +   return \"detect_leaks=false\";\n> >     > > +}\n> >     > > +}\n> >     > > +\n> >     > > +class GstreamerSingleStreamTest : public Test\n> >     > > +{\n> >     > > +protected:\n> >     > > +   int init() override\n> >     > > +   {\n> >     > > +           /*\n> >     > > +            * GStreamer spawns a process to run the\n> >     gst-plugin-scanner\n> >     > > +            * helper. If libcamera is compiled with ASan\n> >     enabled, and as\n> >     > > +            * GStreamer is most likely not, this will cause the\n> >     ASan link\n> >     > > +            * order check to fail when gst-plugin-scanner\n> >     dlopen()s the\n> >     > > +            * plugin as many libraries will have already been\n> >     loaded by\n> >     > > +            * then. Work around this issue by disabling the\n> >     link order\n> >     > > +            * check. This will only affect child processes, as\n> >     ASan is\n> >     > > +            * already loaded for this process by the time this\n> >     code is\n> >     > > +            * executed, and should thus hopefully be safe.\n> >     > > +            *\n> >     > > +            * This option is not available in gcc older than 8,\n> >     the only\n> >     > > +            * option in that case is to skip the test.\n> >     > > +            */\n> >     > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) &&\n> >     __GNUC__ < 8\n> >     > > +           return TestSkip;\n> >     > > +#endif\n> >     > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\",\n> 1);\n> >     > > +\n> >     > > +           /* Initialize GStreamer */\n> >     > > +           GError *errInit = nullptr;\n> >     > >\n> >     > > One can use:\n> >     > >\n> >     > >     g_autoptr(GError) errInit = NULL;\n> >     > >\n> >     > > ...\n> >     > >\n> >     > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> >     > > +                   g_printerr(\"Could not initialize GStreamer:\n> >     %s\\n\",\n> >     > > +                              errInit ? errInit->message :\n> >     \"unknown error\");\n> >     > > +                   if (errInit)\n> >     > > +                           g_error_free(errInit);\n> >     > >\n> >     > > And remove manual free here.\n> >     > >\n> >     > > +\n> >     > > +                   return TestFail;\n> >     > > +           }\n> >     > > +\n> >     > > +           /*\n> >     > > +            * Remove the system libcamera plugin, if any, and\n> >     add the\n> >     > > +            * plugin from the build directory.\n> >     > > +            */\n> >     > > +           GstRegistry *registry = gst_registry_get();\n> >     > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n> >     \"libgstlibcamera.so\");\n> >     > > +           if (plugin) {\n> >     > > +                   gst_registry_remove_plugin(registry, plugin);\n> >     > > +                   gst_object_unref(plugin);\n> >     > > +           }\n> >     > > +\n> >     > > +           std::string path =\n> >     libcamera::utils::libcameraBuildPath()\n> >     > > +                            + \"src/gstreamer\";\n> >     > > +           if (!gst_registry_scan_path(registry, path.c_str()))\n> {\n> >     > > +                   g_printerr(\"Failed to add plugin to\n> >     registry\\n\");\n> >     > > +                   gst_deinit();\n> >     > > +                   return TestFail;\n> >     > > +           }\n> >     > > +\n> >     > > +           /* Create the elements */\n> >     > > +           libcameraSrc_ =\n> >     gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> >     > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n> >     \"convert0\");\n> >     > > +           sink0_ = gst_element_factory_make(\"fakesink\",\n> \"sink0\");\n> >     > > +\n> >     > > +           /* Create the empty pipeline_ */\n> >     > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> >     > > +\n> >     > > +           if (!pipeline_ || !convert0_ || !sink0_ ||\n> >     !libcameraSrc_) {\n> >     > > +                   g_printerr(\"Not all elements could be\n> >     created. %p.%p.%p.%p\\n\",\n> >     > > +                              pipeline_, convert0_, sink0_,\n> >     libcameraSrc_);\n> >     > > +                   if (pipeline_)\n> >     > > +                           gst_object_unref(pipeline_);\n> >     > > +                   if (convert0_)\n> >     > > +                           gst_object_unref(convert0_);\n> >     > > +                   if (sink0_)\n> >     > > +                           gst_object_unref(sink0_);\n> >     > > +                   if (libcameraSrc_)\n> >     > > +                           gst_object_unref(libcameraSrc_);\n> >     > > +                   gst_deinit();\n> >     > > +\n> >     > > +                   return TestFail;\n> >     > > +           }\n> >     > > +\n> >     > > +           return TestPass;\n> >     > > +   }\n> >     > > +\n> >     > > +   void cleanup() override\n> >     > > +   {\n> >     > > +           gst_object_unref(pipeline_);\n> >     > > +           gst_deinit();\n> >     > > +   }\n> >     > > +\n> >     > > +   int run() override\n> >     > > +   {\n> >     > > +           GstStateChangeReturn ret;\n> >     > > +\n> >     > > +           /* Build the pipeline */\n> >     > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n> >     convert0_, sink0_, NULL);\n> >     > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n> >     sink0_, NULL) != TRUE) {\n> >     > > +                   g_printerr(\"Elements could not be\n> linked.\\n\");\n> >     > > +                   return TestFail;\n> >     > > +           }\n> >     > > +\n> >     > > +           /* Start playing */\n> >     > > +           ret = gst_element_set_state(pipeline_,\n> >     GST_STATE_PLAYING);\n> >     > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> >     > > +                   g_printerr(\"Unable to set the pipeline to\n> >     the playing state.\\n\");\n> >     > > +                   return TestFail;\n> >     > > +           }\n> >     > > +\n> >     > > +           /* Wait until error or EOS or timeout after 2\n> seconds */\n> >     > > +           constexpr GstMessageType msgType =\n> >     > > +\n> >      static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> >     > > +           constexpr GstClockTime timeout = 2000000000;\n> >     > > +\n> >     > > +           g_autoptr(GstBus) bus =\n> gst_element_get_bus(pipeline_);\n> >     > > +           g_autoptr(GstMessage) msg =\n> >     gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> >     > > +\n> >     > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> >     > > +\n> >     > > +           /* Parse error message */\n> >     > > +           if (msg == NULL)\n> >     > > +                   return TestPass;\n> >     > > +\n> >     > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> >     > > +           case GST_MESSAGE_ERROR:\n> >     > > +                   gstreamer_print_error(msg);\n> >     > > +                   break;\n> >     > > +           case GST_MESSAGE_EOS:\n> >     > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> >     > >\n> >     > > EOS doesn't sound like a error/Test-fail condition to me. If\n> >     there is a\n> >     > > possibility that EOS will be reached before timeout, what's\n> >     ensures(in the\n> >     > > first place) that the pipeline will be in playing state for\n> >     entire timeout,\n> >     > > before we request masked EOS/Error messages from the bus? What\n> >     am I missing?\n> >     >\n> >     > I am not a gstreamer expert, to the best of my knowledge, EOS\n> >     won't be ever\n> >     > sent by libcamerasrc, as it is the element which drives the\n> >     pipeline. I\n> >     > think this event can be dropped, I just want to confirm it with\n> >     Nicolas\n> >     > once he's back.\n> >     >\n> >     > > It is important to note that *only elements driving the pipeline\n> >     should\n> >     > ever send an EOS event*. If your element is chain-based, it is not\n> >     driving\n> >     > the pipeline. Chain-based elements should just return GST_FLOW_EOS\n> >     from\n> >     > their chain function at the end of the stream (or the configured\n> >     segment)\n> >     >\n> >     >\n> >\n> https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n> >     <\n> https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n> >\n> >     >\n> >     > +                     break;\n> >     > > +           default:\n> >     > > +                   g_printerr(\"Unexpected message received.\\n\");\n> >     > > +                   break;\n> >     > > +           }\n> >     > > +\n> >     > > +           return TestFail;\n> >     > > +   }\n> >     > > +\n> >     > > +private:\n> >     > > +   void gstreamer_print_error(GstMessage *msg)\n> >     > > +   {\n> >     > > +           GError *err;\n> >     > >\n> >     > > This can use:\n> >     > >\n> >     > >     g_autoptr(GError) err = NULL;\n> >     > >\n> >     > > +           gchar *debug_info;\n> >     > >\n> >     > > and\n> >     > >\n> >     > >      g_autofree char *debug_info = NULL;\n> >     > >\n> >     > > +\n> >     > > +           gst_message_parse_error(msg, &err, &debug_info);\n> >     > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> >     > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> >     > > +           g_printerr(\"Debugging information: %s\\n\",\n> >     > > +                      debug_info ? debug_info : \"none\");\n> >     > > +           g_clear_error(&err);\n> >     > > +           g_free(debug_info);\n> >     > >\n> >     > > So, that will lead us to dropping manual free of err and debug\n> info\n> >     > >\n> >     > >\n> >     > > Minor things, so:\n> >     > >\n> >     > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com\n> >     <mailto:umang.jain@ideasonboard.com>>\n> >     > >\n> >     > > +   }\n> >     > > +\n> >     > > +   GstElement *pipeline_;\n> >     > > +   GstElement *libcameraSrc_;\n> >     > > +   GstElement *convert0_;\n> >     > > +   GstElement *sink0_;\n> >     > > +};\n> >     > > +\n> >     > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> >     > > diff --git a/test/gstreamer/meson.build\n> b/test/gstreamer/meson.build\n> >     > > new file mode 100644\n> >     > > index 000000000000..b99aa0da0ba3\n> >     > > --- /dev/null\n> >     > > +++ b/test/gstreamer/meson.build\n> >     > > @@ -0,0 +1,19 @@\n> >     > > +# SPDX-License-Identifier: CC0-1.0\n> >     > > +\n> >     > > +if not gst_enabled\n> >     > > +    subdir_done()\n> >     > > +endif\n> >     > > +\n> >     > > +gstreamer_tests = [\n> >     > > +    ['single_stream_test',\n>  'gstreamer_single_stream_test.cpp'],\n> >     > > +]\n> >     > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> >     > > +\n> >     > > +foreach t : gstreamer_tests\n> >     > > +    exe = executable(t[0], t[1],\n> >     > > +                     dependencies : [libcamera_private,\n> >     gstreamer_dep],\n> >     > > +                     link_with : test_libraries,\n> >     > > +                     include_directories :\n> test_includes_internal)\n> >     > > +\n> >     > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> >     > > +endforeach\n> >     > > diff --git a/test/meson.build b/test/meson.build\n> >     > > index 3bceb5df586f..d0466f17d7b6 100644\n> >     > > --- a/test/meson.build\n> >     > > +++ b/test/meson.build\n> >     > > @@ -11,6 +11,7 @@ subdir('libtest')\n> >     > >\n> >     > >  subdir('camera')\n> >     > >  subdir('controls')\n> >     > > +subdir('gstreamer')\n> >     > >  subdir('ipa')\n> >     > >  subdir('ipc')\n> >     > >  subdir('log')\n> >\n> >     --\n> >     Regards,\n> >\n> >     Laurent Pinchart\n> >\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D0A05BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 14:27:25 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 820B8688A2;\n\tMon, 16 Aug 2021 16:27:25 +0200 (CEST)","from mail-yb1-xb33.google.com (mail-yb1-xb33.google.com\n\t[IPv6:2607:f8b0:4864:20::b33])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5761168891\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 16:27:24 +0200 (CEST)","by mail-yb1-xb33.google.com with SMTP id m193so33182793ybf.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 07:27:24 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"Zr+NuS5M\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=3FbA8fwbOlDSow2cqd1ypEgDfGgAPoOTwX26WBcOOn4=;\n\tb=Zr+NuS5MA/cCFOxYg2dXTMpniJTNLkM6o1dLCnfNaS22Zs4UhKKhZDJo5E3w2wggn8\n\tS/Z7g71r/60pFelVQbVgttQbh/EDKZNU25/SyQ3vrSoRZjhjub6x8Uz4QpMHJd0XyjSK\n\ty0o27MK9Uo2DCjzupOwn753jmnkrrDW/evjT8CSV6y90s1j31X66xSeJfmrAHjQGR8be\n\tMf7De39nUapSgNaxzkrQHZD+l7GSThKe1l30sLDR+nCrbkhVVugckCxZmWThZUq0pQ+l\n\t8whCMBoBCktjWQYOkc/3Svim/gIKmOkjVmpBUj6/NRpzPVLnlARle9fb5csLQwXgcOku\n\tCYuA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=3FbA8fwbOlDSow2cqd1ypEgDfGgAPoOTwX26WBcOOn4=;\n\tb=GbNBbzkLEzfgLvXQ/Xf691yLY7tXzuV6w/6HMoRkcqMEfV5Oflf7tZWHbiGiLJ2DpV\n\tK3frAaEtmvHvATlFBsU1GrCCF7IqhoREIE/2b/H3YoUjAqd+WdLQ9LPK+VmRQvf+taHH\n\tfH+lUEB5EA7W2lFV3wvMUrcCRSihS6SZ3kwq/gedkRNm9bTozXPQ3K7C5uv1m/34Oau+\n\tTmK/DOCAN96Hn6f1kBRZEHB49Zak6E5aYx6qzBW/kUbsy7t59hmqT2mzUMKU+RMayUK1\n\tER0t9PM9ATIocTbtF/7C1iimVyvj+7SabGNw37PBSUCIbomkblxTjfz0iZF9yaEIGrPo\n\t/MTA==","X-Gm-Message-State":"AOAM533bk1SHBgaemUIWm0BCJQlB452XJacyOxRegLQ0lWPtvxhkH9nf\n\tEMJMk1Ma5jJOXSU6A5Sb6sI0hmejaFa6ZT+71d0=","X-Google-Smtp-Source":"ABdhPJyvSJjyAy6f1bs2PBFoeXSb2ddSxxC/RqCoBG27yLiQ6/Y3N8dRHxt+MkSyhBKqx7G0SgkFCg5cERsLti7SuxE=","X-Received":"by 2002:a25:ce01:: with SMTP id x1mr7133158ybe.432.1629124043094;\n\tMon, 16 Aug 2021 07:27:23 -0700 (PDT)","MIME-Version":"1.0","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>\n\t<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>\n\t<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>\n\t<b69307d5-d4b8-8d96-4169-cffff78ac349@ideasonboard.com>","In-Reply-To":"<b69307d5-d4b8-8d96-4169-cffff78ac349@ideasonboard.com>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Mon, 16 Aug 2021 19:57:11 +0530","Message-ID":"<CACGrz-Ow3rOp8_R_nWs1dfdJmC4bADh0So9JqCMXphLV7d0jiw@mail.gmail.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000e23de905c9adff3e\"","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18852,"web_url":"https://patchwork.libcamera.org/comment/18852/","msgid":"<YRqSq5e8W7qjqJ4t@pendragon.ideasonboard.com>","date":"2021-08-16T16:30:35","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Vedant,\n\nOn Mon, Aug 16, 2021 at 07:44:34PM +0530, Vedant Paranjape wrote:\n> Hi Laurent,\n> Yes, what's the camera-name with which vimc camera can be selected ?\n\nYou can find it with 'cam -l' of you have the vimc driver loaded :-)\nIt's the value between parentheses.\n\n> I could simply set the object property to use it. I think it would be nice\n> to have two separate tests, one which runs on real camera and other one on\n> vimc camera.\n> \n> On Mon, Aug 16, 2021 at 7:29 PM Laurent Pinchart wrote:\n> > On Mon, Aug 16, 2021 at 07:04:00PM +0530, Vedant Paranjape wrote:\n> > > Hi Umang,\n> > > Thanks for the Review. I feel the changes are appropriate. I will send a\n> > > patch with the changes.\n> >\n> > While at it, I'm wondering if it would make sense to select the vimc\n> > camera explicitly from the libcamerasrc element instead of using\n> > whatever camera happens to be the first. It would make tests more\n> > reproducible.\n> >\n> > > On Mon, Aug 16, 2021 at 11:17 AM Umang Jain wrote:\n> > >\n> > > > Hi Vedant and Laurent,\n> > > >\n> > > > Thanks for the patch. Overall looks good to me,\n> > > >\n> > > > a few comments below for cleanup paths :)\n> > > > On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> > > >\n> > > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > >\n> > > > This patch adds a test to test if single stream using libcamera's\n> > > > gstreamer element works.\n> > > >\n> > > > We need to work around two distinct issues with ASan when enabled in the\n> > > > build:\n> > > >\n> > > > - glib has a known leak at initialization time. This is covered by the\n> > > >   suppression file shipped with glib, but it's not clear how to use it\n> > > >   automatically. For now, disable leak detection to avoid test failures.\n> > > >\n> > > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > > >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> > > >   the libcamera plugin will cause an ASan link order verification\n> > > >   failure. Disable the verification child processes to work around the\n> > > >   problem. This requires gcc 8 or newer.\n> > > >\n> > > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > ---\n> > > > This version incorporates changes coming from my review of v10, and\n> > > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > > > independent reasons as explained in the commit message. I'd like to find\n> > > > a way to use leak suppression files to handle the glib initialization\n> > > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > > is acceptable in my opinion.\n> > > >\n> > > > The biggest trouble with these workarounds is that they don't work with\n> > > > gcc version older than 8. As the stable version of the most common Linux\n> > > > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > > > is the oldest version that libcamera supports) is also acceptable in my\n> > > > opinion.\n> > > >\n> > > > Changes since v10:\n> > > >\n> > > > - Disable ASan leak detection\n> > > > - Disable ASan link order verification for child processes\n> > > > - Include source_path.h\n> > > > - Remove unneeded explicit std::string construction\n> > > > - Declare variables at usage site\n> > > > - Make constants constexpr\n> > > > - Add a variable for the message type\n> > > > - Blank space fixes\n> > > > ---\n> > > >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> > > >  test/gstreamer/meson.build                    |  19 ++\n> > > >  test/meson.build                              |   1 +\n> > > >  3 files changed, 208 insertions(+)\n> > > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > > >  create mode 100644 test/gstreamer/meson.build\n> > > >\n> > > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > new file mode 100644\n> > > > index 000000000000..e26673b3471a\n> > > > --- /dev/null\n> > > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > @@ -0,0 +1,188 @@\n> > > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > > +/*\n> > > > + * Copyright (C) 2021, Vedant Paranjape\n> > > > + *\n> > > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > > + */\n> > > > +\n> > > > +#include <iostream>\n> > > > +#include <unistd.h>\n> > > > +\n> > > > +#include <libcamera/base/utils.h>\n> > > > +\n> > > > +#include \"libcamera/internal/source_paths.h\"\n> > > > +\n> > > > +#include <gst/gst.h>\n> > > > +\n> > > > +#include \"test.h\"\n> > > > +\n> > > > +using namespace std;\n> > > > +\n> > > > +extern \"C\" {\n> > > > +const char *__asan_default_options()\n> > > > +{\n> > > > +   /*\n> > > > +    * Disable leak detection due to a known global variable initialization\n> > > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > > +    * using a suppression file instead of disabling leak detection.\n> > > > +    */\n> > > > +   return \"detect_leaks=false\";\n> > > > +}\n> > > > +}\n> > > > +\n> > > > +class GstreamerSingleStreamTest : public Test\n> > > > +{\n> > > > +protected:\n> > > > +   int init() override\n> > > > +   {\n> > > > +           /*\n> > > > +            * GStreamer spawns a process to run the gst-plugin-scanner\n> > > > +            * helper. If libcamera is compiled with ASan enabled, and as\n> > > > +            * GStreamer is most likely not, this will cause the ASan link\n> > > > +            * order check to fail when gst-plugin-scanner dlopen()s the\n> > > > +            * plugin as many libraries will have already been loaded by\n> > > > +            * then. Work around this issue by disabling the link order\n> > > > +            * check. This will only affect child processes, as ASan is\n> > > > +            * already loaded for this process by the time this code is\n> > > > +            * executed, and should thus hopefully be safe.\n> > > > +            *\n> > > > +            * This option is not available in gcc older than 8, the only\n> > > > +            * option in that case is to skip the test.\n> > > > +            */\n> > > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> > > > +           return TestSkip;\n> > > > +#endif\n> > > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > > > +\n> > > > +           /* Initialize GStreamer */\n> > > > +           GError *errInit = nullptr;\n> > > >\n> > > > One can use:\n> > > >\n> > > >     g_autoptr(GError) errInit = NULL;\n> > > >\n> > > > ...\n> > > >\n> > > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > > +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > > > +                              errInit ? errInit->message : \"unknown error\");\n> > > > +                   if (errInit)\n> > > > +                           g_error_free(errInit);\n> > > >\n> > > > And remove manual free here.\n> > > >\n> > > > +\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /*\n> > > > +            * Remove the system libcamera plugin, if any, and add the\n> > > > +            * plugin from the build directory.\n> > > > +            */\n> > > > +           GstRegistry *registry = gst_registry_get();\n> > > > +           GstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > > > +           if (plugin) {\n> > > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > > +                   gst_object_unref(plugin);\n> > > > +           }\n> > > > +\n> > > > +           std::string path = libcamera::utils::libcameraBuildPath()\n> > > > +                            + \"src/gstreamer\";\n> > > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > > +                   g_printerr(\"Failed to add plugin to registry\\n\");\n> > > > +                   gst_deinit();\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /* Create the elements */\n> > > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > > > +           convert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > > > +\n> > > > +           /* Create the empty pipeline_ */\n> > > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > > +\n> > > > +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> > > > +                   g_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > > > +                              pipeline_, convert0_, sink0_, libcameraSrc_);\n> > > > +                   if (pipeline_)\n> > > > +                           gst_object_unref(pipeline_);\n> > > > +                   if (convert0_)\n> > > > +                           gst_object_unref(convert0_);\n> > > > +                   if (sink0_)\n> > > > +                           gst_object_unref(sink0_);\n> > > > +                   if (libcameraSrc_)\n> > > > +                           gst_object_unref(libcameraSrc_);\n> > > > +                   gst_deinit();\n> > > > +\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           return TestPass;\n> > > > +   }\n> > > > +\n> > > > +   void cleanup() override\n> > > > +   {\n> > > > +           gst_object_unref(pipeline_);\n> > > > +           gst_deinit();\n> > > > +   }\n> > > > +\n> > > > +   int run() override\n> > > > +   {\n> > > > +           GstStateChangeReturn ret;\n> > > > +\n> > > > +           /* Build the pipeline */\n> > > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> > > > +           if (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /* Start playing */\n> > > > +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > > +                   g_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > > +           constexpr GstMessageType msgType =\n> > > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > > > +           constexpr GstClockTime timeout = 2000000000;\n> > > > +\n> > > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > > +           g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > > +\n> > > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > > +\n> > > > +           /* Parse error message */\n> > > > +           if (msg == NULL)\n> > > > +                   return TestPass;\n> > > > +\n> > > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > > +           case GST_MESSAGE_ERROR:\n> > > > +                   gstreamer_print_error(msg);\n> > > > +                   break;\n> > > > +           case GST_MESSAGE_EOS:\n> > > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > >\n> > > > EOS doesn't sound like a error/Test-fail condition to me. If there is a\n> > > > possibility that EOS will be reached before timeout, what's ensures(in the\n> > > > first place) that the pipeline will be in playing state for entire timeout,\n> > > > before we request masked EOS/Error messages from the bus? What am I missing?\n> > >\n> > > I am not a gstreamer expert, to the best of my knowledge, EOS won't be ever\n> > > sent by libcamerasrc, as it is the element which drives the pipeline. I\n> > > think this event can be dropped, I just want to confirm it with Nicolas\n> > > once he's back.\n> > >\n> > > > It is important to note that *only elements driving the pipeline should\n> > > ever send an EOS event*. If your element is chain-based, it is not driving\n> > > the pipeline. Chain-based elements should just return GST_FLOW_EOS from\n> > > their chain function at the end of the stream (or the configured segment)\n> > >\n> > > https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n> > >\n> > > +                     break;\n> > > > +           default:\n> > > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > > +                   break;\n> > > > +           }\n> > > > +\n> > > > +           return TestFail;\n> > > > +   }\n> > > > +\n> > > > +private:\n> > > > +   void gstreamer_print_error(GstMessage *msg)\n> > > > +   {\n> > > > +           GError *err;\n> > > >\n> > > > This can use:\n> > > >\n> > > >     g_autoptr(GError) err = NULL;\n> > > >\n> > > > +           gchar *debug_info;\n> > > >\n> > > > and\n> > > >\n> > > >      g_autofree char *debug_info = NULL;\n> > > >\n> > > > +\n> > > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > > +                      debug_info ? debug_info : \"none\");\n> > > > +           g_clear_error(&err);\n> > > > +           g_free(debug_info);\n> > > >\n> > > > So, that will lead us to dropping manual free of err and debug info\n> > > >\n> > > >\n> > > > Minor things, so:\n> > > >\n> > > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> > > >\n> > > > +   }\n> > > > +\n> > > > +   GstElement *pipeline_;\n> > > > +   GstElement *libcameraSrc_;\n> > > > +   GstElement *convert0_;\n> > > > +   GstElement *sink0_;\n> > > > +};\n> > > > +\n> > > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > > new file mode 100644\n> > > > index 000000000000..b99aa0da0ba3\n> > > > --- /dev/null\n> > > > +++ b/test/gstreamer/meson.build\n> > > > @@ -0,0 +1,19 @@\n> > > > +# SPDX-License-Identifier: CC0-1.0\n> > > > +\n> > > > +if not gst_enabled\n> > > > +    subdir_done()\n> > > > +endif\n> > > > +\n> > > > +gstreamer_tests = [\n> > > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > > +]\n> > > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > > +\n> > > > +foreach t : gstreamer_tests\n> > > > +    exe = executable(t[0], t[1],\n> > > > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > > > +                     link_with : test_libraries,\n> > > > +                     include_directories : test_includes_internal)\n> > > > +\n> > > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > > +endforeach\n> > > > diff --git a/test/meson.build b/test/meson.build\n> > > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > > --- a/test/meson.build\n> > > > +++ b/test/meson.build\n> > > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > > >\n> > > >  subdir('camera')\n> > > >  subdir('controls')\n> > > > +subdir('gstreamer')\n> > > >  subdir('ipa')\n> > > >  subdir('ipc')\n> > > >  subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 785B2BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 16:30:44 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E9B4368895;\n\tMon, 16 Aug 2021 18:30:43 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B08B96025D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 18:30:41 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2596E3E5;\n\tMon, 16 Aug 2021 18:30:41 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"g/3lP0Ij\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629131441;\n\tbh=7GgQpiWjV2dXUz99m8GDMgL6hiSAcqzQE04hMsEoHRg=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=g/3lP0IjwpJ4fIOzxFarlBSRAwfFLSLzQX3Lwz6plO9lCEoRzm3jIFnWNYNRpI49t\n\twvoZrJzE3OChsP/GwUH7v/pvKGMuQqswDsgpOMOWgo6nAlRiTSpJWe2LSaelmqXuev\n\t+bUvyr4qYrWAFe+kSjFzl8C9nQRgGj97syiebMQM=","Date":"Mon, 16 Aug 2021 19:30:35 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Message-ID":"<YRqSq5e8W7qjqJ4t@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>\n\t<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>\n\t<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18853,"web_url":"https://patchwork.libcamera.org/comment/18853/","msgid":"<YRqTsIGu+klv1gJs@pendragon.ideasonboard.com>","date":"2021-08-16T16:34:56","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Mon, Aug 16, 2021 at 03:22:29PM +0100, Kieran Bingham wrote:\n> On 16/08/2021 15:14, Vedant Paranjape wrote:\n> > Hi Laurent,\n> > Yes, what's the camera-name with which vimc camera can be selected ?\n> > I could simply set the object property to use it. I think it would be\n> > nice to have two separate tests, one which runs on real camera and other\n> > one on vimc camera.\n> \n> Or runs on all available cameras?\n\nI'm in two minds about this. We don't run all our other tests against\nall cameras, and instead consider that pipeline handler testing is the\njob of lc-compliance. For the GStreamer element, it could be different\nif we wanted to unit-test features that don't work with vimc, but for\nthe single stream test, I'm not sure testing all cameras would be the\nbest option.\n\n> > On Mon, Aug 16, 2021 at 7:29 PM Laurent Pinchart wrote:\n> > > On Mon, Aug 16, 2021 at 07:04:00PM +0530, Vedant Paranjape wrote:\n> > > > Hi Umang,\n> > > > Thanks for the Review. I feel the changes are appropriate. I will send a\n> > > > patch with the changes.\n> > >\n> > > While at it, I'm wondering if it would make sense to select the\n> > > vimc camera explicitly from the libcamerasrc element instead of\n> > > using whatever camera happens to be the first. It would make tests\n> > > more reproducible.\n> > >\n> > > > On Mon, Aug 16, 2021 at 11:17 AM Umang Jain wrote:\n> > > >\n> > > > > Hi Vedant and Laurent,\n> > > > >\n> > > > > Thanks for the patch. Overall looks good to me,\n> > > > >\n> > > > > a few comments below for cleanup paths :)\n> > > > > On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n> > > > >\n> > > > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > >\n> > > > > This patch adds a test to test if single stream using libcamera's\n> > > > > gstreamer element works.\n> > > > >\n> > > > > We need to work around two distinct issues with ASan when enabled in the\n> > > > > build:\n> > > > >\n> > > > > - glib has a known leak at initialization time. This is covered by the\n> > > > >   suppression file shipped with glib, but it's not clear how to use it\n> > > > >   automatically. For now, disable leak detection to avoid test failures.\n> > > > >\n> > > > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > > > >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> > > > >   the libcamera plugin will cause an ASan link order verification\n> > > > >   failure. Disable the verification child processes to work around the\n> > > > >   problem. This requires gcc 8 or newer.\n> > > > >\n> > > > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > ---\n> > > > > This version incorporates changes coming from my review of v10, and\n> > > > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > > > > independent reasons as explained in the commit message. I'd like to find\n> > > > > a way to use leak suppression files to handle the glib initialization\n> > > > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > > > is acceptable in my opinion.\n> > > > >\n> > > > > The biggest trouble with these workarounds is that they don't work with\n> > > > > gcc version older than 8. As the stable version of the most common Linux\n> > > > > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > > > > is the oldest version that libcamera supports) is also acceptable in my\n> > > > > opinion.\n> > > > >\n> > > > > Changes since v10:\n> > > > >\n> > > > > - Disable ASan leak detection\n> > > > > - Disable ASan link order verification for child processes\n> > > > > - Include source_path.h\n> > > > > - Remove unneeded explicit std::string construction\n> > > > > - Declare variables at usage site\n> > > > > - Make constants constexpr\n> > > > > - Add a variable for the message type\n> > > > > - Blank space fixes\n> > > > > ---\n> > > > >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> > > > >  test/gstreamer/meson.build                    |  19 ++\n> > > > >  test/meson.build                              |   1 +\n> > > > >  3 files changed, 208 insertions(+)\n> > > > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > >  create mode 100644 test/gstreamer/meson.build\n> > > > >\n> > > > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > new file mode 100644\n> > > > > index 000000000000..e26673b3471a\n> > > > > --- /dev/null\n> > > > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > @@ -0,0 +1,188 @@\n> > > > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > > > +/*\n> > > > > + * Copyright (C) 2021, Vedant Paranjape\n> > > > > + *\n> > > > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > > > + */\n> > > > > +\n> > > > > +#include <iostream>\n> > > > > +#include <unistd.h>\n> > > > > +\n> > > > > +#include <libcamera/base/utils.h>\n> > > > > +\n> > > > > +#include \"libcamera/internal/source_paths.h\"\n> > > > > +\n> > > > > +#include <gst/gst.h>\n> > > > > +\n> > > > > +#include \"test.h\"\n> > > > > +\n> > > > > +using namespace std;\n> > > > > +\n> > > > > +extern \"C\" {\n> > > > > +const char *__asan_default_options()\n> > > > > +{\n> > > > > +   /*\n> > > > > +    * Disable leak detection due to a known global variable initialization\n> > > > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > > > +    * using a suppression file instead of disabling leak detection.\n> > > > > +    */\n> > > > > +   return \"detect_leaks=false\";\n> > > > > +}\n> > > > > +}\n> > > > > +\n> > > > > +class GstreamerSingleStreamTest : public Test\n> > > > > +{\n> > > > > +protected:\n> > > > > +   int init() override\n> > > > > +   {\n> > > > > +           /*\n> > > > > +            * GStreamer spawns a process to run the gst-plugin-scanner\n> > > > > +            * helper. If libcamera is compiled with ASan enabled, and as\n> > > > > +            * GStreamer is most likely not, this will cause the ASan link\n> > > > > +            * order check to fail when gst-plugin-scanner dlopen()s the\n> > > > > +            * plugin as many libraries will have already been loaded by\n> > > > > +            * then. Work around this issue by disabling the link order\n> > > > > +            * check. This will only affect child processes, as ASan is\n> > > > > +            * already loaded for this process by the time this code is\n> > > > > +            * executed, and should thus hopefully be safe.\n> > > > > +            *\n> > > > > +            * This option is not available in gcc older than 8, the only\n> > > > > +            * option in that case is to skip the test.\n> > > > > +            */\n> > > > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> > > > > +           return TestSkip;\n> > > > > +#endif\n> > > > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > > > > +\n> > > > > +           /* Initialize GStreamer */\n> > > > > +           GError *errInit = nullptr;\n> > > > >\n> > > > > One can use:\n> > > > >\n> > > > >     g_autoptr(GError) errInit = NULL;\n> > > > >\n> > > > > ...\n> > > > >\n> > > > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > > > +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > > > > +                              errInit ? errInit->message : \"unknown error\");\n> > > > > +                   if (errInit)\n> > > > > +                           g_error_free(errInit);\n> > > > >\n> > > > > And remove manual free here.\n> > > > >\n> > > > > +\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /*\n> > > > > +            * Remove the system libcamera plugin, if any, and add the\n> > > > > +            * plugin from the build directory.\n> > > > > +            */\n> > > > > +           GstRegistry *registry = gst_registry_get();\n> > > > > +           GstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > > > > +           if (plugin) {\n> > > > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > > > +                   gst_object_unref(plugin);\n> > > > > +           }\n> > > > > +\n> > > > > +           std::string path = libcamera::utils::libcameraBuildPath()\n> > > > > +                            + \"src/gstreamer\";\n> > > > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > > > +                   g_printerr(\"Failed to add plugin to registry\\n\");\n> > > > > +                   gst_deinit();\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /* Create the elements */\n> > > > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > > > > +           convert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > > > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > > > > +\n> > > > > +           /* Create the empty pipeline_ */\n> > > > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > > > +\n> > > > > +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> > > > > +                   g_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > > > > +                              pipeline_, convert0_, sink0_, libcameraSrc_);\n> > > > > +                   if (pipeline_)\n> > > > > +                           gst_object_unref(pipeline_);\n> > > > > +                   if (convert0_)\n> > > > > +                           gst_object_unref(convert0_);\n> > > > > +                   if (sink0_)\n> > > > > +                           gst_object_unref(sink0_);\n> > > > > +                   if (libcameraSrc_)\n> > > > > +                           gst_object_unref(libcameraSrc_);\n> > > > > +                   gst_deinit();\n> > > > > +\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           return TestPass;\n> > > > > +   }\n> > > > > +\n> > > > > +   void cleanup() override\n> > > > > +   {\n> > > > > +           gst_object_unref(pipeline_);\n> > > > > +           gst_deinit();\n> > > > > +   }\n> > > > > +\n> > > > > +   int run() override\n> > > > > +   {\n> > > > > +           GstStateChangeReturn ret;\n> > > > > +\n> > > > > +           /* Build the pipeline */\n> > > > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> > > > > +           if (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > > > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /* Start playing */\n> > > > > +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > > > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > > > +                   g_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > > > +           constexpr GstMessageType msgType =\n> > > > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > > > > +           constexpr GstClockTime timeout = 2000000000;\n> > > > > +\n> > > > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > > > +           g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > > > +\n> > > > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > > > +\n> > > > > +           /* Parse error message */\n> > > > > +           if (msg == NULL)\n> > > > > +                   return TestPass;\n> > > > > +\n> > > > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > > > +           case GST_MESSAGE_ERROR:\n> > > > > +                   gstreamer_print_error(msg);\n> > > > > +                   break;\n> > > > > +           case GST_MESSAGE_EOS:\n> > > > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > > >\n> > > > > EOS doesn't sound like a error/Test-fail condition to me. If there is a\n> > > > > possibility that EOS will be reached before timeout, what's ensures(in the\n> > > > > first place) that the pipeline will be in playing state for entire timeout,\n> > > > > before we request masked EOS/Error messages from the bus? What am I missing?\n> > > >\n> > > > I am not a gstreamer expert, to the best of my knowledge, EOS won't be ever\n> > > > sent by libcamerasrc, as it is the element which drives the pipeline. I\n> > > > think this event can be dropped, I just want to confirm it with Nicolas\n> > > > once he's back.\n> > > >\n> > > > > It is important to note that *only elements driving the pipeline should\n> > > > ever send an EOS event*. If your element is chain-based, it is not driving\n> > > > the pipeline. Chain-based elements should just return GST_FLOW_EOS from\n> > > > their chain function at the end of the stream (or the configured segment)\n> > > >\n> > > > https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n> > > >\n> > > > +                     break;\n> > > > > +           default:\n> > > > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > > > +                   break;\n> > > > > +           }\n> > > > > +\n> > > > > +           return TestFail;\n> > > > > +   }\n> > > > > +\n> > > > > +private:\n> > > > > +   void gstreamer_print_error(GstMessage *msg)\n> > > > > +   {\n> > > > > +           GError *err;\n> > > > >\n> > > > > This can use:\n> > > > >\n> > > > >     g_autoptr(GError) err = NULL;\n> > > > >\n> > > > > +           gchar *debug_info;\n> > > > >\n> > > > > and\n> > > > >\n> > > > >      g_autofree char *debug_info = NULL;\n> > > > >\n> > > > > +\n> > > > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > > > +                      debug_info ? debug_info : \"none\");\n> > > > > +           g_clear_error(&err);\n> > > > > +           g_free(debug_info);\n> > > > >\n> > > > > So, that will lead us to dropping manual free of err and debug info\n> > > > >\n> > > > >\n> > > > > Minor things, so:\n> > > > >\n> > > > > Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n> > > > >\n> > > > > +   }\n> > > > > +\n> > > > > +   GstElement *pipeline_;\n> > > > > +   GstElement *libcameraSrc_;\n> > > > > +   GstElement *convert0_;\n> > > > > +   GstElement *sink0_;\n> > > > > +};\n> > > > > +\n> > > > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > > > new file mode 100644\n> > > > > index 000000000000..b99aa0da0ba3\n> > > > > --- /dev/null\n> > > > > +++ b/test/gstreamer/meson.build\n> > > > > @@ -0,0 +1,19 @@\n> > > > > +# SPDX-License-Identifier: CC0-1.0\n> > > > > +\n> > > > > +if not gst_enabled\n> > > > > +    subdir_done()\n> > > > > +endif\n> > > > > +\n> > > > > +gstreamer_tests = [\n> > > > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > > > +]\n> > > > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > > > +\n> > > > > +foreach t : gstreamer_tests\n> > > > > +    exe = executable(t[0], t[1],\n> > > > > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > > > > +                     link_with : test_libraries,\n> > > > > +                     include_directories : test_includes_internal)\n> > > > > +\n> > > > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > > > +endforeach\n> > > > > diff --git a/test/meson.build b/test/meson.build\n> > > > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > > > --- a/test/meson.build\n> > > > > +++ b/test/meson.build\n> > > > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > > > >\n> > > > >  subdir('camera')\n> > > > >  subdir('controls')\n> > > > > +subdir('gstreamer')\n> > > > >  subdir('ipa')\n> > > > >  subdir('ipc')\n> > > > >  subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 7F5AABD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 16:35:04 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 08FE468892;\n\tMon, 16 Aug 2021 18:35:04 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9451168891\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 18:35:02 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0F5473E5;\n\tMon, 16 Aug 2021 18:35:01 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ABWZkAlQ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629131702;\n\tbh=WRMiXjlMCK0/4eUkz4gODryJdMGXtLULtC+WTtAj1FY=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=ABWZkAlQ4KljMu6tJn+nfOKYedeNidvNQlTLSrOxPHBiMybOCRhBL14la8ZkbqvRd\n\tc5tndG+LJIVqv/aoylqRdL9E+0RSszeBh1leFCR3IfL2VoeVVHvkmrRoYlifuKCgND\n\tM6Vpp9X1dHjXRV0DN9dSrpnszmKNyoKiix54g+xY=","Date":"Mon, 16 Aug 2021 19:34:56 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YRqTsIGu+klv1gJs@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>\n\t<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>\n\t<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>\n\t<b69307d5-d4b8-8d96-4169-cffff78ac349@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<b69307d5-d4b8-8d96-4169-cffff78ac349@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org,\n\tVedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18854,"web_url":"https://patchwork.libcamera.org/comment/18854/","msgid":"<e2977859-c027-ade1-5ebc-acc460a2c3e3@ideasonboard.com>","date":"2021-08-16T19:43:53","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 16/08/2021 17:34, Laurent Pinchart wrote:\n> On Mon, Aug 16, 2021 at 03:22:29PM +0100, Kieran Bingham wrote:\n>> On 16/08/2021 15:14, Vedant Paranjape wrote:\n>>> Hi Laurent,\n>>> Yes, what's the camera-name with which vimc camera can be selected ?\n>>> I could simply set the object property to use it. I think it would be\n>>> nice to have two separate tests, one which runs on real camera and other\n>>> one on vimc camera.\n>>\n>> Or runs on all available cameras?\n> \n> I'm in two minds about this. We don't run all our other tests against\n> all cameras, and instead consider that pipeline handler testing is the\n> job of lc-compliance. For the GStreamer element, it could be different\n> if we wanted to unit-test features that don't work with vimc, but for\n> the single stream test, I'm not sure testing all cameras would be the\n> best option.\n\nActually, I agree - for consistancy and reproducability of the unit\ntests, I think fixing to VIMC is better.\n\nI believe we have defined VIMC as a requirement for the unit tests, if\nnot explicitly - then certainly defined by code already.\n\nUsing VIMC makes the most sense.\n\n\nI do think there is some benefit in some test being able to run on 'all\ncameras' though, but that's more about being able to test multiple\ncameras - which would then /require/ multiple cameras to run the test\n... so - lets not worry about that for now ;-)\n\n--\nKieran\n\n\n>>> On Mon, Aug 16, 2021 at 7:29 PM Laurent Pinchart wrote:\n>>>> On Mon, Aug 16, 2021 at 07:04:00PM +0530, Vedant Paranjape wrote:\n>>>>> Hi Umang,\n>>>>> Thanks for the Review. I feel the changes are appropriate. I will send a\n>>>>> patch with the changes.\n>>>>\n>>>> While at it, I'm wondering if it would make sense to select the\n>>>> vimc camera explicitly from the libcamerasrc element instead of\n>>>> using whatever camera happens to be the first. It would make tests\n>>>> more reproducible.\n>>>>\n>>>>> On Mon, Aug 16, 2021 at 11:17 AM Umang Jain wrote:\n>>>>>\n>>>>>> Hi Vedant and Laurent,\n>>>>>>\n>>>>>> Thanks for the patch. Overall looks good to me,\n>>>>>>\n>>>>>> a few comments below for cleanup paths :)\n>>>>>> On 8/14/21 5:16 AM, Laurent Pinchart wrote:\n>>>>>>\n>>>>>> From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n>>>>>>\n>>>>>> This patch adds a test to test if single stream using libcamera's\n>>>>>> gstreamer element works.\n>>>>>>\n>>>>>> We need to work around two distinct issues with ASan when enabled in the\n>>>>>> build:\n>>>>>>\n>>>>>> - glib has a known leak at initialization time. This is covered by the\n>>>>>>    suppression file shipped with glib, but it's not clear how to use it\n>>>>>>    automatically. For now, disable leak detection to avoid test failures.\n>>>>>>\n>>>>>> - GStreamer spawns a child process to scan plugins. If GStreamer is\n>>>>>>    compiled without ASan (which is likely) but libcamera is, dlopen()ing\n>>>>>>    the libcamera plugin will cause an ASan link order verification\n>>>>>>    failure. Disable the verification child processes to work around the\n>>>>>>    problem. This requires gcc 8 or newer.\n>>>>>>\n>>>>>> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n>>>>>> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n>>>>>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>>>>>> Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>>>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n>>>>>> ---\n>>>>>> This version incorporates changes coming from my review of v10, and\n>>>>>> fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n>>>>>> independent reasons as explained in the commit message. I'd like to find\n>>>>>> a way to use leak suppression files to handle the glib initialization\n>>>>>> leak, but that's a big rabbit hole. The workaround for the second issue\n>>>>>> is acceptable in my opinion.\n>>>>>>\n>>>>>> The biggest trouble with these workarounds is that they don't work with\n>>>>>> gcc version older than 8. As the stable version of the most common Linux\n>>>>>> distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n>>>>>> is the oldest version that libcamera supports) is also acceptable in my\n>>>>>> opinion.\n>>>>>>\n>>>>>> Changes since v10:\n>>>>>>\n>>>>>> - Disable ASan leak detection\n>>>>>> - Disable ASan link order verification for child processes\n>>>>>> - Include source_path.h\n>>>>>> - Remove unneeded explicit std::string construction\n>>>>>> - Declare variables at usage site\n>>>>>> - Make constants constexpr\n>>>>>> - Add a variable for the message type\n>>>>>> - Blank space fixes\n>>>>>> ---\n>>>>>>   .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n>>>>>>   test/gstreamer/meson.build                    |  19 ++\n>>>>>>   test/meson.build                              |   1 +\n>>>>>>   3 files changed, 208 insertions(+)\n>>>>>>   create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n>>>>>>   create mode 100644 test/gstreamer/meson.build\n>>>>>>\n>>>>>> diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n>>>>>> new file mode 100644\n>>>>>> index 000000000000..e26673b3471a\n>>>>>> --- /dev/null\n>>>>>> +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n>>>>>> @@ -0,0 +1,188 @@\n>>>>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n>>>>>> +/*\n>>>>>> + * Copyright (C) 2021, Vedant Paranjape\n>>>>>> + *\n>>>>>> + * ipa_interface_test.cpp - Test the IPA interface\n>>>>>> + */\n>>>>>> +\n>>>>>> +#include <iostream>\n>>>>>> +#include <unistd.h>\n>>>>>> +\n>>>>>> +#include <libcamera/base/utils.h>\n>>>>>> +\n>>>>>> +#include \"libcamera/internal/source_paths.h\"\n>>>>>> +\n>>>>>> +#include <gst/gst.h>\n>>>>>> +\n>>>>>> +#include \"test.h\"\n>>>>>> +\n>>>>>> +using namespace std;\n>>>>>> +\n>>>>>> +extern \"C\" {\n>>>>>> +const char *__asan_default_options()\n>>>>>> +{\n>>>>>> +   /*\n>>>>>> +    * Disable leak detection due to a known global variable initialization\n>>>>>> +    * leak in glib's g_quark_init(). This should ideally be handled by\n>>>>>> +    * using a suppression file instead of disabling leak detection.\n>>>>>> +    */\n>>>>>> +   return \"detect_leaks=false\";\n>>>>>> +}\n>>>>>> +}\n>>>>>> +\n>>>>>> +class GstreamerSingleStreamTest : public Test\n>>>>>> +{\n>>>>>> +protected:\n>>>>>> +   int init() override\n>>>>>> +   {\n>>>>>> +           /*\n>>>>>> +            * GStreamer spawns a process to run the gst-plugin-scanner\n>>>>>> +            * helper. If libcamera is compiled with ASan enabled, and as\n>>>>>> +            * GStreamer is most likely not, this will cause the ASan link\n>>>>>> +            * order check to fail when gst-plugin-scanner dlopen()s the\n>>>>>> +            * plugin as many libraries will have already been loaded by\n>>>>>> +            * then. Work around this issue by disabling the link order\n>>>>>> +            * check. This will only affect child processes, as ASan is\n>>>>>> +            * already loaded for this process by the time this code is\n>>>>>> +            * executed, and should thus hopefully be safe.\n>>>>>> +            *\n>>>>>> +            * This option is not available in gcc older than 8, the only\n>>>>>> +            * option in that case is to skip the test.\n>>>>>> +            */\n>>>>>> +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n>>>>>> +           return TestSkip;\n>>>>>> +#endif\n>>>>>> +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n>>>>>> +\n>>>>>> +           /* Initialize GStreamer */\n>>>>>> +           GError *errInit = nullptr;\n>>>>>>\n>>>>>> One can use:\n>>>>>>\n>>>>>>      g_autoptr(GError) errInit = NULL;\n>>>>>>\n>>>>>> ...\n>>>>>>\n>>>>>> +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n>>>>>> +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n>>>>>> +                              errInit ? errInit->message : \"unknown error\");\n>>>>>> +                   if (errInit)\n>>>>>> +                           g_error_free(errInit);\n>>>>>>\n>>>>>> And remove manual free here.\n>>>>>>\n>>>>>> +\n>>>>>> +                   return TestFail;\n>>>>>> +           }\n>>>>>> +\n>>>>>> +           /*\n>>>>>> +            * Remove the system libcamera plugin, if any, and add the\n>>>>>> +            * plugin from the build directory.\n>>>>>> +            */\n>>>>>> +           GstRegistry *registry = gst_registry_get();\n>>>>>> +           GstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n>>>>>> +           if (plugin) {\n>>>>>> +                   gst_registry_remove_plugin(registry, plugin);\n>>>>>> +                   gst_object_unref(plugin);\n>>>>>> +           }\n>>>>>> +\n>>>>>> +           std::string path = libcamera::utils::libcameraBuildPath()\n>>>>>> +                            + \"src/gstreamer\";\n>>>>>> +           if (!gst_registry_scan_path(registry, path.c_str())) {\n>>>>>> +                   g_printerr(\"Failed to add plugin to registry\\n\");\n>>>>>> +                   gst_deinit();\n>>>>>> +                   return TestFail;\n>>>>>> +           }\n>>>>>> +\n>>>>>> +           /* Create the elements */\n>>>>>> +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n>>>>>> +           convert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n>>>>>> +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n>>>>>> +\n>>>>>> +           /* Create the empty pipeline_ */\n>>>>>> +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n>>>>>> +\n>>>>>> +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n>>>>>> +                   g_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n>>>>>> +                              pipeline_, convert0_, sink0_, libcameraSrc_);\n>>>>>> +                   if (pipeline_)\n>>>>>> +                           gst_object_unref(pipeline_);\n>>>>>> +                   if (convert0_)\n>>>>>> +                           gst_object_unref(convert0_);\n>>>>>> +                   if (sink0_)\n>>>>>> +                           gst_object_unref(sink0_);\n>>>>>> +                   if (libcameraSrc_)\n>>>>>> +                           gst_object_unref(libcameraSrc_);\n>>>>>> +                   gst_deinit();\n>>>>>> +\n>>>>>> +                   return TestFail;\n>>>>>> +           }\n>>>>>> +\n>>>>>> +           return TestPass;\n>>>>>> +   }\n>>>>>> +\n>>>>>> +   void cleanup() override\n>>>>>> +   {\n>>>>>> +           gst_object_unref(pipeline_);\n>>>>>> +           gst_deinit();\n>>>>>> +   }\n>>>>>> +\n>>>>>> +   int run() override\n>>>>>> +   {\n>>>>>> +           GstStateChangeReturn ret;\n>>>>>> +\n>>>>>> +           /* Build the pipeline */\n>>>>>> +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n>>>>>> +           if (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n>>>>>> +                   g_printerr(\"Elements could not be linked.\\n\");\n>>>>>> +                   return TestFail;\n>>>>>> +           }\n>>>>>> +\n>>>>>> +           /* Start playing */\n>>>>>> +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n>>>>>> +           if (ret == GST_STATE_CHANGE_FAILURE) {\n>>>>>> +                   g_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n>>>>>> +                   return TestFail;\n>>>>>> +           }\n>>>>>> +\n>>>>>> +           /* Wait until error or EOS or timeout after 2 seconds */\n>>>>>> +           constexpr GstMessageType msgType =\n>>>>>> +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n>>>>>> +           constexpr GstClockTime timeout = 2000000000;\n>>>>>> +\n>>>>>> +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n>>>>>> +           g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n>>>>>> +\n>>>>>> +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n>>>>>> +\n>>>>>> +           /* Parse error message */\n>>>>>> +           if (msg == NULL)\n>>>>>> +                   return TestPass;\n>>>>>> +\n>>>>>> +           switch (GST_MESSAGE_TYPE(msg)) {\n>>>>>> +           case GST_MESSAGE_ERROR:\n>>>>>> +                   gstreamer_print_error(msg);\n>>>>>> +                   break;\n>>>>>> +           case GST_MESSAGE_EOS:\n>>>>>> +                   g_print(\"End-Of-Stream reached.\\n\");\n>>>>>>\n>>>>>> EOS doesn't sound like a error/Test-fail condition to me. If there is a\n>>>>>> possibility that EOS will be reached before timeout, what's ensures(in the\n>>>>>> first place) that the pipeline will be in playing state for entire timeout,\n>>>>>> before we request masked EOS/Error messages from the bus? What am I missing?\n>>>>>\n>>>>> I am not a gstreamer expert, to the best of my knowledge, EOS won't be ever\n>>>>> sent by libcamerasrc, as it is the element which drives the pipeline. I\n>>>>> think this event can be dropped, I just want to confirm it with Nicolas\n>>>>> once he's back.\n>>>>>\n>>>>>> It is important to note that *only elements driving the pipeline should\n>>>>> ever send an EOS event*. If your element is chain-based, it is not driving\n>>>>> the pipeline. Chain-based elements should just return GST_FLOW_EOS from\n>>>>> their chain function at the end of the stream (or the configured segment)\n>>>>>\n>>>>> https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/events.html?gi-language=c#end-of-stream-eos\n>>>>>\n>>>>> +                     break;\n>>>>>> +           default:\n>>>>>> +                   g_printerr(\"Unexpected message received.\\n\");\n>>>>>> +                   break;\n>>>>>> +           }\n>>>>>> +\n>>>>>> +           return TestFail;\n>>>>>> +   }\n>>>>>> +\n>>>>>> +private:\n>>>>>> +   void gstreamer_print_error(GstMessage *msg)\n>>>>>> +   {\n>>>>>> +           GError *err;\n>>>>>>\n>>>>>> This can use:\n>>>>>>\n>>>>>>      g_autoptr(GError) err = NULL;\n>>>>>>\n>>>>>> +           gchar *debug_info;\n>>>>>>\n>>>>>> and\n>>>>>>\n>>>>>>       g_autofree char *debug_info = NULL;\n>>>>>>\n>>>>>> +\n>>>>>> +           gst_message_parse_error(msg, &err, &debug_info);\n>>>>>> +           g_printerr(\"Error received from element %s: %s\\n\",\n>>>>>> +                      GST_OBJECT_NAME(msg->src), err->message);\n>>>>>> +           g_printerr(\"Debugging information: %s\\n\",\n>>>>>> +                      debug_info ? debug_info : \"none\");\n>>>>>> +           g_clear_error(&err);\n>>>>>> +           g_free(debug_info);\n>>>>>>\n>>>>>> So, that will lead us to dropping manual free of err and debug info\n>>>>>>\n>>>>>>\n>>>>>> Minor things, so:\n>>>>>>\n>>>>>> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>\n>>>>>>\n>>>>>> +   }\n>>>>>> +\n>>>>>> +   GstElement *pipeline_;\n>>>>>> +   GstElement *libcameraSrc_;\n>>>>>> +   GstElement *convert0_;\n>>>>>> +   GstElement *sink0_;\n>>>>>> +};\n>>>>>> +\n>>>>>> +TEST_REGISTER(GstreamerSingleStreamTest)\n>>>>>> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n>>>>>> new file mode 100644\n>>>>>> index 000000000000..b99aa0da0ba3\n>>>>>> --- /dev/null\n>>>>>> +++ b/test/gstreamer/meson.build\n>>>>>> @@ -0,0 +1,19 @@\n>>>>>> +# SPDX-License-Identifier: CC0-1.0\n>>>>>> +\n>>>>>> +if not gst_enabled\n>>>>>> +    subdir_done()\n>>>>>> +endif\n>>>>>> +\n>>>>>> +gstreamer_tests = [\n>>>>>> +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n>>>>>> +]\n>>>>>> +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n>>>>>> +\n>>>>>> +foreach t : gstreamer_tests\n>>>>>> +    exe = executable(t[0], t[1],\n>>>>>> +                     dependencies : [libcamera_private, gstreamer_dep],\n>>>>>> +                     link_with : test_libraries,\n>>>>>> +                     include_directories : test_includes_internal)\n>>>>>> +\n>>>>>> +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n>>>>>> +endforeach\n>>>>>> diff --git a/test/meson.build b/test/meson.build\n>>>>>> index 3bceb5df586f..d0466f17d7b6 100644\n>>>>>> --- a/test/meson.build\n>>>>>> +++ b/test/meson.build\n>>>>>> @@ -11,6 +11,7 @@ subdir('libtest')\n>>>>>>\n>>>>>>   subdir('camera')\n>>>>>>   subdir('controls')\n>>>>>> +subdir('gstreamer')\n>>>>>>   subdir('ipa')\n>>>>>>   subdir('ipc')\n>>>>>>   subdir('log')\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CE24FBD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 16 Aug 2021 19:44:00 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3DC7568893;\n\tMon, 16 Aug 2021 21:44:00 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C64206025D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 16 Aug 2021 21:43:58 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 076E83E5;\n\tMon, 16 Aug 2021 21:43:57 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"h2jxhVrV\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629143038;\n\tbh=w8cMpZ09kqNsUzklgJqQyHLYgSfcituYuDPW80oztuE=;\n\th=From:Subject:To:Cc:References:Date:In-Reply-To:From;\n\tb=h2jxhVrV0dSJsiVgtLtfPruNNsDUG+AElwKd0Z8poJedLUidq2fVZkR7PEifbtZHW\n\t6evYHo2CSNMmqLR04TnLytAwP6sqCwRLHm9nWyFzAGOhzgrFEB8xdi5FZA1kCzr9+3\n\tOxPW+jq4vxNAvKP8YIFxBooltf3CNc+oCugHqXbM=","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<93fb2e59-351e-113e-7f70-d6793651e87c@ideasonboard.com>\n\t<CACGrz-OgD5NXzW2rSBhmx+fwu632Rkzwf_HfLf82G6inpHxY7w@mail.gmail.com>\n\t<YRpvHpgL1PjFtJ78@pendragon.ideasonboard.com>\n\t<CACGrz-PSnPyaOQX3+fDOCJT+BJfvje7GU3ha+Fvi0B+PnsnLmQ@mail.gmail.com>\n\t<b69307d5-d4b8-8d96-4169-cffff78ac349@ideasonboard.com>\n\t<YRqTsIGu+klv1gJs@pendragon.ideasonboard.com>","Message-ID":"<e2977859-c027-ade1-5ebc-acc460a2c3e3@ideasonboard.com>","Date":"Mon, 16 Aug 2021 20:43:53 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101\n\tThunderbird/78.11.0","MIME-Version":"1.0","In-Reply-To":"<YRqTsIGu+klv1gJs@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org,\n\tVedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18875,"web_url":"https://patchwork.libcamera.org/comment/18875/","msgid":"<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>","date":"2021-08-17T16:24:05","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> \n> This patch adds a test to test if single stream using libcamera's\n> gstreamer element works.\n> \n> We need to work around two distinct issues with ASan when enabled in the\n> build:\n> \n> - glib has a known leak at initialization time. This is covered by the\n>   suppression file shipped with glib, but it's not clear how to use it\n>   automatically. For now, disable leak detection to avoid test failures.\n\nAre Valgrind suppression usable for Asan ? Glib installs it into:\n\n  /usr/share/glib-2.0/valgrind/glib.supp\n\nFor GStreamer suppressions, we don't install them (yet, under discussion). They\nare located in each repos. I would enable GStreamer leak tracer though, it does\nnot have such a high run-time overhead, and will detect any GObject or\nGstMiniObject leak, as long as the test calls gst_deinit() at the end (all tests\nshould).\n\n> \n> - GStreamer spawns a child process to scan plugins. If GStreamer is\n>   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n>   the libcamera plugin will cause an ASan link order verification\n>   failure. Disable the verification child processes to work around the\n>   problem. This requires gcc 8 or newer.\n\nHave you considered simply disabling forks ? See gst_registry_fork_set_enabled()\n\n> \n> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> This version incorporates changes coming from my review of v10, and\n> fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> independent reasons as explained in the commit message. I'd like to find\n> a way to use leak suppression files to handle the glib initialization\n> leak, but that's a big rabbit hole. The workaround for the second issue\n> is acceptable in my opinion.\n> \n> The biggest trouble with these workarounds is that they don't work with\n> gcc version older than 8. As the stable version of the most common Linux\n> distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> is the oldest version that libcamera supports) is also acceptable in my\n> opinion.\n> \n> Changes since v10:\n> \n> - Disable ASan leak detection\n> - Disable ASan link order verification for child processes\n> - Include source_path.h\n> - Remove unneeded explicit std::string construction\n> - Declare variables at usage site\n> - Make constants constexpr\n> - Add a variable for the message type\n> - Blank space fixes\n> ---\n>  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n>  test/gstreamer/meson.build                    |  19 ++\n>  test/meson.build                              |   1 +\n>  3 files changed, 208 insertions(+)\n>  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n>  create mode 100644 test/gstreamer/meson.build\n> \n> diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> new file mode 100644\n> index 000000000000..e26673b3471a\n> --- /dev/null\n> +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> @@ -0,0 +1,188 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2021, Vedant Paranjape\n> + *\n> + * ipa_interface_test.cpp - Test the IPA interface\n> + */\n> +\n> +#include <iostream>\n> +#include <unistd.h>\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +#include \"libcamera/internal/source_paths.h\"\n> +\n> +#include <gst/gst.h>\n> +\n> +#include \"test.h\"\n> +\n> +using namespace std;\n> +\n> +extern \"C\" {\n> +const char *__asan_default_options()\n> +{\n> +\t/*\n> +\t * Disable leak detection due to a known global variable initialization\n> +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> +\t * using a suppression file instead of disabling leak detection.\n> +\t */\n> +\treturn \"detect_leaks=false\";\n\nHave you consider GST_TRACERS=leaks ? It will heck for refcounted object leaks\non gstreamer/glib side, with very low overhead, and will abort on gst_deinit()\nif it found some leaks.\n\n\n> +}\n> +}\n> +\n> +class GstreamerSingleStreamTest : public Test\n> +{\n> +protected:\n> +\tint init() override\n> +\t{\n> +\t\t/*\n> +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> +\t\t * GStreamer is most likely not, this will cause the ASan link\n> +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> +\t\t * plugin as many libraries will have already been loaded by\n> +\t\t * then. Work around this issue by disabling the link order\n> +\t\t * check. This will only affect child processes, as ASan is\n> +\t\t * already loaded for this process by the time this code is\n> +\t\t * executed, and should thus hopefully be safe.\n> +\t\t *\n> +\t\t * This option is not available in gcc older than 8, the only\n> +\t\t * option in that case is to skip the test.\n> +\t\t */\n> +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> +\t\treturn TestSkip;\n> +#endif\n> +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n\nI think gst_registry_fork_set_enabled(FALSE) would be much cleaner, what do you\nthink ?\n\n> +\n> +\t\t/* Initialize GStreamer */\n> +\t\tGError *errInit = nullptr;\n> +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> +\t\t\tif (errInit)\n> +\t\t\t\tg_error_free(errInit);\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/*\n> +\t\t * Remove the system libcamera plugin, if any, and add the\n> +\t\t * plugin from the build directory.\n> +\t\t */\n> +\t\tGstRegistry *registry = gst_registry_get();\n> +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> +\t\tif (plugin) {\n> +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> +\t\t\tgst_object_unref(plugin);\n> +\t\t}\n> +\n> +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> +\t\t\t\t + \"src/gstreamer\";\n> +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> +\t\t\tgst_deinit();\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Create the elements */\n> +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n\nPlease use fakevideosink instead.\n\n> +\n> +\t\t/* Create the empty pipeline_ */\n> +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> +\n> +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n\npipeline_ cannot be NULL, glib will abort if malloc failed.\n\n> +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> +\t\t\tif (pipeline_)\n> +\t\t\t\tgst_object_unref(pipeline_);\n> +\t\t\tif (convert0_)\n> +\t\t\t\tgst_object_unref(convert0_);\n> +\t\t\tif (sink0_)\n> +\t\t\t\tgst_object_unref(sink0_);\n> +\t\t\tif (libcameraSrc_)\n> +\t\t\t\tgst_object_unref(libcameraSrc_);\n\nUse local g_autoptr() and g_steal_pointer() on success perhaps ? This will\nreduce a lot the about of error prone code in failure case. Note that in other\nsoftware I've seen, an abort is used instead, as clean exit is just coding\noverhead for a test.\n\nAn alternative, add them immediately to the pipeline, they are not owned by your\nclass anyway.\n\n> +\t\t\tgst_deinit();\n> +\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +\n> +\tvoid cleanup() override\n> +\t{\n> +\t\tgst_object_unref(pipeline_);\n> +\t\tgst_deinit();\n> +\t}\n> +\n> +\tint run() override\n> +\t{\n> +\t\tGstStateChangeReturn ret;\n> +\n> +\t\t/* Build the pipeline */\n> +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n\nThis can fail (e.g. duplicate name).\n\n> +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Start playing */\n> +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> +\t\tconstexpr GstMessageType msgType =\n> +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> +\t\tconstexpr GstClockTime timeout = 2000000000;\nPerhaps 2 * GST_SECOND.\n> +\n> +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> +\n> +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> +\n> +\t\t/* Parse error message */\n> +\t\tif (msg == NULL)\n> +\t\t\treturn TestPass;\n\nI would like some minimal validation. I would expect that after 2s some frames\ngot \"rendered\" properly. You can read the GstStructure property \"stats\" from\nfakevideosink / fakesink, and read the \"rendered\" field. Make sure this not zero\nperhaps ?\n\n> +\n> +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> +\t\tcase GST_MESSAGE_ERROR:\n> +\t\t\tgstreamer_print_error(msg);\n> +\t\t\tbreak;\n> +\t\tcase GST_MESSAGE_EOS:\n> +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> +\t\t\tbreak;\n> +\t\t}\n> +\n> +\t\treturn TestFail;\n> +\t}\n> +\n> +private:\n> +\tvoid gstreamer_print_error(GstMessage *msg)\n> +\t{\n> +\t\tGError *err;\n> +\t\tgchar *debug_info;\n> +\n> +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> +\t\tg_printerr(\"Debugging information: %s\\n\",\n> +\t\t\t   debug_info ? debug_info : \"none\");\n> +\t\tg_clear_error(&err);\n> +\t\tg_free(debug_info);\n> +\t}\n> +\n> +\tGstElement *pipeline_;\n> +\tGstElement *libcameraSrc_;\n> +\tGstElement *convert0_;\n> +\tGstElement *sink0_;\n> +};\n> +\n> +TEST_REGISTER(GstreamerSingleStreamTest)\n> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> new file mode 100644\n> index 000000000000..b99aa0da0ba3\n> --- /dev/null\n> +++ b/test/gstreamer/meson.build\n> @@ -0,0 +1,19 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +if not gst_enabled\n> +    subdir_done()\n> +endif\n> +\n> +gstreamer_tests = [\n> +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> +]\n> +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> +\n> +foreach t : gstreamer_tests\n> +    exe = executable(t[0], t[1],\n> +                     dependencies : [libcamera_private, gstreamer_dep],\n> +                     link_with : test_libraries,\n> +                     include_directories : test_includes_internal)\n> +\n> +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> +endforeach\n> diff --git a/test/meson.build b/test/meson.build\n> index 3bceb5df586f..d0466f17d7b6 100644\n> --- a/test/meson.build\n> +++ b/test/meson.build\n> @@ -11,6 +11,7 @@ subdir('libtest')\n>  \n>  subdir('camera')\n>  subdir('controls')\n> +subdir('gstreamer')\n>  subdir('ipa')\n>  subdir('ipc')\n>  subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 7A4C0BD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 17 Aug 2021 16:24:11 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id E6EDA6025F;\n\tTue, 17 Aug 2021 18:24:10 +0200 (CEST)","from mail-qk1-x72a.google.com (mail-qk1-x72a.google.com\n\t[IPv6:2607:f8b0:4864:20::72a])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 93FDF6025C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 17 Aug 2021 18:24:08 +0200 (CEST)","by mail-qk1-x72a.google.com with SMTP id t3so23656345qkg.11\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 17 Aug 2021 09:24:08 -0700 (PDT)","from nicolas-tpx395.localdomain (173-246-12-168.qc.cable.ebox.net.\n\t[173.246.12.168]) by smtp.gmail.com with ESMTPSA id\n\tk20sm1229223qtx.53.2021.08.17.09.24.06\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 17 Aug 2021 09:24:06 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=ndufresne-ca.20150623.gappssmtp.com\n\theader.i=@ndufresne-ca.20150623.gappssmtp.com\n\theader.b=\"rCRETgnG\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ndufresne-ca.20150623.gappssmtp.com; s=20150623;\n\th=message-id:subject:from:to:cc:date:in-reply-to:references\n\t:user-agent:mime-version:content-transfer-encoding;\n\tbh=usRYokicJmo/cPmX4CeeRkAXOjGVAXjLh+nfg3z78cM=;\n\tb=rCRETgnGx3hBgwhQ/o/2Cbhuy2JPr7XSMjDGU3Dc2mMrs4gxVcMPPNwtqhQVVqLBVX\n\tjmYsPxOtda2fsY014J/vQ0AWoGcpPu/btScRJrxeS0OCzpvfuF87PBI/5XTJrIaaz3bb\n\tDZwq3CnOTyZdGXeMxy57/CpySTevJJddWp3T5IS+yauo4XeSPXyqvjfrj2NnuBpSdAjG\n\tahLzwgjb6jmHZzbld6dqIDRpHp59Ocwg00x06dlqlvm+OT4yMtq981qnlQNQrMgnY1/D\n\tiNLq8p1a1fNQxzZ8kzGn4G5RzQsKXP7ew9zn8Mou6s6yIKvvFn68QZvXp6t9E6jgKK1Q\n\tcC8g==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:message-id:subject:from:to:cc:date:in-reply-to\n\t:references:user-agent:mime-version:content-transfer-encoding;\n\tbh=usRYokicJmo/cPmX4CeeRkAXOjGVAXjLh+nfg3z78cM=;\n\tb=aPYGu+DQZ6KyCkQh0pE8u18LNakk1EIEX3dP9RE+PpqZ44+/TtjeK1XHHFFUohc2dR\n\tcQl2u4SzyMBz1vEi9R/Y4f301e5m1nhFTG5EufsWSmRDTY4PaLmpg2I4OgLnecC2Kdg8\n\ty6r49eoO067BswOpn+f3sPCyX5fax1Q5Hybs/Po2s7va6YN0fZOr43660Db2vUBoHAxW\n\tpHD84gzWdokeR7Sfykz179fSQolgsDKsJaFRvAx8uLq4rhMMkXP4b/eFQYOwdRsGnWLY\n\t1QdJaILIwTGU9K56EF1Y7EbMS43DAdkUI1/KsLG9qSEmtqQM7TjSpmBV8snkhks+9JkF\n\tC6Rw==","X-Gm-Message-State":"AOAM530WCQa2RZPjSpknd6mGXVng/hVM2PeM3MxPNeayYjFUN89rHFWt\n\tUnVg2uVaFhkDZobbYDvi3SwpSQ==","X-Google-Smtp-Source":"ABdhPJze/edez7nYMl0mgYIKOVUOxzMGDWAEr/FD87dSKK5xwhLpx8C0xjbMdr/xgFBZTWUk0Ca7Fw==","X-Received":"by 2002:a37:9643:: with SMTP id\n\ty64mr4533398qkd.213.1629217447367; \n\tTue, 17 Aug 2021 09:24:07 -0700 (PDT)","Message-ID":"<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>","From":"Nicolas Dufresne <nicolas@ndufresne.ca>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>, \n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 17 Aug 2021 12:24:05 -0400","In-Reply-To":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","User-Agent":"Evolution 3.40.3 (3.40.3-1.fc34) ","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18876,"web_url":"https://patchwork.libcamera.org/comment/18876/","msgid":"<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>","date":"2021-08-17T18:34:46","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Nicolas,\n\nOn Tue, Aug 17, 2021 at 12:24:05PM -0400, Nicolas Dufresne wrote:\n> Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > \n> > This patch adds a test to test if single stream using libcamera's\n> > gstreamer element works.\n> > \n> > We need to work around two distinct issues with ASan when enabled in the\n> > build:\n> > \n> > - glib has a known leak at initialization time. This is covered by the\n> >   suppression file shipped with glib, but it's not clear how to use it\n> >   automatically. For now, disable leak detection to avoid test failures.\n> \n> Are Valgrind suppression usable for Asan ? Glib installs it into:\n> \n>   /usr/share/glib-2.0/valgrind/glib.supp\n> \n> For GStreamer suppressions, we don't install them (yet, under discussion). They\n> are located in each repos. I would enable GStreamer leak tracer though, it does\n> not have such a high run-time overhead, and will detect any GObject or\n> GstMiniObject leak, as long as the test calls gst_deinit() at the end (all tests\n> should).\n\nYes, I think they are usable. I was however unsure whether the path to\nthe supression file was standard, especially considering embedded\nsystems. I thus went for a workaround. It would be nice if there was a\nstandard mechanism to pick suppressions automatically.\n\n> > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> >   the libcamera plugin will cause an ASan link order verification\n> >   failure. Disable the verification child processes to work around the\n> >   problem. This requires gcc 8 or newer.\n> \n> Have you considered simply disabling forks ? See gst_registry_fork_set_enabled()\n\nNo, as I had no idea that existed :-)\n\n> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > This version incorporates changes coming from my review of v10, and\n> > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > independent reasons as explained in the commit message. I'd like to find\n> > a way to use leak suppression files to handle the glib initialization\n> > leak, but that's a big rabbit hole. The workaround for the second issue\n> > is acceptable in my opinion.\n> > \n> > The biggest trouble with these workarounds is that they don't work with\n> > gcc version older than 8. As the stable version of the most common Linux\n> > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > is the oldest version that libcamera supports) is also acceptable in my\n> > opinion.\n> > \n> > Changes since v10:\n> > \n> > - Disable ASan leak detection\n> > - Disable ASan link order verification for child processes\n> > - Include source_path.h\n> > - Remove unneeded explicit std::string construction\n> > - Declare variables at usage site\n> > - Make constants constexpr\n> > - Add a variable for the message type\n> > - Blank space fixes\n> > ---\n> >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> >  test/gstreamer/meson.build                    |  19 ++\n> >  test/meson.build                              |   1 +\n> >  3 files changed, 208 insertions(+)\n> >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> >  create mode 100644 test/gstreamer/meson.build\n> > \n> > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > new file mode 100644\n> > index 000000000000..e26673b3471a\n> > --- /dev/null\n> > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > @@ -0,0 +1,188 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2021, Vedant Paranjape\n> > + *\n> > + * ipa_interface_test.cpp - Test the IPA interface\n> > + */\n> > +\n> > +#include <iostream>\n> > +#include <unistd.h>\n> > +\n> > +#include <libcamera/base/utils.h>\n> > +\n> > +#include \"libcamera/internal/source_paths.h\"\n> > +\n> > +#include <gst/gst.h>\n> > +\n> > +#include \"test.h\"\n> > +\n> > +using namespace std;\n> > +\n> > +extern \"C\" {\n> > +const char *__asan_default_options()\n> > +{\n> > +\t/*\n> > +\t * Disable leak detection due to a known global variable initialization\n> > +\t * leak in glib's g_quark_init(). This should ideally be handled by\n> > +\t * using a suppression file instead of disabling leak detection.\n> > +\t */\n> > +\treturn \"detect_leaks=false\";\n> \n> Have you consider GST_TRACERS=leaks ? It will heck for refcounted object leaks\n> on gstreamer/glib side, with very low overhead, and will abort on gst_deinit()\n> if it found some leaks.\n\nAgain, no idea it existed :-) It could be enabled in init() below.\n\n> > +}\n> > +}\n> > +\n> > +class GstreamerSingleStreamTest : public Test\n> > +{\n> > +protected:\n> > +\tint init() override\n> > +\t{\n> > +\t\t/*\n> > +\t\t * GStreamer spawns a process to run the gst-plugin-scanner\n> > +\t\t * helper. If libcamera is compiled with ASan enabled, and as\n> > +\t\t * GStreamer is most likely not, this will cause the ASan link\n> > +\t\t * order check to fail when gst-plugin-scanner dlopen()s the\n> > +\t\t * plugin as many libraries will have already been loaded by\n> > +\t\t * then. Work around this issue by disabling the link order\n> > +\t\t * check. This will only affect child processes, as ASan is\n> > +\t\t * already loaded for this process by the time this code is\n> > +\t\t * executed, and should thus hopefully be safe.\n> > +\t\t *\n> > +\t\t * This option is not available in gcc older than 8, the only\n> > +\t\t * option in that case is to skip the test.\n> > +\t\t */\n> > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> > +\t\treturn TestSkip;\n> > +#endif\n> > +\t\tsetenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> \n> I think gst_registry_fork_set_enabled(FALSE) would be much cleaner, what do you\n> think ?\n\nIf it does the job and has no drawback, that's fine with me.\n\n> > +\n> > +\t\t/* Initialize GStreamer */\n> > +\t\tGError *errInit = nullptr;\n> > +\t\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > +\t\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > +\t\t\t\t   errInit ? errInit->message : \"unknown error\");\n> > +\t\t\tif (errInit)\n> > +\t\t\t\tg_error_free(errInit);\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/*\n> > +\t\t * Remove the system libcamera plugin, if any, and add the\n> > +\t\t * plugin from the build directory.\n> > +\t\t */\n> > +\t\tGstRegistry *registry = gst_registry_get();\n> > +\t\tGstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > +\t\tif (plugin) {\n> > +\t\t\tgst_registry_remove_plugin(registry, plugin);\n> > +\t\t\tgst_object_unref(plugin);\n> > +\t\t}\n> > +\n> > +\t\tstd::string path = libcamera::utils::libcameraBuildPath()\n> > +\t\t\t\t + \"src/gstreamer\";\n> > +\t\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> > +\t\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> > +\t\t\tgst_deinit();\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Create the elements */\n> > +\t\tlibcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > +\t\tconvert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > +\t\tsink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> \n> Please use fakevideosink instead.\n\nThat's what Vedant was doing, and the element wasn't present on Kieran's\nsystem. For my own culture, what's the advantage of using fakevideosink\nover fakesink ?\n\n> > +\n> > +\t\t/* Create the empty pipeline_ */\n> > +\t\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > +\n> > +\t\tif (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> \n> pipeline_ cannot be NULL, glib will abort if malloc failed.\n\nGood point.\n\n> > +\t\t\tg_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > +\t\t\t\t   pipeline_, convert0_, sink0_, libcameraSrc_);\n> > +\t\t\tif (pipeline_)\n> > +\t\t\t\tgst_object_unref(pipeline_);\n> > +\t\t\tif (convert0_)\n> > +\t\t\t\tgst_object_unref(convert0_);\n> > +\t\t\tif (sink0_)\n> > +\t\t\t\tgst_object_unref(sink0_);\n> > +\t\t\tif (libcameraSrc_)\n> > +\t\t\t\tgst_object_unref(libcameraSrc_);\n> \n> Use local g_autoptr() and g_steal_pointer() on success perhaps ? This will\n> reduce a lot the about of error prone code in failure case. Note that in other\n> software I've seen, an abort is used instead, as clean exit is just coding\n> overhead for a test.\n> \n> An alternative, add them immediately to the pipeline, they are not owned by your\n> class anyway.\n> \n> > +\t\t\tgst_deinit();\n> > +\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\treturn TestPass;\n> > +\t}\n> > +\n> > +\tvoid cleanup() override\n> > +\t{\n> > +\t\tgst_object_unref(pipeline_);\n> > +\t\tgst_deinit();\n> > +\t}\n> > +\n> > +\tint run() override\n> > +\t{\n> > +\t\tGstStateChangeReturn ret;\n> > +\n> > +\t\t/* Build the pipeline */\n> > +\t\tgst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> \n> This can fail (e.g. duplicate name).\n> \n> > +\t\tif (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > +\t\t\tg_printerr(\"Elements could not be linked.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Start playing */\n> > +\t\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > +\t\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> > +\t\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > +\t\t\treturn TestFail;\n> > +\t\t}\n> > +\n> > +\t\t/* Wait until error or EOS or timeout after 2 seconds */\n> > +\t\tconstexpr GstMessageType msgType =\n> > +\t\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > +\t\tconstexpr GstClockTime timeout = 2000000000;\n\n> Perhaps 2 * GST_SECOND.\n\nMuch better.\n\n> > +\n> > +\t\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > +\t\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > +\n> > +\t\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> > +\n> > +\t\t/* Parse error message */\n> > +\t\tif (msg == NULL)\n> > +\t\t\treturn TestPass;\n> \n> I would like some minimal validation. I would expect that after 2s some frames\n> got \"rendered\" properly. You can read the GstStructure property \"stats\" from\n> fakevideosink / fakesink, and read the \"rendered\" field. Make sure this not zero\n> perhaps ?\n\nVery good idea.\n\nVedant, I think there's work for you :-)\n\n> > +\n> > +\t\tswitch (GST_MESSAGE_TYPE(msg)) {\n> > +\t\tcase GST_MESSAGE_ERROR:\n> > +\t\t\tgstreamer_print_error(msg);\n> > +\t\t\tbreak;\n> > +\t\tcase GST_MESSAGE_EOS:\n> > +\t\t\tg_print(\"End-Of-Stream reached.\\n\");\n> > +\t\t\tbreak;\n> > +\t\tdefault:\n> > +\t\t\tg_printerr(\"Unexpected message received.\\n\");\n> > +\t\t\tbreak;\n> > +\t\t}\n> > +\n> > +\t\treturn TestFail;\n> > +\t}\n> > +\n> > +private:\n> > +\tvoid gstreamer_print_error(GstMessage *msg)\n> > +\t{\n> > +\t\tGError *err;\n> > +\t\tgchar *debug_info;\n> > +\n> > +\t\tgst_message_parse_error(msg, &err, &debug_info);\n> > +\t\tg_printerr(\"Error received from element %s: %s\\n\",\n> > +\t\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> > +\t\tg_printerr(\"Debugging information: %s\\n\",\n> > +\t\t\t   debug_info ? debug_info : \"none\");\n> > +\t\tg_clear_error(&err);\n> > +\t\tg_free(debug_info);\n> > +\t}\n> > +\n> > +\tGstElement *pipeline_;\n> > +\tGstElement *libcameraSrc_;\n> > +\tGstElement *convert0_;\n> > +\tGstElement *sink0_;\n> > +};\n> > +\n> > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > new file mode 100644\n> > index 000000000000..b99aa0da0ba3\n> > --- /dev/null\n> > +++ b/test/gstreamer/meson.build\n> > @@ -0,0 +1,19 @@\n> > +# SPDX-License-Identifier: CC0-1.0\n> > +\n> > +if not gst_enabled\n> > +    subdir_done()\n> > +endif\n> > +\n> > +gstreamer_tests = [\n> > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > +]\n> > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > +\n> > +foreach t : gstreamer_tests\n> > +    exe = executable(t[0], t[1],\n> > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > +                     link_with : test_libraries,\n> > +                     include_directories : test_includes_internal)\n> > +\n> > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > +endforeach\n> > diff --git a/test/meson.build b/test/meson.build\n> > index 3bceb5df586f..d0466f17d7b6 100644\n> > --- a/test/meson.build\n> > +++ b/test/meson.build\n> > @@ -11,6 +11,7 @@ subdir('libtest')\n> >  \n> >  subdir('camera')\n> >  subdir('controls')\n> > +subdir('gstreamer')\n> >  subdir('ipa')\n> >  subdir('ipc')\n> >  subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B055DBD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 17 Aug 2021 18:34:57 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C492E6025F;\n\tTue, 17 Aug 2021 20:34:56 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 128C66025C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 17 Aug 2021 20:34:55 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5A2352C5;\n\tTue, 17 Aug 2021 20:34:54 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"uPQ16HzI\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629225294;\n\tbh=7vCGBqKTX3PgbewA7XgBMza8583ipjpsXFxzj/MvpnA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=uPQ16HzIiL3maVH4zTMlVqQ5Wb4TcdVsA8QNd87BTxOimonKKYQvGlE6utxNW6ORM\n\t21cHhGybRoiHPBnRQOQupXIVqIxQkXESn18hpaKfwR1h4OWGtiwvJsG5R8/xswpl8B\n\tcAnwp4y/6NQmthp1/oAltpE5T0ULv6BazX2U4gI4=","Date":"Tue, 17 Aug 2021 21:34:46 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Nicolas Dufresne <nicolas@ndufresne.ca>","Message-ID":"<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org,\n\tVedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18877,"web_url":"https://patchwork.libcamera.org/comment/18877/","msgid":"<CACGrz-OCvT4jngCQo+rWyE=BMc1g978DEE0rdiK_C3A1=UaMOA@mail.gmail.com>","date":"2021-08-17T18:38:32","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"Hi Laurent,\nWill address these changes in addition to one Umang suggested within this\nweek itself.\n\nRegards,\n*Vedant Paranjape*\n\nOn Wed, Aug 18, 2021 at 12:04 AM Laurent Pinchart <\nlaurent.pinchart@ideasonboard.com> wrote:\n\n> Hi Nicolas,\n>\n> On Tue, Aug 17, 2021 at 12:24:05PM -0400, Nicolas Dufresne wrote:\n> > Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > >\n> > > This patch adds a test to test if single stream using libcamera's\n> > > gstreamer element works.\n> > >\n> > > We need to work around two distinct issues with ASan when enabled in\n> the\n> > > build:\n> > >\n> > > - glib has a known leak at initialization time. This is covered by the\n> > >   suppression file shipped with glib, but it's not clear how to use it\n> > >   automatically. For now, disable leak detection to avoid test\n> failures.\n> >\n> > Are Valgrind suppression usable for Asan ? Glib installs it into:\n> >\n> >   /usr/share/glib-2.0/valgrind/glib.supp\n> >\n> > For GStreamer suppressions, we don't install them (yet, under\n> discussion). They\n> > are located in each repos. I would enable GStreamer leak tracer though,\n> it does\n> > not have such a high run-time overhead, and will detect any GObject or\n> > GstMiniObject leak, as long as the test calls gst_deinit() at the end\n> (all tests\n> > should).\n>\n> Yes, I think they are usable. I was however unsure whether the path to\n> the supression file was standard, especially considering embedded\n> systems. I thus went for a workaround. It would be nice if there was a\n> standard mechanism to pick suppressions automatically.\n>\n> > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> > >   the libcamera plugin will cause an ASan link order verification\n> > >   failure. Disable the verification child processes to work around the\n> > >   problem. This requires gcc 8 or newer.\n> >\n> > Have you considered simply disabling forks ? See\n> gst_registry_fork_set_enabled()\n>\n> No, as I had no idea that existed :-)\n>\n> > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > > This version incorporates changes coming from my review of v10, and\n> > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > > independent reasons as explained in the commit message. I'd like to\n> find\n> > > a way to use leak suppression files to handle the glib initialization\n> > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > is acceptable in my opinion.\n> > >\n> > > The biggest trouble with these workarounds is that they don't work with\n> > > gcc version older than 8. As the stable version of the most common\n> Linux\n> > > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > > is the oldest version that libcamera supports) is also acceptable in my\n> > > opinion.\n> > >\n> > > Changes since v10:\n> > >\n> > > - Disable ASan leak detection\n> > > - Disable ASan link order verification for child processes\n> > > - Include source_path.h\n> > > - Remove unneeded explicit std::string construction\n> > > - Declare variables at usage site\n> > > - Make constants constexpr\n> > > - Add a variable for the message type\n> > > - Blank space fixes\n> > > ---\n> > >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> > >  test/gstreamer/meson.build                    |  19 ++\n> > >  test/meson.build                              |   1 +\n> > >  3 files changed, 208 insertions(+)\n> > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > >  create mode 100644 test/gstreamer/meson.build\n> > >\n> > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > new file mode 100644\n> > > index 000000000000..e26673b3471a\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > @@ -0,0 +1,188 @@\n> > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > +/*\n> > > + * Copyright (C) 2021, Vedant Paranjape\n> > > + *\n> > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > + */\n> > > +\n> > > +#include <iostream>\n> > > +#include <unistd.h>\n> > > +\n> > > +#include <libcamera/base/utils.h>\n> > > +\n> > > +#include \"libcamera/internal/source_paths.h\"\n> > > +\n> > > +#include <gst/gst.h>\n> > > +\n> > > +#include \"test.h\"\n> > > +\n> > > +using namespace std;\n> > > +\n> > > +extern \"C\" {\n> > > +const char *__asan_default_options()\n> > > +{\n> > > +   /*\n> > > +    * Disable leak detection due to a known global variable\n> initialization\n> > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > +    * using a suppression file instead of disabling leak detection.\n> > > +    */\n> > > +   return \"detect_leaks=false\";\n> >\n> > Have you consider GST_TRACERS=leaks ? It will heck for refcounted object\n> leaks\n> > on gstreamer/glib side, with very low overhead, and will abort on\n> gst_deinit()\n> > if it found some leaks.\n>\n> Again, no idea it existed :-) It could be enabled in init() below.\n>\n> > > +}\n> > > +}\n> > > +\n> > > +class GstreamerSingleStreamTest : public Test\n> > > +{\n> > > +protected:\n> > > +   int init() override\n> > > +   {\n> > > +           /*\n> > > +            * GStreamer spawns a process to run the gst-plugin-scanner\n> > > +            * helper. If libcamera is compiled with ASan enabled, and\n> as\n> > > +            * GStreamer is most likely not, this will cause the ASan\n> link\n> > > +            * order check to fail when gst-plugin-scanner dlopen()s\n> the\n> > > +            * plugin as many libraries will have already been loaded\n> by\n> > > +            * then. Work around this issue by disabling the link order\n> > > +            * check. This will only affect child processes, as ASan is\n> > > +            * already loaded for this process by the time this code is\n> > > +            * executed, and should thus hopefully be safe.\n> > > +            *\n> > > +            * This option is not available in gcc older than 8, the\n> only\n> > > +            * option in that case is to skip the test.\n> > > +            */\n> > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__\n> < 8\n> > > +           return TestSkip;\n> > > +#endif\n> > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> >\n> > I think gst_registry_fork_set_enabled(FALSE) would be much cleaner, what\n> do you\n> > think ?\n>\n> If it does the job and has no drawback, that's fine with me.\n>\n> > > +\n> > > +           /* Initialize GStreamer */\n> > > +           GError *errInit = nullptr;\n> > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > > +                              errInit ? errInit->message : \"unknown\n> error\");\n> > > +                   if (errInit)\n> > > +                           g_error_free(errInit);\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /*\n> > > +            * Remove the system libcamera plugin, if any, and add the\n> > > +            * plugin from the build directory.\n> > > +            */\n> > > +           GstRegistry *registry = gst_registry_get();\n> > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n> \"libgstlibcamera.so\");\n> > > +           if (plugin) {\n> > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > +                   gst_object_unref(plugin);\n> > > +           }\n> > > +\n> > > +           std::string path = libcamera::utils::libcameraBuildPath()\n> > > +                            + \"src/gstreamer\";\n> > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > +                   g_printerr(\"Failed to add plugin to registry\\n\");\n> > > +                   gst_deinit();\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Create the elements */\n> > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\",\n> \"libcamera\");\n> > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n> \"convert0\");\n> > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> >\n> > Please use fakevideosink instead.\n>\n> That's what Vedant was doing, and the element wasn't present on Kieran's\n> system. For my own culture, what's the advantage of using fakevideosink\n> over fakesink ?\n>\n> > > +\n> > > +           /* Create the empty pipeline_ */\n> > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > +\n> > > +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_)\n> {\n> >\n> > pipeline_ cannot be NULL, glib will abort if malloc failed.\n>\n> Good point.\n>\n> > > +                   g_printerr(\"Not all elements could be created.\n> %p.%p.%p.%p\\n\",\n> > > +                              pipeline_, convert0_, sink0_,\n> libcameraSrc_);\n> > > +                   if (pipeline_)\n> > > +                           gst_object_unref(pipeline_);\n> > > +                   if (convert0_)\n> > > +                           gst_object_unref(convert0_);\n> > > +                   if (sink0_)\n> > > +                           gst_object_unref(sink0_);\n> > > +                   if (libcameraSrc_)\n> > > +                           gst_object_unref(libcameraSrc_);\n> >\n> > Use local g_autoptr() and g_steal_pointer() on success perhaps ? This\n> will\n> > reduce a lot the about of error prone code in failure case. Note that in\n> other\n> > software I've seen, an abort is used instead, as clean exit is just\n> coding\n> > overhead for a test.\n> >\n> > An alternative, add them immediately to the pipeline, they are not owned\n> by your\n> > class anyway.\n> >\n> > > +                   gst_deinit();\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           return TestPass;\n> > > +   }\n> > > +\n> > > +   void cleanup() override\n> > > +   {\n> > > +           gst_object_unref(pipeline_);\n> > > +           gst_deinit();\n> > > +   }\n> > > +\n> > > +   int run() override\n> > > +   {\n> > > +           GstStateChangeReturn ret;\n> > > +\n> > > +           /* Build the pipeline */\n> > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n> convert0_, sink0_, NULL);\n> >\n> > This can fail (e.g. duplicate name).\n> >\n> > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n> sink0_, NULL) != TRUE) {\n> > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Start playing */\n> > > +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > +                   g_printerr(\"Unable to set the pipeline to the\n> playing state.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > +           constexpr GstMessageType msgType =\n> > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR |\n> GST_MESSAGE_EOS);\n> > > +           constexpr GstClockTime timeout = 2000000000;\n>\n> > Perhaps 2 * GST_SECOND.\n>\n> Much better.\n>\n> > > +\n> > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > +           g_autoptr(GstMessage) msg =\n> gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > +\n> > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > +\n> > > +           /* Parse error message */\n> > > +           if (msg == NULL)\n> > > +                   return TestPass;\n> >\n> > I would like some minimal validation. I would expect that after 2s some\n> frames\n> > got \"rendered\" properly. You can read the GstStructure property \"stats\"\n> from\n> > fakevideosink / fakesink, and read the \"rendered\" field. Make sure this\n> not zero\n> > perhaps ?\n>\n> Very good idea.\n>\n> Vedant, I think there's work for you :-)\n>\n> > > +\n> > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > +           case GST_MESSAGE_ERROR:\n> > > +                   gstreamer_print_error(msg);\n> > > +                   break;\n> > > +           case GST_MESSAGE_EOS:\n> > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > +                   break;\n> > > +           default:\n> > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > +                   break;\n> > > +           }\n> > > +\n> > > +           return TestFail;\n> > > +   }\n> > > +\n> > > +private:\n> > > +   void gstreamer_print_error(GstMessage *msg)\n> > > +   {\n> > > +           GError *err;\n> > > +           gchar *debug_info;\n> > > +\n> > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > +                      debug_info ? debug_info : \"none\");\n> > > +           g_clear_error(&err);\n> > > +           g_free(debug_info);\n> > > +   }\n> > > +\n> > > +   GstElement *pipeline_;\n> > > +   GstElement *libcameraSrc_;\n> > > +   GstElement *convert0_;\n> > > +   GstElement *sink0_;\n> > > +};\n> > > +\n> > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > new file mode 100644\n> > > index 000000000000..b99aa0da0ba3\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/meson.build\n> > > @@ -0,0 +1,19 @@\n> > > +# SPDX-License-Identifier: CC0-1.0\n> > > +\n> > > +if not gst_enabled\n> > > +    subdir_done()\n> > > +endif\n> > > +\n> > > +gstreamer_tests = [\n> > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > +]\n> > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > +\n> > > +foreach t : gstreamer_tests\n> > > +    exe = executable(t[0], t[1],\n> > > +                     dependencies : [libcamera_private,\n> gstreamer_dep],\n> > > +                     link_with : test_libraries,\n> > > +                     include_directories : test_includes_internal)\n> > > +\n> > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > +endforeach\n> > > diff --git a/test/meson.build b/test/meson.build\n> > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > --- a/test/meson.build\n> > > +++ b/test/meson.build\n> > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > >\n> > >  subdir('camera')\n> > >  subdir('controls')\n> > > +subdir('gstreamer')\n> > >  subdir('ipa')\n> > >  subdir('ipc')\n> > >  subdir('log')\n>\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id D6534BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 17 Aug 2021 18:38:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 57A936025F;\n\tTue, 17 Aug 2021 20:38:47 +0200 (CEST)","from mail-yb1-xb31.google.com (mail-yb1-xb31.google.com\n\t[IPv6:2607:f8b0:4864:20::b31])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 824D76025C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 17 Aug 2021 20:38:45 +0200 (CEST)","by mail-yb1-xb31.google.com with SMTP id k65so51283yba.13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 17 Aug 2021 11:38:45 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"aqDjiI0H\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=yfr+NgfkeYGQqlX1jpTxnupI/x+BU6FQoOp87PUc/Pw=;\n\tb=aqDjiI0HHjAQCZcuP5aBv78bf9n/C5/hqVv9ZYBlJhQTCZH6SpJBY1MIMCMGalltIy\n\tq0TGiJoHWK2EYGSLotsYiXdkeSIUxlcisLCKiJ00kV0o69cFnAD6k4/q2X5Z5B7wZXay\n\tYGS6LsjbNuR3JgZewdMH2eJf0nHhswh1KiuuuXGshCtMeUUQovhQW62SkEGfYorXQfa1\n\twsTwzOEQTwwnLp4ESKXyEJCVUogznSZXYXeE6pQ6bSEme7VBKupHz+uUIHq1wwu4j0/i\n\ttvGIWfOnO6ClE8Jr8kk3hkbjty43L9CuY4PNZvOr3qUP+JZuf+OrYA7haqKpt5/vYyk5\n\tLTEw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=yfr+NgfkeYGQqlX1jpTxnupI/x+BU6FQoOp87PUc/Pw=;\n\tb=t++ieBES3gMM5JOWs8A7CLb9sRBFPC0GvZq9cVXB5Dy0hoIb7y/BvkG37GYNMM/79Z\n\t2R1fjJjAzybCkuqcClxTj/VL3J7FXS3MWxVsZ6WBKOiEmNJvUtGpgqXr0Rj4zX8Yf/6W\n\t+QTroWHEhTAqCKTLAothcixxAzSScNLfOg1rkokpk5IvX3MZNS0353i8ktYLutnr0M8H\n\tAX+Ip8XrXeSar4fmI5vObrMaqChIb6lEmQ+GjzareYqqwGEKDGRntU9r5pTpgNb/h0Rk\n\t3LLpfD8p78SfJ/XjH9rxXof52fmf+UsBVWdHjUIjK704DzJy97jWVLT3Q7W8XFMvGL5v\n\thkLw==","X-Gm-Message-State":"AOAM531OINDA2/Lvx9QzKQjZfa8Owa8BiFeJuBve84CdI7aP6WNdck7g\n\tyoEGF+dnY7kG+IcYFqcTNMH6hS1nirg2x/gDwPw=","X-Google-Smtp-Source":"ABdhPJwcHM6Ni9PZ2Unijrk2vYvY/ykxoUM4L5/ALCY7qlkmnPTvsVjp9O1rc2vASDj1Yq4dMOZ2VjNjDBU/R7on4Fo=","X-Received":"by 2002:a25:d084:: with SMTP id\n\th126mr6194783ybg.175.1629225524263; \n\tTue, 17 Aug 2021 11:38:44 -0700 (PDT)","MIME-Version":"1.0","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>\n\t<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>","In-Reply-To":"<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Wed, 18 Aug 2021 00:08:32 +0530","Message-ID":"<CACGrz-OCvT4jngCQo+rWyE=BMc1g978DEE0rdiK_C3A1=UaMOA@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000a1fee005c9c5a08d\"","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18912,"web_url":"https://patchwork.libcamera.org/comment/18912/","msgid":"<CACGrz-MCkGJ5L+Dn8eBD8wokF75HHjOkofPbwSrStATDv_P_=Q@mail.gmail.com>","date":"2021-08-18T11:24:45","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"Hi Laurent,\n\n\nOn Wed, Aug 18, 2021 at 12:04 AM Laurent Pinchart <\nlaurent.pinchart@ideasonboard.com> wrote:\n\n> Hi Nicolas,\n>\n> On Tue, Aug 17, 2021 at 12:24:05PM -0400, Nicolas Dufresne wrote:\n> > Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > >\n> > > This patch adds a test to test if single stream using libcamera's\n> > > gstreamer element works.\n> > >\n> > > We need to work around two distinct issues with ASan when enabled in\n> the\n> > > build:\n> > >\n> > > - glib has a known leak at initialization time. This is covered by the\n> > >   suppression file shipped with glib, but it's not clear how to use it\n> > >   automatically. For now, disable leak detection to avoid test\n> failures.\n> >\n> > Are Valgrind suppression usable for Asan ? Glib installs it into:\n> >\n> >   /usr/share/glib-2.0/valgrind/glib.supp\n> >\n> > For GStreamer suppressions, we don't install them (yet, under\n> discussion). They\n> > are located in each repos. I would enable GStreamer leak tracer though,\n> it does\n> > not have such a high run-time overhead, and will detect any GObject or\n> > GstMiniObject leak, as long as the test calls gst_deinit() at the end\n> (all tests\n> > should).\n>\n> Yes, I think they are usable. I was however unsure whether the path to\n> the supression file was standard, especially considering embedded\n> systems. I thus went for a workaround. It would be nice if there was a\n> standard mechanism to pick suppressions automatically.\n>\n> > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> > >   the libcamera plugin will cause an ASan link order verification\n> > >   failure. Disable the verification child processes to work around the\n> > >   problem. This requires gcc 8 or newer.\n> >\n> > Have you considered simply disabling forks ? See\n> gst_registry_fork_set_enabled()\n>\n> No, as I had no idea that existed :-)\n>\n\nTested this, it works as expected. Now need to figure out to patch\ngstreamer as it still leaks memory\n\n=================================================================\n==676137==ERROR: LeakSanitizer: detected memory leaks\n\nDirect leak of 16384 byte(s) in 1 object(s) allocated from:\n    #0 0x7f4faf3b8bc8 in malloc\n(/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)\n    #1 0x7f4fae763e98 in g_malloc\n(/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)\n\nSUMMARY: AddressSanitizer: 16384 byte(s) leaked in 1 allocation(s).\n――――――――――――――――――――――――――――――――――――――――\n\n> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > > This version incorporates changes coming from my review of v10, and\n> > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > > independent reasons as explained in the commit message. I'd like to\n> find\n> > > a way to use leak suppression files to handle the glib initialization\n> > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > is acceptable in my opinion.\n> > >\n> > > The biggest trouble with these workarounds is that they don't work with\n> > > gcc version older than 8. As the stable version of the most common\n> Linux\n> > > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > > is the oldest version that libcamera supports) is also acceptable in my\n> > > opinion.\n> > >\n> > > Changes since v10:\n> > >\n> > > - Disable ASan leak detection\n> > > - Disable ASan link order verification for child processes\n> > > - Include source_path.h\n> > > - Remove unneeded explicit std::string construction\n> > > - Declare variables at usage site\n> > > - Make constants constexpr\n> > > - Add a variable for the message type\n> > > - Blank space fixes\n> > > ---\n> > >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> > >  test/gstreamer/meson.build                    |  19 ++\n> > >  test/meson.build                              |   1 +\n> > >  3 files changed, 208 insertions(+)\n> > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > >  create mode 100644 test/gstreamer/meson.build\n> > >\n> > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > new file mode 100644\n> > > index 000000000000..e26673b3471a\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > @@ -0,0 +1,188 @@\n> > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > +/*\n> > > + * Copyright (C) 2021, Vedant Paranjape\n> > > + *\n> > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > + */\n> > > +\n> > > +#include <iostream>\n> > > +#include <unistd.h>\n> > > +\n> > > +#include <libcamera/base/utils.h>\n> > > +\n> > > +#include \"libcamera/internal/source_paths.h\"\n> > > +\n> > > +#include <gst/gst.h>\n> > > +\n> > > +#include \"test.h\"\n> > > +\n> > > +using namespace std;\n> > > +\n> > > +extern \"C\" {\n> > > +const char *__asan_default_options()\n> > > +{\n> > > +   /*\n> > > +    * Disable leak detection due to a known global variable\n> initialization\n> > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > +    * using a suppression file instead of disabling leak detection.\n> > > +    */\n> > > +   return \"detect_leaks=false\";\n> >\n> > Have you consider GST_TRACERS=leaks ? It will heck for refcounted object\n> leaks\n> > on gstreamer/glib side, with very low overhead, and will abort on\n> gst_deinit()\n> > if it found some leaks.\n>\n> Again, no idea it existed :-) It could be enabled in init() below.\n>\n> > > +}\n> > > +}\n> > > +\n> > > +class GstreamerSingleStreamTest : public Test\n> > > +{\n> > > +protected:\n> > > +   int init() override\n> > > +   {\n> > > +           /*\n> > > +            * GStreamer spawns a process to run the gst-plugin-scanner\n> > > +            * helper. If libcamera is compiled with ASan enabled, and\n> as\n> > > +            * GStreamer is most likely not, this will cause the ASan\n> link\n> > > +            * order check to fail when gst-plugin-scanner dlopen()s\n> the\n> > > +            * plugin as many libraries will have already been loaded\n> by\n> > > +            * then. Work around this issue by disabling the link order\n> > > +            * check. This will only affect child processes, as ASan is\n> > > +            * already loaded for this process by the time this code is\n> > > +            * executed, and should thus hopefully be safe.\n> > > +            *\n> > > +            * This option is not available in gcc older than 8, the\n> only\n> > > +            * option in that case is to skip the test.\n> > > +            */\n> > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__\n> < 8\n> > > +           return TestSkip;\n> > > +#endif\n> > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> >\n> > I think gst_registry_fork_set_enabled(FALSE) would be much cleaner, what\n> do you\n> > think ?\n>\n> If it does the job and has no drawback, that's fine with me.\n>\n> > > +\n> > > +           /* Initialize GStreamer */\n> > > +           GError *errInit = nullptr;\n> > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > > +                              errInit ? errInit->message : \"unknown\n> error\");\n> > > +                   if (errInit)\n> > > +                           g_error_free(errInit);\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /*\n> > > +            * Remove the system libcamera plugin, if any, and add the\n> > > +            * plugin from the build directory.\n> > > +            */\n> > > +           GstRegistry *registry = gst_registry_get();\n> > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n> \"libgstlibcamera.so\");\n> > > +           if (plugin) {\n> > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > +                   gst_object_unref(plugin);\n> > > +           }\n> > > +\n> > > +           std::string path = libcamera::utils::libcameraBuildPath()\n> > > +                            + \"src/gstreamer\";\n> > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > +                   g_printerr(\"Failed to add plugin to registry\\n\");\n> > > +                   gst_deinit();\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Create the elements */\n> > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\",\n> \"libcamera\");\n> > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n> \"convert0\");\n> > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> >\n> > Please use fakevideosink instead.\n>\n> That's what Vedant was doing, and the element wasn't present on Kieran's\n> system. For my own culture, what's the advantage of using fakevideosink\n> over fakesink ?\n>\n> > > +\n> > > +           /* Create the empty pipeline_ */\n> > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > +\n> > > +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_)\n> {\n> >\n> > pipeline_ cannot be NULL, glib will abort if malloc failed.\n>\n> Good point.\n>\n> > > +                   g_printerr(\"Not all elements could be created.\n> %p.%p.%p.%p\\n\",\n> > > +                              pipeline_, convert0_, sink0_,\n> libcameraSrc_);\n> > > +                   if (pipeline_)\n> > > +                           gst_object_unref(pipeline_);\n> > > +                   if (convert0_)\n> > > +                           gst_object_unref(convert0_);\n> > > +                   if (sink0_)\n> > > +                           gst_object_unref(sink0_);\n> > > +                   if (libcameraSrc_)\n> > > +                           gst_object_unref(libcameraSrc_);\n> >\n> > Use local g_autoptr() and g_steal_pointer() on success perhaps ? This\n> will\n> > reduce a lot the about of error prone code in failure case. Note that in\n> other\n> > software I've seen, an abort is used instead, as clean exit is just\n> coding\n> > overhead for a test.\n> >\n> > An alternative, add them immediately to the pipeline, they are not owned\n> by your\n> > class anyway.\n> >\n> > > +                   gst_deinit();\n> > > +\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           return TestPass;\n> > > +   }\n> > > +\n> > > +   void cleanup() override\n> > > +   {\n> > > +           gst_object_unref(pipeline_);\n> > > +           gst_deinit();\n> > > +   }\n> > > +\n> > > +   int run() override\n> > > +   {\n> > > +           GstStateChangeReturn ret;\n> > > +\n> > > +           /* Build the pipeline */\n> > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n> convert0_, sink0_, NULL);\n> >\n> > This can fail (e.g. duplicate name).\n> >\n> > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n> sink0_, NULL) != TRUE) {\n> > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Start playing */\n> > > +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > +                   g_printerr(\"Unable to set the pipeline to the\n> playing state.\\n\");\n> > > +                   return TestFail;\n> > > +           }\n> > > +\n> > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > +           constexpr GstMessageType msgType =\n> > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR |\n> GST_MESSAGE_EOS);\n> > > +           constexpr GstClockTime timeout = 2000000000;\n>\n> > Perhaps 2 * GST_SECOND.\n>\n> Much better.\n>\n> > > +\n> > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > +           g_autoptr(GstMessage) msg =\n> gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > +\n> > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > +\n> > > +           /* Parse error message */\n> > > +           if (msg == NULL)\n> > > +                   return TestPass;\n> >\n> > I would like some minimal validation. I would expect that after 2s some\n> frames\n> > got \"rendered\" properly. You can read the GstStructure property \"stats\"\n> from\n> > fakevideosink / fakesink, and read the \"rendered\" field. Make sure this\n> not zero\n> > perhaps ?\n>\n> Very good idea.\n>\n> Vedant, I think there's work for you :-)\n>\n> > > +\n> > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > +           case GST_MESSAGE_ERROR:\n> > > +                   gstreamer_print_error(msg);\n> > > +                   break;\n> > > +           case GST_MESSAGE_EOS:\n> > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > +                   break;\n> > > +           default:\n> > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > +                   break;\n> > > +           }\n> > > +\n> > > +           return TestFail;\n> > > +   }\n> > > +\n> > > +private:\n> > > +   void gstreamer_print_error(GstMessage *msg)\n> > > +   {\n> > > +           GError *err;\n> > > +           gchar *debug_info;\n> > > +\n> > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > +                      debug_info ? debug_info : \"none\");\n> > > +           g_clear_error(&err);\n> > > +           g_free(debug_info);\n> > > +   }\n> > > +\n> > > +   GstElement *pipeline_;\n> > > +   GstElement *libcameraSrc_;\n> > > +   GstElement *convert0_;\n> > > +   GstElement *sink0_;\n> > > +};\n> > > +\n> > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > new file mode 100644\n> > > index 000000000000..b99aa0da0ba3\n> > > --- /dev/null\n> > > +++ b/test/gstreamer/meson.build\n> > > @@ -0,0 +1,19 @@\n> > > +# SPDX-License-Identifier: CC0-1.0\n> > > +\n> > > +if not gst_enabled\n> > > +    subdir_done()\n> > > +endif\n> > > +\n> > > +gstreamer_tests = [\n> > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > +]\n> > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > +\n> > > +foreach t : gstreamer_tests\n> > > +    exe = executable(t[0], t[1],\n> > > +                     dependencies : [libcamera_private,\n> gstreamer_dep],\n> > > +                     link_with : test_libraries,\n> > > +                     include_directories : test_includes_internal)\n> > > +\n> > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > +endforeach\n> > > diff --git a/test/meson.build b/test/meson.build\n> > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > --- a/test/meson.build\n> > > +++ b/test/meson.build\n> > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > >\n> > >  subdir('camera')\n> > >  subdir('controls')\n> > > +subdir('gstreamer')\n> > >  subdir('ipa')\n> > >  subdir('ipc')\n> > >  subdir('log')\n>\n> --\n> Regards,\n>\n> Laurent Pinchart\n>\n\nRegards,\nVedant Paranjape","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 22A2DBD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Aug 2021 11:25:00 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 93E5268890;\n\tWed, 18 Aug 2021 13:24:59 +0200 (CEST)","from mail-yb1-xb30.google.com (mail-yb1-xb30.google.com\n\t[IPv6:2607:f8b0:4864:20::b30])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 043B76888A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 13:24:57 +0200 (CEST)","by mail-yb1-xb30.google.com with SMTP id m193so4541867ybf.9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 04:24:57 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"W62QbK/8\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=ZsEFpVNvY4b+v2MliqdA9zNzg6adJ1sOziPqRKAIHk4=;\n\tb=W62QbK/8MXOXCL6vJqWqKP2Kk9jX9TUe789JNr5ivelD0KPP8geVAp4KrGUCPhdE8y\n\t3vchH3WJpeZLktoTWvZvrJoQfn1l6MgtHAzj3LDzFJzssUuQWqz84kCK/8zS2SW/xp5i\n\t/STih9DdZW0Ekulmt/wJCOiIHOiB160iDdpmLcHoFdNLsidwf3ue0t2t7S2xzV4ianJd\n\tfYOM4uii7Rjiuh0XRLxZR5zfn6JsUcuXymTdtIDwmYcT5IFwI7IcxwRSD0Kl4p34i9rP\n\tTWTMOE5RdgLTlYdB7plbQ37NnrLv9WlhN4Njulf/uk91fplmoizu+YlOzdFo4xhC+iDb\n\tFnWA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=ZsEFpVNvY4b+v2MliqdA9zNzg6adJ1sOziPqRKAIHk4=;\n\tb=BIXGJ40+yoyEnTC7/KffbXN9mQjogzYBtAGSviNJX+dNm0DzmLl9jZg+Aq7eTJU5P1\n\tueRnaflkvpfqVIeCWi4yJTvAKRkWSWXMgV4JrZHi+MWkd+3AdAbnJC/SSV6gYd6zweaD\n\t2b97EZ7G2LWRdyYzqSabJGFry4z7KSRV6Wp65G01o+TvmP3CJ6MZsC+0tokSLWVwldER\n\t6tO2hC2yITqeNOUcmuQheSFWxplObz4E9oV77zUXgpdBVtYUHbE/3RWdW9amFOiu8IOc\n\tLbqv5EzAPZBZjo1eMwbq8W9TI7ldrU9ibUTUrZw2keftPykjrR4eydE9vVcrhNklyCQM\n\tAYPg==","X-Gm-Message-State":"AOAM532CHaaqsLg0VZUQvHkCxl3mlY1TqmR3L/62he104ESzlp2ezuqc\n\tUzKA87r9zxiBSANiZuZkEfxtNqo38Nv25sOsDzg=","X-Google-Smtp-Source":"ABdhPJxreLXeItn1euuCP1frJzGbXLXxEc+PUbBTZNVS1dYS9PbNYhZbA8/g815KPmqCXZlcTR+B7rqbfn6o3Q9Feos=","X-Received":"by 2002:a5b:c52:: with SMTP id\n\td18mr10587103ybr.248.1629285896504; \n\tWed, 18 Aug 2021 04:24:56 -0700 (PDT)","MIME-Version":"1.0","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>\n\t<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>","In-Reply-To":"<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Wed, 18 Aug 2021 16:54:45 +0530","Message-ID":"<CACGrz-MCkGJ5L+Dn8eBD8wokF75HHjOkofPbwSrStATDv_P_=Q@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"0000000000001947d905c9d3af27\"","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18913,"web_url":"https://patchwork.libcamera.org/comment/18913/","msgid":"<YRzwf7gdqrmCNufu@pendragon.ideasonboard.com>","date":"2021-08-18T11:35:27","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Vedant,\n\nOn Wed, Aug 18, 2021 at 04:54:45PM +0530, Vedant Paranjape wrote:\n> On Wed, Aug 18, 2021 at 12:04 AM Laurent Pinchart wrote:\n> > On Tue, Aug 17, 2021 at 12:24:05PM -0400, Nicolas Dufresne wrote:\n> > > Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> > > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > >\n> > > > This patch adds a test to test if single stream using libcamera's\n> > > > gstreamer element works.\n> > > >\n> > > > We need to work around two distinct issues with ASan when enabled in the\n> > > > build:\n> > > >\n> > > > - glib has a known leak at initialization time. This is covered by the\n> > > >   suppression file shipped with glib, but it's not clear how to use it\n> > > >   automatically. For now, disable leak detection to avoid test failures.\n> > >\n> > > Are Valgrind suppression usable for Asan ? Glib installs it into:\n> > >\n> > >   /usr/share/glib-2.0/valgrind/glib.supp\n> > >\n> > > For GStreamer suppressions, we don't install them (yet, under discussion). They\n> > > are located in each repos. I would enable GStreamer leak tracer though, it does\n> > > not have such a high run-time overhead, and will detect any GObject or\n> > > GstMiniObject leak, as long as the test calls gst_deinit() at the end (all tests\n> > > should).\n> >\n> > Yes, I think they are usable. I was however unsure whether the path to\n> > the supression file was standard, especially considering embedded\n> > systems. I thus went for a workaround. It would be nice if there was a\n> > standard mechanism to pick suppressions automatically.\n> >\n> > > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > > >   compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> > > >   the libcamera plugin will cause an ASan link order verification\n> > > >   failure. Disable the verification child processes to work around the\n> > > >   problem. This requires gcc 8 or newer.\n> > >\n> > > Have you considered simply disabling forks ? See gst_registry_fork_set_enabled()\n> >\n> > No, as I had no idea that existed :-)\n> \n> Tested this, it works as expected. Now need to figure out to patch\n> gstreamer as it still leaks memory\n> \n> =================================================================\n> ==676137==ERROR: LeakSanitizer: detected memory leaks\n> \n> Direct leak of 16384 byte(s) in 1 object(s) allocated from:\n>     #0 0x7f4faf3b8bc8 in malloc\n> (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)\n>     #1 0x7f4fae763e98 in g_malloc\n> (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)\n> \n> SUMMARY: AddressSanitizer: 16384 byte(s) leaked in 1 allocation(s).\n> ――――――――――――――――――――――――――――――――――――――――\n\nThat's a known glib issue, and that's why ASan leak detection is\ndisabled (in addition to disabling ASan link order verification, which\ncould be dropped in favour of using gst_registry_fork_set_enabled()).\nglib has a valgrind suppression file that could be used by ASan as well,\nbut I wasn't sure how to integrate it correctly in a way that would work\non all distributions.\n\n> > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > ---\n> > > > This version incorporates changes coming from my review of v10, and\n> > > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\n> > > > independent reasons as explained in the commit message. I'd like to find\n> > > > a way to use leak suppression files to handle the glib initialization\n> > > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > > is acceptable in my opinion.\n> > > >\n> > > > The biggest trouble with these workarounds is that they don't work with\n> > > > gcc version older than 8. As the stable version of the most common Linux\n> > > > distributions ship gcc 8 or newer, skipping this test for gcc 7 (which\n> > > > is the oldest version that libcamera supports) is also acceptable in my\n> > > > opinion.\n> > > >\n> > > > Changes since v10:\n> > > >\n> > > > - Disable ASan leak detection\n> > > > - Disable ASan link order verification for child processes\n> > > > - Include source_path.h\n> > > > - Remove unneeded explicit std::string construction\n> > > > - Declare variables at usage site\n> > > > - Make constants constexpr\n> > > > - Add a variable for the message type\n> > > > - Blank space fixes\n> > > > ---\n> > > >  .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> > > >  test/gstreamer/meson.build                    |  19 ++\n> > > >  test/meson.build                              |   1 +\n> > > >  3 files changed, 208 insertions(+)\n> > > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > > >  create mode 100644 test/gstreamer/meson.build\n> > > >\n> > > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > new file mode 100644\n> > > > index 000000000000..e26673b3471a\n> > > > --- /dev/null\n> > > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > @@ -0,0 +1,188 @@\n> > > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > > +/*\n> > > > + * Copyright (C) 2021, Vedant Paranjape\n> > > > + *\n> > > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > > + */\n> > > > +\n> > > > +#include <iostream>\n> > > > +#include <unistd.h>\n> > > > +\n> > > > +#include <libcamera/base/utils.h>\n> > > > +\n> > > > +#include \"libcamera/internal/source_paths.h\"\n> > > > +\n> > > > +#include <gst/gst.h>\n> > > > +\n> > > > +#include \"test.h\"\n> > > > +\n> > > > +using namespace std;\n> > > > +\n> > > > +extern \"C\" {\n> > > > +const char *__asan_default_options()\n> > > > +{\n> > > > +   /*\n> > > > +    * Disable leak detection due to a known global variable initialization\n> > > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > > +    * using a suppression file instead of disabling leak detection.\n> > > > +    */\n> > > > +   return \"detect_leaks=false\";\n> > >\n> > > Have you consider GST_TRACERS=leaks ? It will heck for refcounted object leaks\n> > > on gstreamer/glib side, with very low overhead, and will abort on gst_deinit()\n> > > if it found some leaks.\n> >\n> > Again, no idea it existed :-) It could be enabled in init() below.\n> >\n> > > > +}\n> > > > +}\n> > > > +\n> > > > +class GstreamerSingleStreamTest : public Test\n> > > > +{\n> > > > +protected:\n> > > > +   int init() override\n> > > > +   {\n> > > > +           /*\n> > > > +            * GStreamer spawns a process to run the gst-plugin-scanner\n> > > > +            * helper. If libcamera is compiled with ASan enabled, and as\n> > > > +            * GStreamer is most likely not, this will cause the ASan link\n> > > > +            * order check to fail when gst-plugin-scanner dlopen()s the\n> > > > +            * plugin as many libraries will have already been loaded by\n> > > > +            * then. Work around this issue by disabling the link order\n> > > > +            * check. This will only affect child processes, as ASan is\n> > > > +            * already loaded for this process by the time this code is\n> > > > +            * executed, and should thus hopefully be safe.\n> > > > +            *\n> > > > +            * This option is not available in gcc older than 8, the only\n> > > > +            * option in that case is to skip the test.\n> > > > +            */\n> > > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) && __GNUC__ < 8\n> > > > +           return TestSkip;\n> > > > +#endif\n> > > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > >\n> > > I think gst_registry_fork_set_enabled(FALSE) would be much cleaner, what do you\n> > > think ?\n> >\n> > If it does the job and has no drawback, that's fine with me.\n> >\n> > > > +\n> > > > +           /* Initialize GStreamer */\n> > > > +           GError *errInit = nullptr;\n> > > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > > +                   g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > > > +                              errInit ? errInit->message : \"unknown error\");\n> > > > +                   if (errInit)\n> > > > +                           g_error_free(errInit);\n> > > > +\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /*\n> > > > +            * Remove the system libcamera plugin, if any, and add the\n> > > > +            * plugin from the build directory.\n> > > > +            */\n> > > > +           GstRegistry *registry = gst_registry_get();\n> > > > +           GstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > > > +           if (plugin) {\n> > > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > > +                   gst_object_unref(plugin);\n> > > > +           }\n> > > > +\n> > > > +           std::string path = libcamera::utils::libcameraBuildPath()\n> > > > +                            + \"src/gstreamer\";\n> > > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > > +                   g_printerr(\"Failed to add plugin to registry\\n\");\n> > > > +                   gst_deinit();\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /* Create the elements */\n> > > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > > > +           convert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > >\n> > > Please use fakevideosink instead.\n> >\n> > That's what Vedant was doing, and the element wasn't present on Kieran's\n> > system. For my own culture, what's the advantage of using fakevideosink\n> > over fakesink ?\n> >\n> > > > +\n> > > > +           /* Create the empty pipeline_ */\n> > > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > > +\n> > > > +           if (!pipeline_ || !convert0_ || !sink0_ || !libcameraSrc_) {\n> > >\n> > > pipeline_ cannot be NULL, glib will abort if malloc failed.\n> >\n> > Good point.\n> >\n> > > > +                   g_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > > > +                              pipeline_, convert0_, sink0_, libcameraSrc_);\n> > > > +                   if (pipeline_)\n> > > > +                           gst_object_unref(pipeline_);\n> > > > +                   if (convert0_)\n> > > > +                           gst_object_unref(convert0_);\n> > > > +                   if (sink0_)\n> > > > +                           gst_object_unref(sink0_);\n> > > > +                   if (libcameraSrc_)\n> > > > +                           gst_object_unref(libcameraSrc_);\n> > >\n> > > Use local g_autoptr() and g_steal_pointer() on success perhaps ? This will\n> > > reduce a lot the about of error prone code in failure case. Note that in other\n> > > software I've seen, an abort is used instead, as clean exit is just coding\n> > > overhead for a test.\n> > >\n> > > An alternative, add them immediately to the pipeline, they are not owned by your\n> > > class anyway.\n> > >\n> > > > +                   gst_deinit();\n> > > > +\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           return TestPass;\n> > > > +   }\n> > > > +\n> > > > +   void cleanup() override\n> > > > +   {\n> > > > +           gst_object_unref(pipeline_);\n> > > > +           gst_deinit();\n> > > > +   }\n> > > > +\n> > > > +   int run() override\n> > > > +   {\n> > > > +           GstStateChangeReturn ret;\n> > > > +\n> > > > +           /* Build the pipeline */\n> > > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> > >\n> > > This can fail (e.g. duplicate name).\n> > >\n> > > > +           if (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /* Start playing */\n> > > > +           ret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> > > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > > +                   g_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > > > +                   return TestFail;\n> > > > +           }\n> > > > +\n> > > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > > +           constexpr GstMessageType msgType =\n> > > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > > > +           constexpr GstClockTime timeout = 2000000000;\n> > >\n> > > Perhaps 2 * GST_SECOND.\n> >\n> > Much better.\n> >\n> > > > +\n> > > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > > +           g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > > +\n> > > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > > +\n> > > > +           /* Parse error message */\n> > > > +           if (msg == NULL)\n> > > > +                   return TestPass;\n> > >\n> > > I would like some minimal validation. I would expect that after 2s some frames\n> > > got \"rendered\" properly. You can read the GstStructure property \"stats\" from\n> > > fakevideosink / fakesink, and read the \"rendered\" field. Make sure this not zero\n> > > perhaps ?\n> >\n> > Very good idea.\n> >\n> > Vedant, I think there's work for you :-)\n> >\n> > > > +\n> > > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > > +           case GST_MESSAGE_ERROR:\n> > > > +                   gstreamer_print_error(msg);\n> > > > +                   break;\n> > > > +           case GST_MESSAGE_EOS:\n> > > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > > +                   break;\n> > > > +           default:\n> > > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > > +                   break;\n> > > > +           }\n> > > > +\n> > > > +           return TestFail;\n> > > > +   }\n> > > > +\n> > > > +private:\n> > > > +   void gstreamer_print_error(GstMessage *msg)\n> > > > +   {\n> > > > +           GError *err;\n> > > > +           gchar *debug_info;\n> > > > +\n> > > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > > +                      debug_info ? debug_info : \"none\");\n> > > > +           g_clear_error(&err);\n> > > > +           g_free(debug_info);\n> > > > +   }\n> > > > +\n> > > > +   GstElement *pipeline_;\n> > > > +   GstElement *libcameraSrc_;\n> > > > +   GstElement *convert0_;\n> > > > +   GstElement *sink0_;\n> > > > +};\n> > > > +\n> > > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > > new file mode 100644\n> > > > index 000000000000..b99aa0da0ba3\n> > > > --- /dev/null\n> > > > +++ b/test/gstreamer/meson.build\n> > > > @@ -0,0 +1,19 @@\n> > > > +# SPDX-License-Identifier: CC0-1.0\n> > > > +\n> > > > +if not gst_enabled\n> > > > +    subdir_done()\n> > > > +endif\n> > > > +\n> > > > +gstreamer_tests = [\n> > > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > > +]\n> > > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > > +\n> > > > +foreach t : gstreamer_tests\n> > > > +    exe = executable(t[0], t[1],\n> > > > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > > > +                     link_with : test_libraries,\n> > > > +                     include_directories : test_includes_internal)\n> > > > +\n> > > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > > +endforeach\n> > > > diff --git a/test/meson.build b/test/meson.build\n> > > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > > --- a/test/meson.build\n> > > > +++ b/test/meson.build\n> > > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > > >\n> > > >  subdir('camera')\n> > > >  subdir('controls')\n> > > > +subdir('gstreamer')\n> > > >  subdir('ipa')\n> > > >  subdir('ipc')\n> > > >  subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 6EDDBBD87C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Aug 2021 11:35:36 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DAD8D68890;\n\tWed, 18 Aug 2021 13:35:35 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C73976888A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 13:35:34 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 4F98DEE;\n\tWed, 18 Aug 2021 13:35:34 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"Fqep99MT\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629286534;\n\tbh=wKEwt1njyiHynih8Efc5d0XCQLEK9KC9q65lwwJrjRw=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Fqep99MTWkyUYukYAfnLbfsy+bBS1bz5dGvEi+oK9H0ta1oXkGiDl/d06kveFDOZw\n\tgv6ILS37RhSlLXDA5F7c3pmng/GfGuGNm3Lmxa5hiqoupq7ZwTQXTkj92aYLMkD+J5\n\tO5gzeDagb7XuidSpFARaC0eXVDVbtb4UQBu1k8Tg=","Date":"Wed, 18 Aug 2021 14:35:27 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Message-ID":"<YRzwf7gdqrmCNufu@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>\n\t<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>\n\t<CACGrz-MCkGJ5L+Dn8eBD8wokF75HHjOkofPbwSrStATDv_P_=Q@mail.gmail.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<CACGrz-MCkGJ5L+Dn8eBD8wokF75HHjOkofPbwSrStATDv_P_=Q@mail.gmail.com>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18916,"web_url":"https://patchwork.libcamera.org/comment/18916/","msgid":"<CACGrz-PFbR2Phnouz2kpvTs_R0SST6+OuJmo-60MWW-Qakx1kw@mail.gmail.com>","date":"2021-08-18T12:24:49","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"Hi,\nHow to use the suppression files ?\n\nRegards\nVedant\n\nOn Wed, Aug 18, 2021 at 5:05 PM Laurent Pinchart <\nlaurent.pinchart@ideasonboard.com> wrote:\n\n> Hi Vedant,\n>\n> On Wed, Aug 18, 2021 at 04:54:45PM +0530, Vedant Paranjape wrote:\n> > On Wed, Aug 18, 2021 at 12:04 AM Laurent Pinchart wrote:\n> > > On Tue, Aug 17, 2021 at 12:24:05PM -0400, Nicolas Dufresne wrote:\n> > > > Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> > > > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > >\n> > > > > This patch adds a test to test if single stream using libcamera's\n> > > > > gstreamer element works.\n> > > > >\n> > > > > We need to work around two distinct issues with ASan when enabled\n> in the\n> > > > > build:\n> > > > >\n> > > > > - glib has a known leak at initialization time. This is covered by\n> the\n> > > > >   suppression file shipped with glib, but it's not clear how to\n> use it\n> > > > >   automatically. For now, disable leak detection to avoid test\n> failures.\n> > > >\n> > > > Are Valgrind suppression usable for Asan ? Glib installs it into:\n> > > >\n> > > >   /usr/share/glib-2.0/valgrind/glib.supp\n> > > >\n> > > > For GStreamer suppressions, we don't install them (yet, under\n> discussion). They\n> > > > are located in each repos. I would enable GStreamer leak tracer\n> though, it does\n> > > > not have such a high run-time overhead, and will detect any GObject\n> or\n> > > > GstMiniObject leak, as long as the test calls gst_deinit() at the\n> end (all tests\n> > > > should).\n> > >\n> > > Yes, I think they are usable. I was however unsure whether the path to\n> > > the supression file was standard, especially considering embedded\n> > > systems. I thus went for a workaround. It would be nice if there was a\n> > > standard mechanism to pick suppressions automatically.\n> > >\n> > > > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > > > >   compiled without ASan (which is likely) but libcamera is,\n> dlopen()ing\n> > > > >   the libcamera plugin will cause an ASan link order verification\n> > > > >   failure. Disable the verification child processes to work around\n> the\n> > > > >   problem. This requires gcc 8 or newer.\n> > > >\n> > > > Have you considered simply disabling forks ? See\n> gst_registry_fork_set_enabled()\n> > >\n> > > No, as I had no idea that existed :-)\n> >\n> > Tested this, it works as expected. Now need to figure out to patch\n> > gstreamer as it still leaks memory\n> >\n> > =================================================================\n> > ==676137==ERROR: LeakSanitizer: detected memory leaks\n> >\n> > Direct leak of 16384 byte(s) in 1 object(s) allocated from:\n> >     #0 0x7f4faf3b8bc8 in malloc\n> > (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)\n> >     #1 0x7f4fae763e98 in g_malloc\n> > (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)\n> >\n> > SUMMARY: AddressSanitizer: 16384 byte(s) leaked in 1 allocation(s).\n> > ――――――――――――――――――――――――――――――――――――――――\n>\n> That's a known glib issue, and that's why ASan leak detection is\n> disabled (in addition to disabling ASan link order verification, which\n> could be dropped in favour of using gst_registry_fork_set_enabled()).\n> glib has a valgrind suppression file that could be used by ASan as well,\n> but I wasn't sure how to integrate it correctly in a way that would work\n> on all distributions.\n>\n> > > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com\n> >\n> > > > > ---\n> > > > > This version incorporates changes coming from my review of v10, and\n> > > > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for\n> two\n> > > > > independent reasons as explained in the commit message. I'd like\n> to find\n> > > > > a way to use leak suppression files to handle the glib\n> initialization\n> > > > > leak, but that's a big rabbit hole. The workaround for the second\n> issue\n> > > > > is acceptable in my opinion.\n> > > > >\n> > > > > The biggest trouble with these workarounds is that they don't work\n> with\n> > > > > gcc version older than 8. As the stable version of the most common\n> Linux\n> > > > > distributions ship gcc 8 or newer, skipping this test for gcc 7\n> (which\n> > > > > is the oldest version that libcamera supports) is also acceptable\n> in my\n> > > > > opinion.\n> > > > >\n> > > > > Changes since v10:\n> > > > >\n> > > > > - Disable ASan leak detection\n> > > > > - Disable ASan link order verification for child processes\n> > > > > - Include source_path.h\n> > > > > - Remove unneeded explicit std::string construction\n> > > > > - Declare variables at usage site\n> > > > > - Make constants constexpr\n> > > > > - Add a variable for the message type\n> > > > > - Blank space fixes\n> > > > > ---\n> > > > >  .../gstreamer_single_stream_test.cpp          | 188\n> ++++++++++++++++++\n> > > > >  test/gstreamer/meson.build                    |  19 ++\n> > > > >  test/meson.build                              |   1 +\n> > > > >  3 files changed, 208 insertions(+)\n> > > > >  create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > >  create mode 100644 test/gstreamer/meson.build\n> > > > >\n> > > > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > new file mode 100644\n> > > > > index 000000000000..e26673b3471a\n> > > > > --- /dev/null\n> > > > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > @@ -0,0 +1,188 @@\n> > > > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > > > +/*\n> > > > > + * Copyright (C) 2021, Vedant Paranjape\n> > > > > + *\n> > > > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > > > + */\n> > > > > +\n> > > > > +#include <iostream>\n> > > > > +#include <unistd.h>\n> > > > > +\n> > > > > +#include <libcamera/base/utils.h>\n> > > > > +\n> > > > > +#include \"libcamera/internal/source_paths.h\"\n> > > > > +\n> > > > > +#include <gst/gst.h>\n> > > > > +\n> > > > > +#include \"test.h\"\n> > > > > +\n> > > > > +using namespace std;\n> > > > > +\n> > > > > +extern \"C\" {\n> > > > > +const char *__asan_default_options()\n> > > > > +{\n> > > > > +   /*\n> > > > > +    * Disable leak detection due to a known global variable\n> initialization\n> > > > > +    * leak in glib's g_quark_init(). This should ideally be\n> handled by\n> > > > > +    * using a suppression file instead of disabling leak\n> detection.\n> > > > > +    */\n> > > > > +   return \"detect_leaks=false\";\n> > > >\n> > > > Have you consider GST_TRACERS=leaks ? It will heck for refcounted\n> object leaks\n> > > > on gstreamer/glib side, with very low overhead, and will abort on\n> gst_deinit()\n> > > > if it found some leaks.\n> > >\n> > > Again, no idea it existed :-) It could be enabled in init() below.\n> > >\n> > > > > +}\n> > > > > +}\n> > > > > +\n> > > > > +class GstreamerSingleStreamTest : public Test\n> > > > > +{\n> > > > > +protected:\n> > > > > +   int init() override\n> > > > > +   {\n> > > > > +           /*\n> > > > > +            * GStreamer spawns a process to run the\n> gst-plugin-scanner\n> > > > > +            * helper. If libcamera is compiled with ASan enabled,\n> and as\n> > > > > +            * GStreamer is most likely not, this will cause the\n> ASan link\n> > > > > +            * order check to fail when gst-plugin-scanner\n> dlopen()s the\n> > > > > +            * plugin as many libraries will have already been\n> loaded by\n> > > > > +            * then. Work around this issue by disabling the link\n> order\n> > > > > +            * check. This will only affect child processes, as\n> ASan is\n> > > > > +            * already loaded for this process by the time this\n> code is\n> > > > > +            * executed, and should thus hopefully be safe.\n> > > > > +            *\n> > > > > +            * This option is not available in gcc older than 8,\n> the only\n> > > > > +            * option in that case is to skip the test.\n> > > > > +            */\n> > > > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) &&\n> __GNUC__ < 8\n> > > > > +           return TestSkip;\n> > > > > +#endif\n> > > > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > > >\n> > > > I think gst_registry_fork_set_enabled(FALSE) would be much cleaner,\n> what do you\n> > > > think ?\n> > >\n> > > If it does the job and has no drawback, that's fine with me.\n> > >\n> > > > > +\n> > > > > +           /* Initialize GStreamer */\n> > > > > +           GError *errInit = nullptr;\n> > > > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > > > +                   g_printerr(\"Could not initialize GStreamer:\n> %s\\n\",\n> > > > > +                              errInit ? errInit->message :\n> \"unknown error\");\n> > > > > +                   if (errInit)\n> > > > > +                           g_error_free(errInit);\n> > > > > +\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /*\n> > > > > +            * Remove the system libcamera plugin, if any, and add\n> the\n> > > > > +            * plugin from the build directory.\n> > > > > +            */\n> > > > > +           GstRegistry *registry = gst_registry_get();\n> > > > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n> \"libgstlibcamera.so\");\n> > > > > +           if (plugin) {\n> > > > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > > > +                   gst_object_unref(plugin);\n> > > > > +           }\n> > > > > +\n> > > > > +           std::string path =\n> libcamera::utils::libcameraBuildPath()\n> > > > > +                            + \"src/gstreamer\";\n> > > > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > > > +                   g_printerr(\"Failed to add plugin to\n> registry\\n\");\n> > > > > +                   gst_deinit();\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /* Create the elements */\n> > > > > +           libcameraSrc_ =\n> gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > > > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n> \"convert0\");\n> > > > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > > >\n> > > > Please use fakevideosink instead.\n> > >\n> > > That's what Vedant was doing, and the element wasn't present on\n> Kieran's\n> > > system. For my own culture, what's the advantage of using fakevideosink\n> > > over fakesink ?\n> > >\n> > > > > +\n> > > > > +           /* Create the empty pipeline_ */\n> > > > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > > > +\n> > > > > +           if (!pipeline_ || !convert0_ || !sink0_ ||\n> !libcameraSrc_) {\n> > > >\n> > > > pipeline_ cannot be NULL, glib will abort if malloc failed.\n> > >\n> > > Good point.\n> > >\n> > > > > +                   g_printerr(\"Not all elements could be created.\n> %p.%p.%p.%p\\n\",\n> > > > > +                              pipeline_, convert0_, sink0_,\n> libcameraSrc_);\n> > > > > +                   if (pipeline_)\n> > > > > +                           gst_object_unref(pipeline_);\n> > > > > +                   if (convert0_)\n> > > > > +                           gst_object_unref(convert0_);\n> > > > > +                   if (sink0_)\n> > > > > +                           gst_object_unref(sink0_);\n> > > > > +                   if (libcameraSrc_)\n> > > > > +                           gst_object_unref(libcameraSrc_);\n> > > >\n> > > > Use local g_autoptr() and g_steal_pointer() on success perhaps ?\n> This will\n> > > > reduce a lot the about of error prone code in failure case. Note\n> that in other\n> > > > software I've seen, an abort is used instead, as clean exit is just\n> coding\n> > > > overhead for a test.\n> > > >\n> > > > An alternative, add them immediately to the pipeline, they are not\n> owned by your\n> > > > class anyway.\n> > > >\n> > > > > +                   gst_deinit();\n> > > > > +\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           return TestPass;\n> > > > > +   }\n> > > > > +\n> > > > > +   void cleanup() override\n> > > > > +   {\n> > > > > +           gst_object_unref(pipeline_);\n> > > > > +           gst_deinit();\n> > > > > +   }\n> > > > > +\n> > > > > +   int run() override\n> > > > > +   {\n> > > > > +           GstStateChangeReturn ret;\n> > > > > +\n> > > > > +           /* Build the pipeline */\n> > > > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n> convert0_, sink0_, NULL);\n> > > >\n> > > > This can fail (e.g. duplicate name).\n> > > >\n> > > > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n> sink0_, NULL) != TRUE) {\n> > > > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /* Start playing */\n> > > > > +           ret = gst_element_set_state(pipeline_,\n> GST_STATE_PLAYING);\n> > > > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > > > +                   g_printerr(\"Unable to set the pipeline to the\n> playing state.\\n\");\n> > > > > +                   return TestFail;\n> > > > > +           }\n> > > > > +\n> > > > > +           /* Wait until error or EOS or timeout after 2 seconds\n> */\n> > > > > +           constexpr GstMessageType msgType =\n> > > > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR\n> | GST_MESSAGE_EOS);\n> > > > > +           constexpr GstClockTime timeout = 2000000000;\n> > > >\n> > > > Perhaps 2 * GST_SECOND.\n> > >\n> > > Much better.\n> > >\n> > > > > +\n> > > > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > > > +           g_autoptr(GstMessage) msg =\n> gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > > > +\n> > > > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > > > +\n> > > > > +           /* Parse error message */\n> > > > > +           if (msg == NULL)\n> > > > > +                   return TestPass;\n> > > >\n> > > > I would like some minimal validation. I would expect that after 2s\n> some frames\n> > > > got \"rendered\" properly. You can read the GstStructure property\n> \"stats\" from\n> > > > fakevideosink / fakesink, and read the \"rendered\" field. Make sure\n> this not zero\n> > > > perhaps ?\n> > >\n> > > Very good idea.\n> > >\n> > > Vedant, I think there's work for you :-)\n> > >\n> > > > > +\n> > > > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > > > +           case GST_MESSAGE_ERROR:\n> > > > > +                   gstreamer_print_error(msg);\n> > > > > +                   break;\n> > > > > +           case GST_MESSAGE_EOS:\n> > > > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > > > +                   break;\n> > > > > +           default:\n> > > > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > > > +                   break;\n> > > > > +           }\n> > > > > +\n> > > > > +           return TestFail;\n> > > > > +   }\n> > > > > +\n> > > > > +private:\n> > > > > +   void gstreamer_print_error(GstMessage *msg)\n> > > > > +   {\n> > > > > +           GError *err;\n> > > > > +           gchar *debug_info;\n> > > > > +\n> > > > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > > > +                      debug_info ? debug_info : \"none\");\n> > > > > +           g_clear_error(&err);\n> > > > > +           g_free(debug_info);\n> > > > > +   }\n> > > > > +\n> > > > > +   GstElement *pipeline_;\n> > > > > +   GstElement *libcameraSrc_;\n> > > > > +   GstElement *convert0_;\n> > > > > +   GstElement *sink0_;\n> > > > > +};\n> > > > > +\n> > > > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > > > diff --git a/test/gstreamer/meson.build\n> b/test/gstreamer/meson.build\n> > > > > new file mode 100644\n> > > > > index 000000000000..b99aa0da0ba3\n> > > > > --- /dev/null\n> > > > > +++ b/test/gstreamer/meson.build\n> > > > > @@ -0,0 +1,19 @@\n> > > > > +# SPDX-License-Identifier: CC0-1.0\n> > > > > +\n> > > > > +if not gst_enabled\n> > > > > +    subdir_done()\n> > > > > +endif\n> > > > > +\n> > > > > +gstreamer_tests = [\n> > > > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > > > +]\n> > > > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > > > +\n> > > > > +foreach t : gstreamer_tests\n> > > > > +    exe = executable(t[0], t[1],\n> > > > > +                     dependencies : [libcamera_private,\n> gstreamer_dep],\n> > > > > +                     link_with : test_libraries,\n> > > > > +                     include_directories : test_includes_internal)\n> > > > > +\n> > > > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > > > +endforeach\n> > > > > diff --git a/test/meson.build b/test/meson.build\n> > > > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > > > --- a/test/meson.build\n> > > > > +++ b/test/meson.build\n> > > > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > > > >\n> > > > >  subdir('camera')\n> > > > >  subdir('controls')\n> > > > > +subdir('gstreamer')\n> > > > >  subdir('ipa')\n> > > > >  subdir('ipc')\n> > > > >  subdir('log')\n>\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id C9F50BD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Aug 2021 12:25:03 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EFE5968890;\n\tWed, 18 Aug 2021 14:25:02 +0200 (CEST)","from mail-yb1-xb31.google.com (mail-yb1-xb31.google.com\n\t[IPv6:2607:f8b0:4864:20::b31])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 01C7B6888A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 14:25:01 +0200 (CEST)","by mail-yb1-xb31.google.com with SMTP id a93so4951950ybi.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 05:25:01 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"XJz12NOe\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025;\n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=/ZD1MIAvKWr1illZbrC8RIKezl+fSuNv9hW+e54cK5I=;\n\tb=XJz12NOeJ88TZzYPXraz5Pla6OMMI4oCbY87M/R3TbAeahzHLuNd2YIcIVr8VSNWdf\n\tljWHQ5trpeyWuk1Y4U3W7KBhqaSSbz+qr6IMxAScrTPly4o15M+m8dZQ5g72NT0bBGmt\n\tkCjcJ9ccB0S9dlw2lSY+0Uq/+8Ie9/NPlgobn5VaoFxa1KEvxhhBySsAxcrCzGavovhh\n\tL18xnjqqfZdv+A6HfBo3rWXjsBgaR/rr5V3hMXj3YMmgk3SJcgQU+26Odyndy33i6oc0\n\tHi5qvH9LKR1EiJ32DiP+uCsHg5vu49fJTAeGZGe5d2KopYDB0G1fHzoc0jh8ZgcGRdxO\n\tdCIQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=/ZD1MIAvKWr1illZbrC8RIKezl+fSuNv9hW+e54cK5I=;\n\tb=pHXC43Y5/pI2oyJ4jU2exbXNQZpBcfza63mSRm/z8Tui/Ovp7ndlBk3S5cfiJCHw0v\n\to/MeCWenxGF6rNdrz3Y9PKVwaCYeN40y/gFyblj7SkwmTJQM9qPIli8io+thj3jbU/5I\n\tfGUq0bSPtwEcnyCIGoea3JOYGeIMdyVsWsy+Gi0hxP1OfmTRhoMnF/YSwjEIAXGW0eNI\n\t+ldtcdD2Bwgtg6l0J/lY9j/rg9+L84TcZ8FAyysfsS0dqnIzWL3STj8fsVoWPlrprGxv\n\tRt/i2EEQA6tBpmXmtkTbvOynUw9kpjWOQJSn4aDCVNDLSDi3x8uD06DTgG9elYZhE0lJ\n\tXNjg==","X-Gm-Message-State":"AOAM532y2Ia5A3qxrZPtP/Olfk5AKqDDPpgFdr/v+Jyy1ro6MVlOwjwh\n\t5+jnw+/i+yEjRIqnnGwf8i6Cim6OANy94EjtvLkTZyqJKC0=","X-Google-Smtp-Source":"ABdhPJykzLTWxVLfqJSDmyjNf8s9WzqOi67NYwh9yQcW5lCZP8H5WwYgo4X8C8XZR3nH4eJX+lPVDQvt7N2KtRZmfoE=","X-Received":"by 2002:a25:b787:: with SMTP id\n\tn7mr11124611ybh.468.1629289500723; \n\tWed, 18 Aug 2021 05:25:00 -0700 (PDT)","MIME-Version":"1.0","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>\n\t<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>\n\t<CACGrz-MCkGJ5L+Dn8eBD8wokF75HHjOkofPbwSrStATDv_P_=Q@mail.gmail.com>\n\t<YRzwf7gdqrmCNufu@pendragon.ideasonboard.com>","In-Reply-To":"<YRzwf7gdqrmCNufu@pendragon.ideasonboard.com>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Wed, 18 Aug 2021 17:54:49 +0530","Message-ID":"<CACGrz-PFbR2Phnouz2kpvTs_R0SST6+OuJmo-60MWW-Qakx1kw@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"multipart/alternative; boundary=\"000000000000ed4e7005c9d48515\"","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18918,"web_url":"https://patchwork.libcamera.org/comment/18918/","msgid":"<7adf86db1c227ce28b1dd333155a598bda18e308.camel@ndufresne.ca>","date":"2021-08-18T13:42:23","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le mercredi 18 août 2021 à 17:54 +0530, Vedant Paranjape a écrit :\n> Hi,\n> How to use the suppression files ?\n\nWith valgrind, you pass it through command line argument. The path is standard,\nread the lib prefix from its pc file, and so <prefix>/share/glib-\n2.0/valgrind/glib.supp\n\n> \n> Regards\n> Vedant\n> \n> On Wed, Aug 18, 2021 at 5:05 PM Laurent Pinchart\n> <laurent.pinchart@ideasonboard.com> wrote:\n> > Hi Vedant,\n> > \n> > On Wed, Aug 18, 2021 at 04:54:45PM +0530, Vedant Paranjape wrote:\n> > > On Wed, Aug 18, 2021 at 12:04 AM Laurent Pinchart wrote:\n> > > > On Tue, Aug 17, 2021 at 12:24:05PM -0400, Nicolas Dufresne wrote:\n> > > > > Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> > > > > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > > > \n> > > > > > This patch adds a test to test if single stream using libcamera's\n> > > > > > gstreamer element works.\n> > > > > > \n> > > > > > We need to work around two distinct issues with ASan when enabled in\n> > the\n> > > > > > build:\n> > > > > > \n> > > > > > - glib has a known leak at initialization time. This is covered by\n> > > > > > the\n> > > > > >    suppression file shipped with glib, but it's not clear how to use\n> > > > > > it\n> > > > > >    automatically. For now, disable leak detection to avoid test\n> > failures.\n> > > > > \n> > > > > Are Valgrind suppression usable for Asan ? Glib installs it into:\n> > > > > \n> > > > >    /usr/share/glib-2.0/valgrind/glib.supp\n> > > > > \n> > > > > For GStreamer suppressions, we don't install them (yet, under\n> > discussion). They\n> > > > > are located in each repos. I would enable GStreamer leak tracer\n> > > > > though,\n> > it does\n> > > > > not have such a high run-time overhead, and will detect any GObject or\n> > > > > GstMiniObject leak, as long as the test calls gst_deinit() at the end\n> > (all tests\n> > > > > should).\n> > > > \n> > > > Yes, I think they are usable. I was however unsure whether the path to\n> > > > the supression file was standard, especially considering embedded\n> > > > systems. I thus went for a workaround. It would be nice if there was a\n> > > > standard mechanism to pick suppressions automatically.\n> > > > \n> > > > > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > > > > >    compiled without ASan (which is likely) but libcamera is,\n> > dlopen()ing\n> > > > > >    the libcamera plugin will cause an ASan link order verification\n> > > > > >    failure. Disable the verification child processes to work around\n> > > > > > the\n> > > > > >    problem. This requires gcc 8 or newer.\n> > > > > \n> > > > > Have you considered simply disabling forks ? See\n> > gst_registry_fork_set_enabled()\n> > > > \n> > > > No, as I had no idea that existed :-)\n> > > \n> > > Tested this, it works as expected. Now need to figure out to patch\n> > > gstreamer as it still leaks memory\n> > > \n> > > =================================================================\n> > > ==676137==ERROR: LeakSanitizer: detected memory leaks\n> > > \n> > > Direct leak of 16384 byte(s) in 1 object(s) allocated from:\n> > >      #0 0x7f4faf3b8bc8 in malloc\n> > > (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)\n> > >      #1 0x7f4fae763e98 in g_malloc\n> > > (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)\n> > > \n> > > SUMMARY: AddressSanitizer: 16384 byte(s) leaked in 1 allocation(s).\n> > > ――――――――――――――――――――――――――――――――――――――――\n> > \n> > That's a known glib issue, and that's why ASan leak detection is\n> > disabled (in addition to disabling ASan link order verification, which\n> > could be dropped in favour of using gst_registry_fork_set_enabled()).\n> > glib has a valgrind suppression file that could be used by ASan as well,\n> > but I wasn't sure how to integrate it correctly in a way that would work\n> > on all distributions.\n> > \n> > > > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > > ---\n> > > > > > This version incorporates changes coming from my review of v10, and\n> > > > > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for\n> > > > > > two\n> > > > > > independent reasons as explained in the commit message. I'd like to\n> > find\n> > > > > > a way to use leak suppression files to handle the glib\n> > > > > > initialization\n> > > > > > leak, but that's a big rabbit hole. The workaround for the second\n> > issue\n> > > > > > is acceptable in my opinion.\n> > > > > > \n> > > > > > The biggest trouble with these workarounds is that they don't work\n> > with\n> > > > > > gcc version older than 8. As the stable version of the most common\n> > Linux\n> > > > > > distributions ship gcc 8 or newer, skipping this test for gcc 7\n> > > > > > (which\n> > > > > > is the oldest version that libcamera supports) is also acceptable in\n> > my\n> > > > > > opinion.\n> > > > > > \n> > > > > > Changes since v10:\n> > > > > > \n> > > > > > - Disable ASan leak detection\n> > > > > > - Disable ASan link order verification for child processes\n> > > > > > - Include source_path.h\n> > > > > > - Remove unneeded explicit std::string construction\n> > > > > > - Declare variables at usage site\n> > > > > > - Make constants constexpr\n> > > > > > - Add a variable for the message type\n> > > > > > - Blank space fixes\n> > > > > > ---\n> > > > > >   .../gstreamer_single_stream_test.cpp          | 188\n> > ++++++++++++++++++\n> > > > > >   test/gstreamer/meson.build                    |  19 ++\n> > > > > >   test/meson.build                              |   1 +\n> > > > > >   3 files changed, 208 insertions(+)\n> > > > > >   create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > >   create mode 100644 test/gstreamer/meson.build\n> > > > > > \n> > > > > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> > b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > > new file mode 100644\n> > > > > > index 000000000000..e26673b3471a\n> > > > > > --- /dev/null\n> > > > > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > > @@ -0,0 +1,188 @@\n> > > > > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > > > > +/*\n> > > > > > + * Copyright (C) 2021, Vedant Paranjape\n> > > > > > + *\n> > > > > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > > > > + */\n> > > > > > +\n> > > > > > +#include <iostream>\n> > > > > > +#include <unistd.h>\n> > > > > > +\n> > > > > > +#include <libcamera/base/utils.h>\n> > > > > > +\n> > > > > > +#include \"libcamera/internal/source_paths.h\"\n> > > > > > +\n> > > > > > +#include <gst/gst.h>\n> > > > > > +\n> > > > > > +#include \"test.h\"\n> > > > > > +\n> > > > > > +using namespace std;\n> > > > > > +\n> > > > > > +extern \"C\" {\n> > > > > > +const char *__asan_default_options()\n> > > > > > +{\n> > > > > > +   /*\n> > > > > > +    * Disable leak detection due to a known global variable\n> > initialization\n> > > > > > +    * leak in glib's g_quark_init(). This should ideally be handled\n> > by\n> > > > > > +    * using a suppression file instead of disabling leak detection.\n> > > > > > +    */\n> > > > > > +   return \"detect_leaks=false\";\n> > > > > \n> > > > > Have you consider GST_TRACERS=leaks ? It will heck for refcounted\n> > > > > object\n> > leaks\n> > > > > on gstreamer/glib side, with very low overhead, and will abort on\n> > gst_deinit()\n> > > > > if it found some leaks.\n> > > > \n> > > > Again, no idea it existed :-) It could be enabled in init() below.\n> > > > \n> > > > > > +}\n> > > > > > +}\n> > > > > > +\n> > > > > > +class GstreamerSingleStreamTest : public Test\n> > > > > > +{\n> > > > > > +protected:\n> > > > > > +   int init() override\n> > > > > > +   {\n> > > > > > +           /*\n> > > > > > +            * GStreamer spawns a process to run the gst-plugin-\n> > scanner\n> > > > > > +            * helper. If libcamera is compiled with ASan enabled,\n> > > > > > and\n> > as\n> > > > > > +            * GStreamer is most likely not, this will cause the\n> > > > > > ASan\n> > link\n> > > > > > +            * order check to fail when gst-plugin-scanner dlopen()s\n> > the\n> > > > > > +            * plugin as many libraries will have already been\n> > > > > > loaded\n> > by\n> > > > > > +            * then. Work around this issue by disabling the link\n> > order\n> > > > > > +            * check. This will only affect child processes, as ASan\n> > is\n> > > > > > +            * already loaded for this process by the time this code\n> > is\n> > > > > > +            * executed, and should thus hopefully be safe.\n> > > > > > +            *\n> > > > > > +            * This option is not available in gcc older than 8, the\n> > only\n> > > > > > +            * option in that case is to skip the test.\n> > > > > > +            */\n> > > > > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) &&\n> > > > > > __GNUC__\n> > < 8\n> > > > > > +           return TestSkip;\n> > > > > > +#endif\n> > > > > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > > > > \n> > > > > I think gst_registry_fork_set_enabled(FALSE) would be much cleaner,\n> > > > > what\n> > do you\n> > > > > think ?\n> > > > \n> > > > If it does the job and has no drawback, that's fine with me.\n> > > > \n> > > > > > +\n> > > > > > +           /* Initialize GStreamer */\n> > > > > > +           GError *errInit = nullptr;\n> > > > > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > > > > +                   g_printerr(\"Could not initialize GStreamer:\n> > > > > > %s\\n\",\n> > > > > > +                              errInit ? errInit->message : \"unknown\n> > error\");\n> > > > > > +                   if (errInit)\n> > > > > > +                           g_error_free(errInit);\n> > > > > > +\n> > > > > > +                   return TestFail;\n> > > > > > +           }\n> > > > > > +\n> > > > > > +           /*\n> > > > > > +            * Remove the system libcamera plugin, if any, and add\n> > > > > > the\n> > > > > > +            * plugin from the build directory.\n> > > > > > +            */\n> > > > > > +           GstRegistry *registry = gst_registry_get();\n> > > > > > +           GstPlugin *plugin = gst_registry_lookup(registry,\n> > \"libgstlibcamera.so\");\n> > > > > > +           if (plugin) {\n> > > > > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > > > > +                   gst_object_unref(plugin);\n> > > > > > +           }\n> > > > > > +\n> > > > > > +           std::string path =\n> > > > > > libcamera::utils::libcameraBuildPath()\n> > > > > > +                            + \"src/gstreamer\";\n> > > > > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > > > > +                   g_printerr(\"Failed to add plugin to\n> > > > > > registry\\n\");\n> > > > > > +                   gst_deinit();\n> > > > > > +                   return TestFail;\n> > > > > > +           }\n> > > > > > +\n> > > > > > +           /* Create the elements */\n> > > > > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\",\n> > \"libcamera\");\n> > > > > > +           convert0_ = gst_element_factory_make(\"videoconvert\",\n> > \"convert0\");\n> > > > > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > > > > \n> > > > > Please use fakevideosink instead.\n> > > > \n> > > > That's what Vedant was doing, and the element wasn't present on Kieran's\n> > > > system. For my own culture, what's the advantage of using fakevideosink\n> > > > over fakesink ?\n> > > > \n> > > > > > +\n> > > > > > +           /* Create the empty pipeline_ */\n> > > > > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > > > > +\n> > > > > > +           if (!pipeline_ || !convert0_ || !sink0_ ||\n> > > > > > !libcameraSrc_)\n> > {\n> > > > > \n> > > > > pipeline_ cannot be NULL, glib will abort if malloc failed.\n> > > > \n> > > > Good point.\n> > > > \n> > > > > > +                   g_printerr(\"Not all elements could be created.\n> > %p.%p.%p.%p\\n\",\n> > > > > > +                              pipeline_, convert0_, sink0_,\n> > libcameraSrc_);\n> > > > > > +                   if (pipeline_)\n> > > > > > +                           gst_object_unref(pipeline_);\n> > > > > > +                   if (convert0_)\n> > > > > > +                           gst_object_unref(convert0_);\n> > > > > > +                   if (sink0_)\n> > > > > > +                           gst_object_unref(sink0_);\n> > > > > > +                   if (libcameraSrc_)\n> > > > > > +                           gst_object_unref(libcameraSrc_);\n> > > > > \n> > > > > Use local g_autoptr() and g_steal_pointer() on success perhaps ? This\n> > will\n> > > > > reduce a lot the about of error prone code in failure case. Note that\n> > > > > in\n> > other\n> > > > > software I've seen, an abort is used instead, as clean exit is just\n> > coding\n> > > > > overhead for a test.\n> > > > > \n> > > > > An alternative, add them immediately to the pipeline, they are not\n> > > > > owned\n> > by your\n> > > > > class anyway.\n> > > > > \n> > > > > > +                   gst_deinit();\n> > > > > > +\n> > > > > > +                   return TestFail;\n> > > > > > +           }\n> > > > > > +\n> > > > > > +           return TestPass;\n> > > > > > +   }\n> > > > > > +\n> > > > > > +   void cleanup() override\n> > > > > > +   {\n> > > > > > +           gst_object_unref(pipeline_);\n> > > > > > +           gst_deinit();\n> > > > > > +   }\n> > > > > > +\n> > > > > > +   int run() override\n> > > > > > +   {\n> > > > > > +           GstStateChangeReturn ret;\n> > > > > > +\n> > > > > > +           /* Build the pipeline */\n> > > > > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,\n> > convert0_, sink0_, NULL);\n> > > > > \n> > > > > This can fail (e.g. duplicate name).\n> > > > > \n> > > > > > +           if (gst_element_link_many(libcameraSrc_, convert0_,\n> > sink0_, NULL) != TRUE) {\n> > > > > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > > > > +                   return TestFail;\n> > > > > > +           }\n> > > > > > +\n> > > > > > +           /* Start playing */\n> > > > > > +           ret = gst_element_set_state(pipeline_,\n> > > > > > GST_STATE_PLAYING);\n> > > > > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > > > > +                   g_printerr(\"Unable to set the pipeline to the\n> > playing state.\\n\");\n> > > > > > +                   return TestFail;\n> > > > > > +           }\n> > > > > > +\n> > > > > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > > > > +           constexpr GstMessageType msgType =\n> > > > > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR |\n> > GST_MESSAGE_EOS);\n> > > > > > +           constexpr GstClockTime timeout = 2000000000;\n> > > > > \n> > > > > Perhaps 2 * GST_SECOND.\n> > > > \n> > > > Much better.\n> > > > \n> > > > > > +\n> > > > > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > > > > +           g_autoptr(GstMessage) msg =\n> > gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > > > > +\n> > > > > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > > > > +\n> > > > > > +           /* Parse error message */\n> > > > > > +           if (msg == NULL)\n> > > > > > +                   return TestPass;\n> > > > > \n> > > > > I would like some minimal validation. I would expect that after 2s\n> > > > > some\n> > frames\n> > > > > got \"rendered\" properly. You can read the GstStructure property\n> > > > > \"stats\"\n> > from\n> > > > > fakevideosink / fakesink, and read the \"rendered\" field. Make sure\n> > > > > this\n> > not zero\n> > > > > perhaps ?\n> > > > \n> > > > Very good idea.\n> > > > \n> > > > Vedant, I think there's work for you :-)\n> > > > \n> > > > > > +\n> > > > > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > > > > +           case GST_MESSAGE_ERROR:\n> > > > > > +                   gstreamer_print_error(msg);\n> > > > > > +                   break;\n> > > > > > +           case GST_MESSAGE_EOS:\n> > > > > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > > > > +                   break;\n> > > > > > +           default:\n> > > > > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > > > > +                   break;\n> > > > > > +           }\n> > > > > > +\n> > > > > > +           return TestFail;\n> > > > > > +   }\n> > > > > > +\n> > > > > > +private:\n> > > > > > +   void gstreamer_print_error(GstMessage *msg)\n> > > > > > +   {\n> > > > > > +           GError *err;\n> > > > > > +           gchar *debug_info;\n> > > > > > +\n> > > > > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > > > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > > > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > > > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > > > > +                      debug_info ? debug_info : \"none\");\n> > > > > > +           g_clear_error(&err);\n> > > > > > +           g_free(debug_info);\n> > > > > > +   }\n> > > > > > +\n> > > > > > +   GstElement *pipeline_;\n> > > > > > +   GstElement *libcameraSrc_;\n> > > > > > +   GstElement *convert0_;\n> > > > > > +   GstElement *sink0_;\n> > > > > > +};\n> > > > > > +\n> > > > > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > > > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > > > > new file mode 100644\n> > > > > > index 000000000000..b99aa0da0ba3\n> > > > > > --- /dev/null\n> > > > > > +++ b/test/gstreamer/meson.build\n> > > > > > @@ -0,0 +1,19 @@\n> > > > > > +# SPDX-License-Identifier: CC0-1.0\n> > > > > > +\n> > > > > > +if not gst_enabled\n> > > > > > +    subdir_done()\n> > > > > > +endif\n> > > > > > +\n> > > > > > +gstreamer_tests = [\n> > > > > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > > > > +]\n> > > > > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > > > > +\n> > > > > > +foreach t : gstreamer_tests\n> > > > > > +    exe = executable(t[0], t[1],\n> > > > > > +                     dependencies : [libcamera_private,\n> > gstreamer_dep],\n> > > > > > +                     link_with : test_libraries,\n> > > > > > +                     include_directories : test_includes_internal)\n> > > > > > +\n> > > > > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > > > > +endforeach\n> > > > > > diff --git a/test/meson.build b/test/meson.build\n> > > > > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > > > > --- a/test/meson.build\n> > > > > > +++ b/test/meson.build\n> > > > > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > > > > > \n> > > > > >   subdir('camera')\n> > > > > >   subdir('controls')\n> > > > > > +subdir('gstreamer')\n> > > > > >   subdir('ipa')\n> > > > > >   subdir('ipc')\n> > > > > >   subdir('log')\n> >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 2BF4ABD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Aug 2021 13:42:29 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 91CEB68895;\n\tWed, 18 Aug 2021 15:42:28 +0200 (CEST)","from mail-qk1-x733.google.com (mail-qk1-x733.google.com\n\t[IPv6:2607:f8b0:4864:20::733])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id F200E6888A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 15:42:26 +0200 (CEST)","by mail-qk1-x733.google.com with SMTP id t66so3070334qkb.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 06:42:26 -0700 (PDT)","from nicolas-tpx395.localdomain (173-246-12-168.qc.cable.ebox.net.\n\t[173.246.12.168])\n\tby smtp.gmail.com with ESMTPSA id l29sm33669qtn.8.2021.08.18.06.42.24\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 18 Aug 2021 06:42:24 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=ndufresne-ca.20150623.gappssmtp.com\n\theader.i=@ndufresne-ca.20150623.gappssmtp.com\n\theader.b=\"m9sb5+gD\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ndufresne-ca.20150623.gappssmtp.com; s=20150623;\n\th=message-id:subject:from:to:cc:date:in-reply-to:references\n\t:user-agent:mime-version:content-transfer-encoding;\n\tbh=wkv8vASqwbJhJaDPESGSDX/cCnb9FCcxwPLqHPjr3pI=;\n\tb=m9sb5+gD1rjwTJb2xzLv55wSfc6fYXwHm3bH67wEo/PnFU5N3E4zIJSQKZgWGOJiZq\n\tiRXBg6hth2LWlMGu1vgaq4hlIjDkKmU2pOzDac1/o7lRQuqig9kDzA6WQO4E+7L/hSPa\n\tvtN4XBIfy9hT+O8Ufp5T+ysLpmbSpeagOhqorT3v8omZk05oR1sOC9zpGWUZKjh5kDAa\n\tLqbThZ90iLSW7bBo4FEhWxdD0bkLna7nB8TDnkC0rJiVUS4TyypPySEkffWVB8y1ghx/\n\tS405rns8teOgFuErRJ1DwP/YpGu1ssuc8zu5Glvc/UxVuQ7sl4ZAYaJYessAZ9/KKmwl\n\t7f9w==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:message-id:subject:from:to:cc:date:in-reply-to\n\t:references:user-agent:mime-version:content-transfer-encoding;\n\tbh=wkv8vASqwbJhJaDPESGSDX/cCnb9FCcxwPLqHPjr3pI=;\n\tb=Dg7aGDoAruxhJmLHmps68lUkt3Owx1h1BAzgxND3krNmmoc5/I9vCiPX+ju+dgDjSw\n\tWNP1tXHid0Fur3ki1v7liklW0Xiz8cSlv70m9h71GsetGqxMz5SopnFq3m55qs6hO5Zd\n\tJ359BEB3lP5mDdpJlPE8Eq9bfmtumNax2JKqc5YsspBu9gZhJGanzjReIiJqqYV+rcss\n\tPTMWui5J+lqDcZ1Wk3l0/kLpTqo8o7g3exQFhT2ajDyuWaCncOWeipC9PmYDkSKjQ45C\n\t/9zXd5IPrnQurVyyEQstwB33JokP8sd5gZUx5eZui9r5QImF8gJ9PvjGiSHbj0Awwnap\n\tRm1A==","X-Gm-Message-State":"AOAM530CaI13ijinT4MsXy65a1jteyMTQpWIA8CpC2RGk6/WKZ54as/0\n\t/dAXsgNEGRgp/BH9NFNL1AdXCg==","X-Google-Smtp-Source":"ABdhPJztN4Uf2JG44ctGLNjy8zQnQFNe1nGM3owDFrb7ZuyBPLeMuE10xo97iBgzEW+CzzRr5mQIiw==","X-Received":"by 2002:a37:9cca:: with SMTP id\n\tf193mr9641821qke.368.1629294145653; \n\tWed, 18 Aug 2021 06:42:25 -0700 (PDT)","Message-ID":"<7adf86db1c227ce28b1dd333155a598bda18e308.camel@ndufresne.ca>","From":"Nicolas Dufresne <nicolas@ndufresne.ca>","To":"Vedant Paranjape <vedantparanjape160201@gmail.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>","Date":"Wed, 18 Aug 2021 09:42:23 -0400","In-Reply-To":"<CACGrz-PFbR2Phnouz2kpvTs_R0SST6+OuJmo-60MWW-Qakx1kw@mail.gmail.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>\n\t<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>\n\t<CACGrz-MCkGJ5L+Dn8eBD8wokF75HHjOkofPbwSrStATDv_P_=Q@mail.gmail.com>\n\t<YRzwf7gdqrmCNufu@pendragon.ideasonboard.com>\n\t<CACGrz-PFbR2Phnouz2kpvTs_R0SST6+OuJmo-60MWW-Qakx1kw@mail.gmail.com>","Content-Type":"text/plain; charset=\"UTF-8\"","User-Agent":"Evolution 3.40.3 (3.40.3-1.fc34) ","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":18921,"web_url":"https://patchwork.libcamera.org/comment/18921/","msgid":"<YR00xxAkVVPvVgJQ@pendragon.ideasonboard.com>","date":"2021-08-18T16:26:47","subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"On Wed, Aug 18, 2021 at 09:42:23AM -0400, Nicolas Dufresne wrote:\n> Le mercredi 18 août 2021 à 17:54 +0530, Vedant Paranjape a écrit :\n> > Hi,\n> > How to use the suppression files ?\n> \n> With valgrind, you pass it through command line argument. The path is standard,\n> read the lib prefix from its pc file, and so <prefix>/share/glib-\n> 2.0/valgrind/glib.supp\n\nASan has a similar option, see\nhttps://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags.\n\n> > On Wed, Aug 18, 2021 at 5:05 PM Laurent Pinchart wrote:\n> > > On Wed, Aug 18, 2021 at 04:54:45PM +0530, Vedant Paranjape wrote:\n> > > > On Wed, Aug 18, 2021 at 12:04 AM Laurent Pinchart wrote:\n> > > > > On Tue, Aug 17, 2021 at 12:24:05PM -0400, Nicolas Dufresne wrote:\n> > > > > > Le samedi 14 août 2021 à 02:46 +0300, Laurent Pinchart a écrit :\n> > > > > > > From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > > > > \n> > > > > > > This patch adds a test to test if single stream using libcamera's\n> > > > > > > gstreamer element works.\n> > > > > > > \n> > > > > > > We need to work around two distinct issues with ASan when enabled in the\n> > > > > > > build:\n> > > > > > > \n> > > > > > > - glib has a known leak at initialization time. This is covered by\n> > > > > > > the\n> > > > > > >    suppression file shipped with glib, but it's not clear how to use\n> > > > > > > it\n> > > > > > >    automatically. For now, disable leak detection to avoid test failures.\n> > > > > > \n> > > > > > Are Valgrind suppression usable for Asan ? Glib installs it into:\n> > > > > > \n> > > > > >    /usr/share/glib-2.0/valgrind/glib.supp\n> > > > > > \n> > > > > > For GStreamer suppressions, we don't install them (yet, under discussion). They\n> > > > > > are located in each repos. I would enable GStreamer leak tracer\n> > > > > > though, it does\n> > > > > > not have such a high run-time overhead, and will detect any GObject or\n> > > > > > GstMiniObject leak, as long as the test calls gst_deinit() at the end (all tests\n> > > > > > should).\n> > > > > \n> > > > > Yes, I think they are usable. I was however unsure whether the path to\n> > > > > the supression file was standard, especially considering embedded\n> > > > > systems. I thus went for a workaround. It would be nice if there was a\n> > > > > standard mechanism to pick suppressions automatically.\n> > > > > \n> > > > > > > - GStreamer spawns a child process to scan plugins. If GStreamer is\n> > > > > > >    compiled without ASan (which is likely) but libcamera is, dlopen()ing\n> > > > > > >    the libcamera plugin will cause an ASan link order verification\n> > > > > > >    failure. Disable the verification child processes to work around\n> > > > > > > the\n> > > > > > >    problem. This requires gcc 8 or newer.\n> > > > > > \n> > > > > > Have you considered simply disabling forks ? See gst_registry_fork_set_enabled()\n> > > > > \n> > > > > No, as I had no idea that existed :-)\n> > > > \n> > > > Tested this, it works as expected. Now need to figure out to patch\n> > > > gstreamer as it still leaks memory\n> > > > \n> > > > =================================================================\n> > > > ==676137==ERROR: LeakSanitizer: detected memory leaks\n> > > > \n> > > > Direct leak of 16384 byte(s) in 1 object(s) allocated from:\n> > > >      #0 0x7f4faf3b8bc8 in malloc\n> > > > (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)\n> > > >      #1 0x7f4fae763e98 in g_malloc\n> > > > (/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x57e98)\n> > > > \n> > > > SUMMARY: AddressSanitizer: 16384 byte(s) leaked in 1 allocation(s).\n> > > > ――――――――――――――――――――――――――――――――――――――――\n> > > \n> > > That's a known glib issue, and that's why ASan leak detection is\n> > > disabled (in addition to disabling ASan link order verification, which\n> > > could be dropped in favour of using gst_registry_fork_set_enabled()).\n> > > glib has a valgrind suppression file that could be used by ASan as well,\n> > > but I wasn't sure how to integrate it correctly in a way that would work\n> > > on all distributions.\n> > > \n> > > > > > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > > > > > > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > > > > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > > > > > Tested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\n> > > > > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > > > ---\n> > > > > > > This version incorporates changes coming from my review of v10, and\n> > > > > > > fixes for ASan issues. ASan is a bit of a pain with GStreamer, for\n> > > > > > > two\n> > > > > > > independent reasons as explained in the commit message. I'd like to find\n> > > > > > > a way to use leak suppression files to handle the glib\n> > > > > > > initialization\n> > > > > > > leak, but that's a big rabbit hole. The workaround for the second issue\n> > > > > > > is acceptable in my opinion.\n> > > > > > > \n> > > > > > > The biggest trouble with these workarounds is that they don't work with\n> > > > > > > gcc version older than 8. As the stable version of the most common Linux\n> > > > > > > distributions ship gcc 8 or newer, skipping this test for gcc 7\n> > > > > > > (which\n> > > > > > > is the oldest version that libcamera supports) is also acceptable in my\n> > > > > > > opinion.\n> > > > > > > \n> > > > > > > Changes since v10:\n> > > > > > > \n> > > > > > > - Disable ASan leak detection\n> > > > > > > - Disable ASan link order verification for child processes\n> > > > > > > - Include source_path.h\n> > > > > > > - Remove unneeded explicit std::string construction\n> > > > > > > - Declare variables at usage site\n> > > > > > > - Make constants constexpr\n> > > > > > > - Add a variable for the message type\n> > > > > > > - Blank space fixes\n> > > > > > > ---\n> > > > > > >   .../gstreamer_single_stream_test.cpp          | 188 ++++++++++++++++++\n> > > > > > >   test/gstreamer/meson.build                    |  19 ++\n> > > > > > >   test/meson.build                              |   1 +\n> > > > > > >   3 files changed, 208 insertions(+)\n> > > > > > >   create mode 100644 test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > > >   create mode 100644 test/gstreamer/meson.build\n> > > > > > > \n> > > > > > > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > > > new file mode 100644\n> > > > > > > index 000000000000..e26673b3471a\n> > > > > > > --- /dev/null\n> > > > > > > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > > > > > > @@ -0,0 +1,188 @@\n> > > > > > > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > > > > > > +/*\n> > > > > > > + * Copyright (C) 2021, Vedant Paranjape\n> > > > > > > + *\n> > > > > > > + * ipa_interface_test.cpp - Test the IPA interface\n> > > > > > > + */\n> > > > > > > +\n> > > > > > > +#include <iostream>\n> > > > > > > +#include <unistd.h>\n> > > > > > > +\n> > > > > > > +#include <libcamera/base/utils.h>\n> > > > > > > +\n> > > > > > > +#include \"libcamera/internal/source_paths.h\"\n> > > > > > > +\n> > > > > > > +#include <gst/gst.h>\n> > > > > > > +\n> > > > > > > +#include \"test.h\"\n> > > > > > > +\n> > > > > > > +using namespace std;\n> > > > > > > +\n> > > > > > > +extern \"C\" {\n> > > > > > > +const char *__asan_default_options()\n> > > > > > > +{\n> > > > > > > +   /*\n> > > > > > > +    * Disable leak detection due to a known global variable initialization\n> > > > > > > +    * leak in glib's g_quark_init(). This should ideally be handled by\n> > > > > > > +    * using a suppression file instead of disabling leak detection.\n> > > > > > > +    */\n> > > > > > > +   return \"detect_leaks=false\";\n> > > > > > \n> > > > > > Have you consider GST_TRACERS=leaks ? It will heck for refcounted\n> > > > > > object leaks\n> > > > > > on gstreamer/glib side, with very low overhead, and will abort on gst_deinit()\n> > > > > > if it found some leaks.\n> > > > > \n> > > > > Again, no idea it existed :-) It could be enabled in init() below.\n> > > > > \n> > > > > > > +}\n> > > > > > > +}\n> > > > > > > +\n> > > > > > > +class GstreamerSingleStreamTest : public Test\n> > > > > > > +{\n> > > > > > > +protected:\n> > > > > > > +   int init() override\n> > > > > > > +   {\n> > > > > > > +           /*\n> > > > > > > +            * GStreamer spawns a process to run the gst-plugin- scanner\n> > > > > > > +            * helper. If libcamera is compiled with ASan enabled,\n> > > > > > > and as\n> > > > > > > +            * GStreamer is most likely not, this will cause the\n> > > > > > > ASan link\n> > > > > > > +            * order check to fail when gst-plugin-scanner dlopen()s the\n> > > > > > > +            * plugin as many libraries will have already been\n> > > > > > > loaded by\n> > > > > > > +            * then. Work around this issue by disabling the link order\n> > > > > > > +            * check. This will only affect child processes, as ASan is\n> > > > > > > +            * already loaded for this process by the time this code is\n> > > > > > > +            * executed, and should thus hopefully be safe.\n> > > > > > > +            *\n> > > > > > > +            * This option is not available in gcc older than 8, the only\n> > > > > > > +            * option in that case is to skip the test.\n> > > > > > > +            */\n> > > > > > > +#if defined(__SANITIZE_ADDRESS__) && !defined(__clang__) &&\n> > > > > > > __GNUC__ < 8\n> > > > > > > +           return TestSkip;\n> > > > > > > +#endif\n> > > > > > > +           setenv(\"ASAN_OPTIONS\", \"verify_asan_link_order=0\", 1);\n> > > > > > \n> > > > > > I think gst_registry_fork_set_enabled(FALSE) would be much cleaner,\n> > > > > > what do you\n> > > > > > think ?\n> > > > > \n> > > > > If it does the job and has no drawback, that's fine with me.\n> > > > > \n> > > > > > > +\n> > > > > > > +           /* Initialize GStreamer */\n> > > > > > > +           GError *errInit = nullptr;\n> > > > > > > +           if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > > > > > > +                   g_printerr(\"Could not initialize GStreamer:\n> > > > > > > %s\\n\",\n> > > > > > > +                              errInit ? errInit->message : \"unknown error\");\n> > > > > > > +                   if (errInit)\n> > > > > > > +                           g_error_free(errInit);\n> > > > > > > +\n> > > > > > > +                   return TestFail;\n> > > > > > > +           }\n> > > > > > > +\n> > > > > > > +           /*\n> > > > > > > +            * Remove the system libcamera plugin, if any, and add\n> > > > > > > the\n> > > > > > > +            * plugin from the build directory.\n> > > > > > > +            */\n> > > > > > > +           GstRegistry *registry = gst_registry_get();\n> > > > > > > +           GstPlugin *plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> > > > > > > +           if (plugin) {\n> > > > > > > +                   gst_registry_remove_plugin(registry, plugin);\n> > > > > > > +                   gst_object_unref(plugin);\n> > > > > > > +           }\n> > > > > > > +\n> > > > > > > +           std::string path =\n> > > > > > > libcamera::utils::libcameraBuildPath()\n> > > > > > > +                            + \"src/gstreamer\";\n> > > > > > > +           if (!gst_registry_scan_path(registry, path.c_str())) {\n> > > > > > > +                   g_printerr(\"Failed to add plugin to\n> > > > > > > registry\\n\");\n> > > > > > > +                   gst_deinit();\n> > > > > > > +                   return TestFail;\n> > > > > > > +           }\n> > > > > > > +\n> > > > > > > +           /* Create the elements */\n> > > > > > > +           libcameraSrc_ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > > > > > > +           convert0_ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > > > > > > +           sink0_ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> > > > > > \n> > > > > > Please use fakevideosink instead.\n> > > > > \n> > > > > That's what Vedant was doing, and the element wasn't present on Kieran's\n> > > > > system. For my own culture, what's the advantage of using fakevideosink\n> > > > > over fakesink ?\n> > > > > \n> > > > > > > +\n> > > > > > > +           /* Create the empty pipeline_ */\n> > > > > > > +           pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > > > > > > +\n> > > > > > > +           if (!pipeline_ || !convert0_ || !sink0_ ||\n> > > > > > > !libcameraSrc_) {\n> > > > > > \n> > > > > > pipeline_ cannot be NULL, glib will abort if malloc failed.\n> > > > > \n> > > > > Good point.\n> > > > > \n> > > > > > > +                   g_printerr(\"Not all elements could be created. %p.%p.%p.%p\\n\",\n> > > > > > > +                              pipeline_, convert0_, sink0_, libcameraSrc_);\n> > > > > > > +                   if (pipeline_)\n> > > > > > > +                           gst_object_unref(pipeline_);\n> > > > > > > +                   if (convert0_)\n> > > > > > > +                           gst_object_unref(convert0_);\n> > > > > > > +                   if (sink0_)\n> > > > > > > +                           gst_object_unref(sink0_);\n> > > > > > > +                   if (libcameraSrc_)\n> > > > > > > +                           gst_object_unref(libcameraSrc_);\n> > > > > > \n> > > > > > Use local g_autoptr() and g_steal_pointer() on success perhaps ? This will\n> > > > > > reduce a lot the about of error prone code in failure case. Note that\n> > > > > > in other\n> > > > > > software I've seen, an abort is used instead, as clean exit is just coding\n> > > > > > overhead for a test.\n> > > > > > \n> > > > > > An alternative, add them immediately to the pipeline, they are not\n> > > > > > owned by your\n> > > > > > class anyway.\n> > > > > > \n> > > > > > > +                   gst_deinit();\n> > > > > > > +\n> > > > > > > +                   return TestFail;\n> > > > > > > +           }\n> > > > > > > +\n> > > > > > > +           return TestPass;\n> > > > > > > +   }\n> > > > > > > +\n> > > > > > > +   void cleanup() override\n> > > > > > > +   {\n> > > > > > > +           gst_object_unref(pipeline_);\n> > > > > > > +           gst_deinit();\n> > > > > > > +   }\n> > > > > > > +\n> > > > > > > +   int run() override\n> > > > > > > +   {\n> > > > > > > +           GstStateChangeReturn ret;\n> > > > > > > +\n> > > > > > > +           /* Build the pipeline */\n> > > > > > > +           gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, convert0_, sink0_, NULL);\n> > > > > > \n> > > > > > This can fail (e.g. duplicate name).\n> > > > > > \n> > > > > > > +           if (gst_element_link_many(libcameraSrc_, convert0_, sink0_, NULL) != TRUE) {\n> > > > > > > +                   g_printerr(\"Elements could not be linked.\\n\");\n> > > > > > > +                   return TestFail;\n> > > > > > > +           }\n> > > > > > > +\n> > > > > > > +           /* Start playing */\n> > > > > > > +           ret = gst_element_set_state(pipeline_,\n> > > > > > > GST_STATE_PLAYING);\n> > > > > > > +           if (ret == GST_STATE_CHANGE_FAILURE) {\n> > > > > > > +                   g_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> > > > > > > +                   return TestFail;\n> > > > > > > +           }\n> > > > > > > +\n> > > > > > > +           /* Wait until error or EOS or timeout after 2 seconds */\n> > > > > > > +           constexpr GstMessageType msgType =\n> > > > > > > +                   static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> > > > > > > +           constexpr GstClockTime timeout = 2000000000;\n> > > > > > \n> > > > > > Perhaps 2 * GST_SECOND.\n> > > > > \n> > > > > Much better.\n> > > > > \n> > > > > > > +\n> > > > > > > +           g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > > > > > > +           g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> > > > > > > +\n> > > > > > > +           gst_element_set_state(pipeline_, GST_STATE_NULL);\n> > > > > > > +\n> > > > > > > +           /* Parse error message */\n> > > > > > > +           if (msg == NULL)\n> > > > > > > +                   return TestPass;\n> > > > > > \n> > > > > > I would like some minimal validation. I would expect that after 2s\n> > > > > > some frames\n> > > > > > got \"rendered\" properly. You can read the GstStructure property\n> > > > > > \"stats\" from\n> > > > > > fakevideosink / fakesink, and read the \"rendered\" field. Make sure\n> > > > > > this not zero\n> > > > > > perhaps ?\n> > > > > \n> > > > > Very good idea.\n> > > > > \n> > > > > Vedant, I think there's work for you :-)\n> > > > > \n> > > > > > > +\n> > > > > > > +           switch (GST_MESSAGE_TYPE(msg)) {\n> > > > > > > +           case GST_MESSAGE_ERROR:\n> > > > > > > +                   gstreamer_print_error(msg);\n> > > > > > > +                   break;\n> > > > > > > +           case GST_MESSAGE_EOS:\n> > > > > > > +                   g_print(\"End-Of-Stream reached.\\n\");\n> > > > > > > +                   break;\n> > > > > > > +           default:\n> > > > > > > +                   g_printerr(\"Unexpected message received.\\n\");\n> > > > > > > +                   break;\n> > > > > > > +           }\n> > > > > > > +\n> > > > > > > +           return TestFail;\n> > > > > > > +   }\n> > > > > > > +\n> > > > > > > +private:\n> > > > > > > +   void gstreamer_print_error(GstMessage *msg)\n> > > > > > > +   {\n> > > > > > > +           GError *err;\n> > > > > > > +           gchar *debug_info;\n> > > > > > > +\n> > > > > > > +           gst_message_parse_error(msg, &err, &debug_info);\n> > > > > > > +           g_printerr(\"Error received from element %s: %s\\n\",\n> > > > > > > +                      GST_OBJECT_NAME(msg->src), err->message);\n> > > > > > > +           g_printerr(\"Debugging information: %s\\n\",\n> > > > > > > +                      debug_info ? debug_info : \"none\");\n> > > > > > > +           g_clear_error(&err);\n> > > > > > > +           g_free(debug_info);\n> > > > > > > +   }\n> > > > > > > +\n> > > > > > > +   GstElement *pipeline_;\n> > > > > > > +   GstElement *libcameraSrc_;\n> > > > > > > +   GstElement *convert0_;\n> > > > > > > +   GstElement *sink0_;\n> > > > > > > +};\n> > > > > > > +\n> > > > > > > +TEST_REGISTER(GstreamerSingleStreamTest)\n> > > > > > > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > > > > > > new file mode 100644\n> > > > > > > index 000000000000..b99aa0da0ba3\n> > > > > > > --- /dev/null\n> > > > > > > +++ b/test/gstreamer/meson.build\n> > > > > > > @@ -0,0 +1,19 @@\n> > > > > > > +# SPDX-License-Identifier: CC0-1.0\n> > > > > > > +\n> > > > > > > +if not gst_enabled\n> > > > > > > +    subdir_done()\n> > > > > > > +endif\n> > > > > > > +\n> > > > > > > +gstreamer_tests = [\n> > > > > > > +    ['single_stream_test',   'gstreamer_single_stream_test.cpp'],\n> > > > > > > +]\n> > > > > > > +gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> > > > > > > +\n> > > > > > > +foreach t : gstreamer_tests\n> > > > > > > +    exe = executable(t[0], t[1],\n> > > > > > > +                     dependencies : [libcamera_private, gstreamer_dep],\n> > > > > > > +                     link_with : test_libraries,\n> > > > > > > +                     include_directories : test_includes_internal)\n> > > > > > > +\n> > > > > > > +    test(t[0], exe, suite : 'gstreamer', is_parallel : false)\n> > > > > > > +endforeach\n> > > > > > > diff --git a/test/meson.build b/test/meson.build\n> > > > > > > index 3bceb5df586f..d0466f17d7b6 100644\n> > > > > > > --- a/test/meson.build\n> > > > > > > +++ b/test/meson.build\n> > > > > > > @@ -11,6 +11,7 @@ subdir('libtest')\n> > > > > > > \n> > > > > > >   subdir('camera')\n> > > > > > >   subdir('controls')\n> > > > > > > +subdir('gstreamer')\n> > > > > > >   subdir('ipa')\n> > > > > > >   subdir('ipc')\n> > > > > > >   subdir('log')","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 1850ABD87D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 18 Aug 2021 16:26:59 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5FD0468890;\n\tWed, 18 Aug 2021 18:26:58 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id C74B26888A\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 18 Aug 2021 18:26:56 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3BD9CEE;\n\tWed, 18 Aug 2021 18:26:56 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"ZSlA0l/v\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1629304016;\n\tbh=2rteEEqWml65iGePkOF3MVD6r/rfhrkTwcmCipCieHo=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=ZSlA0l/vEN5hNybvy6ToBg9lCMkt40SjvpvDuzURfmHXf/WHl+2fLvKDfbwHYiI96\n\td2YmYCQU3DKIeJ5P6IM97/vJ+61MzfTS0JNxNdteqiRcK/kbfqbASNg6sq4TpqYPvi\n\te7KcZk8IQKdaCYZkzdrOb8y6O92xh8kirS3nHdh0=","Date":"Wed, 18 Aug 2021 19:26:47 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Nicolas Dufresne <nicolas@ndufresne.ca>","Message-ID":"<YR00xxAkVVPvVgJQ@pendragon.ideasonboard.com>","References":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>\n\t<86a17b74b0371f141edf7cb65372d18998d3c112.camel@ndufresne.ca>\n\t<YRwBRn/Xsxz7OrFK@pendragon.ideasonboard.com>\n\t<CACGrz-MCkGJ5L+Dn8eBD8wokF75HHjOkofPbwSrStATDv_P_=Q@mail.gmail.com>\n\t<YRzwf7gdqrmCNufu@pendragon.ideasonboard.com>\n\t<CACGrz-PFbR2Phnouz2kpvTs_R0SST6+OuJmo-60MWW-Qakx1kw@mail.gmail.com>\n\t<7adf86db1c227ce28b1dd333155a598bda18e308.camel@ndufresne.ca>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<7adf86db1c227ce28b1dd333155a598bda18e308.camel@ndufresne.ca>","Subject":"Re: [libcamera-devel] [PATCH v11] test: gstreamer: Add test for\n\tgstreamer single stream","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org,\n\tVedant Paranjape <vedantparanjape160201@gmail.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]