Message ID | 20210914084622.118939-1-vedantparanjape160201@gmail.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
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) > >
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) >> >>
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*
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* >
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)