Skip to content

Commit b6f6b1c

Browse files
kiddkaffeinekiddkaffeine
authored andcommitted
Add support for additional video codecs
1 parent 4400b64 commit b6f6b1c

File tree

5 files changed

+1216
-763
lines changed

5 files changed

+1216
-763
lines changed

src/Media/Xiph/BaseYUVPlayer.cs

Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
#region License
2+
/* FNA - XNA4 Reimplementation for Desktop Platforms
3+
* Copyright 2009-2024 Ethan Lee and the MonoGame Team
4+
*
5+
* Released under the Microsoft Public License.
6+
* See LICENSE for details.
7+
*/
8+
#endregion
9+
10+
#region Using Statements
11+
using System;
12+
13+
using Microsoft.Xna.Framework.Graphics;
14+
#endregion
15+
16+
namespace Microsoft.Xna.Framework.Media
17+
{
18+
public abstract class BaseYUVPlayer : IDisposable
19+
{
20+
#region Hardware-accelerated YUV -> RGBA
21+
22+
protected Effect shaderProgram;
23+
private IntPtr stateChangesPtr;
24+
protected Texture2D[] yuvTextures = new Texture2D[3];
25+
private Viewport viewport;
26+
27+
private static VertexPositionTexture[] vertices = new VertexPositionTexture[]
28+
{
29+
new VertexPositionTexture(
30+
new Vector3(-1.0f, -1.0f, 0.0f),
31+
new Vector2(0.0f, 1.0f)
32+
),
33+
new VertexPositionTexture(
34+
new Vector3(1.0f, -1.0f, 0.0f),
35+
new Vector2(1.0f, 1.0f)
36+
),
37+
new VertexPositionTexture(
38+
new Vector3(-1.0f, 1.0f, 0.0f),
39+
new Vector2(0.0f, 0.0f)
40+
),
41+
new VertexPositionTexture(
42+
new Vector3(1.0f, 1.0f, 0.0f),
43+
new Vector2(1.0f, 0.0f)
44+
)
45+
};
46+
private VertexBufferBinding vertBuffer;
47+
48+
// Used to restore our previous GL state.
49+
private Texture[] oldTextures = new Texture[3];
50+
private SamplerState[] oldSamplers = new SamplerState[3];
51+
private RenderTargetBinding[] oldTargets;
52+
private VertexBufferBinding[] oldBuffers;
53+
private BlendState prevBlend;
54+
private DepthStencilState prevDepthStencil;
55+
private RasterizerState prevRasterizer;
56+
private Viewport prevViewport;
57+
private FNA3D.FNA3D_RenderTargetBinding[] nativeVideoTexture =
58+
new FNA3D.FNA3D_RenderTargetBinding[3];
59+
private FNA3D.FNA3D_RenderTargetBinding[] nativeOldTargets =
60+
new FNA3D.FNA3D_RenderTargetBinding[GraphicsDevice.MAX_RENDERTARGET_BINDINGS];
61+
62+
protected void GL_initialize(byte[] shaderProgramBytes)
63+
{
64+
// Load the YUV->RGBA Effect
65+
shaderProgram = new Effect(
66+
currentDevice,
67+
shaderProgramBytes
68+
);
69+
unsafe
70+
{
71+
stateChangesPtr = FNAPlatform.Malloc(
72+
sizeof(Effect.MOJOSHADER_effectStateChanges)
73+
);
74+
}
75+
76+
// Allocate the vertex buffer
77+
vertBuffer = new VertexBufferBinding(
78+
new VertexBuffer(
79+
currentDevice,
80+
VertexPositionTexture.VertexDeclaration,
81+
4,
82+
BufferUsage.WriteOnly
83+
)
84+
);
85+
vertBuffer.VertexBuffer.SetData(vertices);
86+
}
87+
88+
protected void GL_dispose()
89+
{
90+
if (currentDevice == null)
91+
{
92+
// We never initialized to begin with...
93+
return;
94+
}
95+
currentDevice = null;
96+
97+
// Delete the Effect
98+
if (shaderProgram != null)
99+
{
100+
shaderProgram.Dispose();
101+
}
102+
if (stateChangesPtr != IntPtr.Zero)
103+
{
104+
FNAPlatform.Free(stateChangesPtr);
105+
}
106+
107+
// Delete the vertex buffer
108+
if (vertBuffer.VertexBuffer != null)
109+
{
110+
vertBuffer.VertexBuffer.Dispose();
111+
}
112+
113+
// Delete the textures if they exist
114+
for (int i = 0; i < 3; i += 1)
115+
{
116+
if (yuvTextures[i] != null)
117+
{
118+
yuvTextures[i].Dispose();
119+
}
120+
}
121+
}
122+
123+
protected void GL_setupTextures(
124+
int yWidth,
125+
int yHeight,
126+
int uvWidth,
127+
int uvHeight,
128+
SurfaceFormat surfaceFormat
129+
) {
130+
// Allocate YUV GL textures
131+
for (int i = 0; i < 3; i += 1)
132+
{
133+
if (yuvTextures[i] != null)
134+
{
135+
yuvTextures[i].Dispose();
136+
}
137+
}
138+
yuvTextures[0] = new Texture2D(
139+
currentDevice,
140+
yWidth,
141+
yHeight,
142+
false,
143+
surfaceFormat
144+
);
145+
yuvTextures[1] = new Texture2D(
146+
currentDevice,
147+
uvWidth,
148+
uvHeight,
149+
false,
150+
surfaceFormat
151+
);
152+
yuvTextures[2] = new Texture2D(
153+
currentDevice,
154+
uvWidth,
155+
uvHeight,
156+
false,
157+
surfaceFormat
158+
);
159+
160+
// Precalculate the viewport
161+
viewport = new Viewport(0, 0, yWidth, yHeight);
162+
}
163+
164+
protected void GL_pushState()
165+
{
166+
// Begin the effect, flagging to restore previous state on end
167+
FNA3D.FNA3D_BeginPassRestore(
168+
currentDevice.GLDevice,
169+
shaderProgram.glEffect,
170+
stateChangesPtr
171+
);
172+
173+
// Prep our samplers
174+
for (int i = 0; i < 3; i += 1)
175+
{
176+
oldTextures[i] = currentDevice.Textures[i];
177+
oldSamplers[i] = currentDevice.SamplerStates[i];
178+
currentDevice.Textures[i] = yuvTextures[i];
179+
currentDevice.SamplerStates[i] = SamplerState.LinearClamp;
180+
}
181+
182+
// Prep buffers
183+
oldBuffers = currentDevice.GetVertexBuffers();
184+
currentDevice.SetVertexBuffers(vertBuffer);
185+
186+
// Prep target bindings
187+
int oldTargetCount = currentDevice.GetRenderTargetsNoAllocEXT(null);
188+
Array.Resize(ref oldTargets, oldTargetCount);
189+
currentDevice.GetRenderTargetsNoAllocEXT(oldTargets);
190+
191+
unsafe
192+
{
193+
fixed (FNA3D.FNA3D_RenderTargetBinding* rt = &nativeVideoTexture[0])
194+
{
195+
GraphicsDevice.PrepareRenderTargetBindings(
196+
rt,
197+
videoTexture
198+
);
199+
FNA3D.FNA3D_SetRenderTargets(
200+
currentDevice.GLDevice,
201+
rt,
202+
videoTexture.Length,
203+
IntPtr.Zero,
204+
DepthFormat.None,
205+
0
206+
);
207+
}
208+
}
209+
210+
// Prep render state
211+
prevBlend = currentDevice.BlendState;
212+
prevDepthStencil = currentDevice.DepthStencilState;
213+
prevRasterizer = currentDevice.RasterizerState;
214+
currentDevice.BlendState = BlendState.Opaque;
215+
currentDevice.DepthStencilState = DepthStencilState.None;
216+
currentDevice.RasterizerState = RasterizerState.CullNone;
217+
218+
// Prep viewport
219+
prevViewport = currentDevice.Viewport;
220+
FNA3D.FNA3D_SetViewport(
221+
currentDevice.GLDevice,
222+
ref viewport.viewport
223+
);
224+
}
225+
226+
protected void GL_popState()
227+
{
228+
// End the effect, restoring the previous shader state
229+
FNA3D.FNA3D_EndPassRestore(
230+
currentDevice.GLDevice,
231+
shaderProgram.glEffect
232+
);
233+
234+
// Restore GL state
235+
currentDevice.BlendState = prevBlend;
236+
currentDevice.DepthStencilState = prevDepthStencil;
237+
currentDevice.RasterizerState = prevRasterizer;
238+
prevBlend = null;
239+
prevDepthStencil = null;
240+
prevRasterizer = null;
241+
242+
/* Restore targets using GLDevice directly.
243+
* This prevents accidental clearing of previously bound targets.
244+
*/
245+
if (oldTargets == null || oldTargets.Length == 0)
246+
{
247+
FNA3D.FNA3D_SetRenderTargets(
248+
currentDevice.GLDevice,
249+
IntPtr.Zero,
250+
0,
251+
IntPtr.Zero,
252+
DepthFormat.None,
253+
0
254+
);
255+
}
256+
else
257+
{
258+
IRenderTarget oldTarget = oldTargets[0].RenderTarget as IRenderTarget;
259+
260+
unsafe
261+
{
262+
fixed (FNA3D.FNA3D_RenderTargetBinding* rt = &nativeOldTargets[0])
263+
{
264+
GraphicsDevice.PrepareRenderTargetBindings(
265+
rt,
266+
oldTargets
267+
);
268+
FNA3D.FNA3D_SetRenderTargets(
269+
currentDevice.GLDevice,
270+
rt,
271+
oldTargets.Length,
272+
oldTarget.DepthStencilBuffer,
273+
oldTarget.DepthStencilFormat,
274+
(byte) (oldTarget.RenderTargetUsage != RenderTargetUsage.DiscardContents ? 1 : 0) /* lol c# */
275+
);
276+
}
277+
}
278+
}
279+
oldTargets = null;
280+
281+
// Set viewport AFTER setting targets!
282+
FNA3D.FNA3D_SetViewport(
283+
currentDevice.GLDevice,
284+
ref prevViewport.viewport
285+
);
286+
287+
// Restore buffers
288+
currentDevice.SetVertexBuffers(oldBuffers);
289+
oldBuffers = null;
290+
291+
// Restore samplers
292+
currentDevice.Textures.ignoreTargets = true;
293+
for (int i = 0; i < 3; i += 1)
294+
{
295+
/* The application may have set a texture ages
296+
* ago, only to not unset after disposing. We
297+
* have to avoid an ObjectDisposedException!
298+
*/
299+
if (oldTextures[i] == null || !oldTextures[i].IsDisposed)
300+
{
301+
currentDevice.Textures[i] = oldTextures[i];
302+
}
303+
currentDevice.SamplerStates[i] = oldSamplers[i];
304+
oldTextures[i] = null;
305+
oldSamplers[i] = null;
306+
}
307+
currentDevice.Textures.ignoreTargets = false;
308+
}
309+
310+
#endregion
311+
312+
#region Public Member Data: XNA VideoPlayer Implementation
313+
314+
public bool IsDisposed
315+
{
316+
get;
317+
private set;
318+
}
319+
320+
#endregion
321+
322+
#region Private Member Data: XNA VideoPlayer Implementation
323+
324+
// Store this to optimize things on our end.
325+
protected RenderTargetBinding[] videoTexture;
326+
327+
// We need to access the GraphicsDevice frequently.
328+
protected GraphicsDevice currentDevice;
329+
330+
#endregion
331+
332+
#region Private Methods: XNA VideoPlayer Implementation
333+
334+
protected void checkDisposed()
335+
{
336+
if (IsDisposed)
337+
{
338+
throw new ObjectDisposedException("VideoPlayer");
339+
}
340+
}
341+
342+
#endregion
343+
344+
#region Public Methods: XNA VideoPlayer Implementation
345+
346+
protected BaseYUVPlayer()
347+
{
348+
// Initialize public members.
349+
IsDisposed = false;
350+
351+
// Initialize private members.
352+
videoTexture = new RenderTargetBinding[1];
353+
}
354+
355+
public virtual void Dispose()
356+
{
357+
if (IsDisposed)
358+
{
359+
return;
360+
}
361+
362+
// Destroy the other GL bits.
363+
GL_dispose();
364+
365+
// Dispose the Texture.
366+
if (videoTexture[0].RenderTarget != null)
367+
{
368+
videoTexture[0].RenderTarget.Dispose();
369+
}
370+
371+
// Okay, we out.
372+
IsDisposed = true;
373+
}
374+
375+
#endregion
376+
}
377+
}

0 commit comments

Comments
 (0)