Add project files.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Rssdp" Version="4.0.4" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0-rc.2.22472.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\UI\UI\UI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Core">
|
||||
<HintPath>..\..\..\..\..\Downloads\AForge.NET-master (1)\AForge.NET-master\Sources\Video.DirectShow\bin\Release\net5.0-windows\Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Video">
|
||||
<HintPath>..\..\..\..\..\Downloads\AForge.NET-master (1)\AForge.NET-master\Sources\Video.DirectShow\bin\Release\net5.0-windows\Video.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Video.DirectShow">
|
||||
<HintPath>..\..\..\..\..\Downloads\AForge.NET-master (1)\AForge.NET-master\Sources\Video.DirectShow\bin\Release\net5.0-windows\Video.DirectShow.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public record CapturedPhoto(Bitmap Photo, int Width, int Height);
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace TheXamlGuy.Media.Capture
|
||||
{
|
||||
public interface ILowLagPhotoCapture
|
||||
{
|
||||
Task<CapturedPhoto?> CaptureAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace TheXamlGuy.Media.Capture
|
||||
{
|
||||
public interface IMediaCapture
|
||||
{
|
||||
IMediaFrameSource? FrameSource { get; }
|
||||
|
||||
Task<IMediaFrameReader?> CreateFrameReaderAsync();
|
||||
|
||||
Task<IMediaFrameReader?> CreateFrameReaderAsync(IMediaFrameSource frameSource);
|
||||
|
||||
void Initialize(IMediaCaptureInitializationSettings initializationSettings);
|
||||
|
||||
Task<LowLagPhotoCapture?> PrepareLowLagPhotoCaptureAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public interface IMediaCaptureInitializationSettings
|
||||
{
|
||||
IMediaFrameSource? Source { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using TheXamlGuy.UI;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public interface IMediaFrameReader : IDisposable
|
||||
{
|
||||
event TypedEventHandler<IMediaFrameReader, MediaFrameArrivedEventArgs>? FrameArrived;
|
||||
|
||||
Task StartAsync();
|
||||
|
||||
Task StopAsync();
|
||||
|
||||
Task<MediaFrame?> TryAcquireLatestFrameAsync();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public interface IMediaFrameSource
|
||||
{
|
||||
MediaFrameFormat CurrentFormat { get; }
|
||||
|
||||
string DisplayName { get; }
|
||||
|
||||
MediaFrameSourceInfo Info { get; }
|
||||
|
||||
IReadOnlyList<MediaFrameFormat> SupportedFormats { get; }
|
||||
|
||||
void SetFormat(MediaFrameFormat format);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace TheXamlGuy.Media.Capture
|
||||
{
|
||||
public interface IRemoteMediaCapture
|
||||
{
|
||||
IRemoteMediaFrameSource? FrameSource { get; }
|
||||
|
||||
Task<IRemoteMediaFrameReader?> CreateFrameReaderAsync(IRemoteMediaFrameSource frameSource);
|
||||
|
||||
void Initialize(IRemoteMediaCaptureInitializationSettings initializationSettings);
|
||||
|
||||
Task<LowLagPhotoCapture?> PrepareLowLagPhotoCaptureAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public interface IRemoteMediaCaptureInitializationSettings
|
||||
{
|
||||
IRemoteMediaFrameSource? Source { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public interface IRemoteMediaFrameReader : IMediaFrameReader
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
internal interface IRemoteMediaFrameReaderFactory
|
||||
{
|
||||
Func<IRemoteMediaFrameReader> Factory { get; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public interface IRemoteMediaFrameSource
|
||||
{
|
||||
string DisplayName { get; }
|
||||
|
||||
MediaFrameSourceInfo Info { get; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public interface IRemoteMediaFrameSourceDescriptor
|
||||
{
|
||||
string DisplayName { get; }
|
||||
|
||||
Func<IRemoteMediaFrameReader> FrameReaderFactory { get; }
|
||||
|
||||
string Id { get; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class LowLagPhotoCapture : ILowLagPhotoCapture
|
||||
{
|
||||
private readonly IMediaFrameReader frameReader;
|
||||
|
||||
internal LowLagPhotoCapture(IMediaFrameReader frameReader)
|
||||
{
|
||||
this.frameReader = frameReader;
|
||||
}
|
||||
|
||||
public async Task<CapturedPhoto?> CaptureAsync()
|
||||
{
|
||||
if (await frameReader.TryAcquireLatestFrameAsync() is MediaFrame frame)
|
||||
{
|
||||
return new CapturedPhoto(frame.Bitmap, frame.Width, frame.Height);
|
||||
}
|
||||
|
||||
return await Task.FromResult<CapturedPhoto?>(default);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class MediaCapture : IMediaCapture
|
||||
{
|
||||
private readonly Dictionary<IMediaFrameSource, IMediaFrameReader> frameReaderCache = new();
|
||||
|
||||
public IMediaFrameSource? FrameSource { get; private set; }
|
||||
|
||||
public async Task<IMediaFrameReader?> CreateFrameReaderAsync()
|
||||
{
|
||||
IMediaFrameSource? frameSource = FrameSource;
|
||||
if (FrameSource is null)
|
||||
{
|
||||
if (await MediaFrameSource.FindAllAsync() is IReadOnlyList<IMediaFrameSource> { Count: > 0 } sourceGroups)
|
||||
{
|
||||
frameSource = sourceGroups[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (frameSource is not null)
|
||||
{
|
||||
if (frameReaderCache.TryGetValue(frameSource, out IMediaFrameReader? frameReader))
|
||||
{
|
||||
return frameReader;
|
||||
}
|
||||
|
||||
frameReader = new MediaFrameReader(frameSource);
|
||||
frameReaderCache.Add(frameSource, frameReader);
|
||||
|
||||
return frameReader;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task<IMediaFrameReader?> CreateFrameReaderAsync(IMediaFrameSource frameSource)
|
||||
{
|
||||
if (frameSource is not null)
|
||||
{
|
||||
if (frameReaderCache.TryGetValue(frameSource, out IMediaFrameReader? frameReader))
|
||||
{
|
||||
return frameReader;
|
||||
}
|
||||
|
||||
frameReader = new MediaFrameReader(frameSource);
|
||||
frameReaderCache.Add(frameSource, frameReader);
|
||||
|
||||
return await Task.FromResult(frameReader);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Initialize(IMediaCaptureInitializationSettings initializationSettings)
|
||||
{
|
||||
FrameSource = initializationSettings.Source;
|
||||
}
|
||||
|
||||
public async Task<LowLagPhotoCapture?> PrepareLowLagPhotoCaptureAsync()
|
||||
{
|
||||
IMediaFrameSource? frameSource = FrameSource;
|
||||
if (FrameSource is null)
|
||||
{
|
||||
if (await MediaFrameSource.FindAllAsync() is IReadOnlyList<IMediaFrameSource> { Count: > 0 } sourceGroups)
|
||||
{
|
||||
frameSource = sourceGroups[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (frameSource is not null)
|
||||
{
|
||||
IMediaFrameReader? frameReader = await CreateFrameReaderAsync(frameSource);
|
||||
if (frameReader is not null)
|
||||
{
|
||||
LowLagPhotoCapture photoCapture = new(frameReader);
|
||||
return photoCapture;
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class MediaCaptureInitializationSettings : IMediaCaptureInitializationSettings
|
||||
{
|
||||
public IMediaFrameSource? Source { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class MediaFrameFormat
|
||||
{
|
||||
|
||||
public Size Size { get; set; }
|
||||
|
||||
public long FrameRate { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public record MediaFrame(Bitmap Bitmap, int Width, int Height);
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class MediaFrameArrivedEventArgs : EventArgs
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using TheXamlGuy.UI;
|
||||
using AForge.Video.DirectShow;
|
||||
using System.Drawing;
|
||||
using AForge.Video;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class MediaFrameReader : IMediaFrameReader
|
||||
{
|
||||
private readonly VideoCaptureDevice captureDevice;
|
||||
private bool isAquiringFrames;
|
||||
|
||||
internal MediaFrameReader(IMediaFrameSource frameSource)
|
||||
{
|
||||
captureDevice = new(frameSource.Info.Id);
|
||||
|
||||
captureDevice.VideoResolution = captureDevice
|
||||
.VideoCapabilities
|
||||
.FirstOrDefault(x => frameSource.CurrentFormat.FrameRate == x.AverageFrameRate && x.FrameSize.Equals(frameSource.CurrentFormat.Size));
|
||||
|
||||
captureDevice.NewFrame += OnNewFrame;
|
||||
}
|
||||
|
||||
public event TypedEventHandler<IMediaFrameReader, MediaFrameArrivedEventArgs>? FrameArrived;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
captureDevice.Stop();
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if (!captureDevice.IsRunning)
|
||||
{
|
||||
isAquiringFrames = true;
|
||||
captureDevice.Start();
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
if (captureDevice.IsRunning)
|
||||
{
|
||||
isAquiringFrames = false;
|
||||
captureDevice.Stop();
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<MediaFrame?> TryAcquireLatestFrameAsync()
|
||||
{
|
||||
TaskCompletionSource<Bitmap> completionSource = new();
|
||||
|
||||
void HandleNewFrame(object sender, NewFrameEventArgs args)
|
||||
{
|
||||
if (args.Frame.PixelFormat is not PixelFormat.DontCare)
|
||||
{
|
||||
captureDevice.NewFrame -= HandleNewFrame;
|
||||
completionSource.SetResult(new Bitmap(args.Frame));
|
||||
}
|
||||
}
|
||||
|
||||
captureDevice.NewFrame += HandleNewFrame;
|
||||
if (!isAquiringFrames)
|
||||
{
|
||||
captureDevice.Start();
|
||||
}
|
||||
|
||||
Bitmap frame = await completionSource.Task;
|
||||
|
||||
if (!isAquiringFrames)
|
||||
{
|
||||
captureDevice.SignalToStop();
|
||||
captureDevice.WaitForStop();
|
||||
}
|
||||
|
||||
return new MediaFrame(frame, frame.Width, frame.Height);
|
||||
}
|
||||
|
||||
private void OnNewFrame(object sender, NewFrameEventArgs args)
|
||||
{
|
||||
FrameArrived?.Invoke(this, new MediaFrameArrivedEventArgs());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using AForge.Video.DirectShow;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class MediaFrameSource : IMediaFrameSource
|
||||
{
|
||||
public List<MediaFrameFormat> supportedFormats = new();
|
||||
private MediaFrameFormat? currentFormat = null;
|
||||
|
||||
internal MediaFrameSource(string id, string displayName)
|
||||
{
|
||||
Info = new MediaFrameSourceInfo(id);
|
||||
DisplayName = displayName;
|
||||
|
||||
InitializeSupportedFormats(id);
|
||||
}
|
||||
|
||||
public MediaFrameFormat CurrentFormat => currentFormat ?? supportedFormats.FirstOrDefault()!;
|
||||
|
||||
public string DisplayName { get; }
|
||||
|
||||
public MediaFrameSourceInfo Info { get; }
|
||||
|
||||
public IReadOnlyList<MediaFrameFormat> SupportedFormats => supportedFormats;
|
||||
|
||||
public static async Task<IReadOnlyList<IMediaFrameSource>> FindAllAsync()
|
||||
{
|
||||
List<IMediaFrameSource> result = new();
|
||||
foreach (FilterInfo videoInputDevice in new FilterInfoCollection(FilterCategory.VideoInputDevice))
|
||||
{
|
||||
result.Add(new MediaFrameSource(videoInputDevice.MonikerString, videoInputDevice.Name));
|
||||
}
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
public void SetFormat(MediaFrameFormat format)
|
||||
{
|
||||
currentFormat = format;
|
||||
}
|
||||
|
||||
private void InitializeSupportedFormats(string id)
|
||||
{
|
||||
VideoCaptureDevice videoCaptureDevice = new(id);
|
||||
foreach (VideoCapabilities videoCapabilities in videoCaptureDevice.VideoCapabilities)
|
||||
{
|
||||
supportedFormats.Add(new MediaFrameFormat
|
||||
{
|
||||
Size = videoCapabilities.FrameSize,
|
||||
FrameRate = videoCapabilities.AverageFrameRate
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class MediaFrameSourceInfo
|
||||
{
|
||||
internal MediaFrameSourceInfo(string id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,60 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class RemoteMediaCapture : IRemoteMediaCapture
|
||||
{
|
||||
private readonly Dictionary<IRemoteMediaFrameSource, IRemoteMediaFrameReader> frameReaderCache = new();
|
||||
|
||||
public IRemoteMediaFrameSource? FrameSource { get; private set; }
|
||||
|
||||
public async Task<IRemoteMediaFrameReader?> CreateFrameReaderAsync(IRemoteMediaFrameSource frameSource)
|
||||
{
|
||||
if (frameSource is not null)
|
||||
{
|
||||
if (frameReaderCache.TryGetValue(frameSource, out IRemoteMediaFrameReader? frameReader))
|
||||
{
|
||||
return frameReader;
|
||||
}
|
||||
|
||||
if (frameSource is IRemoteMediaFrameReaderFactory frameReaderFactory)
|
||||
{
|
||||
frameReader = frameReaderFactory.Factory();
|
||||
frameReaderCache.Add(frameSource, frameReader);
|
||||
}
|
||||
|
||||
return await Task.FromResult(frameReader);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public void Initialize(IRemoteMediaCaptureInitializationSettings initializationSettings)
|
||||
{
|
||||
FrameSource = initializationSettings.Source;
|
||||
}
|
||||
|
||||
public async Task<LowLagPhotoCapture?> PrepareLowLagPhotoCaptureAsync()
|
||||
{
|
||||
IRemoteMediaFrameSource? frameSource = FrameSource;
|
||||
if (FrameSource is null)
|
||||
{
|
||||
if (await RemoteMediaFrameSource.FindAllAsync() is IReadOnlyList<IRemoteMediaFrameSource> { Count: > 0 } sourceGroups)
|
||||
{
|
||||
frameSource = sourceGroups[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (frameSource is not null)
|
||||
{
|
||||
IMediaFrameReader? frameReader = await CreateFrameReaderAsync(frameSource);
|
||||
if (frameReader is not null)
|
||||
{
|
||||
LowLagPhotoCapture photoCapture = new(frameReader);
|
||||
await frameReader.StartAsync();
|
||||
|
||||
return photoCapture;
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class RemoteMediaCaptureInitializationSettings : IRemoteMediaCaptureInitializationSettings
|
||||
{
|
||||
public IRemoteMediaFrameSource? Source { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Rssdp;
|
||||
using static TheXamlGuy.Media.Capture.SonyMediaFrameSourceDescriptor;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class RemoteMediaFrameSource : IRemoteMediaFrameSource, IRemoteMediaFrameReaderFactory
|
||||
{
|
||||
private static readonly Dictionary<string, Func<Uri, Task<IRemoteMediaFrameSourceDescriptor>>> supportedDeviceSchemas = new()
|
||||
{
|
||||
{ "urn:schemas-sony-com:service:ScalarWebAPI:1", async args => (await CreateAsync(args))! }
|
||||
};
|
||||
|
||||
internal RemoteMediaFrameSource(string id, string displayName, Func<IRemoteMediaFrameReader> factory)
|
||||
{
|
||||
Info = new MediaFrameSourceInfo(id);
|
||||
DisplayName = displayName;
|
||||
Factory = factory;
|
||||
}
|
||||
|
||||
public string DisplayName { get; }
|
||||
|
||||
public MediaFrameSourceInfo Info { get; }
|
||||
|
||||
public Func<IRemoteMediaFrameReader> Factory { get; }
|
||||
|
||||
public static async Task<IReadOnlyList<IRemoteMediaFrameSource>> FindAllAsync()
|
||||
{
|
||||
List<IRemoteMediaFrameSource> result = new();
|
||||
|
||||
using (SsdpDeviceLocator deviceLocator = new())
|
||||
{
|
||||
IEnumerable<DiscoveredSsdpDevice> foundDevices = await deviceLocator.SearchAsync();
|
||||
foreach (DiscoveredSsdpDevice foundDevice in foundDevices)
|
||||
{
|
||||
if (supportedDeviceSchemas.TryGetValue(foundDevice.NotificationType, out Func<Uri, Task<IRemoteMediaFrameSourceDescriptor>>? factory))
|
||||
{
|
||||
if (await factory.Invoke(foundDevice.DescriptionLocation) is IRemoteMediaFrameSourceDescriptor descriptor)
|
||||
{
|
||||
result.Add(new RemoteMediaFrameSource(descriptor.Id, descriptor.DisplayName, descriptor.FrameReaderFactory));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using System.Drawing;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using TheXamlGuy.UI;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class SonyMediaFrameReader : IRemoteMediaFrameReader
|
||||
{
|
||||
private readonly HttpClient client;
|
||||
private readonly string endpoint;
|
||||
|
||||
public SonyMediaFrameReader(string endpoint)
|
||||
{
|
||||
client = new HttpClient();
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public event TypedEventHandler<IMediaFrameReader, MediaFrameArrivedEventArgs>? FrameArrived;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
await PostRequestAsync<Initialize>();
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<MediaFrame?> TryAcquireLatestFrameAsync()
|
||||
{
|
||||
await PostRequestAsync<Initialize>();
|
||||
|
||||
if (await PostRequestAsync<Capture>() is Capture result && result.Captures is not null )
|
||||
{
|
||||
if (result.Captures.FirstOrDefault() is List<string> captures)
|
||||
{
|
||||
if (captures.FirstOrDefault() is string url)
|
||||
{
|
||||
HttpResponseMessage content = await client.GetAsync(url);
|
||||
|
||||
using Stream stream = content.Content.ReadAsStream();
|
||||
using Image bitmap = Image.FromStream(stream);
|
||||
return new MediaFrame((Bitmap)bitmap.Clone(), bitmap.Width, bitmap.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private async Task<TReponse?> PostRequestAsync<TRequest, TReponse>() where TRequest : Request, new()
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpResponseMessage response = await client.PostAsJsonAsync(endpoint, new TRequest());
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
return await response.Content.ReadFromJsonAsync<TReponse>();
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private async Task<TRequest?> PostRequestAsync<TRequest>() where TRequest : Request, new()
|
||||
{
|
||||
return await PostRequestAsync<TRequest, TRequest>();
|
||||
}
|
||||
|
||||
private record Initialize() : Request("startRecMode", "1.0", Array.Empty<string>());
|
||||
|
||||
private record Capture() : Request("actTakePicture", "1.0", Array.Empty<string>())
|
||||
{
|
||||
[JsonPropertyName("result")]
|
||||
public List<List<string>>? Captures { get; set; }
|
||||
}
|
||||
|
||||
private record Request
|
||||
{
|
||||
private static int requestId;
|
||||
|
||||
public Request(string method, string version = "1.0", params string[] parameters)
|
||||
{
|
||||
Method = method;
|
||||
Version = version;
|
||||
Id = GenerateId();
|
||||
Paramaters = parameters;
|
||||
}
|
||||
|
||||
private static int GenerateId()
|
||||
{
|
||||
int id = Interlocked.Increment(ref requestId);
|
||||
if (requestId > 1000000000)
|
||||
{
|
||||
requestId = 0;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
[JsonPropertyName("method")]
|
||||
public string? Method { get; init; }
|
||||
|
||||
[JsonPropertyName("version")]
|
||||
public string? Version { get; init; }
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public int? Id { get; init; }
|
||||
|
||||
[JsonPropertyName("params")]
|
||||
public string[]? Paramaters { get; init; }
|
||||
|
||||
public static Request CreateRequest(string method, string version = "1.0", params string[] parameters)
|
||||
{
|
||||
return new(method, version, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace TheXamlGuy.Media.Capture;
|
||||
|
||||
public class SonyMediaFrameSourceDescriptor : IRemoteMediaFrameSourceDescriptor
|
||||
{
|
||||
public SonyMediaFrameSourceDescriptor(string id, string displayName, Func<IRemoteMediaFrameReader> factory)
|
||||
{
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
FrameReaderFactory = factory;
|
||||
}
|
||||
|
||||
public string DisplayName { get; }
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public Func<IRemoteMediaFrameReader> FrameReaderFactory { get; }
|
||||
|
||||
public static async Task<IRemoteMediaFrameSourceDescriptor?> CreateAsync(Uri descriptionLocation)
|
||||
{
|
||||
using HttpClient httpClient = new();
|
||||
HttpResponseMessage responseMessage = await httpClient.GetAsync(descriptionLocation);
|
||||
string content = await responseMessage.Content.ReadAsStringAsync();
|
||||
|
||||
XmlSerializer serializer = new(typeof(Descriptor));
|
||||
using TextReader reader = new StringReader(content);
|
||||
|
||||
if ((Descriptor?)serializer.Deserialize(reader) is Descriptor result && result.Device is Device device && device.DeviceInfo is DeviceInfo deviceInfo
|
||||
&& deviceInfo.ServiceList is ServiceList serviceList && serviceList.Service is List<Service> services
|
||||
&& services.FirstOrDefault(x => x.ServiceType is string serviceType
|
||||
&& serviceType.Equals("camera", StringComparison.InvariantCultureIgnoreCase)) is Service service)
|
||||
{
|
||||
return new SonyMediaFrameSourceDescriptor(device.UDN ?? "", device.FriendlyName ?? "", () => new SonyMediaFrameReader($"{service.Action}\\{service.ServiceType}"));
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private record Initialize() : Request("startRecMode", "1.0", Array.Empty<string>());
|
||||
|
||||
private record Capture() : Request("actTakePicture", "1.0", Array.Empty<string>())
|
||||
{
|
||||
[JsonPropertyName("result")]
|
||||
public List<List<string>>? Captures { get; set; }
|
||||
}
|
||||
|
||||
private record Request
|
||||
{
|
||||
private static int requestId;
|
||||
|
||||
public Request(string method, string version = "1.0", params string[] parameters)
|
||||
{
|
||||
Method = method;
|
||||
Version = version;
|
||||
Id = GenerateId();
|
||||
Paramaters = parameters;
|
||||
}
|
||||
|
||||
private static int GenerateId()
|
||||
{
|
||||
int id = Interlocked.Increment(ref requestId);
|
||||
if (requestId > 1000000000)
|
||||
{
|
||||
requestId = 0;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
[JsonPropertyName("method")]
|
||||
public string? Method { get; init; }
|
||||
|
||||
[JsonPropertyName("version")]
|
||||
public string? Version { get; init; }
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public int? Id { get; init; }
|
||||
|
||||
[JsonPropertyName("params")]
|
||||
public string[]? Paramaters { get; init; }
|
||||
|
||||
public static Request CreateRequest(string method, string version = "1.0", params string[] parameters)
|
||||
{
|
||||
return new(method, version, parameters);
|
||||
}
|
||||
}
|
||||
[XmlRoot(ElementName = "root", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public class Descriptor
|
||||
{
|
||||
[XmlElement(ElementName = "device", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public Device? Device { get; set; }
|
||||
}
|
||||
|
||||
[XmlRoot(ElementName = "device", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public class Device
|
||||
{
|
||||
[XmlElement(ElementName = "X_ScalarWebAPI_DeviceInfo", Namespace = "urn:schemas-sony-com:av")]
|
||||
public DeviceInfo? DeviceInfo { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "friendlyName", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public string? FriendlyName { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "manufacturer", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public string? Manufacturer { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "modelDescription", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public string? ModelDescription { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "modelName", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public string? ModelName { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "UDN", Namespace = "urn:schemas-upnp-org:device-1-0")]
|
||||
public string? UDN { get; set; }
|
||||
}
|
||||
|
||||
[XmlRoot(ElementName = "X_ScalarWebAPI_DeviceInfo", Namespace = "urn:schemas-sony-com:av")]
|
||||
public class DeviceInfo
|
||||
{
|
||||
[XmlElement(ElementName = "X_ScalarWebAPI_ServiceList", Namespace = "urn:schemas-sony-com:av")]
|
||||
public ServiceList? ServiceList { get; set; }
|
||||
}
|
||||
|
||||
[XmlRoot(ElementName = "X_ScalarWebAPI_Service", Namespace = "urn:schemas-sony-com:av")]
|
||||
public class Service
|
||||
{
|
||||
[XmlElement(ElementName = "X_ScalarWebAPI_ActionList_URL", Namespace = "urn:schemas-sony-com:av")]
|
||||
public string? Action { get; set; }
|
||||
|
||||
[XmlElement(ElementName = "X_ScalarWebAPI_ServiceType", Namespace = "urn:schemas-sony-com:av")]
|
||||
public string? ServiceType { get; set; }
|
||||
}
|
||||
|
||||
[XmlRoot(ElementName = "X_ScalarWebAPI_ServiceList", Namespace = "urn:schemas-sony-com:av")]
|
||||
public class ServiceList
|
||||
{
|
||||
[XmlElement(ElementName = "X_ScalarWebAPI_Service", Namespace = "urn:schemas-sony-com:av")]
|
||||
public List<Service>? Service { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user