Accelered gstreamer memory leak with appsrc and RTSP streaming

Hy all,

I am streaming RTSP using app-src to send custom data to the pipeline.
I first tried to use the default example with some modifications to be able to get properly out of the program :

#include <iostream>
#include <gst/gst.h>

#include <gst/rtsp-server/rtsp-server.h>

  GMainLoop *loop;
  GstRTSPServer *server;
  GstRTSPMountPoints *mounts;
  GstRTSPMediaFactory *factory;

typedef struct
{
  gboolean white;
  GstClockTime timestamp;
} MyContext;

/* called when we need to give data to appsrc */
static void
need_data (GstElement * appsrc, guint unused, MyContext * ctx)
{
  GstBuffer *buffer;
  guint size;
  GstFlowReturn ret;

  size = 640 * 480 * 3;

  buffer = gst_buffer_new_allocate (NULL, size, NULL);

  /* this makes the image black/white */
  gst_buffer_memset (buffer, 0, ctx->white ? 0xff : 0x0, size);

  ctx->white = !ctx->white;

  /* increment the timestamp every 1/2 second */
  GST_BUFFER_PTS (buffer) = ctx->timestamp;
  GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale_int (1, GST_SECOND, 2);
  ctx->timestamp += GST_BUFFER_DURATION (buffer);

  g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret);
  gst_buffer_unref (buffer);
}

/* called when a new media pipeline is constructed. We can query the
 * pipeline and configure our appsrc */
static void
media_configure (GstRTSPMediaFactory * factory, GstRTSPMedia * media,
    gpointer user_data)
{
  GstElement *element, *appsrc;
  MyContext *ctx;

  /* get the element used for providing the streams of the media */
  element = gst_rtsp_media_get_element (media);

  /* get our appsrc, we named it 'mysrc' with the name property */
  appsrc = gst_bin_get_by_name_recurse_up (GST_BIN (element), "mysrc");

  /* this instructs appsrc that we will be dealing with timed buffer */
  gst_util_set_object_arg (G_OBJECT (appsrc), "format", "time");
  /* configure the caps of the video */
  g_object_set (G_OBJECT (appsrc), "caps",
      gst_caps_new_simple ("video/x-raw",
          "format", G_TYPE_STRING, "BGR",
          "width", G_TYPE_INT, 640,
          "height", G_TYPE_INT, 480,
          "framerate", GST_TYPE_FRACTION, 2, 1, NULL), NULL);

  ctx = g_new0 (MyContext, 1);
  ctx->white = FALSE;
  ctx->timestamp = 0;
  /* make sure ther datais freed when the media is gone */
  g_object_set_data_full (G_OBJECT (media), "my-extra-data", ctx,
      (GDestroyNotify) g_free);

  /* install the callback that will be called when a buffer is needed */
  g_signal_connect (appsrc, "need-data", (GCallback) need_data, ctx);
  gst_object_unref (appsrc);
  gst_object_unref (element);
}

void signalHandler(int signal)
{
	g_print("SIGINT caught\n");

	if (nullptr != loop)
	{
		g_print("quitting main loop\n");
		g_main_loop_quit(loop);
	}

	g_main_loop_quit(loop);
	g_main_loop_unref(loop);
	loop = nullptr;

	if (G_IS_OBJECT(server)) {
		g_object_unref(server);
		server = nullptr;
	}
}

int main (int argc, char *argv[])
{
  signal(SIGINT, signalHandler);
  gst_init (&argc, &argv);

  loop = g_main_loop_new (NULL, FALSE);

  /* create a server instance */
  server = gst_rtsp_server_new();

  /* get the mount points for this server, every server has a default object
   * that be used to map uri mount points to media factories */
  mounts = gst_rtsp_server_get_mount_points (server);

  /* make a media factory for a test stream. The default media factory can use
   * gst-launch syntax to create pipelines.
   * any launch line works as long as it contains elements named pay%d. Each
   * element with pay%d names will be a stream */
  factory = gst_rtsp_media_factory_new ();
  gst_rtsp_media_factory_set_launch (factory,
      "( appsrc name=mysrc ! videoconvert ! x264enc ! "
      "video/x-h264, profile=high-4:4:4 ! rtph264pay name=pay0 pt=96 )");

  /* notify when our media is ready, This is called whenever someone asks for
   * the media and a new pipeline with our appsrc is created */
  g_signal_connect (factory, "media-configure", (GCallback) media_configure, NULL);

  /* attach the test factory to the /test url */
  gst_rtsp_mount_points_add_factory (mounts, "/test", factory);

  /* don't need the ref to the mounts anymore */
  g_object_unref (mounts);

  /* attach the server to the default maincontext */
  gst_rtsp_server_attach (server, NULL);

  /* start serving */
  g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
  g_main_loop_run (loop);

  gst_deinit();

  return 0;
}

I run this code using Valgrind with the following command : valgrind --tool=memcheck --leak-check=full --suppressions=./min.suppressions ./SoftwareXavierNX
I have attached the suppression file, wich suppresses the leaks of a basic application [ int main() { gst_init(null, null); gst_deinit); } ]
min.suppressions (18.0 KB)

If I run this code and open the video on the client PC using GStreamer with the following command: `gst-launch-1.0 playbin uri=rtsp://<JETSON_IP>:8554/test, it works and when I shut down the program, I get just one error according to Valgrind :

root@merio-desktop:/home/merio/Documents/SoftwareXavierNX/Release# valgrind --tool=memcheck --leak-check=full --suppressions=./min.suppressions ./SoftwareXavierNX
==9023== Memcheck, a memory error detector
==9023== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==9023== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==9023== Command: ./SoftwareXavierNX
==9023== 
stream ready at rtsp://127.0.0.1:8554/test
^CSIGINT caught
quitting main loop
==9023== 
==9023== HEAP SUMMARY:
==9023==     in use at exit: 1,008,038 bytes in 6,553 blocks
==9023==   total heap usage: 89,446 allocs, 82,893 frees, 952,027,558 bytes allocated
==9023== 
==9023== 960 bytes in 2 blocks are possibly lost in loss record 38 of 54
==9023==    at 0x484C120: calloc (vg_replace_malloc.c:1117)
==9023==    by 0x400FD07: allocate_dtv (dl-tls.c:286)
==9023==    by 0x40105AF: _dl_allocate_tls (dl-tls.c:530)
==9023==    by 0x5A98BDF: allocate_stack (allocatestack.c:627)
==9023==    by 0x5A98BDF: pthread_create@@GLIBC_2.17 (pthread_create.c:644)
==9023==    by 0x592F6EF: ??? (in /usr/lib/aarch64-linux-gnu/libglib-2.0.so.0.5600.4)
==9023== 
==9023== LEAK SUMMARY:
==9023==    definitely lost: 0 bytes in 0 blocks
==9023==    indirectly lost: 552 bytes in 12 blocks
==9023==      possibly lost: 960 bytes in 2 blocks
==9023==    still reachable: 43,617 bytes in 33 blocks
==9023==                       of which reachable via heuristic:
==9023==                         length64           : 872 bytes in 20 blocks
==9023==                         newarray           : 1,680 bytes in 25 blocks
==9023==         suppressed: 866,661 bytes in 5,938 blocks
==9023== Reachable blocks (those to which a pointer was found) are not shown.
==9023== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==9023== 
==9023== For lists of detected and suppressed errors, rerun with: -s
==9023== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

So it is not perfect, but this memory issue is perfectly stable in time, I get exactly the same number of lost bytes and errors if the program runs a few seconds and 10 minutes, so it is not a big deal.

However, if I run exactly the same code, but I replace the line

  gst_rtsp_media_factory_set_launch (factory,
      "( appsrc name=mysrc ! videoconvert ! x264enc ! "
      "video/x-h264, profile=high-4:4:4 ! rtph264pay name=pay0 pt=96 )");

by

  gst_rtsp_media_factory_set_launch (factory,
      "( appsrc name=mysrc is-live=true ! "
		"videoconvert ! video/x-raw format=NV12 ! "
		"nvvidconv ! video/x-raw(memory:NVMM) ! "
		"nvv4l2h264enc ! rtph264pay name=pay0 pt=96 )");

To use accelerated GStreamer, I get a few hundred errors :
MemoryLeakAccelertedGstreamer.txt (196.8 KB)
here is the summary :

==8169== LEAK SUMMARY:
==8169==    definitely lost: 10,847 bytes in 185 blocks
==8169==    indirectly lost: 3,553 bytes in 55 blocks
==8169==      possibly lost: 2,312 bytes in 17 blocks
==8169==    still reachable: 238,209 bytes in 181 blocks
==8169==                       of which reachable via heuristic:
==8169==                         length64           : 1,192 bytes in 28 blocks
==8169==                         newarray           : 1,680 bytes in 25 blocks
==8169==         suppressed: 754,090 bytes in 5,464 blocks
==8169== Reachable blocks (those to which a pointer was found) are not shown.
==8169== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==8169== 
==8169== Use --track-origins=yes to see where uninitialised values come from
==8169== For lists of detected and suppressed errors, rerun with: -s
==8169== ERROR SUMMARY: 543 errors from 216 contexts (suppressed: 4 from 4)

and with a run of 10 minutes :

==8224== LEAK SUMMARY:
==8224==    definitely lost: 10,847 bytes in 185 blocks
==8224==    indirectly lost: 3,553 bytes in 55 blocks
==8224==      possibly lost: 2,312 bytes in 17 blocks
==8224==    still reachable: 238,209 bytes in 181 blocks
==8224==                       of which reachable via heuristic:
==8224==                         length64           : 1,192 bytes in 28 blocks
==8224==                         newarray           : 1,680 bytes in 25 blocks
==8224==         suppressed: 754,090 bytes in 5,464 blocks
==8224== Reachable blocks (those to which a pointer was found) are not shown.
==8224== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==8224== 
==8224== Use --track-origins=yes to see where uninitialised values come from
==8224== For lists of detected and suppressed errors, rerun with: -s
==8224== ERROR SUMMARY: 15749 errors from 216 contexts (suppressed: 4 from 4)

The number of errors increased, but not the number of leaked bytes.

I use a Jetson XavierNX Jetpack 4.5.1 R32.5.1-20210219084708

Any suggestion on how to fix the memory leaks?
In this example, the issue might not cause big problems but in my final application, the program will run for hours, maybe even days with many connections and disconnections. So all those errors may slow down the program after a long run.

Thank’s,
Matteo