Hi,
I can see a strange behaviour with this simple code reading a RTSP stream from an IP camera (hikvision one at 1920x1080).
Memory of my program starts at 2.2% of the total when running for the first time the stream, and then each time I stop and I start the stream, the memory is increasing (GPU memory is stable).
It seems to be more stable when reaching 7.1% of the total but then seems to increase less quick than at the beginning.
This is very strange, and I cannot afford this kind of “leak” (if it’s a leak, maybe on my code), because this is for 1 stream, but we plan to receive up to 8 streams.
The program is running the stream for 3 sec, stops it for 3 secs, in an infinite loop until Ctrl+C is pressed.
I check memory usage of the program using htop watching at the percent of the total, and GPU usage using “cat /proc/meminfo | grep NvMapMemUsed” @dusty_nv told me on another thread.
The code is a bit ugly (using global, debug prints, etc) but it’s to show the problem.
#include <gst/gst.h>
#include <gst/app/gstappsink.h>
#include <gstreamer-1.0/gst/rtsp/gstrtsptransport.h>
#include <gio/gio.h>
#include <iostream>
#include <cassert>
GMainLoop* g_loop = nullptr;
void on_eos(GstAppSink* app_sink, gpointer user_data)
{
std::cout << "on_eos" << std::endl;
}
GstFlowReturn on_new_preroll(GstAppSink* app_sink, gpointer user_data)
{
static std::size_t i = 0;
std::cout << "on_new_preroll " << i++ << std::endl;
GstSample* sample = gst_app_sink_pull_preroll(app_sink);
if (sample != nullptr)
{
GstCaps* caps = gst_sample_get_caps(sample);
if (caps != nullptr)
{
GstStructure* structure = gst_caps_get_structure(caps, 0);
if (structure != nullptr)
{
int width;
gst_structure_get_int(structure, "width", &width);
int height;
gst_structure_get_int(structure, "height", &height);
std::cout << "width: " << width << ", height: " << height << std::endl;
const gchar* name = gst_structure_get_name(structure);
if (strcmp(name, "video/x-raw") == 0)
{
const gchar* format = gst_structure_get_string(structure, "format");
if (format != nullptr)
{
std::cout << "format: " << format << std::endl;
}
}
GstBuffer* buffer = gst_sample_get_buffer(sample);
if (buffer != nullptr)
{
GstMapInfo map;
if (gst_buffer_map(buffer, &map, GST_MAP_READ) == TRUE)
{
std::cout << "size: " << map.size << std::endl;
gst_buffer_unmap(buffer, &map);
}
}
}
}
gst_sample_unref(sample);
}
return GST_FLOW_OK;
}
GstFlowReturn on_new_sample(GstAppSink* app_sink, gpointer user_data)
{
static std::size_t i = 0;
std::cout << "on_new_sample " << i++ << std::endl;
GstSample* sample = gst_app_sink_pull_sample(app_sink);
if (sample != nullptr)
{
GstCaps* caps = gst_sample_get_caps(sample);
if (caps != nullptr)
{
GstStructure* structure = gst_caps_get_structure(caps, 0);
if (structure != nullptr)
{
int width;
gst_structure_get_int(structure, "width", &width);
int height;
gst_structure_get_int(structure, "height", &height);
std::cout << "width: " << width << ", height: " << height << std::endl;
const gchar* name = gst_structure_get_name(structure);
if (strcmp(name, "video/x-raw") == 0)
{
const gchar* format = gst_structure_get_string(structure, "format");
if (format != nullptr)
{
std::cout << "format: " << format << std::endl;
}
}
GstBuffer* buffer = gst_sample_get_buffer(sample);
if (buffer != nullptr)
{
GstMapInfo map;
if (gst_buffer_map(buffer, &map, GST_MAP_READ) == TRUE)
{
std::cout << "size: " << map.size << std::endl;
gst_buffer_unmap(buffer, &map);
}
}
}
}
gst_sample_unref(sample);
}
return GST_FLOW_OK;
}
void on_source_setup(GstElement* /*bin*/, GstElement* source, gpointer /*data*/)
{
const gchar* type_name = g_type_name(G_OBJECT_TYPE(source));
if (strcmp(type_name, "GstRTSPSrc") == 0)
{
g_object_set(G_OBJECT(source), "protocols", GST_RTSP_LOWER_TRANS_TCP, NULL);
g_object_set(G_OBJECT(source), "latency", 300, NULL);
g_object_set(G_OBJECT(source), "user-id", "admin", NULL);
g_object_set(G_OBJECT(source), "user-pw", "MYPASSWORD", NULL);
}
}
gboolean print_cap(GstCapsFeatures* features, GstStructure* structure, gpointer user_data)
{
gchar* s = gst_structure_to_string(structure);
g_print("%s\n", s);
g_free(s);
return TRUE;
}
void on_source_pad_added(GstElement* element, GstPad* pad, gpointer data)
{
g_print("Dynamic pad created, linking source -> convert\n");
GstElement* convert = reinterpret_cast<GstElement*>(data);
GstCaps* caps = gst_pad_get_current_caps(pad);
gst_caps_foreach(caps, print_cap, nullptr);
gst_caps_unref(caps);
GstPad* sinkpad = gst_element_get_static_pad(convert, "sink");
gst_pad_link(pad, sinkpad);
gst_object_unref(sinkpad);
}
void print_gst_error(GstMessage* msg)
{
gchar* debug;
GError* error;
gst_message_parse_error(msg, &error, &debug);
g_printerr("Error: %s, Debug: %s\n", error->message, debug);
g_free(debug);
g_error_free(error);
}
gboolean bus_call(GstBus* bus, GstMessage* msg, gpointer data)
{
switch (GST_MESSAGE_TYPE(msg))
{
case GST_MESSAGE_EOS:
std::cout << "bus_call: GST_MESSAGE_EOS" << std::endl;
break;
case GST_MESSAGE_ERROR:
std::cout << "bus_call: GST_MESSAGE_ERROR" << std::endl;
print_gst_error(msg);
break;
default:
break;
}
return FALSE;
}
void handle_signal(int sig)
{
if (sig == SIGINT)
{
std::cout << "SIGINT" << std::endl;
g_main_loop_quit(g_loop);
signal(SIGINT, SIG_DFL);
}
}
GstElement* g_pipeline = nullptr;
struct args
{
int argc;
char** argv;
};
gboolean stop_stream_callback(gpointer user_data);
gboolean start_stream_callback(gpointer user_data);
bool start(args* args)
{
std::cout << "start()" << std::endl;
//gst_init(&args->argc, &args->argv);
GstElement* uri_decode_bin = gst_element_factory_make("uridecodebin", "uridecodebin");
GstElement* convert = gst_element_factory_make("nvvidconv", "convert");
GstElement* sink = gst_element_factory_make("appsink", "sink");
if (uri_decode_bin != nullptr &&
convert != nullptr &&
sink != nullptr)
{
GstAppSink* app_sink = GST_APP_SINK(sink);
// Configure source
g_object_set(G_OBJECT(uri_decode_bin), "uri", "rtsp://192.168.10.52/Streaming/Channels/1", NULL);
// Configure sink
gst_app_sink_set_emit_signals(app_sink, TRUE);
gst_app_sink_set_drop(app_sink, TRUE);
gst_app_sink_set_max_buffers(app_sink, 1);
//g_object_set(sink, "drop", TRUE, NULL);
//g_object_set(sink, "max-buffers", 1, NULL);
GstCaps* caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "BGRx", NULL);
gst_app_sink_set_caps(app_sink, caps);
gst_caps_unref(caps);
// Pìpeline setup
g_pipeline = gst_pipeline_new("pipeline");
assert(g_pipeline != nullptr);
gst_bin_add_many(GST_BIN(g_pipeline), uri_decode_bin, convert, sink, NULL);
gst_element_link_many(convert, sink, NULL);
//g_signal_connect(g_pipeline, "deep-notify", G_CALLBACK(gst_object_default_deep_notify), NULL);
g_signal_connect(uri_decode_bin, "pad-added", G_CALLBACK(on_source_pad_added), convert);
// Configure source
g_signal_connect(uri_decode_bin, "source-setup", G_CALLBACK(on_source_setup), NULL);
// appsink signals
g_signal_connect(app_sink, "new-preroll", G_CALLBACK(on_new_preroll), NULL);
g_signal_connect(app_sink, "new-sample", G_CALLBACK(on_new_sample), NULL);
g_signal_connect(app_sink, "eos", G_CALLBACK(on_eos), NULL);
GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(g_pipeline));
gst_bus_add_watch(bus, bus_call, NULL);
gst_object_unref(bus);
gst_element_set_state(g_pipeline, GST_STATE_PLAYING);
return true;
}
else
{
gst_object_unref(GST_OBJECT(g_pipeline));
gst_object_unref(GST_OBJECT(uri_decode_bin));
gst_object_unref(GST_OBJECT(convert));
gst_object_unref(GST_OBJECT(sink));
return false;
}
}
void stop()
{
std::cout << "stop()" << std::endl;
if (g_pipeline != nullptr)
{
gst_element_set_state(g_pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(g_pipeline));
g_pipeline = nullptr;
}
//gst_deinit();
}
gboolean stop_stream_callback(gpointer user_data)
{
stop();
g_timeout_add_seconds(4, start_stream_callback, user_data);
return FALSE;
}
gboolean start_stream_callback(gpointer user_data)
{
start(reinterpret_cast<args*>(user_data));
g_timeout_add_seconds(4, stop_stream_callback, user_data);
return FALSE;
}
int main(int argc, char* argv[])
{
gst_init(&argc, &argv);
args args;
args.argc = argc;
args.argv = argv;
if (start(&args))
{
g_timeout_add_seconds(4, stop_stream_callback, &args);
struct sigaction signal_handler;
signal_handler.sa_handler = handle_signal;
sigemptyset(&signal_handler.sa_mask);
signal_handler.sa_flags = 0;
sigaction(SIGINT, &signal_handler, nullptr);
g_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(g_loop);
stop();
std::cout << "gst_deinit()" << std::endl;
gst_deinit();
g_main_loop_unref(g_loop);
return 0;
}
else
{
return 1;
}
}
EDIT:
After more investigation, using
valgrind --tool=massif --time-unit=ms --detailed-freq=1 --threshold=0.5 --suppressions=/usr/share/glib-2.0/valgrind/glib.supp --depth=30 --alloc-fn=g_malloc --alloc-fn=g_realloc --alloc-fn=g_try_malloc --alloc-fn=g_malloc0 --alloc-fn=g_mem_chunk_alloc myprogram
I get this result in massif-visualizer, maybe the problem is not related to nvvidconv… maybe someone here has had this problem?
EDIT2:
I have more information! I installed the dbg packages of base/ugly/good/bad on the jetson, and run valgrind again, now it gives me this:
It seems to be when the gstreamer receives SPS and PPS h264 NALs…
EDIT3:
If I run
GST_TRACERS="leaks" GST_DEBUG="GST_TRACER:7" gst-launch-1.0 uridecodebin uri=rtsp://admin:MYPASSWORD@192.168.10.52/Streaming/Channels/1 ! nvvidconv ! video/x-raw, format=BGRx ! fakesink -v
I get the following:
0:00:02.024830313 32299 0x55c1048870 TRACE GST_TRACER :0:: object-alive, type-name=(string)GstCaps, address=(gpointer)0x7f2c079450, description=(string)EMPTY, ref-count=(uint)1, trace=(string);
0:00:02.024909690 32299 0x55c1048870 TRACE GST_TRACER :0:: object-alive, type-name=(string)GstCaps, address=(gpointer)0x7f2c079e80, description=(string)video/x-raw(memory:NVMM), format=(string)NV12, width=(int)1920, height=(int)1080, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, chroma-site=(string)mpeg2, colorimetry=(string)bt709, framerate=(fraction)25/1, ref-count=(uint)1, trace=(string);
And if I run:
GST_TRACERS="leaks" GST_DEBUG="GST_TRACER:7" gst-launch-1.0 uridecodebin uri=rtsp://admin:MYPASSWORD@192.168.10.52/Streaming/Channels/1 ! nvvidconv ! video/x-raw(memory:NVMM), format=NV12 ! fakesink -v
I get the following:
0:00:01.384970454 1684 0x5594793870 TRACE GST_TRACER :0:: object-alive, type-name=(string)GstCaps, address=(gpointer)0x7f640b2140, description=(string)video/x-raw(memory:NVMM), format=(string)NV12, width=(int)1920, height=(int)1080, interlace-mode=(string)progressive, multiview-mode=(string)mono, multiview-flags=(GstVideoMultiviewFlagsSet)0:ffffffff:/right-view-first/left-flipped/left-flopped/right-flipped/right-flopped/half-aspect/mixed-mono, pixel-aspect-ratio=(fraction)1/1, chroma-site=(string)mpeg2, colorimetry=(string)bt709, framerate=(fraction)25/1, ref-count=(uint)1, trace=(string);
Does this mean there is really a leak in nvvidconv?