Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[.NET/MediaToolbox] Use [UnmanagedCallersOnly] instead of [MonoPInvokeCallback] Partial Fix for #10470 #15942

Merged
merged 1 commit into from
Sep 13, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 71 additions & 3 deletions src/MediaToolbox/MTAudioProcessingTap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,19 @@ unsafe struct Callbacks
#pragma warning disable 169
/* int */ int version; // kMTAudioProcessingTapCallbacksVersion_0 == 0
public /* void* */ IntPtr clientInfo;
#if NET
public /* MTAudioProcessingTapInitCallback */ delegate* unmanaged<IntPtr, IntPtr, void**, void> init;
public /* MTAudioProcessingTapFinalizeCallback */ delegate* unmanaged<IntPtr, void> finalize;
public /* MTAudioProcessingTapPrepareCallback */ delegate* unmanaged<IntPtr, IntPtr, AudioStreamBasicDescription*, void> prepare;
public /* MTAudioProcessingTapUnprepareCallback */ delegate* unmanaged<IntPtr, void> unprepare;
public /* MTAudioProcessingTapProcessCallback */ delegate* unmanaged<IntPtr, IntPtr, MTAudioProcessingTapFlags, IntPtr, IntPtr*, MTAudioProcessingTapFlags*, void> process;
#else
public /* MTAudioProcessingTapInitCallback */ MTAudioProcessingTapInitCallbackProxy init;
public /* MTAudioProcessingTapFinalizeCallback */ Action_IntPtr finalize;
public /* MTAudioProcessingTapPrepareCallback */ MTAudioProcessingTapPrepareCallbackProxy prepare;
public /* MTAudioProcessingTapUnprepareCallback */ Action_IntPtr unprepare;
public /* MTAudioProcessingTapProcessCallback */ MTAudioProcessingTapProcessCallbackProxy process;
#endif
#pragma warning restore 169
}

Expand Down Expand Up @@ -107,6 +115,17 @@ public MTAudioProcessingTap (MTAudioProcessingTapCallbacks callbacks, MTAudioPro

var c = new Callbacks ();
unsafe {
#if NET
if (callbacks.Initialize is not null)
c.init = &InitializeProxy;
if (callbacks.Finalize is not null)
c.finalize = &FinalizeProxy;
if (callbacks.Prepare is not null)
c.prepare = &PrepareProxy;
if (callbacks.Unprepare is not null)
c.unprepare = &UnprepareProxy;
c.process = &ProcessProxy;
#else
if (callbacks.Initialize is not null)
c.init = InitializeProxy;
if (callbacks.Finalize is not null)
Expand All @@ -115,8 +134,9 @@ public MTAudioProcessingTap (MTAudioProcessingTapCallbacks callbacks, MTAudioPro
c.prepare = PrepareProxy;
if (callbacks.Unprepare is not null)
c.unprepare = UnprepareProxy;

c.process = ProcessProxy;
#endif

}

// a GCHandle is needed because we do not have an handle before calling MTAudioProcessingTapCreate
Expand Down Expand Up @@ -177,36 +197,72 @@ public MTAudioProcessingTapError GetSourceAudio (nint frames, AudioBuffers buffe
//
// Proxy callbacks
//
#if NET
[UnmanagedCallersOnly]
unsafe static void InitializeProxy (IntPtr tap, IntPtr /*void**/ clientInfo, void** tapStorage)
#else
[MonoPInvokeCallback (typeof (MTAudioProcessingTapInitCallbackProxy))]
unsafe static void InitializeProxy (IntPtr tap, IntPtr /*void**/ clientInfo, out void* tapStorage)
#endif
{
#if NET
void *tempTapStorage = null;
*tapStorage = tempTapStorage;
#else
tapStorage = null;
#endif
// at this stage the handle is not yet known (or part of the `handles` dictionary
// so we track back the managed MTAudioProcessingTap instance from the GCHandle
var apt = (MTAudioProcessingTap?) GCHandle.FromIntPtr (clientInfo).Target;
if (apt?.callbacks.Initialize is not null)
if (apt?.callbacks.Initialize is not null) {
#if NET
apt?.callbacks.Initialize (apt, out tempTapStorage);
*tapStorage = tempTapStorage;
#else
apt?.callbacks.Initialize (apt, out tapStorage);
#endif
}
}

#if NET
[UnmanagedCallersOnly]
static unsafe void ProcessProxy (IntPtr tap, IntPtr numberFrames, MTAudioProcessingTapFlags flags,
IntPtr bufferList, IntPtr* numberFramesOut, MTAudioProcessingTapFlags* flagsOut)
#else
[MonoPInvokeCallback (typeof (MTAudioProcessingTapProcessCallbackProxy))]
static void ProcessProxy (IntPtr tap, IntPtr numberFrames, MTAudioProcessingTapFlags flags,
IntPtr bufferList, out IntPtr numberFramesOut, out MTAudioProcessingTapFlags flagsOut)
#endif
{
// from here we do not have access to `clientInfo` so it's not possible to use the GCHandle to get the
// MTAudioProcessingTap managed instance. Instead we get it from a static Dictionary
nint numberOut;
MTAudioProcessingTap apt;
lock (handles)
apt = handles [tap];
#if NET
*flagsOut = default (MTAudioProcessingTapFlags);
*numberFramesOut = IntPtr.Zero;
#else
flagsOut = default (MTAudioProcessingTapFlags);
numberFramesOut = IntPtr.Zero;
#endif
if (apt.callbacks.Processing is not null) {
#if NET
apt.callbacks.Processing (apt, (nint) numberFrames, flags, new AudioBuffers (bufferList), out numberOut, out System.Runtime.CompilerServices.Unsafe.AsRef<MTAudioProcessingTapFlags> (flagsOut));
*numberFramesOut = (IntPtr) numberOut;
#else
apt.callbacks.Processing (apt, (nint) numberFrames, flags, new AudioBuffers (bufferList), out numberOut, out flagsOut);
numberFramesOut = (IntPtr) numberOut;
#endif
}
}

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (Action_IntPtr))]
#endif
static void FinalizeProxy (IntPtr tap)
{
MTAudioProcessingTap apt;
Expand All @@ -215,18 +271,30 @@ static void FinalizeProxy (IntPtr tap)
if (apt.callbacks.Finalize is not null)
apt.callbacks.Finalize (apt);
}

#if NET
[UnmanagedCallersOnly]
static unsafe void PrepareProxy (IntPtr tap, IntPtr maxFrames, AudioStreamBasicDescription* processingFormat)
#else
[MonoPInvokeCallback (typeof (MTAudioProcessingTapPrepareCallbackProxy))]
static void PrepareProxy (IntPtr tap, IntPtr maxFrames, ref AudioStreamBasicDescription processingFormat)
#endif
{
MTAudioProcessingTap apt;
lock (handles)
apt = handles [tap];
if (apt.callbacks.Prepare is not null)
#if NET
apt.callbacks.Prepare (apt, (nint) maxFrames, ref System.Runtime.CompilerServices.Unsafe.AsRef<AudioStreamBasicDescription> (processingFormat));
#else
apt.callbacks.Prepare (apt, (nint) maxFrames, ref processingFormat);
#endif
}

#if NET
[UnmanagedCallersOnly]
#else
[MonoPInvokeCallback (typeof (Action_IntPtr))]
#endif
static void UnprepareProxy (IntPtr tap)
{
MTAudioProcessingTap apt;
Expand Down