diff --git a/Camera-Renderer_cs b/Camera-Renderer_cs
deleted file mode 160000
index ab141da..0000000
--- a/Camera-Renderer_cs
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit ab141dac9c1a811b032598de458b54d518fa0e71
diff --git a/Camera-Renderer_cs/.gitattributes b/Camera-Renderer_cs/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/Camera-Renderer_cs/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/Camera-Renderer_cs/.gitignore b/Camera-Renderer_cs/.gitignore
new file mode 100644
index 0000000..cdc05b2
--- /dev/null
+++ b/Camera-Renderer_cs/.gitignore
@@ -0,0 +1,365 @@
+# ---> VisualStudio
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Oo]ut/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
diff --git a/Camera-Renderer_cs/App.xaml b/Camera-Renderer_cs/App.xaml
new file mode 100644
index 0000000..10701b2
--- /dev/null
+++ b/Camera-Renderer_cs/App.xaml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/Camera-Renderer_cs/App.xaml.cs b/Camera-Renderer_cs/App.xaml.cs
new file mode 100644
index 0000000..65b56a3
--- /dev/null
+++ b/Camera-Renderer_cs/App.xaml.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Activation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+namespace CameraRendererCS
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ sealed partial class App : Application
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ this.Suspending += OnSuspending;
+ }
+
+ ///
+ /// Invoked when the application is launched normally by the end user. Other entry points
+ /// will be used such as when the application is launched to open a specific file.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnLaunched(LaunchActivatedEventArgs e)
+ {
+ Frame rootFrame = Window.Current.Content as Frame;
+
+ // Do not repeat app initialization when the Window already has content,
+ // just ensure that the window is active
+ if (rootFrame == null)
+ {
+ // Create a Frame to act as the navigation context and navigate to the first page
+ rootFrame = new Frame();
+
+ rootFrame.NavigationFailed += OnNavigationFailed;
+
+ if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
+ {
+ //TODO: Load state from previously suspended application
+ }
+
+ // Place the frame in the current Window
+ Window.Current.Content = rootFrame;
+ }
+
+ if (e.PrelaunchActivated == false)
+ {
+ if (rootFrame.Content == null)
+ {
+ // When the navigation stack isn't restored navigate to the first page,
+ // configuring the new page by passing required information as a navigation
+ // parameter
+ rootFrame.Navigate(typeof(MainPage), e.Arguments);
+ }
+ // Ensure the current window is active
+ Window.Current.Activate();
+ }
+ }
+
+ ///
+ /// Invoked when Navigation to a certain page fails
+ ///
+ /// The Frame which failed navigation
+ /// Details about the navigation failure
+ void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
+ {
+ throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
+ }
+
+ ///
+ /// Invoked when application execution is being suspended. Application state is saved
+ /// without knowing whether the application will be terminated or resumed with the contents
+ /// of memory still intact.
+ ///
+ /// The source of the suspend request.
+ /// Details about the suspend request.
+ private void OnSuspending(object sender, SuspendingEventArgs e)
+ {
+ var deferral = e.SuspendingOperation.GetDeferral();
+ //TODO: Save application state and stop any background activity
+ deferral.Complete();
+ }
+ }
+}
diff --git a/Camera-Renderer_cs/Camera Renderer CS.csproj b/Camera-Renderer_cs/Camera Renderer CS.csproj
new file mode 100644
index 0000000..6849c82
--- /dev/null
+++ b/Camera-Renderer_cs/Camera Renderer CS.csproj
@@ -0,0 +1,174 @@
+
+
+
+
+ Debug
+ x86
+ {33A294C7-3146-405C-BCCA-C4E36985D0D7}
+ AppContainerExe
+ Properties
+ CameraDirect
+ CameraDirect
+ en-US
+ UAP
+ 10.0.19041.0
+ 10.0.17763.0
+ 14
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ true
+ false
+ False
+ True
+ True
+ Always
+ x86|x64|arm
+ 0
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x86
+ false
+ prompt
+ true
+
+
+ bin\x86\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ bin\ARM\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ bin\ARM64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM64
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ bin\x64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+ true
+
+
+ PackageReference
+
+
+
+ App.xaml
+
+
+ MainPage.xaml
+
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+ 6.2.12
+
+
+
+ 14.0
+
+
+
+
\ No newline at end of file
diff --git a/Camera-Renderer_cs/MainPage.xaml b/Camera-Renderer_cs/MainPage.xaml
new file mode 100644
index 0000000..744d3ef
--- /dev/null
+++ b/Camera-Renderer_cs/MainPage.xaml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Camera-Renderer_cs/MainPage.xaml.cs b/Camera-Renderer_cs/MainPage.xaml.cs
new file mode 100644
index 0000000..d099dae
--- /dev/null
+++ b/Camera-Renderer_cs/MainPage.xaml.cs
@@ -0,0 +1,554 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+using Windows.UI.Core;
+using Windows.Media.Capture;
+using Windows.ApplicationModel;
+using System.Threading.Tasks;
+using Windows.System.Display;
+using Windows.Graphics.Display;
+using Windows.Media.Capture.Frames;
+using Windows.Media.Devices;
+using Windows.Devices.Enumeration;
+using Windows.Media.Audio;
+using Windows.Media.Render;
+using Windows.Media.MediaProperties;
+using Windows.Graphics.Imaging;
+using System.Threading;
+using Windows.UI.Xaml.Media.Imaging;
+using System.Diagnostics;
+using System.Collections.Concurrent;
+
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
+
+namespace CameraRendererCS
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MainPage : Page
+ {
+
+ MediaCapture mediaCapture;
+ MediaFrameReader frameReader;
+ bool isStreamRunning = false;
+ bool calcPerfStats = true;
+ DisplayRequest displayRequest = new DisplayRequest();
+ bool _taskRunning = false;
+ private SoftwareBitmap _backBuffer = new SoftwareBitmap(BitmapPixelFormat.Rgba8, 1, 1);
+
+ AudioGraph graph;
+
+ // Populated lists
+ DeviceInformationCollection audioDevices;
+ IReadOnlyList videoDevices;
+
+ public MainPage()
+ {
+ this.InitializeComponent();
+ visualDecayTimer = new Timer((t) =>
+ {
+ fadeWindow();
+ });
+ }
+
+ protected override async void OnNavigatedTo(NavigationEventArgs e)
+ {
+ // Grab the list of video media sources and show them to the user
+ await FindVideoSources();
+ await FindAudioSources();
+ // Grab the audio sources too
+ }
+
+ private async Task FindVideoSources()
+ {
+ videoComboBox.Items.Clear();
+ videoDevices = await MediaFrameSourceGroup.FindAllAsync();
+ videoComboBox.Items.Add("-- Pick input device --");
+ foreach (MediaFrameSourceGroup device in videoDevices)
+ {
+ videoComboBox.Items.Add(device.DisplayName);
+ }
+ if (videoComboBox.SelectedIndex == -1 && videoComboBox.Items.Count > 0) videoComboBox.SelectedIndex = 0;
+ }
+
+ private async Task FindAudioSources()
+ {
+ audioComboBox.Items.Clear();
+ audioDevices = await DeviceInformation.FindAllAsync(MediaDevice.GetAudioCaptureSelector());
+ audioComboBox.Items.Add("-- Pick input device --");
+ foreach (DeviceInformation device in audioDevices)
+ {
+ audioComboBox.Items.Add(device.Name);
+ }
+ if (audioComboBox.SelectedIndex == -1 && audioComboBox.Items.Count > 0) audioComboBox.SelectedIndex = 0;
+ }
+ private async void startStreamButton_Click(object sender, RoutedEventArgs e)
+ {
+
+ await CreateVideoReader();
+ await CreateAudioGraph();
+ displayRequest.RequestActive();
+ }
+
+ private async Task CreateAudioGraph()
+ {
+ AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Media)
+ {
+ //settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency;
+ PrimaryRenderDevice = await DeviceInformation.CreateFromIdAsync(MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default))
+ };
+
+ CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);
+ graph = result.Graph;
+
+ if (result.Status != AudioGraphCreationStatus.Success)
+ {
+ return;
+ }
+ CreateAudioDeviceOutputNodeResult deviceOutputNodeResult = await graph.CreateDeviceOutputNodeAsync();
+ if (deviceOutputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
+ {
+ return;
+ }
+
+ CreateAudioDeviceInputNodeResult deviceInputNodeResult = await graph.CreateDeviceInputNodeAsync(MediaCategory.Other, graph.EncodingProperties, audioDevices[audioComboBox.SelectedIndex - 1]);
+
+ if (deviceInputNodeResult.Status != AudioDeviceNodeCreationStatus.Success)
+ {
+ return;
+ }
+ deviceInputNodeResult.DeviceInputNode.AddOutgoingConnection(deviceOutputNodeResult.DeviceOutputNode);
+ graph.Start();
+ }
+
+ private async Task CreateVideoReader()
+ {
+ MediaFrameFormat format = ((FrameFormatModel)videoTypeComboBox.SelectedItem).Format;
+ MediaFrameSource source = (MediaFrameSource)videoStreamComboBox.SelectedItem;
+
+
+ // Look for a format which the FrameRenderer can render.
+ string requestedSubtype = null;
+ requestedSubtype = GetSubtypeForFrameReader(source.Info.SourceKind, format);
+ if (requestedSubtype != null)
+ {
+ // Tell the source to use the format we can render.
+ await source.SetFormatAsync(format);
+ }
+ if (requestedSubtype == null)
+ {
+ }
+
+ Task t = new Task(async () =>
+ {
+
+ try
+ {
+ frameReader = await mediaCapture.CreateFrameReaderAsync(source, requestedSubtype);
+ frameReader.AcquisitionMode = MediaFrameReaderAcquisitionMode.Realtime;
+ frameReader.FrameArrived += FrameReader_FrameArrived;
+ lastFrameTime = Stopwatch.GetTimestamp();
+ MediaFrameReaderStartStatus status = await frameReader.StartAsync();
+ if (status == MediaFrameReaderStartStatus.Success)
+ {
+
+ }
+ else
+ {
+ }
+ }
+ catch { }
+
+ });
+ try
+ {
+ t.Start();
+ isStreamRunning = true;
+ if (!mouseInside)
+ {
+ _ = visualDecayTimer.Change(15000, Timeout.Infinite);
+ }
+
+ }
+ catch (InvalidOperationException e)
+ {
+ Console.WriteLine(e.Message);
+ Console.WriteLine("Media Source Info: {0}", source.Info.ToString());
+ }
+ }
+ long lastFrameTime = 0;
+ double averageFrameTime = -1.0;
+ double lastFrameTimeMaximum = 0;
+ // Time taken to process the frame, in delta-ticks
+ long frameProcessingTime = 0;
+ TaskFactory perfTaskFactory = new TaskFactory(TaskCreationOptions.None, TaskContinuationOptions.None);
+
+ // Use a concurrent queue to ensure synchronisation
+ ConcurrentQueue previousFrameTimes = new ConcurrentQueue();
+
+ private void FrameReader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
+ {
+ long newTime = Stopwatch.GetTimestamp();
+ if (calcPerfStats)
+ {
+ // Build the new task, to help fire off frames quickly
+ _ = perfTaskFactory.StartNew(() =>
+ {
+ // TODO: Push the calculation onto a different thread, as it isn't needed here. May save some time as this function takes ~6 ms to execute
+ // Most of this time is likely spent converting the bitmap
+ if (previousFrameTimes.Count >= 180)
+ {
+ _ = previousFrameTimes.TryDequeue(out _);
+ }
+ string deltaTime = "";
+ if (averageFrameTime < 0)
+ {
+ averageFrameTime = (newTime - lastFrameTime) / (double)TimeSpan.TicksPerMillisecond;
+ }
+ else
+ {
+ double newVal = (newTime - lastFrameTime) / (double)TimeSpan.TicksPerMillisecond;
+
+ deltaTime = (averageFrameTime - newVal).ToString("N1");
+ previousFrameTimes.Enqueue(newVal);
+ double localMaxIn = previousFrameTimes.Max();
+ _ = Interlocked.Exchange(ref lastFrameTimeMaximum, localMaxIn);
+ double sampledAverage = previousFrameTimes.Sum() / previousFrameTimes.Count();
+ /*double localAvg = averageFrameTime * 9;
+ localAvg += newVal;
+ localAvg /= 10;*/
+ _ = Interlocked.Exchange(ref averageFrameTime, sampledAverage);
+ }
+ string time = averageFrameTime.ToString("N1");
+ _ = Interlocked.Exchange(ref lastFrameTime, newTime);
+
+ var task = renderTarget.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ timelabel.Text = time;
+ timelabel_delta.Text = deltaTime;
+ maxTimeLabel.Text = lastFrameTimeMaximum.ToString("N2");
+ // Delegate the double-precision division till this function
+ frameProcessingTimeLabel.Text = (frameProcessingTime / (double)TimeSpan.TicksPerMillisecond).ToString("N2");
+ });
+ });
+ }
+ // TryAcquireLatestFrame will return the latest frame that has not yet been acquired.
+ // This can return null if there is no such frame, or if the reader is not in the
+ // "Started" state. The latter can occur if a FrameArrived event was in flight
+ // when the reader was stopped.
+ using (MediaFrameReference frame = sender.TryAcquireLatestFrame())
+ {
+ if (frame != null)
+ {
+ SoftwareBitmap result = null;
+ using (SoftwareBitmap inputBitmap = frame.VideoMediaFrame.SoftwareBitmap)
+ {
+ if (inputBitmap != null)
+ {
+ // XAML requires Bgra8 with premultiplied alpha.
+ // We requested Bgra8 from the MediaFrameReader, so all that's
+ // left is fixing the alpha channel if necessary.
+ if (inputBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8)
+ {
+ //
+ int i = 0;
+ }
+ else if (inputBitmap.BitmapAlphaMode == BitmapAlphaMode.Premultiplied)
+ {
+ // Already in the correct format.
+ result = SoftwareBitmap.Copy(inputBitmap);
+ //result = inputBitmap;
+ }
+ else
+ {
+ // Convert to premultiplied alpha.
+ result = SoftwareBitmap.Convert(inputBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore);
+ }
+ }
+ }
+
+ if (result != null)
+ {
+ // Swap the processed frame to _backBuffer and trigger UI thread to render it
+ result = Interlocked.Exchange(ref _backBuffer, result);
+
+ // UI thread always reset _backBuffer before using it. Unused bitmap should be disposed.
+ result?.Dispose();
+ // Changes to xaml ImageElement must happen in UI thread through Dispatcher
+ var task = renderTarget.Dispatcher.RunAsync(CoreDispatcherPriority.High,
+ async () =>
+ {
+ var imageSource = (SoftwareBitmapSource)renderTarget.Source;
+ // Don't let two copies of this task run at the same time.
+ if (_taskRunning)
+ {
+ return;
+ }
+ _taskRunning = true;
+
+ // Keep draining frames from the backbuffer until the backbuffer is empty.
+ SoftwareBitmap latestBitmap;
+ // About to push a frame; get the delta at this point
+ long thisFrameProcessTime = Stopwatch.GetTimestamp() - newTime;
+ _ = Interlocked.Exchange(ref frameProcessingTime, thisFrameProcessTime);
+
+ while ((latestBitmap = Interlocked.Exchange(ref _backBuffer, null)) != null)
+ {
+
+ await ((SoftwareBitmapSource)renderTarget.Source).SetBitmapAsync(latestBitmap);
+ latestBitmap.Dispose();
+ }
+
+ _taskRunning = false;
+ });
+ }
+ }
+ }
+ }
+
+ public static string GetSubtypeForFrameReader(MediaFrameSourceKind kind, MediaFrameFormat format)
+ {
+ // Note that media encoding subtypes may differ in case.
+ // https://docs.microsoft.com/en-us/uwp/api/Windows.Media.MediaProperties.MediaEncodingSubtypes
+ string subtype = format.Subtype;
+ switch (kind)
+ {
+ // For color sources, we accept anything and request that it be converted to Bgra8.
+ case MediaFrameSourceKind.Color:
+ return MediaEncodingSubtypes.Bgra8;
+
+ // The only depth format we can render is D16.
+ case MediaFrameSourceKind.Depth:
+ return String.Equals(subtype, MediaEncodingSubtypes.D16, StringComparison.OrdinalIgnoreCase) ? subtype : null;
+
+ // The only infrared formats we can render are Nv12, L8, and L16.
+ case MediaFrameSourceKind.Infrared:
+ return (String.Equals(subtype, MediaEncodingSubtypes.Nv12, StringComparison.OrdinalIgnoreCase) ||
+ String.Equals(subtype, MediaEncodingSubtypes.L8, StringComparison.OrdinalIgnoreCase) ||
+ String.Equals(subtype, MediaEncodingSubtypes.L16, StringComparison.OrdinalIgnoreCase)) ? subtype : null;
+
+ // No other source kinds are supported by this class.
+ default:
+ return null;
+ }
+ }
+ bool flyOutState = true;
+ private void CollapseButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (flyOutState)
+ {
+ FlyoutGrid.RowDefinitions[0].Height = new GridLength(0);
+ FlyoutGrid.Width = CollapseButton.Width;
+ CollapseButton.Content = "";
+ flyOutState = false;
+ }
+ else
+ {
+ FlyoutGrid.RowDefinitions[0].Height = GridLength.Auto;
+ CollapseButton.Content = "";
+
+ FlyoutGrid.Width = double.NaN;
+ flyOutState = true;
+ }
+ }
+
+ // Time taken to decay
+ Timer visualDecayTimer;
+ bool mouseInside = false;
+
+ private void Page_PointerMoved(object sender, PointerRoutedEventArgs e)
+ {
+ if (isStreamRunning & !mouseInside)
+ {
+ var task = renderTarget.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
+ {
+ ScalarTransition fade = new ScalarTransition
+ {
+ Duration = TimeSpan.FromSeconds(0)
+ };
+ FlyoutGrid.OpacityTransition = fade;
+ FlyoutGrid.Opacity = 1.0;
+ });
+ visualDecayTimer.Change(5000, Timeout.Infinite);
+ }
+ }
+
+ private void fadeWindow()
+ {
+ var task = renderTarget.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () =>
+ {
+ ScalarTransition fade = new ScalarTransition
+ {
+ Duration = TimeSpan.FromSeconds(5)
+ };
+ FlyoutGrid.OpacityTransition = fade;
+ FlyoutGrid.Opacity = 0.01;
+ });
+ }
+
+ private void FlyoutGrid_PointerEntered(object sender, PointerRoutedEventArgs e)
+ {
+ mouseInside = true;
+ }
+
+ private void FlyoutGrid_PointerExited(object sender, PointerRoutedEventArgs e)
+ {
+ mouseInside = false;
+ }
+
+ private async void videoComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ // Deregister and desroy an existing player
+ if (frameReader != null)
+ {
+ frameReader.FrameArrived -= FrameReader_FrameArrived;
+ frameReader.Dispose();
+ frameReader = null;
+ }
+
+
+ // reset items
+ mediaCapture?.Dispose();
+ mediaCapture = null;
+ if (videoComboBox.SelectedIndex > 0)
+ {
+ // Blank the existing canvas
+ renderTarget.Source = new SoftwareBitmapSource();
+ // Initialise a new MediaCapture
+ mediaCapture = new MediaCapture();
+ MediaCaptureInitializationSettings settings = new MediaCaptureInitializationSettings
+ {
+ SourceGroup = videoDevices[videoComboBox.SelectedIndex - 1],
+
+ // This media capture can share streaming with other apps.
+ SharingMode = MediaCaptureSharingMode.ExclusiveControl,
+
+ // Only stream video and don't initialize audio capture devices.
+ StreamingCaptureMode = StreamingCaptureMode.Video,
+
+ // Set to CPU to ensure frames always contain CPU SoftwareBitmap images
+ // instead of preferring GPU D3DSurface images.
+ MemoryPreference = MediaCaptureMemoryPreference.Cpu
+ };
+ await mediaCapture.InitializeAsync(settings);
+ // Grab the kinds of media source we have
+ HashSet startedKinds = new HashSet();
+
+ IEnumerable mediaFrameSources = mediaCapture.FrameSources.Values.ToArray();
+ videoStreamComboBox.ItemsSource = mediaFrameSources;
+ MediaFrameSource selectedSource;
+
+ // TODO: If the count of streams is greater than 1, allow the user to select the specific stream
+ if (mediaCapture.FrameSources.Values.Count() > 1)
+ {
+ videoStreamComboBox.SelectedIndex = 0;
+ // Add another combobox; allow user to select desired source
+ selectedSource = mediaCapture.FrameSources.Values.First();
+
+ videoStreamLabel.Visibility = Visibility.Visible;
+ videoStreamComboBox.Visibility = Visibility.Visible;
+ }
+ else if (mediaCapture.FrameSources.Values.Count() == 0)
+ {
+ // Error, as camera has no streams
+ return;
+ }
+ else
+ {
+ // Just the one, continue
+ selectedSource = mediaCapture.FrameSources.Values.First();
+ videoStreamComboBox.SelectedIndex = 0;
+ }
+
+ // Source selected; enumerate formats
+
+ IEnumerable streamFormats = selectedSource.SupportedFormats
+ .Where(format => GetSubtypeForFrameReader(selectedSource.Info.SourceKind, format) != null)
+ .Select(format => new FrameFormatModel(format));
+ videoTypeComboBox.ItemsSource = streamFormats;
+ }
+ }
+
+ ///
+ /// View model for MediaFrameFormat used in XAML ContentControl.
+ ///
+ public class FrameFormatModel
+ {
+ public MediaFrameFormat Format { get; }
+
+ public string DisplayName { get; }
+
+ public FrameFormatModel(MediaFrameFormat format)
+ {
+ this.Format = format;
+ this.DisplayName = string.Format("{0} | {1} | {2} x {3} | {4:#.##}fps",
+ format.MajorType,
+ format.Subtype,
+ format.VideoFormat?.Width,
+ format.VideoFormat?.Height,
+ Math.Round((double)format.FrameRate.Numerator / format.FrameRate.Denominator, 2));
+ }
+
+ public override string ToString()
+ {
+ return this.DisplayName;
+ }
+
+ ///
+ /// Compares the Format contained by this view model to the given format for equivalency.
+ ///
+ /// The MediaFrameFormat to compare to the MediaFrameFormat in this view model.
+ ///
+ public bool HasSameFormat(MediaFrameFormat otherFormat)
+ {
+ if (otherFormat == null)
+ {
+ return (Format == null);
+ }
+
+ return this.Format.MajorType == otherFormat.MajorType &&
+ this.Format.Subtype == otherFormat.Subtype &&
+ this.Format.FrameRate.Numerator == otherFormat.FrameRate.Numerator &&
+ this.Format.FrameRate.Denominator == otherFormat.FrameRate.Denominator &&
+ this.Format.VideoFormat?.Width == otherFormat.VideoFormat?.Width &&
+ this.Format.VideoFormat?.Height == otherFormat.VideoFormat?.Height;
+ }
+ }
+
+ private void PerfStatsView_Unchecked(object sender, RoutedEventArgs e)
+ {
+ calcPerfStats = false;
+ _ = renderTarget.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ StatisticsPane.Visibility = Visibility.Collapsed;
+ });
+ }
+
+ private void PerfStatsView_Checked(object sender, RoutedEventArgs e)
+ {
+ calcPerfStats = true;
+ _ = renderTarget.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ StatisticsPane.Visibility = Visibility.Visible;
+ timelabel.Text = "0.0";
+ timelabel_delta.Text = "0.0";
+ maxTimeLabel.Text = "";
+ frameProcessingTimeLabel.Text = "";
+ });
+
+ }
+ }
+}
diff --git a/Camera-Renderer_cs/Package.appxmanifest b/Camera-Renderer_cs/Package.appxmanifest
new file mode 100644
index 0000000..0530dc9
--- /dev/null
+++ b/Camera-Renderer_cs/Package.appxmanifest
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+ CameraRendererCS
+ Brych
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Camera-Renderer_cs/Properties/AssemblyInfo.cs b/Camera-Renderer_cs/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..56999ac
--- /dev/null
+++ b/Camera-Renderer_cs/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CameraDirect")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CameraDirect")]
+[assembly: AssemblyCopyright("Copyright © 2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/Camera-Renderer_cs/Properties/Default.rd.xml b/Camera-Renderer_cs/Properties/Default.rd.xml
new file mode 100644
index 0000000..af00722
--- /dev/null
+++ b/Camera-Renderer_cs/Properties/Default.rd.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Camera-Renderer_cs/README.md b/Camera-Renderer_cs/README.md
new file mode 100644
index 0000000..17e2564
--- /dev/null
+++ b/Camera-Renderer_cs/README.md
@@ -0,0 +1,4 @@
+# CaptureRenderer
+
+Renders the web-cam style capture data in a window. Also retrieves sound from the specified device.
+Intended to be used with HDMI capture cards
\ No newline at end of file
diff --git a/Camera-Renderer_cs/assets/LockScreenLogo.scale-200.png b/Camera-Renderer_cs/assets/LockScreenLogo.scale-200.png
new file mode 100644
index 0000000..735f57a
Binary files /dev/null and b/Camera-Renderer_cs/assets/LockScreenLogo.scale-200.png differ
diff --git a/Camera-Renderer_cs/assets/SplashScreen.scale-200.png b/Camera-Renderer_cs/assets/SplashScreen.scale-200.png
new file mode 100644
index 0000000..023e7f1
Binary files /dev/null and b/Camera-Renderer_cs/assets/SplashScreen.scale-200.png differ
diff --git a/Camera-Renderer_cs/assets/Square150x150Logo.scale-200.png b/Camera-Renderer_cs/assets/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..af49fec
Binary files /dev/null and b/Camera-Renderer_cs/assets/Square150x150Logo.scale-200.png differ
diff --git a/Camera-Renderer_cs/assets/Square44x44Logo.scale-200.png b/Camera-Renderer_cs/assets/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..ce342a2
Binary files /dev/null and b/Camera-Renderer_cs/assets/Square44x44Logo.scale-200.png differ
diff --git a/Camera-Renderer_cs/assets/Square44x44Logo.targetsize-24_altform-unplated.png b/Camera-Renderer_cs/assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 0000000..f6c02ce
Binary files /dev/null and b/Camera-Renderer_cs/assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/Camera-Renderer_cs/assets/StoreLogo.png b/Camera-Renderer_cs/assets/StoreLogo.png
new file mode 100644
index 0000000..7385b56
Binary files /dev/null and b/Camera-Renderer_cs/assets/StoreLogo.png differ
diff --git a/Camera-Renderer_cs/assets/Wide310x150Logo.scale-200.png b/Camera-Renderer_cs/assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..288995b
Binary files /dev/null and b/Camera-Renderer_cs/assets/Wide310x150Logo.scale-200.png differ