Releases: masa-iwm/GstSharp.Net
Release list
v1.28.10
The release where the containers the gir hides finally cross the boundary. A
GBytes is an argument and a return like any other, a GHashTable of strings
arrives as a dictionary, a GSList return is read the way a GList already
was, a counted array the caller supplies is filled in place, and the borrowed constant
structure a C lookup hands back is copied out rather than refused. Around them a
fraction becomes a value type the property system knows, the child properties of
a timeline element can be listed and overridden, an RTSP message yields the
credentials it was challenged with, an ICE agent the candidates it gathered, and
a bus the descriptor a foreign event loop can wait on. Net, forty-three fewer
symbols sit under the catch-all skip reason. Three
things changed behaviour, and each of them is one line below. All eighteen
packages ship as one version, 1.28.10, generated from the GStreamer 1.28.6
.gir files, 50 commits after 1.28.9.
Added
- A block of bytes across the boundary.
Gst.Buffer.NewWrappedBytes(Gst.GLib.Bytes)
wraps the block zero-copy as read-only memory, and an empty block gives a
buffer with no memory in it. - The adapter copy that answers a block.
Gst.Base.Adapter.Copy(nuint offset, nuint size)
answers aGst.GLib.Bytes; a size of zero is an empty block, and an offset
pastAvailablethrows even then. - The RTP payload and header extension as blocks.
Gst.Rtp.RTPBuffer.GetPayload()
andGetExtensionData(out ushort bits)answer the payload and the header
extension as blocks, and the static
GetExtensionOnebyteHeaderFromBytes(Gst.GLib.Bytes, ushort, byte, uint, out byte[]?)
reads the nth one-byte header extension of a given id back out of one. - A MIKEY message from and to a block.
Gst.Sdp.MIKEYMessage.NewFromBytes(Gst.GLib.Bytes, Gst.Sdp.MIKEYDecryptInfo?)
answers null for a malformed message, andToBytes(Gst.Sdp.MIKEYEncryptInfo?)
serialises one back out. - A fraction the property system knows.
Gst.Fractionis a readonly record
struct whoseToStringisN/D, withGetFraction()on
Gst.GObject.Value,ValueViewandValueRef,SetFraction(Gst.Fraction)
onValueandValueRef, and
Gst.GObject.Object.SetProperty(string, object)andGetProperty<Gst.Fraction>
widened to carry it — writing a denominator of zero, or either term as
int.MinValue, is anArgumentOutOfRangeException, and GStreamer reduces and
sign-normalises what it stores. - The first fraction-typed property.
Gst.Audio.AudioAggregator.OutputBufferDurationFraction
is emitted through those accessors. - The child properties of a timeline element.
GES.TimelineElement.ListChildrenProperties()
answers an ownedGst.GObject.ParamSpec[]in the name order GES sorts, and the
caller disposes each element. - Three child-property slots to override.
GES.TimelineElement.OnListChildrenProperties,
OnLookupChild(string, out Gst.GObject.Object?, out Gst.GObject.ParamSpec?)and
OnSetChildProperty(Gst.GObject.Object, Gst.GObject.ParamSpec, Gst.GObject.ValueView)
with their chain-ups; aParamSpecreturned or passed on is consumed, a
borrowed one is call-scoped, and a true answer requires both outputs. - The query and fragment of a URI as tables.
Gst.Uri.GetQueryTable()and
GetMediaFragmentTable()answer aDictionary<string, string?>?snapshot —
a null value is a key with no=,""iskey=, no query or fragment is
null and a bare?or#is empty — andGst.Uri.SetQueryTable(IReadOnlyDictionary<string, string?>?)
copies one in, or clears the query when given null. - Every control binding of a track element.
GES.TrackElement.GetAllControlBindings()
answers aDictionary<string, Gst.ControlBinding>snapshot keyed by the name
passed toSetControlSource, whose values are interned and need no dispose. - The debug categories the library registered.
Gst.Global.DebugGetAllCategories()
answers anIReadOnlyList<Gst.DebugCategory>snapshot of borrowed categories
that live untilgst_deinit. - A format list that carries its own end.
Gst.Global.FormatsContains(ReadOnlySpan<Gst.Format>, Gst.Format)
searches one without the caller writing the terminator loop. - A control source sampled a run at a time.
Gst.ControlBinding.GetGValueArray(ClockTime timestamp, ClockTime interval, Span<Value> values)
andGst.Object.GetGValueArray(string propertyName, ClockTime, ClockTime, Span<Value>)
fill empty slots the caller supplies; what lands in them is the control
source's business. - A MIKEY timestamp whose length its kind decides.
Gst.Sdp.MIKEYPayload.TSet(MIKEYTSType, ReadOnlySpan<byte>)
andGst.Sdp.MIKEYMessage.AddT(MIKEYTSType, ReadOnlySpan<byte>)take exactly
the 8, 8 or 4 bytes the type calls for. - One VBI line at a time.
Gst.Video.VideoVBIEncoder.WriteLine(Span<byte>)
andGst.Video.VideoVBIParser.AddLine(ReadOnlySpan<byte>)want one line of
the stride the format and the pixel width ofNewfix, which the wrapper now
remembers. - A channel order a ring buffer will accept.
Gst.Audio.AudioRingBuffer.SetChannelPositions(ReadOnlySpan<Gst.Audio.AudioChannelPosition>)
checks the acquired state and the channel count, and may be called from inside
OnPrepare. - Both blocks of a DSD conversion measured.
Gst.Audio.AudioGlobal.DsdConvert(...)
validates the extent, the overlap and the whole-frame rule of the input and
output blocks before the call. - The credentials a message was challenged with.
Gst.Rtsp.RTSPMessage.ParseAuthCredentials(RTSPHeaderField)
answers anRTSPAuthCredential[]of the Basic and Digest credentials of a
WWW-Authenticate,Proxy-AuthenticateorAuthorizationheader, copied so
they outlive the message, and an absent header is an empty array. - The parameters of one credential.
Gst.Rtsp.RTSPAuthCredential.GetParams()
answers a freshRTSPAuthParam[]on each call, which the caller disposes. - The ICE candidates an agent gathered.
Gst.WebRTC.WebRTCICE.GetLocalCandidates(WebRTCICEStream)
andGetRemoteCandidates(WebRTCICEStream)answer aWebRTCICECandidateStats[]
snapshot whose elements belong to the caller. - The transports a session media keeps a slot for.
Gst.RtspServer.RTSPSessionMedia.GetTransports()
answers anRTSPStreamTransport?[]indexed by stream index, with null where
no SETUP has happened yet. - Six borrowed constant structures copied out.
Gst.FormatExtensions.GetDetails(Gst.Format),
Gst.Rtp.RTPPayloadInfo.ForName(string media, string encodingName)and
ForPt(byte),Gst.Sdp.MIKEYMessage.GetCsSrtp(uint)and
Gst.Sdp.MIKEYPayload.SpGetParam(uint)answer a nullable copy of the static
row the C points at, null for a value the library does not know, while
Gst.Video.VideoColorPrimariesExtensions.GetInfo(VideoColorPrimaries)is
non-nullable behind a guard;Gst.Sdp.MIKEYPayloadSPParam.Valreads the
counted block that record's C field points at. - A property-notify message built without losing the value.
Gst.Message.NewPropertyNotify(Gst.Object src, string propertyName, in Gst.GObject.Value? value)
hands the C ag_value_copyduplicate, so the caller'sValuestays alive and
stays the caller's to dispose and an object value keeps a reference of the
message's own;nullis a notification without a value and an emptyValueis
anArgumentException. - A poll descriptor and the wait over it.
Gst.GLib.PollFD(Fd,Events,
Revents),Gst.GLib.IOConditionand the static
Gst.GLib.PollFD.Poll(Span<PollFD>, int timeoutMs)projectGPollFDand
g_pollwith the descriptor width the platform decides — aHANDLEon 64-bit
Windows, a file descriptor elsewhere, checked by the ABI probe against the
running library; an empty set is anArgumentException, Windows waits on
handles, answersRevents = Eventsand stops at 64, and a Unix wait broken by
a signal resumes with the remaining timeout.Gst.PollFDisGstPollFDand
Gst.GLib.PollFDisGPollFD, so code that opens both namespaces qualifies
the two, as it already does forGst.DateTime. - The descriptor a bus signals through.
Gst.Bus.GetPollfd()gives a foreign
event loop the descriptor that reports a pending message; the descriptor
belongs to the bus and must not be read, written or closed, and a bus built
without asynchronous delivery throwsInvalidOperationException. - The read half of a poll set.
Gst.Poll.GetReadGpollfd()supplies the out
direction the gir omits;Gst.Pollitself is still not constructible from
managed code. - A variant handle and the discoverer round trip.
Gst.GLib.Variant
(Handle,TypeString,ToBytes(),FromBytes(string, Gst.GLib.Bytes),
Dispose) sinks the floatingGVariantthe C returns and refuses an
indefinite type string, andGst.Pbutils.DiscovererInfo.ToVariant(DiscovererSerializeFlags)
/static FromVariant(Gst.GLib.Variant)carry a discoverer result through
bytes —ToVariantrefuses a result that is neitherOknor
MissingPluginsor has no stream tree,FromVariantrefuses anything that is
not a"v", both of them ahead of a NULL dereference in the C.
Behavioural
Gst.Sdp.MIKEYMessage.NewFromData(ReadOnlySpan<byte>, Gst.Sdp.MIKEYDecryptInfo? info)takes a nullableinfoand is callable for the first time: the argument used to be non-nullable and every call threwArgumentNullExceptionbefore reaching the C, and an empty span is still a GLib critical and anInvalidOperationException.- Every generated method, extension and subclass chain-up now places its
GC.KeepAlivebarriers at the end of the body rather than before the output conversions, closing a latent window in which a wrapper could be finalized while a borrowed output array, string or list was still being read; no signature and no observable behaviour changed. - `G...
v1.28.9
Another gap-closing release. An appsink can be consumed as one queue of samples
and events rather than two, four mini objects the gir hides behind static
inline functions can be copied, a bus can be built without the asynchronous
half it may not need, the quark keyed data native code hangs off a mini object
can be read and stolen, and the RTSP surface gains header serialisation and the
TLS authentication mode of its authenticator. Some of what moved in the counts
is a correction rather than new surface: a hand written record no longer files
its members under the catch-all reason. Nothing that shipped changed behaviour.
All eighteen packages ship as one version, 1.28.9, generated from the GStreamer
1.28.6 .gir files, 16 commits after 1.28.8.
Added
- One pull loop for samples and events.
Gst.App.AppSink.PullObject()and
TryPullObject(Gst.ClockTime)answer aGst.MiniObject?, which is a
Gst.Samplefor a buffer or a buffer list and aGst.Eventfor every
serialized event but the end of the stream — stream-start, caps, segment,
tag, gap and a serialized custom event among them — so the caller matches on
the type it is handed. The queue read is the onePullSampleand
TryPullSampleread, and those two discard every event they are given, so a
consumer picks one of the two families and stays with it. The end of the
stream is never queued: a null answer always comes withIsEos()true, and
for the timed overload a null withIsEos()false is the timeout and nothing
else. The caller owns what it is handed and disposes it. The generator refuses
both on their shape — the return is aGstMiniObject*whose concrete type is
decided at run time — so the two members read theGTypeout of the first
word of the object and let the type registry build the wrapper. - The copy of an event, a sample, a buffer list and a query.
Gst.Event.Copy(),Gst.Sample.Copy(),Gst.BufferList.Copy()and
Gst.Query.Copy(). All four are static inline functions in the C headers and
the gir marks eachintrospectable="0", so no overlay can bring them back;
the library exports the four by name as well, but the one import of the
gst_mini_object_copythey all forward to serves every type, since the copy
carries the type of the original, and that call is why each of them answers a
nullable value — the C answers NULL for a type that installed no copy
function, which none of these four is. What a copy carries is what the C
copies: an event and a query take a copy of their structure, a sample
references the buffer, the caps and the buffer list of the original and
copies its segment and info structure, and a buffer list references its
buffers rather than copying them —CopyDeepis the other half of that one.
Each copy holds the only reference to itself and is therefore writable where
the original usually is not. - A bus without asynchronous delivery.
Gst.Bus.New(bool enableAsync).
enable-asyncis construct only and write only, so the only moment it can be
given a value is while the bus is being built, which is what this overload
does;New()is the same call with the property left at its default, true.
A bus built withfalseruns its synchronous handler and emits
sync-message, and then drops the message instead of queueing it, which is
the point of the option: an element that needs only its own synchronous
handler pays for no delivery machinery. Such a bus is for a synchronous
handler and the non-blocking pops, and for nothing else — the watches install
a source that never delivers,Pollsees nothing, andTimedPopand
TimedPopFilteredanswer null with a GLib critical for every timeout but
zero — and the binding can guard none of it, because a write-only property
cannot be read back. The remarks say so member by member. - The quark keyed data of a mini object.
Gst.MiniObject.GetQData(Gst.GLib.Quark)andStealQData(Gst.GLib.Quark)
answer annint, wherenint.Zeromeans nothing is stored and is an ordinary
answer rather than a failure. WhatGetQDatareads stays owned by the mini
object, together with the destroy notification that will release it; what
StealQDatatakes off the object belongs to the caller, and dropping it is a
leak, since nothing will run that notification any more.Gst.GLib.Quark.Zero
names no string and is refused with anArgumentException, which keeps the
programming error the C answers with a critical apart from the empty entry. - Two RTSP additions.
Gst.Rtsp.RTSPMessage.AppendHeaders(StringBuilder)
answers anRTSPResultand appends every header of the message as
Name: valuefollowed by CRLF, in the order the message holds them, leaving
what is already in the builder alone; the blank line that separates the
headers from the body is deliberately not written, so the call composes with
the request line before it. The C takes aGString, which has no place in a
managed signature, and the member owns that buffer for the length of the call.
Gst.RtspServer.RTSPAuth.GetTlsAuthenticationMode()and
SetTlsAuthenticationMode(Gst.Gio.TlsAuthenticationMode)are generated now
that the planner's runtime-enumeration map names
Gst.Gio.TlsAuthenticationMode, a hand-written mirror of
GTlsAuthenticationModein the runtime besideTlsCertificateFlagsand
SocketFamily, whoseNone,RequestedandRequiredare the same numbers
on every platform. Anything butNonemakes the server emit
accept-certificate, which then has to be handled.
Samples
samples/CustomMetais new: an API type registered with no tags, an
implementation over an unmanaged payload through theMeta.Register<T>of
1.28.8, one item attached to every frame pushed into anappsrcand read
back off the frames avideoconvertallocated on the other side;
samples/RtspServergains--mount <path>and--disable-rtcp, the last two
options oftest-launch.cit was missing;samples/GstPlaygains
--playlist <file>,--wait-on-eosand--flags <spec>, and its header
comment says why--gaplessand--instant-uristay out;
samples/tutorials/README.mdnow lists every basic and playback tutorial
that is not ported and why. CI gains aCustomMetastep on each of its three
sample-running legs, and theGstPlaystep of all three runs the new flags
and playlist path.docs/samples.mdis a new page of the documentation site: the sixteen
samples grouped by theme, the binding APIs each one exercises and what it
deliberately leaves out, with the README table as the short index into it.
Tooling
- The skip ledger stops mixing a decision with a shape. A member of a
hand-written record used to be filed under the catch-all
UnsupportedSignatureunless an overlay named it; it is now filed under
whatever the rules actually say about the symbol —NotIntrospectable, an
overlay skip, and so on — and only a member the rules would have let through
falls back to the catch-all, because it is generatable and still absent.
ShadowedByandMovedToare the two answers that must not be passed on,
since both claim the symbol is emitted under another declaration and of a
hand-written record nothing is emitted at all. Beside it,
gst_param_spec_fractionandgst_param_spec_arrayare filed as hand bound
rather than as an unsupported signature, and the overlay comment that
explained the skip is corrected: the gir does declare the fields and the two
functions, and generating the records unskipped would answer a public class
with no public member in it. - CI gates NativeAOT on the macOS leg as well, publishing and running
samples/AotSmokeforosx-arm64, so all three RIDs the binding claims are
covered.
Compatibility
This release is additive: every entry above is new surface, and nothing that
shipped changed behaviour, which is what the 1.28.x promise allows. The
binding is generated from the GStreamer 1.28.6 .gir files and the run-time
floor is still GStreamer 1.24 — neither moved.
The census counts moved for two reasons this time. Where new surface says they
should: GstApp counts no unsupported signature rather than 2 and 7 hand-bound
symbols rather than 5, the two appsink pulls; GstRtsp counts 12 unsupported
signatures rather than 13 and 3 hand-bound symbols rather than 2,
gst_rtsp_message_append_headers; GstRtspServer counts 14 unsupported
signatures rather than 16 and 384 emitted methods rather than 382, the two TLS
authentication mode accessors; and in Gst, Gst.Bus:enable-async,
gst_mini_object_copy and the two qdata calls leave UnsupportedSignature
while the four copy functions leave NotIntrospectable, all eight for the
hand-bound ledger. Where the ledger was simply wrong: gst_param_spec_fraction
and gst_param_spec_array were hand bound all along and are filed as such now,
and gst_mini_object_init, _steal, _weak_ref and _weak_unref move from
the catch-all UnsupportedSignature to NotIntrospectable, the reason that
really keeps them out. Net, in girs/skip-report.md, Gst counts 43
unsupported signatures rather than 53 and its HandBound section grows from 52
entries to 62, while its 112 symbols under NotIntrospectable are 112 before
and after: the four mini object internals arrived exactly as the four copy
functions left.
PackageValidationBaselineVersion in src/Directory.Build.props is 1.28.8, and
all eighteen packages are packed against it.
Changes
- Gate NativeAOT on the macOS leg
- Add a samples page to the documentation site
- List the tutorials that are not ported yet
- Port the playlist, wait-on-eos and flags options to the play sample
- Add mount and rtcp options to the RTSP server sample
- Add a custom meta sample
- Correct the remarks of the pulled object and copy members
- Copy an event, sample, buffer list or query through the mini object
- P...
v1.28.8
A gap-closing release. Managed code can author a buffer metadata implementation
of its own now, two attach functions the gir could not describe are bound by
hand, the frame and state fields a subclass writes in C have take-and-replace
setters, the GES asynchronous requests accept a GCancellable the caller
already holds and can run on the application's own GLib main context, and four
GES signals whose shapes the generator used to refuse are emitted. Nothing that
shipped changed behaviour. All eighteen packages ship as one version, 1.28.8,
generated from the GStreamer 1.28.6 .gir files, 33 commits after 1.28.7.
Added
- A metadata implementation written in C#.
Gst.Meta.Register<T>(api, impl, …)
registers an implementation whose item is theGstMetaheader followed by one
unmanagedT, with optional init, free, transform, serialize, deserialize and
clear delegates —Gst.MetaInitFunction,MetaFreeFunction,
MetaTransformFunction,MetaSerializeFunction,MetaDeserializeFunctionand
MetaClearFunction, mirroring the C callbacks including the serialisation,
deserialisation and clear ones that arrived in 1.24. The payload of such an
item is reached as aref Tthrough the item'sPayload<T>(), and
Gst.ByteArrayInterface.AppendData(ReadOnlySpan<byte>)is the sink a
serialize delegate writes through. A registration lives for the rest of the
process, and the delegates are rooted for that long.docs/ownership.md
gains an "Authoring a metadata implementation" section. - Two attach functions that were not introspectable.
Gst.Audio.AudioGlobal.BufferAddAudioDownmixMetatakes the downmix matrix as
one row-majorReadOnlySpan<float>and builds the row table the C function
wants; a shared buffer throwsInvalidOperationException.
Gst.Video.VideoGlobal.BufferAddVideoGLTextureUploadMetaattaches a managed
upload function, the newGst.Video.VideoGLTextureUploaddelegate, which stays
reachable for as long as any buffer carries the item — every copy takes a
handle of its own to the same delegate; the upload is invoked through
VideoGLTextureUploadMeta.Upload(ReadOnlySpan<uint>), and a shared buffer
answersnullthe wayBufferAddVideoMetadoes. - Take-and-replace setters for the fields a subclass owns.
Gst.Base.BaseParseFrame.SetBuffer(Buffer?),
Gst.Video.VideoCodecFrame.SetInputBuffer(Buffer?)andSetOutputBuffer(Buffer?),
Gst.Video.VideoCodecState.SetCaps(Caps?)andSetAllocationCaps(Caps?)— the
same shape as the shippedBaseParseFrame.SetOutBuffer: the wrapper's reference
is taken over, the replaced value is released, andnullclears the field. - A
GCancellablethe caller already holds.GES.Asset.RequestAsyncand
GES.UriClipAsset.NewAsyncgained overloads taking aGst.Gio.Cancellable.
The binding borrows it — it never cancels, resets or disposes it — and a
cancelled result surfaces as a canceledTaskcarrying no token. GstSharp.GioAsyncContext. A staticMainContext?, null by default. An
application that runs its own GLib main loop can host the binding's
asynchronous Gio operations on that context instead of on the binding's
background thread: the application iterates the context, the value is read once
when an operation starts, and the binding pushes the context thread-default
around the native_asynccall so the callback lands on it.docs/gio-async.md
describes it.- Four GES signals.
GES.Layer.ActiveChanged(Active, andTracksas a
GES.Track[]snapshot),GES.Timeline.GroupRemoved(Group, andChildrenas
aGES.Container[]snapshot),GES.Timeline.SelectTracksForObject— whose
handler answersGES.Track[]?, wherenulland an empty array both mean the
element joins no track, and connecting a handler replaces the timeline's default
track selection — and thenotify-metasignal ofGESMetaContainer, connected
throughGES.MetaContainerExtensions.AddNotifyMetaHandlerand
RemoveNotifyMetaHandleronGES.IMetaContainer, whose arguments carryKey,
HasValueandValue, aValueViewlent for the emission only; reading it once
the emission has ended throwsInvalidOperationException. The generator rules
behind them are narrow — aGPtrArrayof GObject-class elements, and a const
GValueargument on a signal — anddocs/ownership.mdstates the ownership of
the lent value and of the answered array.
Samples
samples/GstPlaygainedgst-play-1.0's--shuffleoption and itsdkey,
which flips the playback direction. Trick modes stay out becauseGstPlay
offers no seek-flag API; the header comment says so.samples/GesLaunchports the last fourges-launch-1.0branches:
--container-profile, which re-parents the encoding profile under a bare muxer
profile;--profile-from <clip name>, which rebuilds every track from the named
URI clip's discoverer streams; the profile pick--smart-renderingmakes when
no--formatis given, taken from the URI-clip assets and faithfully the
least common qualifying one, because the C tool sorts its candidates
ascending; and--embed-nesteds, which embeds nested.xges
projects into the saved project. The sample prints which path chose the encoding
profile and how many nested projects were embedded, and four Linux-only CI steps
exercise them.
Tooling
- The generator no longer counts what it refuses to ship: a slot rejected by
GEN0040(a return type collision) orGEN0047(a borrowed return with no
documented owner) is left out of the census as well as out of the file. The
generator also takes--report-dir, which defaults to the--gir-dir
directory; a run that writes its sources elsewhere passes it as well, so that
the committedgirs/skip-report.mdis left alone.
Compatibility
This release is additive: every entry above is new surface, and nothing that
shipped changed behaviour, which is what the 1.28.x promise allows. The
binding is generated from the GStreamer 1.28.6 .gir files and the run-time
floor is still GStreamer 1.24 — neither moved. The census counts did move,
and only where the new surface says they should: the four GES signals leave
UnsupportedSignature and are counted as emitted, so GES counts 39 signals
rather than 35 and 8 unsupported signatures rather than 12; and six symbols move
into the hand-bound ledger from the reasons that used to keep them out — the
three registration entry points behind Gst.Meta.Register<T>, the
gst_buffer_iterate_meta_filtered that Gst.Buffer.IterateMeta already
implemented in managed code, and the two attach functions above.
PackageValidationBaselineVersion in src/Directory.Build.props is 1.28.7, and
GstSharp.Net.RtspServer no longer opts out of the gate: with the package now
published, all eighteen are packed against a baseline on nuget.org rather than
seventeen of them.
Source note. A literal default in the cancellation position of
GES.Asset.RequestAsync or GES.UriClipAsset.NewAsync is now ambiguous
(CS0121) between CancellationToken and Gst.Gio.Cancellable. Omit the
argument, or write default(CancellationToken).
Changes
- Gate the smart rendering step on the profile pick alone
- Gate the nested project embedding on the Linux leg
- Give the rebuilt tracks back to the timeline that owns them
- Gate the three new profile paths of GesLaunch on Linux
- Embed the nested timelines into the saved project
- Pick the smart rendering profile out of the clips of the timeline
- Build the tracks and the profile from a named clip
- Re-parent the encoding profile into --container-profile
- State the ownership of the ungroup call the test makes
- Say what the pointer array rules and the ungroup call really do
- Cover the four GES container and value signals against the library
- Show a signal handler the GValue an emission lends it
- Let a signal handler answer with a GPtrArray of objects
- Read a GPtrArray signal argument out into an array of wrappers
- Port shuffle and the direction key to the GstPlay sample
- Take the reference on a borrowed GCancellable where it is released
- Let an application host the Gio async operations on its own loop
- Let a caller bring the GCancellable an async request watches
- Let subclasses write the frame and state fields they own
- Take back only the entry the refused registration filed
- Close the two windows a metadata registration leaves open
- Say what each refusal of a metadata registration means
- File the metadata authorship entry points under HandBound
- Let managed code author a fixed-layout metadata implementation
- Say which half of the upload pair leaks and which double-frees
- Say what the copy of a texture upload item takes with it
- Attach a managed OpenGL texture upload to a buffer
- Attach a downmix matrix to a buffer from managed code
- Name the report-dir test and the CONTRIBUTING sentence for what they check
- Let a run say where the skip report belongs
- Leave a refused slot out of the count as well as the file
- Validate the RTSP server package against its published surface
- Move the version and the validation baseline to 1.28.7
Full Changelog: v1.28.7...v1.28.8
v1.28.7
The subclassing release: a GStreamer element written in C# is a first-class element now. Twenty eight classes are subclassable — Gst.Element and Gst.Bin, Gst.Pad, the GstBase sources, sinks, transforms, parsers and aggregators, the GstAudio and GstVideo decoders, encoders, filters and sinks, and seven GES timeline classes — with thirty class struct mirrors and 242 vfunc slots, every mirror probed against the running library. The surface is generated, not hand written, which is what made it possible to go from the six hand-bound bases of the previous release to twenty eight in one cycle. An instance the library creates reaches managed code as its managed type, so ElementFactory.Make of a registered type, a pad built from a template and a pad an aggregator requests all dispatch their vfuncs; a managed type installs GObject properties, defines signals and implements GstURIHandler; and a lent GstSegment, GstVideoInfo or GstVideoCodecFrame is wrapped without a copy and invalidated when the call returns. Beside it, an eighteenth package ships for the first time — GstSharp.Net.RtspServer, libgstrtspserver-1.0 — and the introspection half of GObject arrives: the GParamSpec subclasses with their ranges and defaults, the enumeration, flags and interface tables of a GType, and Object.As<T>(). Three samples are new, RtspServer, GesCustomSource and GesLaunch, the last a port of ges-launch-1.0; samples/GstInspect prints every section the real gst-inspect-1.0 prints and CI diffs the two; and benches/ holds a BenchmarkDotNet harness for the dispatch, property and mapping paths. All eighteen packages ship as one version, 1.28.7, generated from the GStreamer 1.28 girs, 249 commits after 1.28.6.
Highlights
- Managed subclasses, generated. Every subclassable class gets its subclassing surface emitted by the generator rather than written by hand. Nineteen of them are the core and base-library classes —
Gst.Element,Gst.Bin, GstBaseBaseSrc,PushSrc,BaseSink,BaseTransform,AggregatorandBaseParse, GstAudioAudioBaseSink,AudioBaseSrc,AudioSink,AudioSrc,AudioFilter,AudioDecoderandAudioEncoder, GstVideoVideoSink,VideoFilter,VideoDecoderandVideoEncoder. Each class struct is mirrored, and every mirror's layout is checked against the running library by the integration tests, so a slot patched at the wrong offset fails a test run rather than a pipeline. More than twenty gir annotation corrections were needed to make the slots come out right. A lent boxed record — aGstSegment, anAudioInfoorVideoInfo, aBaseParseFrame, aVideoCodecFrameorVideoCodecState— is wrapped around the address the caller owns instead of copied, and detached when the call returns, which is also what binds six slots the first wave had to skip (BaseSrc::do_seekand::prepare_seek_segment,BaseTransform::filter_meta,AudioFilter::setup,VideoFilterandVideoSink::set_info);VideoCodecFrame.Copy()andVideoCodecState.Copy()are there for a handler that needs one past the call.AudioDecoder.OnPrePush(ref Buffer?)and its encoder sibling take the inout parameter whose ownership transfers in both directions.handle_frameis required atDefineSubclasstime for the parser and the four codec classes, the way a pad template is. - Native-initiated construction.
IManagedSubclass<TSelf>.CreateWrapperand a genericDefineSubclass<TSelf>overload let an instance GStreamer creates arrive as its managed type with its vfuncs dispatched: an element made throughElementFactory.Make, a pad built from aPadTemplate.NewWithGtypetemplate, a pad an aggregator requests.SubclassType.NewInstance(IReadOnlyDictionary<string, object?>)passes construct-only properties, which is what aGstPadneeds for itsdirection.Gst.PadandGstBase.AggregatorPadare subclassable in their own right —OnLinked,OnUnlinked,OnFlush,OnSkipBuffer— andAggregator.OnCreateNewPadis bound. A type defined with the non-genericDefineSubclasskeeps the ancestor-wrapper behaviour it had.docs/subclassing.md§5.4 states the fabrication rules: never sink, one winner per handle, and aCreateWrapperthat runs on a streaming thread and must do nothing but forward its arguments. - Properties, signals and
GstURIHandler.ObjectClassConfig.InstallPropertywithOnSetPropertyandOnGetPropertyinstalls a GObject property on a managed type,Object.Notify(ParamSpec)emits its notification, andObject.SetPropertyOverride/GetPropertyOverridereach a property a base class owns.AddSignaldefines a signal with a class handler and an accumulator,SignalAccumulator.TrueHandledorFirstWins. ADefineSubclass<TSelf>(…, SubclassOptions, …)overload on every subclassable class adds interfaces,GstURIHandlerfirst, throughURIHandlerImplementation.For<TSelf>().CONSTRUCTandCONSTRUCT_ONLYare refused by design: GObject dispatches the write to the class that owns the property while the instance is still being built, before the wrapper that would serve it exists. TwentyParamSpecX.Newfactories cover the GObject kinds, plusParamSpecFractionandParamSpecArray, and five more ParamSpec classes are bound — boxed, object, pointer, param and variant. - GES subclassing.
GES.TimelineElement,TrackElement,Source,VideoSource,AudioSource,ClipandSourceClipjoin the subclassable set, withOnCreateSource,OnCreateTrackElement,OnSetParentand the timeline-element setters; class struct mirrors lay out<union>members for it, andAsset.Extract<T>()is hand bound, because that is the child contract a managedOnCreateTrackElementhas to satisfy. Acreate_sourceorcreate_elementoverride that answers null or throws gets anidentityelement substituted and the failure reported: GES 1.28.6 would otherwise free the track element's nleobject while it is still referenced. GstSharp.Net.RtspServer— GstRtspServer. The eighteenth package binds libgstrtspserver-1.0: 18 classes and 41 signals, including the 21 request signals ofRTSPClientand the eight socket members ofRTSPStream.RTSPMountPoints.AddFactoryis hand written so the factory wrapper and its handlers stay alive as long as the mount point holds them, andRTSPServer.Detachis hand written too.samples/RtspServeris a port of the upstreamtest-launch.c.docs/ownership.mdgains an "RTSP server" section with the lock, thread and shutdown rules. Skipped on purpose: theset_send_funcpair, which asserts on every reachable path, andRTSPThreadPool.GetThreadandRTSPThread.New, which leak a thread without a stop.GParamSpecandGTypeintrospection, andObject.As<T>(). The ParamSpec subclasses are bound —ParamSpecIntthroughParamSpecGTypeinGst.GObject, plusGst.ParamSpecFractionandParamSpecArray— withMinimum,Maximum,Default,Epsilon,ValuesandIsAType, andParamSpecitself gainsNick,Blurb,OwnerType,NativeType,DefaultValueas a borrowedValueView,RedirectTargetandFromNative.GType.GetEnumValues,GetFlagsValues,GetInterfacesandIsInterfaceread the tables aGTypecarries,SignalQuery.List(GType)lists its signals,Gst.Object.FlagsandIsFlagSetread the object's own flags, andObject.As<T>()casts to an interface through a generated adapter — the module that declares the interface has to be initialised first.ModuleInterfaceEntryand a three-argumentNativeModulecarry that registration.- Runtime additions.
Gst.GObject.Object.ListProperties(GType)lists the properties of a class without an instance, which is whatgst-inspect-1.0prints as "Pad Properties".Discoverer.TryDiscoverUri(uri, out GException? error)hands back theDiscovererInfotogether with the error, so a missing-plugin result keeps its installer details;DiscoverUristill throws.Gst.GLib.UserDirectories.CacheDirreadsg_get_user_cache_dir. - Pad functions. A pad's chain, chain-list, get-range, event, event-full, query, iterate-internal-links, link, unlink, activate and activate-mode functions can be set from C# —
Pad.SetChainFunctionthroughSetActivateModeFunction, keyed by the pad instance, withnullunsetting — andCollectPads.SetBufferFunctionandSetClipFunctionbeside them. - Samples and tools.
samples/GstInspectis a full port: properties with their ranges and their enumeration and flags tables, element flags, clocking, implemented interfaces, URI protocols, pads with theirType:and pad-property blocks, signals and action signals, children, presets and typefind extensions. CI diffs the page against the realgst-inspect-1.0on every leg whose tool is new enough.samples/GesLaunchportsges-launch-1.0— theges:timeline grammar, project load and save, rendering, preview sinks, track options,--list-transitionsand the interactive keyboard — and shows the one thing the C tool hides inside its GMainLoop:GESProject::loadedis always deferred to the default GMainContext, so a loop-less application pumpsGst.GLib.MainContext.Default.Iteration(false)on the extracting thread untilLoadedorErrorLoading.samples/GesCustomSourceis a managed GES source end to end.samples/GstDiscovererprints installer details, strips arrays of buffers out of its caps and answers--print-cache-dir;samples/GstTranscoderuns on the Linux CI leg. Every sample and the integration tests build with the analyzers applied, so a sample that uses the binding the way a rule forbids fails the build. benches/GstSharp.Benchmarks. A BenchmarkDotNet harness for the four paths that carry the cost — trampoline dispatch against a nativeidentity, property get and set with their boxing,Buffer.Mapas a span against a copy, and interned wrapper lookup. Every CI job builds it and none of them runs it. The numbers in `benches/README...
v1.28.6
The RTP release: a seventeenth package ships for the first time — GstSharp.Net.Rtp, the RTP and RTCP buffer helpers of gst-plugins-base — and the planner stops handing a number to a C function that dereferences a pointer. A gir parameter or return whose type is a plain scalar and whose C type carries a star was projected by value: an address truncated to the scalar's width coming out, and the value itself where an address was meant going in. That shape is refused now, on a member, on a callback argument and on a signal argument alike, and the two published members that projected it are hand written with signatures that do what the C does. The other half of the same reading — the direction an overlay corrects, taken after the correction — is what lets an out parameter reach a pointer to a scalar and to an enumeration, and that is what unlocks the eighteen RTCP extended report readers of the new module. Beside the new package, the fifteen field accessors the last release parked are decided, and a video time code hands out the configuration it embeds. All seventeen packages ship as one version, 1.28.6, generated from the GStreamer 1.28 girs. The callable ledger goes from 861 entries to 910: forty-seven are the new module's own section, and the remaining two are the members that moved into hand-written code and stay listed as skipped. The field ledger falls from 163 entries to 147, and the section of the fields another member already answers grows from 14 to 18.
Highlights
GstSharp.Net.Rtp— GstRtp. The RTP and RTCP buffer helpers of gst-plugins-base, the payloader and depayloader base classes and the header extension API, which is what drives the payloader and depayloader elements the RTP plugins ship and the header extensions built into the library. Authoring a payloader of your own means subclassingGstRTPBasePayload, which the binding does not ship: the module binds the classes as they are used from the outside, not as they are derived from. The gir needs eleven corrections to come out right — the RTCP map, first-packet and add-packet structures are outputs, three returns answer NULL on ordinary input, the digit-led payload name becomesRTPPayload.Fs1016, andgst_rtp_buffer_mapandgst_rtcp_buffer_mapbecomeMapBufferwhilegst_rtcp_packet_get_typebecomesGetPacketType, because the names they derive are taken by a field and byobject.GetType, and the eighteen extended report readers whose out parameters are typed as bare scalars are skipped until the planner can redirect one — which is the correction the next bullet lifts. Eight members are bound by hand: the FCI and APP data spans, the four byte APP name, the source meta whose SSRC pointer the generator would have passed by value, and the two NTP setters of a later bullet.- The RTP mapped structures are not scopes.
RTPBufferandRTCPBufferare the plain structures the C API declares, and the binding hands them out as they are: declare one as a local, map it once withMapBuffer, unmap it exactly once, and never copy it, store it in a field or capture it in a lambda — the internalensure_buffersofgstrtpbuffer.cunmaps and remaps through the very structure it is handed, so a call made on a copy unmaps a second time. TheGst.Bufferwrapper the mapping came from has to stay reachable until after the unmap, because the library stores the raw pointer and takes no reference of it; reachability ends at the last use and not at the end of a variable's scope, so a buffer obtained inline as the argument ofMapBufferis finalizable the moment that call returns. AnRTCPPacketborrows the address of theRTCPBufferit was taken from and is usable only while that variable lives. A header accessor called on anRTPBufferthat was never mapped crashes the process — the C side dereferencesrtp->data[0]without a guard — while the RTCP half is guarded throughout.docs/ownership.mdstates all of it under "RTP mapped structures". - The RTCP XR report block readers — 18 members,
RTCPPacket.XrGetDlrrBlockthroughXrGetVoipSignalMetrics, with 51 out parameters between them. The gir types every one of those parameters as a bare scalar with the star in thec:typealone and no direction, so adirection: outcorrection could not reach them: the out and ref arm of the override stopped at a plain structure and at aGValue. It reaches a blittable scalar and a boolean whose C type is a pointer now — the out projection of aguint32or agbooleanis a local of exactly the width the C declaration names, and its address is what the callee was handed all along — and, separately, an enumeration and a bitfield of the same shape, which is whatRTCPPacket.SdesGetEntryandSdesCopyEntryneeded for theGstRTCPSDESType *they write through (gstrtcpbuffer.c:1539-1540). The star of thec:typeis the evidence that the C side writes through the parameter, so a scalar whosec:typecarries none keeps the value projection it had. GstRtp.Initialize(). The new module gets the public entry class every other binding assembly already has, so an application names it besideGstApp.Initialize()rather than relying on the firstRTPBuffermapping to bring the library up.- The NTP header extension setters take a
Span<byte>. The gir spells the data ofgst_rtp_hdrext_set_ntp_56and_64as a baregpointerwith no array element, while the matching getters carry an array length, so the generated setters handed the block out as a rawnintbeside a size the caller had to keep honest. Both are hand written overSpan<byte>instead, and the seven or eight byte length the extension needs is checked before the call, so a short span answersfalseand leaves the block untouched rather than reaching the critical the C logs. A sweep of every gir finds this shape nowhere else in GStreamer. samples/RtpPacketDump— the RTP module end to end. Anaudiotestsrcruns throughrtpL16payinto anappsink; every packet is read through a mappedRTPBuffer— sequence number, timestamp, SSRC, payload type, marker and payload length — and then a compound RTCP packet of a sender report and an SDES item is built throughRTCPBufferandRTCPPacket, unmapped, mapped again and walked back. Each mapping is released in afinally, so a console write that throws cannot leave one alive into the disposal of its buffer. It is headless, and the three CI legs with a native installation run it.- Field accessors the previous release held back — 15 members. Eight are pointers whose consumer contract the C sources do state, so the borrowed read is safe where the C makes it:
Buffer.Pool,AudioRingBufferSpec.GetCaps(),BaseParseFrame.GetBuffer()and.GetOutBuffer(),VideoCodecFrame.GetInputBuffer()and.GetOutputBuffer(),VideoCodecState.GetCaps()and.GetAllocationCaps(). Three are embedded records the wave that landed that rule left behind:AudioRingBufferSpec.GetInfo(),CollectData.GetSegment()andVideoTimeCodeMeta.GetTc(). Two are metadata infos whose derived name is taken by a static member that shipped, so the overlays name them after the type they hand out instead:AudioMeta.GetAudioInfo()andProtectionMeta.GetStructure(). And two are pointers to a plain structure, a shape with no wrapper to project — the address is the library's own storage and a NULL pointer is the structure saying it carries none — so they are copied out as nullable values:VideoCodecState.ContentLightLeveland.MasteringDisplayInfo. Two more fields left the ledger for the section another member answers,CollectData.bufferthroughCollectPads.PeekandAudioBuffer.infothrough the hand-written mapping scope, and nine stay unbound for reasons the entries now state one by one rather than for one reason that held for none of them. The window a read of one of these has to happen in is a place as much as a time, sodocs/ownership.mdsays which call, which thread and which lock each of them names, and five integration tests read each accessor where the C contract puts the read. VideoTimeCodereads the configuration it embeds.GstVideoTimeCodecarries itsGstVideoTimeCodeConfigby value and the configuration wrapper has no public producer, so the frame rate, the flags and the daily jam of a time code were unreachable from C#.FpsN,FpsDandFlagsare copies read through the handle of the time code;GetLatestDailyJam()comes back owning a reference of its own, which is why it is a method the caller disposes — the instant stays valid after the time code is gone — and whynullsays the time code carries none.- A field newer than the support floor carries no accessor. The support floor is GStreamer 1.24, and a structure a 1.24 library allocates is not long enough to hold a field that arrived later; there is nothing for a field access to fail on, so no accessor is emitted at all and the ledger line says which version put the field there. The rule covers fixed size inline storage as well now, which used to be answered by a branch that ran ahead of the check. No shipped accessor changes: no fixed size field of any gir is newer than the floor, which the generator verify run confirms.
- Ledger and diagnostics. A direction, or any other non-nullable override, written on a callback or signal parameter key used to be consumed silently before it could take effect; it is reported as GEN0017 now, and the committed overlays produce none. The entry classes no longer let the claim that the registry sweep covers an application which never names them stand on its own: the sweep reaches assemblies rather than wrappers, and a wrapper built before an assembly is loaded keeps the type it was given. And the remarks of the obsolete override say which member an entry must not name, because a generated property delegates to its accessors, so an entry keyed on one of those turns the property body into a call to an o...
v1.28.5
The record field release: a sixteenth package ships for the first time — GstSharp.Net.Play, the high level playback API of gst-plugins-bad — and wave E turns the fields of a record into API. A string field, a field that points at something the bindings wrap, a record another record embeds by value and the members of the reserved ABI union at the end of a released structure all reach the surface as get-only accessors, so a hundred-odd values that carried API in C and nothing in C# can be read. All sixteen packages ship as one version, 1.28.5, generated from the GStreamer 1.28 girs. The field ledger falls from 267 entries to 163, with fourteen more moved to a section of the fields another member already answers; the callable ledger goes from 817 to 861, and the whole increase of forty-four is the new module's own section.
Highlights
GstSharp.Net.Play— GstPlay. A URI, the play controls around it, its media information and its message bus. Upstream marks the library API unstable (docs/libs/play/index.mdin the GStreamer monorepo). The 1.24 floor carries only the index-based track selection API, which the generator marks[Obsolete]because upstream deprecated it in 1.26 in favour of the track-id calls.Play.Start()isgst_play_play, renamed becausePlay.Playis not a legal C# member name and playback could not be started at all without it.- What the Play module hand writes. The entry points the planner refuses, or that the C source shows to be unsafe as generated, are written by hand with the same signatures:
Play(IPlayVideoRenderer?)mints the extra referencegst_play_newconsumes, so the renderer stays usable;Play.SetConfigfrees the copy the C function leaks whenever the play is not stopped;PlayMessageExtensions.ParseError/ParseWarningtake aGError **beside aGstStructure **, a shape the planner does not bind, andParseErrorMissingPlugin/ParseWarningMissingPlugincheck the message kind first, where the imported pair reads an uninitialised one for a foreign message;PlaySignalAdapter.New/NewWithMainContext/NewSyncEmitkeep the play alive, because the C adapter stores it without referencing it, andPlaySignalAdapter.GetPlayanswersObjectDisposedExceptionafterwards;Play.GetVisualizations()walks the NULL-terminated array and frees it once; and the twoPlayVideoOverlayVideoRendererconstructors route a null sink to the constructor that does not touch the video-sink property.Play.Disposesets the API bus flushing, which breaks the reference cycle the C documentation describes — dispose everyPlaySignalAdapterof a play before the play itself. samples/GstPlay— the gst-play-1.0 user experience on thePlayobject: a playlist, the keyboard controls with the volume and rate pairs moved apart,--volume,--audiosink/--videosinkwritten onto the pipelineGetPipelineanswers,--visualizationbeside--list-visualizations, and a media info summary, all driven by a timed pop of the API bus rather than by a main loop or a signal adapter. Headless is the default: nothing reads the keyboard without--interactive, and--durationbounds the run, which is what lets CI gate it.- String fields of a record — 61 get-only properties. A
const gchar*field is copied out of the structure on every read, because the storage belongs to the C structure and may be freed or replaced with it. No gir spells nullable on a field, so an accessor isstring?unless the overlays correct it against the C. The session description strings and most of the RTSP and WebRTC ones stay nullable; the nineteen the overlays correct arePluginDesc.Name,.Description,.License,.Origin,.Package,.Sourceand.Version,RTSPUrl.Abspathand.Host,RTSPAuthParam.Nameand.Value,StaticPadTemplate.NameTemplate,AudioFormatInfo.Nameand.Description,VideoFormatInfo.Nameand.Description,FormatDefinition.Nickand.Description, andWebRTCICECandidateStats.Proto. - Handle fields of a record — 20 accessors. A field that points at something the bindings wrap is projected through the very expression a transfer-none return of the same type gets, so a field and a getter of it never hand out two different things. The flavour of the wrapper decides the form: a GObject is interned and an opaque wrapper owns nothing, so both are properties —
Memory.Allocator,CollectData.Collectand.Pad— while a mini object is referenced and a boxed value copied, so both are read through aGetmethod the caller disposes:Memory.GetParent(),MapInfo.GetMemory(),VideoMeta.GetBuffer(),VideoCodecState.GetCodecData(),VideoMetaTransform.GetInInfo()/.GetOutInfo()and the same pair onVideoMetaTransformMatrix,WebRTCICECandidatePair.GetLocal()/.GetRemote(),WebRTCICECandidate.GetStats()and the rest. A pointer to a pointer is refused the way the planner refuses an argument of that shape, and a pointer the library replaces or clears while a consumer holds the structure is held back, because the reference the projection takes would be taken after the read. - Embedded records — 8 accessors for a record another record holds by value: the four port ranges of a transport,
RTSPTransport.Interleaved,.Port,.ClientPortand.ServerPort;VideoInfo.Colorimetry;VideoMeta.Alignment; and the embedded video infosVideoCodecState.GetInfo()andVideoInfoDmaDrm.GetVinfo()answer. A plain structure is copied out by the assignment and read as a property; a boxed value is wrapped from the address the field sits at through the transfer-none projection, which copies it, so the caller disposes what a read produced and the member is aGetmethod. An embedded record whose wrapper is a bare pointer holder is refused: it would borrow what the declaring record owns and dangle with it. - The reserved ABI union is laid out. GStreamer grows a released structure by spending the pointers it reserved at its end, declaring the padding array and the new fields as the two members of a union. The mirrors stopped in front of every union, so six records ended as a prefix shorter than the C structure and three more that embed one of them by value were not laid out at all. That shape is now recognised and laid out as the pointers it reserves, and the accessor is named from the member alone:
VideoInfo.MultiviewMode,.MultiviewFlagsand.FieldOrder,AudioRingBufferSpec.DsdFormat,CollectData.Dts,WebRTCICECandidateStats.RelatedPortand.TcpType,VideoInfoDmaDrm.DrmFourccand.DrmModifier, andVideoFrame.Flagsand.Id.PadProbeInfo,AudioRingBufferSpec,CollectData,VideoCodecFrame,VideoInfo,WebRTCICECandidateStats,VideoFrame,VideoInfoDmaDrmandVideoCodecStateall reach the size of the C structure; onlyRTSPMessageis a prefix mirror now. The ABI probes are the gate, extended and added against the 1.28.5 headers. AudioClippingMeta.Formatis aGst.Format. An enumeration another generated module declares is one an accessor can hand out after all: the mirror keeps the underlying integer, because a mirror is interop storage and the integer is what crosses, and the accessor casts it — the same thing every signature naming a type of another module already relies on.- Ledger and diagnostics. A record field is not a callable and has no
c:identifier, so the overlays gain a vocabulary of their own:fieldSkips, keyed by thec:typeof the record and the gir name of the field, states either the generated member that answers the same value or that a hand-written one does — exactly one of the two — and moves the field into a new## Fields exposed elsewheresection;fieldAnnotationscarries thenullable: falseandaccessor: falsecorrections, every entry citing the C file and line the claim rests on. A stale or self-contradictory entry is reported as GEN0025 and GEN0026, and both codes join GEN0020, GEN0023 and GEN0024 among the five the committed overlays are held to producing none of. The field ledger stops filing eleven fields underOtherand names the cause instead —HandWritten,NoLayout,CrossNamespaceEnum— and reports a laid-out union member by member rather than as one line. - Documentation and coverage.
docs/ownership.mdstates the three contracts of a field accessor — a string copied on read, a wrapper projected the way a transfer-none return of the same type is, and an embedded record copied out whole — and says when a read has to happen for a structure the library only fills for the length of one call.samples/PlaybinPlayerwas the one sample no job ran and now runs headless on the three legs that have a native installation, beside the gst-play port.
Behavioural notes
One shipped member is marked obsolete; nothing else that shipped changed. IRTSPExtension.GetTransports(RTSPLowerTrans, string) gains [Obsolete] in favour of the new RTSPResult GetTransports(this IRTSPExtension ext, RTSPLowerTrans protocols, out string? transport) extension method, which is what the C function actually does (it writes the transport string back through a gchar **). It reads the string the extension wrote, frees it, and answers null when no extension named a transport, which is also what an extension that does not implement the call leaves behind. The old member's signature and behaviour are unchanged. No other member of the fifteen packages of 1.28.4 changes its signature or its behaviour in this release. Everything else above is new surface.
How that is checked. The two generator changes that refuse a pointer-to-handle-pointer parameter — in a call, and in a callback or signal — leave the generated tree and the skip report byte-identical: no shipped gir has an introspectable instance of the first shape, and every instance of the second was already refused ahead of it. Adding the Play module changes no other module's generated output, an...
v1.28.4
The module release: three new packages ship for the first time — GstSharp.Net.Tag, GstSharp.Net.Allocators and GstSharp.Net.Transcoder — which takes the set to fifteen, and the API reference is published as a site. Beside them, wave D closes more families of the skip ledger (the mutators that consume their instance, the GValue a callback is handed, the fixed-size fields of a record, the GParamSpec callables) and the G4 batch fixes the callback-state leaks the clock and custom-meta entry points were hiding. The ledger goes from 848 entries to 817 even with the entries the new modules bring, and a new ## Fields section counts the 267 record fields no accessor reaches yet.
Highlights
GstSharp.Net.Tag— the GstTag library. The whole gir: the two abstract element bases that tag demuxers and muxers derive from, the XMP writer interface, and the forty functions that turn an ID3v1 record, a Vorbis comment, an XMP packet or an EXIF IFD into a tag list and back. Five returns the gir does not mark nullable are corrected against the C, because NULL is an ordinary answer there and not an error.GstSharp.Net.Allocators— the fd, dmabuf, shm and DRM dumb allocators. The module registers its five allocator classes; the 1.28-only udmabuf type is left out of the registry on older installations, and the two DRM dumb returns that are NULL on every build without libdrm carry a nullable annotation overlay.GstSharp.Net.Transcoder— GstTranscoder.Transcoder.Runis a one-shot per instance and has to be called from a thread that has no thread-default main context — that is, one that uses the global default: build a newTranscoderfor every job, and do not mixRunandRunAsyncon one instance.RunAsyncreports its two synchronous failures — no encoding profile, and a state change the pipeline refused — on the calling thread before it returns, and every other message arrives on the internal worker thread of the transcoder; the recommended route isRunAsynctogether with a poll ofGetMessageBus, which is whatsamples/GstTranscodedoes.TranscoderMessageExtensions.ParseError/ParseWarningare hand written so that a message without issue details answersnull, where the imported pair aborts the process.- API reference site — the API reference extracted from the XML documentation of the packable projects, together with the guides under
docs/, is published with DocFX at https://masa-iwm.github.io/GstSharp.Net/, deployed frommain. - Mutators that consume their instance — the twenty-one callables whose instance is transfer-full are bound. A
_make_writableadopts in place and returns the wrapper it was called on, socaps.MakeWritable()reads as the C idiom it stands for; everything else mints the reference the call takes over and answers a second wrapper, leaving the receiver untouched. - GValue arguments of a callback — a
const GValue*argument arrives asGst.GObject.ValueViewand a writable one asGst.GObject.ValueRef, bothref structs over storage somebody else owns. That unlocks the three structure walks, their_id_strtwins and the two iterator walks. - Fixed-size record fields — 18 get-only properties over GstVideo, GstAudio and GstSdp, the plane stride and offset arrays of
VideoInfoand the per-component tables ofVideoFormatInfoamong them, with hand-writtenFormatandFormatInfoonVideoInfoandAudioInfobeside them. The skip report gains a## Fieldssection that counts the 267 public fields no accessor answers yet. GParamSpeccallables — a parameter specification reaches a generated signature in both directions, which unlocks eight callables:gst_child_proxy_lookup,gst_object_default_deep_notify,gst_value_deserialize_with_pspecand the five child-property members ofGESTimelineElement.- Element factories that carry their properties —
ElementFactory.MakeWithPropertiesand.CreateWithPropertiesask the class of the loaded factory what type each named value has to hold, write a construct-only property rather than refusing it, and answer anArgumentExceptionfor an unknown or unwritable name instead of the console message GLib prints. GLib.Date↔System.DateOnly— a GDate has no identity and no state a caller could observe, so it is converted at the boundary instead of wrapped, at the six date callables of Gst and the GES meta container.- Ledger and diagnostics — sixty-nine C symbols the bindings cover from
src/*/Customand from the runtime are declared hand-bound, so the remaining sections of the skip report measure the gap that is real; a stale overlay entry is now reported (GEN0020 / GEN0023 / GEN0024) and the committed overlays are held to producing none;generatedeletes a committed generated file the run no longer emits andverifyreports it as a difference; and the class remarks now describe every callback scope the planner binds. - Community and coverage — the Contributor Covenant 2.1, a new
docs/platform-notes.mdfor what is a property of a platform rather than of the binding, and new integration tests and samples: BasicTutorial07 and BasicTutorial09,samples/AppSrcPush, the base-utils feature surface, the audio format / meta / converter / volume surfaces, and theVideoCodecFrameuser-data contract.
Behavioural fixes
Gst.Clock.IdWaitAsync — callback state leak, and a double free on a refused wait. gst_clock_id_wait_async writes the callback, its state and the destroy notification onto the entry immediately before it dispatches to the clock, so the three exits in front of that assignment never see the notification and the generated member leaked the GCHandle on every one of them. The entry point is now hand written with the same signature and documentation: it releases the state on GST_CLOCK_BADTIME, on GST_CLOCK_UNSUPPORTED and on a GST_CLOCK_ERROR with no clock alone, and leaves it with the entry for the refusals the clock itself answers — GST_CLOCK_UNSCHEDULED for an entry that was unscheduled beforehand, and GST_CLOCK_ERROR when the clock could not start its waiting thread — which the earlier code freed a second time, hitting an unrelated live handle. The reference taken on the clock of the entry across the call is held as a bare handle rather than through a wrapper, because wrappers are interned and disposing one disposed the clock of the caller.
Gst.Meta.RegisterCustom — transform state leak on a refused registration. gst_meta_register_custom only writes the transform function, its state and the destroy notification onto the implementation block once that block exists; both failures before it — an API type that could not be registered, which is what a name that is already taken produces, and a block that could not be allocated — answer NULL. The generated member allocated the GCHandle of the transform first and reported the NULL as an InvalidOperationException, so the throw leaked it. The entry point is now hand written with the same signature and releases the state before it raises. gst_meta_register_custom_simple is a different entry point and stays generated.
Callback state is allocated after every guard and prologue. A member whose only materializing argument was a callback took the GCHandle of the callback before the guard of a later parameter and before the UTF-8 copy of a later string, so either of those throwing stranded the handle, the delegate and everything the closure captured for the life of the process. The allocation is now the last statement before the call.
WebRTCDataChannel.OnMessageData — documentation correction. The remarks named a streaming thread of the pipeline; the handler in fact runs on the main context of the thread webrtcbin starts for the connection. Doc-only.
Behavioural notes
Two hardenings that only change what happens on input C would have mishandled:
Buffer.MakeWritable(),Caps.MakeWritable()and every generated*.MakeWritable()now throw anInvalidOperationExceptionwhen the writable copy could not be made. The C function consumed the reference all the same, so the wrapper is left disposed and the failure is raised, instead of silently handing back an already-consumed wrapper.Value.SetBoxed(Boxed?)andValue.SetMiniObject(MiniObject?)now check that the value holds a boxed type at all and that the GType of the content is that type or derives from it, and throw anInvalidOperationExceptionotherwise — on an uninitialized value, a value of another family, or a content of another type. GLib copies the content with the copy function of the type the value holds, so a wrapper of another type was silently handed to the wrong copy function and left a corrupt value behind; the rawSetBoxed(nint)overload has no type to check and is unchanged. This mirrors the checkValueRefhas performed since it shipped.
No signature changed for either.
Changes
- Check the content type at the two wrapper setters of Value
- Say which context the discoverer takes and that its signal is bound
- Cover the base utils features and port the tee, discoverer and appsrc samples
- Convert GLib.Date to System.DateOnly at the six date callables
- Count the three new modules across the docs, workflows and tests
- Bind the Gst.Transcoder module, its sample and the API bus parses
- Bind the GstTag library as the GstSharp.Net.Tag module
- Bind the fd, dmabuf, shm and DRM dumb allocators of GstAllocators
- Publish the API reference and guides with DocFX on GitHub Pages
- Bind the element factories that take their properties with them
- Bind the callables that take or hand out a GParamSpec
- Pin the VideoCodecFrame user data contract through theoradec
- Cover the audio format, meta, converter and volume surfaces with integration tests
- Ledger on-message-data as hand bound and correct the thread its remarks name
- Replace the gir returns text of adopt in place membe...
v1.28.3
The generator capability release: fourteen planner and emitter extensions turn the largest remaining gaps of the skip ledger into generated members. Every existing API keeps compiling, enforced by package validation against the 1.28.2 baseline; the ledger shrinks from 1,335 entries to 848.
Highlights
- Consuming in-parameters are generated — callables whose callee takes ownership (
AppSrc.PushBuffer-style) are emitted with the same mint-and-dispose contract the hand-written members established; the wrapper stays valid after the call. - GValue everywhere — callables that take or return a
Value, theValueArrayentry points, and the fundamental value-container functions (ValueArray/ValueList/ValueUniqueList) are bound, so caps fields that hold lists can be read and written. - Properties without a C accessor — 191 GObject properties that only existed as pspecs are now typed C# properties.
- Caller-allocates out parameters, value-struct methods and record fields — the
(out caller-allocates)shape, the methods declared on plain value structs, and accessors on boxed and opaque records (Meta records included) are generated, with the layouts pinned by ABI probes. - Strings and arrays — NULL-terminated string arrays, the array annotations the girs leave short, and
GLib.DateTimewith the transfer-full signal returns. - Callbacks — asynchronous callbacks with a destroy notify, honouring the nullable annotation on callback parameters.
- Lists and errors —
GList/GSListinput parameters, and theGErrorsignals and callables projected ontoGException. - Buffer meta — the meta attachment cluster is hand-bound (
Buffer.AddMeta/ForeachMetaand friends). - Zero-copy wrappers —
Buffer.NewWrappedFullandMemory.NewWrappedhand a managed buffer to GStreamer without a copy and release it through the notify.
Behavioural fixes (release-note callouts per the compatibility promise)
Gst.Base.CollectPads.SetFunction / SetCompareFunction / SetEventFunction / SetFlushFunction / SetQueryFunction — use-after-free fix. The gir declares these callbacks as call-scoped; the C library stores them and invokes them from the streaming thread. The shipped members freed the managed trampoline when the setter returned, so the next buffer dereferenced a dead delegate. The binding now keeps the callback state alive for the life of the process, because the library offers no destroy notification to release it: one handle is leaked per call, so install each function once, at construction — replacing it does not release the previous state.
Gst.Meta.ApiTypeAggregateParams — signature and behaviour fix. The overload that shipped in 1.28.2 never worked: the C function takes a GstStructure**, the gir declares a GstStructure*, and every call overwrote the GType header of the structure the caller handed in and leaked the copy the aggregator had just made. The result now comes back through an out parameter: bool ApiTypeAggregateParams(GType api, out Structure? aggregatedParams, Structure? params0, Structure? params1). The shipped overload is kept as an [Obsolete] member that throws NotSupportedException and will be removed in 1.30 — it is kept because an out parameter is passed by reference, so a consumer compiled against 1.28.2 would otherwise get a MissingMethodException and one recompiling would get CS1620. Package validation does not see that break: it compares member signatures without the ref kind, so the 1.28.x source and binary promise is what keeps the bridge alive, not the gate. Do not delete it on the strength of a green gate.
Gst.Video.VideoGlobal.BufferAddVideoMeta and BufferAddVideoMetaFull now return VideoMeta?. Both C functions answer NULL for a format/width/height combination gst_video_info_set_format rejects, and on a buffer that is not writable — the gir marks neither return nullable, so the generated members turned a legitimate caller condition into an InvalidOperationException whose message named the wrong cause. They now answer null. Code that dereferenced the result unconditionally will see a nullable-reference warning; that warning is the bug it was hiding.
Behavioural notes
Three hardenings that only change what happens on input C would have mishandled:
- Hidden array lengths narrower than
int(byte/ushort, the nine MIKEY members) now throwArgumentExceptionfor a span too long to describe instead of silently wrapping the count. Value.CopyFromwith a non-NULL source whose type is 0 now leaves the target empty instead of forwarding tog_value_copyand itsg_critical.Value.SetObjectkeeps the wrapper it was given alive for as long as the value holds it.
No signature changed for any of these. Members bound for the first time in this release (AudioGlobal.BufferGetAudioDownmixMetaForChannels among them) chose their nullable shape at first binding and affect no existing caller.
Changes
- Bind the GError signals and callables onto GException
- Bind the GList and GSList input parameters
- Hand-bind the buffer meta attachment cluster and fix the aggregate-params corruption
- Honour the nullable annotation on callback parameters
- Bind the asynchronous callbacks and keep the CollectPads functions alive
- Complete the array annotations the girs leave short
- Bind the NULL-terminated string arrays
- Branch the null select-element-track answer on the 1.28 discard gate
- Bind GLib.DateTime and the transfer-full GObject signal returns
- Plan a discarded gboolean return as a void callee
- Bind the methods declared on plain value structs
- Bind the caller-allocates out parameters
- Bind field accessors on boxed and opaque records
- Bind the fundamental value container functions
- Add a shared GStreamer 1.28 availability probe for tests
- Bind properties that have no C accessor
- Bind the GValueArray entry points
- Bind callables that take or return a GValue
- Say what AudioBuffer actually ships
- Bind in-parameters whose callee takes ownership
- Order generated prologues as guards, then handle reads, then allocations
- Say what the SDK floor actually decides
- Say 1.28.2 in the pin table too
- Accept any .NET 10 SDK
- Guard the 1.28.2 surface too
- Say 1.28.2 when nobody passes a version
- Drop csproj files from the NuGet cache key
Full Changelog: v1.28.2...v1.28.3
v1.28.2
Additive quick wins on the 1.28.1 surface: every existing API keeps compiling, enforced by package validation against the 1.28.1 baseline.
Highlights
- playbin3 track switching —
Event.NewSelectStreams/ParseSelectStreams. - TagList write path —
AddValueplus typed writers (AddString,AddUint, ...); a recording can finally carry a title. - Properties by name in one line —
SetProperty(string, object?),GetProperty<T>(string)andFindProperty(string)for the elements no.girfile describes (x264enc, the d3d11 family, webrtcbin). - Segment fields — rate, start, stop, position and the rest are readable and writable, with the layout pinned by ABI probes.
- Five consuming calls hand-bound —
Event.NewCustom,Message.NewCustom,Query.NewCustom,Promise.Reply,EncodingContainerProfile.AddProfile. - Controller module complete — LFO and trigger control sources, ARGB and proxy bindings, still zero
InternalsVisibleTo. TypeFind.TryPeek— a registered typefinder can read the stream it identifies.Gst.Global.MacosMain/MacosMainSimple— really bound,[SupportedOSPlatform("macos")].
Behavioral fixes (release-note callouts per the compatibility promise)
Two by-name property members no longer fail silently. In C, g_object_get_property / g_object_set_property answer misuse with a console warning and no effect; these now throw instead:
Object.GetProperty(string)throwsArgumentExceptionfor a property that cannot be read (previously: an unfilledValue).Object.SetProperty(string, in Value)throwsArgumentExceptionfor an unknown name, an unwritable or construct-only property, an empty value, or a value type GObject cannot transform (previously: a silent no-op). Every conversion GObject performs — anintinto agdoubleproperty, for example — still goes through unchanged.
No signature changed; code that used these members correctly is unaffected.
Changes
- Guard the value-taking property writer too
- Let a property be asked about before it is touched
- Keep the segment tests on the 1.24 floor
- Tell the twelve packages apart on nuget.org
- Bind the gst_macos_main family for real
- Bind the rest of libgstcontroller
- Make a property one line in either direction
- Expose the fields of a segment
- Give stream selection its write side
- Hand-bind the five hottest consuming calls
- Give the tag list a write path
- Let typefinders read the stream they identify
- Say 1.28.1 when nobody passes a version
- Hold every push to the 1.28.1 surface
Full Changelog: v1.28.1...v1.28.2
v1.28.1
This release is the new compatibility baseline, and the last breaking one. 1.28.1 is the one-time shape fix taken in the first week of the series, while nothing yet depended on the surface: the buffer metas, StaticCaps/StaticPadTemplate, VideoResampler and their kin moved from value snapshots to the pointer-backed wrappers their C contracts require; the members that discarded or overran what the native call wrote — two of them corrupting the caller's stack, one freeing the ring buffer's own memory — were repaired; the digit-led enum members got names that say what they mean; and the byte/bit cursors were declared out of scope in favor of Span<byte> and BinaryPrimitives. From here on, within 1.28.x the public surface only grows — the promise in the README's "Status and versioning" counts from this release. 1.28.0 and its previews are unlisted accordingly.
Changes
- Hold the Roslyn floor by hand
- build: Bump Microsoft.CodeAnalysis.CSharp.Analyzer.Testing from 1.1.2 to 1.1.4 (#2)
- build: Bump Microsoft.NET.Test.Sdk from 17.14.1 to 18.9.0 (#3)
- build: Bump xunit.runner.visualstudio from 3.1.5 to 4.0.0 (#4)
- Stop freeing the ring buffer's own memory
- Ride the fixes that break nothing
- Count the promise from 1.28.1
- Keep the natural field names free for typed accessors
- Hand back what the callee wrote
- Reach the static caps cache instead of copying it
- Address the buffer metas through the buffer that owns them
- Name the enum members the underscore was hiding
- Declare the byte and bit cursors out of scope
- Run the NativeAOT gate on linux-x64 as well
- Give the WebRTC tests a leg that runs them
- List the commits in the release notes
- Pin the actions to commit shas and let dependabot move them
- Add issue forms, a PR template and a security policy
- Explain what to do when the census fails
- Stop claiming gst_macos_main is bound
Full Changelog: v1.28.0...v1.28.1