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

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

Commit Message

Vedant Paranjape Sept. 23, 2021, 2:56 p.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 raspberrypi pipeline. This was tested on
a Raspberry Pi 4B+.

Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
---
 .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
 test/gstreamer/meson.build                    |   1 +
 2 files changed, 129 insertions(+)
 create mode 100644 test/gstreamer/gstreamer_multi_stream_test.cpp

Comments

Nicolas Dufresne Sept. 23, 2021, 2:58 p.m. UTC | #1
Le jeudi 23 septembre 2021 à 20:26 +0530, Vedant Paranjape a écrit :
> 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 raspberrypi pipeline. This was tested on
> a Raspberry Pi 4B+.
> 
> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>

Ack.

> ---
>  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
>  test/gstreamer/meson.build                    |   1 +
>  2 files changed, 129 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..6f63f1e748e2
> --- /dev/null
> +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> @@ -0,0 +1,128 @@
> +/* 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;
> +		}
> +
> +		const gchar *streamDescription = "queue ! fakesink";
> +		g_autoptr(GError) error = NULL;
> +
> +		stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
> +						NULL,
> +						GST_PARSE_FLAG_FATAL_ERRORS,
> +						&error);
> +		if (!stream0_) {
> +			g_printerr("Stream0 could not be created (%s)\n", error->message);
> +			return TestFail;
> +		}
> +		g_object_ref_sink(stream0_);
> +
> +		stream1_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
> +						NULL,
> +						GST_PARSE_FLAG_FATAL_ERRORS,
> +						&error);
> +		if (!stream1_) {
> +			g_printerr("Stream1 could not be created (%s)\n", error->message);
> +			return TestFail;
> +		}
> +		g_object_ref_sink(stream1_);
> +
> +		if (createPipeline() != TestPass)
> +			return TestFail;
> +
> +		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_,
> +							stream0_, stream1_, NULL);
> +
> +		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");
> +
> +		{
> +			g_autoptr(GstPad) queue0_sink_pad = gst_element_get_static_pad(stream0_, "sink");
> +			g_autoptr(GstPad) queue1_sink_pad = gst_element_get_static_pad(stream1_, "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) {
> +				g_printerr("Pads could not be linked.\n");
> +				return TestFail;
> +			}
> +		}
> +
> +		if (startPipeline() != TestPass)
> +			return TestFail;
> +
> +		if (processEvent() != TestPass)
> +			return TestFail;
> +
> +		return TestPass;
> +	}
> +
> +	void cleanup() override
> +	{
> +		g_clear_object(&stream0_);
> +		g_clear_object(&stream1_);
> +	}
> +
> +private:
> +	std::string cameraName;
> +	GstElement *stream0_;
> +	GstElement *stream1_;
> +};
> +
> +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)
>
Paul Elder Sept. 24, 2021, 6:08 a.m. UTC | #2
Hi Vedant,

On Thu, Sep 23, 2021 at 08:26:37PM +0530, Vedant Paranjape wrote:
> 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.,

I think s/output/capture/ might be better.

Also s/i.e./e.g/

> devices that use IPU3 and raspberrypi pipeline. This was tested on
> a Raspberry Pi 4B+.
> 
> Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> ---
>  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
>  test/gstreamer/meson.build                    |   1 +
>  2 files changed, 129 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..6f63f1e748e2
> --- /dev/null
> +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> @@ -0,0 +1,128 @@
> +/* 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>

You don't use anything from this.

> +
> +#include <libcamera/libcamera.h>
> +
> +#include "libcamera/internal/source_paths.h"

Nor this.

(The single stream tests have these too)

> +
> +#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;
> +		}
> +
> +		const gchar *streamDescription = "queue ! fakesink";
> +		g_autoptr(GError) error = NULL;
> +
> +		stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
> +						NULL,
> +						GST_PARSE_FLAG_FATAL_ERRORS,
> +						&error);
> +		if (!stream0_) {
> +			g_printerr("Stream0 could not be created (%s)\n", error->message);
> +			return TestFail;
> +		}
> +		g_object_ref_sink(stream0_);
> +
> +		stream1_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
> +						NULL,
> +						GST_PARSE_FLAG_FATAL_ERRORS,
> +						&error);
> +		if (!stream1_) {
> +			g_printerr("Stream1 could not be created (%s)\n", error->message);
> +			return TestFail;
> +		}
> +		g_object_ref_sink(stream1_);
> +
> +		if (createPipeline() != TestPass)
> +			return TestFail;
> +
> +		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_,
> +							stream0_, stream1_, NULL);

Indentation.

> +
> +		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");
> +
> +		{
> +			g_autoptr(GstPad) queue0_sink_pad = gst_element_get_static_pad(stream0_, "sink");
> +			g_autoptr(GstPad) queue1_sink_pad = gst_element_get_static_pad(stream1_, "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) {

Indentation.

> +				g_printerr("Pads could not be linked.\n");
> +				return TestFail;
> +			}
> +		}
> +
> +		if (startPipeline() != TestPass)
> +			return TestFail;
> +
> +		if (processEvent() != TestPass)
> +			return TestFail;
> +
> +		return TestPass;
> +	}
> +
> +	void cleanup() override
> +	{
> +		g_clear_object(&stream0_);
> +		g_clear_object(&stream1_);
> +	}
> +
> +private:
> +	std::string cameraName;

s/cameraName/cameraName_/

> +	GstElement *stream0_;
> +	GstElement *stream1_;
> +};
> +
> +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'],

It would be nice if the second elements lined up.


Otherwise, looks good.


Paul

>  ]
>  gstreamer_dep = dependency('gstreamer-1.0', required: true)
>  
> -- 
> 2.25.1
>
Vedant Paranjape Sept. 24, 2021, 6:12 a.m. UTC | #3
Hi Paul,
Can you also tell me the expected indentation ?

checkstyle suggestions didn't adhere with the 80 char width thing




On Fri, 24 Sep, 2021, 11:39 , <paul.elder@ideasonboard.com> wrote:

> Hi Vedant,
>
> On Thu, Sep 23, 2021 at 08:26:37PM +0530, Vedant Paranjape wrote:
> > 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.,
>
> I think s/output/capture/ might be better.
>
> Also s/i.e./e.g/
>
> > devices that use IPU3 and raspberrypi pipeline. This was tested on
> > a Raspberry Pi 4B+.
> >
> > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> > Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > ---
> >  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
> >  test/gstreamer/meson.build                    |   1 +
> >  2 files changed, 129 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..6f63f1e748e2
> > --- /dev/null
> > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> > @@ -0,0 +1,128 @@
> > +/* 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>
>
> You don't use anything from this.
>
> > +
> > +#include <libcamera/libcamera.h>
> > +
> > +#include "libcamera/internal/source_paths.h"
>
> Nor this.
>
> (The single stream tests have these too)
>
> > +
> > +#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;
> > +             }
> > +
> > +             const gchar *streamDescription = "queue ! fakesink";
> > +             g_autoptr(GError) error = NULL;
> > +
> > +             stream0_ =
> gst_parse_bin_from_description_full(streamDescription, TRUE,
> > +                                             NULL,
> > +
>  GST_PARSE_FLAG_FATAL_ERRORS,
> > +                                             &error);
> > +             if (!stream0_) {
> > +                     g_printerr("Stream0 could not be created (%s)\n",
> error->message);
> > +                     return TestFail;
> > +             }
> > +             g_object_ref_sink(stream0_);
> > +
> > +             stream1_ =
> gst_parse_bin_from_description_full(streamDescription, TRUE,
> > +                                             NULL,
> > +
>  GST_PARSE_FLAG_FATAL_ERRORS,
> > +                                             &error);
> > +             if (!stream1_) {
> > +                     g_printerr("Stream1 could not be created (%s)\n",
> error->message);
> > +                     return TestFail;
> > +             }
> > +             g_object_ref_sink(stream1_);
> > +
> > +             if (createPipeline() != TestPass)
> > +                     return TestFail;
> > +
> > +             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_,
> > +                                                     stream0_,
> stream1_, NULL);
>
> Indentation.
>
> > +
> > +             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");
> > +
> > +             {
> > +                     g_autoptr(GstPad) queue0_sink_pad =
> gst_element_get_static_pad(stream0_, "sink");
> > +                     g_autoptr(GstPad) queue1_sink_pad =
> gst_element_get_static_pad(stream1_, "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) {
>
> Indentation.
>
> > +                             g_printerr("Pads could not be linked.\n");
> > +                             return TestFail;
> > +                     }
> > +             }
> > +
> > +             if (startPipeline() != TestPass)
> > +                     return TestFail;
> > +
> > +             if (processEvent() != TestPass)
> > +                     return TestFail;
> > +
> > +             return TestPass;
> > +     }
> > +
> > +     void cleanup() override
> > +     {
> > +             g_clear_object(&stream0_);
> > +             g_clear_object(&stream1_);
> > +     }
> > +
> > +private:
> > +     std::string cameraName;
>
> s/cameraName/cameraName_/
>
> > +     GstElement *stream0_;
> > +     GstElement *stream1_;
> > +};
> > +
> > +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'],
>
> It would be nice if the second elements lined up.
>
>
> Otherwise, looks good.
>

Can I add a R-b then?

Paul
>
> >  ]
> >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
> >
> > --
> > 2.25.1
> >
>
Paul Elder Sept. 24, 2021, 7:04 a.m. UTC | #4
On Fri, Sep 24, 2021 at 11:42:18AM +0530, Vedant Paranjape wrote:
> Hi Paul,
> Can you also tell me the expected indentation ?
> 
> checkstyle suggestions didn't adhere with the 80 char width thing

80 char can be broken (up to 120) as long as it increases readability.

> 
> 
> 
> 
> On Fri, 24 Sep, 2021, 11:39 , <paul.elder@ideasonboard.com> wrote:
> 
>     Hi Vedant,
> 
>     On Thu, Sep 23, 2021 at 08:26:37PM +0530, Vedant Paranjape wrote:
>     > 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.,
> 
>     I think s/output/capture/ might be better.
> 
>     Also s/i.e./e.g/
> 
>     > devices that use IPU3 and raspberrypi pipeline. This was tested on
>     > a Raspberry Pi 4B+.
>     >
>     > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
>     > Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
>     > ---
>     >  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
>     >  test/gstreamer/meson.build                    |   1 +
>     >  2 files changed, 129 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..6f63f1e748e2
>     > --- /dev/null
>     > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
>     > @@ -0,0 +1,128 @@
>     > +/* 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>
> 
>     You don't use anything from this.
> 
>     > +
>     > +#include <libcamera/libcamera.h>
>     > +
>     > +#include "libcamera/internal/source_paths.h"
> 
>     Nor this.
> 
>     (The single stream tests have these too)
> 
>     > +
>     > +#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;
>     > +             }
>     > +
>     > +             const gchar *streamDescription = "queue ! fakesink";
>     > +             g_autoptr(GError) error = NULL;
>     > +
>     > +             stream0_ = gst_parse_bin_from_description_full
>     (streamDescription, TRUE,
>     > +                                             NULL,
>     > +                                           
>      GST_PARSE_FLAG_FATAL_ERRORS,
>     > +                                             &error);

Oh I let this and the next one go because it goes over, but I think I
agree with checkstyle here:


 		stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
							       NULL,
							       GST_PARSE_FLAG_FATAL_ERRORS,
							       &error);


>     > +             if (!stream0_) {
>     > +                     g_printerr("Stream0 could not be created (%s)\n",
>     error->message);
>     > +                     return TestFail;
>     > +             }
>     > +             g_object_ref_sink(stream0_);
>     > +
>     > +             stream1_ = gst_parse_bin_from_description_full
>     (streamDescription, TRUE,
>     > +                                             NULL,
>     > +                                           
>      GST_PARSE_FLAG_FATAL_ERRORS,
>     > +                                             &error);
>     > +             if (!stream1_) {
>     > +                     g_printerr("Stream1 could not be created (%s)\n",
>     error->message);
>     > +                     return TestFail;
>     > +             }
>     > +             g_object_ref_sink(stream1_);
>     > +
>     > +             if (createPipeline() != TestPass)
>     > +                     return TestFail;
>     > +
>     > +             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_,
>     > +                                                     stream0_, stream1_,
>     NULL);
> 
>     Indentation.

checkstyle has this one

> 
>     > +
>     > +             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");
>     > +
>     > +             {
>     > +                     g_autoptr(GstPad) queue0_sink_pad =
>     gst_element_get_static_pad(stream0_, "sink");
>     > +                     g_autoptr(GstPad) queue1_sink_pad =
>     gst_element_get_static_pad(stream1_, "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) {
> 
>     Indentation.


			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) {

> 
>     > +                             g_printerr("Pads could not be linked.\n");
>     > +                             return TestFail;
>     > +                     }
>     > +             }
>     > +
>     > +             if (startPipeline() != TestPass)
>     > +                     return TestFail;
>     > +
>     > +             if (processEvent() != TestPass)
>     > +                     return TestFail;
>     > +
>     > +             return TestPass;
>     > +     }
>     > +
>     > +     void cleanup() override
>     > +     {
>     > +             g_clear_object(&stream0_);
>     > +             g_clear_object(&stream1_);
>     > +     }
>     > +
>     > +private:
>     > +     std::string cameraName;
> 
>     s/cameraName/cameraName_/
> 
>     > +     GstElement *stream0_;
>     > +     GstElement *stream1_;
>     > +};
>     > +
>     > +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'],
> 
>     It would be nice if the second elements lined up.
> 
> 
>     Otherwise, looks good.
> 
> 
> Can I add a R-b then?

Not unless I give you one. I'll check in a new ersion.


Paul

> 
>     >  ]
>     >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
>     > 
>     > --
>     > 2.25.1
>     >
>
Vedant Paranjape Sept. 24, 2021, 8:32 a.m. UTC | #5
On Fri, Sep 24, 2021 at 12:35 PM <paul.elder@ideasonboard.com> wrote:
>
> On Fri, Sep 24, 2021 at 11:42:18AM +0530, Vedant Paranjape wrote:
> > Hi Paul,
> > Can you also tell me the expected indentation ?
> >
> > checkstyle suggestions didn't adhere with the 80 char width thing
>
> 80 char can be broken (up to 120) as long as it increases readability.
>
> >
> >
> >
> >
> > On Fri, 24 Sep, 2021, 11:39 , <paul.elder@ideasonboard.com> wrote:
> >
> >     Hi Vedant,
> >
> >     On Thu, Sep 23, 2021 at 08:26:37PM +0530, Vedant Paranjape wrote:
> >     > 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.,
> >
> >     I think s/output/capture/ might be better.
> >
> >     Also s/i.e./e.g/
> >
> >     > devices that use IPU3 and raspberrypi pipeline. This was tested on
> >     > a Raspberry Pi 4B+.
> >     >
> >     > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> >     > Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> >     > ---
> >     >  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
> >     >  test/gstreamer/meson.build                    |   1 +
> >     >  2 files changed, 129 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..6f63f1e748e2
> >     > --- /dev/null
> >     > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> >     > @@ -0,0 +1,128 @@
> >     > +/* 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>
> >
> >     You don't use anything from this.
> >
> >     > +
> >     > +#include <libcamera/libcamera.h>
> >     > +
> >     > +#include "libcamera/internal/source_paths.h"
> >
> >     Nor this.
> >
> >     (The single stream tests have these too)
> >
> >     > +
> >     > +#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;
> >     > +             }
> >     > +
> >     > +             const gchar *streamDescription = "queue ! fakesink";
> >     > +             g_autoptr(GError) error = NULL;
> >     > +
> >     > +             stream0_ = gst_parse_bin_from_description_full
> >     (streamDescription, TRUE,
> >     > +                                             NULL,
> >     > +
> >      GST_PARSE_FLAG_FATAL_ERRORS,
> >     > +                                             &error);
>
> Oh I let this and the next one go because it goes over, but I think I
> agree with checkstyle here:
>
>
>                 stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
>                                                                NULL,
>                                                                GST_PARSE_FLAG_FATAL_ERRORS,
>                                                                &error);
>
>
> >     > +             if (!stream0_) {
> >     > +                     g_printerr("Stream0 could not be created (%s)\n",
> >     error->message);
> >     > +                     return TestFail;
> >     > +             }
> >     > +             g_object_ref_sink(stream0_);
> >     > +
> >     > +             stream1_ = gst_parse_bin_from_description_full
> >     (streamDescription, TRUE,
> >     > +                                             NULL,
> >     > +
> >      GST_PARSE_FLAG_FATAL_ERRORS,
> >     > +                                             &error);
> >     > +             if (!stream1_) {
> >     > +                     g_printerr("Stream1 could not be created (%s)\n",
> >     error->message);
> >     > +                     return TestFail;
> >     > +             }
> >     > +             g_object_ref_sink(stream1_);
> >     > +
> >     > +             if (createPipeline() != TestPass)
> >     > +                     return TestFail;
> >     > +
> >     > +             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_,
> >     > +                                                     stream0_, stream1_,
> >     NULL);
> >
> >     Indentation.
>
> checkstyle has this one
>
> >
> >     > +
> >     > +             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");

This looks bad.

> >     > +
> >     > +             {
> >     > +                     g_autoptr(GstPad) queue0_sink_pad =
> >     gst_element_get_static_pad(stream0_, "sink");
> >     > +                     g_autoptr(GstPad) queue1_sink_pad =
> >     gst_element_get_static_pad(stream1_, "sink");

even this.

> >     > +
> >     > +                     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) {
> >
> >     Indentation.
>
>
>                         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) {
>
> >
> >     > +                             g_printerr("Pads could not be linked.\n");
> >     > +                             return TestFail;
> >     > +                     }
> >     > +             }
> >     > +
> >     > +             if (startPipeline() != TestPass)
> >     > +                     return TestFail;
> >     > +
> >     > +             if (processEvent() != TestPass)
> >     > +                     return TestFail;
> >     > +
> >     > +             return TestPass;
> >     > +     }
> >     > +
> >     > +     void cleanup() override
> >     > +     {
> >     > +             g_clear_object(&stream0_);
> >     > +             g_clear_object(&stream1_);
> >     > +     }
> >     > +
> >     > +private:
> >     > +     std::string cameraName;
> >
> >     s/cameraName/cameraName_/
> >
> >     > +     GstElement *stream0_;
> >     > +     GstElement *stream1_;
> >     > +};
> >     > +
> >     > +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'],
> >
> >     It would be nice if the second elements lined up.
> >
> >
> >     Otherwise, looks good.
> >
> >
> > Can I add a R-b then?
>
> Not unless I give you one. I'll check in a new ersion.
>
>
> Paul
>
> >
> >     >  ]
> >     >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
> >     >
> >     > --
> >     > 2.25.1
> >     >
> >
Paul Elder Sept. 24, 2021, 8:41 a.m. UTC | #6
On Fri, Sep 24, 2021 at 02:02:07PM +0530, Vedant Paranjape wrote:
> On Fri, Sep 24, 2021 at 12:35 PM <paul.elder@ideasonboard.com> wrote:
> >
> > On Fri, Sep 24, 2021 at 11:42:18AM +0530, Vedant Paranjape wrote:
> > > Hi Paul,
> > > Can you also tell me the expected indentation ?
> > >
> > > checkstyle suggestions didn't adhere with the 80 char width thing
> >
> > 80 char can be broken (up to 120) as long as it increases readability.
> >
> > >
> > >
> > >
> > >
> > > On Fri, 24 Sep, 2021, 11:39 , <paul.elder@ideasonboard.com> wrote:
> > >
> > >     Hi Vedant,
> > >
> > >     On Thu, Sep 23, 2021 at 08:26:37PM +0530, Vedant Paranjape wrote:
> > >     > 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.,
> > >
> > >     I think s/output/capture/ might be better.
> > >
> > >     Also s/i.e./e.g/
> > >
> > >     > devices that use IPU3 and raspberrypi pipeline. This was tested on
> > >     > a Raspberry Pi 4B+.
> > >     >
> > >     > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> > >     > Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > >     > ---
> > >     >  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
> > >     >  test/gstreamer/meson.build                    |   1 +
> > >     >  2 files changed, 129 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..6f63f1e748e2
> > >     > --- /dev/null
> > >     > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> > >     > @@ -0,0 +1,128 @@
> > >     > +/* 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>
> > >
> > >     You don't use anything from this.
> > >
> > >     > +
> > >     > +#include <libcamera/libcamera.h>
> > >     > +
> > >     > +#include "libcamera/internal/source_paths.h"
> > >
> > >     Nor this.
> > >
> > >     (The single stream tests have these too)
> > >
> > >     > +
> > >     > +#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;
> > >     > +             }
> > >     > +
> > >     > +             const gchar *streamDescription = "queue ! fakesink";
> > >     > +             g_autoptr(GError) error = NULL;
> > >     > +
> > >     > +             stream0_ = gst_parse_bin_from_description_full
> > >     (streamDescription, TRUE,
> > >     > +                                             NULL,
> > >     > +
> > >      GST_PARSE_FLAG_FATAL_ERRORS,
> > >     > +                                             &error);
> >
> > Oh I let this and the next one go because it goes over, but I think I
> > agree with checkstyle here:
> >
> >
> >                 stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
> >                                                                NULL,
> >                                                                GST_PARSE_FLAG_FATAL_ERRORS,
> >                                                                &error);
> >
> >
> > >     > +             if (!stream0_) {
> > >     > +                     g_printerr("Stream0 could not be created (%s)\n",
> > >     error->message);
> > >     > +                     return TestFail;
> > >     > +             }
> > >     > +             g_object_ref_sink(stream0_);
> > >     > +
> > >     > +             stream1_ = gst_parse_bin_from_description_full
> > >     (streamDescription, TRUE,
> > >     > +                                             NULL,
> > >     > +
> > >      GST_PARSE_FLAG_FATAL_ERRORS,
> > >     > +                                             &error);
> > >     > +             if (!stream1_) {
> > >     > +                     g_printerr("Stream1 could not be created (%s)\n",
> > >     error->message);
> > >     > +                     return TestFail;
> > >     > +             }
> > >     > +             g_object_ref_sink(stream1_);
> > >     > +
> > >     > +             if (createPipeline() != TestPass)
> > >     > +                     return TestFail;
> > >     > +
> > >     > +             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_,
> > >     > +                                                     stream0_, stream1_,
> > >     NULL);
> > >
> > >     Indentation.
> >
> > checkstyle has this one
> >
> > >
> > >     > +
> > >     > +             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");
> 
> This looks bad.

Get rid of the html indentation...

+		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");

I think it's fine. If you're concerned about the line length you can
indent it.

+		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");

Either way is fine.

> 
> > >     > +
> > >     > +             {
> > >     > +                     g_autoptr(GstPad) queue0_sink_pad =
> > >     gst_element_get_static_pad(stream0_, "sink");
> > >     > +                     g_autoptr(GstPad) queue1_sink_pad =
> > >     gst_element_get_static_pad(stream1_, "sink");
> 
> even this.

Same here.


Paul

> 
> > >     > +
> > >     > +                     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) {
> > >
> > >     Indentation.
> >
> >
> >                         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) {
> >
> > >
> > >     > +                             g_printerr("Pads could not be linked.\n");
> > >     > +                             return TestFail;
> > >     > +                     }
> > >     > +             }
> > >     > +
> > >     > +             if (startPipeline() != TestPass)
> > >     > +                     return TestFail;
> > >     > +
> > >     > +             if (processEvent() != TestPass)
> > >     > +                     return TestFail;
> > >     > +
> > >     > +             return TestPass;
> > >     > +     }
> > >     > +
> > >     > +     void cleanup() override
> > >     > +     {
> > >     > +             g_clear_object(&stream0_);
> > >     > +             g_clear_object(&stream1_);
> > >     > +     }
> > >     > +
> > >     > +private:
> > >     > +     std::string cameraName;
> > >
> > >     s/cameraName/cameraName_/
> > >
> > >     > +     GstElement *stream0_;
> > >     > +     GstElement *stream1_;
> > >     > +};
> > >     > +
> > >     > +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'],
> > >
> > >     It would be nice if the second elements lined up.
> > >
> > >
> > >     Otherwise, looks good.
> > >
> > >
> > > Can I add a R-b then?
> >
> > Not unless I give you one. I'll check in a new ersion.
> >
> >
> > Paul
> >
> > >
> > >     >  ]
> > >     >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
> > >     >
> > >     > --
> > >     > 2.25.1
> > >     >
> > >
Vedant Paranjape Sept. 24, 2021, 8:44 a.m. UTC | #7
On Fri, Sep 24, 2021 at 2:11 PM <paul.elder@ideasonboard.com> wrote:
>
> On Fri, Sep 24, 2021 at 02:02:07PM +0530, Vedant Paranjape wrote:
> > On Fri, Sep 24, 2021 at 12:35 PM <paul.elder@ideasonboard.com> wrote:
> > >
> > > On Fri, Sep 24, 2021 at 11:42:18AM +0530, Vedant Paranjape wrote:
> > > > Hi Paul,
> > > > Can you also tell me the expected indentation ?
> > > >
> > > > checkstyle suggestions didn't adhere with the 80 char width thing
> > >
> > > 80 char can be broken (up to 120) as long as it increases readability.
> > >
> > > >
> > > >
> > > >
> > > >
> > > > On Fri, 24 Sep, 2021, 11:39 , <paul.elder@ideasonboard.com> wrote:
> > > >
> > > >     Hi Vedant,
> > > >
> > > >     On Thu, Sep 23, 2021 at 08:26:37PM +0530, Vedant Paranjape wrote:
> > > >     > 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.,
> > > >
> > > >     I think s/output/capture/ might be better.
> > > >
> > > >     Also s/i.e./e.g/
> > > >
> > > >     > devices that use IPU3 and raspberrypi pipeline. This was tested on
> > > >     > a Raspberry Pi 4B+.
> > > >     >
> > > >     > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> > > >     > Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > > >     > ---
> > > >     >  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
> > > >     >  test/gstreamer/meson.build                    |   1 +
> > > >     >  2 files changed, 129 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..6f63f1e748e2
> > > >     > --- /dev/null
> > > >     > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> > > >     > @@ -0,0 +1,128 @@
> > > >     > +/* 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>
> > > >
> > > >     You don't use anything from this.
> > > >
> > > >     > +
> > > >     > +#include <libcamera/libcamera.h>
> > > >     > +
> > > >     > +#include "libcamera/internal/source_paths.h"
> > > >
> > > >     Nor this.
> > > >
> > > >     (The single stream tests have these too)
> > > >
> > > >     > +
> > > >     > +#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;
> > > >     > +             }
> > > >     > +
> > > >     > +             const gchar *streamDescription = "queue ! fakesink";
> > > >     > +             g_autoptr(GError) error = NULL;
> > > >     > +
> > > >     > +             stream0_ = gst_parse_bin_from_description_full
> > > >     (streamDescription, TRUE,
> > > >     > +                                             NULL,
> > > >     > +
> > > >      GST_PARSE_FLAG_FATAL_ERRORS,
> > > >     > +                                             &error);
> > >
> > > Oh I let this and the next one go because it goes over, but I think I
> > > agree with checkstyle here:
> > >
> > >
> > >                 stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
> > >                                                                NULL,
> > >                                                                GST_PARSE_FLAG_FATAL_ERRORS,
> > >                                                                &error);
> > >
> > >
> > > >     > +             if (!stream0_) {
> > > >     > +                     g_printerr("Stream0 could not be created (%s)\n",
> > > >     error->message);
> > > >     > +                     return TestFail;
> > > >     > +             }
> > > >     > +             g_object_ref_sink(stream0_);
> > > >     > +
> > > >     > +             stream1_ = gst_parse_bin_from_description_full
> > > >     (streamDescription, TRUE,
> > > >     > +                                             NULL,
> > > >     > +
> > > >      GST_PARSE_FLAG_FATAL_ERRORS,
> > > >     > +                                             &error);
> > > >     > +             if (!stream1_) {
> > > >     > +                     g_printerr("Stream1 could not be created (%s)\n",
> > > >     error->message);
> > > >     > +                     return TestFail;
> > > >     > +             }
> > > >     > +             g_object_ref_sink(stream1_);
> > > >     > +
> > > >     > +             if (createPipeline() != TestPass)
> > > >     > +                     return TestFail;
> > > >     > +
> > > >     > +             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_,
> > > >     > +                                                     stream0_, stream1_,
> > > >     NULL);
> > > >
> > > >     Indentation.
> > >
> > > checkstyle has this one
> > >
> > > >
> > > >     > +
> > > >     > +             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");
> >
> > This looks bad.
>
> Get rid of the html indentation...
What ? I put in the indentation myself.
>
> +               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");
>
> I think it's fine. If you're concerned about the line length you can
> indent it.
>
> +               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");
>
> Either way is fine.
>
> >
> > > >     > +
> > > >     > +             {
> > > >     > +                     g_autoptr(GstPad) queue0_sink_pad =
> > > >     gst_element_get_static_pad(stream0_, "sink");
> > > >     > +                     g_autoptr(GstPad) queue1_sink_pad =
> > > >     gst_element_get_static_pad(stream1_, "sink");
> >
> > even this.
>
> Same here.
>
>
> Paul
>
> >
> > > >     > +
> > > >     > +                     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) {
> > > >
> > > >     Indentation.
> > >
> > >
> > >                         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) {
> > >
> > > >
> > > >     > +                             g_printerr("Pads could not be linked.\n");
> > > >     > +                             return TestFail;
> > > >     > +                     }
> > > >     > +             }
> > > >     > +
> > > >     > +             if (startPipeline() != TestPass)
> > > >     > +                     return TestFail;
> > > >     > +
> > > >     > +             if (processEvent() != TestPass)
> > > >     > +                     return TestFail;
> > > >     > +
> > > >     > +             return TestPass;
> > > >     > +     }
> > > >     > +
> > > >     > +     void cleanup() override
> > > >     > +     {
> > > >     > +             g_clear_object(&stream0_);
> > > >     > +             g_clear_object(&stream1_);
> > > >     > +     }
> > > >     > +
> > > >     > +private:
> > > >     > +     std::string cameraName;
> > > >
> > > >     s/cameraName/cameraName_/
> > > >
> > > >     > +     GstElement *stream0_;
> > > >     > +     GstElement *stream1_;
> > > >     > +};
> > > >     > +
> > > >     > +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'],
> > > >
> > > >     It would be nice if the second elements lined up.
> > > >
> > > >
> > > >     Otherwise, looks good.
> > > >
> > > >
> > > > Can I add a R-b then?
> > >
> > > Not unless I give you one. I'll check in a new ersion.
> > >
> > >
> > > Paul
> > >
> > > >
> > > >     >  ]
> > > >     >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
> > > >     >
> > > >     > --
> > > >     > 2.25.1
> > > >     >
> > > >
Paul Elder Sept. 24, 2021, 9:34 a.m. UTC | #8
On Fri, Sep 24, 2021 at 02:14:11PM +0530, Vedant Paranjape wrote:
> On Fri, Sep 24, 2021 at 2:11 PM <paul.elder@ideasonboard.com> wrote:
> >
> > On Fri, Sep 24, 2021 at 02:02:07PM +0530, Vedant Paranjape wrote:
> > > On Fri, Sep 24, 2021 at 12:35 PM <paul.elder@ideasonboard.com> wrote:
> > > >
> > > > On Fri, Sep 24, 2021 at 11:42:18AM +0530, Vedant Paranjape wrote:
> > > > > Hi Paul,
> > > > > Can you also tell me the expected indentation ?
> > > > >
> > > > > checkstyle suggestions didn't adhere with the 80 char width thing
> > > >
> > > > 80 char can be broken (up to 120) as long as it increases readability.
> > > >
> > > > >
> > > > >
> > > > >
> > > > >
> > > > > On Fri, 24 Sep, 2021, 11:39 , <paul.elder@ideasonboard.com> wrote:
> > > > >
> > > > >     Hi Vedant,
> > > > >
> > > > >     On Thu, Sep 23, 2021 at 08:26:37PM +0530, Vedant Paranjape wrote:
> > > > >     > 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.,
> > > > >
> > > > >     I think s/output/capture/ might be better.
> > > > >
> > > > >     Also s/i.e./e.g/
> > > > >
> > > > >     > devices that use IPU3 and raspberrypi pipeline. This was tested on
> > > > >     > a Raspberry Pi 4B+.
> > > > >     >
> > > > >     > Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com>
> > > > >     > Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
> > > > >     > ---
> > > > >     >  .../gstreamer/gstreamer_multi_stream_test.cpp | 128 ++++++++++++++++++
> > > > >     >  test/gstreamer/meson.build                    |   1 +
> > > > >     >  2 files changed, 129 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..6f63f1e748e2
> > > > >     > --- /dev/null
> > > > >     > +++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
> > > > >     > @@ -0,0 +1,128 @@
> > > > >     > +/* 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>
> > > > >
> > > > >     You don't use anything from this.
> > > > >
> > > > >     > +
> > > > >     > +#include <libcamera/libcamera.h>
> > > > >     > +
> > > > >     > +#include "libcamera/internal/source_paths.h"
> > > > >
> > > > >     Nor this.
> > > > >
> > > > >     (The single stream tests have these too)
> > > > >
> > > > >     > +
> > > > >     > +#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;
> > > > >     > +             }
> > > > >     > +
> > > > >     > +             const gchar *streamDescription = "queue ! fakesink";
> > > > >     > +             g_autoptr(GError) error = NULL;
> > > > >     > +
> > > > >     > +             stream0_ = gst_parse_bin_from_description_full
> > > > >     (streamDescription, TRUE,
> > > > >     > +                                             NULL,
> > > > >     > +
> > > > >      GST_PARSE_FLAG_FATAL_ERRORS,
> > > > >     > +                                             &error);
> > > >
> > > > Oh I let this and the next one go because it goes over, but I think I
> > > > agree with checkstyle here:
> > > >
> > > >
> > > >                 stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
> > > >                                                                NULL,
> > > >                                                                GST_PARSE_FLAG_FATAL_ERRORS,
> > > >                                                                &error);
> > > >
> > > >
> > > > >     > +             if (!stream0_) {
> > > > >     > +                     g_printerr("Stream0 could not be created (%s)\n",
> > > > >     error->message);
> > > > >     > +                     return TestFail;
> > > > >     > +             }
> > > > >     > +             g_object_ref_sink(stream0_);
> > > > >     > +
> > > > >     > +             stream1_ = gst_parse_bin_from_description_full
> > > > >     (streamDescription, TRUE,
> > > > >     > +                                             NULL,
> > > > >     > +
> > > > >      GST_PARSE_FLAG_FATAL_ERRORS,
> > > > >     > +                                             &error);
> > > > >     > +             if (!stream1_) {
> > > > >     > +                     g_printerr("Stream1 could not be created (%s)\n",
> > > > >     error->message);
> > > > >     > +                     return TestFail;
> > > > >     > +             }
> > > > >     > +             g_object_ref_sink(stream1_);
> > > > >     > +
> > > > >     > +             if (createPipeline() != TestPass)
> > > > >     > +                     return TestFail;
> > > > >     > +
> > > > >     > +             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_,
> > > > >     > +                                                     stream0_, stream1_,
> > > > >     NULL);
> > > > >
> > > > >     Indentation.
> > > >
> > > > checkstyle has this one
> > > >
> > > > >
> > > > >     > +
> > > > >     > +             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");
> > >
> > > This looks bad.
> >
> > Get rid of the html indentation...
> What ? I put in the indentation myself.

I was talking to myself and re-showing the code block without the html
indentation. With the html indentation it's very nice to read:

+             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");

See, it's even missing the + to show that it's adding new lines!

This is why html email is the best :)

(I haven't even showed the extra tab that the html email added before >)

> >
> > +               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");
> >
> > I think it's fine. If you're concerned about the line length you can
> > indent it.
> >
> > +               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");
> >
> > Either way is fine.

Anyway, either way is fine, and I already merged it. Not that big of a
deal.


Paul

> >
> > >
> > > > >     > +
> > > > >     > +             {
> > > > >     > +                     g_autoptr(GstPad) queue0_sink_pad =
> > > > >     gst_element_get_static_pad(stream0_, "sink");
> > > > >     > +                     g_autoptr(GstPad) queue1_sink_pad =
> > > > >     gst_element_get_static_pad(stream1_, "sink");
> > >
> > > even this.
> >
> > Same here.
> >
> >
> > Paul
> >
> > >
> > > > >     > +
> > > > >     > +                     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) {
> > > > >
> > > > >     Indentation.
> > > >
> > > >
> > > >                         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) {
> > > >
> > > > >
> > > > >     > +                             g_printerr("Pads could not be linked.\n");
> > > > >     > +                             return TestFail;
> > > > >     > +                     }
> > > > >     > +             }
> > > > >     > +
> > > > >     > +             if (startPipeline() != TestPass)
> > > > >     > +                     return TestFail;
> > > > >     > +
> > > > >     > +             if (processEvent() != TestPass)
> > > > >     > +                     return TestFail;
> > > > >     > +
> > > > >     > +             return TestPass;
> > > > >     > +     }
> > > > >     > +
> > > > >     > +     void cleanup() override
> > > > >     > +     {
> > > > >     > +             g_clear_object(&stream0_);
> > > > >     > +             g_clear_object(&stream1_);
> > > > >     > +     }
> > > > >     > +
> > > > >     > +private:
> > > > >     > +     std::string cameraName;
> > > > >
> > > > >     s/cameraName/cameraName_/
> > > > >
> > > > >     > +     GstElement *stream0_;
> > > > >     > +     GstElement *stream1_;
> > > > >     > +};
> > > > >     > +
> > > > >     > +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'],
> > > > >
> > > > >     It would be nice if the second elements lined up.
> > > > >
> > > > >
> > > > >     Otherwise, looks good.
> > > > >
> > > > >
> > > > > Can I add a R-b then?
> > > >
> > > > Not unless I give you one. I'll check in a new ersion.
> > > >
> > > >
> > > > Paul
> > > >
> > > > >
> > > > >     >  ]
> > > > >     >  gstreamer_dep = dependency('gstreamer-1.0', required: true)
> > > > >     >
> > > > >     > --
> > > > >     > 2.25.1
> > > > >     >
> > > > >

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..6f63f1e748e2
--- /dev/null
+++ b/test/gstreamer/gstreamer_multi_stream_test.cpp
@@ -0,0 +1,128 @@ 
+/* 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;
+		}
+
+		const gchar *streamDescription = "queue ! fakesink";
+		g_autoptr(GError) error = NULL;
+
+		stream0_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
+						NULL,
+						GST_PARSE_FLAG_FATAL_ERRORS,
+						&error);
+		if (!stream0_) {
+			g_printerr("Stream0 could not be created (%s)\n", error->message);
+			return TestFail;
+		}
+		g_object_ref_sink(stream0_);
+
+		stream1_ = gst_parse_bin_from_description_full(streamDescription, TRUE,
+						NULL,
+						GST_PARSE_FLAG_FATAL_ERRORS,
+						&error);
+		if (!stream1_) {
+			g_printerr("Stream1 could not be created (%s)\n", error->message);
+			return TestFail;
+		}
+		g_object_ref_sink(stream1_);
+
+		if (createPipeline() != TestPass)
+			return TestFail;
+
+		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_,
+							stream0_, stream1_, NULL);
+
+		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");
+
+		{
+			g_autoptr(GstPad) queue0_sink_pad = gst_element_get_static_pad(stream0_, "sink");
+			g_autoptr(GstPad) queue1_sink_pad = gst_element_get_static_pad(stream1_, "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) {
+				g_printerr("Pads could not be linked.\n");
+				return TestFail;
+			}
+		}
+
+		if (startPipeline() != TestPass)
+			return TestFail;
+
+		if (processEvent() != TestPass)
+			return TestFail;
+
+		return TestPass;
+	}
+
+	void cleanup() override
+	{
+		g_clear_object(&stream0_);
+		g_clear_object(&stream1_);
+	}
+
+private:
+	std::string cameraName;
+	GstElement *stream0_;
+	GstElement *stream1_;
+};
+
+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)