I was trying to play multiple graphs simultaneously with directshow. After doing some research, I found Geraint Davies' awesome solution at How to play multiple videos in sync over multiple monitors using directshow?
- Pick one graph as the master.
- Make sure the master has a clock. This is normally done when going active, but you can force it to happen earlier by calling SetDefaultSyncSource on the graph.
- Query the graphs for IMediaFilter, get the clock from the master graph using GetSyncSource and pass it to the other graphs using SetSyncSource.
- Pause all the graphs.
- Wait until GetState returns S_OK (the pause is complete).
- Get the time from the graph and add 10ms or so.
- Call IMediaFilter::Run on all graphs, passing this time (now + 10ms) as the parameter.
The results turned out pretty good, the video looked in sync visually. However, something really bothered me when I was testing with IMediaFilter::Run.
I tried to pass in a large offset value into IMediaFilter::Run such as (now + 1000ms), the video still seems to start immediately visually instead of delaying 1000ms before it starts.
Is this behavior normal or I am doing something wrong?
Edit: Thank you for your quick reply, Roman R. I assume when you say "video time", you mean timestamp? I will check them out right away.
Edit: The video doesn't only show the first frame, it just plays normally without any delay. I also posted some rough code of what I did. Hope someone can shed some light on it.
CComQIPtr<IMediaFilter>pMasterF(m_pMasterGraphBuilder);
CComQIPtr<IMediaFilter>pSlaveF(m_pSlaveGraphBuilder);
IReferenceClock* pClock;
HRESULT clkHR = pMasterF->GetSyncSource(&pClock); // Get Master Clock
pSlaveF->SetSyncSource(pClock); //Set slave to Master Clock
pMasterF->Pause(); //Pause Master
pSlaveF->Pause(); // Pause slave
FILTER_STATE sFs;
HRESULT hrcue;
hrcue = pMasterF->GetState(0, &sFs);
while(hrcue == VFW_S_STATE_INTERMEDIATE) { // wait for master to cue data
hrcue = pMasterF->GetState(0, &sFs);
if (hrcue == S_OK)
break;
Sleep(5);
}
FILTER_STATE ssFs;
HRESULT hrcues;
hrcues = pSlaveF->GetState(0, &ssFs);
while(hrcues == VFW_S_STATE_INTERMEDIATE) { // wait for slave cue data
hrcue = pSlaveF->GetState(0, &ssFs);
if (hrcues == S_OK)
break;
Sleep(5);
}
REFERENCE_TIME refTime;
HRESULT timeHR = pClock->GetTime(&refTime); // get master ref time
REFERENCE_TIME temp = refTime+100000000000000; // now + offset
pMF->Run(temp); // run master
pSF->Run(temp); // run slave
As you can see, I set some ridiculous offset. However, I still can't see any delay visually. Perhaps I got the "now" time wrong?
Edit: Thank you for responding, Geraint. I do found every IReferenceClock::GetTime call(pClock->GetTime) gives me a reference time with huge leap (probably due to huge offset I gave). Is that what you meant by the stream time jumping forward? How could I check if the Audio Renderer is providing the clock?