GNOME Bugzilla – Bug 677708
Shared Gst.Buffers make .Dispose() unusable
Last modified: 2018-11-03 11:08:50 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).
This should be still a problem with the 1.x based bindings.
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); } }
-- 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.