Index: AudioPin.cpp =================================================================== --- AudioPin.cpp (revision 27941) +++ AudioPin.cpp (working copy) @@ -181,7 +181,6 @@ return CSourceStream::BreakConnect(); } -extern int ShowBuffer; HRESULT CAudioPin::FillBuffer(IMediaSample *pSample) { @@ -200,7 +199,7 @@ //we dont try to read any packets, but simply return... if (m_pTsReaderFilter->IsSeeking() || m_pTsReaderFilter->IsStopping())// /*|| m_bSeeking*/ || m_pTsReaderFilter->IsSeekingToEof()) { - //if (ShowBuffer) LogDebug("aud:isseeking"); + //if (m_pTsReaderFilter->m_ShowBufferAudio) LogDebug("aud:isseeking"); Sleep(20); pSample->SetTime(NULL,NULL); pSample->SetActualDataLength(0); @@ -391,11 +390,11 @@ float clock = (double)(RefClock-m_rtStart.m_time)/10000000.0 ; float fTime=(float)cRefTime.Millisecs()/1000.0f - clock ; - if (ShowBuffer || fTime < 0.030) + if (m_pTsReaderFilter->m_ShowBufferAudio || fTime < 0.030) { LogDebug("Aud/Ref : %03.3f, Late Compensated = %03.3f ( %0.3f A/V buffers=%02d/%02d), Clk : %f, State %d", (float)RefTime.Millisecs()/1000.0f, (float)cRefTime.Millisecs()/1000.0f, fTime,cntA,cntV, clock, m_pTsReaderFilter->State()); } - if (ShowBuffer) ShowBuffer--; + if (m_pTsReaderFilter->m_ShowBufferAudio) m_pTsReaderFilter->m_ShowBufferAudio--; } } else Index: DeMultiplexer.cpp =================================================================== --- DeMultiplexer.cpp (revision 27941) +++ DeMultiplexer.cpp (working copy) @@ -802,7 +802,11 @@ if (m_pids.PcrPid==0) return; if (header.Pid==0) return; - if (header.TScrambling) return; + + // 'TScrambling' check commented out - headers are never scrambled, + // so it's safe to detect scrambled payload at PES level (in FillVideo()/FillAudio()) + + //if (header.TScrambling) return; //skip any packets with errors in it if (header.TransportError) return; @@ -896,7 +900,9 @@ { CBuffer *Cbuf=*m_t_vecAudioBuffers.begin(); byte *p = Cbuf->Data() ; - if ((p[0]==0) && (p[1]==0) && (p[2]==1)) + if ((p[0]==0) && (p[1]==0) && (p[2]==1) //Valid start code + && ((p[3] & 0x80)!=0) //Valid stream ID + && ((p[6] & 0xE0)==0x80)) //Valid marker bits and payload not scrambled { //get pts/dts from pes header CPcr pts; @@ -1116,7 +1122,8 @@ return; } - if ((start[0]!=0) || (start[1]!=0) || (start[2]!=1)) + if ((start[0]!=0) || (start[1]!=0) || (start[2]!=1) //Invalid start code + || ((start[3] & 0x80)==0)) //Invalid stream ID { LogDebug("Pes 0-0-1 fail"); m_VideoValidPES=false; @@ -1132,6 +1139,10 @@ } else { // full PES header is available. + if ((start[6] & 0xE0)==0xA0) //Valid marker bits and payload scrambled + { + return; + } CPcr pts; CPcr dts; @@ -1414,7 +1425,8 @@ return; } - if ((start[0]!=0) || (start[1]!=0) || (start[2]!=1)) + if ((start[0]!=0) || (start[1]!=0) || (start[2]!=1) //Invalid start code + || ((start[3] & 0x80)==0)) //Invalid stream ID { LogDebug("Pes 0-0-1 fail"); m_VideoValidPES=false; @@ -1430,6 +1442,10 @@ } else { // full PES header is available. + if ((start[6] & 0xE0)==0xA0) //Valid marker bits and payload scrambled + { + return; + } CPcr pts; CPcr dts; @@ -1640,6 +1656,10 @@ } } } + else + { + m_bSetVideoDiscontinuity = true; + } m_VideoValidPES=true ; // We've just completed a frame, set flag until problem clears it m_pl.RemoveAll() ; } @@ -1666,6 +1686,7 @@ /// if so store it in the subtitle buffers void CDeMultiplexer::FillSubtitle(CTsHeader& header, byte* tsPacket) { + if (header.TScrambling) return; if (m_filter.GetSubtitlePin()->IsConnected()==false) return; if (m_iSubtitleStream<0 || m_iSubtitleStream>=m_subtitleStreams.size()) return; @@ -1717,6 +1738,7 @@ void CDeMultiplexer::FillTeletext(CTsHeader& header, byte* tsPacket) { + if (header.TScrambling) return; if (m_pids.TeletextPid==0) return; if (header.Pid!=m_pids.TeletextPid) return; if ( header.AdaptionFieldOnly() ) return; Index: TsReader.cpp =================================================================== --- TsReader.cpp (revision 27941) +++ TsReader.cpp (working copy) @@ -170,8 +170,9 @@ TCHAR filename[1024]; GetLogFile(filename); ::DeleteFile(filename); - LogDebug("--------- Fast forward debug ---------"); - LogDebug("-------------- v0.4.7 ----------------"); + LogDebug("---- FFDShow A/V sync fix ------------"); + LogDebug("---- Relaxed scrambling mod ----------"); + LogDebug("---------- v0.4.10 -------------------"); m_fileReader=NULL; m_fileDuration=NULL; @@ -223,6 +224,11 @@ m_bForceSeekOnStop=false ; m_bForceSeekAfterRateChange=false ; m_bSeekAfterRcDone=false ; + m_videoDecoderCLSID=GUID_NULL; + m_bFastSyncFFDShow=false; + m_ShowBufferAudio = INIT_SHOWBUFFERAUDIO; + m_ShowBufferVideo = INIT_SHOWBUFFERVIDEO; + m_MPmainThreadID = GetCurrentThreadId() ; } @@ -356,7 +362,7 @@ return S_OK; } -extern int ShowBuffer; + STDMETHODIMP CTsReaderFilter::SetRelaxedMode(BOOL relaxedReading) { LogDebug("SetRelaxedMode"); @@ -415,7 +421,8 @@ LogDebug("Elapsed time from pause to Audio/Video ( total zapping time ) : %d mS",GetTickCount()-m_lastPause); } - ShowBuffer=40; + m_ShowBufferVideo = INIT_SHOWBUFFERVIDEO; + m_ShowBufferAudio = INIT_SHOWBUFFERAUDIO; CAutoLock cObjectLock(m_pLock); @@ -502,10 +509,13 @@ { return m_bTimeShifting; } -extern int ShowBuffer; + + STDMETHODIMP CTsReaderFilter::Pause() { - ShowBuffer=100 ; + //m_ShowBufferVideo = INIT_SHOWBUFFERVIDEO; + //m_ShowBufferAudio = INIT_SHOWBUFFERAUDIO; + LogDebug("CTsReaderFilter::Pause() - IsTimeShifting = %d - state = %d", IsTimeShifting(), m_State); CAutoLock cObjectLock(m_pLock); @@ -1550,6 +1560,29 @@ *pMediaPos = (m_MediaPos + m_LastTime - m_BaseTime) ; return ; } + +//---------------------------------------------------- +// Derived from FFDShow code +CLSID CTsReaderFilter::GetCLSIDFromPin(IPin* pPin) +{ + if (!pPin) + { + return GUID_NULL; + } + CLSID clsid=GUID_NULL; + PIN_INFO pi; + if (SUCCEEDED(pPin->QueryPinInfo(&pi))) + { + if (pi.pFilter) // IBaseFilter pointer + { + pi.pFilter->GetClassID(&clsid); + pi.pFilter->Release(); + } + } + return clsid; +} + + //////////////////////////////////////////////////////////////////////// // // Exported entry points for registration and unregistration Index: TsReader.h =================================================================== --- TsReader.h (revision 27941) +++ TsReader.h (working copy) @@ -32,6 +32,10 @@ #include "IAudioStream.h" #include "ITeletextSource.h" #include + +#define INIT_SHOWBUFFERVIDEO 20 +#define INIT_SHOWBUFFERAUDIO 5 + using namespace std; class CSubtitlePin; @@ -44,6 +48,15 @@ DEFINE_GUID(IID_ITSReader, 0xb9559486, 0xe1bb, 0x45d3, 0xa2, 0xa2, 0x9a, 0x7a, 0xfe, 0x49, 0xb2, 0x4f); //DEFINE_GUID(IID_ITSReaderAudioChange, 0xb9559486, 0xe1bb, 0x45d3, 0xa2, 0xa2, 0x9a, 0x7a, 0xfe, 0x49, 0xb2, 0x5f); +// CLSIDs used to identify connected filters +// {04FE9017-F873-410e-871E-AB91661A4EF7} +DEFINE_GUID(CLSID_FFDSHOWVIDEO, 0x04fe9017, 0xf873, 0x410e, 0x87, 0x1e, 0xab, 0x91, 0x66, 0x1a, 0x4e, 0xf7); +// {0B390488-D80F-4a68-8408-48DC199F0E97} +DEFINE_GUID(CLSID_FFDSHOWDXVA, 0xb0eff97, 0xc750, 0x462c, 0x94, 0x88, 0xb1, 0xe, 0x7d, 0x87, 0xf1, 0xa6); +// {DBF9000E-F08C-4858-B769-C914A0FBB1D7} +DEFINE_GUID(CLSID_FFDSHOWSUBTITLES, 0xdbf9000e, 0xf08c, 0x4858, 0xb7, 0x69, 0xc9, 0x14, 0xa0, 0xfb, 0xb1, 0xd7); + + DECLARE_INTERFACE_(ITSReaderCallback, IUnknown) { STDMETHOD(OnMediaTypeChanged) (int mediaTypes)PURE; @@ -186,6 +199,14 @@ bool m_bForceSeekAfterRateChange; bool m_bSeekAfterRcDone; + int m_ShowBufferAudio; + int m_ShowBufferVideo; + + CLSID m_videoDecoderCLSID; + bool m_bFastSyncFFDShow; + + CLSID GetCLSIDFromPin(IPin* pPin); + protected: void ThreadProc(); Index: VideoPin.cpp =================================================================== --- VideoPin.cpp (revision 27941) +++ VideoPin.cpp (working copy) @@ -125,14 +125,32 @@ //LogDebug("vid:CheckConnect()"); return CBaseOutputPin::CheckConnect(pReceivePin); } + + HRESULT CVideoPin::CompleteConnect(IPin *pReceivePin) { - //LogDebug("vid:CompleteConnect()"); HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin); if (SUCCEEDED(hr)) { + CLSID &ref=m_pTsReaderFilter->GetCLSIDFromPin(pReceivePin); + m_pTsReaderFilter->m_videoDecoderCLSID = ref; + if (m_pTsReaderFilter->m_videoDecoderCLSID == CLSID_FFDSHOWVIDEO) + { + m_pTsReaderFilter->m_bFastSyncFFDShow=true; + LogDebug("vid:CompleteConnect() FFDShow Video Decoder connected, fast sync enabled"); + } + else if (m_pTsReaderFilter->m_videoDecoderCLSID == CLSID_FFDSHOWDXVA) + { + m_pTsReaderFilter->m_bFastSyncFFDShow=true; + LogDebug("vid:CompleteConnect() FFDShow DXVA Video Decoder connected, fast sync enabled"); + } + else + { + m_pTsReaderFilter->m_bFastSyncFFDShow=false; + } + + m_bConnected=true; LogDebug("vid:CompleteConnect() done"); - m_bConnected=true; } else { @@ -245,7 +263,6 @@ return S_FALSE; } -int ShowBuffer=100; HRESULT CVideoPin::FillBuffer(IMediaSample *pSample) { @@ -264,7 +281,7 @@ //we dont try to read any packets, but simply return... if (m_pTsReaderFilter->IsSeeking() || m_pTsReaderFilter->IsStopping()) //|| m_bSeeking*/ || m_pTsReaderFilter->IsSeekingToEof()) { - //if (ShowBuffer) LogDebug("vid:isseeking:%d %d",m_pTsReaderFilter->IsSeeking() ,m_bSeeking); + //if (m_pTsReaderFilter->m_ShowBufferVideo) LogDebug("vid:isseeking:%d %d",m_pTsReaderFilter->IsSeeking() ,m_bSeeking); Sleep(5); pSample->SetActualDataLength(0); return NOERROR; @@ -304,30 +321,29 @@ cRefTime -= m_rtStart; //adjust the timestamp with the compensation cRefTime -= m_pTsReaderFilter->Compensation; - cRefTime -= AddOffset; - cRefTime -= m_pTsReaderFilter->m_ClockOnStart.m_time; - CRefTime Dur; - Dur = (m_pTsReaderFilter->AddVideoComp.m_time * DRIFT_RATE); + //CRefTime Dur; + //Dur = (m_pTsReaderFilter->AddVideoComp.m_time * DRIFT_RATE); - if (cRefTime.m_time < (m_pTsReaderFilter->AddVideoComp.m_time * DRIFT_RATE)) + if (!m_pTsReaderFilter->m_bFastSyncFFDShow && (cRefTime.m_time < (m_pTsReaderFilter->AddVideoComp.m_time * DRIFT_RATE)) ) { // Ambass : try to stretch video after zapping + cRefTime -= AddOffset; + cRefTime -= m_pTsReaderFilter->m_ClockOnStart.m_time; AddOffset = cRefTime.m_time / DRIFT_RATE; // LogDebug("%03.3f, %03.3f, %03.3f", (float)AddOffset.Millisecs()/1000.0f,(float)cRefTime.Millisecs()/1000.0f, (float)m_pTsReaderFilter->AddVideoComp.Millisecs()/1000.0f); // m_pTsReaderFilter->AddVideoComp.m_time =0; + cRefTime += AddOffset; + cRefTime += m_pTsReaderFilter->m_ClockOnStart.m_time; } - - cRefTime += AddOffset; - cRefTime += m_pTsReaderFilter->m_ClockOnStart.m_time; if (cRefTime.m_time >= 0) + { Sleep(1); // Ambass : avoid blocking audio FillBuffer method ( on audio/video starting ) by excessive video Fill buffer preemption + } + m_bPresentSample = true; - //else - // Sample is too late. - //m_ShowSample = false ; - } + } if (m_bPresentSample) { @@ -346,17 +362,44 @@ //now we have the final timestamp, set timestamp in sample REFERENCE_TIME refTime=(REFERENCE_TIME)cRefTime; pSample->SetSyncPoint(TRUE); + + bool stsDiscon = TimestampDisconChecker(refTime); //Update with current timestamp refTime /= m_dRateSeeking; pSample->SetTime(&refTime,&refTime); if (m_dRateSeeking == 1.0) { + if (m_pTsReaderFilter->m_bFastSyncFFDShow) + { + if (stsDiscon || (pSample->IsDiscontinuity()==S_OK)) + { + pSample->SetDiscontinuity(TRUE); + m_delayedDiscont = 2; + } + + if ((m_delayedDiscont > 0) && (buffer->GetFrameType() == 'I')) + { + if ((buffer->GetVideoServiceType() == SERVICE_TYPE_VIDEO_MPEG1 || + buffer->GetVideoServiceType() == SERVICE_TYPE_VIDEO_MPEG2)) + { + //Use delayed discontinuity + pSample->SetDiscontinuity(TRUE); + m_delayedDiscont--; + LogDebug("vid:set I-frame discontinuity"); + } + else + { + m_delayedDiscont = 0; + } + } + } + REFERENCE_TIME RefClock = 0; m_pTsReaderFilter->GetMediaPosition(&RefClock) ; float clock = (double)(RefClock-m_rtStart.m_time)/10000000.0 ; float fTime=(float)cRefTime.Millisecs()/1000.0f - clock ; - if (ShowBuffer || fTime < 0.030) + if (m_pTsReaderFilter->m_ShowBufferVideo || fTime < 0.030) { int cntA, cntV; CRefTime firstAudio, lastAudio; @@ -364,9 +407,10 @@ cntA = demux.GetAudioBufferPts(firstAudio, lastAudio); cntV = demux.GetVideoBufferPts(firstVideo, lastVideo) + 1; - LogDebug("Vid/Ref : %03.3f, Late %c-frame(%02d), Compensated = %03.3f ( %0.3f A/V buffers=%02d/%02d), Clk : %f, State %d", (float)RefTime.Millisecs()/1000.0f,buffer->GetFrameType(),buffer->GetFrameCount(), (float)cRefTime.Millisecs()/1000.0f, fTime, cntA,cntV,clock, m_pTsReaderFilter->State()); - } - if (ShowBuffer) ShowBuffer--; + LogDebug("Vid/Ref : %03.3f, Late %c-frame(%02d), Compensated = %03.3f ( %0.3f A/V buffers=%02d/%02d), Clk : %f, State %d, TsMeanDiff %0.3f ms", (float)RefTime.Millisecs()/1000.0f,buffer->GetFrameType(),buffer->GetFrameCount(), (float)cRefTime.Millisecs()/1000.0f, fTime, cntA,cntV,clock, m_pTsReaderFilter->State(), (float)m_fMTDMean/10000.0f); + } + + if (m_pTsReaderFilter->m_ShowBufferVideo) m_pTsReaderFilter->m_ShowBufferVideo--; } } else @@ -402,6 +446,48 @@ return NOERROR; } +// Check for timestamp discontinuities +bool CVideoPin::TimestampDisconChecker(REFERENCE_TIME timeStamp) +{ + bool mtdDiscontinuity = false; + REFERENCE_TIME stsDiff = timeStamp - m_llLastMTDts; + m_llLastMTDts = timeStamp; + + // Calculate the mean timestamp difference + if (m_nNextMTD >= NB_MTDSIZE) + { + m_fMTDMean = m_llMTDSumAvg / (REFERENCE_TIME)NB_MTDSIZE; + } + else if (m_nNextMTD > 0) + { + m_fMTDMean = m_llMTDSumAvg / (REFERENCE_TIME)m_nNextMTD; + } + else + { + m_fMTDMean = stsDiff; + } + + // Check for discontinuity + if (stsDiff > (m_fMTDMean + (300 * 10000))) // diff - mean > 300ms + { + mtdDiscontinuity = true; + LogDebug("vid:Timestamp discontinuity, TsDiff %0.3f ms, TsMeanDiff %0.3f ms, samples %d", (float)stsDiff/10000.0f, (float)m_fMTDMean/10000.0f, m_nNextMTD); + } + + // Update the rolling timestamp difference sum + // (these values are initialised in OnThreadStartPlay()) + int tempNextMTD = (m_nNextMTD % NB_MTDSIZE); + m_llMTDSumAvg -= m_pllMTD[tempNextMTD]; + m_pllMTD[tempNextMTD] = stsDiff; + m_llMTDSumAvg += stsDiff; + m_nNextMTD++; + + //LogDebug("vid:TimestampDisconChecker, nextMTD %d, TsMeanDiff %0.3f, stsDiff %0.3f", m_nNextMTD, (float)m_fMTDMean/10000.0f, (float)stsDiff/10000.0f); + + return mtdDiscontinuity; +} + + //****************************************************** /// Called when thread is about to start delivering data to the codec /// @@ -411,7 +497,14 @@ //is not belonging to any previous data m_bDiscontinuity=TRUE; m_bPresentSample=false; + m_delayedDiscont = 0; + m_llLastMTDts = 0; + m_nNextMTD = 0; + m_fMTDMean = 0; + m_llMTDSumAvg = 0; + ZeroMemory((void*)&m_pllMTD, sizeof(REFERENCE_TIME) * NB_MTDSIZE); + LogDebug("vid:OnThreadStartPlay(%f) %02.2f %d", (float)m_rtStart.Millisecs()/1000.0f,m_dRateSeeking,m_pTsReaderFilter->IsSeeking()); //start playing @@ -437,6 +530,12 @@ m_dRateSeeking = 1.0; // Reset to a reasonable value. return E_FAIL; } + if( m_dRateSeeking > 2.0 && m_pTsReaderFilter->m_videoDecoderCLSID == CLSID_FFDSHOWVIDEO) + { + //FFDShow video decoder doesn't handle rate > 2.0 properly + m_dRateSeeking = 1.0; // Reset to a reasonable value. + return E_FAIL; + } LogDebug("vid: ChangeRate, m_dRateSeeking %f, Force seek done %d",(float)m_dRateSeeking, m_pTsReaderFilter->m_bSeekAfterRcDone); if (!m_pTsReaderFilter->m_bSeekAfterRcDone) //Don't force seek if another pin has already triggered it { Index: VideoPin.h =================================================================== --- VideoPin.h (revision 27941) +++ VideoPin.h (working copy) @@ -23,6 +23,8 @@ #define __VideoPin_H #include "tsreader.h" +#define NB_MTDSIZE 32 + class CVideoPin : public CSourceStream, public CSourceSeeking { public: @@ -39,6 +41,7 @@ HRESULT FillBuffer(IMediaSample *pSample); HRESULT BreakConnect(); + HRESULT DoBufferProcessingLoop(void); // CSourceSeeking @@ -66,6 +69,17 @@ bool m_bPresentSample; FILTER_INFO m_filterInfo; + + int m_delayedDiscont; + + bool TimestampDisconChecker(REFERENCE_TIME timeStamp); + + REFERENCE_TIME m_pllMTD [NB_MTDSIZE]; // timestamp buffer for average Video sample timestamp calculation + REFERENCE_TIME m_llLastMTDts; + int m_nNextMTD; + REFERENCE_TIME m_fMTDMean; + REFERENCE_TIME m_llMTDSumAvg; + }; #endif