initial video support
							parent
							
								
									5a5fbc0c77
								
							
						
					
					
						commit
						845881533b
					
				|  | @ -10,3 +10,6 @@ | |||
| [submodule "lib/WellspringCS"] | ||||
| 	path = lib/WellspringCS | ||||
| 	url = https://gitea.moonside.games/MoonsideGames/WellspringCS.git | ||||
| [submodule "lib/Theorafile"] | ||||
| 	path = lib/Theorafile | ||||
| 	url = https://github.com/FNA-XNA/Theorafile.git | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| 		<ProjectReference Include=".\lib\RefreshCS\RefreshCS.csproj" /> | ||||
| 		<ProjectReference Include=".\lib\FAudio\csharp\FAudio-CS.Core.csproj" /> | ||||
| 		<ProjectReference Include=".\lib\WellspringCS\WellspringCS.csproj" /> | ||||
| 		<ProjectReference Include=".\lib\Theorafile\csharp\Theorafile-CS.Core.csproj" /> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
|  | @ -22,4 +23,13 @@ | |||
| 			<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
| 		</None> | ||||
| 	</ItemGroup> | ||||
| 
 | ||||
| 	<ItemGroup> | ||||
| 		<EmbeddedResource Include="src\Video\Shaders\Compiled\FullscreenVert.spv"> | ||||
| 			<LogicalName>MoonWorks.Shaders.FullscreenVert.spv</LogicalName> | ||||
| 		</EmbeddedResource> | ||||
| 		<EmbeddedResource Include="src\Video\Shaders\Compiled\YUV2RGBAFrag.spv"> | ||||
| 			<LogicalName>MoonWorks.Shaders.YUV2RGBAFrag.spv</LogicalName> | ||||
| 		</EmbeddedResource> | ||||
| 	</ItemGroup> | ||||
| </Project> | ||||
|  |  | |||
|  | @ -15,4 +15,8 @@ | |||
| 	<dllmap dll="Wellspring" os="windows" target="Wellspring.dll"/> | ||||
| 	<dllmap dll="Wellspring" os="osx" target="libWellspring.0.dylib"/> | ||||
| 	<dllmap dll="Wellspring" os="linux,freebsd,netbsd" target="libWellspring.so.0"/> | ||||
| 
 | ||||
| 	<dllmap dll="Theorafile" os="windows" target="libtheorafile.dll"/> | ||||
| 	<dllmap dll="Theorafile" os="osx" target="libtheorafile.dylib"/> | ||||
| 	<dllmap dll="Theorafile" os="linux,freebsd,netbsd" target="libtheorafile.so"/> | ||||
| </configuration> | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| Subproject commit 3ed1726b1e294799e85c3b597b114fb3b21cba72 | ||||
|  | @ -1,6 +1,4 @@ | |||
| using System; | ||||
| using System.Runtime.InteropServices; | ||||
| using MoonWorks.Math; | ||||
| using RefreshCS; | ||||
| 
 | ||||
| namespace MoonWorks.Graphics | ||||
|  | @ -835,6 +833,26 @@ namespace MoonWorks.Graphics | |||
| 			SetTextureData(new TextureSlice(texture), dataPtr, dataLengthInBytes); | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Asynchronously copies YUV data into three textures. Use with compressed video. | ||||
| 		/// </summary> | ||||
| 		public void SetTextureDataYUV(Texture yTexture, Texture uTexture, Texture vTexture, IntPtr dataPtr, uint dataLengthInBytes) | ||||
| 		{ | ||||
| 			Refresh.Refresh_SetTextureDataYUV( | ||||
| 				Device.Handle, | ||||
| 				Handle, | ||||
| 				yTexture.Handle, | ||||
| 				uTexture.Handle, | ||||
| 				vTexture.Handle, | ||||
| 				yTexture.Width, | ||||
| 				yTexture.Height, | ||||
| 				uTexture.Width, | ||||
| 				uTexture.Height, | ||||
| 				dataPtr, | ||||
| 				dataLengthInBytes | ||||
| 			); | ||||
| 		} | ||||
| 
 | ||||
| 		/// <summary> | ||||
| 		/// Performs an asynchronous texture-to-texture copy on the GPU. | ||||
| 		/// </summary> | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using RefreshCS; | ||||
| 
 | ||||
| namespace MoonWorks.Graphics | ||||
|  | @ -8,6 +9,11 @@ namespace MoonWorks.Graphics | |||
| 	{ | ||||
| 		public IntPtr Handle { get; } | ||||
| 
 | ||||
| 		// Built-in video pipeline | ||||
| 		private ShaderModule VideoVertexShader { get; } | ||||
| 		private ShaderModule VideoFragmentShader { get; } | ||||
| 		internal GraphicsPipeline VideoPipeline { get; } | ||||
| 
 | ||||
| 		public bool IsDisposed { get; private set; } | ||||
| 
 | ||||
| 		private readonly List<WeakReference<GraphicsResource>> resources = new List<WeakReference<GraphicsResource>>(); | ||||
|  | @ -28,6 +34,26 @@ namespace MoonWorks.Graphics | |||
| 				presentationParameters, | ||||
| 				Conversions.BoolToByte(debugMode) | ||||
| 			); | ||||
| 
 | ||||
| 			VideoVertexShader = new ShaderModule(this, GetEmbeddedResource("MoonWorks.Shaders.FullscreenVert.spv")); | ||||
| 			VideoFragmentShader = new ShaderModule(this, GetEmbeddedResource("MoonWorks.Shaders.YUV2RGBAFrag.spv")); | ||||
| 
 | ||||
| 			VideoPipeline = new GraphicsPipeline( | ||||
| 				this, | ||||
| 				new GraphicsPipelineCreateInfo | ||||
| 				{ | ||||
| 					AttachmentInfo = new GraphicsPipelineAttachmentInfo( | ||||
| 						new ColorAttachmentDescription(TextureFormat.R8G8B8A8, ColorAttachmentBlendState.None) | ||||
| 					), | ||||
| 					DepthStencilState = DepthStencilState.Disable, | ||||
| 					VertexShaderInfo = GraphicsShaderInfo.Create(VideoVertexShader, "main", 0), | ||||
| 					FragmentShaderInfo = GraphicsShaderInfo.Create(VideoFragmentShader, "main", 3), | ||||
| 					VertexInputState = VertexInputState.Empty, | ||||
| 					RasterizerState = RasterizerState.CCW_CullNone, | ||||
| 					PrimitiveType = PrimitiveType.TriangleList, | ||||
| 					MultisampleState = MultisampleState.None | ||||
| 				} | ||||
| 			); | ||||
| 		} | ||||
| 
 | ||||
| 		public CommandBuffer AcquireCommandBuffer() | ||||
|  | @ -77,6 +103,11 @@ namespace MoonWorks.Graphics | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private static Stream GetEmbeddedResource(string name) | ||||
| 		{ | ||||
| 			return typeof(GraphicsDevice).Assembly.GetManifestResourceStream(name); | ||||
| 		} | ||||
| 
 | ||||
| 		protected virtual void Dispose(bool disposing) | ||||
| 		{ | ||||
| 			if (!IsDisposed) | ||||
|  |  | |||
|  | @ -196,6 +196,7 @@ namespace MoonWorks | |||
| 			NativeLibrary.SetDllImportResolver(typeof(RefreshCS.Refresh).Assembly, MapAndLoad); | ||||
| 			NativeLibrary.SetDllImportResolver(typeof(FAudio).Assembly, MapAndLoad); | ||||
| 			NativeLibrary.SetDllImportResolver(typeof(WellspringCS.Wellspring).Assembly, MapAndLoad); | ||||
| 			NativeLibrary.SetDllImportResolver(typeof(Theorafile).Assembly, MapAndLoad); | ||||
| 		} | ||||
| 
 | ||||
| 		#endregion | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,9 @@ | |||
| #version 450 | ||||
| 
 | ||||
| layout(location = 0) out vec2 outTexCoord; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
| 	outTexCoord = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); | ||||
| 	gl_Position = vec4(outTexCoord * 2.0 - 1.0, 0.0, 1.0); | ||||
| } | ||||
|  | @ -0,0 +1,38 @@ | |||
| /* | ||||
|  * This effect is based on the YUV-to-RGBA GLSL shader found in SDL. | ||||
|  * Thus, it also released under the zlib license: | ||||
|  * http://libsdl.org/license.php | ||||
|  */ | ||||
| #version 450 | ||||
| 
 | ||||
| layout(location = 0) in vec2 TexCoord; | ||||
| 
 | ||||
| layout(location = 0) out vec4 FragColor; | ||||
| 
 | ||||
| layout(binding = 0, set = 1) uniform sampler2D YSampler; | ||||
| layout(binding = 1, set = 1) uniform sampler2D USampler; | ||||
| layout(binding = 2, set = 1) uniform sampler2D VSampler; | ||||
| 
 | ||||
| /* More info about colorspace conversion: | ||||
|  * http://www.equasys.de/colorconversion.html | ||||
|  * http://www.equasys.de/colorformat.html | ||||
|  */ | ||||
| 
 | ||||
| const vec3 offset = vec3(-0.0625, -0.5, -0.5); | ||||
| const vec3 Rcoeff = vec3(1.164,  0.000,  1.793); | ||||
| const vec3 Gcoeff = vec3(1.164, -0.213, -0.533); | ||||
| const vec3 Bcoeff = vec3(1.164,  2.112,  0.000); | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
| 	vec3 yuv; | ||||
| 	yuv.x = texture(YSampler, TexCoord).r; | ||||
| 	yuv.y = texture(USampler, TexCoord).r; | ||||
| 	yuv.z = texture(VSampler, TexCoord).r; | ||||
| 	yuv += offset; | ||||
| 
 | ||||
| 	FragColor.r = dot(yuv, Rcoeff); | ||||
| 	FragColor.g = dot(yuv, Gcoeff); | ||||
| 	FragColor.b = dot(yuv, Bcoeff); | ||||
| 	FragColor.a = 1.0; | ||||
| } | ||||
|  | @ -0,0 +1,94 @@ | |||
| /* Heavily based on https://github.com/FNA-XNA/FNA/blob/master/src/Media/Xiph/Video.cs */ | ||||
| using System; | ||||
| 
 | ||||
| namespace MoonWorks.Video | ||||
| { | ||||
| 	public class Video : IDisposable | ||||
| 	{ | ||||
| 		public IntPtr Handle => handle; | ||||
| 		public int YWidth => yWidth; | ||||
| 		public int YHeight => yHeight; | ||||
| 		public int UVWidth => uvWidth; | ||||
| 		public int UVHeight => uvHeight; | ||||
| 		public double FramesPerSecond => fps; | ||||
| 
 | ||||
| 		private IntPtr handle; | ||||
| 		private int yWidth; | ||||
| 		private int yHeight; | ||||
| 		private int uvWidth; | ||||
| 		private int uvHeight; | ||||
| 		private double fps; | ||||
| 
 | ||||
| 		private bool disposed; | ||||
| 
 | ||||
| 		public Video(string filename) | ||||
| 		{ | ||||
| 			Theorafile.th_pixel_fmt format; | ||||
| 
 | ||||
| 			if (!System.IO.File.Exists(filename)) | ||||
| 			{ | ||||
| 				throw new ArgumentException("Video file not found!"); | ||||
| 			} | ||||
| 
 | ||||
| 			if (Theorafile.tf_fopen(filename, out handle) < 0) | ||||
| 			{ | ||||
| 				throw new ArgumentException("Invalid video file!"); | ||||
| 			} | ||||
| 
 | ||||
| 			Theorafile.tf_videoinfo( | ||||
| 				handle, | ||||
| 				out yWidth, | ||||
| 				out yHeight, | ||||
| 				out fps, | ||||
| 				out format | ||||
| 			); | ||||
| 
 | ||||
| 			if (format == Theorafile.th_pixel_fmt.TH_PF_420) | ||||
| 			{ | ||||
| 				uvWidth = yWidth / 2; | ||||
| 				uvHeight = yHeight / 2; | ||||
| 			} | ||||
| 			else if (format == Theorafile.th_pixel_fmt.TH_PF_422) | ||||
| 			{ | ||||
| 				uvWidth = yWidth / 2; | ||||
| 				uvHeight = yHeight; | ||||
| 			} | ||||
| 			else if (format == Theorafile.th_pixel_fmt.TH_PF_444) | ||||
| 			{ | ||||
| 				uvWidth = yWidth; | ||||
| 				uvHeight = yHeight; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				throw new NotSupportedException("Unrecognized YUV format!"); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		protected virtual void Dispose(bool disposing) | ||||
| 		{ | ||||
| 			if (!disposed) | ||||
| 			{ | ||||
| 				if (disposing) | ||||
| 				{ | ||||
| 					// TODO: dispose managed state (managed objects) | ||||
| 				} | ||||
| 
 | ||||
| 				Theorafile.tf_close(ref handle); | ||||
| 				disposed = true; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		~Video() | ||||
| 		{ | ||||
| 		    // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||||
| 		    Dispose(disposing: false); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Dispose() | ||||
| 		{ | ||||
| 			// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||||
| 			Dispose(disposing: true); | ||||
| 			GC.SuppressFinalize(this); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,285 @@ | |||
| /* Heavily based on https://github.com/FNA-XNA/FNA/blob/master/src/Media/Xiph/VideoPlayer.cs */ | ||||
| using System; | ||||
| using System.Diagnostics; | ||||
| using System.Runtime.InteropServices; | ||||
| using MoonWorks.Graphics; | ||||
| 
 | ||||
| namespace MoonWorks.Video | ||||
| { | ||||
| 	public enum VideoState | ||||
| 	{ | ||||
| 		Playing, | ||||
| 		Paused, | ||||
| 		Stopped | ||||
| 	} | ||||
| 
 | ||||
| 	public unsafe class VideoPlayer : IDisposable | ||||
| 	{ | ||||
| 		public bool Loop { get; set; } | ||||
| 		public bool Mute { get; set; } | ||||
| 		public float Volume { get; set; } | ||||
| 
 | ||||
| 		private Video Video = null; | ||||
| 		private VideoState State = VideoState.Stopped; | ||||
| 
 | ||||
| 		private void* yuvData = null; | ||||
| 		private int yuvDataLength; | ||||
| 		private int currentFrame; | ||||
| 
 | ||||
| 		private GraphicsDevice GraphicsDevice; | ||||
| 		private Texture RenderTexture = null; | ||||
| 		private Texture[] YUVTextures = new Texture[3]; | ||||
| 		private Sampler LinearSampler; | ||||
| 
 | ||||
| 		private Stopwatch timer; | ||||
| 
 | ||||
| 		private bool disposed; | ||||
| 
 | ||||
| 		public VideoPlayer(GraphicsDevice graphicsDevice) | ||||
| 		{ | ||||
| 			GraphicsDevice = graphicsDevice; | ||||
| 			timer = new Stopwatch(); | ||||
| 
 | ||||
| 			LinearSampler = new Sampler(GraphicsDevice, SamplerCreateInfo.LinearClamp); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Load(Video video) | ||||
| 		{ | ||||
| 			Video = video; | ||||
| 			State = VideoState.Stopped; | ||||
| 
 | ||||
| 			if (yuvData != null) | ||||
| 			{ | ||||
| 				NativeMemory.Free(yuvData); | ||||
| 			} | ||||
| 
 | ||||
| 			yuvDataLength = ( | ||||
| 				(Video.YWidth * Video.YHeight) + | ||||
| 				(Video.UVWidth * video.UVHeight * 2) | ||||
| 			); | ||||
| 
 | ||||
| 			yuvData = NativeMemory.Alloc((nuint) yuvDataLength); | ||||
| 
 | ||||
| 			InitializeTheoraStream(); | ||||
| 
 | ||||
| 			if (Theorafile.tf_hasvideo(Video.Handle) == 1) | ||||
| 			{ | ||||
| 				if (RenderTexture != null) | ||||
| 				{ | ||||
| 					RenderTexture.Dispose(); | ||||
| 				} | ||||
| 
 | ||||
| 				RenderTexture = Texture.CreateTexture2D( | ||||
| 					GraphicsDevice, | ||||
| 					(uint) Video.YWidth, | ||||
| 					(uint) Video.YHeight, | ||||
| 					TextureFormat.R8G8B8A8, | ||||
| 					TextureUsageFlags.ColorTarget | TextureUsageFlags.Sampler | ||||
| 				); | ||||
| 
 | ||||
| 				for (int i = 0; i < 3; i += 1) | ||||
| 				{ | ||||
| 					if (YUVTextures[i] != null) | ||||
| 					{ | ||||
| 						YUVTextures[i].Dispose(); | ||||
| 					} | ||||
| 				} | ||||
| 
 | ||||
| 				YUVTextures[0] = Texture.CreateTexture2D( | ||||
| 					GraphicsDevice, | ||||
| 					(uint) Video.YWidth, | ||||
| 					(uint) Video.YHeight, | ||||
| 					TextureFormat.R8, | ||||
| 					TextureUsageFlags.Sampler | ||||
| 				); | ||||
| 
 | ||||
| 				YUVTextures[1] = Texture.CreateTexture2D( | ||||
| 					GraphicsDevice, | ||||
| 					(uint) Video.UVWidth, | ||||
| 					(uint) Video.UVHeight, | ||||
| 					TextureFormat.R8, | ||||
| 					TextureUsageFlags.Sampler | ||||
| 				); | ||||
| 
 | ||||
| 				YUVTextures[2] = Texture.CreateTexture2D( | ||||
| 					GraphicsDevice, | ||||
| 					(uint) Video.UVWidth, | ||||
| 					(uint) Video.UVHeight, | ||||
| 					TextureFormat.R8, | ||||
| 					TextureUsageFlags.Sampler | ||||
| 				); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		public void Play(bool loop = false) | ||||
| 		{ | ||||
| 			if (State == VideoState.Playing) | ||||
| 			{ | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			Loop = loop; | ||||
| 			timer.Start(); | ||||
| 
 | ||||
| 			State = VideoState.Playing; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Pause() | ||||
| 		{ | ||||
| 			if (State == VideoState.Paused) | ||||
| 			{ | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			timer.Stop(); | ||||
| 
 | ||||
| 			State = VideoState.Paused; | ||||
| 		} | ||||
| 
 | ||||
| 		public void Stop() | ||||
| 		{ | ||||
| 			if (State == VideoState.Stopped) | ||||
| 			{ | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			timer.Stop(); | ||||
| 			timer.Reset(); | ||||
| 
 | ||||
| 			Theorafile.tf_reset(Video.Handle); | ||||
| 
 | ||||
| 			State = VideoState.Stopped; | ||||
| 		} | ||||
| 
 | ||||
| 		public Texture GetTexture() | ||||
| 		{ | ||||
| 			if (Video == null) | ||||
| 			{ | ||||
| 				throw new InvalidOperationException(); | ||||
| 			} | ||||
| 
 | ||||
| 			if (State == VideoState.Stopped || Video.Handle == IntPtr.Zero || Theorafile.tf_hasvideo(Video.Handle) == 0) | ||||
| 			{ | ||||
| 				return RenderTexture; | ||||
| 			} | ||||
| 
 | ||||
| 			int thisFrame = (int) (timer.Elapsed.TotalMilliseconds / (1000.0 / Video.FramesPerSecond)); | ||||
| 			if (thisFrame > currentFrame) | ||||
| 			{ | ||||
| 				if (Theorafile.tf_readvideo( | ||||
| 					Video.Handle, | ||||
| 					(IntPtr) yuvData, | ||||
| 					thisFrame - currentFrame | ||||
| 				) == 1 || currentFrame == -1) { | ||||
| 					UpdateTexture(); | ||||
| 				} | ||||
| 
 | ||||
| 				currentFrame = thisFrame; | ||||
| 			} | ||||
| 
 | ||||
| 			bool ended = Theorafile.tf_eos(Video.Handle) == 1; | ||||
| 			if (ended) | ||||
| 			{ | ||||
| 				timer.Stop(); | ||||
| 				timer.Reset(); | ||||
| 
 | ||||
| 				Theorafile.tf_reset(Video.Handle); | ||||
| 
 | ||||
| 				if (Loop) | ||||
| 				{ | ||||
| 					// Start over! | ||||
| 					InitializeTheoraStream(); | ||||
| 
 | ||||
| 					timer.Start(); | ||||
| 
 | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					State = VideoState.Stopped; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			return RenderTexture; | ||||
| 		} | ||||
| 
 | ||||
| 		private void InitializeTheoraStream() | ||||
| 		{ | ||||
| 			// Grab the first video frame ASAP. | ||||
| 			while (Theorafile.tf_readvideo(Video.Handle, (IntPtr) yuvData, 1) == 0); | ||||
| 
 | ||||
| 			// Grab the first bit of audio. We're trying to start the decoding ASAP. | ||||
| 			if (Theorafile.tf_hasaudio(Video.Handle) == 1) | ||||
| 			{ | ||||
| 				int channels, samplerate; | ||||
| 				Theorafile.tf_audioinfo(Video.Handle, out channels, out samplerate); | ||||
| 
 | ||||
| 				// TODO: audio stream | ||||
| 			} | ||||
| 
 | ||||
| 			currentFrame = -1; | ||||
| 		} | ||||
| 
 | ||||
| 		private void UpdateTexture() | ||||
| 		{ | ||||
| 			var commandBuffer = GraphicsDevice.AcquireCommandBuffer(); | ||||
| 
 | ||||
| 			commandBuffer.SetTextureDataYUV( | ||||
| 				YUVTextures[0], | ||||
| 				YUVTextures[1], | ||||
| 				YUVTextures[2], | ||||
| 				(IntPtr) yuvData, | ||||
| 				(uint) yuvDataLength | ||||
| 			); | ||||
| 
 | ||||
| 			commandBuffer.BeginRenderPass( | ||||
| 				new ColorAttachmentInfo(RenderTexture, Color.Black) | ||||
| 			); | ||||
| 
 | ||||
| 			commandBuffer.BindGraphicsPipeline(GraphicsDevice.VideoPipeline); | ||||
| 			commandBuffer.BindFragmentSamplers( | ||||
| 				new TextureSamplerBinding(YUVTextures[0], LinearSampler), | ||||
| 				new TextureSamplerBinding(YUVTextures[1], LinearSampler), | ||||
| 				new TextureSamplerBinding(YUVTextures[2], LinearSampler) | ||||
| 			); | ||||
| 
 | ||||
| 			commandBuffer.DrawPrimitives(0, 1, 0, 0); | ||||
| 
 | ||||
| 			commandBuffer.EndRenderPass(); | ||||
| 
 | ||||
| 			GraphicsDevice.Submit(commandBuffer); | ||||
| 		} | ||||
| 
 | ||||
| 		protected virtual void Dispose(bool disposing) | ||||
| 		{ | ||||
| 			if (!disposed) | ||||
| 			{ | ||||
| 				if (disposing) | ||||
| 				{ | ||||
| 					// dispose managed state (managed objects) | ||||
| 					RenderTexture.Dispose(); | ||||
| 					YUVTextures[0].Dispose(); | ||||
| 					YUVTextures[1].Dispose(); | ||||
| 					YUVTextures[2].Dispose(); | ||||
| 				} | ||||
| 
 | ||||
| 				// free unmanaged resources (unmanaged objects) and override finalizer | ||||
| 
 | ||||
| 				NativeMemory.Free(yuvData); | ||||
| 				disposed = true; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		~VideoPlayer() | ||||
| 		{ | ||||
| 		    // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||||
| 		    Dispose(disposing: false); | ||||
| 		} | ||||
| 
 | ||||
| 		public void Dispose() | ||||
| 		{ | ||||
| 			// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method | ||||
| 			Dispose(disposing: true); | ||||
| 			GC.SuppressFinalize(this); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in New Issue