C++ 如何在WinRT(8.1)中从MediaElement捕获当前帧?
我正在尝试在WinRT应用程序中实现一个屏幕截图功能,该应用程序通过MediaElement显示视频。我有下面的代码,它保存了一个与MediaElement大小相同的屏幕截图,但是图像是空的(完全黑色)。尝试使用各种类型的媒体文件。如果我在Surface RT上使用Win Key+Vol Down,屏幕截图包括媒体帧内容,但如果我使用以下代码,则周围都是黑色:( 这里的MultimediaItem是我的视图模型类,它有一个唯一的属性,即字符串 “Player”是媒体元素的名称 <代码>有什么不对吗?或者这种方法是错误的,我必须在C++(?)/P>中进入战壕。 另外,我只对WinRT API感兴趣 更新1看起来RenderTargetBitmap不支持此功能,MSDN文档对此进行了澄清。C++ 如何在WinRT(8.1)中从MediaElement捕获当前帧?,c++,windows-runtime,winrt-xaml,rendertargetbitmap,C++,Windows Runtime,Winrt Xaml,Rendertargetbitmap,我正在尝试在WinRT应用程序中实现一个屏幕截图功能,该应用程序通过MediaElement显示视频。我有下面的代码,它保存了一个与MediaElement大小相同的屏幕截图,但是图像是空的(完全黑色)。尝试使用各种类型的媒体文件。如果我在Surface RT上使用Win Key+Vol Down,屏幕截图包括媒体帧内容,但如果我使用以下代码,则周围都是黑色:( 这里的MultimediaItem是我的视图模型类,它有一个唯一的属性,即字符串 “Player”是媒体元素的名称 有什么不对吗?或者
我将感谢使用DirectX C++的任何指针。这是我的一项主要任务,所以我将破解这一种或另一种方式,并用解决方案报告。 < >强>是< <强> >,这有可能——有点棘手,但工作得好。 您不使用
mediaElement
,而是使用StorageFile
本身。
您需要借助名称空间创建可写位图
适用于UWP(Windows 10)
这是一个完整的例子,包括文件拾取、获取视频分辨率以及将图像保存到图片库
TimeSpan timeOfFrame = new TimeSpan(0, 0, 1);//one sec
//pick mp4 file
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
picker.FileTypeFilter.Add(".mp4");
StorageFile pickedFile = await picker.PickSingleFileAsync();
if (pickedFile == null)
{
return;
}
///
//Get video resolution
List<string> encodingPropertiesToRetrieve = new List<string>();
encodingPropertiesToRetrieve.Add("System.Video.FrameHeight");
encodingPropertiesToRetrieve.Add("System.Video.FrameWidth");
IDictionary<string, object> encodingProperties = await pickedFile.Properties.RetrievePropertiesAsync(encodingPropertiesToRetrieve);
uint frameHeight = (uint)encodingProperties["System.Video.FrameHeight"];
uint frameWidth = (uint)encodingProperties["System.Video.FrameWidth"];
///
//Use Windows.Media.Editing to get ImageStream
var clip = await MediaClip.CreateFromFileAsync(pickedFile);
var composition = new MediaComposition();
composition.Clips.Add(clip);
var imageStream = await composition.GetThumbnailAsync(timeOfFrame, (int)frameWidth, (int)frameHeight, VideoFramePrecision.NearestFrame);
///
//generate bitmap
var writableBitmap = new WriteableBitmap((int)frameWidth, (int)frameHeight);
writableBitmap.SetSource(imageStream);
//generate some random name for file in PicturesLibrary
var saveAsTarget = await KnownFolders.PicturesLibrary.CreateFileAsync("IMG" + Guid.NewGuid().ToString().Substring(0, 4) + ".jpg");
//get stream from bitmap
Stream stream = writableBitmap.PixelBuffer.AsStream();
byte[] pixels = new byte[(uint)stream.Length];
await stream.ReadAsync(pixels, 0, pixels.Length);
using (var writeStream = await saveAsTarget.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, writeStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied,
(uint)writableBitmap.PixelWidth,
(uint)writableBitmap.PixelHeight,
96,
96,
pixels);
await encoder.FlushAsync();
using (var outputStream = writeStream.GetOutputStreamAt(0))
{
await outputStream.FlushAsync();
}
}
TimeSpan timeoffframe=new TimeSpan(0,0,1);//一秒
//选择mp4文件
var picker=new Windows.Storage.Pickers.FileOpenPicker();
picker.SuggestedStartLocation=Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
picker.FileTypeFilter.Add(“.mp4”);
StorageFile pickedFile=Wait picker.PickSingleFileAsync();
if(pickedFile==null)
{
返回;
}
///
//获取视频分辨率
List encodingPropertiesToRetrieve=新列表();
encodingPropertiesToRetrieve.Add(“System.Video.FrameHeight”);
encodingPropertiesToRetrieve.Add(“System.Video.FrameWidth”);
IDictionary encodingProperties=await pickedFile.Properties.RetrievePropertiesAsync(encodingPropertiesToRetrieve);
uint frameHeight=(uint)encodingProperties[“System.Video.frameHeight”];
uint frameWidth=(uint)encodingProperties[“System.Video.frameWidth”];
///
//使用Windows.Media.Editing获取ImageStream
var clip=wait MediaClip.CreateFromFileAsync(pickedFile);
var composition=新的MediaComposition();
合成.剪辑.添加(剪辑);
var imageStream=wait composition.GetThumbnailAsync(timeOfFrame,(int)frameWidth,(int)frameHeight,VideoFramePrecision.NearestFrame);
///
//生成位图
var WriteableBitmap=新的WriteableBitmap((int)frameWidth,(int)frameHeight);
writableBitmap.SetSource(imageStream);
//为PicturesLibrary中的文件生成一些随机名称
var saveAsTarget=await KnownFolders.PicturesLibrary.CreateFileAsync(“IMG”+Guid.NewGuid().ToString().Substring(0,4)+“.jpg”);
//从位图中获取流
Stream=writableBitmap.PixelBuffer.AsStream();
字节[]像素=新字节[(uint)stream.Length];
wait stream.ReadAsync(像素,0,像素,长度);
使用(var writeStream=await saveAsTarget.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder=等待BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId,writeStream);
编码器.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.预乘,
(uint)writableBitmap.PixelWidth,
(uint)writableBitmap.PixelHeight,
96,
96,
像素);
等待编码器。FlushAsync();
使用(var outputStream=writeStream.getOutputstreeamat(0))
{
等待outputStream.FlushAsync();
}
}
是的……我花了很多时间在这上面好吧,我已经设法从MediaElement通过按键来制作快照 我正在使用SetMediaStreamSource方法将MediaStreamSource对象传递给MediaElement。MediaStreamSource具有事件SampleRequested,基本上每次绘制新帧时都会激发该事件SampleRequested。然后使用布尔值控制何时创建位图
private async void MediaStream_SampleRequested(MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
if (!takeSnapshot)
{
return;
}
takeSnapshot = false;
Task.Run(() => DecodeAndSaveVideoFrame(args.Request.Sample));
}
在这之后,剩下的就是对压缩后的图像进行解码,并将其转换为WriteableBitmap。图像是(或者至少在我的例子中是)YUV fromat中的。您可以使用
byte[] yvuArray = sample.Buffer.ToArray();
然后从该数组中获取数据并将其转换为RGB。不幸的是,我无法发布完整的代码,但我将给您一些提示:
这里有一个wiki,描述了YUV到RGB的转换是如何工作的
我采用了哪种解决方案(并且工作得非常完美)。为了更精确,您必须分析方法NV12Converter的工作原理
最后一件事是在按下按钮或执行其他活动后将takeSnapshot boolean更改为true:)。我不确定是否可以将MediaElement渲染为位图。您是否在其他类型的元素上尝试过代码?也看看这个:是的,看起来这在目前是不可能的。在我处理它时,也可以保持它对于C++解决方案的一些指针。你可能想使用MediaFoundation。DirectX与MF一起使用也可能有意义。不过,我现在找不到与之配合使用的接口。多亏了菲利普,我一直在想,自定义媒体转换可能会奏效,但它太深了,所以进展很慢。希望这个周末我能抽出一些时间。首先完成其他功能;-)@JakubWisniewski抱歉,我还没有机会深入了解它。由于Windows10UPnP播放被破坏,我的应用程序现在已经停止使用,我几乎没有什么可用的
byte[] yvuArray = sample.Buffer.ToArray();