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