3

我正在尝试在通过 MediaElement 显示视频的 WinRT 应用程序中实现屏幕截图功能。我有以下代码,它保存了一个 MediaElement 大小的屏幕截图,但图像是空的(完全黑色)。尝试使用各种类型的媒体文件。如果我在 Surface RT 上执行 Win Key + Vol Down,则屏幕截图包括媒体帧内容,但如果我使用以下代码,则周围一片漆黑:(

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++ 进入战壕吗?

PS 我只对 WinRT API 感兴趣。

更新 1看起来 RenderTargetBitmap 不支持这一点,MSDN 文档澄清了它http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.media.imaging.rendertargetbitmap。我将不胜感激有关如何使用 DirectX C++ 执行此操作的任何指示。这对我来说是一项重大任务,所以我会以一种或另一种方式破解这个问题并报告解决方案。

4

2 回答 2

2

好的,我已经设法在按下按钮时从 MediaElement 制作快照以工作。

我正在使用 SetMediaStreamSource 方法将 MediaStreamSource 对象传递给 MediaElement。MediaStreamSource 具有事件 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。不幸的是,我无法发布整个代码,但我会给你一些提示:

YUV 到 RGB wiki在这里你有 wiki 描述 YUV 到 RGB 转换是如何工作的。

在这里,我找到了我已经适应的解决方案(并且完美运行)的python 项目。更准确地说,您必须分析 NV12Converter 方法的工作原理。

最后一件事是在按下按钮或执行其他活动后将 takeSnapshot 布尔值更改为 true :)。

于 2016-03-15T11:04:37.287 回答
2

的,这是可能的——有点棘手,但效果很好。

你不使用mediaElement,但StorageFile它本身。您需要在Windows.Media.EditingwritableBitmap命名空间的帮助下创建。

在 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();
            }
        }

是的...我为此花了很多时间

于 2016-02-12T16:14:29.577 回答