3

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?

mbaros
  • 825
  • 8
  • 31

1 Answers1

2

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
}
Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • Thanks for the comment, Roman. The idea you suggested seems to be prietty good and working, but seems design-wise I can't do that way. The problem is that my function ReadNextFrame() should be completed at a single function call and return a value.If I wait for the event in the SampleCB code that means it will be an active thread and my function would not be completed. – mbaros Aug 31 '17 at 09:17
  • Your top level `ReadNextFrame` might wait for an event set by `SampleCB`. Once synchronized, `ReadNextFrame` would copy the data prepared by the callback and set another event to release callback wait and continue to get next frame. `ReadNextFrame`, in turn, already has a copy of data to return back to its caller. – Roman R. Aug 31 '17 at 09:50
  • Can you please explain how to do #4 a bit detailed? Thank you very much. – mbaros Aug 31 '17 at 13:02
  • I implemented the above pseudo code with 2 shared boolean variables `is_dataAvailable` and `is_nextFrameRequested`. I just pass that variables (by reference) to callback function. So my main thread waits until the callback functions sets the `is_dataAvailable` and my callback waits until main thread sets the `is_nextFrameRequested`. I built a DLL. The release version hangs in an infinite loop. Debug version works if I don't call ReadNextFrame() to fast (one after each other), otherwise, it also hangs. What is the clever way to handle such a situation? – mbaros Sep 06 '17 at 16:45
  • I noticed you mentioned `event` in your pseudo code. I also tried to do it with DirectShow event handling, but I couldn't make it work too. Any help will be appreciated – mbaros Sep 06 '17 at 16:45
  • 1
    By event I mean regular synchronization object, such as created with `CreateEvent` Win32 API, or I more often use `CEvent` ATL class. – Roman R. Sep 06 '17 at 17:39