Skip to content

Commit

Permalink
Enabled video resolution picker
Browse files Browse the repository at this point in the history
  • Loading branch information
yassirbisteni committed Apr 24, 2024
1 parent 52ce431 commit b163f8c
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 63 deletions.
141 changes: 119 additions & 22 deletions RawVideo/CameraCaptureService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<VideoFormatBundle>
{
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<MediaFrameSourceGroup, MediaFrameSourceInfo> 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()
Expand Down Expand Up @@ -88,23 +110,98 @@ public async Task StopAsync()
}
}

public static async Task<List<Tuple<MediaFrameSourceGroup, MediaFrameSourceInfo>>> GetCameraListAsync()
public static async Task<List<MediaFrameSourceBundle>> GetCameraListAsync()
{
IReadOnlyList<MediaFrameSourceGroup> groups = await MediaFrameSourceGroup.FindAllAsync();
var cameraList = new List<Tuple<MediaFrameSourceGroup, MediaFrameSourceInfo>>();
var cameraList = new List<MediaFrameSourceBundle>();

foreach (MediaFrameSourceGroup sourceGroup in groups)
{
foreach (MediaFrameSourceInfo sourceInfo in sourceGroup.SourceInfos)
{
if (sourceInfo.SourceKind == MediaFrameSourceKind.Color)
{
cameraList.Add(new Tuple<MediaFrameSourceGroup, MediaFrameSourceInfo>(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<List<VideoFormatBundle>> GetSupportedVideoFormats(MediaFrameSourceBundle bundle)
{
var settings = new MediaCaptureInitializationSettings
{
SourceGroup = bundle.Group,
SharingMode = MediaCaptureSharingMode.SharedReadOnly,
StreamingCaptureMode = StreamingCaptureMode.Video,
MemoryPreference = MediaCaptureMemoryPreference.Cpu
};

IReadOnlyList<MediaFrameFormat> sourceFormatList = null;
var videoFormatList= new List<VideoFormatBundle>();

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<VideoStreamResolution>()
.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;
}
}
}
28 changes: 15 additions & 13 deletions RawVideo/CaptureService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,25 @@ namespace RawVideo
internal abstract class CaptureService
{
public event EventHandler<RawVideoFrame> 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);
}
Expand All @@ -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);

Expand All @@ -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;
Expand All @@ -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;
}
}
}
41 changes: 28 additions & 13 deletions RawVideo/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter
Width="500"
Width="Auto"
Height="50"
FontSize="25"
Background="White"
Foreground="Black"
Padding="20,0,20,0"
/>
</Grid>
</ControlTemplate>
Expand Down Expand Up @@ -215,9 +216,8 @@
</ComboBox>
</StackPanel>


<StackPanel
Width="500"
Width="Auto"
Orientation="Horizontal"
>
<ComboBox
Expand Down Expand Up @@ -250,6 +250,22 @@
<SolidColorBrush x:Key="ComboBoxBorderBrushPointerOver" Color="Black"/>
</ComboBox.Resources>
</ComboBox>

<ComboBox
Width="200"
x:Name="outgoingVideoFormatComboBox"
PlaceholderText="OutgoingVideoFormat"
SelectionChanged="OutgoingVideoFormatSelected"
Style="{StaticResource ComboBoxStyle}"
ItemContainerStyle="{StaticResource ComboBoxItemStyle}"
Margin="10,10,0,0"
>
<ComboBox.Resources>
<SolidColorBrush x:Key="ComboBoxBackgroundPointerOver" Color="white"/>
<SolidColorBrush x:Key="ComboBoxForegroundPointerOver" Color="Black"/>
<SolidColorBrush x:Key="ComboBoxBorderBrushPointerOver" Color="Black"/>
</ComboBox.Resources>
</ComboBox>
</StackPanel>

<ComboBox
Expand Down Expand Up @@ -298,34 +314,33 @@
</ComboBox>
</StackPanel>

<RelativePanel
<StackPanel
x:Name="videoContainer"
Width="640"
Height="360"
Margin="0,10,0,0"
Visibility="Collapsed"
Orientation="Vertical"
>
<RelativePanel
x:Name="incomingVideoContainer"
Width="640"
Height="360"
Width="480"
Height="270"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="8"
Margin="0,0,0,10"
>
</RelativePanel>

<RelativePanel
x:Name="outgoingVideoContainer"
Width="224"
Height="108"
Margin="10,10,0,0"
Width="480"
Height="270"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="8"
>
</RelativePanel>
</RelativePanel>
</StackPanel>

<StackPanel
Width="Auto"
Expand All @@ -334,7 +349,7 @@
Spacing="10"
Background="White"
HorizontalAlignment="Center"
Margin="0,10,0,0"
Margin="0,10,0,0"
>
<Button
x:Name="startCallButton"
Expand Down
Loading

0 comments on commit b163f8c

Please sign in to comment.