After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 677708 - Shared Gst.Buffers make .Dispose() unusable
Shared Gst.Buffers make .Dispose() unusable
Status: RESOLVED OBSOLETE
Product: GStreamer
Classification: Platform
Component: gst-sharp
git master
Other Windows
: Normal normal
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
Depends on:
Blocks:
 
 
Reported: 2012-06-08 13:47 UTC by Petteri Aimonen
Modified: 2018-11-03 11:08 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Petteri Aimonen 2012-06-08 13:47:48 UTC
GStreamer shares buffers between sinks to conserve memory. Gst-sharp returns the same managed object when two sinks receive the buffer. If one sink disposes the object, the other sink gets NullReferenceException.

Obvious way to avoid this problem is not to call .Dispose(), and instead rely on garbage collection. However, this causes a lot of buffers to accumulate before GC kicks in. I measured 100 MB memory overhead on an otherwise 50 MB usage with a simple videotestsrc + appsink combination. With many streams and larger resolution/framerate it could be much worse.

(The same problem exists with shared Glib.Object references, but it is much less acute as they are not circulated as often as Gst.Buffers.)

There are two ways to fix this problem:

1) Make Gst.MiniObject not shared. This might have some implications if complex C# objects are built upon MiniObject, as there would be multiple managed instances referring a single unmanaged object. However, all the currently implemented MiniObject derivatives (Buffer, Event, Message, etc.) are simple wrappers with no managed data.

Implemented by this patch: http://code.google.com/p/ossbuild-vs2010/source/detail?r=cdcfefd5b9652a8910f0b27449cb5a7682aa69ce

2) Make Gst.MiniObject maintain a count of 'users', a bit like reference counting but not directly tied to the destruction of the underlying unmanaged object. This is less clean than 1, but on the other hand less intrusive.

Implemented by this patch: http://code.google.com/p/ossbuild-vs2010/source/detail?r=6ced992458f96e3b5bf8eb25d81a2ce8808081a9

I vote for 1).
Comment 1 Sebastian Dröge (slomo) 2014-03-02 20:51:51 UTC
This should be still a problem with the 1.x based bindings.
Comment 2 blad 2016-05-31 09:16:30 UTC
I can confirm the problem using 1.x based bindings.

Reproducable with:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Gst;
using System.Runtime.InteropServices;

namespace AppSinkSample
{
    class Program
    {
        static Element VideoTestSource, VideoCaps, VideoSink, AppQueue;
        static Gst.App.AppSink AppSink;

        static GLib.MainLoop MainLoop; // GLib's Main Loop

        // The appsink has received a buffer
        static void NewSample(object sender, GLib.SignalArgs args)
        {
            var sink = (Gst.App.AppSink)sender;

            // Retrieve the buffer
            var sample = sink.PullSample();
            if (sample != null)
            {
                // The only thing we do in this example is print a * to indicate a received buffer
                Console.Write("*");
                sample.Dispose();
            }
        }

        static void Main(string[] args)
        {
            // Initialize Gstreamer
            Gst.Application.Init(ref args);

            // Create the elements
            VideoTestSource = ElementFactory.Make("videotestsrc", "videotestsrc0");
            VideoCaps = ElementFactory.Make("capsfilter", "capsfilter0");
            AppQueue = ElementFactory.Make("queue", "app_queue");
            AppSink = new Gst.App.AppSink("app_sink");
            
            // Create the empty pipeline
            var pipeline = new Pipeline("test-pipeline");

            if (VideoTestSource == null || VideoCaps == null || AppQueue == null || AppSink == null)
            {
                return;
            }

            // Configure videotestsrc
            VideoTestSource["pattern"] = "snow";
            VideoCaps["caps"] = Gst.Caps.FromString("video/x-raw,width=640,height=480");

            // Configure appsink
            AppSink["emit-signals"] = true;
            AppSink.NewSample += NewSample;

            pipeline.Add(VideoTestSource, VideoCaps, AppQueue, AppSink);

            if (!Element.Link(VideoTestSource, VideoCaps) || !Element.Link(VideoCaps, AppQueue) || !Element.Link(AppQueue, AppSink))
            {
                return;
            }

            // Start playing the pipeline
            pipeline.SetState(State.Playing);

            // Create a GLib Main Loop and set it to run
            MainLoop = new GLib.MainLoop();
            MainLoop.Run();

            // Free resources
            pipeline.SetState(State.Playing);

        }
    }
}

Workaround is using native functions for appsink:


        [DllImport("libgstreamer-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern void gst_mini_object_unref(IntPtr raw);

        [DllImport("libgstapp-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern IntPtr gst_app_sink_pull_sample(IntPtr raw);

        [DllImport("libgstreamer-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern bool gst_buffer_map(IntPtr raw, IntPtr info, int flags);

        [DllImport("libgstreamer-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern void gst_buffer_unmap(IntPtr raw, IntPtr info);

        [DllImport("libgstreamer-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern IntPtr gst_sample_get_buffer(IntPtr raw);

        [DllImport("libgstreamer-1.0-0.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern IntPtr gst_sample_get_caps(IntPtr raw);


        private void GetSinkSampleImageAppSink(object sender, GLib.SignalArgs args)
        {
            var sink = (Gst.App.AppSink)sender;

            IntPtr sampleNativePtr = gst_app_sink_pull_sample(sink.Handle);

            if (sampleNativePtr != null)
            {
                IntPtr capsNativePtr = gst_sample_get_caps(sampleNativePtr);

                Gst.Caps caps = capsNativePtr == IntPtr.Zero ? null : (Gst.Caps)GLib.Opaque.GetOpaque(capsNativePtr, typeof(Gst.Caps), false);

                var wVal = caps[0].GetValue("width");
                var hVal = caps[0].GetValue("height");

                var height = (int)hVal.Val;
                var width = (int)wVal.Val;

                IntPtr bufferNativePtr = gst_sample_get_buffer(sampleNativePtr);

                IntPtr mapInfoNativePtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Gst.MapInfo)));
                gst_buffer_map(bufferNativePtr, mapInfoNativePtr, (int)MapFlags.Read);
                Gst.MapInfo mapInfo = Gst.MapInfo.New(mapInfoNativePtr);
                
                Bitmap bitmapSample = new Bitmap(width, height, width * 3, PixelFormat.Format24bppRgb, mapInfo.DataPtr);

                gst_buffer_unmap(bufferNativePtr, mapInfoNativePtr);
                gst_mini_object_unref(sampleNativePtr);
            }
        }
Comment 3 GStreamer system administrator 2018-11-03 11:08:50 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/gstreamer/gstreamer-sharp/issues/1.