From b163f8ce8f8379c5b838c81d3ed66ebf1d38dda1 Mon Sep 17 00:00:00 2001 From: Yassir Bisteni Date: Tue, 23 Apr 2024 19:45:25 -0600 Subject: [PATCH] Enabled video resolution picker --- RawVideo/CameraCaptureService.cs | 141 ++++++++++++++++++++++++++----- RawVideo/CaptureService.cs | 28 +++--- RawVideo/MainPage.xaml | 41 ++++++--- RawVideo/MainPage.xaml.cs | 63 ++++++++++---- 4 files changed, 210 insertions(+), 63 deletions(-) diff --git a/RawVideo/CameraCaptureService.cs b/RawVideo/CameraCaptureService.cs index 64426c7a..6a3f1df0 100644 --- a/RawVideo/CameraCaptureService.cs +++ b/RawVideo/CameraCaptureService.cs @@ -3,59 +3,81 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Windows.Graphics; using Windows.Graphics.Imaging; using Windows.Media.Capture; using Windows.Media.Capture.Frames; namespace RawVideo { + internal class MediaFrameSourceBundle + { + public MediaFrameSourceGroup Group { get; set; } + public MediaFrameSourceInfo Info { get; set; } + public MediaFrameFormat Format { get; set; } + } + + internal class VideoFormatBundle + { + public SizeInt32 Size { get; set; } + public MediaFrameFormat Format { get; set; } + } + + internal class VideoFormatBundleComparer : IEqualityComparer + { + public bool Equals(VideoFormatBundle s1, VideoFormatBundle s2) + { + return s1.Size.Width == s2.Size.Width && s1.Size.Height == s2.Size.Height; + } + + public int GetHashCode(VideoFormatBundle obj) + { + return obj.Size.Width.GetHashCode() ^ obj.Size.Height.GetHashCode(); + } + } + internal class CameraCaptureService : CaptureService { + private readonly MediaFrameSourceBundle bundle; private MediaCapture mediaCapture; private MediaFrameReader mediaFrameReader; - private MediaFrameSourceGroup sourceGroup; - private MediaFrameSourceInfo sourceInfo; public CameraCaptureService(RawOutgoingVideoStream rawOutgoingVideoStream, - RawVideoFrameKind rawVideoFrameKind, - Tuple mediaFrameSource) : + RawVideoFrameKind rawVideoFrameKind, MediaFrameSourceBundle bundle) : base(rawOutgoingVideoStream, rawVideoFrameKind) { - sourceGroup = mediaFrameSource.Item1; - sourceInfo = mediaFrameSource.Item2; + this.bundle = bundle; } public async Task StartAsync() { var settings = new MediaCaptureInitializationSettings { - SourceGroup = sourceGroup, - SharingMode = MediaCaptureSharingMode.SharedReadOnly, + SourceGroup = bundle.Group, + SharingMode = MediaCaptureSharingMode.ExclusiveControl, StreamingCaptureMode = StreamingCaptureMode.Video, MemoryPreference = MediaCaptureMemoryPreference.Cpu }; mediaCapture = new MediaCapture(); - MediaFrameReaderStartStatus mediaFrameReaderStatus; + MediaFrameReaderStartStatus mediaFrameReaderStatus = + MediaFrameReaderStartStatus.UnknownFailure; try { await mediaCapture.InitializeAsync(settings); - MediaFrameSource selectedSource = mediaCapture.FrameSources[sourceInfo.Id]; - mediaFrameReader = await mediaCapture.CreateFrameReaderAsync(selectedSource); + MediaFrameSource source = mediaCapture.FrameSources[bundle.Info.Id]; + + await source.SetFormatAsync(bundle.Format); + + mediaFrameReader = await mediaCapture.CreateFrameReaderAsync(source); + mediaFrameReader.FrameArrived += FrameArrived; mediaFrameReaderStatus = await mediaFrameReader.StartAsync(); } catch (Exception ex) { - mediaFrameReaderStatus = MediaFrameReaderStartStatus.UnknownFailure; - Console.WriteLine(ex.Message); } - - if (mediaFrameReaderStatus == MediaFrameReaderStartStatus.Success) - { - mediaFrameReader.FrameArrived += FrameArrived; - } } public async Task StopAsync() @@ -88,10 +110,10 @@ public async Task StopAsync() } } - public static async Task>> GetCameraListAsync() + public static async Task> GetCameraListAsync() { IReadOnlyList groups = await MediaFrameSourceGroup.FindAllAsync(); - var cameraList = new List>(); + var cameraList = new List(); foreach (MediaFrameSourceGroup sourceGroup in groups) { @@ -99,12 +121,87 @@ public static async Task { if (sourceInfo.SourceKind == MediaFrameSourceKind.Color) { - cameraList.Add(new Tuple(sourceGroup, sourceInfo)); + cameraList.Add(new MediaFrameSourceBundle() + { + Group = sourceGroup, + Info = sourceInfo + }); } } } - return cameraList.OrderBy(item => item.Item2.DeviceInformation.Name).ToList(); + return cameraList + .OrderBy(x => x.Info.DeviceInformation.Name) + .ToList(); + } + + public static async Task> GetSupportedVideoFormats(MediaFrameSourceBundle bundle) + { + var settings = new MediaCaptureInitializationSettings + { + SourceGroup = bundle.Group, + SharingMode = MediaCaptureSharingMode.SharedReadOnly, + StreamingCaptureMode = StreamingCaptureMode.Video, + MemoryPreference = MediaCaptureMemoryPreference.Cpu + }; + + IReadOnlyList sourceFormatList = null; + var videoFormatList= new List(); + + using (var mediaCapture = new MediaCapture()) + { + try + { + await mediaCapture.InitializeAsync(settings); + MediaFrameSource source = mediaCapture.FrameSources[bundle.Info.Id]; + sourceFormatList = source.SupportedFormats; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + + if (sourceFormatList != null) + { + var resolutionList = Enum.GetValues(typeof(VideoStreamResolution)) + .Cast() + .ToList(); + + var acsFormatList = resolutionList + .Select(x => + { + var format = new VideoStreamFormat + { + Resolution = x + }; + + return new SizeInt32() + { + Width = format.Width, + Height = format.Height + }; + }) + .Distinct() + .ToList(); + + videoFormatList = sourceFormatList + .Select(x => new VideoFormatBundle() + { + Size = new SizeInt32() + { + Width = (int) x.VideoFormat.Width, + Height = (int) x.VideoFormat.Height + }, + Format = x + }) + .Distinct(new VideoFormatBundleComparer()) + .Where(x => acsFormatList.Contains(x.Size)) + .OrderByDescending(x => x.Size.Width) + .ToList(); + } + + return videoFormatList; } } } diff --git a/RawVideo/CaptureService.cs b/RawVideo/CaptureService.cs index a45903a8..88211529 100644 --- a/RawVideo/CaptureService.cs +++ b/RawVideo/CaptureService.cs @@ -11,24 +11,25 @@ namespace RawVideo internal abstract class CaptureService { public event EventHandler FrameArrived; - protected readonly RawOutgoingVideoStream rawOutgoingVideoStream; + protected readonly RawOutgoingVideoStream stream; private readonly RawVideoFrameKind rawVideoFrameKind; - protected CaptureService(RawOutgoingVideoStream rawOutgoingVideoStream, RawVideoFrameKind rawVideoFrameKind) + protected CaptureService(RawOutgoingVideoStream stream, RawVideoFrameKind rawVideoFrameKind) { - this.rawOutgoingVideoStream = rawOutgoingVideoStream; + this.stream = stream; this.rawVideoFrameKind = rawVideoFrameKind; } protected async Task SendRawVideoFrame(SoftwareBitmap bitmap) { - if (bitmap != null && CanSendRawVideoFrames()) + var format = stream.Format; + if (bitmap != null && format != null && CanSendRawVideoFrames()) { try { - RawVideoFrame frame = ConvertSoftwareBitmapToRawVideoFrame(bitmap); + RawVideoFrame frame = ConvertSoftwareBitmapToRawVideoFrame(bitmap, format); - await rawOutgoingVideoStream.SendRawVideoFrameAsync(frame); + await stream.SendRawVideoFrameAsync(frame); FrameArrived?.Invoke(this, frame); } @@ -39,7 +40,9 @@ protected async Task SendRawVideoFrame(SoftwareBitmap bitmap) } } - private unsafe RawVideoFrame ConvertSoftwareBitmapToRawVideoFrame(SoftwareBitmap bitmap) + private unsafe RawVideoFrame ConvertSoftwareBitmapToRawVideoFrame( + SoftwareBitmap bitmap, + VideoStreamFormat format) { bitmap = SoftwareBitmap.Convert(bitmap, BitmapPixelFormat.Rgba8); @@ -59,21 +62,22 @@ private unsafe RawVideoFrame ConvertSoftwareBitmapToRawVideoFrame(SoftwareBitmap { case RawVideoFrameKind.Buffer: MemoryBuffer buffer = Buffer.CreateMemoryBufferOverIBuffer(bitmapBuffer); + frame = new RawVideoFrameBuffer() { Buffers = new MemoryBuffer[] { buffer }, - StreamFormat = rawOutgoingVideoStream.Format + StreamFormat = format }; break; case RawVideoFrameKind.Texture: - var timeSpan = new TimeSpan(rawOutgoingVideoStream.TimestampInTicks); + var timeSpan = new TimeSpan(stream.TimestampInTicks); var sample = MediaStreamSample.CreateFromBuffer(bitmapBuffer, timeSpan); frame = new RawVideoFrameTexture() { Texture = sample, - StreamFormat = rawOutgoingVideoStream.Format + StreamFormat = stream.Format }; break; @@ -84,9 +88,7 @@ private unsafe RawVideoFrame ConvertSoftwareBitmapToRawVideoFrame(SoftwareBitmap private bool CanSendRawVideoFrames() { - return rawOutgoingVideoStream != null && - rawOutgoingVideoStream.Format != null && - rawOutgoingVideoStream.State == VideoStreamState.Started; + return stream != null && stream.State == VideoStreamState.Started; } } } diff --git a/RawVideo/MainPage.xaml b/RawVideo/MainPage.xaml index 656d243f..df92c356 100644 --- a/RawVideo/MainPage.xaml +++ b/RawVideo/MainPage.xaml @@ -23,11 +23,12 @@ @@ -215,9 +216,8 @@ - + + + + + + + + - - +