Skip to content

Commit

Permalink
Source update
Browse files Browse the repository at this point in the history
  • Loading branch information
marcos4503 committed Oct 5, 2024
1 parent 2c5ff75 commit 142c969
Show file tree
Hide file tree
Showing 13 changed files with 718 additions and 23 deletions.
2 changes: 2 additions & 0 deletions Source-Code/Motoplay.Desktop/Motoplay.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>motoplay-logo.ico</ApplicationIcon>
<AssemblyVersion>0.0.2</AssemblyVersion>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<Content Include="motoplay-logo.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Expand Down
28 changes: 18 additions & 10 deletions Source-Code/Motoplay/Assets/Languages/LangStrings.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,20 @@ Attempt #%n</x:String>
<x:String x:Key="musicPlayer_bluetoothConnectTryAlready">Bluetooth Sound Device is now connected!</x:String>
<x:String x:Key="musicPlayer_openSoundOutput">Open Sound Output Selector</x:String>

<!-- Web Browser -->
<x:String x:Key="webBrowser_title">Web Browser</x:String>
<x:String x:Key="webBrowser_unvailableWarn">Unfortunately, the Web Browser is not available in Motoplay due to technical limitations. This feature will be added in a future version of Motoplay once it becomes possible.</x:String>

<!-- USB Camera -->
<x:String x:Key="usbCamera_title">USB Camera</x:String>
<x:String x:Key="usbCamera_autoSelectButton">Auto Select</x:String>
<x:String x:Key="usbCamera_resetButton">Reset Select</x:String>
<x:String x:Key="usbCamera_startSeeingErrorMsg">Unable to start viewing the selected Camera and/or Feature. Please check if the Camera is still connected.</x:String>
<x:String x:Key="usbCamera_notFoundDevice">No connected USB Camera found!</x:String>
<x:String x:Key="usbCamera_notFeatureFound">Couldn't find the best Feature for the connected Camera!</x:String>











<!-- Mirror Phone -->
<x:String x:Key="mirrorPhone_title">Mirror Phone</x:String>

<!-- Preferences Page -->
<x:String x:Key="appPreferences_title">Motoplay Preferences</x:String>
Expand Down Expand Up @@ -164,5 +166,11 @@ Attempt #%n</x:String>
<x:String x:Key="appPreferences_playerTab_volumeMark6">Automatic Volume Mark 6 (Km/h, Vol%)</x:String>
<x:String x:Key="appPreferences_playerTab_volumeMark7">Automatic Volume Mark 7 (Km/h, Vol%)</x:String>
<x:String x:Key="appPreferences_playerTab_volumeBoost">Max Relative Volume Boost at Max RPM (%)</x:String>
<x:String x:Key="appPreferences_cameraTab_title">Camera</x:String>
<x:String x:Key="appPreferences_cameraTab_projectionQuality">Projection Quality in the Interface</x:String>
<x:String x:Key="appPreferences_cameraTab_maxQueuingFrames">Maximum Number of Frames in Queue</x:String>
<x:String x:Key="appPreferences_cameraTab_enableMultiThread">Enable Multi Thread Rendering - May Cause Crash if Disconnect</x:String>
<x:String x:Key="appPreferences_cameraTab_showStatistics">Enable Statistics Display</x:String>
<x:String x:Key="appPreferences_cameraTab_miniViewSize">Connected Camera Miniview Size</x:String>

</ResourceDictionary>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Source-Code/Motoplay/Assets/web-sad-face.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:skiav="clr-namespace:SkiaImageView;assembly=SkiaImageView"
mc:Ignorable="d" x:Class="Motoplay.UvcCameraHandler"
d:DesignWidth="800" d:DesignHeight="450">

<!-- Root Element -->
<Grid Name="rootElement" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<skiav:SKImageView Name="skiaImageView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stretch="Fill"></skiav:SKImageView>
<StackPanel Name="statistics_root" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0, 8, 8, 0">
<TextBlock Name="statisticFps" Text="Real 0 FPS - Index 0 FPS" TextAlignment="Right" HorizontalAlignment="Right" VerticalAlignment="Top" Foreground="Lime"></TextBlock>
<TextBlock Name="statisticResolution" Text="0x0 - IMG" TextAlignment="Right" HorizontalAlignment="Right" VerticalAlignment="Top" Foreground="Lime"></TextBlock>
</StackPanel>
</Grid>

</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using Coroutine;
using FlashCap;
using SkiaImageView;
using SkiaSharp;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using static Motoplay.BluetoothDeviceItem.ClassDelegates;
using static Motoplay.Scripts.MusicPlayer.ClassDelegates;

namespace Motoplay;

/*
* This script is resposible by the work of the "UvcCameraHandler" that represents
* and manage UVC Cameras connected to system.
* This Control requires "SkiaImageView" and "FlashCap" packages.
*/

public partial class UvcCameraHandler : UserControl
{
//Classes of script
public class UvcDevice()
{
//Public variables
public CaptureDeviceDescriptor deviceRealRef = null;
public int realIndexOnConnectedDevices = -1;
public string name = "";
public string description = "";
}
public class ClassDelegates
{
public delegate void OnUpdateDevices(ref List<UvcDevice> devices);
public delegate void OnReceiveErrorOnStartSeeing();
public delegate void OnStartedSeeing();
public delegate void OnStoppedSeeing();
}

//Cache variables
private List<UvcDevice> currentConnectedUvcDevices = new List<UvcDevice>();
private CaptureDevice currentSeeingUvcDevice = null;
private double realCountFrames = 0;
private SKImageView alternativeSkiaImageView = null;

//Private variables
private int cameraProjectionQuality = -1;
private bool enableMultiThread = false;
private int maxQueuingFrames = -1;
private bool showStatistics = false;
private event ClassDelegates.OnUpdateDevices onUpdateDevices = null;
private event ClassDelegates.OnReceiveErrorOnStartSeeing onReceiveErrorOnStartSeeing = null;
private event ClassDelegates.OnStartedSeeing onStartedSeeing = null;
private event ClassDelegates.OnStoppedSeeing onStoppedSeeing = null;

//Core methods

public UvcCameraHandler()
{
//Initialize the window normally on Avalonia side
InitializeComponent();

//Inform that is the DataConext of this User Control
this.DataContext = this;
}

public void SetCameraProjectionQuality(int cameraProjectionQuality)
{
//Save the data
this.cameraProjectionQuality = cameraProjectionQuality;
}

public void SetEnabledMultiThread(bool enableMultiThread)
{
//Save the data
this.enableMultiThread = enableMultiThread;
}

public void SetMaxQueuingFrames(int maxQueuingFrames)
{
//Save the data
this.maxQueuingFrames = maxQueuingFrames;
}

public void SetShowStatistics(bool showStatistics)
{
//Save the data
this.showStatistics = showStatistics;
}

public void RegisterOnUpdateDevicesCallback(ClassDelegates.OnUpdateDevices onUpdateDevices)
{
//Register the event
this.onUpdateDevices = onUpdateDevices;
}

public void RegisterOnReceiveErrorOnStartSeeingCallback(ClassDelegates.OnReceiveErrorOnStartSeeing onReceiveErrorOnStartSeeing)
{
//Register the event
this.onReceiveErrorOnStartSeeing = onReceiveErrorOnStartSeeing;
}

public void RegisterOnStartedSeeingCallback(ClassDelegates.OnStartedSeeing onStartedSeeing)
{
//Register the event
this.onStartedSeeing = onStartedSeeing;
}

public void RegisterOnStoppedSeeingCallback(ClassDelegates.OnStoppedSeeing onStoppedSeeing)
{
//Register the event
this.onStoppedSeeing = onStoppedSeeing;
}

//Public methods

public void Initialize()
{
//Start the UVC devices monitor routine
CoroutineHandler.Start(ConnectedDevicesUpdateRoutine());

//Setup the projection quality of SkiaImageView
if (cameraProjectionQuality == 0)
skiaImageView.ProjectionQuality = SkiaImageView.ProjectionQuality.Low;
if (cameraProjectionQuality == 1)
skiaImageView.ProjectionQuality = SkiaImageView.ProjectionQuality.Middle;
if (cameraProjectionQuality == 2)
skiaImageView.ProjectionQuality = SkiaImageView.ProjectionQuality.High;
if (cameraProjectionQuality == 3)
skiaImageView.ProjectionQuality = SkiaImageView.ProjectionQuality.Perfect;

//Hide the statistics
statistics_root.IsVisible = false;
}

public void StartSeeLiveUvcDeviceWithFeature(int realIndexOfUvcCamera, int featureId)
{
//Stop seeing current UVC Device, if is seeing
StopSeeLiveUvcDevice();

//Get referece for the real selected device
CaptureDeviceDescriptor selectedDevice = currentConnectedUvcDevices[realIndexOfUvcCamera].deviceRealRef;

//Start a new thread to prepare and start seeing the UVC Device
new Thread(async () =>
{
//Try to start seeing...
try
{
//Prepare the Frame processor Loop and store this as current seeing device
currentSeeingUvcDevice = await selectedDevice.OpenAsync(selectedDevice.Characteristics[featureId], TranscodeFormats.Auto, enableMultiThread, maxQueuingFrames, async bufferScope =>
{
//Get bytes of current frame
byte[] frameImageData = bufferScope.Buffer.ExtractImage();

//Convert to bitmap (example)
//MemoryStream memoryStream = new MemoryStream(frameImageData);
//Bitmap bitmap = System.Drawing.Bitmap.FromStream(ms);

//Get the image converted into a SkiaImageView Bitmap
SKBitmap skiaBitmap = SKBitmap.Decode(frameImageData);
//Get statistics
realCountFrames += 1;
long currentFrameIndex = bufferScope.Buffer.FrameIndex;
TimeSpan currentTimeStamp = bufferScope.Buffer.Timestamp;

//Release the buffer now, because is not necessary
bufferScope.ReleaseNow();

//Run on main thread...
Dispatcher.UIThread.Invoke(() =>
{
//If is desired statistics
if (showStatistics == true)
{
statisticFps.Text = ("Real " + (int)(realCountFrames / currentTimeStamp.TotalSeconds) + " FPS - Index " + (int)((double)currentFrameIndex / currentTimeStamp.TotalSeconds) + " FPS");
statisticResolution.Text = (skiaBitmap.Width + "x" + skiaBitmap.Height + " - " + skiaBitmap.ColorType.ToString().ToUpper());
}

//Show the current frame on UI, in "SKImageView" component
if (alternativeSkiaImageView == null)
skiaImageView.Source = skiaBitmap;
if (alternativeSkiaImageView != null)
alternativeSkiaImageView.Source = skiaBitmap;

//Dispose from the SkiaImageView Bitmap
skiaBitmap.Dispose();

}, DispatcherPriority.MaxValue);

//...
});

//Start the seeing
currentSeeingUvcDevice.StartAsync();

//If is desired to show the statistics, enable it
if (showStatistics == true)
Dispatcher.UIThread.Invoke(() => { statistics_root.IsVisible = true; }, DispatcherPriority.MaxValue);

//Send the callback of start
if (onStartedSeeing != null)
Dispatcher.UIThread.Invoke(() => { onStartedSeeing(); }, DispatcherPriority.MaxValue);
}
catch (Exception e)
{
//Send the error message
if (onReceiveErrorOnStartSeeing != null)
Dispatcher.UIThread.Invoke(() => { onReceiveErrorOnStartSeeing(); }, DispatcherPriority.MaxValue);
}

//...
}).Start();
}

public void SetAlternativeSkiaImageView(SKImageView alternativeSkiaImageView)
{
//Set the alternativa Skia Image View
this.alternativeSkiaImageView = alternativeSkiaImageView;
}

public void StopSeeLiveUvcDevice()
{
//Try to stop seeing the UVC Device...
try
{
//Stop seeing the UVC Device, and release it, if exists one at the moment
if (currentSeeingUvcDevice != null)
{
currentSeeingUvcDevice.StopAsync();
currentSeeingUvcDevice.DisposeAsync();
}
}
catch (Exception e) { }

//Reset the SkiaImageView component
skiaImageView.Source = null;
if (alternativeSkiaImageView != null)
alternativeSkiaImageView.Source = null;

//Disable the statistics
realCountFrames = 0;
statistics_root.IsVisible = false;

//Send the callback of start
if (onStoppedSeeing != null)
Dispatcher.UIThread.Invoke(() => { onStoppedSeeing(); }, DispatcherPriority.MaxValue);
}

//Auxiliar methods

private IEnumerator<Wait> ConnectedDevicesUpdateRoutine()
{
//Prepare the interval time
Wait intervalTime = new Wait(15.0f);

//Start the monitor loop
while (true)
{
//Get UVC devices
CaptureDevices captureDevices = new CaptureDevices();
List<CaptureDeviceDescriptor> foundUvcDevicesList = new List<CaptureDeviceDescriptor>();
foreach (CaptureDeviceDescriptor dev in captureDevices.EnumerateDescriptors())
if (dev.Characteristics.Length >= 1)
foundUvcDevicesList.Add(dev);

//If the quantity of found devices is different from the cache, force cache update now
if (foundUvcDevicesList.Count != currentConnectedUvcDevices.Count)
{
UpdateCurrentConnectedUvcDevicesList(foundUvcDevicesList.ToArray());
continue;
}

//If the quantity of found devices is equal to the cache, check if have difference
if (foundUvcDevicesList.Count == currentConnectedUvcDevices.Count)
{
//Prepare the information
bool needUpdateToCache = false;

//Check if need to update
for (int i = 0; i < foundUvcDevicesList.Count; i++)
if (currentConnectedUvcDevices[i].name != foundUvcDevicesList[i].Name)
needUpdateToCache = true;

//If need update, run the updater
if (needUpdateToCache == true)
UpdateCurrentConnectedUvcDevicesList(foundUvcDevicesList.ToArray());
}

//Wait time
yield return intervalTime;
}
}

private void UpdateCurrentConnectedUvcDevicesList(CaptureDeviceDescriptor[] captureDevices)
{
//Clear the list of current connected UVC devices
currentConnectedUvcDevices.Clear();

//Refill the list
for (int i = 0; i < captureDevices.Length; i++)
{
//Create a instance of device
UvcDevice uvcDevice = new UvcDevice();
uvcDevice.deviceRealRef = captureDevices[i];
uvcDevice.realIndexOnConnectedDevices = i;
uvcDevice.name = captureDevices[i].Name;
uvcDevice.description = captureDevices[i].Description;

//Add to list of current connected UVC devices
currentConnectedUvcDevices.Add(uvcDevice);
}

//Send a callback
if (onUpdateDevices != null)
onUpdateDevices(ref currentConnectedUvcDevices);
}
}
Loading

0 comments on commit 142c969

Please sign in to comment.