c++winapidirectshowsamplegrabber

ISampleGrabberFilter one frame step at a time


I have a graph end point of which is a SampleGrabber where I get uncompressed data of all frames using callback function.

In my top level interface code I want to have a function ReadNextFrame() which gets the data of the next frame in the graph (until it reaches the end of file).

A performance-wise bad implementation would be to pass a nextFrameIndex to grabberCB class specifying which frame I want at this moment. So my callback function would skip all other frames and would pick only the one I want. This is costly since the graph has to travel through the whole file for picking a single frame data.

I noticed there is a IVideoFrameStep interface which is ideally what I want. But it seems that this interface is not compatible with Sample Grabber and it's documentation says:

Decoders that implement frame-accurate seeking under Microsoft DirectShow must implement the AM_KSPROPSETID_FrameStep property set, which is used in conjunction with the IVideoFrameStep interface.

I tried to connect it to my graph but IVideoFrameStep::CanStep() function returned false for me meaning that I can't use it with Sample Grabber.

So my question is: Is there an easy and performance-wise good solution so I can have my graph to keep the current state and then make a single frame forward and get the data using Sample Grabber?


Solution

  • You discovered that IVideoFrameStep has certain requirements to work and the idea behind it is that entire filter graph does state transitions, runs and pauses playing exactly one frame.

    To get all frames once by one using Sample Grabber you can implement an easier approach, which does not have any special requirements.

    1. Have your filter graph running; it would make sense to remove clock from the graph - see IMediaFilter::SetSyncSource(NULL)
    2. Have a Sample Grabber at position of interest with a SampleCB callback
    3. Once you have your SampleCB executed, do your thing with the data and indicate frame availability to higher level code that runs the graph
    4. While inside SampleCB and #3 above is done, do not return from the method and instead fall into wait on a event, which is to indicate that you are ready to continue
    5. High level application code would process a frame and set the event from #4 above allowing SampleCB to exit and to work further on getting a new frame; with the new frame you are repeating from #3 above.

    That is, your SampleCB wait is locking the entire pipeline preserving its state for the required time letting you process frame by frame at the convenient pace.

    Pseudo code for the SampleCB would be (with manual set/reset events):

    data m_Data;
    event m_DataAvailability;
    event m_NextFrameRequest;
    
    void SampleCB(Frame)
    {
      m_Data = Frame.GetData();
      m_DataAvailability.Set();
      m_NextFrameRequest.WaitFor(); // Sleeping here until signaled to continue
      m_NextFrameRequest.Reset();
    }
    
    data ReadNextFrame()
    {
      m_DataAvailability.WaitFor();
      data Data = m_Data;
      m_DataAvailability.Reset(); // Current data processed, we need next portion
      m_NextFrameRequest.Set(); // Indicate that we allow next callback call
      return Data; // Make captured frame available
    }