How to Extract a Still Image from an MP4 at a Specific Time with Media Foundation - A Single .cpp File You Can Paste In

· · Media Foundation, C++, Windows Development, WIC

Bottom line

To grab one still image from an MP4 at a specific time, use IMFSourceReader. It is not a simple “seek once, read once, done” - watch out for these pitfalls:

  • SetCurrentPosition does not guarantee an exact seek (it usually lands on a key frame before the target)
  • Even when ReadSample succeeds, pSample can be NULL
  • Setting the output to MFVideoFormat_RGB32 makes saving easier
  • The fourth byte of RGB32 is not necessarily alpha, so force it to 0xFF before saving the PNG
  • Account for stride and image orientation when repacking into top-down BGRA

The key: seek -> compare timestamps before and after -> copy with stride in mind -> save as PNG. Implement it in that order and it stays stable.

Processing flow

Step API What it does
Open the MP4 MFCreateSourceReaderFromURL Create a media source from the file
Pick video only SetStreamSelection Skip the audio stream
Convert to RGB32 SetCurrentMediaType + video processing Switch to a save-friendly format
Move to the target time SetCurrentPosition Seek in 100ns units
Read a frame ReadSample Get a decoded sample
Compare before and after sample timestamp Pick the frame closest to the target
Save as PNG WIC Write the image file

Frame selection rule

Rules for picking “the frame closest to the target time”:

  1. After seeking, keep calling ReadSample
  2. Hold on to the last sample where timestamp < target
  3. When the first sample with timestamp >= target arrives, compare its distance to the target against the held sample’s distance
  4. Take whichever is closer

Pitfalls

1. SetCurrentPosition is not an exact seek

It lands a bit before the requested position - typically on a key frame. Doing only SetCurrentPosition -> one ReadSample -> save will drift considerably for videos with long GOPs. Always compare timestamps before and after.

2. ReadSample can succeed and still hand you a NULL sample

Don’t just check HRESULT. Also inspect flags (MF_SOURCE_READERF_ENDOFSTREAM at the end of stream) and pSample.

3. Stride and orientation

The image buffer is not necessarily a flat width * bytesPerPixel. There may be padding at the end of each row. Use the actual stride returned by IMF2DBuffer::Lock2D, and remember that bottom-up images can have a negative stride.

4. The fourth byte of RGB32

RGB32 is laid out in memory as B, G, R, X (undefined). That X is not necessarily alpha. Save it to PNG as-is and you may end up with a transparent image, so set the fourth byte to 0xFF before saving.

Implementation flow

Creating the Source Reader

  • MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING = TRUE
  • Set the output type to MFVideoFormat_RGB32
  • Synchronous mode is fine (you only need a single frame)

Acquiring the frame

  • Seek with SetCurrentPosition
  • Loop on ReadSample, comparing timestamps before and after
  • Pick the sample closest to the target

Buffer reshaping

  • Convert to a contiguous buffer with ConvertToContiguousBuffer
  • Get the actual stride via IMF2DBuffer::Lock2D
  • Copy row by row into a top-down buffer (flip vertically for bottom-up)
  • Force alpha to 0xFF

Saving the PNG

  • Encode as PNG with WIC (IWICBitmapEncoder)
  • Use GUID_WICPixelFormat32bppBGRA as the pixel format

Field checklist

Check If you miss it
Compare timestamps before and after the seek You save a frame well before the target time
Look at HRESULT, flags, and pSample together Crash on a NULL dereference
Use the actual stride Image is corrupted or upside down
Force the fourth byte of RGB32 to 0xFF You get a transparent PNG
Verify 0 <= target < duration Exceptions near the end of the file
Reuse the Reader and reseek for batch extraction Needlessly slow

How to run it

ExtractFrameFromMp4.exe <input.mp4> <seconds> <output.png>

Example:

ExtractFrameFromMp4.exe C:\work\input.mp4 12.345 C:\work\frame.png

Wrap-up

  • Seek is not exact - compare before and after to pick the closest frame
  • A successful ReadSample can still give you no sample
  • Absorb stride and orientation before saving
  • Don’t assume the fourth byte of RGB32 is alpha
  • Drops straight into thumbnail generation, representative-frame capture for surveillance footage, and evidence trails in inspection logs

References

Related Articles

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

Related Topics

These topic pages place the article in a broader service and decision context.

Where This Topic Connects

This article connects naturally to the following service pages.

Back to the Blog