0

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?

  1. Pick one graph as the master.
  2. 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.
  3. Query the graphs for IMediaFilter, get the clock from the master graph using GetSyncSource and pass it to the other graphs using SetSyncSource.
  4. Pause all the graphs.
  5. Wait until GetState returns S_OK (the pause is complete).
  6. Get the time from the graph and add 10ms or so.
  7. 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?

Community
  • 1
  • 1
HBZ
  • 499
  • 4
  • 11
  • can you show your codes ? – Raptor Jun 14 '13 at 06:07
  • It should display an initial video frame immediately, and then next one in up to your 1000 ms of added delay. This is behavior by design. – Roman R. Jun 14 '13 at 06:14
  • BTW another reason is that your filter graph has some filter which strip video time off the frames. In this case renderer does not enforce times and displays frames immediately. – Roman R. Jun 14 '13 at 07:14

1 Answers1

1

This might be caused by your audio renderer. If it is providing the clock, it might start playback immediately, ignoring the actual start time (I've seen this problem with some audio renderers). Then once it's running, the time on the clock is what you're playing. So you will see the stream time jump forward. Maybe you could check that?

Geraint Davies
  • 2,847
  • 15
  • 9