メディア プレーヤー、ありますよね。
WinUI 3ならMediaPlayerElement クラスです。
それで動画を再生したなら、決定的瞬間を写真に収めたいと思いませんか?私は思います。
やっていきましょうか。
先に結論
- MediaPlayer.CopyFrameToVideoSurfaceで今映ってるやつ静止画に写せます
- 写す先のビットマップはWin2DのCanvasBitmap.CreateFromSoftwareBitmapで作れます
- Win2D.uwpじゃなくてMicrosoft.Graphics.Win2Dだからね!
サンプル:https://github.com/mifumi323/WinUI3MediaPlayerElementSnapshotSample
画面
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="WinUI3MediaPlayerElementSnapshotSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinUI3MediaPlayerElementSnapshotSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="WinUI3MediaPlayerElementSnapshotSample">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<CommandBar HorizontalAlignment="Left" DefaultLabelPosition="Right">
<AppBarButton Click="PhotoButton_Click" Icon="Camera" Label="写真"/>
</CommandBar>
<MediaPlayerElement x:Name="Player" Source="ms-appx:///Assets/sample.mp4" AreTransportControlsEnabled="True" Grid.Row="1" />
</Grid>
</Window>
サンプルですのでシンプルにメディアプレーヤーとボタンだけです。
MediaPlayerElement.Sourceにファイルを指定すれば簡単に動画を読み込めるわけですね。
本筋とは関係ありませんが、ボタンにはコマンド バーを使っています。
NuGetパッケージのインストール
先に書いたように、Win2Dが必要になります。
Win2D.uwpのほうが上に表示されますが、名前を見てわかるようにWinUI3じゃなくてUWP用なので、インストールしても使えません。
Microsoft.Graphics.Win2Dのほうをインストールしましょう。
保存するコード
private async void PhotoButton_Click(object sender, RoutedEventArgs e)
{
var savePicker = new FileSavePicker();
savePicker.FileTypeChoices.Add("PNG", [".png"]);
// このキモいおまじない無くなるといいな(https://qiita.com/hayashida-katsutoshi/items/21b8f99c462c01bb7c6d)
InitializeWithWindow.Initialize(savePicker, WindowNative.GetWindowHandle(this));
var file = await savePicker.PickSaveFileAsync();
if (file != null)
{
int naturalVideoWidth = (int)Player.MediaPlayer.PlaybackSession.NaturalVideoWidth;
int naturalVideoHeight = (int)Player.MediaPlayer.PlaybackSession.NaturalVideoHeight;
CanvasDevice canvasDevice = CanvasDevice.GetSharedDevice();
var frameServerDest = new SoftwareBitmap(BitmapPixelFormat.Rgba8, naturalVideoWidth, naturalVideoHeight, BitmapAlphaMode.Ignore);
using var inputBitmap = CanvasBitmap.CreateFromSoftwareBitmap(canvasDevice, frameServerDest);
Player.MediaPlayer.CopyFrameToVideoSurface(inputBitmap);
var photoPath = file.Path;
await inputBitmap.SaveAsync(photoPath, CanvasBitmapFileFormat.Png);
}
}
これで画像として保存できます。
まあ、コードに書いた通りです。
コードの前半は保存ダイアログを出しているだけです。
コメントに書いてあるように、メインウィンドウを先に登録してやる必要があります(参考:Windows App SDK, WinUI3, Desktopでファイル選択ダイアログを表示する)。
CanvasDevice以降の保存処理本体は公式ドキュメントのフレーム サーバー モードでの MediaPlayer の使用を参考にしています。
実はフレームサーバーモードじゃなくても普通に保存できるのと、VS2022にお任せで足りないライブラリ入れさせようとするとWin2D.uwp(WinUI3では使えない)を提案してくるあたりが罠でした。
コピー先のビットマップ作るだけで結構回りくどい処理しますね。
環境
Windows 11
Visual Studio 2022 Version 17.13.1
終わりに
WinUI3、DPI Aware PerMonitorV2に最初から対応しているので高解像度モニタできれいに表示されてなかなかいいですよ。