From 74337e253c6aa08a04045a0d7a45fcf00eb8311f Mon Sep 17 00:00:00 2001 From: Remi Bettan Date: Mon, 9 Dec 2024 12:36:18 +0200 Subject: [PATCH 1/3] adding gil to pipeline start --- wrappers/python/pyrs_pipeline.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/wrappers/python/pyrs_pipeline.cpp b/wrappers/python/pyrs_pipeline.cpp index c00e7fb7de..a3a3f09254 100644 --- a/wrappers/python/pyrs_pipeline.cpp +++ b/wrappers/python/pyrs_pipeline.cpp @@ -101,7 +101,8 @@ void init_pipeline(py::module &m) { "blocks, according to each module requirements and threading model.\n" "During the loop execution, the application can access the camera streams by calling wait_for_frames() or poll_for_frames().\n" "The streaming loop runs until the pipeline is stopped.\n" - "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n") + "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n", + py::call_guard()) .def("start", (rs2::pipeline_profile(rs2::pipeline::*)(const rs2::config&)) &rs2::pipeline::start, "Start the pipeline streaming according to the configuraion.\n" "The pipeline streaming loop captures samples from the device, and delivers them to the attached computer vision modules and processing blocks, according to " "each module requirements and threading model.\n" @@ -112,11 +113,12 @@ void init_pipeline(py::module &m) { "When the rs2::config is provided to the method, the pipeline tries to activate the config resolve() result.\n" "If the application requests are conflicting with pipeline computer vision modules or no matching device is available on the platform, the method fails.\n" "Available configurations and devices may change between config resolve() call and pipeline start, in case devices are connected or disconnected, or another " - "application acquires ownership of a device.", "config"_a) + "application acquires ownership of a device.", "config"_a, py::call_guard()) .def("start", [](rs2::pipeline& self, std::function f) { return self.start(f); }, "Start the pipeline streaming with its default configuration.\n" "The pipeline captures samples from the device, and delivers them to the provided frame callback.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" - "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "callback"_a) + "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "callback"_a, + py::call_guard()) .def("start", [](rs2::pipeline& self, const rs2::config& config, std::function f) { return self.start(config, f); }, "Start the pipeline streaming according to the configuraion.\n" "The pipeline captures samples from the device, and delivers them to the provided frame callback.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" @@ -125,11 +127,12 @@ void init_pipeline(py::module &m) { "When the rs2::config is provided to the method, the pipeline tries to activate the config resolve() result.\n" "If the application requests are conflicting with pipeline computer vision modules or no matching device is available on the platform, the method fails.\n" "Available configurations and devices may change between config resolve() call and pipeline start, in case devices are connected or disconnected, " - "or another application acquires ownership of a device.", "config"_a, "callback"_a) + "or another application acquires ownership of a device.", "config"_a, "callback"_a, py::call_guard()) .def("start", [](rs2::pipeline& self, rs2::frame_queue& queue) { return self.start(queue); },"Start the pipeline streaming with its default configuration.\n" "The pipeline captures samples from the device, and delivers them to the provided frame queue.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" - "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "queue"_a) + "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "queue"_a, + py::call_guard()) .def("start", [](rs2::pipeline& self, const rs2::config& config, rs2::frame_queue queue) { return self.start(config, queue); }, "Start the pipeline streaming according to the configuraion.\n" "The pipeline captures samples from the device, and delivers them to the provided frame queue.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" @@ -138,7 +141,7 @@ void init_pipeline(py::module &m) { "When the rs2::config is provided to the method, the pipeline tries to activate the config resolve() result.\n" "If the application requests are conflicting with pipeline computer vision modules or no matching device is available on the platform, the method fails.\n" "Available configurations and devices may change between config resolve() call and pipeline start, in case devices are connected or disconnected, " - "or another application acquires ownership of a device.", "config"_a, "queue"_a) + "or another application acquires ownership of a device.", "config"_a, "queue"_a, py::call_guard()) .def("stop", &rs2::pipeline::stop, "Stop the pipeline streaming.\n" "The pipeline stops delivering samples to the attached computer vision modules and processing blocks, stops the device streaming and releases " "the device resources used by the pipeline. It is the application's responsibility to release any frame reference it owns.\n" From 09696f984aff458c7bc2f3ea0e9fe1ee9b2ec532 Mon Sep 17 00:00:00 2001 From: Remi Bettan Date: Wed, 11 Dec 2024 09:08:45 +0200 Subject: [PATCH 2/3] restoring pipeline wrapper without GIL --- wrappers/python/pyrs_pipeline.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/wrappers/python/pyrs_pipeline.cpp b/wrappers/python/pyrs_pipeline.cpp index a3a3f09254..c00e7fb7de 100644 --- a/wrappers/python/pyrs_pipeline.cpp +++ b/wrappers/python/pyrs_pipeline.cpp @@ -101,8 +101,7 @@ void init_pipeline(py::module &m) { "blocks, according to each module requirements and threading model.\n" "During the loop execution, the application can access the camera streams by calling wait_for_frames() or poll_for_frames().\n" "The streaming loop runs until the pipeline is stopped.\n" - "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n", - py::call_guard()) + "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n") .def("start", (rs2::pipeline_profile(rs2::pipeline::*)(const rs2::config&)) &rs2::pipeline::start, "Start the pipeline streaming according to the configuraion.\n" "The pipeline streaming loop captures samples from the device, and delivers them to the attached computer vision modules and processing blocks, according to " "each module requirements and threading model.\n" @@ -113,12 +112,11 @@ void init_pipeline(py::module &m) { "When the rs2::config is provided to the method, the pipeline tries to activate the config resolve() result.\n" "If the application requests are conflicting with pipeline computer vision modules or no matching device is available on the platform, the method fails.\n" "Available configurations and devices may change between config resolve() call and pipeline start, in case devices are connected or disconnected, or another " - "application acquires ownership of a device.", "config"_a, py::call_guard()) + "application acquires ownership of a device.", "config"_a) .def("start", [](rs2::pipeline& self, std::function f) { return self.start(f); }, "Start the pipeline streaming with its default configuration.\n" "The pipeline captures samples from the device, and delivers them to the provided frame callback.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" - "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "callback"_a, - py::call_guard()) + "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "callback"_a) .def("start", [](rs2::pipeline& self, const rs2::config& config, std::function f) { return self.start(config, f); }, "Start the pipeline streaming according to the configuraion.\n" "The pipeline captures samples from the device, and delivers them to the provided frame callback.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" @@ -127,12 +125,11 @@ void init_pipeline(py::module &m) { "When the rs2::config is provided to the method, the pipeline tries to activate the config resolve() result.\n" "If the application requests are conflicting with pipeline computer vision modules or no matching device is available on the platform, the method fails.\n" "Available configurations and devices may change between config resolve() call and pipeline start, in case devices are connected or disconnected, " - "or another application acquires ownership of a device.", "config"_a, "callback"_a, py::call_guard()) + "or another application acquires ownership of a device.", "config"_a, "callback"_a) .def("start", [](rs2::pipeline& self, rs2::frame_queue& queue) { return self.start(queue); },"Start the pipeline streaming with its default configuration.\n" "The pipeline captures samples from the device, and delivers them to the provided frame queue.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" - "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "queue"_a, - py::call_guard()) + "When starting the pipeline with a callback both wait_for_frames() and poll_for_frames() will throw exception.", "queue"_a) .def("start", [](rs2::pipeline& self, const rs2::config& config, rs2::frame_queue queue) { return self.start(config, queue); }, "Start the pipeline streaming according to the configuraion.\n" "The pipeline captures samples from the device, and delivers them to the provided frame queue.\n" "Starting the pipeline is possible only when it is not started. If the pipeline was started, an exception is raised.\n" @@ -141,7 +138,7 @@ void init_pipeline(py::module &m) { "When the rs2::config is provided to the method, the pipeline tries to activate the config resolve() result.\n" "If the application requests are conflicting with pipeline computer vision modules or no matching device is available on the platform, the method fails.\n" "Available configurations and devices may change between config resolve() call and pipeline start, in case devices are connected or disconnected, " - "or another application acquires ownership of a device.", "config"_a, "queue"_a, py::call_guard()) + "or another application acquires ownership of a device.", "config"_a, "queue"_a) .def("stop", &rs2::pipeline::stop, "Stop the pipeline streaming.\n" "The pipeline stops delivering samples to the attached computer vision modules and processing blocks, stops the device streaming and releases " "the device resources used by the pipeline. It is the application's responsibility to release any frame reference it owns.\n" From 9827237edbd6e299e2b1a9c21613bb503dc35062 Mon Sep 17 00:00:00 2001 From: Remi Bettan Date: Thu, 12 Dec 2024 10:29:18 +0200 Subject: [PATCH 3/3] debug added --- examples/capture/rs-capture.cpp | 89 +++++++++++++------ src/linux/backend-v4l2.cpp | 1 + src/rs.cpp | 1 + .../live/frames/test-pipeline-start-stop.py | 8 +- 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/examples/capture/rs-capture.cpp b/examples/capture/rs-capture.cpp index b7cc55bdd0..f13f5efb31 100644 --- a/examples/capture/rs-capture.cpp +++ b/examples/capture/rs-capture.cpp @@ -1,39 +1,77 @@ -// License: Apache 2.0. See LICENSE file in root directory. +// License: Apache 2.0. See LICENSE file in root directory. // Copyright(c) 2017 Intel Corporation. All Rights Reserved. #include // Include RealSense Cross Platform API -#include "example.hpp" // Include short list of convenience functions for rendering +#include +#include + +std::ostream & operator<<( std::ostream & ss, const rs2::frame & self ) +{ + ss << rs2_format_to_string( self.get_profile().format() ); + ss << " #" << self.get_frame_number(); + ss << " @" << rsutils::string::from( self.get_timestamp() ); + return ss; +} + +std::string print_frameset(rs2::frame f) +{ + std::ostringstream ss; + ss << "frame"; + if (auto fs = f.as()) + { + ss << "set"; + for(auto sf : fs) + { + ss << " " << sf; + } + } + else + { + ss << " " << f; + } + return ss.str(); +} + +void run_and_verify_frame_received(rs2::pipeline& pipe) +{ + auto start = std::chrono::high_resolution_clock().now(); + pipe.start(); + auto frameset = pipe.wait_for_frames(); + auto stop = std::chrono::high_resolution_clock().now(); + auto duration_ms = std::chrono::duration_cast(stop - start).count(); + /*auto depth_frame = frameset.get_depth_frame(); + if( depth_frame) + { + std::cout << "D"; + } + auto color_frame = frameset.get_color_frame(); + if( color_frame) + { + std::cout << "C"; + } + std::cout << std::endl;*/ + std::ostringstream ss; + ss << "After " << duration_ms << "[msec], got first frame of " << print_frameset(frameset); + std::cout << ss.str() << std::endl; + pipe.stop(); +} // Capture Example demonstrates how to // capture depth and color video streams and render them to the screen int main(int argc, char * argv[]) try { - rs2::log_to_console(RS2_LOG_SEVERITY_ERROR); - // Create a simple OpenGL window for rendering: - window app(1280, 720, "RealSense Capture Example"); + rs2::log_to_file(RS2_LOG_SEVERITY_DEBUG, "mylog.txt"); - // Declare depth colorizer for pretty visualization of depth data - rs2::colorizer color_map; - // Declare rates printer for showing streaming rates of the enabled streams. - rs2::rates_printer printer; + auto ctx = rs2::context(); + auto dev = ctx.query_devices().front(); + rs2::pipeline pipe(ctx); + pipe.set_device(&dev); - // Declare RealSense pipeline, encapsulating the actual device and sensors - rs2::pipeline pipe; - - // Start streaming with default recommended configuration - // The default video configuration contains Depth and Color streams - // If a device is capable to stream IMU data, both Gyro and Accelerometer are enabled by default - pipe.start(); - - while (app) // Application still alive? + int iterations = 0; + while (++iterations < 500) // Application still alive? { - rs2::frameset data = pipe.wait_for_frames(). // Wait for next set of frames from the camera - apply_filter(printer). // Print each enabled stream frame rate - apply_filter(color_map); // Find and colorize the depth data - - // The show method, when applied on frameset, break it to frames and upload each frame into a gl textures - // Each texture is displayed on different viewport according to it's stream unique id - app.show(data); + std::cout << "iteration no" << iterations << std::endl; + run_and_verify_frame_received(pipe); } return EXIT_SUCCESS; @@ -48,3 +86,4 @@ catch (const std::exception& e) std::cerr << e.what() << std::endl; return EXIT_FAILURE; } + diff --git a/src/linux/backend-v4l2.cpp b/src/linux/backend-v4l2.cpp index 8889d3f50d..e67cf6322d 100644 --- a/src/linux/backend-v4l2.cpp +++ b/src/linux/backend-v4l2.cpp @@ -2098,6 +2098,7 @@ namespace librealsense ++pixel_format.index; } + LOG_WARNING("GET_PROFILES() - results_size = " << results.size()); return results; } diff --git a/src/rs.cpp b/src/rs.cpp index 035380315b..78af09ffa7 100644 --- a/src/rs.cpp +++ b/src/rs.cpp @@ -2307,6 +2307,7 @@ NOEXCEPT_RETURN(, pipe) rs2_pipeline_profile* rs2_pipeline_start(rs2_pipeline* pipe, rs2_error ** error) BEGIN_API_CALL { VALIDATE_NOT_NULL(pipe); + LOG_WARNING("PIPELINE START"); return new rs2_pipeline_profile{ pipe->pipeline->start(std::make_shared()) }; } HANDLE_EXCEPTIONS_AND_RETURN(nullptr, pipe) diff --git a/unit-tests/live/frames/test-pipeline-start-stop.py b/unit-tests/live/frames/test-pipeline-start-stop.py index 5f8a6b3a74..244e9d1f42 100644 --- a/unit-tests/live/frames/test-pipeline-start-stop.py +++ b/unit-tests/live/frames/test-pipeline-start-stop.py @@ -1,19 +1,21 @@ # License: Apache 2.0. See LICENSE file in root directory. # Copyright(c) 2023-2024 Intel Corporation. All Rights Reserved. -# test:donotrun:!nightly + # Currently, we exclude D457 as it's failing # test:device each(D400*) !D457 # On D455 and other units with IMU it takes ~4 seconds per iteration -# test:timeout 220 +# test:timeout 500 import pyrealsense2 as rs from rspy.stopwatch import Stopwatch from rspy import test, log import time +rs.log_to_file(rs.log_severity.debug, "pipeine_test_log.log") + # Run multiple start stop of all streams and verify we get a frame for each once -ITERATIONS_COUNT = 50 +ITERATIONS_COUNT = 500 dev, ctx = test.find_first_device_or_exit() pipe = rs.pipeline(ctx)