{"id":13352,"url":"https://patchwork.libcamera.org/api/patches/13352/?format=json","web_url":"https://patchwork.libcamera.org/patch/13352/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","date":"2021-08-13T23:46:52","name":"[libcamera-devel,v11] test: gstreamer: Add test for gstreamer single stream","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"c01e018b1368d5a53601af806453e04fc3e49b55","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/13352/mbox/","series":[{"id":2357,"url":"https://patchwork.libcamera.org/api/series/2357/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=2357","date":"2021-08-13T23:46:52","name":"[libcamera-devel,v11] test: gstreamer: Add test for gstreamer single stream","version":11,"mbox":"https://patchwork.libcamera.org/series/2357/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/13352/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/13352/checks/","tags":{},"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 97961C3240\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 13 Aug 2021 23:47:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id ECA276888F;\n\tSat, 14 Aug 2021 01:47: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 5EC6868823\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 14 Aug 2021 01:47:03 +0200 (CEST)","from pendragon.lan (62-78-145-57.bb.dnainternet.fi [62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id C5D6C3F0;\n\tSat, 14 Aug 2021 01:47:02 +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=\"ALN2DAH6\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1628898423;\n\tbh=xx81ZVx4g5H/PDx45IynMlQsm6mNuYVPmKzkTSpmQZ0=;\n\th=From:To:Cc:Subject:Date:From;\n\tb=ALN2DAH6x4gvoG5kxaU0u34VSOy5WtbJL/62qeyLPG/JytiL0fSVVrvLxQ+YBgZaS\n\t0gsTcUkJUhPsdjgIsSL/L/OydPuMiQKOyHR6QUihAV33bku2PsDwemtd1fXmfZ1U2R\n\tFneqDvH5ArT5FVpzhfzhCFYYsAZpD+fuQ6YqAN0E=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Sat, 14 Aug 2021 02:46:52 +0300","Message-Id":"<20210813234652.32200-1-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.31.1","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[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>"},"content":"From: Vedant Paranjape <vedantparanjape160201@gmail.com>\n\nThis patch adds a test to test if single stream using libcamera's\ngstreamer element works.\n\nWe need to work around two distinct issues with ASan when enabled in the\nbuild:\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\nSigned-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nTested-by: Kieran Bingham <kieran.bingham@@ideasonboard.com>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\nThis version incorporates changes coming from my review of v10, and\nfixes for ASan issues. ASan is a bit of a pain with GStreamer, for two\nindependent reasons as explained in the commit message. I'd like to find\na way to use leak suppression files to handle the glib initialization\nleak, but that's a big rabbit hole. The workaround for the second issue\nis acceptable in my opinion.\n\nThe biggest trouble with these workarounds is that they don't work with\ngcc version older than 8. As the stable version of the most common Linux\ndistributions ship gcc 8 or newer, skipping this test for gcc 7 (which\nis the oldest version that libcamera supports) is also acceptable in my\nopinion.\n\nChanges 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","diff":"diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\nnew file mode 100644\nindex 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+\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)\ndiff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\nnew file mode 100644\nindex 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\ndiff --git a/test/meson.build b/test/meson.build\nindex 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","prefixes":["libcamera-devel","v11"]}