[{"id":19220,"web_url":"https://patchwork.libcamera.org/comment/19220/","msgid":"<837483ed8df3204ca539c066c6912264312f344a.camel@ndufresne.ca>","date":"2021-08-31T19:42:39","subject":"Re: [libcamera-devel] [PATCH v2] test: gstreamer: Factor out code\n\tinto a base class","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le mardi 31 août 2021 à 12:07 +0530, Vedant Paranjape a écrit :\n> Lot of code used in single stream test is boiler plate and common across\n> every gstreamer test. Factored out this code into a base class called\n> GstreamerTest.\n> \n> Also updated the gstreamer_single_stream_test to use the GstreamerTest\n> base class\n> \n> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> ---\n>  .../gstreamer_single_stream_test.cpp          | 145 +++------------\n>  test/gstreamer/gstreamer_test.cpp             | 165 ++++++++++++++++++\n>  test/gstreamer/gstreamer_test.h               |  39 +++++\n>  test/gstreamer/meson.build                    |   2 +-\n>  4 files changed, 229 insertions(+), 122 deletions(-)\n>  create mode 100644 test/gstreamer/gstreamer_test.cpp\n>  create mode 100644 test/gstreamer/gstreamer_test.h\n> \n> diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp b/test/gstreamer/gstreamer_single_stream_test.cpp\n> index 4c8d4804..c27e4d36 100644\n> --- a/test/gstreamer/gstreamer_single_stream_test.cpp\n> +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> @@ -14,104 +14,48 @@\n>  \n>  #include <gst/gst.h>\n>  \n> +#include \"gstreamer_test.h\"\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> +class GstreamerSingleStreamTest : public GstreamerTest, public Test\n>  {\n> +public:\n> +\tGstreamerSingleStreamTest()\n> +\t\t: GstreamerTest()\n> +\t{\n> +\t}\n> +\n>  protected:\n>  \tint init() override\n>  \t{\n> -\t\t/*\n> -\t\t * GStreamer by default spawns a process to run the\n> -\t\t * gst-plugin-scanner helper. If libcamera is compiled with ASan\n> -\t\t * enabled, and as GStreamer is most likely not, this causes the\n> -\t\t * ASan link order check to fail when gst-plugin-scanner\n> -\t\t * dlopen()s the plugin as many libraries will have already been\n> -\t\t * loaded by then. Fix this issue by disabling spawning of a\n> -\t\t * child helper process when scanning the build directory for\n> -\t\t * plugins.\n> -\t\t */\n> -\t\tgst_registry_fork_set_enabled(false);\n> -\n> -\t\t/* Initialize GStreamer */\n> -\t\tg_autoptr(GError) errInit = NULL;\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\tif (status_ != TestPass)\n> +\t\t\treturn status_;\n>  \n> -\t\t\treturn TestFail;\n> -\t\t}\n> +\t\tg_autoptr(GstElement) convert0__ = gst_element_factory_make(\"videoconvert\", \"convert0\");\n> +\t\tg_autoptr(GstElement) sink0__ = gst_element_factory_make(\"fakesink\", \"sink0\");\n> +\t\tg_object_ref_sink(convert0__);\n> +\t\tg_object_ref_sink(sink0__);\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> +\t\tif (!convert0__ || !sink0__) {\n> +\t\t\tg_printerr(\"Not all elements could be created. %p.%p\\n\",\n> +\t\t\t\t   convert0__, sink0__);\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> +\t\tconvert0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert0__));\n> +\t\tsink0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink0__));\n>  \n> +\t\tif (gstreamer_create_pipeline() != TestPass)\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> @@ -119,57 +63,16 @@ protected:\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\tif (gstreamer_start_pipeline() != TestPass)\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 = 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> -\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> +\t\tif (gstreamer_process_event() != TestPass)\n> +\t\t\treturn TestFail;\n>  \n> -\t\treturn TestFail;\n> +\t\treturn TestPass;\n>  \t}\n>  \n>  private:\n> -\tvoid gstreamer_print_error(GstMessage *msg)\n> -\t{\n> -\t\tg_autoptr(GError) err = NULL;\n> -\t\tg_autofree gchar *debug_info = NULL;\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}\n> -\n> -\tGstElement *pipeline_;\n> -\tGstElement *libcameraSrc_;\n>  \tGstElement *convert0_;\n>  \tGstElement *sink0_;\n>  };\n> diff --git a/test/gstreamer/gstreamer_test.cpp b/test/gstreamer/gstreamer_test.cpp\n> new file mode 100644\n> index 00000000..d9a9618a\n> --- /dev/null\n> +++ b/test/gstreamer/gstreamer_test.cpp\n> @@ -0,0 +1,165 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2021, Vedant Paranjape\n> + *\n> + * libcamera Gstreamer element API tests\n> + */\n> +\n> +#include \"gstreamer_test.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> +GstreamerTest::GstreamerTest()\n> +{\n> +\t/*\n> +\t* GStreamer by default spawns a process to run the\n> +\t* gst-plugin-scanner helper. If libcamera is compiled with ASan\n> +\t* enabled, and as GStreamer is most likely not, this causes the\n> +\t* ASan link order check to fail when gst-plugin-scanner\n> +\t* dlopen()s the plugin as many libraries will have already been\n> +\t* loaded by then. Fix this issue by disabling spawning of a\n> +\t* child helper process when scanning the build directory for\n> +\t* plugins.\n> +\t*/\n> +\tgst_registry_fork_set_enabled(false);\n> +\n> +\t/* Initialize GStreamer */\n> +\tg_autoptr(GError) errInit = NULL;\n> +\tif (!gst_init_check(nullptr, nullptr, &errInit)) {\n> +\t\tg_printerr(\"Could not initialize GStreamer: %s\\n\",\n> +\t\t\t   errInit ? errInit->message : \"unknown error\");\n> +\n> +\t\tstatus_ = TestFail;\n> +\t\treturn;\n> +\t}\n> +\n> +\t/*\n> +\t* Remove the system libcamera plugin, if any, and add the\n> +\t* plugin from the build directory.\n> +\t*/\n> +\tGstRegistry *registry = gst_registry_get();\n> +\tg_autoptr(GstPlugin) plugin = gst_registry_lookup(registry, \"libgstlibcamera.so\");\n> +\tif (plugin) {\n> +\t\tgst_registry_remove_plugin(registry, plugin);\n> +\t}\n> +\n> +\tstd::string path = libcamera::utils::libcameraBuildPath() + \"src/gstreamer\";\n> +\tif (!gst_registry_scan_path(registry, path.c_str())) {\n> +\t\tg_printerr(\"Failed to add plugin to registry\\n\");\n> +\n> +\t\tstatus_ = TestFail;\n> +\t\treturn;\n> +\t}\n> +\n> +\t/* Create the element */\n> +\tg_autoptr(GstElement) libcameraSrc__ = gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> +\tg_object_ref_sink(libcameraSrc__);\n\nI'm a bit insisting, but the base class should not limit to 1 libcamerasrc. One\nof the I fixed recently was to driver two cameras concurrently. I was to write a\ntest when this settle, but it would be quite some work with current case class.\n\nI would like to suggest a virtual function, create_pipeline(), which delegate\ncompletely the creation of the pipeline.\n\n\n> +\n> +\tif (!libcameraSrc__) {\n> +\t\tg_printerr(\"Unable to create libcamera element\\n\");\n> +\n> +\t\tstatus_ = TestFail;\n> +\t\treturn;\n> +\t}\n> +\n> +\tlibcameraSrc_ = reinterpret_cast<GstElement *>(g_steal_pointer(&libcameraSrc__));\n> +\n> +\tstatus_ = TestPass;\n> +}\n> +\n> +GstreamerTest::~GstreamerTest()\n> +{\n> +\tif (pipeline_)\n> +\t\tgst_object_unref(pipeline_);\n> +\tif (status_ == TestFail) {\n> +\t\tgst_object_unref(libcameraSrc_);\n> +\t}\n> +\n> +\tgst_deinit();\n> +}\n> +\n> +int GstreamerTest::gstreamer_create_pipeline()\n> +{\n> +\tpipeline_ = gst_pipeline_new(\"test-pipeline\");\n> +\n> +\tif (!pipeline_) {\n> +\t\tg_printerr(\"Unable to create pipeline\\n\");\n> +\n> +\t\treturn TestFail;\n> +\t}\n> +\n> +\treturn TestPass;\n> +}:la\n> +\n> +int GstreamerTest::gstreamer_start_pipeline()\n> +{\n> +\tGstStateChangeReturn ret;\n> +\n> +\t/* Start playing */\n> +\tret = gst_element_set_state(pipeline_, GST_STATE_PLAYING);\n> +\tif (ret == GST_STATE_CHANGE_FAILURE) {\n> +\t\tg_printerr(\"Unable to set the pipeline to the playing state.\\n\");\n> +\t\treturn TestFail;\n> +\t}\n> +\n> +\treturn TestPass;\n> +}\n> +\n> +int GstreamerTest::gstreamer_process_event()\n> +{\n> +\t/* Wait until error or EOS or timeout after 2 seconds */\n> +\tconstexpr GstMessageType msgType =\n> +\t\tstatic_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS);\n> +\tconstexpr GstClockTime timeout = 2 * GST_SECOND;\n> +\n> +\tg_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> +\tg_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus, timeout, msgType);\n> +\n> +\tgst_element_set_state(pipeline_, GST_STATE_NULL);\n> +\n> +\t/* Parse error message */\n> +\tif (msg == NULL) {\n> +\t\treturn TestPass;\n> +\t}\n> +\n> +\tswitch (GST_MESSAGE_TYPE(msg)) {\n> +\tcase GST_MESSAGE_ERROR:\n> +\t\tgstreamer_print_error(msg);\n> +\t\tbreak;\n> +\tcase GST_MESSAGE_EOS:\n> +\t\tg_print(\"End-Of-Stream reached.\\n\");\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tg_printerr(\"Unexpected message received.\\n\");\n> +\t\tbreak;\n> +\t}\n> +\n> +\treturn TestFail;\n> +}\n> +\n> +void GstreamerTest::gstreamer_print_error(GstMessage *msg)\n> +{\n> +\tg_autoptr(GError) err = NULL;\n> +\tg_autofree gchar *debug_info = NULL;\n> +\n> +\tgst_message_parse_error(msg, &err, &debug_info);\n> +\tg_printerr(\"Error received from element %s: %s\\n\",\n> +\t\t   GST_OBJECT_NAME(msg->src), err->message);\n> +\tg_printerr(\"Debugging information: %s\\n\",\n> +\t\t   debug_info ? debug_info : \"none\");\n> +}\n> +\n> diff --git a/test/gstreamer/gstreamer_test.h b/test/gstreamer/gstreamer_test.h\n> new file mode 100644\n> index 00000000..cdffdea9\n> --- /dev/null\n> +++ b/test/gstreamer/gstreamer_test.h\n> @@ -0,0 +1,39 @@\n> +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> +/*\n> + * Copyright (C) 2021, Vedant Paranjape\n> + *\n> + * gstreamer_test.cpp - GStreamer test base class\n> + */\n> +\n> +#ifndef __LIBCAMERA_GSTREAMER_TEST_H__\n> +#define __LIBCAMERA_GSTREAMER_TEST_H__\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> +using namespace std;\n> +\n> +class GstreamerTest\n> +{\n> +public:\n> +\tGstreamerTest();\n> +\tvirtual ~GstreamerTest();\n> +\n> +protected:\n> +\tvirtual int gstreamer_create_pipeline();\n> +\tint gstreamer_start_pipeline();\n> +\tint gstreamer_process_event();\n> +\tvoid gstreamer_print_error(GstMessage *msg);\n> +\n> +\tGstElement *pipeline_;\n> +\tGstElement *libcameraSrc_;\n> +\tint status_;\n> +};\n> +\n> +#endif /* __LIBCAMERA_GSTREAMER_TEST_H__ */\n> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> index b99aa0da..aca53b92 100644\n> --- a/test/gstreamer/meson.build\n> +++ b/test/gstreamer/meson.build\n> @@ -10,7 +10,7 @@ gstreamer_tests = [\n>  gstreamer_dep = dependency('gstreamer-1.0', required: true)\n>  \n>  foreach t : gstreamer_tests\n> -    exe = executable(t[0], t[1],\n> +    exe = executable(t[0], t[1], 'gstreamer_test.cpp',\n>                       dependencies : [libcamera_private, gstreamer_dep],\n>                       link_with : test_libraries,\n>                       include_directories : test_includes_internal)","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 CEE5FBDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 31 Aug 2021 19:42:47 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CBB676916C;\n\tTue, 31 Aug 2021 21:42:44 +0200 (CEST)","from mail-io1-xd2d.google.com (mail-io1-xd2d.google.com\n\t[IPv6:2607:f8b0:4864:20::d2d])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8A7E668890\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 31 Aug 2021 21:42:42 +0200 (CEST)","by mail-io1-xd2d.google.com with SMTP id b200so303902iof.13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 31 Aug 2021 12:42:42 -0700 (PDT)","from nicolas-tpx395.localdomain (mtl.collabora.ca. [66.171.169.34])\n\tby smtp.gmail.com with ESMTPSA id\n\tp7sm10627906iln.70.2021.08.31.12.42.40\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tTue, 31 Aug 2021 12:42:40 -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=\"yRVL3NOg\"; 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:date:in-reply-to:references:user-agent\n\t:mime-version:content-transfer-encoding;\n\tbh=9eCLBQOTjQmJ4BsTw8a1hscElqIJtt/lLEwZxFKlJPM=;\n\tb=yRVL3NOgdiHaNlmmDAkJB4HhT3Xa2sP7f/4uAlKCyUMRForl9TL+f/vPuZ/dy+O5OH\n\tvSwsIeiFPLbTgi8HE4+2U9cB0oScmBzzU/PJLCoTt/ncjr0UONcksq2wjpRS+UcvOhuJ\n\tO/ZD6zoOSv6aATkLs4XmLXHvlzUdKwDeB0cL9vlYbvnlD/0Vc7YbVf7VbZ5kXNvBE/Mz\n\tSVkjJQ7oaYk7jHZ66e7k5Q/yuuOsld7+80pSnIPAGQ0BAWKIFHovhld+SWMkRwKzmrHK\n\tI28hSadgTNc06xeHJiG0zldKVRO52uvKIlls3jkSdh+VuUNHypX8Yj4CJzj5L1uFYydl\n\tZQgQ==","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:date:in-reply-to\n\t:references:user-agent:mime-version:content-transfer-encoding;\n\tbh=9eCLBQOTjQmJ4BsTw8a1hscElqIJtt/lLEwZxFKlJPM=;\n\tb=aqPfzj+QQL9r3SmDEa5XC4rPx3XT5i1AbmTtnXeCSkWS0bnhxObjk8RoFG6aql80Et\n\t9QUhIyoQnxjBlZiLgtr6H9ZorBslFnqoV6WDL33Tap9HPB722EUS0MbHs0YMO3N1HMOv\n\trb5XqY78A/WgOItsJO7mjN79mYnIcgj+41T3GvH123Ek/OKAq8i14IQ8HuZqD8wBTrFk\n\tkOBpWGOnRlUnMkUMrseseforJzLH+6zwswoqDMeIo1iJLFtsXz7xCH9LGKorekyQSdZW\n\tyIQWh/AEh1+4m2D7z/MG/UIRQvmkaL0mNymZjehTTa89v0mGmV/3cQFd3AdrEFZJ2uUR\n\t3WHA==","X-Gm-Message-State":"AOAM531wcq61v5Qa68R0rT1lDZwmbDsHzmyim6mcGt4mQBSe7ADffX+5\n\t3GDU47Cws3HwzxnQWmSivLmCuQmhRr7vsQ==","X-Google-Smtp-Source":"ABdhPJxrgYXAbiAz226fjxQzpfdf7SezmQMUjxrkq/y8nN/mISVoFMytVhwzNmcWWPDpd0FRo3uzwA==","X-Received":"by 2002:a5d:9653:: with SMTP id\n\td19mr23871653ios.74.1630438961200; \n\tTue, 31 Aug 2021 12:42:41 -0700 (PDT)","Message-ID":"<837483ed8df3204ca539c066c6912264312f344a.camel@ndufresne.ca>","From":"Nicolas Dufresne <nicolas@ndufresne.ca>","To":"Vedant Paranjape <vedantparanjape160201@gmail.com>, \n\tlibcamera-devel@lists.libcamera.org","Date":"Tue, 31 Aug 2021 15:42:39 -0400","In-Reply-To":"<20210831063735.1136615-1-vedantparanjape160201@gmail.com>","References":"<20210831063735.1136615-1-vedantparanjape160201@gmail.com>","Content-Type":"text/plain; charset=\"UTF-8\"","User-Agent":"Evolution 3.40.4 (3.40.4-1.fc34) ","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH v2] test: gstreamer: Factor out code\n\tinto a base class","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>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":19221,"web_url":"https://patchwork.libcamera.org/comment/19221/","msgid":"<CACGrz-MmFXRs9hGaM-5a-+W67=C=AOVUv67rf0K2B=Uj6u4o+w@mail.gmail.com>","date":"2021-08-31T19:45:46","subject":"Re: [libcamera-devel] [PATCH v2] test: gstreamer: Factor out code\n\tinto a base class","submitter":{"id":85,"url":"https://patchwork.libcamera.org/api/people/85/","name":"Vedant Paranjape","email":"vedantparanjape160201@gmail.com"},"content":"On Wed, 1 Sep, 2021, 01:12 Nicolas Dufresne, <nicolas@ndufresne.ca> wrote:\n\n> Le mardi 31 août 2021 à 12:07 +0530, Vedant Paranjape a écrit :\n> > Lot of code used in single stream test is boiler plate and common across\n> > every gstreamer test. Factored out this code into a base class called\n> > GstreamerTest.\n> >\n> > Also updated the gstreamer_single_stream_test to use the GstreamerTest\n> > base class\n> >\n> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>\n> > ---\n> >  .../gstreamer_single_stream_test.cpp          | 145 +++------------\n> >  test/gstreamer/gstreamer_test.cpp             | 165 ++++++++++++++++++\n> >  test/gstreamer/gstreamer_test.h               |  39 +++++\n> >  test/gstreamer/meson.build                    |   2 +-\n> >  4 files changed, 229 insertions(+), 122 deletions(-)\n> >  create mode 100644 test/gstreamer/gstreamer_test.cpp\n> >  create mode 100644 test/gstreamer/gstreamer_test.h\n> >\n> > diff --git a/test/gstreamer/gstreamer_single_stream_test.cpp\n> b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > index 4c8d4804..c27e4d36 100644\n> > --- a/test/gstreamer/gstreamer_single_stream_test.cpp\n> > +++ b/test/gstreamer/gstreamer_single_stream_test.cpp\n> > @@ -14,104 +14,48 @@\n> >\n> >  #include <gst/gst.h>\n> >\n> > +#include \"gstreamer_test.h\"\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> > +class GstreamerSingleStreamTest : public GstreamerTest, public Test\n> >  {\n> > +public:\n> > +     GstreamerSingleStreamTest()\n> > +             : GstreamerTest()\n> > +     {\n> > +     }\n> > +\n> >  protected:\n> >       int init() override\n> >       {\n> > -             /*\n> > -              * GStreamer by default spawns a process to run the\n> > -              * gst-plugin-scanner helper. If libcamera is compiled\n> with ASan\n> > -              * enabled, and as GStreamer is most likely not, this\n> causes the\n> > -              * ASan link order check to fail when gst-plugin-scanner\n> > -              * dlopen()s the plugin as many libraries will have\n> already been\n> > -              * loaded by then. Fix this issue by disabling spawning of\n> a\n> > -              * child helper process when scanning the build directory\n> for\n> > -              * plugins.\n> > -              */\n> > -             gst_registry_fork_set_enabled(false);\n> > -\n> > -             /* Initialize GStreamer */\n> > -             g_autoptr(GError) errInit = NULL;\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 (status_ != TestPass)\n> > +                     return status_;\n> >\n> > -                     return TestFail;\n> > -             }\n> > +             g_autoptr(GstElement) convert0__ =\n> gst_element_factory_make(\"videoconvert\", \"convert0\");\n> > +             g_autoptr(GstElement) sink0__ =\n> gst_element_factory_make(\"fakesink\", \"sink0\");\n> > +             g_object_ref_sink(convert0__);\n> > +             g_object_ref_sink(sink0__);\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> > +             if (!convert0__ || !sink0__) {\n> > +                     g_printerr(\"Not all elements could be created.\n> %p.%p\\n\",\n> > +                                convert0__, sink0__);\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> > +             convert0_ = reinterpret_cast<GstElement\n> *>(g_steal_pointer(&convert0__));\n> > +             sink0_ = reinterpret_cast<GstElement\n> *>(g_steal_pointer(&sink0__));\n> >\n> > +             if (gstreamer_create_pipeline() != TestPass)\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> > @@ -119,57 +63,16 @@ protected:\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> > +             if (gstreamer_start_pipeline() != TestPass)\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 = 2 * GST_SECOND;\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> > -                     break;\n> > -             default:\n> > -                     g_printerr(\"Unexpected message received.\\n\");\n> > -                     break;\n> > -             }\n> > +             if (gstreamer_process_event() != TestPass)\n> > +                     return TestFail;\n> >\n> > -             return TestFail;\n> > +             return TestPass;\n> >       }\n> >\n> >  private:\n> > -     void gstreamer_print_error(GstMessage *msg)\n> > -     {\n> > -             g_autoptr(GError) err = NULL;\n> > -             g_autofree gchar *debug_info = NULL;\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> > -     }\n> > -\n> > -     GstElement *pipeline_;\n> > -     GstElement *libcameraSrc_;\n> >       GstElement *convert0_;\n> >       GstElement *sink0_;\n> >  };\n> > diff --git a/test/gstreamer/gstreamer_test.cpp\n> b/test/gstreamer/gstreamer_test.cpp\n> > new file mode 100644\n> > index 00000000..d9a9618a\n> > --- /dev/null\n> > +++ b/test/gstreamer/gstreamer_test.cpp\n> > @@ -0,0 +1,165 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2021, Vedant Paranjape\n> > + *\n> > + * libcamera Gstreamer element API tests\n> > + */\n> > +\n> > +#include \"gstreamer_test.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> > +GstreamerTest::GstreamerTest()\n> > +{\n> > +     /*\n> > +     * GStreamer by default spawns a process to run the\n> > +     * gst-plugin-scanner helper. If libcamera is compiled with ASan\n> > +     * enabled, and as GStreamer is most likely not, this causes the\n> > +     * ASan link order check to fail when gst-plugin-scanner\n> > +     * dlopen()s the plugin as many libraries will have already been\n> > +     * loaded by then. Fix this issue by disabling spawning of a\n> > +     * child helper process when scanning the build directory for\n> > +     * plugins.\n> > +     */\n> > +     gst_registry_fork_set_enabled(false);\n> > +\n> > +     /* Initialize GStreamer */\n> > +     g_autoptr(GError) errInit = NULL;\n> > +     if (!gst_init_check(nullptr, nullptr, &errInit)) {\n> > +             g_printerr(\"Could not initialize GStreamer: %s\\n\",\n> > +                        errInit ? errInit->message : \"unknown error\");\n> > +\n> > +             status_ = TestFail;\n> > +             return;\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> > +     g_autoptr(GstPlugin) plugin = gst_registry_lookup(registry,\n> \"libgstlibcamera.so\");\n> > +     if (plugin) {\n> > +             gst_registry_remove_plugin(registry, 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> > +\n> > +             status_ = TestFail;\n> > +             return;\n> > +     }\n> > +\n> > +     /* Create the element */\n> > +     g_autoptr(GstElement) libcameraSrc__ =\n> gst_element_factory_make(\"libcamerasrc\", \"libcamera\");\n> > +     g_object_ref_sink(libcameraSrc__);\n>\n> I'm a bit insisting, but the base class should not limit to 1\n> libcamerasrc. One\n> of the I fixed recently was to driver two cameras concurrently. I was to\n> write a\n> test when this settle, but it would be quite some work with current case\n> class.\n>\n> I would like to suggest a virtual function, create_pipeline(), which\n> delegate\n> completely the creation of the pipeline.\n>\n\nOkay so you mean, libcamera element and pipeline making all in one function\ncreate_pipeline, as I have actually defined the function below.\n\n\n>\n>\n> > +\n> > +     if (!libcameraSrc__) {\n> > +             g_printerr(\"Unable to create libcamera element\\n\");\n> > +\n> > +             status_ = TestFail;\n> > +             return;\n> > +     }\n> > +\n> > +     libcameraSrc_ = reinterpret_cast<GstElement\n> *>(g_steal_pointer(&libcameraSrc__));\n> > +\n> > +     status_ = TestPass;\n> > +}\n> > +\n> > +GstreamerTest::~GstreamerTest()\n> > +{\n> > +     if (pipeline_)\n> > +             gst_object_unref(pipeline_);\n> > +     if (status_ == TestFail) {\n> > +             gst_object_unref(libcameraSrc_);\n> > +     }\n> > +\n> > +     gst_deinit();\n> > +}\n> > +\n> > +int GstreamerTest::gstreamer_create_pipeline()\n> > +{\n> > +     pipeline_ = gst_pipeline_new(\"test-pipeline\");\n> > +\n> > +     if (!pipeline_) {\n> > +             g_printerr(\"Unable to create pipeline\\n\");\n> > +\n> > +             return TestFail;\n> > +     }\n> > +\n> > +     return TestPass;\n> > +}:la\n> > +\n> > +int GstreamerTest::gstreamer_start_pipeline()\n> > +{\n> > +     GstStateChangeReturn ret;\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\n> state.\\n\");\n> > +             return TestFail;\n> > +     }\n> > +\n> > +     return TestPass;\n> > +}\n> > +\n> > +int GstreamerTest::gstreamer_process_event()\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 = 2 * GST_SECOND;\n> > +\n> > +     g_autoptr(GstBus) bus = gst_element_get_bus(pipeline_);\n> > +     g_autoptr(GstMessage) msg = gst_bus_timed_pop_filtered(bus,\n> 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> > +\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> > +void GstreamerTest::gstreamer_print_error(GstMessage *msg)\n> > +{\n> > +     g_autoptr(GError) err = NULL;\n> > +     g_autofree gchar *debug_info = NULL;\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> > +}\n> > +\n> > diff --git a/test/gstreamer/gstreamer_test.h\n> b/test/gstreamer/gstreamer_test.h\n> > new file mode 100644\n> > index 00000000..cdffdea9\n> > --- /dev/null\n> > +++ b/test/gstreamer/gstreamer_test.h\n> > @@ -0,0 +1,39 @@\n> > +/* SPDX-License-Identifier: GPL-2.0-or-later */\n> > +/*\n> > + * Copyright (C) 2021, Vedant Paranjape\n> > + *\n> > + * gstreamer_test.cpp - GStreamer test base class\n> > + */\n> > +\n> > +#ifndef __LIBCAMERA_GSTREAMER_TEST_H__\n> > +#define __LIBCAMERA_GSTREAMER_TEST_H__\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> > +using namespace std;\n> > +\n> > +class GstreamerTest\n> > +{\n> > +public:\n> > +     GstreamerTest();\n> > +     virtual ~GstreamerTest();\n> > +\n> > +protected:\n> > +     virtual int gstreamer_create_pipeline();\n> > +     int gstreamer_start_pipeline();\n> > +     int gstreamer_process_event();\n> > +     void gstreamer_print_error(GstMessage *msg);\n> > +\n> > +     GstElement *pipeline_;\n> > +     GstElement *libcameraSrc_;\n> > +     int status_;\n> > +};\n> > +\n> > +#endif /* __LIBCAMERA_GSTREAMER_TEST_H__ */\n> > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build\n> > index b99aa0da..aca53b92 100644\n> > --- a/test/gstreamer/meson.build\n> > +++ b/test/gstreamer/meson.build\n> > @@ -10,7 +10,7 @@ gstreamer_tests = [\n> >  gstreamer_dep = dependency('gstreamer-1.0', required: true)\n> >\n> >  foreach t : gstreamer_tests\n> > -    exe = executable(t[0], t[1],\n> > +    exe = executable(t[0], t[1], 'gstreamer_test.cpp',\n> >                       dependencies : [libcamera_private, gstreamer_dep],\n> >                       link_with : test_libraries,\n> >                       include_directories : test_includes_internal)\n>\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 C062EBDC71\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 31 Aug 2021 19:46:01 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1D3DC69166;\n\tTue, 31 Aug 2021 21:46:01 +0200 (CEST)","from mail-yb1-xb34.google.com (mail-yb1-xb34.google.com\n\t[IPv6:2607:f8b0:4864:20::b34])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0305268890\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 31 Aug 2021 21:45:58 +0200 (CEST)","by mail-yb1-xb34.google.com with SMTP id k65so469684yba.13\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 31 Aug 2021 12:45:58 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"H2Kdcnkn\"; 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=8+wX7JJFE5Ddusme7MmhIxq3C+lPDpHO7fXaBV9+WOg=;\n\tb=H2KdcnknTzatrGjTHaeC4ijY4uqcr7KzfDxbH3lnZjACDP5MoR1Prvr3BblymI879r\n\t27KF1dqFKfcMaEK5UqHcQsJSoWnJ9C8m7E8PYerrT7jSFoduQPzP4DtlTLAZbuLX0wDN\n\tvY37mzvJ8+4oJAXTH+CvMIbDIylo2Gsf2gIfhqaSb8TAUIXe2fyL41KaWADETF6QIlhg\n\tM/gMTX3HqHsEO2hAkOst1h5SVJulOuwCZAZVWKaeD4KdDWtW0iKtqU4MqHqx0qR9AyK+\n\tFKIqjoMXk8NS3QaUxy15Bid7uxX3rbm5Yg5Aa5wdA6Yzzub8lth3t1o7sTzcXCev1ScK\n\tjeeg==","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=8+wX7JJFE5Ddusme7MmhIxq3C+lPDpHO7fXaBV9+WOg=;\n\tb=hjG1CD9XXI41/s3H6JN5MDCjSPXHO969VAO7gN/oe46k4rg5fz0YK110ENA+vK1G2a\n\tRoGeuH3VoO31Tr0AZq65Fdf5cI+GbTPfwKZTH54mjR5heVkBRR8hV5AbhYzzmbiGF5VK\n\tDo44/0QiBC8jmtkQF4D5MCZFsLBKQwCDsjeRZaMJc3MAyVVMMUPQktrCgCHpiAIK4ruE\n\tHbObzEEILVnWhnWUfXpM1RRTccEUlkVUm8IM5xAGwanPxSKjAkL7kdiWjeQqLbiwQNmu\n\tL5+Q8llAkqvJ/766GFBzNQ6YKuhu8PbzWiwjziEonV1z8cWudSkgu08DcLlcIDUA8pzO\n\ts+QA==","X-Gm-Message-State":"AOAM5305se1YYi2s4vAoe66YjMhpxgp79CGH7QY3Awd3sFdInr8MilA8\n\ttR4ZjvXLi9YSZDoKIbd2+KqXKjL8y2K9909jymk=","X-Google-Smtp-Source":"ABdhPJx1C8xr25TpwF9COYzSr0xvseBdjuqoSu1kd+rxFDhNxjnkqkq+83Kt+kT/OndSUJ5hyR7hWFcMBRhohPtAsvY=","X-Received":"by 2002:a25:99c8:: with SMTP id q8mr35742010ybo.63.1630439157626;\n\tTue, 31 Aug 2021 12:45:57 -0700 (PDT)","MIME-Version":"1.0","References":"<20210831063735.1136615-1-vedantparanjape160201@gmail.com>\n\t<837483ed8df3204ca539c066c6912264312f344a.camel@ndufresne.ca>","In-Reply-To":"<837483ed8df3204ca539c066c6912264312f344a.camel@ndufresne.ca>","From":"Vedant Paranjape <vedantparanjape160201@gmail.com>","Date":"Wed, 1 Sep 2021 01:15:46 +0530","Message-ID":"<CACGrz-MmFXRs9hGaM-5a-+W67=C=AOVUv67rf0K2B=Uj6u4o+w@mail.gmail.com>","To":"Nicolas Dufresne <nicolas@ndufresne.ca>","Content-Type":"multipart/alternative; boundary=\"000000000000d17c2105cae032f7\"","Subject":"Re: [libcamera-devel] [PATCH v2] test: gstreamer: Factor out code\n\tinto a base class","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>"}}]