如何保存媒体元素的当前帧?

时间:2017-09-20 05:04:40

标签: c# uwp

我正在开发像vlc这样的媒体播放器。其中一项功能是在播放期间保存快照。我可以使用rendertargetbitmap类成功保存任何元素的快照,使用以下c#代码。

RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(); 
await renderTargetBitmap.RenderAsync(RenderedGrid, width, height); 
RenderedImage.Source = renderTargetBitmap;

但它不允许捕获媒体元素。请帮我解决一下。我想,我不确定,是否可以通过使用StorageItemThumbnail类的位置从特定位置提取缩略图。如果是这样,它将是缩略图而不是当前帧的图像。有人帮忙。这是我的应用程序的基本功能!

1 个答案:

答案 0 :(得分:2)

在我开发的数字标牌uwp应用程序中,我也有类似的要求。在网上搜索我如何做,我发现在UWP中你不能直接截取媒体元素的屏幕截图,因为保护策略(例如你可以创建一个小应用程序,捕获没有DRM的视频文件的所有帧管理)。

我提出了一个解决方法:我在本地拥有视频文件,因此我可以在某一点提取单个帧并将其保存到磁盘上,并在屏幕上呈现相同的视频尺寸(这样我就有了一个完整的稍后使用的帧的图像)。为此,我写了这个函数:

private async Task<string> Capture(StorageFile file, TimeSpan timeOfFrame, Size imageDimension)
{
    if (file == null)
{
    return null;
}

//Get FrameWidth & FrameHeight
List<string> encodingPropertiesToRetrieve = new List<string>();
encodingPropertiesToRetrieve.Add("System.Video.FrameHeight");
encodingPropertiesToRetrieve.Add("System.Video.FrameWidth");
IDictionary<string, object> encodingProperties = await file.Properties.RetrievePropertiesAsync(encodingPropertiesToRetrieve);
uint frameHeight = (uint)encodingProperties["System.Video.FrameHeight"];
uint frameWidth = (uint)encodingProperties["System.Video.FrameWidth"];

//Get image stream
var clip = await MediaClip.CreateFromFileAsync(file);
var composition = new MediaComposition();
composition.Clips.Add(clip);
var imageStream = await composition.GetThumbnailAsync(timeOfFrame, (int)frameWidth, (int)frameHeight, VideoFramePrecision.NearestFrame);

//Create BMP
var writableBitmap = new WriteableBitmap((int)frameWidth, (int)frameHeight);
writableBitmap.SetSource(imageStream);

//Get stream from BMP
string mediaCaptureFileName = "IMG" + Guid.NewGuid().ToString().Substring(0, 4) + ".jpg";
var saveAsTarget = await CreateMediaFile(mediaCaptureFileName);
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();
    }
}
return saveAsTarget.Name;
}

此功能将帧作为图像保存到磁盘并返回其文件名。 在我的应用程序中,我有更多“小部件”(其中一些带有图像,有些带有视频)所以我使用此功能来获取视频帧并将新图像放在具有相同尺寸和更高Canvas Z的媒体元素之上-Index以使用RenderTargetBitmap拍摄最终截图。 这不是一个干净的解决方案,但它是RenderTargetBitmap不考虑MediaElement的解决方法。

<强> 编辑: 我在应用程序中忘记了对内部函数的引用,因此我对前面的代码进行了解释,而var file = await _mediaEngine.CreateMediaFile($"{Guid.NewGuid().ToString()}.jpg");行现在是var file = await CreateMediaFile($"{Guid.NewGuid().ToString()}.jpg");

异步函数CreateMediaFile(string fileName)只是在磁盘上创建一个新文件:

public async Task<StorageFile> CreateMediaFile(string filename)
    {
        StorageFolder _mediaFolder = KnownFolders.PicturesLibrary;
        return await _mediaFolder.CreateFileAsync(filename);
    }