问题描述
我必须在Image控件(如视频)中显示来自媒体元素的帧。我试图使用计时器从媒体元素中获取连续帧,并将该位图绑定到Image源。但是,将比例设置为1时,帧抓取似乎很慢。当将比例缩小到0.3或以下时,抓取会非常快速地工作。但是帧的质量正在下降。 有什么办法解决这个问题? 简而言之,我想不加延迟地以原始质量显示从媒体元素到图像源的帧。
<MediaElement x:Name="MediaEL" Volume="0" ScrubbingEnabled="True" SnapsToDevicePixels="True" MediaOpened="MediaEL_MediaOpened" LoadedBehavior="Manual" MediaEnded="MediaEL_MediaEnded" MediaFailed="MediaEL_MediaFailed">
</MediaElement>
<Image Name="ImageViewerMediaEL" />
ScreenShotimer = new dispatcherTimer();
ScreenShotimer.Interval = TimeSpan.FromMilliseconds(35);//35//
ScreenShotimer.Tick += ScreenShotimer_Tick;
public Bitmap TakeScreenshot(MediaElement medElement,double scale)
{
Bitmap screenBitmap = null;
double actualHeight = medElement.NaturalVideoHeight;
double actualWidth = medElement.NaturalVideoWidth;
double renderHeight = actualHeight * scale;
double renderWidth = actualWidth * scale;
if ((int)renderWidth > 0 && (int)renderHeight > 0)
{
rendertargetBitmap rendertarget = new rendertargetBitmap((int)renderWidth,(int)renderHeight,96,PixelFormats.Default);
VisualBrush sourceBrush = new VisualBrush(medElement);
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.Renderopen();
using (drawingContext)
{
drawingContext.PushTransform(new ScaleTransform(scale,scale));
drawingContext.DrawRectangle(sourceBrush,null,new Rect(new System.Windows.Point(0,0),new System.Windows.Point(actualWidth,actualHeight)));
}
rendertarget.Render(drawingVisual);
MemoryStream stream = new MemoryStream();
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rendertarget));
encoder.Save(stream);
screenBitmap = new Bitmap(stream);
}
return screenBitmap;
}
解决方法
在WPF应用程序中似乎不需要使用WinForms Bitmap
。
重用仅在必要时创建并分配给Image元素的Source属性的RenderTargetBitmap的单个实例。
同样,重用DrawingVisual并仅在大小更改时使用VisualBrush绘制矩形。
private readonly DrawingVisual visual = new DrawingVisual();
private RenderTargetBitmap bitmap;
...
private void OnTimerTick(object sender,EventArgs e)
{
var width = MediaEL.NaturalVideoWidth;
var height = MediaEL.NaturalVideoHeight;
if (width > 0 && height > 0)
{
if (bitmap == null ||
bitmap.PixelWidth != width ||
bitmap.PixelHeight != height)
{
using (var dc = visual.RenderOpen())
{
dc.DrawRectangle(
new VisualBrush(MediaEL),null,new Rect(0,width,height));
}
bitmap = new RenderTargetBitmap(
width,height,96,PixelFormats.Default);
ImageViewerMediaEL.Source = bitmap;
}
bitmap.Render(visual);
}
}
上面的代码对于Microsoft的Wildlife.wmv和35毫秒的计时器间隔对我来说很好用。