简体   繁体   中英

Capture each WPF MediaElement frame

Is there a way to capture each WPF MediaElement frame? Like an event that fires at each rendered frame and allows me to access it. If MediaElement does not provide such functionality, how could it be implemented or what other control could I use? On a side note, is there such a control or method that would allow for off-screen fast rendering of media clips with frame capture? (so I could process frames as fast as possible)

Try out my WPF MediaKit project. Allows you to do pretty much anything in WPF with Media. Try out the MediaDetector.cs, it allows you to extract out frames from any time in the media. It's a little buggy as I've never put a lot of time in it, but should work for what you need.

There is no built-in WPF way:

  • MediaElement does not have this ability.
  • BitmapDecoder has the API to request this but using BitmapDecoder to extract frames from arbitrary media is not implemented: It can only extract frames from a few animated bitmap formats like .gif.

I was able to get frame images from .mpg, .wmv, .mov, .flv, .avi and other movie formats using DirectShow. I constructed a filter graph using DirectShow's COM graph builder interfaces. The resulting filter graph decoded the movie and connected it to a custom renderer filter written in C#. My custom filter received the frame data and converted it into BitmapSource objects for display using BitmapSource.Create.

The DirectShow solution performed quite well, and the managed to unmanaged transition was no big deal, but it took a while to figure out the details of the DirectShow graph building.

If you use your imagination perhaps this snippet can give you some ideas:

MediaPlayer player = new MediaPlayer();
player.Open(new Uri(_inputFilename));
player.ScrubbingEnabled = true;
DrawingVisual dv = new DrawingVisual();
for (int i = 0; i < session.FramesList.Count; i++)
{
    Frame f = session.FramesList[i];
    player.Position = new TimeSpan((long)(f.Time * 10000000));
    using (DrawingContext dc = dv.RenderOpen())
    {
        dc.DrawVideo(player, new Rect(0, 0, 1024, 576));
    }
    RenderTargetBitmap bmp = new RenderTargetBitmap(1024, 576, 96, 96, PixelFormats.Pbgra32);
    bmp.Render(dv);
    f.Thumbnail = bmp.GetAsFrozen() as ImageSource;
    framesListView.Dispatcher.Invoke(() => FramesList.Add(f));
}

you can try this method to output any UI Element including the MediaElement in WPF

 public static void ConvertUiElementToBitmap(UIElement elt, string path)
        {
            double h = elt.RenderSize.Height;
            double w = elt.RenderSize.Width;
            if (h > 0)
            {
                PresentationSource source = PresentationSource.FromVisual(elt);
                RenderTargetBitmap rtb = new RenderTargetBitmap((int)w, (int)h, 96, 96, PixelFormats.Default);

                VisualBrush sourceBrush = new VisualBrush(elt);
                DrawingVisual drawingVisual = new DrawingVisual();
                DrawingContext drawingContext = drawingVisual.RenderOpen();
                using (drawingContext)
                {
                    drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0),
                          new Point(w, h)));
                }
                rtb.Render(drawingVisual);

                // return rtb;
                var encoder = new PngBitmapEncoder();
                var outputFrame = BitmapFrame.Create(rtb);
                encoder.Frames.Add(outputFrame);

                using (var file = System.IO.File.OpenWrite(path))
                {
                    encoder.Save(file);
                }
            }
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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