Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a "Capture element / camera preview" page in the Media section. #1357

Merged
merged 5 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion WinUIGallery/ContentIncludes.props
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
<Content Include="Assets\ControlImages\Button.png" />
<Content Include="Assets\ControlImages\CalendarDatePicker.png" />
<Content Include="Assets\ControlImages\CalendarView.png" />
<!-- TODO: Need a camera capture icon. -->
<Content Include="Assets\ControlImages\Canvas.png" />
<Content Include="Assets\ControlImages\Checkbox.png" />
<Content Include="Assets\ControlImages\Clipboard.png" />
Expand Down Expand Up @@ -303,6 +304,8 @@
<Content Include="ControlPagesSampleCode\ListView\ListViewSample4_cs.txt" />
<Content Include="ControlPagesSampleCode\ListView\ListViewSample4_xaml.txt" />
<Content Include="ControlPagesSampleCode\ListView\ListViewStickyHeaderSample_xaml.txt" />
<Content Include="ControlPagesSampleCode\Media\CaptureElementPreviewSample_cs.txt" />
<Content Include="ControlPagesSampleCode\Media\CaptureElementPreviewSample_xaml.txt" />
<Content Include="ControlPagesSampleCode\MenuBar\MenuBarSample1.txt" />
<Content Include="ControlPagesSampleCode\MenuBar\MenuBarSample2.txt" />
<Content Include="ControlPagesSampleCode\MenuBar\MenuBarSample3.txt" />
Expand Down Expand Up @@ -372,4 +375,4 @@
<Content Include="Common\ReadMe.txt" />
<Content Include="DataModel\ControlInfoData.json" />
</ItemGroup>
</Project>
</Project>
31 changes: 31 additions & 0 deletions WinUIGallery/ControlPages/CaptureElementPreviewPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Page x:Class="AppUIBasics.ControlPages.CaptureElementPreviewPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:AppUIBasics"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<StackPanel>
<local:ControlExample x:Name="Example1" HeaderText="A MediaCapture preview displayed via a MediaPlayerElement." XamlSource="Media/CaptureElementPreviewSample_xaml.txt" CSharpSource="Media/CaptureElementPreviewSample_cs.txt">
<local:ControlExample.Example>
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,100" MinWidth="400" MinHeight="300" RowSpacing="10" ColumnSpacing="4">
<TextBlock x:Name="frameSourceName" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/>
<MediaPlayerElement x:Name="captureElement" Grid.Row="1" Grid.Column="0" Stretch="Uniform" AutoPlay="True" />
<TextBlock x:Name="capturedText" Visibility="Collapsed" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Text="Captured:" />
<Grid x:Name="captureContainer" Grid.Row="1" Grid.Column="1">
<ScrollViewer VerticalScrollMode="Enabled">
<StackPanel x:Name="snapshots" Spacing="2"/>
</ScrollViewer>
</Grid>
</Grid>
</local:ControlExample.Example>
<local:ControlExample.Options>
<StackPanel>
<ToggleSwitch x:Name="mirrorSwitch" Header="Mirror preview" IsOn="False" Toggled="MirrorToggleSwitch_Toggled" ToolTipService.ToolTip="Mirrors only the preview, not captured photos"/>
<Button x:Name="captureButton" Content="Capture Photo" Click="CapturePhoto_Click" />
</StackPanel>
</local:ControlExample.Options>
<local:ControlExample.Substitutions>
<local:ControlExampleSubstitution Key="MirrorPreview" Value="{x:Bind MirrorTextReplacement, Mode=OneWay}"/>
</local:ControlExample.Substitutions>
</local:ControlExample>
</StackPanel>
</Page>
130 changes: 130 additions & 0 deletions WinUIGallery/ControlPages/CaptureElementPreviewPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
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 Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Media.Capture.Frames;
using Windows.Media.Capture;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.Media.MediaProperties;
using Windows.Storage.Streams;
using System.ComponentModel;
using AppUIBasics.Helper;

namespace AppUIBasics.ControlPages
{
public sealed partial class CaptureElementPreviewPage : Page, INotifyPropertyChanged
{
public CaptureElementPreviewPage()
{
this.InitializeComponent();

StartCaptureElement();

// Move the ScrollViewer from the captureContainer under an ExpandToFillContainer.
// This will allow the snapshots column to use all available height without
// influencing the height.
var expandToFillContainer = new ExpandToFillContainer();
var sv = captureContainer.Children[0];
captureContainer.Children.Remove(sv);
captureContainer.Children.Add(expandToFillContainer);
expandToFillContainer.Children.Add(sv);
}

private MediaFrameSourceGroup mediaFrameSourceGroup;
private MediaCapture mediaCapture;

async private void StartCaptureElement()
{
var groups = await MediaFrameSourceGroup.FindAllAsync();
if (groups.Count == 0)
{
frameSourceName.Text = "No camera devices found.";
return;
}
mediaFrameSourceGroup = groups.First();

frameSourceName.Text = "Viewing: " + mediaFrameSourceGroup.DisplayName;
mediaCapture = new MediaCapture();
var mediaCaptureInitializationSettings = new MediaCaptureInitializationSettings()
{
SourceGroup = this.mediaFrameSourceGroup,
SharingMode = MediaCaptureSharingMode.SharedReadOnly,
StreamingCaptureMode = StreamingCaptureMode.Video,
MemoryPreference = MediaCaptureMemoryPreference.Cpu
};
await mediaCapture.InitializeAsync(mediaCaptureInitializationSettings);

// Set the MediaPlayerElement's Source property to the MediaSource for the mediaCapture.
var frameSource = mediaCapture.FrameSources[this.mediaFrameSourceGroup.SourceInfos[0].Id];
captureElement.Source = Windows.Media.Core.MediaSource.CreateFromMediaFrameSource(frameSource);
}

public string MirrorTextReplacement = ""; // starts not mirrored, so no text in that case

public event PropertyChangedEventHandler PropertyChanged;

public void OnPropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}

private void MirrorToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
if (mirrorSwitch.IsOn)
{
captureElement.RenderTransform = new ScaleTransform() { ScaleX = -1 };
captureElement.RenderTransformOrigin = new Point(0.5, 0.5);
MirrorTextReplacement =
"\n" +
" // Mirror the preview\n" +
" captureElement.RenderTransform = new ScaleTransform() { ScaleX = -1 };\n" +
" captureElement.RenderTransformOrigin = new Point(0.5, 0.5);\n";
}
else
{
captureElement.RenderTransform = null;
MirrorTextReplacement = "";
}
OnPropertyChanged("MirrorTextReplacement");
}

async private void CapturePhoto_Click(object sender, RoutedEventArgs e)
{
// Capture a photo to a stream
var imgFormat = ImageEncodingProperties.CreateJpeg();
var stream = new InMemoryRandomAccessStream();
await mediaCapture.CapturePhotoToStreamAsync(imgFormat, stream);
stream.Seek(0);

// Show the photo in an Image element
BitmapImage bmpImage = new BitmapImage();
await bmpImage.SetSourceAsync(stream);
var image = new Image() { Source = bmpImage };
snapshots.Children.Insert(0, image);

capturedText.Visibility = Visibility.Visible;

UIHelper.AnnounceActionForAccessibility(captureButton, "Photo successfully captured.", "CameraPreviewSampleCaptureNotificationId");
}
}

class ExpandToFillContainer : Grid
{
protected override Size MeasureOverride(Size availableSize)
{
// Measure with the minimum height so it will just expand to whatever space is available.
var desiredSize = base.MeasureOverride(new Size(availableSize.Width, 100));
return desiredSize;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Windows.Media.Capture.Frames;
using Windows.Media.Capture;

private MediaFrameSourceGroup mediaFrameSourceGroup;
private MediaCapture mediaCapture;

async private void StartCaptureElement()
{
var groups = await MediaFrameSourceGroup.FindAllAsync();
if (groups.Count == 0)
{
frameSourceName.Text = "No camera devices found.";
return;
}
mediaFrameSourceGroup = groups.First();

frameSourceName.Text = "Viewing: " + mediaFrameSourceGroup.DisplayName;
mediaCapture = new MediaCapture();
var mediaCaptureInitializationSettings = new MediaCaptureInitializationSettings()
{
SourceGroup = this.mediaFrameSourceGroup,
SharingMode = MediaCaptureSharingMode.SharedReadOnly,
StreamingCaptureMode = StreamingCaptureMode.Video,
MemoryPreference = MediaCaptureMemoryPreference.Cpu
};
await mediaCapture.InitializeAsync(mediaCaptureInitializationSettings);

// Set the MediaPlayerElement's Source property to the MediaSource for the mediaCapture.
var frameSource = mediaCapture.FrameSources[this.mediaFrameSourceGroup.SourceInfos[0].Id];
captureElement.Source = Windows.Media.Core.MediaSource.CreateFromMediaFrameSource(frameSource);
$(MirrorPreview) }

async private void CapturePhoto_Click(object sender, RoutedEventArgs e)
{
// Capture a photo to a stream
var imgFormat = ImageEncodingProperties.CreateJpeg();
var stream = new InMemoryRandomAccessStream();
await mediaCapture.CapturePhotoToStreamAsync(imgFormat, stream);
stream.Seek(0);

// Show the photo in an Image element
BitmapImage bmpImage = new BitmapImage();
await bmpImage.SetSourceAsync(stream);
var image = new Image() { Source = bmpImage };
snapshots.Children.Insert(0, image);

capturedText.Visibility = Visibility.Visible;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*,100" MinWidth="400" MinHeight="300" RowSpacing="10" ColumnSpacing="4">
<TextBlock x:Name="frameSourceName" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/>
<MediaPlayerElement x:Name="captureElement" Grid.Row="1" Grid.Column="0" Stretch="Uniform" AutoPlay="True" />
<TextBlock x:Name="capturedText" Visibility="Collapsed" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" Text="Captured:" />
<Grid x:Name="captureContainer" Grid.Row="1" Grid.Column="1">
<ScrollViewer VerticalScrollMode="Enabled">
<StackPanel x:Name="snapshots" Spacing="2"/>
</ScrollViewer>
</Grid>
</Grid>
18 changes: 18 additions & 0 deletions WinUIGallery/DataModel/ControlInfoData.json
Original file line number Diff line number Diff line change
Expand Up @@ -2009,6 +2009,24 @@
}
]
},
{
"UniqueId": "CaptureElementPreview",
"Title": "Capture Element / Camera Preview",
"Subtitle": "A sample for doing a camera preview.",
"ImagePath": "ms-appx:///Assets/ControlImages/MediaPlayerElement.png",
"ImageIconPath": "ms-appx:///Assets/ControlIcons/MediaElementIcon.png",
"Description": "You can use a MediaPlayerElement control to show a camera preview with a MediaCapture object.",
"IsNew": true,
"Docs": [
{
"Title": "MediaCapture - API",
"Uri": "https://learn.microsoft.com/uwp/api/windows.media.capture.mediacapture"
}
],
"RelatedControls": [
"MediaPlayerElement"
]
},
{
"UniqueId": "Image",
"Title": "Image",
Expand Down