Skip to content

GitLab

  • Menu
Projects Groups Snippets
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • gst-plugins-bad gst-plugins-bad
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 985
    • Issues 985
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 132
    • Merge requests 132
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Monitor
    • Monitor
    • Incidents
  • Packages & Registries
    • Packages & Registries
    • Container Registry
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • GStreamer
  • gst-plugins-badgst-plugins-bad
  • Issues
  • #946

Closed
Open
Created Apr 08, 2019 by Peter Seiderer@psreportContributor

msdkh264enc: memory leak in case used with dynamic pipelines (Windows)

Using the msdkh264enc element with dynamic pipelines eats my system memory when run on Windows.

Running the following test program, add/remove 'queue ! msdkh264enc ! fakesink' dynamically to/from a running pipeline (derived from dyanamic-tee-vsink example, see [1], [2]), on every loop ca. 20 MB of Memory are leaked (happens only when running on Windows, not on Linux):

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

#define VIDEO_RECORDING_ENCODER  "msdkh264enc"
//#define VIDEO_RECORDING_ENCODER  "x264enc"
//#define VIDEO_RECORDING_ENCODER  "openh264enc"

static GMainLoop* loop;

static GstElement* pipeline;

static GstPad* videoTeeSrcPad;
static GstElement* recordingQueue;
static GstElement* recordingEncoder;
static GstElement* recordingSink;

static volatile int timeoutCbCounter;

static gboolean unlinkCbCalled;

static GstPadProbeReturn unlink_cb(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) {
    g_print("Debug: unlink_cb()\n");

    if (!g_atomic_int_compare_and_exchange(&unlinkCbCalled, false, true)) {
        g_print("Debug: unlink_cb() - removing already set\n");
        return GST_PAD_PROBE_OK;
    }

    GstElement* tee = gst_bin_get_by_name(GST_BIN(pipeline), "VideoTee");
    if (tee == nullptr) {
        g_print("Error: gst_bin_get_by_name(VideoTee) failed\n");
        return GST_PAD_PROBE_OK;
    }

    GstPad* sinkpad = gst_element_get_static_pad(recordingQueue, "sink");
    gst_pad_unlink(videoTeeSrcPad, sinkpad);
    gst_object_unref(sinkpad);

    gst_bin_remove(GST_BIN(pipeline), recordingQueue);
    gst_bin_remove(GST_BIN(pipeline), recordingEncoder);
    gst_bin_remove(GST_BIN(pipeline), recordingSink);

    gst_element_set_state(recordingSink, GST_STATE_NULL);
    gst_element_set_state(recordingEncoder, GST_STATE_NULL);
    gst_element_set_state(recordingQueue, GST_STATE_NULL);

#if 0
    gst_object_unref(recordingQueue);
    recordingQueue = nullptr;
    gst_object_unref(recordingEncoder);
    recordingEncoder = nullptr;
    gst_object_unref(recordingSink);
    recordingSink = nullptr;
#endif

    gst_element_release_request_pad(tee, videoTeeSrcPad);
    gst_object_unref(videoTeeSrcPad);
    videoTeeSrcPad = nullptr;

    gst_object_unref(tee);

    timeoutCbCounter++;

    g_print("Debug: unlink done\n");

    return GST_PAD_PROBE_REMOVE;
}

static gboolean timeout_cb(gpointer user_data) {
    g_print("Debug: timeout_cb() - timeoutCbCounter = %d\n", timeoutCbCounter);

#if 0
    // abort after 3 times encoder start/stop
    if (timeoutCbCounter >= 6) {
        g_print("Debug: timeout_cb() - g_main_loop_quit()\n");
        g_main_loop_quit(loop);
        return false;
    }
#endif

    if ((timeoutCbCounter % 2) == 1) {
        // unlink the recording branch
        unlinkCbCalled = false;
        gst_pad_add_probe(videoTeeSrcPad, GST_PAD_PROBE_TYPE_IDLE, unlink_cb, nullptr, nullptr);
        return true;
    } else {
        // add the recoding branch
        GstElement* tee = gst_bin_get_by_name(GST_BIN(pipeline), "VideoTee");
        if (tee == nullptr) {
            g_print("Error: gst_bin_get_by_name(VideoTee) failed\n");
            return false;
        }

        if (recordingQueue == nullptr) {
            recordingQueue = gst_element_factory_make("queue", "RecordingQueue");
            if (recordingQueue == nullptr) {
                g_print("Error: gst_element_factory_make(queue) failed\n");
                return false;
            }
            gst_object_ref_sink(recordingQueue);
        }

        if (recordingEncoder == nullptr) {
            recordingEncoder = gst_element_factory_make(VIDEO_RECORDING_ENCODER, "RecordingEncoder");
            if (recordingEncoder == nullptr) {
                g_print("Error: gst_element_factory_make(" VIDEO_RECORDING_ENCODER ") failed\n");
                return false;
            }
            gst_object_ref_sink(recordingEncoder);
        }

        if (recordingSink == nullptr) {
            recordingSink = gst_element_factory_make("fakesink", "RecordingSink");
            if (recordingSink == nullptr) {
                g_print("Error: gst_element_factory_make(fakesink) failed\n");
                return false;
            }
            gst_object_ref_sink(recordingSink);
        }

        gst_bin_add_many(GST_BIN(pipeline), recordingQueue, recordingEncoder, recordingSink, nullptr);
        gst_element_link_many(recordingQueue, recordingEncoder, recordingSink, nullptr);

        gst_element_sync_state_with_parent(recordingQueue);
        gst_element_sync_state_with_parent(recordingEncoder);
        gst_element_sync_state_with_parent(recordingSink);

        GstPadTemplate* srcPadTemplate = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS(tee), "src_%u");
        videoTeeSrcPad = gst_element_request_pad(tee, srcPadTemplate, NULL, NULL);

        GstPad* sinkPad = gst_element_get_static_pad(recordingQueue, "sink");
        if (gst_pad_link(videoTeeSrcPad, sinkPad) != GST_PAD_LINK_OK) {
            g_print("Error: gst_pad_link() failed\n");
            gst_object_unref(sinkPad);
            gst_element_release_request_pad(tee, videoTeeSrcPad);
            gst_object_unref(videoTeeSrcPad);
            return false;
        }
        g_print("Debug: gst_pad_link() queue --> " VIDEO_RECORDING_ENCODER " --> fakesink done\n");

        gst_object_unref(sinkPad);
        gst_object_unref(tee);

        timeoutCbCounter++;
        return true;
    }
}

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

  loop = g_main_loop_new(nullptr, false);

  pipeline = gst_parse_launch("videotestsrc name=VideoSrc ! tee name=VideoTee ! queue name=VideoQueue ! fakesink name=VideoSink", nullptr);

  g_return_val_if_fail(pipeline, -1);

  g_timeout_add_seconds(3, timeout_cb, loop);

  gst_element_set_state(pipeline, GST_STATE_PLAYING);

  g_main_loop_run(loop);

  gst_element_set_state(pipeline, GST_STATE_NULL);
  gst_object_unref(pipeline);

  if (recordingQueue != nullptr) {
      gst_object_unref(recordingQueue);
  }
  if (recordingEncoder != nullptr) {
      gst_object_unref(recordingEncoder);
  }
  if (recordingSink != nullptr) {
      gst_object_unref(recordingSink);
  }

  gst_deinit();

  exit(0);
}
  • OS: Windows 10
  • GStreamer: 1.15.2
  • Hardware: Intel i7-6920

[1] https://coaxion.net/blog/2014/01/gstreamer-dynamic-pipelines/

[2] https://github.com/sdroege/gst-snippets/blob/217ae015aaddfe3f7aa66ffc936ce93401fca04e/dynamic-tee-vsink.c

Assignee
Assign to
Time tracking