diff --git a/Makefile b/Makefile
index 878b7e6b..b2d5dca7 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@ else
endif
CXX_VERSION:=c++17
-DSL_VERSION:='L"v0.30.alpha"'
+DSL_VERSION:='L"v0.30.a.alpha"'
GLIB_VERSION:=2.0
GSTREAMER_VERSION:=1.0
diff --git a/README.md b/README.md
index 265a1bd1..2d5dfe10 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,25 @@ The DeepStream SDK and DSL use the open source [GStreamer](https://gstreamer.fre
---
## Important Bulletins
-The latest release `v0.30.alpha` was developed to support DeepSteam 6.4 and 7.0 on Ubuntu 22.04.
+The latest release [v0.30.a.alpha](/Release%20Notes/v0.30.a.alpha.md) is a patch for the v0.30.alpha release that fixes a [critical bug](https://github.com/prominenceai/deepstream-services-library/issues/1238) in the V4L2 Sink.
+
+The [v0.30.alpha](/Release%20Notes/v0.30.alpha.md) release was developed to support DeepSteam 6.4 and 7.0 on Ubuntu 22.04.
+
+> WARNING! There is a cricical error in the DeepStream 7.0 Installation Instructions.
+
+Under the section [Install librdkafka](https://docs.nvidia.com/metropolis/deepstream/dev-guide/text/DS_Installation.html#install-librdkafka-to-enable-kafka-protocol-adaptor-for-message-broker), the following instructions
+```bash
+$ sudo mkdir -p /opt/nvidia/deepstream/deepstream/lib
+$ sudo cp /usr/local/lib/librdkafka* /opt/nvidia/deepstream/deepstream/lib
+```
+Must be replaced with.
+```bash
+$ sudo mkdir -p /opt/nvidia/deepstream/deepstream-7.0/lib
+$ sudo cp /usr/local/lib/librdkafka* /opt/nvidia/deepstream/deepstream-7.0/lib
+```
+See [Error in DeepStream 7.0 installation instructions - symlink fails to create](https://forums.developer.nvidia.com/t/error-in-deepstream-7-0-installation-instructions-symlink-fails-to-create/296026) for more information.
+
+---
> WARNING! There is a cricical error in the DeepStream 7.0 Installation Instructions.
diff --git a/Release Notes/dsl-releases.md b/Release Notes/dsl-releases.md
index 01fc6754..a6d0976b 100644
--- a/Release Notes/dsl-releases.md
+++ b/Release Notes/dsl-releases.md
@@ -2,11 +2,12 @@
| Release | Date |
| --------------------------------------------------- | ----------- |
+| [v0.30.a.alpha (patch)](/Release%20Notes/v0.30.a.alpha.md) | 07/14/2024 |
| [v0.30.alpha](/Release%20Notes/v0.30.alpha.md) | 05/28/2024 |
| [v0.29.alpha](/Release%20Notes/v0.29.alpha.md) | 03/23/2024 |
| [v0.28.alpha](/Release%20Notes/v0.28.alpha.md) | 01/29/2024 |
-| [v0.27.b.alpha](/Release%20Notes/v0.27.b.alpha.md) | 11/08/2023 |
-| [v0.27.a.alpha](/Release%20Notes/v0.27.a.alpha.md) | 10/24/2023 |
+| [v0.27.b.alpha (patch)](/Release%20Notes/v0.27.b.alpha.md) | 11/08/2023 |
+| [v0.27.a.alpha (patch)](/Release%20Notes/v0.27.a.alpha.md) | 10/24/2023 |
| [v0.27.alpha](/Release%20Notes/v0.27.alpha.md) | 09/13/2023 |
| [v0.26.a.alpha](/Release%20Notes/v0.26.a.alpha.md) | 06/07/2023 |
| [v0.26.alpha](/Release%20Notes/v0.26.alpha.md) | 05/03/2023 |
diff --git a/Release Notes/v0.30.a.alpha.md b/Release Notes/v0.30.a.alpha.md
new file mode 100644
index 00000000..aa30a1f0
--- /dev/null
+++ b/Release Notes/v0.30.a.alpha.md
@@ -0,0 +1,14 @@
+# v0.30.a.alpha (patch) Release Notes
+**Important!**
+* `v0.30.a.alpha` is a **patch** release (patch `a` for the `v0.30.alpha` release).
+* The public/client API in DslApi.h has not been changed - i.e there are no new services.
+* There is one critical bug fix, 2 new examples, minor documentation updates, and more test coverage.
+
+## Issues closed in this release
+### Bugs closed in this release
+* Fix and complete the V4L2 Sink - revert previous invalid change, and add test coverage and examples [#1238](https://github.com/prominenceai/deepstream-services-library/issues/1238).
+
+## New Examples in this release
+* [1file_pgie_iou_tracker_osd_window_v4l2.py](/examples/python/1file_pgie_iou_tracker_osd_window_v4l2.py)
+* [1file_pgie_iou_tracker_osd_window_v4l2.cpp](/examples/python/1file_pgie_iou_tracker_osd_window_v4l2.cpp)
+
diff --git a/docs/api-sink.md b/docs/api-sink.md
index dff9ad9a..392a2ae2 100644
--- a/docs/api-sink.md
+++ b/docs/api-sink.md
@@ -81,6 +81,31 @@ As a general rule
* 1 _The NVIDIA Smart Recording Bin - used by the Record Sink - does not support/extern any of the common sink properties._ [↩](#a1)
* 2 _The rtspclientsink plugin is not derived from the GStreamer basesink which implements the common sink properties._ [↩](#a2)
+## Using the V4L2 Sink with V4L2 Loopback
+From the [GStream documentation](https://gstreamer.freedesktop.org/documentation/video4linux2/v4l2sink.html?gi-language=c#v4l2sink-page):
+> _"The V4L2 Sink can be used to display video to V4L2 capatible video devices (screen overlays provided by the graphics hardware, tv-out, etc)."_
+
+[V4L2 Loopback](https://github.com/umlaeute/v4l2loopback) can be used to create "virtual V4L2 video devices" allowing applications to read the virtual devices as V4L2 input sources. See: https://github.com/umlaeute/v4l2loopback for more information.
+
+Applicable DSL examples:
+* [1file_pgie_iou_tracker_osd_window_v4l2.py](/examples/python/1file_pgie_iou_tracker_osd_window_v4l2.py)
+* [1file_pgie_iou_tracker_osd_window_v4l2.cpp](/examples/python/1file_pgie_iou_tracker_osd_window_v4l2.cpp)
+
+You can install v4l2loopback with the command below. Depending on your device, there may be extra steps to install a kernel module. Follow the prompts as directed.
+```bash
+ $ sudo apt-get install v4l2loopback-dkms
+```
+
+Run the following to setup '/dev/video3' (used by the examples above)
+```bash
+ $ sudo modprobe v4l2loopback video_nr=3
+```
+
+You can use the following GStreamer launch command to test the loopback device when the example pipeline is running.
+```bash
+ $ gst-launch-1.0 v4l2src device=/dev/video3 ! videoconvert ! xvimagesink
+```
+
## Sink API
**Types:**
* [`dsl_recording_info`](#dsl_recording_info)
diff --git a/examples/cpp/1file_pgie_iou_tracker_osd_window_v4l2.cpp b/examples/cpp/1file_pgie_iou_tracker_osd_window_v4l2.cpp
new file mode 100644
index 00000000..481ece69
--- /dev/null
+++ b/examples/cpp/1file_pgie_iou_tracker_osd_window_v4l2.cpp
@@ -0,0 +1,220 @@
+/*
+The MIT License
+
+Copyright (c) 2024, Prominence AI, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in-
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* ##############################################################################
+#
+# This simple example demonstrates how to create a set of Pipeline components,
+# specifically:
+# - A File Source
+# - Primary GST Inference Engine (PGIE)
+# - IOU Tracker
+# - On-Screen Display
+# - Window Sink
+# - V4L2 Sink
+# ...and how to add them to a new Pipeline and play.
+#
+# The V4L2 Sink is used to display video to v4l2 video devices.
+#
+# V4L2 Loopback can be used to create "virtual video devices". Normal (v4l2)
+# applications will read these devices as if they were ordinary video devices.
+# See: https://github.com/umlaeute/v4l2loopback for more information.
+#
+# You can install v4l2loopback with
+# $ sudo apt-get install v4l2loopback-dkms
+#
+# Run the following to setup '/dev/video3'
+# $ sudo modprobe v4l2loopback video_nr=3
+#
+# When the script is running, you can use the following GStreamer launch
+# command to test the loopback
+# $ gst-launch-1.0 v4l2src device=/dev/video3 ! videoconvert ! xvimagesink
+#
+# The example registers handler callback functions for:
+# - key-release events
+# - delete-window events
+# - end-of-stream EOS events
+# - Pipeline change-of-state events
+#
+############################################################################## */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "DslApi.h"
+
+std::wstring file_path(
+ L"/opt/nvidia/deepstream/deepstream/samples/streams/sample_1080p_h265.mp4");
+
+// Config and model-engine files
+std::wstring primary_infer_config_file(
+ L"/opt/nvidia/deepstream/deepstream/sources/apps/sample_apps/deepstream-preprocess-test/config_infer.txt");
+std::wstring primary_model_engine_file(
+ L"/opt/nvidia/deepstream/deepstream/samples/models/Primary_Detector/resnet18_trafficcamnet.etlt_b8_gpu0_int8.engine");
+
+// Config file used by the IOU Tracker
+std::wstring iou_tracker_config_file(
+ L"/opt/nvidia/deepstream/deepstream/samples/configs/deepstream-app/config_tracker_IOU.yml");
+
+// V4L2 Sink device location to stream to
+std::wstring DEVICE_LOCATION = L"/dev/video3";
+
+uint WINDOW_WIDTH = 1280;
+uint WINDOW_HEIGHT = 720;
+
+//
+// Function to be called on XWindow KeyRelease event
+//
+void xwindow_key_event_handler(const wchar_t* in_key, void* client_data)
+{
+ std::wstring wkey(in_key);
+ std::string key(wkey.begin(), wkey.end());
+ std::cout << "key released = " << key << std::endl;
+ key = std::toupper(key[0]);
+ if(key == "P"){
+ dsl_pipeline_pause(L"pipeline");
+ } else if (key == "R"){
+ dsl_pipeline_play(L"pipeline");
+ } else if (key == "Q" or key == "" or key == ""){
+ std::cout << "Main Loop Quit" << std::endl;
+ dsl_pipeline_stop(L"pipeline");
+ dsl_main_loop_quit();
+ }
+}
+
+//
+// Function to be called on XWindow Delete event
+//
+void xwindow_delete_event_handler(void* client_data)
+{
+ std::cout<<"delete window event"<SetAttribute("drop-allocation", 1);
-
+
LOG_INFO("");
LOG_INFO("Initial property values for v4L2SinkBintr '" << name << "'");
LOG_INFO(" device-location : " << m_deviceLocation);
@@ -3240,16 +3240,14 @@ namespace DSL
if (!m_pQueue->LinkToSink(m_pTransform) or
!m_pTransform->LinkToSink(m_pCapsFilter) or
- !m_pCapsFilter->LinkToSink(m_pSink))
- // !m_pCapsFilter->LinkToSink(m_pIdentity) or
- // !m_pIdentity->LinkToSink(m_pSink))
+ !m_pCapsFilter->LinkToSink(m_pIdentity) or
+ !m_pIdentity->LinkToSink(m_pSink))
{
return false;
}
-
- m_isLinked = true;
- return true;
-}
+ m_isLinked = true;
+ return true;
+ }
void V4l2SinkBintr::UnlinkAll()
{
@@ -3378,12 +3376,6 @@ namespace DSL
{
LOG_FUNC();
- if (m_isLinked)
- {
- LOG_ERROR("Can't set picture-settings for V4l2SinkBintr '"
- << GetName() << "' as it is currently in a linked state");
- return false;
- }
m_brightness = brightness;
m_contrast = contrast;
m_saturation = saturation;
diff --git a/src/DslSinkBintr.h b/src/DslSinkBintr.h
index 1eb46872..8e72e3e7 100644
--- a/src/DslSinkBintr.h
+++ b/src/DslSinkBintr.h
@@ -1813,15 +1813,14 @@ namespace DSL
DSL_ELEMENT_PTR m_pIdentity;
/**
- * @brief Caps Filter required for for the V4l2SinkBintr.
- */
- DSL_ELEMENT_PTR m_pCapsFilter;
-
- /**
- * @brief Video converter required for the V4l2SinkBintr.
+ * @brief NV Video converter required for the V4l2SinkBintr.
*/
DSL_ELEMENT_PTR m_pTransform;
+ /**
+ * @brief Caps Filter required for the V4l2SinkBintr.
+ */
+ DSL_ELEMENT_PTR m_pCapsFilter;
};
}
diff --git a/src/DslSourceBintr.h b/src/DslSourceBintr.h
index 903376ee..c6943c84 100644
--- a/src/DslSourceBintr.h
+++ b/src/DslSourceBintr.h
@@ -1104,7 +1104,7 @@ namespace DSL
DSL_ELEMENT_PTR m_pSourceCapsFilter;
/**
- * @brief Video converter, first of two, for the V4L2 Source if dGPU
+ * @brief Video converter for the V4L2 Source if dGPU
*/
DSL_ELEMENT_PTR m_pdGpuVidConv;
diff --git a/test/api/DslPipelinePlayComponentsTest.cpp b/test/api/DslPipelinePlayComponentsTest.cpp
index 4a7b98d5..01a50abb 100644
--- a/test/api/DslPipelinePlayComponentsTest.cpp
+++ b/test/api/DslPipelinePlayComponentsTest.cpp
@@ -111,6 +111,8 @@ static const uint sink_width(1280);
static const uint sink_height(720);
static const std::wstring window_sink_name(L"egl-sink");
+static const std::wstring v4l2_sink_name(L"v4l2-sink");
+static const std::wstring device_location = L"/dev/video3";
static const std::wstring rtsp_sink_name(L"rtsp-sink");
static const std::wstring host(L"rjhowell-desktop.local");
@@ -120,7 +122,7 @@ static const uint codec(DSL_CODEC_H264);
static const uint bitrate(4000000);
static const uint interval(0);
-SCENARIO( "A new Pipeline with a URI File Source, FakeSink", "[pipeline-play]" )
+SCENARIO( "A new Pipeline with a URI File Source and FakeSink can play", "[pipeline-play]" )
{
GIVEN( "A Pipeline, URI source, Fake Sink" )
{
@@ -1979,3 +1981,78 @@ SCENARIO( "A new Pipeline-Stream-Muxer with Tiler 4 URI Sources, Primary GIE, Wi
}
}
+SCENARIO( "A new Pipeline with a URI File Source, Window Sink, and V4L2 Sink can play",
+ "[pipeline-play]" )
+{
+ GIVEN( "A Pipeline, URI source, Window Sink, V4L2 Sink" )
+ {
+ REQUIRE( dsl_component_list_size() == 0 );
+
+ REQUIRE( dsl_source_uri_new(source_name1.c_str(), uri.c_str(),
+ false, skip_frames, drop_frame_interval) == DSL_RESULT_SUCCESS );
+
+ REQUIRE( dsl_sink_window_egl_new(window_sink_name.c_str(),
+ offest_x, offest_y, sink_width, sink_height) == DSL_RESULT_SUCCESS );
+
+ REQUIRE( dsl_sink_v4l2_new(v4l2_sink_name.c_str(),
+ device_location.c_str()) == DSL_RESULT_SUCCESS );
+
+ const wchar_t* components[] = {L"uri-source-1", L"egl-sink", L"v4l2-sink", NULL};
+
+ WHEN( "When the Pipeline is Assembled" )
+ {
+ REQUIRE( dsl_pipeline_new(pipeline_name.c_str()) == DSL_RESULT_SUCCESS );
+
+ REQUIRE( dsl_pipeline_component_add_many(pipeline_name.c_str(),
+ components) == DSL_RESULT_SUCCESS );
+
+ THEN( "Pipeline is Able to LinkAll and Play" )
+ {
+ REQUIRE( dsl_pipeline_link_method_set(pipeline_name.c_str(),
+ DSL_PIPELINE_LINK_METHOD_BY_POSITION) == DSL_RESULT_SUCCESS );
+
+ REQUIRE( dsl_pipeline_play(pipeline_name.c_str())
+ == DSL_RESULT_SUCCESS );
+
+ uint currentState(DSL_STATE_NULL);
+ REQUIRE( dsl_pipeline_state_get(pipeline_name.c_str(),
+ ¤tState) == DSL_RESULT_SUCCESS );
+ REQUIRE( currentState == DSL_STATE_PLAYING );
+
+ std::this_thread::sleep_for(TIME_TO_SLEEP_FOR);
+ REQUIRE( dsl_pipeline_stop(pipeline_name.c_str())
+ == DSL_RESULT_SUCCESS );
+
+ dsl_delete_all();
+ REQUIRE( dsl_pipeline_list_size() == 0 );
+ REQUIRE( dsl_component_list_size() == 0 );
+ }
+ }
+ WHEN( "When the Pipeline is Assembled" )
+ {
+ REQUIRE( dsl_pipeline_new(pipeline_name.c_str()) == DSL_RESULT_SUCCESS );
+
+ REQUIRE( dsl_pipeline_component_add_many(pipeline_name.c_str(), components) == DSL_RESULT_SUCCESS );
+
+ THEN( "Pipeline is Able to LinkAll and Play" )
+ {
+ REQUIRE( dsl_pipeline_link_method_set(pipeline_name.c_str(),
+ DSL_PIPELINE_LINK_METHOD_BY_ADD_ORDER) == DSL_RESULT_SUCCESS );
+
+ REQUIRE( dsl_pipeline_play(pipeline_name.c_str()) == DSL_RESULT_SUCCESS );
+
+ uint currentState(DSL_STATE_NULL);
+ REQUIRE( dsl_pipeline_state_get(pipeline_name.c_str(), ¤tState) == DSL_RESULT_SUCCESS );
+ REQUIRE( currentState == DSL_STATE_PLAYING );
+
+ std::this_thread::sleep_for(TIME_TO_SLEEP_FOR);
+ REQUIRE( dsl_pipeline_stop(pipeline_name.c_str()) == DSL_RESULT_SUCCESS );
+
+ dsl_delete_all();
+ REQUIRE( dsl_pipeline_list_size() == 0 );
+ REQUIRE( dsl_component_list_size() == 0 );
+ }
+ }
+ }
+}
+