我正在尝试在通过MediaElement显示视频的WinRT应用中实现屏幕截图功能。我有以下代码,它保存了一个截图,它是MediaElement的大小,但图像是空的(全黑)。试过各种类型的媒体文件。如果我在Surface RT上执行Win Key + Vol Down,屏幕截图包含Media框架内容,但是如果我使用以下代码,那么它的黑度就是:(
private async Task SaveCurrentFrame()
{
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(Player);
var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
MultimediaItem currentItem = (MultimediaItem)this.DefaultViewModel["Group"];
StorageFolder currentFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
var saveFile = await currentFolder.CreateFileAsync(currentItem.UniqueId + ".png", CreationCollisionOption.ReplaceExisting);
if (saveFile == null)
return;
// Encode the image to the selected file on disk
using (var fileStream = await saveFile.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, fileStream);
encoder.SetPixelData(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Ignore,
(uint)renderTargetBitmap.PixelWidth,
(uint)renderTargetBitmap.PixelHeight,
DisplayInformation.GetForCurrentView().LogicalDpi,
DisplayInformation.GetForCurrentView().LogicalDpi,
pixelBuffer.ToArray());
await encoder.FlushAsync();
}
}
这里MultimediaItem是我的View Model类,其中包含一个字符串的UniqueId属性。
'播放器'是媒体元素的名称。
代码有什么问题或者这种方法是错误的,我要用C ++进入战壕?
P.S。我只对WinRT API感兴趣。
更新1 看起来RenderTargetBitmap不支持此功能,MSDN文档澄清了它http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.rendertargetbitmap。 我会很感激有关如何使用DirectX C ++的指示。这对我来说是一项重大任务,因此我将以这种方式或其他方式进行破解,并使用解决方案进行报告。
答案 0 :(得分:2)
是,这有可能 - 有点棘手,但运作良好。
您不使用mediaElement
,而是StorageFile
本身。
您需要在Windows.Media.Editing命名空间的帮助下创建writableBitmap
。
适用于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();
}
}
是的......我花了很多时间来这个
答案 1 :(得分:2)
好的,我已经设法在按钮按下时从MediaElement制作快照。
我使用SetMediaStreamSource方法将MediaStreamSource对象传递给MediaElement。 MediaStreamSource具有事件SampleRequested,每次绘制新帧时都会基本触发该事件。然后使用布尔I控制何时创建位图
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。不幸的是,我无法发布完整的代码,但我还会给你一些提示:
YUV to RGB wiki这里有wiki描述了YUV到RGB转换的工作原理。
Here I found python project我已经采用了哪种解决方案(并且工作得很好)。更准确地说,你必须分析NV12Converter方法的工作原理。
最后一件事是在按下按钮或进行其他活动后将takeSnapshot boolean更改为true:)。