[libcamera-devel,v4] test: gstreamer: Add a test for gstreamer multi stream
diff mbox series

Message ID 20210914084622.118939-1-vedantparanjape160201@gmail.com
State Accepted
Headers show
Series
  • [libcamera-devel,v4] test: gstreamer: Add a test for gstreamer multi stream
Related show

Commit Message

Vedant Paranjape Sept. 14, 2021, 8:46 a.m. UTC
This patch adds a test to test if multi stream using libcamera's
gstreamer element works.

Test will run only on devices that support multistream output, i.e.,
devices that use IPU3 and RPI pipeline. This was tested on a Raspberry
Pi 4B+

Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Tested-by: Paul Elder <paul.elder@ideasonboard.com>
---
 .../gstreamer/gstreamer_multi_stream_test.cpp | 138 ++++++++++++++++++
 test/gstreamer/meson.build                    |   1 +
 2 files changed, 139 insertions(+)
 create mode 100644 test/gstreamer/gstreamer_multi_stream_test.cpp

Comments

Jean-Michel Hautbois Sept. 14, 2021, 10:23 a.m. UTC | #1
Hi Vedant,

On 14/09/2021 10:46, Vedant Paranjape wrote:
> This patch adds a test to test if multi stream using libcamera's
> gstreamer element works.

A test to test :-) ?
A proposal (I am not good at writing commit messages though so, please
use it with care :-)):

> 
> Test will run only on devices that support multistream output, i.e.,
> devices that use IPU3 and RPI pipeline. This was tested on a Raspberry
> Pi 4B+
> 
> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> Tested-by: Paul Elder <paul.elder@ideasonboard.com>
> ---
>  .../gstreamer/gstreamer_multi_stream_test.cpp | 138 ++++++++++++++++++
>  test/gstreamer/meson.build                    |   1 +
>  2 files changed, 139 insertions(+)
>  create mode 100644 test/gstreamer/gstreamer_multi_stream_test.cpp
> 
> diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp b/test/gstreamer/gstreamer_multi_stream_test.cpp
> new file mode 100644
> index 000000000000..aea9a1dcb211
> --- /dev/null
> +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> @@ -0,0 +1,138 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2021, Vedant Paranjape
> + *
> + * gstreamer_multi_stream_test.cpp - GStreamer multi stream capture test
> + */
> +
> +#include <iostream>
> +#include <unistd.h>
> +
> +#include <libcamera/base/utils.h>
> +
> +#include <libcamera/libcamera.h>
> +
> +#include "libcamera/internal/source_paths.h"
> +
> +#include <gst/gst.h>
> +
> +#include "gstreamer_test.h"
> +#include "test.h"
> +
> +using namespace std;
> +
> +class GstreamerMultiStreamTest : public GstreamerTest, public Test
> +{
> +public:
> +	GstreamerMultiStreamTest()
> +		: GstreamerTest()
> +	{
> +	}
> +
> +protected:
> +	int init() override
> +	{
> +		if (status_ != TestPass)
> +			return status_;
> +
> +		/* Check if platform support multistream output */
> +		libcamera::CameraManager cm;
> +		cm.start();
> +		bool cameraFound = false;
> +		for (auto &camera : cm.cameras()) {
> +			if (camera->streams().size() > 1) {
> +				cameraName = camera->id();
> +				cameraFound = true;
> +				cm.stop();
> +				break;
> +			}
> +		}
> +
> +		if (!cameraFound) {
> +			cm.stop();
> +			return TestSkip;
> +		}
> +
> +		g_autoptr(GstElement) convert0 = gst_element_factory_make("videoconvert", "convert0");
> +		g_autoptr(GstElement) convert1 = gst_element_factory_make("videoconvert", "convert1");

Why do you need a convert element ? AFAIK you are outputting into a
fakesink it should not be an issue ?

> +		g_autoptr(GstElement) sink0 = gst_element_factory_make("fakesink", "sink0");
> +		g_autoptr(GstElement) sink1 = gst_element_factory_make("fakesink", "sink1");
> +		g_autoptr(GstElement) queue0 = gst_element_factory_make("queue", "queue0");
> +		g_autoptr(GstElement) queue1 = gst_element_factory_make("queue", "queue1");
> +		g_object_ref_sink(convert0);
> +		g_object_ref_sink(convert1);
> +		g_object_ref_sink(sink0);
> +		g_object_ref_sink(sink1);
> +		g_object_ref_sink(queue0);
> +		g_object_ref_sink(queue1);

I don't really like that, why can't you use
gst_parse_bin_from_description() for instance ?
That way you would get a bin you could link to libcamerasrc.

Note: it is a long time since I really use GStreamer so I may not be
up-to-date, Nicolas may a different comment, and he would be the one to
follow ;-).

> +
> +		if (!convert0 || !convert1 || !sink0 || !sink1 || !queue0 || !queue1) {
> +			g_printerr("Not all elements could be created. %p.%p.%p.%p.%p.%p\n",
> +				   convert0, convert1, sink0, sink1, queue0, queue1);
> +
> +			return TestFail;
> +		}
> +
> +		if (createPipeline() != TestPass)
> +			return TestFail;
> +
> +		convert0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert0));
> +		convert1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert1));
> +		sink0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink0));
> +		sink1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink1));
> +		queue0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue0));
> +		queue1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue1));
> +
> +		return TestPass;
> +	}
> +
> +	int run() override
> +	{
> +		g_object_set(libcameraSrc_, "camera-name", cameraName.c_str(), NULL);
> +
> +		/* Build the pipeline */
> +		gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, queue0_, queue1_,
> +				 convert0_, convert1_, sink0_, sink1_, NULL);
> +		if (gst_element_link_many(queue0_, convert0_, sink0_, NULL) != TRUE ||
> +		    gst_element_link_many(queue1_, convert1_, sink1_, NULL) != TRUE) {
> +			g_printerr("Elements could not be linked.\n");
> +			return TestFail;
> +		}
> +
> +		g_autoptr(GstPad) src_pad = gst_element_get_static_pad(libcameraSrc_, "src");
> +		g_autoptr(GstPad) request_pad = gst_element_get_request_pad(libcameraSrc_, "src_%u");
> +		GstPad *queue0_sink_pad = gst_element_get_static_pad(queue0_, "sink");
> +		GstPad *queue1_sink_pad = gst_element_get_static_pad(queue1_, "sink");
> +
> +		if (gst_pad_link(src_pad, queue0_sink_pad) != GST_PAD_LINK_OK
> +			|| gst_pad_link(request_pad, queue1_sink_pad) != GST_PAD_LINK_OK) {
> +			if (queue0_sink_pad)
> +				gst_object_unref(queue0_sink_pad);
> +			if (queue1_sink_pad)
> +				gst_object_unref(queue1_sink_pad);
> +			g_printerr("Pads could not be linked.\n");
> +			return TestFail;
> +		}
> +		gst_object_unref(queue0_sink_pad);
> +		gst_object_unref(queue1_sink_pad);
> +
> +		if (startPipeline() != TestPass)
> +			return TestFail;
> +
> +		if (processEvent() != TestPass)
> +			return TestFail;
> +
> +		return TestPass;
> +	}
> +
> +private:
> +	std::string cameraName;
> +	GstElement *convert0_;
> +	GstElement *convert1_;
> +	GstElement *sink0_;
> +	GstElement *sink1_;
> +	GstElement *queue0_;
> +	GstElement *queue1_;
> +};
> +
> +TEST_REGISTER(GstreamerMultiStreamTest)
> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
> index aca53b920365..13652e87d05c 100644
> --- a/test/gstreamer/meson.build
> +++ b/test/gstreamer/meson.build
> @@ -6,6 +6,7 @@ endif
>  
>  gstreamer_tests = [
>      ['single_stream_test',   'gstreamer_single_stream_test.cpp'],
> +    ['multi_stream_test',   'gstreamer_multi_stream_test.cpp'],
>  ]
>  gstreamer_dep = dependency('gstreamer-1.0', required: true)
>  
>
Jean-Michel Hautbois Sept. 14, 2021, 10:24 a.m. UTC | #2
On 14/09/2021 12:23, Jean-Michel Hautbois wrote:
> Hi Vedant,
> 
> On 14/09/2021 10:46, Vedant Paranjape wrote:
>> This patch adds a test to test if multi stream using libcamera's
>> gstreamer element works.
> 
> A test to test :-) ?
> A proposal (I am not good at writing commit messages though so, please
> use it with care :-)):

I obviously forgot to paste it... :-/ and now I lost it...
Basically, introduce the fact we are not testing it and just say what
this test does (outputs two streams in fakesink if multi-stream is
supported by the device).

> 
>>
>> Test will run only on devices that support multistream output, i.e.,
>> devices that use IPU3 and RPI pipeline. This was tested on a Raspberry
>> Pi 4B+
>>
>> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
>> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
>> Tested-by: Paul Elder <paul.elder@ideasonboard.com>
>> ---
>>  .../gstreamer/gstreamer_multi_stream_test.cpp | 138 ++++++++++++++++++
>>  test/gstreamer/meson.build                    |   1 +
>>  2 files changed, 139 insertions(+)
>>  create mode 100644 test/gstreamer/gstreamer_multi_stream_test.cpp
>>
>> diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp b/test/gstreamer/gstreamer_multi_stream_test.cpp
>> new file mode 100644
>> index 000000000000..aea9a1dcb211
>> --- /dev/null
>> +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
>> @@ -0,0 +1,138 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright (C) 2021, Vedant Paranjape
>> + *
>> + * gstreamer_multi_stream_test.cpp - GStreamer multi stream capture test
>> + */
>> +
>> +#include <iostream>
>> +#include <unistd.h>
>> +
>> +#include <libcamera/base/utils.h>
>> +
>> +#include <libcamera/libcamera.h>
>> +
>> +#include "libcamera/internal/source_paths.h"
>> +
>> +#include <gst/gst.h>
>> +
>> +#include "gstreamer_test.h"
>> +#include "test.h"
>> +
>> +using namespace std;
>> +
>> +class GstreamerMultiStreamTest : public GstreamerTest, public Test
>> +{
>> +public:
>> +	GstreamerMultiStreamTest()
>> +		: GstreamerTest()
>> +	{
>> +	}
>> +
>> +protected:
>> +	int init() override
>> +	{
>> +		if (status_ != TestPass)
>> +			return status_;
>> +
>> +		/* Check if platform support multistream output */
>> +		libcamera::CameraManager cm;
>> +		cm.start();
>> +		bool cameraFound = false;
>> +		for (auto &camera : cm.cameras()) {
>> +			if (camera->streams().size() > 1) {
>> +				cameraName = camera->id();
>> +				cameraFound = true;
>> +				cm.stop();
>> +				break;
>> +			}
>> +		}
>> +
>> +		if (!cameraFound) {
>> +			cm.stop();
>> +			return TestSkip;
>> +		}
>> +
>> +		g_autoptr(GstElement) convert0 = gst_element_factory_make("videoconvert", "convert0");
>> +		g_autoptr(GstElement) convert1 = gst_element_factory_make("videoconvert", "convert1");
> 
> Why do you need a convert element ? AFAIK you are outputting into a
> fakesink it should not be an issue ?
> 
>> +		g_autoptr(GstElement) sink0 = gst_element_factory_make("fakesink", "sink0");
>> +		g_autoptr(GstElement) sink1 = gst_element_factory_make("fakesink", "sink1");
>> +		g_autoptr(GstElement) queue0 = gst_element_factory_make("queue", "queue0");
>> +		g_autoptr(GstElement) queue1 = gst_element_factory_make("queue", "queue1");
>> +		g_object_ref_sink(convert0);
>> +		g_object_ref_sink(convert1);
>> +		g_object_ref_sink(sink0);
>> +		g_object_ref_sink(sink1);
>> +		g_object_ref_sink(queue0);
>> +		g_object_ref_sink(queue1);
> 
> I don't really like that, why can't you use
> gst_parse_bin_from_description() for instance ?
> That way you would get a bin you could link to libcamerasrc.
> 
> Note: it is a long time since I really use GStreamer so I may not be
> up-to-date, Nicolas may a different comment, and he would be the one to
> follow ;-).
> 
>> +
>> +		if (!convert0 || !convert1 || !sink0 || !sink1 || !queue0 || !queue1) {
>> +			g_printerr("Not all elements could be created. %p.%p.%p.%p.%p.%p\n",
>> +				   convert0, convert1, sink0, sink1, queue0, queue1);
>> +
>> +			return TestFail;
>> +		}
>> +
>> +		if (createPipeline() != TestPass)
>> +			return TestFail;
>> +
>> +		convert0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert0));
>> +		convert1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert1));
>> +		sink0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink0));
>> +		sink1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink1));
>> +		queue0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue0));
>> +		queue1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue1));
>> +
>> +		return TestPass;
>> +	}
>> +
>> +	int run() override
>> +	{
>> +		g_object_set(libcameraSrc_, "camera-name", cameraName.c_str(), NULL);
>> +
>> +		/* Build the pipeline */
>> +		gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, queue0_, queue1_,
>> +				 convert0_, convert1_, sink0_, sink1_, NULL);
>> +		if (gst_element_link_many(queue0_, convert0_, sink0_, NULL) != TRUE ||
>> +		    gst_element_link_many(queue1_, convert1_, sink1_, NULL) != TRUE) {
>> +			g_printerr("Elements could not be linked.\n");
>> +			return TestFail;
>> +		}
>> +
>> +		g_autoptr(GstPad) src_pad = gst_element_get_static_pad(libcameraSrc_, "src");
>> +		g_autoptr(GstPad) request_pad = gst_element_get_request_pad(libcameraSrc_, "src_%u");
>> +		GstPad *queue0_sink_pad = gst_element_get_static_pad(queue0_, "sink");
>> +		GstPad *queue1_sink_pad = gst_element_get_static_pad(queue1_, "sink");
>> +
>> +		if (gst_pad_link(src_pad, queue0_sink_pad) != GST_PAD_LINK_OK
>> +			|| gst_pad_link(request_pad, queue1_sink_pad) != GST_PAD_LINK_OK) {
>> +			if (queue0_sink_pad)
>> +				gst_object_unref(queue0_sink_pad);
>> +			if (queue1_sink_pad)
>> +				gst_object_unref(queue1_sink_pad);
>> +			g_printerr("Pads could not be linked.\n");
>> +			return TestFail;
>> +		}
>> +		gst_object_unref(queue0_sink_pad);
>> +		gst_object_unref(queue1_sink_pad);
>> +
>> +		if (startPipeline() != TestPass)
>> +			return TestFail;
>> +
>> +		if (processEvent() != TestPass)
>> +			return TestFail;
>> +
>> +		return TestPass;
>> +	}
>> +
>> +private:
>> +	std::string cameraName;
>> +	GstElement *convert0_;
>> +	GstElement *convert1_;
>> +	GstElement *sink0_;
>> +	GstElement *sink1_;
>> +	GstElement *queue0_;
>> +	GstElement *queue1_;
>> +};
>> +
>> +TEST_REGISTER(GstreamerMultiStreamTest)
>> diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
>> index aca53b920365..13652e87d05c 100644
>> --- a/test/gstreamer/meson.build
>> +++ b/test/gstreamer/meson.build
>> @@ -6,6 +6,7 @@ endif
>>  
>>  gstreamer_tests = [
>>      ['single_stream_test',   'gstreamer_single_stream_test.cpp'],
>> +    ['multi_stream_test',   'gstreamer_multi_stream_test.cpp'],
>>  ]
>>  gstreamer_dep = dependency('gstreamer-1.0', required: true)
>>  
>>
Vedant Paranjape Sept. 14, 2021, 10:39 a.m. UTC | #3
Hi Jean-Michel
Thanks for your review.


On Tue, Sep 14, 2021 at 3:53 PM Jean-Michel Hautbois <
jeanmichel.hautbois@ideasonboard.com> wrote:

> Hi Vedant,
>
> On 14/09/2021 10:46, Vedant Paranjape wrote:
> > This patch adds a test to test if multi stream using libcamera's
> > gstreamer element works.
>
> A test to test :-) ?
> A proposal (I am not good at writing commit messages though so, please
> use it with care :-)):
>

haha, even the single stream test had same commit message, and it went in,
so didn't think about anything different.

>
> > Test will run only on devices that support multistream output, i.e.,
> > devices that use IPU3 and RPI pipeline. This was tested on a Raspberry
> > Pi 4B+
> >
> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> > Tested-by: Paul Elder <paul.elder@ideasonboard.com>
> > ---
> >  .../gstreamer/gstreamer_multi_stream_test.cpp | 138 ++++++++++++++++++
> >  test/gstreamer/meson.build                    |   1 +
> >  2 files changed, 139 insertions(+)
> >  create mode 100644 test/gstreamer/gstreamer_multi_stream_test.cpp
> >
> > diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp
> b/test/gstreamer/gstreamer_multi_stream_test.cpp
> > new file mode 100644
> > index 000000000000..aea9a1dcb211
> > --- /dev/null
> > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> > @@ -0,0 +1,138 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
> > +/*
> > + * Copyright (C) 2021, Vedant Paranjape
> > + *
> > + * gstreamer_multi_stream_test.cpp - GStreamer multi stream capture test
> > + */
> > +
> > +#include <iostream>
> > +#include <unistd.h>
> > +
> > +#include <libcamera/base/utils.h>
> > +
> > +#include <libcamera/libcamera.h>
> > +
> > +#include "libcamera/internal/source_paths.h"
> > +
> > +#include <gst/gst.h>
> > +
> > +#include "gstreamer_test.h"
> > +#include "test.h"
> > +
> > +using namespace std;
> > +
> > +class GstreamerMultiStreamTest : public GstreamerTest, public Test
> > +{
> > +public:
> > +     GstreamerMultiStreamTest()
> > +             : GstreamerTest()
> > +     {
> > +     }
> > +
> > +protected:
> > +     int init() override
> > +     {
> > +             if (status_ != TestPass)
> > +                     return status_;
> > +
> > +             /* Check if platform support multistream output */
> > +             libcamera::CameraManager cm;
> > +             cm.start();
> > +             bool cameraFound = false;
> > +             for (auto &camera : cm.cameras()) {
> > +                     if (camera->streams().size() > 1) {
> > +                             cameraName = camera->id();
> > +                             cameraFound = true;
> > +                             cm.stop();
> > +                             break;
> > +                     }
> > +             }
> > +
> > +             if (!cameraFound) {
> > +                     cm.stop();
> > +                     return TestSkip;
> > +             }
> > +
> > +             g_autoptr(GstElement) convert0 =
> gst_element_factory_make("videoconvert", "convert0");
> > +             g_autoptr(GstElement) convert1 =
> gst_element_factory_make("videoconvert", "convert1");
>
> Why do you need a convert element ? AFAIK you are outputting into a
> fakesink it should not be an issue ?
>

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.

> +             g_autoptr(GstElement) sink0 =
> gst_element_factory_make("fakesink", "sink0");
> > +             g_autoptr(GstElement) sink1 =
> gst_element_factory_make("fakesink", "sink1");
> > +             g_autoptr(GstElement) queue0 =
> gst_element_factory_make("queue", "queue0");
> > +             g_autoptr(GstElement) queue1 =
> gst_element_factory_make("queue", "queue1");
> > +             g_object_ref_sink(convert0);
> > +             g_object_ref_sink(convert1);
> > +             g_object_ref_sink(sink0);
> > +             g_object_ref_sink(sink1);
> > +             g_object_ref_sink(queue0);
> > +             g_object_ref_sink(queue1);
>
> I don't really like that, why can't you use
> gst_parse_bin_from_description() for instance ?
> That way you would get a bin you could link to libcamerasrc.
>

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.

Note: it is a long time since I really use GStreamer so I may not be
> up-to-date, Nicolas may a different comment, and he would be the one to
> follow ;-).
>
> > +
> > +             if (!convert0 || !convert1 || !sink0 || !sink1 || !queue0
> || !queue1) {
> > +                     g_printerr("Not all elements could be created.
> %p.%p.%p.%p.%p.%p\n",
> > +                                convert0, convert1, sink0, sink1,
> queue0, queue1);
> > +
> > +                     return TestFail;
> > +             }
> > +
> > +             if (createPipeline() != TestPass)
> > +                     return TestFail;
> > +
> > +             convert0_ = reinterpret_cast<GstElement
> *>(g_steal_pointer(&convert0));
> > +             convert1_ = reinterpret_cast<GstElement
> *>(g_steal_pointer(&convert1));
> > +             sink0_ = reinterpret_cast<GstElement
> *>(g_steal_pointer(&sink0));
> > +             sink1_ = reinterpret_cast<GstElement
> *>(g_steal_pointer(&sink1));
> > +             queue0_ = reinterpret_cast<GstElement
> *>(g_steal_pointer(&queue0));
> > +             queue1_ = reinterpret_cast<GstElement
> *>(g_steal_pointer(&queue1));
> > +
> > +             return TestPass;
> > +     }
> > +
> > +     int run() override
> > +     {
> > +             g_object_set(libcameraSrc_, "camera-name",
> cameraName.c_str(), NULL);
> > +
> > +             /* Build the pipeline */
> > +             gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,
> queue0_, queue1_,
> > +                              convert0_, convert1_, sink0_, sink1_,
> NULL);
> > +             if (gst_element_link_many(queue0_, convert0_, sink0_,
> NULL) != TRUE ||
> > +                 gst_element_link_many(queue1_, convert1_, sink1_,
> NULL) != TRUE) {
> > +                     g_printerr("Elements could not be linked.\n");
> > +                     return TestFail;
> > +             }
> > +
> > +             g_autoptr(GstPad) src_pad =
> gst_element_get_static_pad(libcameraSrc_, "src");
> > +             g_autoptr(GstPad) request_pad =
> gst_element_get_request_pad(libcameraSrc_, "src_%u");
> > +             GstPad *queue0_sink_pad =
> gst_element_get_static_pad(queue0_, "sink");
> > +             GstPad *queue1_sink_pad =
> gst_element_get_static_pad(queue1_, "sink");
> > +
> > +             if (gst_pad_link(src_pad, queue0_sink_pad) !=
> GST_PAD_LINK_OK
> > +                     || gst_pad_link(request_pad, queue1_sink_pad) !=
> GST_PAD_LINK_OK) {
> > +                     if (queue0_sink_pad)
> > +                             gst_object_unref(queue0_sink_pad);
> > +                     if (queue1_sink_pad)
> > +                             gst_object_unref(queue1_sink_pad);
> > +                     g_printerr("Pads could not be linked.\n");
> > +                     return TestFail;
> > +             }
> > +             gst_object_unref(queue0_sink_pad);
> > +             gst_object_unref(queue1_sink_pad);
> > +
> > +             if (startPipeline() != TestPass)
> > +                     return TestFail;
> > +
> > +             if (processEvent() != TestPass)
> > +                     return TestFail;
> > +
> > +             return TestPass;
> > +     }
> > +
> > +private:
> > +     std::string cameraName;
> > +     GstElement *convert0_;
> > +     GstElement *convert1_;
> > +     GstElement *sink0_;
> > +     GstElement *sink1_;
> > +     GstElement *queue0_;
> > +     GstElement *queue1_;
> > +};
> > +
> > +TEST_REGISTER(GstreamerMultiStreamTest)
> > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
> > index aca53b920365..13652e87d05c 100644
> > --- a/test/gstreamer/meson.build
> > +++ b/test/gstreamer/meson.build
> > @@ -6,6 +6,7 @@ endif
> >
> >  gstreamer_tests = [
> >      ['single_stream_test',   'gstreamer_single_stream_test.cpp'],
> > +    ['multi_stream_test',   'gstreamer_multi_stream_test.cpp'],
> >  ]
> >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
> >
> >
>

Regards,
*Vedant Paranjape*
Vedant Paranjape Sept. 14, 2021, 10:42 a.m. UTC | #4
On Tue, Sep 14, 2021 at 4:09 PM Vedant Paranjape <
vedantparanjape160201@gmail.com> wrote:

> Hi Jean-Michel
> Thanks for your review.
>
>
> On Tue, Sep 14, 2021 at 3:53 PM Jean-Michel Hautbois <
> jeanmichel.hautbois@ideasonboard.com> wrote:
>
>> Hi Vedant,
>>
>> On 14/09/2021 10:46, Vedant Paranjape wrote:
>> > This patch adds a test to test if multi stream using libcamera's
>> > gstreamer element works.
>>
>> A test to test :-) ?
>> A proposal (I am not good at writing commit messages though so, please
>> use it with care :-)):
>>
>
> haha, even the single stream test had same commit message, and it went in,
> so didn't think about anything different.
>
> >
>> > Test will run only on devices that support multistream output, i.e.,
>> > devices that use IPU3 and RPI pipeline. This was tested on a Raspberry
>> > Pi 4B+
>> >
>> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
>> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
>> > Tested-by: Paul Elder <paul.elder@ideasonboard.com>
>> > ---
>> >  .../gstreamer/gstreamer_multi_stream_test.cpp | 138 ++++++++++++++++++
>> >  test/gstreamer/meson.build                    |   1 +
>> >  2 files changed, 139 insertions(+)
>> >  create mode 100644 test/gstreamer/gstreamer_multi_stream_test.cpp
>> >
>> > diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp
>> b/test/gstreamer/gstreamer_multi_stream_test.cpp
>> > new file mode 100644
>> > index 000000000000..aea9a1dcb211
>> > --- /dev/null
>> > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
>> > @@ -0,0 +1,138 @@
>> > +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> > +/*
>> > + * Copyright (C) 2021, Vedant Paranjape
>> > + *
>> > + * gstreamer_multi_stream_test.cpp - GStreamer multi stream capture
>> test
>> > + */
>> > +
>> > +#include <iostream>
>> > +#include <unistd.h>
>> > +
>> > +#include <libcamera/base/utils.h>
>> > +
>> > +#include <libcamera/libcamera.h>
>> > +
>> > +#include "libcamera/internal/source_paths.h"
>> > +
>> > +#include <gst/gst.h>
>> > +
>> > +#include "gstreamer_test.h"
>> > +#include "test.h"
>> > +
>> > +using namespace std;
>> > +
>> > +class GstreamerMultiStreamTest : public GstreamerTest, public Test
>> > +{
>> > +public:
>> > +     GstreamerMultiStreamTest()
>> > +             : GstreamerTest()
>> > +     {
>> > +     }
>> > +
>> > +protected:
>> > +     int init() override
>> > +     {
>> > +             if (status_ != TestPass)
>> > +                     return status_;
>> > +
>> > +             /* Check if platform support multistream output */
>> > +             libcamera::CameraManager cm;
>> > +             cm.start();
>> > +             bool cameraFound = false;
>> > +             for (auto &camera : cm.cameras()) {
>> > +                     if (camera->streams().size() > 1) {
>> > +                             cameraName = camera->id();
>> > +                             cameraFound = true;
>> > +                             cm.stop();
>> > +                             break;
>> > +                     }
>> > +             }
>> > +
>> > +             if (!cameraFound) {
>> > +                     cm.stop();
>> > +                     return TestSkip;
>> > +             }
>> > +
>> > +             g_autoptr(GstElement) convert0 =
>> gst_element_factory_make("videoconvert", "convert0");
>> > +             g_autoptr(GstElement) convert1 =
>> gst_element_factory_make("videoconvert", "convert1");
>>
>> Why do you need a convert element ? AFAIK you are outputting into a
>> fakesink it should not be an issue ?
>>
>
> 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.
>
> > +             g_autoptr(GstElement) sink0 =
>> gst_element_factory_make("fakesink", "sink0");
>> > +             g_autoptr(GstElement) sink1 =
>> gst_element_factory_make("fakesink", "sink1");
>> > +             g_autoptr(GstElement) queue0 =
>> gst_element_factory_make("queue", "queue0");
>> > +             g_autoptr(GstElement) queue1 =
>> gst_element_factory_make("queue", "queue1");
>> > +             g_object_ref_sink(convert0);
>> > +             g_object_ref_sink(convert1);
>> > +             g_object_ref_sink(sink0);
>> > +             g_object_ref_sink(sink1);
>> > +             g_object_ref_sink(queue0);
>> > +             g_object_ref_sink(queue1);
>>
>> I don't really like that, why can't you use
>> gst_parse_bin_from_description() for instance ?
>> That way you would get a bin you could link to libcamerasrc.
>>
>
> 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.
>

s/ I'm doing using request pads / I'm working with pads of the queue element

Note: it is a long time since I really use GStreamer so I may not be
>> up-to-date, Nicolas may a different comment, and he would be the one to
>> follow ;-).
>>
>> > +
>> > +             if (!convert0 || !convert1 || !sink0 || !sink1 || !queue0
>> || !queue1) {
>> > +                     g_printerr("Not all elements could be created.
>> %p.%p.%p.%p.%p.%p\n",
>> > +                                convert0, convert1, sink0, sink1,
>> queue0, queue1);
>> > +
>> > +                     return TestFail;
>> > +             }
>> > +
>> > +             if (createPipeline() != TestPass)
>> > +                     return TestFail;
>> > +
>> > +             convert0_ = reinterpret_cast<GstElement
>> *>(g_steal_pointer(&convert0));
>> > +             convert1_ = reinterpret_cast<GstElement
>> *>(g_steal_pointer(&convert1));
>> > +             sink0_ = reinterpret_cast<GstElement
>> *>(g_steal_pointer(&sink0));
>> > +             sink1_ = reinterpret_cast<GstElement
>> *>(g_steal_pointer(&sink1));
>> > +             queue0_ = reinterpret_cast<GstElement
>> *>(g_steal_pointer(&queue0));
>> > +             queue1_ = reinterpret_cast<GstElement
>> *>(g_steal_pointer(&queue1));
>> > +
>> > +             return TestPass;
>> > +     }
>> > +
>> > +     int run() override
>> > +     {
>> > +             g_object_set(libcameraSrc_, "camera-name",
>> cameraName.c_str(), NULL);
>> > +
>> > +             /* Build the pipeline */
>> > +             gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_,
>> queue0_, queue1_,
>> > +                              convert0_, convert1_, sink0_, sink1_,
>> NULL);
>> > +             if (gst_element_link_many(queue0_, convert0_, sink0_,
>> NULL) != TRUE ||
>> > +                 gst_element_link_many(queue1_, convert1_, sink1_,
>> NULL) != TRUE) {
>> > +                     g_printerr("Elements could not be linked.\n");
>> > +                     return TestFail;
>> > +             }
>> > +
>> > +             g_autoptr(GstPad) src_pad =
>> gst_element_get_static_pad(libcameraSrc_, "src");
>> > +             g_autoptr(GstPad) request_pad =
>> gst_element_get_request_pad(libcameraSrc_, "src_%u");
>> > +             GstPad *queue0_sink_pad =
>> gst_element_get_static_pad(queue0_, "sink");
>> > +             GstPad *queue1_sink_pad =
>> gst_element_get_static_pad(queue1_, "sink");
>> > +
>> > +             if (gst_pad_link(src_pad, queue0_sink_pad) !=
>> GST_PAD_LINK_OK
>> > +                     || gst_pad_link(request_pad, queue1_sink_pad) !=
>> GST_PAD_LINK_OK) {
>> > +                     if (queue0_sink_pad)
>> > +                             gst_object_unref(queue0_sink_pad);
>> > +                     if (queue1_sink_pad)
>> > +                             gst_object_unref(queue1_sink_pad);
>> > +                     g_printerr("Pads could not be linked.\n");
>> > +                     return TestFail;
>> > +             }
>> > +             gst_object_unref(queue0_sink_pad);
>> > +             gst_object_unref(queue1_sink_pad);
>> > +
>> > +             if (startPipeline() != TestPass)
>> > +                     return TestFail;
>> > +
>> > +             if (processEvent() != TestPass)
>> > +                     return TestFail;
>> > +
>> > +             return TestPass;
>> > +     }
>> > +
>> > +private:
>> > +     std::string cameraName;
>> > +     GstElement *convert0_;
>> > +     GstElement *convert1_;
>> > +     GstElement *sink0_;
>> > +     GstElement *sink1_;
>> > +     GstElement *queue0_;
>> > +     GstElement *queue1_;
>> > +};
>> > +
>> > +TEST_REGISTER(GstreamerMultiStreamTest)
>> > diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
>> > index aca53b920365..13652e87d05c 100644
>> > --- a/test/gstreamer/meson.build
>> > +++ b/test/gstreamer/meson.build
>> > @@ -6,6 +6,7 @@ endif
>> >
>> >  gstreamer_tests = [
>> >      ['single_stream_test',   'gstreamer_single_stream_test.cpp'],
>> > +    ['multi_stream_test',   'gstreamer_multi_stream_test.cpp'],
>> >  ]
>> >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
>> >
>> >
>>
>
> Regards,
> *Vedant Paranjape*
>

Patch
diff mbox series

diff --git a/test/gstreamer/gstreamer_multi_stream_test.cpp b/test/gstreamer/gstreamer_multi_stream_test.cpp
new file mode 100644
index 000000000000..aea9a1dcb211
--- /dev/null
+++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
@@ -0,0 +1,138 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2021, Vedant Paranjape
+ *
+ * gstreamer_multi_stream_test.cpp - GStreamer multi stream capture test
+ */
+
+#include <iostream>
+#include <unistd.h>
+
+#include <libcamera/base/utils.h>
+
+#include <libcamera/libcamera.h>
+
+#include "libcamera/internal/source_paths.h"
+
+#include <gst/gst.h>
+
+#include "gstreamer_test.h"
+#include "test.h"
+
+using namespace std;
+
+class GstreamerMultiStreamTest : public GstreamerTest, public Test
+{
+public:
+	GstreamerMultiStreamTest()
+		: GstreamerTest()
+	{
+	}
+
+protected:
+	int init() override
+	{
+		if (status_ != TestPass)
+			return status_;
+
+		/* Check if platform support multistream output */
+		libcamera::CameraManager cm;
+		cm.start();
+		bool cameraFound = false;
+		for (auto &camera : cm.cameras()) {
+			if (camera->streams().size() > 1) {
+				cameraName = camera->id();
+				cameraFound = true;
+				cm.stop();
+				break;
+			}
+		}
+
+		if (!cameraFound) {
+			cm.stop();
+			return TestSkip;
+		}
+
+		g_autoptr(GstElement) convert0 = gst_element_factory_make("videoconvert", "convert0");
+		g_autoptr(GstElement) convert1 = gst_element_factory_make("videoconvert", "convert1");
+		g_autoptr(GstElement) sink0 = gst_element_factory_make("fakesink", "sink0");
+		g_autoptr(GstElement) sink1 = gst_element_factory_make("fakesink", "sink1");
+		g_autoptr(GstElement) queue0 = gst_element_factory_make("queue", "queue0");
+		g_autoptr(GstElement) queue1 = gst_element_factory_make("queue", "queue1");
+		g_object_ref_sink(convert0);
+		g_object_ref_sink(convert1);
+		g_object_ref_sink(sink0);
+		g_object_ref_sink(sink1);
+		g_object_ref_sink(queue0);
+		g_object_ref_sink(queue1);
+
+		if (!convert0 || !convert1 || !sink0 || !sink1 || !queue0 || !queue1) {
+			g_printerr("Not all elements could be created. %p.%p.%p.%p.%p.%p\n",
+				   convert0, convert1, sink0, sink1, queue0, queue1);
+
+			return TestFail;
+		}
+
+		if (createPipeline() != TestPass)
+			return TestFail;
+
+		convert0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert0));
+		convert1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&convert1));
+		sink0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink0));
+		sink1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&sink1));
+		queue0_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue0));
+		queue1_ = reinterpret_cast<GstElement *>(g_steal_pointer(&queue1));
+
+		return TestPass;
+	}
+
+	int run() override
+	{
+		g_object_set(libcameraSrc_, "camera-name", cameraName.c_str(), NULL);
+
+		/* Build the pipeline */
+		gst_bin_add_many(GST_BIN(pipeline_), libcameraSrc_, queue0_, queue1_,
+				 convert0_, convert1_, sink0_, sink1_, NULL);
+		if (gst_element_link_many(queue0_, convert0_, sink0_, NULL) != TRUE ||
+		    gst_element_link_many(queue1_, convert1_, sink1_, NULL) != TRUE) {
+			g_printerr("Elements could not be linked.\n");
+			return TestFail;
+		}
+
+		g_autoptr(GstPad) src_pad = gst_element_get_static_pad(libcameraSrc_, "src");
+		g_autoptr(GstPad) request_pad = gst_element_get_request_pad(libcameraSrc_, "src_%u");
+		GstPad *queue0_sink_pad = gst_element_get_static_pad(queue0_, "sink");
+		GstPad *queue1_sink_pad = gst_element_get_static_pad(queue1_, "sink");
+
+		if (gst_pad_link(src_pad, queue0_sink_pad) != GST_PAD_LINK_OK
+			|| gst_pad_link(request_pad, queue1_sink_pad) != GST_PAD_LINK_OK) {
+			if (queue0_sink_pad)
+				gst_object_unref(queue0_sink_pad);
+			if (queue1_sink_pad)
+				gst_object_unref(queue1_sink_pad);
+			g_printerr("Pads could not be linked.\n");
+			return TestFail;
+		}
+		gst_object_unref(queue0_sink_pad);
+		gst_object_unref(queue1_sink_pad);
+
+		if (startPipeline() != TestPass)
+			return TestFail;
+
+		if (processEvent() != TestPass)
+			return TestFail;
+
+		return TestPass;
+	}
+
+private:
+	std::string cameraName;
+	GstElement *convert0_;
+	GstElement *convert1_;
+	GstElement *sink0_;
+	GstElement *sink1_;
+	GstElement *queue0_;
+	GstElement *queue1_;
+};
+
+TEST_REGISTER(GstreamerMultiStreamTest)
diff --git a/test/gstreamer/meson.build b/test/gstreamer/meson.build
index aca53b920365..13652e87d05c 100644
--- a/test/gstreamer/meson.build
+++ b/test/gstreamer/meson.build
@@ -6,6 +6,7 @@  endif
 
 gstreamer_tests = [
     ['single_stream_test',   'gstreamer_single_stream_test.cpp'],
+    ['multi_stream_test',   'gstreamer_multi_stream_test.cpp'],
 ]
 gstreamer_dep = dependency('gstreamer-1.0', required: true)