Tool/software:
We are porting our video processing pipeline to the TI platform and have encountered an issue with the dynamic reconfiguration of GStreamer elements. Our pipeline utilizes the tiovx
, tiovxmultiscaler
and v4l2h265enc
GStreamer modules. We have observed that several of these modules do not correctly support dynamic property changes, a standard GStreamer feature crucial for our application. When new property values are set, they are not applied, and the modules continue to operate with the previous settings.
Currently, we have identified tiovxmultiscaler
, tivxldc
, and v4l2h265enc
as being affected by this issue.
- For
tiovxmultiscaler
, changing the "roi" property does not affect the output video. - For
tivxldc
, updating the "lut-file" property is ineffective. Furthermore, we have observed that changing the resolution is also not working as expected. - The
v4l2h265enc
module also exhibits issues with dynamic resolution updates.
Restarting the entire pipeline to update these properties is not a viable solution for us, as the stop/start sequence introduces an unacceptable delay.
We have attached a simplified example code(test.cpp) demonstrating this issue. The code functions correctly when using software-based modules, such as videoscale
(instead of tiovxmultiscaler
) and avenc_mjpeg
(instead of v4l2h265enc
). This serves to isolate the problem to the TI-specific hardware acceleration modules.
The program can be compiled using the following command:
g++ -o test test.cpp `pkg-config --cflags --libs gstreamer-1.0`
And then run with the following options:
- Using software scaler and encoder:
./test useV4L2=0 useScaler=1 useSwScaler=1
- Using hardware encoder and scaler:
./test useV4L2=1 useScaler=1 useSwScaler=0
The output video is saved as vid.out
. Analysis of this video will demonstrate that the resolution and ROI are not updated, even though the new values are set correctly within the application.
We are using SDK 10 - > oe-layersetup/configs/processor-sdk-analytics /processor-sdk-analytics-10.00.00-config.txt
We would greatly appreciate your assistance in resolving this issue. Please let us know if you require any further information.
Thank you for your time and attention to this matter.
#include <gst/gst.h> #include <iostream> #include <thread> #include <chrono> #include <map> int main(int argc, char *argv[]) { // Default parameter values bool useScaler{true}; bool useSwScaler{true}; bool useV4L2{false}; // Parse command line arguments std::map<std::string, std::string> args; std::cout << "argc: " << argc << std::endl; for (int i = 1; i < argc; i++) { std::string arg = argv[i]; size_t pos = arg.find('='); if (pos != std::string::npos) { std::string key = arg.substr(0, pos); std::string value = arg.substr(pos + 1); std::cout << "key: " << key << ", value: " << value << std::endl; args[key] = value; } } // Process arguments if (args.count("useScaler")) { std::string val = args["useScaler"]; useScaler = (val == "true" || val == "1" || val == "yes"); } if (args.count("useSwScaler")) { std::string val = args["useSwScaler"]; useSwScaler = (val == "true" || val == "1" || val == "yes"); } if (args.count("useV4L2")) { std::string val = args["useV4L2"]; useV4L2 = (val == "true" || val == "1" || val == "yes"); } gst_init(&argc, &argv); // Create pipeline elements auto pipeline = gst_pipeline_new("test-pipeline"); auto source = gst_element_factory_make("videotestsrc", "source"); auto capsfilter = gst_element_factory_make("capsfilter", "capsfilter"); auto tiovxldc = gst_element_factory_make("tiovxldc", "tiovxldc"); auto scaler = gst_element_factory_make(useSwScaler ? "videoscale" : "tiovxmultiscaler", "scaler"); auto capsfilter2 = gst_element_factory_make("capsfilter", "capsfilter2"); auto videoconvert = gst_element_factory_make("videoconvert", "videoconvert"); auto encoder = gst_element_factory_make(useV4L2? "v4l2h265enc" : "avenc_mjpeg", "encoder"); auto sink = gst_element_factory_make("filesink", "sink"); // Check if all elements were created successfully if (!pipeline || !source || !capsfilter || !encoder || !capsfilter2 || !tiovxldc || ! scaler || !videoconvert || !sink) { std::cerr << "Failed to create all elements!" << std::endl; return -1; } // Configure elements g_object_set(source, "pattern", 19, nullptr); // 19 = smpte100 auto caps = gst_caps_new_simple("video/x-raw", "width", G_TYPE_INT, 2896, "height", G_TYPE_INT, 1876, "framerate", GST_TYPE_FRACTION, 30, 1, nullptr); g_object_set(capsfilter, "caps", caps, nullptr); gst_caps_unref(caps); auto caps2 = gst_caps_new_simple("video/x-raw", "width", G_TYPE_INT, 1280, "height", G_TYPE_INT, 720, "format", G_TYPE_STRING, "NV12", nullptr); g_object_set(capsfilter2, "caps", caps2, nullptr); gst_caps_unref(caps2); g_object_set(sink, "location", "vid.out", nullptr); if (useScaler) { if (!useSwScaler) { g_object_set(scaler, "target", 2, nullptr); g_signal_connect( scaler, "pad-added", G_CALLBACK(+[](GstElement * /*compositor*/, GstPad *new_pad, void *) { std::string const sNewPadName{GST_PAD_NAME(new_pad)}; if (sNewPadName == "src_0") { std::cout << "Update ROI" << std::endl; g_object_set(new_pad, "roi-starty", 320, "roi-height", 720, "roi-startx", 320, "roi-width", 1280, NULL); } }), NULL); } } else { g_object_set(tiovxldc, "target", 1, "ldc-ds-factor", 3,"ldc-table-width", 1920, "ldc-table-height", 1080, "lut-file" , "/root/btomc/mesh.txt.bin1080_zero", nullptr); } // Add elements to pipeline gst_bin_add_many(GST_BIN(pipeline), source, capsfilter, useScaler ? scaler : tiovxldc, capsfilter2, videoconvert , encoder, sink, nullptr); // Link elements if (!gst_element_link_many(source, capsfilter, useScaler ? scaler : tiovxldc, capsfilter2, videoconvert, encoder, sink, nullptr)) { std::cerr << "Failed to link elements!" << std::endl; gst_object_unref(pipeline); return -1; } // Start pipeline GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { std::cerr << "Failed to start pipeline!" << std::endl; gst_object_unref(pipeline); return -1; } // Wait for pipeline to reach PLAYING state GstState state; ret = gst_element_get_state(pipeline, &state, nullptr, GST_CLOCK_TIME_NONE); if (ret == GST_STATE_CHANGE_FAILURE || state != GST_STATE_PLAYING) { std::cerr << "Pipeline failed to reach PLAYING state!" << std::endl; gst_element_set_state(pipeline, GST_STATE_NULL); gst_object_unref(pipeline); return -1; } std::cout << "Pipeline is now playing. Pattern is smpte100 (19)." << std::endl; // Wait for 5 second std::this_thread::sleep_for(std::chrono::seconds(5)); std::cout << "Dynamic update start" << std::endl; // Change pattern to 18 (ball) //g_object_set(source, "pattern", 18, nullptr); //std::cout << "Changed pattern to ball (18)" << std::endl; if (useScaler) { if (!useSwScaler) { std::cout << "Update ROI" << std::endl; auto pad{gst_element_get_static_pad(scaler, "src_0")}; g_object_set(pad, "roi-starty", 0, "roi-height", 1080, "roi-startx", 0, "roi-width", 1920, NULL); gst_object_unref(pad); } auto caps3 = gst_caps_new_simple("video/x-raw", "width", G_TYPE_INT, 1920, "height", G_TYPE_INT, 1080, "format", G_TYPE_STRING, "NV12", nullptr); g_object_set(capsfilter2, "caps", caps3, nullptr); } else { std::cout << "Update Lut file and output size" << std::endl; g_object_set(tiovxldc, "target", 1, "ldc-ds-factor",3 ,"ldc-table-width", 1920, "ldc-table-height", 1080, "lut-file" , "/root/btomc/mesh.txt.bin1080", nullptr); auto caps3 = gst_caps_new_simple("video/x-raw", "width", G_TYPE_INT, 1920, "height", G_TYPE_INT, 1080, "format", G_TYPE_STRING, "NV12", nullptr); g_object_set(capsfilter2, "caps", caps3, nullptr); } std::cout << "Dynamic update done" << std::endl; // Wait for 5 second std::this_thread::sleep_for(std::chrono::seconds(5)); # if 0 // Wait for EOS or error auto bus = gst_element_get_bus(pipeline); auto msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, static_cast<GstMessageType>(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); if (msg != nullptr) gst_message_unref(msg); gst_object_unref(bus); #endif // Clean up gst_element_set_state(pipeline, GST_STATE_NULL); gst_object_unref(pipeline); return 0; }