Skip to content

Commit 4236901

Browse files
committed
adding spout and adding required dependencies
code is still broken though since it came from master
1 parent 0b88747 commit 4236901

File tree

10 files changed

+83211
-0
lines changed

10 files changed

+83211
-0
lines changed

Editor/Editor.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
<Examples Include="$(Ops)/examples/$(RelativeBuildFolder)/**" SubFolder="examples" />
162162
<Unsplash Include="$(Ops)/unsplash/$(RelativeBuildFolder)/**" SubFolder="unsplash" />
163163
<Ndi Include="$(Ops)/Ndi/$(RelativeBuildFolder)/**" SubFolder="Ndi" />
164+
<Spout Include="$(Ops)/Spout/$(RelativeBuildFolder)/**" SubFolder="Spout" />
164165
<pixtur Include="$(UserOps)/pixtur/$(RelativeBuildFolder)/**" SubFolder="pixtur" />
165166
</ItemGroup>
166167

Operators/Spout/lib/interop/SpoutDX.cs

Lines changed: 79020 additions & 0 deletions
Large diffs are not rendered by default.

Operators/Spout/lib/interop/Std.cs

Lines changed: 3328 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
using SharpDX.Direct3D11;
2+
using SharpDX.DXGI;
3+
using SpoutDX;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Runtime.InteropServices;
7+
using T3.Core.DataTypes;
8+
using T3.Core.Logging;
9+
using T3.Core.Operator;
10+
using T3.Core.Operator.Attributes;
11+
using T3.Core.Operator.Slots;
12+
using T3.Core.Resource;
13+
using DeviceContext = OpenGL.DeviceContext;
14+
using Resource = SharpDX.DXGI.Resource;
15+
16+
namespace Lib.io.video
17+
{
18+
public class SpoutInput : Instance<SpoutInput>
19+
{
20+
[Output(Guid = "10955469-F5C0-476D-8A1B-9CB2803820A9", DirtyFlagTrigger = DirtyFlagTrigger.Animated)]
21+
public readonly Slot<Texture2D> Texture = new();
22+
23+
public SpoutInput()
24+
{
25+
Texture.UpdateAction = Update;
26+
_instance++;
27+
}
28+
~SpoutInput()
29+
{
30+
// FIXME: Dispose OpenGL context correctly
31+
// Dispose();
32+
}
33+
34+
private void Update(EvaluationContext context)
35+
{
36+
Command.GetValue(context);
37+
var receiverName = ReceiverName.GetValue(context);
38+
39+
if (!ReceiveTexture(receiverName))
40+
Texture.Value = null;
41+
42+
// ReceiverName.Update(context);
43+
}
44+
45+
private string GetAdapterName(int adapterIndex)
46+
{
47+
IntPtr adapterName = Marshal.AllocHGlobal(1024);
48+
string adapter;
49+
unsafe
50+
{
51+
sbyte* name = (sbyte*)adapterName;
52+
_spoutDX.GetAdapterName(adapterIndex, name, 1024);
53+
adapter = new string(name);
54+
}
55+
Marshal.FreeHGlobal(adapterName);
56+
return adapter;
57+
}
58+
59+
private string GetSenderAdapter()
60+
{
61+
IntPtr adapterName = Marshal.AllocHGlobal(1024);
62+
string adapter;
63+
unsafe
64+
{
65+
sbyte* name = (sbyte*)adapterName;
66+
_spoutDX.GetSenderAdapter(_receiverName, name, 1024);
67+
adapter = new string(name);
68+
}
69+
Marshal.FreeHGlobal(adapterName);
70+
return adapter;
71+
}
72+
73+
private unsafe bool GetSenderInfo(ref uint width, ref uint height, ref IntPtr dxSharedHandle, ref uint dwFormat)
74+
{
75+
fixed (IntPtr* handle = &dxSharedHandle)
76+
return _spoutDX.GetSenderInfo(_receiverName, ref width, ref height, handle, ref dwFormat);
77+
}
78+
79+
private static Texture2D CreateD3D11Texture2D(Texture2D d3d11Texture2D)
80+
{
81+
using (var resource = d3d11Texture2D.QueryInterface<Resource>())
82+
{
83+
return ResourceManager.Device.OpenSharedResource<Texture2D>(resource.SharedHandle);
84+
}
85+
}
86+
87+
private bool InitializeSpout(string receiverName, bool useWidthAndHeight, uint width, uint height)
88+
{
89+
// TODO: combine OpenGL context creation of Spout input and output
90+
if (!_initialized)
91+
{
92+
// create OpenGL context and make this become the primary context
93+
_deviceContext = DeviceContext.Create();
94+
_glContext = DeviceContext.GetCurrentContext();
95+
if (_glContext == IntPtr.Zero)
96+
{
97+
_glContext = _deviceContext.CreateContext(IntPtr.Zero);
98+
_deviceContext.MakeCurrent(_glContext);
99+
}
100+
_device = ID3D11Device.__CreateInstance(((IntPtr)ResourceManager.Device));
101+
_initialized = true;
102+
}
103+
else if (_glContext != DeviceContext.GetCurrentContext())
104+
{
105+
// Make this become the primary context
106+
if (_deviceContext == null ||!_deviceContext.MakeCurrent(_glContext))
107+
return false;
108+
}
109+
110+
// get rid of old textures?
111+
if ((useWidthAndHeight && (width != _width || height != _height))
112+
|| receiverName != _receiverName)
113+
{
114+
DisposeTextures();
115+
}
116+
117+
// create new spoutDX object
118+
if (_spoutDX == null)
119+
{
120+
_spoutDX = new SpoutDX.SpoutDX();
121+
_spoutDX.AdapterAuto = true;
122+
_spoutDX.OpenDirectX11(_device);
123+
124+
// set new receiver
125+
_spoutDX.SetReceiverName(receiverName);
126+
_spoutDX.CheckSenderFormat(receiverName);
127+
_receiverName = receiverName;
128+
ReceiverName.SetTypedInputValue(_receiverName);
129+
}
130+
else if (receiverName != _receiverName || !_spoutDX.IsConnected)
131+
{
132+
// set new receiver
133+
_spoutDX.SetReceiverName(receiverName);
134+
_spoutDX.CheckSenderFormat(receiverName);
135+
_receiverName = receiverName;
136+
ReceiverName.SetTypedInputValue(_receiverName);
137+
}
138+
139+
// switch to sender adapter if connected and adapter is not automatically chosen
140+
if (!_adapterSet && _spoutDX != null && _spoutDX.IsConnected && !_spoutDX.AdapterAuto)
141+
{
142+
string adapterName = GetSenderAdapter();
143+
for (var i = 0; i < _spoutDX.NumAdapters; i++)
144+
{
145+
if (GetAdapterName(i) == adapterName)
146+
{
147+
if (_spoutDX.Adapter != i)
148+
{
149+
_spoutDX.SetAdapter(i);
150+
Console.WriteLine(@$"Spout input switched to sender adapter");
151+
break;
152+
}
153+
}
154+
}
155+
156+
Console.WriteLine(@$"Spout input is using adapter {GetAdapterName(_spoutDX.Adapter)}");
157+
_adapterSet = true;
158+
}
159+
160+
return true;
161+
}
162+
163+
private bool ReceiveTexture(string receiverName)
164+
{
165+
uint width = 0;
166+
uint height = 0;
167+
try
168+
{
169+
// initialize spout to get width and height
170+
if (!InitializeSpout(receiverName, false, 0, 0))
171+
return false;
172+
173+
// re-initialize with correct receiver width and height
174+
if (_spoutDX != null)
175+
{
176+
_spoutDX.ReceiveTexture();
177+
width = _spoutDX.SenderWidth;
178+
height = _spoutDX.SenderHeight;
179+
if (width == 0 || height == 0)
180+
return false;
181+
182+
if (!InitializeSpout(receiverName, true, width, height))
183+
return false;
184+
}
185+
}
186+
catch (Exception e)
187+
{
188+
Log.Debug("Initialization of Spout failed. Are Spout.dll and SpoutDX.dll present in the executable folder?", this);
189+
Log.Debug(e.ToString());
190+
_spoutDX?.ReleaseReceiver();
191+
_spoutDX?.CloseDirectX11();
192+
_spoutDX?.Dispose();
193+
_spoutDX = null;
194+
return false;
195+
}
196+
197+
var device = ResourceManager.Device;
198+
try
199+
{
200+
if (!_spoutDX.IsFrameNew)
201+
return true;
202+
203+
Texture2D readTexture = new Texture2D(_spoutDX.SenderTexture.__Instance);
204+
205+
// check the input format
206+
uint senderWidth = 0;
207+
uint senderHeight = 0;
208+
IntPtr senderHandle = IntPtr.Zero;
209+
uint directXFormat = 0;
210+
211+
// default to RGBA input
212+
Format textureFormat = Format.R8G8B8A8_UNorm;
213+
// check the currently received format
214+
if (GetSenderInfo(ref senderWidth, ref senderHeight, ref senderHandle, ref directXFormat))
215+
{
216+
// if a format is provided, use that,
217+
// otherwise use the format provided in the image that was received
218+
if (directXFormat != 0)
219+
textureFormat = (Format)directXFormat;
220+
else
221+
textureFormat = readTexture.Description.Format;
222+
}
223+
224+
// create several textures with a given format with CPU access
225+
// to be able to read out the initial texture values
226+
if (ImagesWithGpuAccess.Count == 0
227+
|| ImagesWithGpuAccess[0].Description.Format != textureFormat
228+
|| ImagesWithGpuAccess[0].Description.Width != (int)width
229+
|| ImagesWithGpuAccess[0].Description.Height != (int)height
230+
|| ImagesWithGpuAccess[0].Description.MipLevels != 1)
231+
{
232+
var imageDesc = new Texture2DDescription
233+
{
234+
BindFlags = BindFlags.ShaderResource | BindFlags.RenderTarget,
235+
Format = textureFormat,
236+
Width = (int)width,
237+
Height = (int)height,
238+
MipLevels = 1,
239+
SampleDescription = new SampleDescription(1, 0),
240+
Usage = ResourceUsage.Default,
241+
OptionFlags = ResourceOptionFlags.None,
242+
CpuAccessFlags = CpuAccessFlags.None,
243+
ArraySize = 1
244+
};
245+
246+
DisposeTextures();
247+
248+
Log.Debug($"Spout input wxh = {senderWidth}x{senderHeight}, " +
249+
$"handle = {senderHandle}, " +
250+
$"format = {textureFormat} ({directXFormat})");
251+
252+
for (var i = 0; i < NumTextureEntries; ++i)
253+
{
254+
ImagesWithGpuAccess.Add(new Texture2D(device, imageDesc));
255+
}
256+
_width = width;
257+
_height = height;
258+
259+
_currentIndex = 0;
260+
}
261+
262+
// sanity check
263+
if (_spoutDX == null || width == 0 || height == 0 || _width != width || _height != height)
264+
return false;
265+
266+
// copy the spout texture to an internal image
267+
var immediateContext = device.ImmediateContext;
268+
var readableImage = ImagesWithGpuAccess[_currentIndex];
269+
immediateContext.CopyResource(readTexture, readableImage);
270+
_currentIndex = (_currentIndex + 1) % NumTextureEntries;
271+
272+
Texture.Value = readableImage;
273+
}
274+
catch (Exception e)
275+
{
276+
Log.Debug("Spout input texture reception failed: " + e.ToString());
277+
}
278+
279+
return true;
280+
}
281+
282+
protected void DisposeTextures()
283+
{
284+
foreach (var image in ImagesWithGpuAccess)
285+
image?.Dispose();
286+
287+
ImagesWithGpuAccess.Clear();
288+
}
289+
290+
#region IDisposable Support
291+
292+
public new void Dispose()
293+
{
294+
// dispose textures
295+
DisposeTextures();
296+
297+
_spoutDX?.ReleaseSender();
298+
_spoutDX?.CloseDirectX11();
299+
_spoutDX?.Dispose();
300+
_adapterSet = false;
301+
302+
if (_instance > 0)
303+
--_instance;
304+
305+
if (_instance <= 0)
306+
{
307+
_device?.Dispose();
308+
_deviceContext?.MakeCurrent(IntPtr.Zero);
309+
_deviceContext?.Dispose();
310+
_initialized = false;
311+
}
312+
}
313+
314+
#endregion
315+
316+
private static int _instance; // number of instances of this object
317+
private static bool _initialized; // were static members initialized?
318+
private static DeviceContext _deviceContext; // OpenGL device context
319+
private static IntPtr _glContext; // OpenGL context
320+
private static ID3D11Device _device; // Direct3D11 device
321+
322+
private SpoutDX.SpoutDX _spoutDX; // spout object supporting DirectX
323+
private static bool _adapterSet; // was adapter set to sender's adapter?
324+
private string _receiverName; // name of our spout receiver
325+
private uint _width; // current width of our receiver
326+
private uint _height; // current height of our receiver
327+
328+
// hold several textures internally to speed up calculations
329+
private const int NumTextureEntries = 2;
330+
private readonly List<Texture2D> ImagesWithGpuAccess = new();
331+
// current image index (used for circular access of ImagesWithGpuAccess)
332+
private int _currentIndex;
333+
334+
[Input(Guid = "F7A7A410-FB91-448C-A0FE-BA4278D82107")]
335+
public readonly InputSlot<Command> Command = new();
336+
337+
[Input(Guid = "C5C487D2-3EC0-49E3-9EA5-0B1238E82233")]
338+
public InputSlot<string> ReceiverName = new();
339+
}
340+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"Id": "25307357-6f6c-45b1-a38d-de635510a845",
3+
"Inputs": [
4+
{
5+
"Id": "f7a7a410-fb91-448c-a0fe-ba4278d82107"/*Command*/,
6+
"DefaultValue": null
7+
},
8+
{
9+
"Id": "c5c487d2-3ec0-49e3-9ea5-0b1238e82233"/*ReceiverName*/,
10+
"DefaultValue": "Spout Demo Sender"
11+
}
12+
],
13+
"Children": [],
14+
"Connections": []
15+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"Id": "25307357-6f6c-45b1-a38d-de635510a845"/*SpoutInput*/,
3+
"Description": "Spout live video input",
4+
"InputUis": [
5+
{
6+
"InputId": "f7a7a410-fb91-448c-a0fe-ba4278d82107"/*Command*/,
7+
"Position": {
8+
"X": -200.0,
9+
"Y": 45.0
10+
}
11+
},
12+
{
13+
"InputId": "c5c487d2-3ec0-49e3-9ea5-0b1238e82233"/*ReceiverName*/,
14+
"Position": {
15+
"X": -200.0,
16+
"Y": 90.0
17+
},
18+
"Usage": "Default"
19+
}
20+
],
21+
"SymbolChildUis": [],
22+
"OutputUis": [
23+
{
24+
"OutputId": "10955469-f5c0-476d-8a1b-9cb2803820a9"/*Texture*/,
25+
"Position": {
26+
"X": 300.0,
27+
"Y": 200.0
28+
}
29+
}
30+
]
31+
}

0 commit comments

Comments
 (0)