<div dir="ltr"><div dir="ltr"><div>Hi Jean-Michel</div><div>Thanks for your review.</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Sep 14, 2021 at 3:53 PM Jean-Michel Hautbois <<a href="mailto:jeanmichel.hautbois@ideasonboard.com">jeanmichel.hautbois@ideasonboard.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Vedant,<br>
<br>
On 14/09/2021 10:46, Vedant Paranjape wrote:<br>
> This patch adds a test to test if multi stream using libcamera's<br>
> gstreamer element works.<br>
<br>
A test to test :-) ?<br>
A proposal (I am not good at writing commit messages though so, please<br>
use it with care :-)):<br></blockquote><div><br></div><div>haha, even the single stream test had same commit message, and it went in, so didn't think about anything different.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
><br>
> Test will run only on devices that support multistream output, i.e.,<br>
> devices that use IPU3 and RPI pipeline. This was tested on a Raspberry<br>
> Pi 4B+<br>
> <br>
> Signed-off-by: Vedant Paranjape <<a href="mailto:vedantparanjape160201@gmail.com" target="_blank">vedantparanjape160201@gmail.com</a>><br>
> Reviewed-by: Paul Elder <<a href="mailto:paul.elder@ideasonboard.com" target="_blank">paul.elder@ideasonboard.com</a>><br>
> Tested-by: Paul Elder <<a href="mailto:paul.elder@ideasonboard.com" target="_blank">paul.elder@ideasonboard.com</a>><br>
> ---<br>
>  .../gstreamer/gstreamer_multi_stream_test.cpp | 138 ++++++++++++++++++<br>
>  test/gstreamer/meson.build                    |   1 +<br>
>  2 files changed, 139 insertions(+)<br>
>  create mode 100644 test/gstreamer/gstreamer_multi_stream_test.cpp<br>
> <br>
> diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp b/test/gstreamer/gstreamer_multi_stream_test.cpp<br>
> new file mode 100644<br>
> index 000000000000..aea9a1dcb211<br>
> --- /dev/null<br>
> +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp<br>
> @@ -0,0 +1,138 @@<br>
> +/* SPDX-License-Identifier: GPL-2.0-or-later */<br>
> +/*<br>
> + * Copyright (C) 2021, Vedant Paranjape<br>
> + *<br>
> + * gstreamer_multi_stream_test.cpp - GStreamer multi stream capture test<br>
> + */<br>
> +<br>
> +#include <iostream><br>
> +#include <unistd.h><br>
> +<br>
> +#include <libcamera/base/utils.h><br>
> +<br>
> +#include <libcamera/libcamera.h><br>
> +<br>
> +#include "libcamera/internal/source_paths.h"<br>
> +<br>
> +#include <gst/gst.h><br>
> +<br>
> +#include "gstreamer_test.h"<br>
> +#include "test.h"<br>
> +<br>
> +using namespace std;<br>
> +<br>
> +class GstreamerMultiStreamTest : public GstreamerTest, public Test<br>
> +{<br>
> +public:<br>
> +     GstreamerMultiStreamTest()<br>
> +             : GstreamerTest()<br>
> +     {<br>
> +     }<br>
> +<br>
> +protected:<br>
> +     int init() override<br>
> +     {<br>
> +             if (status_ != TestPass)<br>
> +                     return status_;<br>
> +<br>
> +             /* Check if platform support multistream output */<br>
> +             libcamera::CameraManager cm;<br>
> +             cm.start();<br>
> +             bool cameraFound = false;<br>
> +             for (auto &camera : cm.cameras()) {<br>
> +                     if (camera->streams().size() > 1) {<br>
> +                             cameraName = camera->id();<br>
> +                             cameraFound = true;<br>
> +                             cm.stop();<br>
> +                             break;<br>
> +                     }<br>
> +             }<br>
> +<br>
> +             if (!cameraFound) {<br>
> +                     cm.stop();<br>
> +                     return TestSkip;<br>
> +             }<br>
> +<br>
> +             g_autoptr(GstElement) convert0 = gst_element_factory_make("videoconvert", "convert0");<br>
> +             g_autoptr(GstElement) convert1 = gst_element_factory_make("videoconvert", "convert1");<br>
<br>
Why do you need a convert element ? AFAIK you are outputting into a<br>
fakesink it should not be an issue ?<br></blockquote><div><br></div><div>This is an artifact from the old code which used autovideosink but was changed to fakesink. I'll test if removing it works, I'd want to use stats property in future, I assume it won't work if I remove videoconvert.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
> +             g_autoptr(GstElement) sink0 = gst_element_factory_make("fakesink", "sink0");<br>
> +             g_autoptr(GstElement) sink1 = gst_element_factory_make("fakesink", "sink1");<br>
> +             g_autoptr(GstElement) queue0 = gst_element_factory_make("queue", "queue0");<br>
> +             g_autoptr(GstElement) queue1 = gst_element_factory_make("queue", "queue1");<br>
> +             g_object_ref_sink(convert0);<br>
> +             g_object_ref_sink(convert1);<br>
> +             g_object_ref_sink(sink0);<br>
> +             g_object_ref_sink(sink1);<br>
> +             g_object_ref_sink(queue0);<br>
> +             g_object_ref_sink(queue1);<br>
<br>
I don't really like that, why can't you use<br>
gst_parse_bin_from_description() for instance ?<br>
That way you would get a bin you could link to libcamerasrc.<br></blockquote><div><br></div><div>I think the answer to that lies in run() function, I'm doing using request pads so, that won't be possible if I just use a bin. I maybe wrong, I'll wait for Nicolas's comment as you said.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Note: it is a long time since I really use GStreamer so I may not be<br>
up-to-date, Nicolas may a different comment, and he would be the one to<br>
follow ;-).<br>
<br>
> +<br>
> +             if (!convert0 || !convert1 || !sink0 || !sink1 || !queue0 || !queue1) {<br>
> +                     g_printerr("Not all elements could be created. %p.%p.%p.%p.%p.%p\n",<br>
> +                                convert0, convert1, sink0, sink1, queue0, queue1);<br>
> +<br>
> +                     return TestFail;<br>
> +             }<br>
> +<br>
> +             if (createPipeline() != TestPass)<br>
> +                     return TestFail;<br>
> +<br>
> +             convert0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert0));<br>
> +             convert1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert1));<br>
> +             sink0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink0));<br>
> +             sink1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink1));<br>
> +             queue0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue0));<br>
> +             queue1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue1));<br>
> +<br>
> +             return TestPass;<br>
> +     }<br>
> +<br>
> +     int run() override<br>
> +     {<br>
> +             g_object_set(libcameraSrc_, "camera-name", cameraName.c_str(), NULL);<br>
> +<br>
> +             /* Build the pipeline */<br>
> +             gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, queue0_, queue1_,<br>
> +                              convert0_, convert1_, sink0_, sink1_, NULL);<br>
> +             if (gst_element_link_many(queue0_, convert0_, sink0_, NULL) != TRUE ||<br>
> +                 gst_element_link_many(queue1_, convert1_, sink1_, NULL) != TRUE) {<br>
> +                     g_printerr("Elements could not be linked.\n");<br>
> +                     return TestFail;<br>
> +             }<br>
> +<br>
> +             g_autoptr(GstPad) src_pad = gst_element_get_static_pad(libcameraSrc_, "src");<br>
> +             g_autoptr(GstPad) request_pad = gst_element_get_request_pad(libcameraSrc_, "src_%u");<br>
> +             GstPad *queue0_sink_pad = gst_element_get_static_pad(queue0_, "sink");<br>
> +             GstPad *queue1_sink_pad = gst_element_get_static_pad(queue1_, "sink");<br>
> +<br>
> +             if (gst_pad_link(src_pad, queue0_sink_pad) != GST_PAD_LINK_OK<br>
> +                     || gst_pad_link(request_pad, queue1_sink_pad) != GST_PAD_LINK_OK) {<br>
> +                     if (queue0_sink_pad)<br>
> +                             gst_object_unref(queue0_sink_pad);<br>
> +                     if (queue1_sink_pad)<br>
> +                             gst_object_unref(queue1_sink_pad);<br>
> +                     g_printerr("Pads could not be linked.\n");<br>
> +                     return TestFail;<br>
> +             }<br>
> +             gst_object_unref(queue0_sink_pad);<br>
> +             gst_object_unref(queue1_sink_pad);<br>
> +<br>
> +             if (startPipeline() != TestPass)<br>
> +                     return TestFail;<br>
> +<br>
> +             if (processEvent() != TestPass)<br>
> +                     return TestFail;<br>
> +<br>
> +             return TestPass;<br>
> +     }<br>
> +<br>
> +private:<br>
> +     std::string cameraName;<br>
> +     GstElement *convert0_;<br>
> +     GstElement *convert1_;<br>
> +     GstElement *sink0_;<br>
> +     GstElement *sink1_;<br>
> +     GstElement *queue0_;<br>
> +     GstElement *queue1_;<br>
> +};<br>
> +<br>
> +TEST_REGISTER(GstreamerMultiStreamTest)<br>
> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build<br>
> index aca53b920365..13652e87d05c 100644<br>
> --- a/test/gstreamer/meson.build<br>
> +++ b/test/gstreamer/meson.build<br>
> @@ -6,6 +6,7 @@ endif<br>
>  <br>
>  gstreamer_tests = [<br>
>      ['single_stream_test',   'gstreamer_single_stream_test.cpp'],<br>
> +    ['multi_stream_test',   'gstreamer_multi_stream_test.cpp'],<br>
>  ]<br>
>  gstreamer_dep = dependency('gstreamer-1.0', required: true)<br>
>  <br>
> <br></blockquote><div><br></div><div>Regards,</div><div><i><b>Vedant Paranjape</b></i><br></div></div></div>