繁体   English   中英

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

[英]How to save media element's current frame?

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

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

但是它不允许捕获媒体元素。 请为我提供解决方法。 我不确定,我不确定是否可以通过使用StorageItemThumbnail Class的位置从特定位置提取缩略图来实现。 即使这样,也将是缩略图而不是当前帧的图像。 请有人帮忙。 这是我的应用程序的基本功能!

在我开发的数字标牌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拍摄最终屏幕截图。 这不是一个干净的解决方案,但这是不考虑MediaElement的RenderTargetBitmap的解决方法。

编辑:我忘记了对应用程序中内部函数的引用,因此我无法使用前面的代码,而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);
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM