当前位置: 首页 > news >正文

WPF使用MediaCapture开发相机应用(四、相机录视频)

在WPF中使用MediaCapture录视频还是挺简单的,教程是WinUI3的,WPF也可以直接用。
主要代码就几句,相对简单:

var videos = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
StorageFile file = await videos.SaveFolder.CreateFileAsync($"{DateTime.Now.Ticks}.mp4", CreationCollisionOption.GenerateUniqueName);
RecordingFile = file.Path;  //记录临时路径,停止录制后移动到用户指定的路径
var recording = await Capture.PrepareLowLagRecordToStorageFileAsync(MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto), file);
//Capture.RecordLimitationExceeded += Capture_RecordLimitationExceeded;教程里说视频最多只能录三个小时,超出后需要额外处理,我这里偷懒了。
await recording.StartAsync();
MediaRecording = recording;

教程还提供了一下进阶方法,不过我手头上的摄像头不支持,没法做,感兴趣的可以自行研究一下。

相机应用到这里就结束了,代码不多,才两百行。贴出来给大家参考一下,希望大家工作顺利!

using Microsoft.Win32;
using OpenCvSharp;
using OpenCvSharp.WpfExtensions;
using System.DirectoryServices.ActiveDirectory;
using System.IO;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Windows.Graphics.Imaging;
using Windows.Media.Capture;
using Windows.Media.Capture.Frames;
using Windows.Media.MediaProperties;
using Windows.Storage;
using Windows.Storage.Streams;
using Window = System.Windows.Window;namespace VideoCapture
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{#region 初始化public MainWindow(){InitializeComponent();}protected override void OnSourceInitialized(EventArgs e){base.OnSourceInitialized(e);Task.Run(CaptureInit);}private MediaCapture? Capture = null;private async Task CaptureInit(){//查找摄像头设备,这里仅作示例,只取第一个。实际开发请根据实际情况变通。var device = (await MediaFrameSourceGroup.FindAllAsync()).FirstOrDefault();if (device is null) return;//初始化 MediaCapture 对象var capture = new MediaCapture();var settings = new MediaCaptureInitializationSettings(){SourceGroup = device,SharingMode = MediaCaptureSharingMode.ExclusiveControl,//控制设备。StreamingCaptureMode = StreamingCaptureMode.Video,MemoryPreference = MediaCaptureMemoryPreference.Cpu,//这里不要用auto,不然可能会读不到帧数据。};await capture.InitializeAsync(settings);//获取预览流foreach (MediaFrameSource source in capture.FrameSources.Values){MediaFrameSourceKind kind = source.Info.SourceKind;if (kind != MediaFrameSourceKind.Color) continue;//为了方便,这里只要Nv12的,实际开发可根据情况变通if (source.CurrentFormat.Subtype.ToUpper() != "NV12"){//查找指定的格式foreach(var f in source.SupportedFormats){if (f.Subtype.ToUpper() != "NV12") continue;//尝试设置格式try { await source.SetFormatAsync(f); }catch { continue; }}}//创建帧数据读取设备MediaFrameReader frameReader = await capture.CreateFrameReaderAsync(source, MediaEncodingSubtypes.Nv12);frameReader.FrameArrived += Reader_FrameArrived;MediaFrameReaderStartStatus status = await frameReader.StartAsync();if (status == MediaFrameReaderStartStatus.Success){Capture = capture;break;}//没能正确创建,将其释放掉await frameReader.StopAsync();frameReader.Dispose();}}#endregion#region 预览/// <summary>/// 帧数据缓冲区/// </summary>Windows.Storage.Streams.Buffer FrameBuf { get; set; }/// <summary>/// 帧数据/// </summary>public byte[] FrameData { get; private set; }/// <summary>/// NV12帧数据/// </summary>Mat FrameNV12 { get; set; }/// <summary>/// Bgr帧数据/// </summary>Mat FrameBgr { get; set; }/// <summary>/// 捕获帧/// </summary>private void Reader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args){var frame = sender.TryAcquireLatestFrame()?.VideoMediaFrame?.SoftwareBitmap;if (frame is null) return;//创建缓冲if (FrameNV12 is null || FrameNV12.Width != frame.PixelWidth || FrameNV12.Height != frame.PixelHeight){using var lockbuf = frame.LockBuffer(Windows.Graphics.Imaging.BitmapBufferAccessMode.Read);if (lockbuf is null) return;using var n = lockbuf.CreateReference();var t = lockbuf.GetPlaneDescription(0);FrameBuf = new Windows.Storage.Streams.Buffer(n.Capacity);FrameNV12 = new Mat(frame.PixelHeight * 3 / 2, frame.PixelWidth, MatType.CV_8UC1);FrameBgr = new Mat();}//读取数据frame.CopyToBuffer(FrameBuf);if (FrameData is null || FrameData.Length != FrameBuf.Length) FrameData = new byte[FrameBuf.Length];using var reader = DataReader.FromBuffer(FrameBuf);reader.ReadBytes(FrameData);//NV12转Rbg24Marshal.Copy(FrameData, 0, FrameNV12.Data, FrameData.Length);Cv2.CvtColor(FrameNV12, FrameBgr, ColorConversionCodes.YUV2BGR_NV12);Dispatcher.InvokeAsync(() => RendererFrame(FrameBgr, frame.PixelWidth, frame.PixelHeight));}/// <summary>/// 渲染帧/// </summary>private void RendererFrame(Mat bgr, int width, int height){if (bgr is null || width <= 0 || height <= 0 || FrameBgr.Width != width || FrameBgr.Height != height) return;if (FrameImage.Source is not WriteableBitmap bmp || bmp.PixelWidth != width || bmp.PixelHeight != height){bmp = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgr24, default);FrameImage.Source = bmp;}WriteableBitmapConverter.ToWriteableBitmap(bgr, bmp);//显示当前预览像素,可删除Rel.Text = $"{width}x{height}";}#endregion#region 拍照/// <summary>/// 拍照/// </summary>private async void Photo_Click(object sender, RoutedEventArgs e){if (Capture is null) return;//预览可以使用较小的清晰度以保证流畅,但照片需要设置最高的像素。var max = Capture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.Photo).OfType<VideoEncodingProperties>().OrderByDescending(p => p.Width * p.Height).First();//await Capture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.Photo, max);//控制设备低快门拍照。其实教程用的是CapturePhotoToStorageFileAsync,但那是UWP的内容,我没找到在WPF上使用的方法。var iep = ImageEncodingProperties.CreateUncompressed(MediaPixelFormat.Nv12);iep.Width = max.Width;iep.Height = max.Height;var ahc = await Capture.PrepareLowLagPhotoCaptureAsync(iep);var res = await ahc.CaptureAsync();var bmp = res.Frame.SoftwareBitmap;await ahc.FinishAsync();//读取缓冲区using var m = bmp.LockBuffer(BitmapBufferAccessMode.Read);using var n = m.CreateReference();var buf = new Windows.Storage.Streams.Buffer(n.Capacity);var data = new byte[n.Capacity];bmp.CopyToBuffer(buf);using var reader = DataReader.FromBuffer(buf);reader.ReadBytes(data);//保存文件var sfd = new SaveFileDialog() {Filter = "图片|*.jpg;",FileName = $"{DateTimeOffset.Now.Ticks}.jpg",DefaultDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)};if (sfd.ShowDialog() != true) return;//using var mat = new Mat(bmp.PixelHeight * 3 / 2, bmp.PixelWidth, MatType.CV_8UC1, data); 不知道啥时候起不能这么写了,吐槽一下。using var nv12 = new Mat(bmp.PixelHeight * 3 / 2, bmp.PixelWidth, MatType.CV_8UC1);Marshal.Copy(data, 0, nv12.Data, data.Length);using var bgr = new Mat();Cv2.CvtColor(nv12, bgr, ColorConversionCodes.YUV2BGR_NV12);bgr.SaveImage(sfd.FileName);}#endregion#region 录视频LowLagMediaRecording MediaRecording {  get; set; }string RecordingFile {  get; set; }/// <summary>/// 录视频/// </summary>private async void Record_Click(object sender, RoutedEventArgs e){if (Capture is null || sender is not Button btn) return;//停止录制if (btn.Content as string != "录  制"){btn.Content = "录  制";await MediaRecording?.StopAsync();await MediaRecording?.FinishAsync();if (File.Exists(RecordingFile)){var sfd = new SaveFileDialog(){Filter = "视频|*.mp4",DefaultDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos)};if(sfd.ShowDialog() == true) File.Move(RecordingFile, sfd.FileName, true);}return;}//开始录制btn.Content = "停  止";var videos = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);StorageFile file = await videos.SaveFolder.CreateFileAsync($"{DateTime.Now.Ticks}.mp4", CreationCollisionOption.GenerateUniqueName);RecordingFile = file.Path;  //记录临时路径,停止录制后移动到用户指定的路径var recording = await Capture.PrepareLowLagRecordToStorageFileAsync(MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto), file);//Capture.RecordLimitationExceeded += Capture_RecordLimitationExceeded;教程里说视频最多只能录三个小时,超出后需要额外处理,我这里偷懒了。await recording.StartAsync();MediaRecording = recording;}#endregion}
}

然后是xaml,虽然非常简单,但防止有纯萌新,也贴一下吧:

<Window x:Class="VideoCapture.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:VideoCapture"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid Background="#333"><Image x:Name="FrameImage"/><TextBlock x:Name="Rel" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="16" Foreground="#f21"/><Button Width="88" Height="33" Content="拍  照" Click="Photo_Click" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="16"/><Button Width="88" Height="33" Content="录  制" Click="Record_Click" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0 0 126 16"/></Grid>
</Window>
http://www.hskmm.com/?act=detail&tid=36584

相关文章:

  • 链队
  • Gitee本土化战略深度解析:中国开发者生态的合规与效率革命
  • 2025年10月上海装修公司口碑榜:十强对比评测
  • 02-GPIO-铁头山羊STM32标准库新版笔记
  • 【多校支持、EI检索】第六届大数据与社会科学国际学术会议(ICBDSS 2025)
  • IDC iPaaS市场报告解读:独立厂商与云巨头的“双轨竞速”
  • 2025年10月仓储管理系统推荐:鸿链云仓领衔五大方案对比评测榜
  • 2025年10月电动叉车销售公司排行榜:五家主流服务商对比评测
  • 2025年口罩机厂家权威推荐榜:全自动口罩机器,全自动KN95口罩机源头企业综合评测与采购指南
  • 2025年包装机厂家权威推荐榜单:全自动包装机/包装生产线/非标定制机器与生产线专业选购指南
  • Timing Signoff 技术精要
  • Oracle故障处理:10G RAC srvctl注册实例正常,但是crs切不能管理实例
  • 杂题选做-2
  • 读书笔记:白话解读Oracle范围分区
  • 2025年10月人形机器人场景落地商评测榜:赛飞特工程技术集团数据透视
  • 科林电气与利驰软件续签合作,共启数字化协同新篇章!
  • 详细介绍:资产信息收集与指纹识别:HTTPX联动工具实战指南
  • 易基因:剑桥大学团队利用微量WGBS等揭示DNMT3L在胎盘发育中的DNA甲基化调控机制:CSC(IF20.5)
  • 10.22
  • 和橘子学AI创作【500集120实战】
  • iOS 26 性能调试工具全景指南 多工具组合 + 实战流程
  • 102302134陈蔡裔数据采集第一次作业
  • 2025年10月蒸汽发生器品牌榜:辰能能源领衔五强对比
  • 2025吹塑机厂家权威推荐:鼎浩包装科技实力企业,专业定制高效生产方案
  • 2025年10月蒸汽发生器品牌评测榜:节能与合规全解析
  • 活动邀请丨2025 全球机器学习技术大会
  • 2025年10月低空经济核心公司对比评测榜:五强排名与全维度数据解析
  • 01-准备-铁头山羊STM32标准库新版笔记
  • platformio上ESP32-s3,N16R8选择板子的解决方案
  • 6. Z 字形变换