feat(interop): add NDI 6 native bindings, handle types, and version constants
Some checks failed
CI / build-and-test (push) Has been cancelled
Some checks failed
CI / build-and-test (push) Has been cancelled
This commit is contained in:
parent
60b12eb637
commit
6f09ca35ef
3 changed files with 235 additions and 0 deletions
154
src/TeamsISO.Engine.NdiInterop/NdiNative.cs
Normal file
154
src/TeamsISO.Engine.NdiInterop/NdiNative.cs
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TeamsISO.Engine.NdiInterop;
|
||||
|
||||
/// <summary>
|
||||
/// P/Invoke declarations for the NewTek/Vizrt NDI SDK 6 native library.
|
||||
/// On Windows the import target is <c>Processing.NDI.Lib.x64.dll</c>; the loader resolves it
|
||||
/// from the NDI Runtime installation path or from the application directory.
|
||||
/// </summary>
|
||||
internal static class NdiNative
|
||||
{
|
||||
private const string LibName = "Processing.NDI.Lib.x64";
|
||||
|
||||
// ---- Lifecycle ----
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_initialize", CallingConvention = CallingConvention.Cdecl)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool Initialize();
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_destroy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void Destroy();
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_version", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr Version();
|
||||
|
||||
// ---- Find ----
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_find_create_v2", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr FindCreateV2(IntPtr p_create_settings);
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_find_destroy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void FindDestroy(IntPtr p_instance);
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_find_get_current_sources", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr FindGetCurrentSources(IntPtr p_instance, out uint p_no_sources);
|
||||
|
||||
// ---- Receive ----
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_recv_create_v3", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr RecvCreateV3(ref RecvCreateV3 p_create_settings);
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_recv_destroy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void RecvDestroy(IntPtr p_instance);
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_recv_capture_v3", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern FrameType RecvCaptureV3(
|
||||
IntPtr p_instance,
|
||||
out VideoFrameV2 p_video_data,
|
||||
IntPtr p_audio_data,
|
||||
IntPtr p_metadata,
|
||||
uint timeout_in_ms);
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_recv_free_video_v2", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void RecvFreeVideoV2(IntPtr p_instance, ref VideoFrameV2 p_video_data);
|
||||
|
||||
// ---- Send ----
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_send_create", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr SendCreate(ref SendCreate p_create_settings);
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_send_destroy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SendDestroy(IntPtr p_instance);
|
||||
|
||||
[DllImport(LibName, EntryPoint = "NDIlib_send_send_video_v2", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void SendSendVideoV2(IntPtr p_instance, ref VideoFrameV2 p_video_data);
|
||||
|
||||
// ---- Enums ----
|
||||
|
||||
public enum FrameType
|
||||
{
|
||||
None = 0,
|
||||
Video = 1,
|
||||
Audio = 2,
|
||||
Metadata = 3,
|
||||
Error = 4,
|
||||
StatusChange = 100
|
||||
}
|
||||
|
||||
public enum RecvBandwidth
|
||||
{
|
||||
MetadataOnly = -10,
|
||||
AudioOnly = 10,
|
||||
Lowest = 0,
|
||||
Highest = 100
|
||||
}
|
||||
|
||||
public enum RecvColorFormat
|
||||
{
|
||||
BgrxBgra = 0,
|
||||
UyvyBgra = 1,
|
||||
Rgbx_Rgba = 2,
|
||||
UyvyRgba = 3,
|
||||
Fastest = 100,
|
||||
Best = 101
|
||||
}
|
||||
|
||||
public enum FrameFormatType
|
||||
{
|
||||
Progressive = 1,
|
||||
Interleaved = 0,
|
||||
FieldZero = 2,
|
||||
FieldOne = 3,
|
||||
Max = 0x7FFFFFFF
|
||||
}
|
||||
|
||||
public static class FourCC
|
||||
{
|
||||
public const uint UYVY = 0x59565955; // 'U','Y','V','Y'
|
||||
public const uint BGRA = 0x41524742; // 'B','G','R','A'
|
||||
public const uint BGRX = 0x58524742; // 'B','G','R','X'
|
||||
public const uint RGBA = 0x41424752; // 'R','G','B','A'
|
||||
}
|
||||
|
||||
// ---- Structs ----
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Source
|
||||
{
|
||||
public IntPtr p_ndi_name; // const char*
|
||||
public IntPtr p_url_address; // const char* (union, treat as URL)
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RecvCreateV3
|
||||
{
|
||||
public Source source_to_connect_to;
|
||||
public RecvColorFormat color_format;
|
||||
public RecvBandwidth bandwidth;
|
||||
[MarshalAs(UnmanagedType.U1)] public bool allow_video_fields;
|
||||
public IntPtr p_ndi_recv_name; // const char*
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SendCreate
|
||||
{
|
||||
public IntPtr p_ndi_name; // const char*
|
||||
public IntPtr p_groups; // const char*
|
||||
[MarshalAs(UnmanagedType.U1)] public bool clock_video;
|
||||
[MarshalAs(UnmanagedType.U1)] public bool clock_audio;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct VideoFrameV2
|
||||
{
|
||||
public int xres;
|
||||
public int yres;
|
||||
public uint FourCC;
|
||||
public int frame_rate_N;
|
||||
public int frame_rate_D;
|
||||
public float picture_aspect_ratio;
|
||||
public FrameFormatType frame_format_type;
|
||||
public long timecode;
|
||||
public IntPtr p_data;
|
||||
public int line_stride_in_bytes; // (union with data_size_in_bytes)
|
||||
public IntPtr p_metadata;
|
||||
public long timestamp;
|
||||
}
|
||||
}
|
||||
61
src/TeamsISO.Engine.NdiInterop/NdiPInvokeHandles.cs
Normal file
61
src/TeamsISO.Engine.NdiInterop/NdiPInvokeHandles.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using TeamsISO.Engine.Interop;
|
||||
|
||||
namespace TeamsISO.Engine.NdiInterop;
|
||||
|
||||
internal sealed class NdiPInvokeFindHandle : NdiFindHandle
|
||||
{
|
||||
public IntPtr Native { get; private set; }
|
||||
|
||||
public NdiPInvokeFindHandle(IntPtr native) => Native = native;
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (Native != IntPtr.Zero)
|
||||
{
|
||||
NdiNative.FindDestroy(Native);
|
||||
Native = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NdiPInvokeReceiverHandle : NdiReceiverHandle
|
||||
{
|
||||
public IntPtr Native { get; private set; }
|
||||
public string SourceName { get; }
|
||||
|
||||
public NdiPInvokeReceiverHandle(IntPtr native, string sourceName)
|
||||
{
|
||||
Native = native;
|
||||
SourceName = sourceName;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (Native != IntPtr.Zero)
|
||||
{
|
||||
NdiNative.RecvDestroy(Native);
|
||||
Native = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NdiPInvokeSenderHandle : NdiSenderHandle
|
||||
{
|
||||
public IntPtr Native { get; private set; }
|
||||
public string OutputName { get; }
|
||||
|
||||
public NdiPInvokeSenderHandle(IntPtr native, string outputName)
|
||||
{
|
||||
Native = native;
|
||||
OutputName = outputName;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (Native != IntPtr.Zero)
|
||||
{
|
||||
NdiNative.SendDestroy(Native);
|
||||
Native = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/TeamsISO.Engine.NdiInterop/NdiVersion.cs
Normal file
20
src/TeamsISO.Engine.NdiInterop/NdiVersion.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
namespace TeamsISO.Engine.NdiInterop;
|
||||
|
||||
/// <summary>
|
||||
/// Constants describing the NDI SDK version this build was compiled against.
|
||||
/// The runtime version reported by <see cref="NdiNative.Version"/> is compared against
|
||||
/// <see cref="ExpectedRuntimeVersionPrefix"/> by the engine's runtime probe to detect
|
||||
/// installations that pre-date or post-date the SDK headers (per spec §6).
|
||||
/// </summary>
|
||||
public static class NdiVersion
|
||||
{
|
||||
/// <summary>The SDK family this build targets (NDI 6).</summary>
|
||||
public const string SdkFamily = "NDI 6";
|
||||
|
||||
/// <summary>
|
||||
/// Prefix of the runtime version string we expect (NDI runtime reports e.g.
|
||||
/// "NDI SDK for Windows v6.0.1.0"). Any major change of the leading "v6" is treated
|
||||
/// as a mismatch.
|
||||
/// </summary>
|
||||
public const string ExpectedRuntimeVersionPrefix = "NDI SDK for Windows v6";
|
||||
}
|
||||
Loading…
Reference in a new issue