locked
Capture screenshot of video playing in MediaElement RRS feed

  • Question

  • Hi everyone:

      I have a task to take picture of video which playing in MediaElement in Windows Store APP(8.1). But I find there isn't a way like WPF to take picture of video in windows store. Maybe MFT or Direct X can do it, but I didn't find any useful example. I'm not good at C++. If you have any userful information, better a sample code, it would be great.

    Thanks.

    Tuesday, January 21, 2014 2:26 AM

Answers

  • You will need to write an MFT for this in C++. We don't have an exact example, but the Media extensions sample will demonstrate the basic techniques needed to get started.
    • Marked as answer by Anne Jing Tuesday, January 28, 2014 1:59 AM
    Tuesday, January 21, 2014 2:38 AM
    Moderator

All replies

  • You will need to write an MFT for this in C++. We don't have an exact example, but the Media extensions sample will demonstrate the basic techniques needed to get started.
    • Marked as answer by Anne Jing Tuesday, January 28, 2014 1:59 AM
    Tuesday, January 21, 2014 2:38 AM
    Moderator
  • Hi,

    I am also trying to get video screenshot of the current video frame playing on MediaElement on Windows Phone 8.1 but no success so far. I could get a static screenshot using RenderTargetBitmap but video area gets blank.

    Any help on this case?

    Thanks a lot

    Monday, May 5, 2014 10:07 PM
  • You will need to write an MFT to grab the frame from the video feed. RenderTargetBitmap cannot see the video or DX output (see the RenderTargetBitmap documentation for more details).

    --Rob

    Monday, May 12, 2014 4:57 PM
    Moderator
  • I had this same need and wound up using Media Foundation (in a separate C++ runtime component DLL and it works great. MF is intimidating at first but it's not bad once you get used to the COM syntax.

    The basic steps are numerous, but straightforward

    1) Call MFStartup

    2) Get an IMFByteStream from MFCreateMFByteStreamOnStreamEx using a RandomAccessStream you got from a StorageFile or whatnot

    3) MFCreateSourceReaderFromByteStream to create an IMFSourceReader. (Make sure to set the MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING attribute to 1, assuming you want ARGB data. This enables software decoding; many hardware decoders don't output ARGB data. That's one of the reasons, I suspect, MediaElement does not include this functionality built-in).

    4) Create a "partial media type" IMFMediaType using MFCreateMediaType, and calling SetGuid(MF_MT_MAJOR_TYPE, MFMediaType_Video) and SetGuid(MF_MT_SUBTYPE, MFVideoFormat_RGB32).

    5) Apply the partial media type to the source reader using IMFSourceReader.SetCurrentMediaType. What that does is cause the source reader to activate the necessary decoder to provide you uncompressed RGB32 data regardless of what the input data is, but keeps all other parameters (frame rate, width, height, etc.) the same. (Note, you can use the IMFMediaType object to retrieve parameters about the video, like height, width, framerate, etc., some of which MediaElement won't give you.)

    6) Now, to get a frame from the source reader:

    (a) Call IMFSourceReader.SetCurrentPosition. That will take you to the closest keyframe preceding the time you want to seek to. Note it takes "ticks" (100ns increments) which you can always get from TimeSpan.Ticks. 

    (b) Call IMFSourceReader.ReadSample repeatedly until the retrieved sample time is >= your desired sample time. MAKE SURE TO RELEASE the IMFSample each time, until you hit the sample you want, otherwise you'll have a nasty memory leak!

    (c) Once you get the sample you want, get the IMFMediaBuffer using IMFSample.GetBufferByIndex (it should always be index 0).

    (d) Call IMFMediaBuffer.Lock to get a pointer to the raw data

    (e) Use memcpy to copy the raw data into a Platform::Array<BYTE> (aka byte[]) that you give the module from your C# code.

    (f) Unlock the IMFMediaBuffer

    (g) Release the IMFMediaBuffer and the IMFSample!!!

    7) The byte[] you gave the module will now be filled with 32-bpp raw frame image data. You can give that to BitmapEncoder.SetPixelData to make an in-memory bitmap object that you can source a BitmapImage with. Make sure to use BitmapAlphaMode.Ignore when you call BitmapEncoder.SetPixelData because the alpha channel data you got from MF will be all zeroes.

    And you're done. Phew.

    Thursday, May 29, 2014 3:40 PM