I’m utilizing GstSharpBundle in my .NET app. I’m randomly seeing the below error in Windows Event Viewer and it seems to kill my app when it happens, though I’m not sure it always kills it. And it appears to be somewhat random.
Application: SecurityCameraRecorderProcess.exe
CoreCLR Version: 9.0.24.52809
.NET Version: 9.0.0
Description: The process was terminated due to an unhandled exception.
Stack:
at GLib.ToggleRef.g_object_remove_toggle_ref(IntPtr, ToggleNotifyHandler, IntPtr)
at GLib.ToggleRef.g_object_remove_toggle_ref(IntPtr, ToggleNotifyHandler, IntPtr)
at GLib.ToggleRef.Free()
at GLib.ToggleRef.PerformQueuedUnrefs()
at GLib.Timeout+TimeoutProxy.Handler()
at GLib.MainLoop.g_main_loop_run(IntPtr)
at GLib.MainLoop.g_main_loop_run(IntPtr)
at GLib.MainLoop.Run()
at SecurityCameraRecorderProcess.Recorder.StartMainLoop()
at SecurityCameraRecorderProcess.Recorder.PlayPipeline()
at SecurityCameraRecorderProcess.Recorder.StartRecording()
at SecurityCameraRecorderProcess.Recorder.Run()
at SecurityCameraRecorderProcess.Program.Run(SecurityCameraRecorderProcess.Options)
at CommandLine.ParserResultExtensions.WithParsed[[System.__Canon, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](CommandLine.ParserResult1<System.__Canon>, System.Action1<System.__Canon>)
at SecurityCameraRecorderProcess.Program.Main(System.String[])
In addition, here’s what the app prints to the console:
(SecurityCameraRecorderProcess:9060): GStreamer-CRITICAL **: 05:39:20.820: The created element should be floating, this is probably caused by faulty bindings
(SecurityCameraRecorderProcess:9060): GStreamer-CRITICAL **: 05:39:20.914: The created element should be floating, this is probably caused by faulty bindings
[05:39:20 WRN] ../gst/rtsp/gstrtspsrc.c:gst_rtsp_src_receive_response@7226 - receive interrupted
[05:39:21 WRN] ../gst/rtsp/gstrtspsrc.c:gst_rtspsrc_try_send@7329 - receive interrupted
[05:39:21 WRN] ../gst/rtsp/gstrtspsrc.c:gst_rtspsrc_pause@10046 - PAUSE interrupted
(SecurityCameraRecorderProcess:9060): GStreamer-CRITICAL **: 05:39:21.336: The created element should be floating, this is probably caused by faulty bindings
(SecurityCameraRecorderProcess:9060): GStreamer-CRITICAL **: 05:39:21.336: The created element should be floating, this is probably caused by faulty bindings
Fatal error. 0xC0000005
at GLib.ToggleRef.g_object_remove_toggle_ref(IntPtr, ToggleNotifyHandler, IntPtr)
at GLib.ToggleRef.Free()
at GLib.ToggleRef.PerformQueuedUnrefs()
at GLib.Timeout+TimeoutProxy.Handler()
at GLib.MainLoop.g_main_loop_run(IntPtr)
at GLib.MainLoop.g_main_loop_run(IntPtr)
at GLib.MainLoop.Run()
at SecurityCameraRecorderProcess.Recorder.StartMainLoop()
at SecurityCameraRecorderProcess.Recorder.PlayPipeline()
at SecurityCameraRecorderProcess.Recorder.StartRecording()
at SecurityCameraRecorderProcess.Recorder.Run()
at SecurityCameraRecorderProcess.Program.Run(SecurityCameraRecorderProcess.Options)
at CommandLine.ParserResultExtensions.WithParsed[[System.__Canon, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](CommandLine.ParserResult1<System.__Canon>, System.Action1<System.__Canon>)
at SecurityCameraRecorderProcess.Program.Main(System.String[])
c:\Security Cameras\Security Recorder>
I’ve tested with GStreamer 1.28.5, 1.26.11, and 1.22.12. All seem to have this issue.
I attempted to create a reproducible test case, but I haven’t gotten it to fail. It’s pretty much exactly what the production code is, except for how OnFormatLocationFull returns files as well as some other unrelated code. Here it is:
using System.Collections.Concurrent;
using GLib;
using Gst;
using Gst.Rtsp;
using Gst.WebRTC;
using Application = Gst.Application;
using Constants = Gst.Constants;
using DateTime = System.DateTime;
using ObjectManager = GtkSharp.GstreamerSharp.ObjectManager;
using Task = System.Threading.Tasks.Task;
namespace GStreamerCrash
{
public class Program
{
private const string Field_Location = "location";
private const string MessageName_FragmentClosed = "splitmuxsink-fragment-closed";
private const string MessageName_FragmentOpened = "splitmuxsink-fragment-opened";
private const string PipelineText =
"rtspsrc location=rtsp://10.64.25.55:554/s0 name=rtspsrc rtspsrc. ! rtph264depay ! queue max-size-bytes=0 max-size-time=0 ! h264parse config-interval=-1 ! h264timestamper ! mux.video rtspsrc. ! application/x-rtp,media=audio ! queue max-size-bytes=0 max-size-time=0 ! decodebin ! audioconvert ! avenc_aac ! mux.audio_0 splitmuxsink name=mux async-finalize=true sink-properties=properties,file-mode=1 max-size-bytes=10000000";
private static readonly CancellationTokenSource CancellationTokenSource = new();
private static int FileIndex;
private static readonly IList<string> Files = new List<string>
{
"./video0.mp4",
"./video1.mp4",
"./video2.mp4",
};
private static BlockingCollection<(DateTime Date, string Filename)>? FragmentClosedCollection
{
get;
set;
}
private static Task? FragmentClosedTask
{
get;
set;
}
private static BlockingCollection<(DateTime Date, string Filename)>? FragmentOpenedCollection
{
get;
set;
}
private static Task? FragmentOpenedTask
{
get;
set;
}
private static Task? MessageListenerTask
{
get;
set;
}
private static Pipeline? Pipeline
{
get;
set;
}
public static void Main(
string[] args)
{
Initialize();
Run();
}
private static void AddFormatLocationListener()
{
var mux = Pipeline!.GetByName("mux");
mux.Connect("format-location-full", OnFormatLocationFull);
}
private static void AddFragmentLocationTasks()
{
FragmentOpenedCollection = new BlockingCollection<(DateTime, string)>();
FragmentClosedCollection = new BlockingCollection<(DateTime, string)>();
FragmentOpenedTask = Task.Run(() =>
{
while (!FragmentOpenedCollection.IsCompleted)
{
if (!FragmentOpenedCollection.TryTake(out var item, 100, CancellationTokenSource.Token))
{
continue;
}
Console.WriteLine($"Opened fragment {item.Filename} at {item.Date}");
}
}, CancellationTokenSource.Token);
FragmentClosedTask = Task.Run(() =>
{
while (!FragmentClosedCollection.IsCompleted)
{
if (!FragmentClosedCollection.TryTake(out var item, 100, CancellationTokenSource.Token))
{
continue;
}
Console.WriteLine($"Closed fragment {item.Filename} at {item.Date}");
}
}, CancellationTokenSource.Token);
}
private static void AddMessageListener()
{
MessageListenerTask = Task.Run(async () =>
{
while (!CancellationTokenSource.IsCancellationRequested)
{
var message = Pipeline!.Bus.TimedPopFiltered(Constants.CLOCK_TIME_NONE,
MessageType.Error | MessageType.Eos | MessageType.Element);
switch (message.Type)
{
case MessageType.Eos:
Console.WriteLine("EOS");
await Shutdown();
break;
case MessageType.Error:
message.ParseError(out var error, out var debug);
Console.WriteLine($"Error: {error.Message}, {debug}");
await Shutdown();
break;
case MessageType.Element:
try
{
var date = DateTime.Now;
var messageStructure = message.Structure;
var location = messageStructure.HasField(Field_Location)
? messageStructure.GetString(Field_Location)
: "";
var messageName = messageStructure.Name.ToLower();
switch (messageName)
{
case MessageName_FragmentOpened:
FragmentOpenedCollection?.Add((date, location), CancellationTokenSource.Token);
break;
case MessageName_FragmentClosed:
FragmentClosedCollection?.Add((date, location), CancellationTokenSource.Token);
break;
case "gstrtspsrctimeout":
Console.WriteLine("There was an RTSP timeout");
break;
}
}
catch (Exception ex)
{
Console.WriteLine($"Got error: {ex.Message}");
}
break;
}
}
}, CancellationTokenSource.Token);
}
private static void Initialize()
{
Console.WriteLine("Initializing");
Environment.SetEnvironmentVariable("GST_REGISTRY_UPDATE", "no");
Environment.SetEnvironmentVariable("GST_REGISTRY_1_0", "./gstreamer_registry.bin");
Environment.SetEnvironmentVariable("GST_DEBUG", "3, *rtp*:4, *rtsp*:4, *rtcp*:4");
Application.Init();
ObjectManager.Initialize();
GType.Register(WebRTCSessionDescription.GType, typeof(WebRTCSessionDescription));
GType.Register(RTSPMessage.GType, typeof(RTSPMessage));
ExceptionManager.UnhandledException += OnUnhandledGlibException;
}
private static void LaunchPipeline()
{
Pipeline = (Pipeline)Parse.Launch(PipelineText);
}
private static void OnFormatLocationFull(
object o,
SignalArgs args)
{
var file = Files[FileIndex];
file = Path.GetFullPath(file);
FileIndex++;
FileIndex %= Files.Count;
args.RetVal = file;
}
private static void OnUnhandledGlibException(
UnhandledExceptionArgs args)
{
if (args.ExceptionObject is not Exception ex)
{
Console.WriteLine($"Got unhandled glib exception of type {args.ExceptionObject.GetType()}");
return;
}
Console.WriteLine($"Unhandled Glib Exception: {ex.Message}\n{ex.StackTrace}");
}
private static void PlayPipeline()
{
var pipelineState = Pipeline!.SetState(State.Playing);
if (pipelineState != StateChangeReturn.Failure)
{
StartMainLoop();
}
throw new Exception($"Pipeline couldn't play. State was {pipelineState}");
}
private static void Run()
{
LaunchPipeline();
AddFragmentLocationTasks();
AddFormatLocationListener();
AddMessageListener();
PlayPipeline();
StopPipeline();
}
private static async Task Shutdown()
{
await CancellationTokenSource.CancelAsync();
StopPipeline();
throw new Exception("Recorder has been shut down due to an error or EOS.");
}
private static void StartMainLoop()
{
try
{
var mainLoop = new MainLoop();
using var tokenRegister = CancellationTokenSource.Token.Register(mainLoop.Quit);
mainLoop.Run();
Console.WriteLine(
$"Main loop done. Pipeline state is: {Pipeline!.CurrentState}. Error Cancelled? {CancellationTokenSource.IsCancellationRequested}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void StopPipeline()
{
Pipeline!.SetState(State.Null);
}
}
}
I’m not really sure where to go from here. Maybe loop my test code a bunch and hope it fails at some point? Or is there something obviously wrong that I’m doing?