Index: AudioPin.cpp =================================================================== --- AudioPin.cpp (revision 28131) +++ AudioPin.cpp (working copy) @@ -208,7 +208,11 @@ return NOERROR; } - buffer=demux.GetAudio(); + if (!demux.m_bHoldFileRead) + { + CAutoLock flock (&demux.m_sectionFlushAudio); + buffer=demux.GetAudio(); + } //did we reach the end of the file if (demux.EndOfFile()) // || ((GetTickCount()-m_LastTickCount > 3000) && !m_pTsReaderFilter->IsTimeShifting())) @@ -343,6 +347,8 @@ CRefTime RefTime,cRefTime ; bool HasTimestamp ; + float fTime = 0.0; + float clock = 0.0; //check if it has a timestamp if ((HasTimestamp=buffer->MediaTime(RefTime))) { @@ -351,7 +357,13 @@ //adjust the timestamp with the compensation cRefTime-= m_pTsReaderFilter->Compensation ; - if (cRefTime.m_time >= m_pTsReaderFilter->m_ClockOnStart) // m_rtStart.m_time+m_pTsReaderFilter->Compensation.m_time) // + PRESENT_DELAY) + REFERENCE_TIME RefClock = 0; + m_pTsReaderFilter->GetMediaPosition(&RefClock) ; + clock = (double)(RefClock-m_rtStart.m_time)/10000000.0 ; + fTime = (float)cRefTime.Millisecs()/1000.0f - clock ; + + //if (cRefTime.m_time >= m_pTsReaderFilter->m_ClockOnStart) // m_rtStart.m_time+m_pTsReaderFilter->Compensation.m_time) // + PRESENT_DELAY) + if (fTime >= 0.0) { m_bPresentSample = true ; Sleep(5) ; @@ -360,6 +372,7 @@ { // Sample is too late. m_bPresentSample = false ; + m_bDiscontinuity = TRUE; //Next good sample will be discontinuous } } @@ -385,11 +398,6 @@ pSample->SetTime(&refTime,&refTime); if (m_dRateSeeking == 1.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 (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()); Index: DeMultiplexer.cpp =================================================================== --- DeMultiplexer.cpp (revision 28131) +++ DeMultiplexer.cpp (working copy) @@ -52,9 +52,7 @@ // *** UNCOMMENT THE NEXT LINE TO ENABLE DYNAMIC VIDEO PIN HANDLING!!!! ****** #define USE_DYNAMIC_PINS -extern int ShowBuffer; - CDeMultiplexer::CDeMultiplexer(CTsDuration& duration,CTsReaderFilter& filter) :m_duration(duration) ,m_filter(filter) @@ -87,7 +85,13 @@ m_iAudioReadCount = 0; m_lastVideoPTS.IsValid = false; m_lastAudioPTS.IsValid = false; + m_bFlushDelegated = false; + m_bFlushDelgNow = false; + m_bHoldFileRead = false; + m_bReadAheadFromFile = false; m_mpegParserTriggerFormatChange = false; + m_videoChanged=false; + m_audioChanged=false; SetMediaChanging(false); SetAudioChanging(false); m_DisableDiscontinuitiesFiltering = false; @@ -178,8 +182,11 @@ SetMediaChanging(true); m_filter.m_bForceSeekOnStop=true; // Force stream to be resumed after } - else // Mpeg parser info is required or audio graph is already rebuilding. + else // Mpeg parser info is required or audio graph is already rebuilding. + { LogDebug("SetAudioStream : Media already changing"); // just wait 1st GOP + m_audioChanged = true; + } } } else @@ -366,6 +373,7 @@ void CDeMultiplexer::FlushVideo() { LogDebug("demux:flush video"); + CAutoLock flock (&m_sectionFlushVideo); CAutoLock lock (&m_sectionVideo); delete m_pCurrentVideoBuffer; m_pCurrentVideoBuffer = NULL; @@ -397,19 +405,21 @@ m_FirstVideoSample = 0x7FFFFFFF00000000LL; m_LastVideoSample = 0; m_lastVideoPTS.IsValid = false; - m_VideoValidPES = true; + m_VideoValidPES = false; m_mVideoValidPES = false; m_WaitHeaderPES=-1 ; m_bVideoAtEof=false; m_MinVideoDelta = 10.0 ; m_filter.m_bRenderingClockTooFast=false ; - + m_bSetVideoDiscontinuity=true; + Reset(); // PacketSync reset. } void CDeMultiplexer::FlushAudio() { LogDebug("demux:flush audio"); + CAutoLock flock (&m_sectionFlushAudio); CAutoLock lock (&m_sectionAudio); delete m_pCurrentAudioBuffer; ivecBuffers it = m_vecAudioBuffers.begin(); @@ -437,13 +447,15 @@ m_bAudioAtEof = false; m_MinAudioDelta = 10.0; m_filter.m_bRenderingClockTooFast=false; - + m_bSetAudioDiscontinuity=true; + Reset(); // PacketSync reset. } void CDeMultiplexer::FlushSubtitle() { LogDebug("demux:flush subtitle"); + CAutoLock flock (&m_sectionFlushSubtitle); CAutoLock lock (&m_sectionSubtitle); delete m_pCurrentSubtitleBuffer; ivecBuffers it = m_vecSubtitleBuffers.begin(); @@ -459,8 +471,12 @@ /// Flushes all buffers void CDeMultiplexer::Flush() { + if (m_bHoldFileRead) return; + LogDebug("demux:flushing"); + m_bHoldFileRead = true; //Stall GetVideo()/GetAudio()/GetSubtitle() calls from pins + m_iAudioReadCount = 0; m_LastDataFromRtsp = GetTickCount(); bool holdAudio = HoldAudio(); @@ -475,6 +491,11 @@ SetHoldAudio(holdAudio); SetHoldVideo(holdVideo); SetHoldSubtitle(holdSubtitle); + m_bFlushDelegated = false; + m_bFlushDelgNow = false; + m_bReadAheadFromFile = false; + + m_bHoldFileRead = false; } /// @@ -486,7 +507,7 @@ if (m_currentSubtitlePid==0) return NULL; if (m_bEndOfFile) return NULL; if (m_bHoldSubtitle) return NULL; - + //are there subtitle packets in the buffer? if (m_vecSubtitleBuffers.size()!=0 ) { @@ -506,10 +527,10 @@ // or NULL if there is none available CBuffer* CDeMultiplexer::GetVideo() { - if (m_filter.GetVideoPin()->IsConnected() && ((m_iAudioStream == -1) || IsAudioChanging())) return NULL; + if (m_filter.GetVideoPin()->IsConnected() && (m_iAudioStream == -1)) return NULL; //if there is no video pid, then simply return NULL - if ((m_pids.videoPids.size() > 0 && m_pids.videoPids[0].Pid==0) || IsMediaChanging()) + if ((m_pids.videoPids.size() > 0 && m_pids.videoPids[0].Pid==0) || IsMediaChanging() || IsAudioChanging()) { ReadFromFile(false,true); return NULL; @@ -532,6 +553,13 @@ if (ReadFromFile(false,true)INITIAL_READ_SIZE || GetAudioStreamCount()>0) { #ifdef USE_DYNAMIC_PINS @@ -647,6 +687,7 @@ Flush(); m_streamPcr.Reset(); m_bStarting=false; + LogDebug("demux:Start() end1 BytesProcessed:%d", dwBytesProcessed); return; } dwBytesProcessed+=BytesRead; @@ -654,6 +695,7 @@ m_streamPcr.Reset(); m_iAudioReadCount=0; m_bStarting=false; + LogDebug("demux:Start() end2 BytesProcessed:%d", dwBytesProcessed); } void CDeMultiplexer::SetEndOfFile(bool bEndOfFile) @@ -666,6 +708,14 @@ return m_bEndOfFile; } +int CDeMultiplexer::ReadAheadFromFile() +{ + if (!m_bAudioVideoReady) return 0; + //LogDebug("demux:ReadAheadFromFile"); + int SizeRead = ReadFromFile(false,false) ; + return SizeRead; +} + /// This method reads the next READ_SIZE bytes from the file /// and processes the raw data /// When a TS packet has been discovered, OnTsPacket(byte* tsPacket) gets called @@ -674,7 +724,8 @@ { // if (m_bWeos) return 0 ; // if (IsAudioChanging()) return 0 ; // Do not read any data during stream selection from MP C# - if (m_filter.IsSeeking()) return 0; // Ambass : to check + if (m_filter.IsSeeking() || m_bHoldFileRead) return 0; // Ambass : to check + CAutoLock lock (&m_sectionRead); if (m_reader==NULL) return false; byte buffer[READ_SIZE]; @@ -702,7 +753,7 @@ { //yes, then process the raw data result=true; - OnRawData(buffer,(int)dwReadBytes); + OnRawData2(buffer,(int)dwReadBytes); m_LastDataFromRtsp = GetTickCount(); } } @@ -737,7 +788,7 @@ if (dwReadBytes > 0) { //succeeded, process data - OnRawData(buffer,(int)dwReadBytes); + OnRawData2(buffer,(int)dwReadBytes); } else { @@ -773,6 +824,7 @@ /// - decode any audio/video packets and put the PES packets in the appropiate buffers void CDeMultiplexer::OnTsPacket(byte* tsPacket) { + //LogDebug("OnTsPacket() start"); CTsHeader header(tsPacket); m_patParser.OnTsPacket(tsPacket); @@ -846,11 +898,23 @@ { return; } + + //Buffers about to be flushed + if (m_filter.IsThreadRunning() && m_bFlushDelgNow) + { + return; + } +// //process the ts packet further +// FillSubtitle(header,tsPacket); +// FillAudio(header,tsPacket); +// FillVideo(header,tsPacket); +// FillTeletext(header,tsPacket); + //process the ts packet further + FillVideo(header,tsPacket); + FillAudio(header,tsPacket); FillSubtitle(header,tsPacket); - FillAudio(header,tsPacket); - FillVideo(header,tsPacket); FillTeletext(header,tsPacket); } @@ -879,7 +943,10 @@ if(!CheckContinuity(m_AudioPrevCC, header)) { LogDebug("Audio Continuity error... %x ( prev %x )", header.ContinuityCounter, m_AudioPrevCC); - if (!m_DisableDiscontinuitiesFiltering) m_AudioValidPES=false; + if (!m_DisableDiscontinuitiesFiltering) + { + m_AudioValidPES=false; + } } m_AudioPrevCC = header.ContinuityCounter; @@ -888,7 +955,26 @@ //does tspacket contain the start of a pes packet? if (header.PayloadUnitStart) { - //yes packet contains start of a pes packet. + //Sanity check PES header + int posn=header.PayLoadStart; + if ( + ((tsPacket[posn+0]!=0) || (tsPacket[posn+1]!=0) || (tsPacket[posn+2]!=1)) //Invalid start code + || ((tsPacket[posn+3] & 0x80)==0) //Invalid stream ID + || ((tsPacket[posn+6] & 0xC0)!=0x80) //Invalid marker bits + || ((tsPacket[posn+6] & 0x20)==0x20) //Payload scrambled + ) + { + //Discard this new/current PES packet + m_AudioValidPES=false; + m_bSetAudioDiscontinuity=true; + LogDebug("PES audio 0-0-1 fail"); + //Flushing is delegated to CTsReaderFilter::ThreadProc() + m_bFlushDelegated = true; + m_filter.WakeThread(); + return; + } + + //yes, packet contains start of a pes packet. //does current buffer hold any data ? if (m_pCurrentAudioBuffer->Length() > 0) { @@ -896,18 +982,17 @@ m_pCurrentAudioBuffer = new CBuffer(); } - if (m_t_vecAudioBuffers.size()) + if (m_t_vecAudioBuffers.size()) //Process the previous PES packet { CBuffer *Cbuf=*m_t_vecAudioBuffers.begin(); byte *p = Cbuf->Data() ; - if ((p[0]==0) && (p[1]==0) && (p[2]==1) //Valid start code - && ((p[3] & 0x80)!=0) //Valid stream ID - && ((p[6] & 0xC0)==0x80) //Valid marker bits - && ((p[6] & 0x20)!=0x20)) //Payload not scrambled + + if (m_AudioValidPES) { //get pts/dts from pes header CPcr pts; CPcr dts; + if (CPcr::DecodeFromPesHeader(p,0,pts,dts)) { double diff; @@ -917,10 +1002,16 @@ diff=m_lastAudioPTS.ToClock()-pts.ToClock(); else diff=pts.ToClock()-m_lastAudioPTS.ToClock(); - if (diff>10.0) + if (diff>2.0) { + //Large PTS jump - flush the world... LogDebug("DeMultiplexer::FillAudio pts jump found : %f %f, %f", (float) diff, (float)pts.ToClock(), (float)m_lastAudioPTS.ToClock()); - m_AudioValidPES=false; + m_lastAudioPTS.IsValid=false; + m_lastVideoPTS.IsValid=false; + m_bSetAudioDiscontinuity=true; + //Flushing is delegated to CTsReaderFilter::ThreadProc() + m_bFlushDelegated = true; + m_filter.WakeThread(); } else { @@ -960,14 +1051,9 @@ else { LogDebug(" No data"); - m_AudioValidPES=false; + m_AudioValidPES=false; //stop further processing } } - else - { - LogDebug("Pes header 0-0-1 fail"); - m_AudioValidPES=false; - } if (m_AudioValidPES) { @@ -1012,7 +1098,7 @@ delete *it; m_t_vecAudioBuffers.erase(it); } - m_bSetAudioDiscontinuity = true; + m_bSetAudioDiscontinuity = true; //Next good packet will be discontinuous } } m_AudioValidPES = true; @@ -1047,7 +1133,7 @@ /// This method will check if the tspacket is an video packet void CDeMultiplexer::FillVideo(CTsHeader& header, byte* tsPacket) -{ +{ if (m_pids.videoPids.size() == 0 || m_pids.videoPids[0].Pid==0) return; if (header.Pid!=m_pids.videoPids[0].Pid) return; @@ -1056,7 +1142,10 @@ if (!CheckContinuity(m_VideoPrevCC, header)) { LogDebug("Video Continuity error... %x ( prev %x )", header.ContinuityCounter, m_VideoPrevCC); - if (!m_DisableDiscontinuitiesFiltering) m_VideoValidPES = false; + if (!m_DisableDiscontinuitiesFiltering) + { + m_VideoValidPES = false; + } } m_VideoPrevCC = header.ContinuityCounter; @@ -1072,7 +1161,6 @@ } else { - //ParseVideoH264(header, tsPacket); FillVideoH264(header, tsPacket); } } @@ -1120,13 +1208,24 @@ return; } - if ((start[0]!=0) || (start[1]!=0) || (start[2]!=1) //Invalid start code - || ((start[3] & 0x80)==0)) //Invalid stream ID + if ( + ((start[0]!=0) || (start[1]!=0) || (start[2]!=1)) //Invalid start code + || ((start[3] & 0x80)==0) //Invalid stream ID + || ((start[6] & 0xC0)!=0x80) //Invalid marker bits + || ((start[6] & 0x20)==0x20) //Payload scrambled + ) { - LogDebug("Pes 0-0-1 fail"); + LogDebug("PES H264 0-0-1 fail"); + //LogDebug("PES H264 0-0-1 fail, %x-%x-%x-%x-%x-%x-%x-%x", start[0], start[1], start[2], start[3], start[4], start[5], start[6], start[7]); m_VideoValidPES=false; + m_mVideoValidPES = false; m_p->rtStart = Packet::INVALID_TIME; m_WaitHeaderPES = -1; + m_bSetVideoDiscontinuity=true; + //Flushing is delegated to CTsReaderFilter::ThreadProc() + m_bFlushDelegated = true; + m_filter.WakeThread(); + return; } else { @@ -1137,11 +1236,6 @@ } else { // full PES header is available. - if ((start[6] & 0xC0)!=0x80 //Invalid marker bits - || (start[6] & 0x20)==0x20) //Payload scrambled - { - return; - } CPcr pts; CPcr dts; @@ -1155,14 +1249,19 @@ diff=m_lastVideoPTS.ToClock()-pts.ToClock(); else diff=pts.ToClock()-m_lastVideoPTS.ToClock(); - if (diff>10.0) + if (diff>2.0) { - LogDebug("DeMultiplexer::FillVideo pts jump found : %f %f, %f", (float) diff, (float)pts.ToClock(), (float)m_lastVideoPTS.ToClock()); - m_VideoValidPES=false; + //Large PTS jump - flush the world... + LogDebug("DeMultiplexer::FillVideoH264 pts jump found : %f %f, %f", (float) diff, (float)pts.ToClock(), (float)m_lastVideoPTS.ToClock()); + m_lastAudioPTS.IsValid=false; + m_lastVideoPTS.IsValid=false; + //Flushing is delegated to CTsReaderFilter::ThreadProc() + m_bFlushDelegated = true; + m_filter.WakeThread(); } else { - // LogDebug("DeMultiplexer::FillVideo pts : %f ", (float)pts.ToClock()); + //LogDebug("DeMultiplexer::FillVideoH264 pts diff : %f ", (float)pts.ToClock()); m_lastVideoPTS=pts; } } @@ -1321,28 +1420,47 @@ if (lastVidResX!=m_mpegPesParser->basicVideoInfo.width || lastVidResY!=m_mpegPesParser->basicVideoInfo.height) { LogDebug("DeMultiplexer: %x video format changed: res=%dx%d aspectRatio=%d:%d fps=%d isInterlaced=%d",header.Pid,m_mpegPesParser->basicVideoInfo.width,m_mpegPesParser->basicVideoInfo.height,m_mpegPesParser->basicVideoInfo.arx,m_mpegPesParser->basicVideoInfo.ary,m_mpegPesParser->basicVideoInfo.fps,m_mpegPesParser->basicVideoInfo.isInterlaced); - if (m_mpegParserTriggerFormatChange) + if (m_mpegParserTriggerFormatChange && !IsAudioChanging()) { - LogDebug("DeMultiplexer: OnMediaFormatChange triggered by mpeg2Parser"); + LogDebug("DeMultiplexer: OnMediaFormatChange triggered by H264Parser, aud %d, vid 1", m_audioChanged); SetMediaChanging(true); - m_filter.OnMediaTypeChanged(3); + if (m_audioChanged) + m_filter.OnMediaTypeChanged(3); //Video and audio + else + m_filter.OnMediaTypeChanged(2); //Video only m_mpegParserTriggerFormatChange=false; } LogDebug("DeMultiplexer: triggering OnVideoFormatChanged"); m_filter.OnVideoFormatChanged(m_mpegPesParser->basicVideoInfo.streamType,m_mpegPesParser->basicVideoInfo.width,m_mpegPesParser->basicVideoInfo.height,m_mpegPesParser->basicVideoInfo.arx,m_mpegPesParser->basicVideoInfo.ary,15000000,m_mpegPesParser->basicVideoInfo.isInterlaced); } - else + else //video resolution is unchanged, but there may be other format changes { - if (m_mpegParserTriggerFormatChange && Gop) + if (m_mpegParserTriggerFormatChange && Gop && !IsAudioChanging()) { - LogDebug("DeMultiplexer: Got GOP after the channel change was detected without correct mpeg header parsing, so we trigger the format change now."); - m_filter.OnMediaTypeChanged(3); + LogDebug("DeMultiplexer: Got GOP after channel change detected, format change, aud %d, vid %d", m_audioChanged, m_videoChanged); + if (m_audioChanged || m_videoChanged) + { + SetMediaChanging(true); + if (m_audioChanged && m_videoChanged) + m_filter.OnMediaTypeChanged(3); + else if (m_audioChanged) + m_filter.OnMediaTypeChanged(1); + else + m_filter.OnMediaTypeChanged(2); + } + else + { + SetMediaChanging(false); + } m_mpegParserTriggerFormatChange=false; } } } else + { m_bSetVideoDiscontinuity = !m_mVideoValidPES; + } + m_pl.RemoveAll(); p2->bDiscontinuity = m_p->bDiscontinuity; m_p->bDiscontinuity = FALSE; @@ -1421,13 +1539,24 @@ return; } - if ((start[0]!=0) || (start[1]!=0) || (start[2]!=1) //Invalid start code - || ((start[3] & 0x80)==0)) //Invalid stream ID + if ( + ((start[0]!=0) || (start[1]!=0) || (start[2]!=1)) //Invalid start code + || ((start[3] & 0x80)==0) //Invalid stream ID + || ((start[6] & 0xC0)!=0x80) //Invalid marker bits + || ((start[6] & 0x20)==0x20) //Payload scrambled + ) { - LogDebug("Pes 0-0-1 fail"); - m_VideoValidPES=false; + LogDebug("PES MPEG2 0-0-1 fail"); + //LogDebug("Pes MPEG2 0-0-1 fail, %x-%x-%x-%x-%x-%x-%x-%x", start[0], start[1], start[2], start[3], start[4], start[5], start[6], start[7]); + m_VideoValidPES = false; + m_mVideoValidPES = false; m_p->rtStart = Packet::INVALID_TIME; m_WaitHeaderPES = -1; + m_bSetVideoDiscontinuity=true; + //Flushing is delegated to CTsReaderFilter::ThreadProc() + m_bFlushDelegated = true; + m_filter.WakeThread(); + return; } else { @@ -1438,15 +1567,10 @@ } else { // full PES header is available. - if ((start[6] & 0xC0)!=0x80 //Invalid marker bits - || (start[6] & 0x20)==0x20) //Payload scrambled - { - return; - } CPcr pts; CPcr dts; -// m_VideoValidPES=true ; + m_VideoValidPES=true ; if (CPcr::DecodeFromPesHeader(start,0,pts,dts)) { double diff; @@ -1456,10 +1580,15 @@ diff=m_lastVideoPTS.ToClock()-pts.ToClock(); else diff=pts.ToClock()-m_lastVideoPTS.ToClock(); - if (diff>10.0) + if (diff>2.0) { - LogDebug("DeMultiplexer::FillVideo pts jump found : %f %f, %f", (float) diff, (float)pts.ToClock(), (float)m_lastVideoPTS.ToClock()); - m_VideoValidPES=false; + //Large PTS jump - flush the world... + LogDebug("DeMultiplexer::FillVideoMPEG2 pts jump found : %f %f, %f", (float) diff, (float)pts.ToClock(), (float)m_lastVideoPTS.ToClock()); + m_lastAudioPTS.IsValid=false; + m_lastVideoPTS.IsValid=false; + //Flushing is delegated to CTsReaderFilter::ThreadProc() + m_bFlushDelegated = true; + m_filter.WakeThread(); } else { @@ -1536,7 +1665,7 @@ m_pl.AddTail(p2); // LogDebug("DeMultiplexer::FillVideo Frame length : %d %x %x", size, *(DWORD*)start, *(DWORD*)next); - if (m_VideoValidPES) + if (m_mVideoValidPES) { CAutoPtr p(new Packet()); p = m_pl.RemoveHead(); @@ -1633,31 +1762,47 @@ if (lastVidResX!=m_mpegPesParser->basicVideoInfo.width || lastVidResY!=m_mpegPesParser->basicVideoInfo.height) { LogDebug("DeMultiplexer: %x video format changed: res=%dx%d aspectRatio=%d:%d fps=%d isInterlaced=%d",header.Pid,m_mpegPesParser->basicVideoInfo.width,m_mpegPesParser->basicVideoInfo.height,m_mpegPesParser->basicVideoInfo.arx,m_mpegPesParser->basicVideoInfo.ary,m_mpegPesParser->basicVideoInfo.fps,m_mpegPesParser->basicVideoInfo.isInterlaced); - if (m_mpegParserTriggerFormatChange) + if (m_mpegParserTriggerFormatChange && !IsAudioChanging()) { - LogDebug("DeMultiplexer: OnMediaFormatChange triggered by mpeg2Parser"); + LogDebug("DeMultiplexer: OnMediaFormatChange triggered by mpeg2Parser, aud %d, vid 1", m_audioChanged); SetMediaChanging(true); - m_filter.OnMediaTypeChanged(3); + if (m_audioChanged) + m_filter.OnMediaTypeChanged(3); //Video and audio + else + m_filter.OnMediaTypeChanged(2); //Video only m_mpegParserTriggerFormatChange=false; } LogDebug("DeMultiplexer: triggering OnVideoFormatChanged"); m_filter.OnVideoFormatChanged(m_mpegPesParser->basicVideoInfo.streamType,m_mpegPesParser->basicVideoInfo.width,m_mpegPesParser->basicVideoInfo.height,m_mpegPesParser->basicVideoInfo.arx,m_mpegPesParser->basicVideoInfo.ary,15000000,m_mpegPesParser->basicVideoInfo.isInterlaced); } - else + else //video resolution is unchanged, but there may be other format changes { - if (m_mpegParserTriggerFormatChange && Gop) + if (m_mpegParserTriggerFormatChange && Gop && !IsAudioChanging()) { - LogDebug("DeMultiplexer: Got GOP after the channel change was detected without correct mpeg header parsing, so we trigger the format change now."); - m_filter.OnMediaTypeChanged(3); + LogDebug("DeMultiplexer: Got GOP after channel change detected, format change, aud %d, vid %d", m_audioChanged, m_videoChanged); + if (m_audioChanged || m_videoChanged) + { + SetMediaChanging(true); + if (m_audioChanged && m_videoChanged) + m_filter.OnMediaTypeChanged(3); + else if (m_audioChanged) + m_filter.OnMediaTypeChanged(1); + else + m_filter.OnMediaTypeChanged(2); + } + else + { + SetMediaChanging(false); + } m_mpegParserTriggerFormatChange=false; } } } else { - m_bSetVideoDiscontinuity = true; + m_bSetVideoDiscontinuity = !m_mVideoValidPES; } - m_VideoValidPES=true ; // We've just completed a frame, set flag until problem clears it + //m_VideoValidPES=true ; // We've just completed a frame, set flag until problem clears it m_pl.RemoveAll() ; } else // sequence_header_code @@ -1772,6 +1917,8 @@ { //CAutoLock lock (&m_section); CPidTable pids=info.PidTable; + + //LogDebug("OnNewChannel callback, pat version:%d->%d",m_iPatVersion, info.PatVersion); if (info.PatVersion != m_iPatVersion) { @@ -1785,7 +1932,11 @@ m_iPatVersion=info.PatVersion; m_bSetAudioDiscontinuity=true; m_bSetVideoDiscontinuity=true; - Flush(); + //Flushing is delegated to CTsReaderFilter::ThreadProc() + m_bFlushDelgNow = true; + m_filter.WakeThread(); + +// Flush(); // m_filter.m_bOnZap = true ; } else @@ -1860,22 +2011,35 @@ } bool changed=false; - bool videoChanged=false; + m_videoChanged=false; + m_audioChanged=false; + + #ifdef USE_DYNAMIC_PINS + //Is the video pin connected? + if ((m_filter.GetVideoPin()->IsConnected()) && (m_pids.videoPids.size() > 0)) + { + changed=true; //force a check in the mpeg parser + if (oldVideoServiceType != m_pids.videoPids[0].VideoServiceType) + { + m_videoChanged=true; + } + } + #else //did the video format change? - if (m_pids.videoPids.size() > 0 && oldVideoServiceType != m_pids.videoPids[0].VideoServiceType ) + if (m_pids.videoPids.size() > 0 && oldVideoServiceType != m_pids.videoPids[0].VideoServiceType) { //yes, is the video pin connected? if (m_filter.GetVideoPin()->IsConnected()) { changed=true; - videoChanged=true; + m_videoChanged=true; } } - + #endif + m_iAudioStream = 0; LogDebug ("Setting initial audio index to : %i", m_iAudioStream); - bool audioChanged=false; //get the new audio format int newAudioStreamType=SERVICE_TYPE_AUDIO_MPEG2; @@ -1891,7 +2055,7 @@ if (m_filter.GetAudioPin()->IsConnected()) { changed=true; - audioChanged=true; + m_audioChanged=true; } } @@ -1899,13 +2063,22 @@ if (changed) { #ifdef USE_DYNAMIC_PINS - // if we have a video stream and it's format changed, let the mpeg parser trigger the OnMediaTypeChanged - if (m_pids.videoPids.size() > 0 && m_pids.videoPids[0].Pid>0x1 && videoChanged) + // if we have a video stream, let the mpeg parser trigger the OnMediaTypeChanged + if (m_pids.videoPids.size() > 0 && m_pids.videoPids[0].Pid>0x1) { - LogDebug("DeMultiplexer: We detected a new media type change which has a video stream, so we let the mpegParser trigger the event"); + LogDebug("DeMultiplexer: We have a video stream, so we let the mpegParser check/trigger format changes"); m_receivedPackets=0; + SetMediaChanging(true); + if (m_audioStreams.size() == 1) + { + if ((m_AudioStreamType == SERVICE_TYPE_AUDIO_UNKNOWN) || (m_AudioStreamType != newAudioStreamType)) + { + m_AudioStreamType = newAudioStreamType ; + m_audioChanged=true; + LogDebug("DeMultiplexer: Audio media types changed"); + } + } m_mpegParserTriggerFormatChange=true; - SetMediaChanging(true); } else { @@ -1922,10 +2095,10 @@ } } #else - if (audioChanged && videoChanged) + if (m_audioChanged && m_videoChanged) m_filter.OnMediaTypeChanged(3); else - if (audioChanged) + if (m_audioChanged) m_filter.OnMediaTypeChanged(1); else m_filter.OnMediaTypeChanged(2); Index: DeMultiplexer.h =================================================================== --- DeMultiplexer.h (revision 28131) +++ DeMultiplexer.h (working copy) @@ -130,10 +130,20 @@ void SetAudioChanging(bool onOff); bool IsAudioChanging(void); + int ReadAheadFromFile(); + bool m_DisableDiscontinuitiesFiltering; DWORD m_LastDataFromRtsp; bool m_bAudioVideoReady; + bool m_bFlushDelegated; + bool m_bFlushDelgNow; + bool m_bHoldFileRead; + bool m_bReadAheadFromFile; + CCritSec m_sectionFlushAudio; + CCritSec m_sectionFlushVideo; + CCritSec m_sectionFlushSubtitle; + private: struct stAudioStream { @@ -219,6 +229,8 @@ bool m_bStarting; bool m_mpegParserTriggerFormatChange; + bool m_videoChanged; + bool m_audioChanged; bool m_bSetAudioDiscontinuity; bool m_bSetVideoDiscontinuity; CPcr m_subtitlePcr; Index: SubtitlePin.cpp =================================================================== --- SubtitlePin.cpp (revision 28131) +++ SubtitlePin.cpp (working copy) @@ -217,13 +217,12 @@ return NOERROR; } - if (m_pTsReaderFilter->m_bStreamCompensated) + if (m_pTsReaderFilter->m_bStreamCompensated && !demux.m_bHoldFileRead) { //get next buffer from demultiplexer - { - CAutoLock lock(&m_bufferLock); - buffer=demux.GetSubtitle(); - } + CAutoLock flock (&demux.m_sectionFlushSubtitle); + CAutoLock lock(&m_bufferLock); + buffer=demux.GetSubtitle(); } //did we reach the end of the file Index: TsDuration.cpp =================================================================== --- TsDuration.cpp (revision 28131) +++ TsDuration.cpp (working copy) @@ -69,27 +69,35 @@ // Determines the total duration of the file (or timeshifting files) // // -void CTsDuration::UpdateDuration() +void CTsDuration::UpdateDuration(bool logging) { - byte buffer[32712]; + byte buffer[188*330]; DWORD dwBytesRead; int Loop=5 ; + int searchLoopCnt; do { m_bSearchStart=true; m_bSearchEnd=false; - m_bSearchMax=false; m_startPcr.Reset(); m_maxPcr.Reset(); - m_reader->SetFilePointer(0,FILE_BEGIN); + //m_reader->SetFilePointer(0,FILE_BEGIN); + searchLoopCnt = 2; + __int64 offset=0; Reset() ; // Reset internal "PacketSync" buffer + if (logging) + { + LogDebug("UpdateDuration - find pcr"); + } //find the first pcr in the file while (!m_startPcr.IsValid) { + DWORD dwBytesRead; + m_reader->SetFilePointer(offset,FILE_BEGIN); if (!SUCCEEDED(m_reader->Read(buffer,sizeof(buffer),&dwBytesRead))) { //park filepointer at end of file @@ -104,15 +112,26 @@ m_reader->Read(buffer,1,&dwBytesRead); return; } - OnRawData(buffer,dwBytesRead); + OnRawData2(buffer,dwBytesRead); + if (searchLoopCnt<65) + { + searchLoopCnt++; + } + offset += (sizeof(buffer)*(searchLoopCnt/2)); //Move file pointer + Sleep(1) ; } + if (logging) + { + LogDebug("UpdateDuration - found startPcr, iterations:%d offset:%d", searchLoopCnt-2, offset); + } //find the last pcr in the file m_bSearchEnd=true; m_bSearchStart=false; m_endPcr.Reset(); - __int64 offset=sizeof(buffer); - __int64 fileSize=m_reader->GetFileSize(); + searchLoopCnt = 2; + offset=sizeof(buffer); + //__int64 fileSize=m_reader->GetFileSize(); while (!m_endPcr.IsValid) { @@ -127,9 +146,18 @@ break; } Reset() ; // Reset internal "PacketSync" buffer - OnRawData(buffer,dwBytesRead); - offset+=sizeof(buffer); + OnRawData2(buffer,dwBytesRead); + if (searchLoopCnt<65) + { + searchLoopCnt++; + } + offset += ( (sizeof(buffer)*(searchLoopCnt/2)) - (188*16)); //step back a few packets less than a buffer so that buffers overlap + Sleep(1) ; } + if (logging) + { + LogDebug("UpdateDuration - found endPcr, iterations:%d offset:%d", searchLoopCnt-2, offset); + } Loop-- ; if(m_endPcr.PcrReferenceBase < m_startPcr.PcrReferenceBase) @@ -160,26 +188,9 @@ //and fill maxPcr if (m_endPcr.PcrReferenceBase < m_startPcr.PcrReferenceBase) { - //PCR rollover - m_bSearchMax=true; - m_bSearchEnd=false; - __int64 offset=sizeof(buffer); - while (!m_maxPcr.IsValid) - { - DWORD dwBytesRead; - m_reader->SetFilePointer(-offset,FILE_END); - if (!SUCCEEDED(m_reader->Read(buffer,sizeof(buffer),&dwBytesRead))) - { - break; - } - if (dwBytesRead==0) - { - break; - } - Reset() ; // Reset internal "PacketSync" buffer - OnRawData(buffer,dwBytesRead); - offset+=sizeof(buffer); - } + m_maxPcr.PcrReferenceBase = 0x1ffffffffULL; + m_maxPcr.PcrReferenceExtension = 0x1ffULL; + m_maxPcr.IsValid = true; } //park filepointer at end of file @@ -211,13 +222,6 @@ { m_endPcr=field.Pcr; } - if (m_bSearchMax && m_pid==header.Pid) - { - if (field.Pcr.ToClock() > m_startPcr.ToClock()) - { - m_maxPcr=field.Pcr; - } - } } } Index: TsDuration.h =================================================================== --- TsDuration.h (revision 28131) +++ TsDuration.h (working copy) @@ -11,7 +11,7 @@ virtual ~CTsDuration(void); void SetFileReader(FileReader* reader); void OnTsPacket(byte* tsPacket); - void UpdateDuration(); + void UpdateDuration(bool logging); void SetVideoPid(int pid); int GetPid(); CRefTime Duration(); @@ -41,5 +41,4 @@ CPcr m_firstStartPcr; bool m_bSearchStart; bool m_bSearchEnd; - bool m_bSearchMax; }; Index: TsFileSeek.cpp =================================================================== --- TsFileSeek.cpp (revision 28131) +++ TsFileSeek.cpp (working copy) @@ -38,7 +38,6 @@ CTsFileSeek::CTsFileSeek( CTsDuration& duration) :m_duration(duration) { - m_useBinarySearch = true; } CTsFileSeek::~CTsFileSeek(void) @@ -73,49 +72,35 @@ filePos/=188; filePos*=188; - if( m_duration.FirstStartPcr() > m_duration.EndPcr() ) - { - // no PCR rollover is allowed when using binary search with seeking - m_useBinarySearch = false; - } - else - { - m_useBinarySearch = true; - } - seekTimeStamp /= 1000.0f; // convert to seconds. m_seekPid=m_duration.GetPid(); LogDebug("seek to %f filepos:%x pid:%x", seekTimeStamp,(DWORD)filePos, m_seekPid); byte buffer[188*10]; - if (filePos<=0) - { - //no need to seek for timestamp 0, - //simply set the pointer at the beginning of the file - m_reader->SetFilePointer(0,FILE_BEGIN); - return; - } - if (filePos+sizeof(buffer) > m_reader->GetFileSize()) - { - //no need to seek when we want to seek to end of file - //simply set the pointer at the end of the file - m_reader->SetFilePointer(0,FILE_END); - return; - } - __int64 prevfilePos=filePos; __int64 binaryMax=m_reader->GetFileSize(); __int64 binaryMin=0; __int64 lastFilePos=0; int seekingIteration=0; - SeekState state=FindPcr; Reset() ; // Reset "PacketSync" while (true) { //sanity checks - if (filePos<0) return; - if (filePos+sizeof(buffer) > m_reader->GetFileSize()) return; + if (filePos<=0) + { + //no need to seek for timestamp 0, + //simply set the pointer at the beginning of the file + m_reader->SetFilePointer(0,FILE_BEGIN); + return; + } + if (filePos+sizeof(buffer) > m_reader->GetFileSize()) + { + //no need to seek when we want to seek to end of file + //simply set the pointer at the end of the file + m_reader->SetFilePointer(0,FILE_END); + return; + } //set filepointer to filePos m_reader->SetFilePointer(filePos,FILE_BEGIN); @@ -132,138 +117,63 @@ } //process data m_pcrFound.Reset(); - OnRawData(buffer,dwBytesRead); + OnRawData2(buffer,dwBytesRead); //did we find a pcr? if (m_pcrFound.IsValid) { //yes. pcr found double clockFound=m_pcrFound.ToClock(); - if( m_useBinarySearch ) + double diff = clockFound - seekTimeStamp; + //LogDebug(" got %f at filepos %x diff %f ( %I64x, %I64x )", clockFound, (DWORD)filePos, diff, binaryMin, binaryMax); + + // Make sure that seeking position is at least the target one + if (0 <= diff && diff <= SEEKING_ACCURACY) { - double diff = clockFound - seekTimeStamp; - //LogDebug(" got %f at filepos %x diff %f ( %I64x, %I64x )", clockFound, (DWORD)filePos, diff, binaryMin, binaryMax); - - // Make sure that seeking position is at least the target one - if (0 <= diff && diff <= SEEKING_ACCURACY) - { - LogDebug(" stop seek: %f at %x - target: %f, diff: %f", - clockFound, (DWORD)filePos, seekTimeStamp, diff); - m_reader->SetFilePointer(filePos,FILE_BEGIN); - return; - } + LogDebug(" stop seek: %f at %x - target: %f, diff: %f", + clockFound, (DWORD)filePos, seekTimeStamp, diff); + m_reader->SetFilePointer(filePos,FILE_BEGIN); + return; + } - seekingIteration++; - if( seekingIteration > MAX_SEEKING_ITERATIONS ) - { - LogDebug(" stop seek max iterations reached (%d): %f at %x - target: %f, diff: %f", - MAX_SEEKING_ITERATIONS, clockFound, (DWORD)filePos, seekTimeStamp, diff); - m_reader->SetFilePointer(filePos,FILE_BEGIN); - return; - } + seekingIteration++; + if( seekingIteration > MAX_SEEKING_ITERATIONS ) + { + LogDebug(" stop seek max iterations reached (%d): %f at %x - target: %f, diff: %f", + MAX_SEEKING_ITERATIONS, clockFound, (DWORD)filePos, seekTimeStamp, diff); + m_reader->SetFilePointer(filePos,FILE_BEGIN); + return; + } - // lower bound becomes valid - if( clockFound > seekTimeStamp ) - { - if (filePos < binaryMax) binaryMax = filePos-1; - } - else - { - if (filePos > binaryMin) binaryMin = filePos+1; - } - - if (lastFilePos==filePos) - { - LogDebug(" stop seek closer target found : %f at %x - target: %f, diff: %f", - clockFound, (DWORD)filePos, seekTimeStamp, diff); - m_reader->SetFilePointer(filePos,FILE_BEGIN); - return; - } - - lastFilePos=filePos; - filePos = binaryMin + ( binaryMax - binaryMin ) / 2; - Reset() ; // Random jump, Reset "PacketSync" + // lower bound becomes valid + if( clockFound > seekTimeStamp ) + { + if (filePos < binaryMax) binaryMax = filePos-1; } else { - if (state==FindPcr) - { - prevfilePos=filePos; - //we found the pcr. - //compare it with the timestamp we want to seek to - if (clockFound < seekTimeStamp) - { - // pcr found is too low, move forward in file and seek next pcr - state=FindNextPcr; - - //LogDebug(" got %f at filepos %x ->find next", clockFound, (DWORD)filePos); - filePos += sizeof(buffer); - } - else if (clockFound > seekTimeStamp) - { - // pcr found is too high, move backward in file and seek previous pcr - //LogDebug(" got %f at filepos %x ->find prev", clockFound, (DWORD)filePos); - state=FindPreviousPcr; - filePos -= sizeof(buffer); - Reset() ; // Backward jump, Reset "PacketSync" - } - else - { - //pcr is correct, just return - //LogDebug(" got %f", clockFound); - m_reader->SetFilePointer(filePos,FILE_BEGIN); - return; - } - } - else - { - //pcr found, check state - if (state==FindNextPcr) - { - //LogDebug(" got %f at filepos %x", clockFound, (DWORD)filePos); - //looking for a pcr > seektime - if (clockFound > seekTimeStamp) - { - //found it.. - //LogDebug(" stop seek too big: %f at %x", clockFound, (DWORD)filePos); - m_reader->SetFilePointer(prevfilePos,FILE_BEGIN); - return; - } - prevfilePos=filePos; - filePos+=sizeof(buffer); - } - else if (state==FindPreviousPcr) - { - //LogDebug(" got %f at filepos %x", clockFound, (DWORD)filePos); - //looking for a pcr < seektime - if (clockFound < seekTimeStamp) - { - //found it... - //LogDebug(" stop seek too small: %f at %x", clockFound, (DWORD)filePos); - m_reader->SetFilePointer(filePos,FILE_BEGIN); - return; - } - prevfilePos=filePos; - filePos-=sizeof(buffer); - Reset() ; // Backward jump, Reset "PacketSync" - } - } + if (filePos > binaryMin) binaryMin = filePos+1; } + + lastFilePos=filePos; + filePos = binaryMin + ( binaryMax - binaryMin ) / 2; + filePos/=188; + filePos*=188; + + if (lastFilePos==filePos) + { + LogDebug(" stop seek closer target found : %f at %x - target: %f, diff: %f", + clockFound, (DWORD)filePos, seekTimeStamp, diff); + m_reader->SetFilePointer(filePos,FILE_BEGIN); + return; + } + + Reset() ; // Random jump, Reset "PacketSync" } else // no first PCR { - //no pcr found. - if (state == FindPreviousPcr) - { - //move filepointer back and continue searching for a PCR - filePos -=sizeof(buffer); - Reset() ; // Backward jump, Reset "PacketSync" - } - else - { - //move filepointer forward and continue searching for a PCR - filePos += sizeof(buffer); - } + //move filepointer forward and continue searching for a PCR + filePos += sizeof(buffer); } } } @@ -271,7 +181,7 @@ //********************************************************* // Callback method. This method gets called via -// CTsFileSeek::Seek()->OnRawData(buffer,dwBytesRead) +// CTsFileSeek::Seek()->OnRawData2(buffer,dwBytesRead) // tsPacket : pointer to 188 byte Transport Stream packet // // This method checks if the ts packet contains a PCR timestamp @@ -290,7 +200,7 @@ { // pid is valid // did we have a pcr rollover ?? - if (m_duration.MaxPcr().IsValid) + if (m_duration.FirstStartPcr() > m_duration.EndPcr()) { //pcr rollover occured. //next we need to convert the pcr into filestamp @@ -298,23 +208,28 @@ //but the file can start with any pcr timestamp if (field.Pcr.ToClock() <=m_duration.EndPcr().ToClock()) { - // pcr < endpcr - // pcrFound= (pcr-startpcr) + // pcr < endpcr (second half of the file) + // pcrFound= pcr+(MAXIMUM_PCR - startpcr) + // StartPcr------>(0x1ffffffff;0x1ff), (0x0,0x0)--------->EndPcr m_pcrFound=field.Pcr; double d1=m_pcrFound.ToClock(); - double start=m_duration.StartPcr().ToClock();//earliest pcr available in the file - d1-=start; + CPcr pcr2; + pcr2.PcrReferenceBase = 0x1ffffffffULL; + pcr2.PcrReferenceExtension = 0x1ffULL; + double start=pcr2.ToClock()- m_duration.StartPcr().ToClock(); + d1+=start; m_pcrFound.FromClock(d1); } else { - //PCR > endpcr - // pcrFound- pcr+(maxPcr-startpcr) - // StartPcr------>MaxPcr--->0--------->EndPcr + //PCR > endpcr (first half of the file) + // pcrFound= (pcr-startpcr) m_pcrFound=field.Pcr; double d1=m_pcrFound.ToClock(); - double start=m_duration.MaxPcr().ToClock()- m_duration.StartPcr().ToClock(); - d1+=start; + double start=m_duration.StartPcr().ToClock();//earliest pcr available in the file + LogDebug(" found clock %f earliest is %f", d1, start); + d1-=start; + LogDebug(" after sub %f", d1); m_pcrFound.FromClock(d1); } } Index: TsFileSeek.h =================================================================== --- TsFileSeek.h (revision 28131) +++ TsFileSeek.h (working copy) @@ -8,12 +8,6 @@ class CTsFileSeek: public CPacketSync { public: - enum SeekState - { - FindPreviousPcr=-1, - FindPcr=0, - FindNextPcr=1 - }; CTsFileSeek( CTsDuration& duration ); virtual ~CTsFileSeek(void); void OnTsPacket(byte* tsPacket); @@ -25,5 +19,4 @@ CTsDuration& m_duration; CPcr m_pcrFound; int m_seekPid; - bool m_useBinarySearch; }; Index: TsReader.cpp =================================================================== --- TsReader.cpp (revision 28131) +++ TsReader.cpp (working copy) @@ -170,7 +170,8 @@ TCHAR filename[1024]; GetLogFile(filename); ::DeleteFile(filename); - LogDebug("---------- v0.4.12 -------------------"); + LogDebug("----- Continue after corruption testing ---------"); + LogDebug("---------- v0.4.28 XXX -------------------"); m_fileReader=NULL; m_fileDuration=NULL; @@ -228,11 +229,17 @@ m_ShowBufferVideo = INIT_SHOWBUFFERVIDEO; m_MPmainThreadID = GetCurrentThreadId() ; + + LogDebug("Start duration thread"); + StartThread(); } CTsReaderFilter::~CTsReaderFilter() { LogDebug("CTsReaderFilter::dtor"); + //stop duration thread + StopThread(); + HRESULT hr = m_pAudioPin->Disconnect(); delete m_pAudioPin; @@ -471,7 +478,7 @@ } //stop duration thread - StopThread(); + //StopThread(); LogDebug("CTsReaderFilter::Stop() -stop source"); //stop filter @@ -614,13 +621,13 @@ m_demultiplexer.m_LastDataFromRtsp = GetTickCount() ; } - //is the duration update thread running? - if (!IsThreadRunning()) - { - //nop? then start it - //LogDebug(" CTsReaderFilter::Pause()->start duration thread"); - StartThread(); - } +// //is the duration update thread running? +// if (!IsThreadRunning()) +// { +// //nop? then start it +// //LogDebug(" CTsReaderFilter::Pause()->start duration thread"); +// StartThread(); +// } LogDebug("CTsReaderFilter::Pause() - END - state = %d", m_State); return hr; @@ -772,7 +779,7 @@ //get file duration m_duration.SetFileReader(m_fileDuration); - m_duration.UpdateDuration(); + m_duration.UpdateDuration(true); float milli = m_duration.Duration().Millisecs(); milli /= 1000.0; @@ -1041,8 +1048,9 @@ if (!m_bOnZap || !m_demultiplexer.IsNewPatReady() || m_bAnalog) // On zapping, new PAT has occured, we should not flush to avoid loosing data. { // new PAT has not occured, we should flush to avoid restart with old data. - m_demultiplexer.FlushAudio() ; - m_demultiplexer.FlushVideo() ; + //m_demultiplexer.FlushAudio() ; + //m_demultiplexer.FlushVideo() ; + m_demultiplexer.Flush(); } m_bOnZap=false ; // m_demultiplexer.SetHoldAudio(false) ; @@ -1136,14 +1144,19 @@ /// Every second it will check the stream or local file and determine the total duration of the file/stream /// The duration can/will grow if we are playing a timeshifting buffer/stream // If the duration has changed it will update m_duration and send a EC_LENGTH_CHANGED event -// to the graph +// to the graph. +// Flushing after large video/audio PTS jump and PES '0-0-1 fail' errors +// are also delegated to this thread. void CTsReaderFilter::ThreadProc() { LogDebug("CTsReaderFilter::ThreadProc start()"); - int durationUpdateLoop = 1; + int durationUpdateLoop = 1; long Old_rtspDuration = -1 ; long PauseDuration =0; + int timeNow = GetTickCount(); + int lastFlushTime = timeNow; + int lastDurTime = timeNow - 2000; ::SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_BELOW_NORMAL); do @@ -1152,107 +1165,161 @@ //since we're no longer playing if (m_demultiplexer.EndOfFile()) break; - //are we playing an RTSP stream? - if (m_fileDuration!=NULL) + + timeNow = GetTickCount(); + + //Flush delegated to this thread + if (m_demultiplexer.m_bFlushDelegated || m_demultiplexer.m_bFlushDelgNow) { - //no, then get the duration from the local file - CTsDuration duration; - duration.SetFileReader(m_fileDuration); - duration.SetVideoPid(m_duration.GetPid()); - duration.UpdateDuration(); + if (!m_demultiplexer.m_bFlushDelgNow && ((timeNow - 500) < lastFlushTime) && (timeNow > lastFlushTime)) + { + // Too early for next flush + m_demultiplexer.m_bFlushDelegated = false; + } + else + { + lastFlushTime = timeNow; + + LogDebug("CTsReaderFilter::ThreadProc - Flush"); + //Flush the internal data + m_demultiplexer.Flush(); + m_bStreamCompensated=false ; + m_demultiplexer.m_bAudioVideoReady=false ; + } + } - //did we find a duration? - if (duration.Duration().Millisecs()>0) + //File read prefetch + if (m_demultiplexer.m_bReadAheadFromFile) + { + m_demultiplexer.m_bReadAheadFromFile = false; + m_demultiplexer.ReadAheadFromFile(); + } + + if ((((timeNow - 1000) > lastDurTime) || (timeNow < lastDurTime)) && IsFilterRunning()) + { + lastDurTime = timeNow; + //are we playing an RTSP stream? + if (m_fileDuration!=NULL) { - //yes, is it different then the one we determined last time? - if (duration.StartPcr().PcrReferenceBase!=m_duration.StartPcr().PcrReferenceBase || - duration.EndPcr().PcrReferenceBase!=m_duration.EndPcr().PcrReferenceBase) - { - //yes, then update it - m_duration.Set(duration.StartPcr(), duration.EndPcr(), duration.MaxPcr()); // Local file - - // Is graph running? - if (m_State == State_Running||m_State==State_Paused) + //no, then get the duration from the local file + if (m_demultiplexer.m_bAudioVideoReady) //Normal play started + { + if((durationUpdateLoop == 2) || m_bRecording) { - //yes, then send a EC_LENGTH_CHANGED event to the graph - NotifyEvent(EC_LENGTH_CHANGED, NULL, NULL); - SetDuration(); + CTsDuration duration; + duration.SetFileReader(m_fileDuration); + duration.SetVideoPid(m_duration.GetPid()); + duration.UpdateDuration(false); + + //did we find a duration? + if (duration.Duration().Millisecs()>0) + { + //yes, is it different then the one we determined last time? + if (duration.StartPcr().PcrReferenceBase!=m_duration.StartPcr().PcrReferenceBase || + duration.EndPcr().PcrReferenceBase!=m_duration.EndPcr().PcrReferenceBase) + { + //yes, then update it - we must be timeshifting or playing an in-progress recording + m_duration.Set(duration.StartPcr(), duration.EndPcr(), duration.MaxPcr()); // Local file + m_bRecording = true; + + // Is graph running? + if (m_State == State_Running||m_State==State_Paused) + { + //yes, then send a EC_LENGTH_CHANGED event to the graph + NotifyEvent(EC_LENGTH_CHANGED, NULL, NULL); + SetDuration(); + } + } + else + { + m_bRecording = false; + } + } } + + if (m_bLiveTv && (m_State == State_Paused)) + { + // After 10 secs Pause, for sure, liveTv is cancelled. + PauseDuration++ ; + if (PauseDuration > 10) + { + m_bLiveTv=false; + LogDebug("CTsReaderFilter, Live Tv is paused for more than 10 secs => m_bLiveTv=false."); + } + } + else + { + PauseDuration=0 ; + } } - } - if (m_bLiveTv && (m_State == State_Paused)) - { - // After 10 secs Pause, for sure, liveTv is cancelled. - PauseDuration++ ; - if (PauseDuration > 10) + else { - m_bLiveTv=false; - LogDebug("CTsReaderFilter, Live Tv is paused for more than 10 secs => m_bLiveTv=false."); + m_bRecording = true; //Force duration update next time m_bAudioVideoReady is true } } else - PauseDuration=0 ; - } - else - { - // we are not playing a local file - // we are playing a (RTSP) stream? - if(m_bTimeShifting || m_bRecording) { - if(durationUpdateLoop == 0) + // we are not playing a local file + // we are playing a (RTSP) stream? + if(m_bTimeShifting || m_bRecording) { - Old_rtspDuration = m_rtspClient.Duration(); - m_rtspClient.UpdateDuration(); - } - - CPcr pcrStart, pcrEnd, pcrMax ; - double duration = m_rtspClient.Duration() / 1000.0f ; - double start = m_duration.StartPcr().ToClock() ; - double end = m_duration.EndPcr().ToClock() ; - - if (m_bTimeShifting) - { - // EndPcr is continuously increasing ( until ~26 hours for rollover that will fail ! ) - // So, we refer duration to End, and just update start. - end = (double)(GetTickCount()-m_tickCount)/1000.0 ; if(durationUpdateLoop == 0) { - start = end - duration; - if (start<0) start=0 ; + Old_rtspDuration = m_rtspClient.Duration(); + m_rtspClient.UpdateDuration(); } - } - else - { - end = start + duration ; - if (Old_rtspDuration!=m_rtspClient.Duration()) // recording alive, continue to increase every second. - { - end += (double)(durationUpdateLoop % 5) ; - } - else + + CPcr pcrStart, pcrEnd, pcrMax ; + double duration = m_rtspClient.Duration() / 1000.0f ; + double start = m_duration.StartPcr().ToClock() ; + double end = m_duration.EndPcr().ToClock() ; + + if (m_bTimeShifting) { - m_bRecording = false; + // EndPcr is continuously increasing ( until ~26 hours for rollover that will fail ! ) + // So, we refer duration to End, and just update start. + end = (double)(GetTickCount()-m_tickCount)/1000.0 ; + if(durationUpdateLoop == 0) + { + start = end - duration; + if (start<0) start=0 ; + } + } + else + { + end = start + duration ; + if (Old_rtspDuration!=m_rtspClient.Duration()) // recording alive, continue to increase every second. + { + end += (double)(durationUpdateLoop % 5) ; + } + else + { + m_bRecording = false; + } + } + //set the duration + pcrStart.FromClock(start) ; + pcrEnd.FromClock(end); + m_duration.Set( pcrStart, pcrEnd, pcrMax); // Continuous update + + // LogDebug("Start : %f, End : %f",(float)m_duration.StartPcr().ToClock(),(float)m_duration.EndPcr().ToClock()) ; + + // Is graph running? + if (m_State == State_Running) + { + //yes, then send a EC_LENGTH_CHANGED event to the graph + NotifyEvent(EC_LENGTH_CHANGED, NULL, NULL); + SetDuration(); } - } - //set the duration - pcrStart.FromClock(start) ; - pcrEnd.FromClock(end); - m_duration.Set( pcrStart, pcrEnd, pcrMax); // Continuous update - -// LogDebug("Start : %f, End : %f",(float)m_duration.StartPcr().ToClock(),(float)m_duration.EndPcr().ToClock()) ; - - durationUpdateLoop = (durationUpdateLoop + 1) % 5; - - // Is graph running? - if (m_State == State_Running) - { - //yes, then send a EC_LENGTH_CHANGED event to the graph - NotifyEvent(EC_LENGTH_CHANGED, NULL, NULL); - SetDuration(); } } + + durationUpdateLoop = (durationUpdateLoop + 1) % 5; } + + Sleep(1); } - while (!ThreadIsStopping(1000)) ; + while (!ThreadIsStopping(210)) ; LogDebug("CTsReaderFilter::ThreadProc stopped()"); } Index: TSThread.cpp =================================================================== --- TSThread.cpp (revision 28131) +++ TSThread.cpp (working copy) @@ -51,6 +51,7 @@ { m_hStopEvent = CreateEvent(NULL, TRUE, TRUE, NULL); m_hDoneEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + m_hWakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //Auto-reset m_threadHandle = INVALID_HANDLE_VALUE; m_bThreadRunning=FALSE; } @@ -60,6 +61,7 @@ StopThread(); CloseHandle(m_hStopEvent); CloseHandle(m_hDoneEvent); + CloseHandle(m_hWakeEvent); } @@ -102,10 +104,34 @@ return hr; } +void TSThread::WakeThread() +{ + if (m_bThreadRunning) + { + SetEvent(m_hWakeEvent); + } +} + BOOL TSThread::ThreadIsStopping(DWORD dwTimeoutMilliseconds) { - DWORD result = WaitForSingleObject(m_hStopEvent, dwTimeoutMilliseconds); - return (result != WAIT_TIMEOUT); + HANDLE hEvts[] = {m_hStopEvent, m_hWakeEvent}; + + DWORD result = WaitForMultipleObjects(2, hEvts, FALSE, dwTimeoutMilliseconds); + switch (result) + { + case WAIT_OBJECT_0 : //m_hStopEvent + return true; + break; + case WAIT_OBJECT_0 + 1 : //m_hWakeEvent + ResetEvent(m_hWakeEvent); + return false; + break; + case WAIT_TIMEOUT : //Timeout + return false; + break; + default : //Error conditions + return true; + } } void TSThread::InternalThreadProc() Index: TSThread.h =================================================================== --- TSThread.h (revision 28131) +++ TSThread.h (working copy) @@ -36,6 +36,7 @@ virtual void ThreadProc() = 0; HRESULT StartThread(); HRESULT StopThread(DWORD dwTimeoutMilliseconds = 1000); + void WakeThread(); BOOL ThreadIsStopping(DWORD dwTimeoutMilliseconds = 10); BOOL IsThreadRunning(); @@ -43,6 +44,7 @@ virtual void InternalThreadProc(); HANDLE m_hDoneEvent; HANDLE m_hStopEvent; + HANDLE m_hWakeEvent; private: BOOL m_bThreadRunning; Index: VideoPin.cpp =================================================================== --- VideoPin.cpp (revision 28131) +++ VideoPin.cpp (working copy) @@ -139,11 +139,11 @@ 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 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; @@ -287,8 +287,9 @@ return NOERROR; } - if (m_pTsReaderFilter->m_bStreamCompensated) - { + if (m_pTsReaderFilter->m_bStreamCompensated && !demux.m_bHoldFileRead) + { + CAutoLock flock (&demux.m_sectionFlushVideo); // Get next video buffer from demultiplexer buffer=demux.GetVideo(); } @@ -312,6 +313,8 @@ { CRefTime RefTime, cRefTime; bool HasTimestamp; + float fTime = 0.0; + float clock = 0.0; //check if it has a timestamp if ((HasTimestamp=buffer->MediaTime(RefTime))) { @@ -322,9 +325,6 @@ //adjust the timestamp with the compensation cRefTime -= m_pTsReaderFilter->Compensation; - //CRefTime Dur; - //Dur = (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 @@ -336,13 +336,25 @@ cRefTime += AddOffset; cRefTime += m_pTsReaderFilter->m_ClockOnStart.m_time; } + + REFERENCE_TIME RefClock = 0; + m_pTsReaderFilter->GetMediaPosition(&RefClock) ; + clock = (double)(RefClock-m_rtStart.m_time)/10000000.0 ; + fTime = (float)cRefTime.Millisecs()/1000.0f - clock ; - if (cRefTime.m_time >= 0) + if (fTime >= 0.0) { - Sleep(1); // Ambass : avoid blocking audio FillBuffer method ( on audio/video starting ) by excessive video Fill buffer preemption + m_bPresentSample = true; + Sleep(2); // Ambass : avoid blocking audio FillBuffer method ( on audio/video starting ) by excessive video Fill buffer preemption } + else + { + // Sample is too late. + m_bPresentSample = false ; + m_bDiscontinuity = TRUE; //Next good sample will be discontinuous + } - m_bPresentSample = true; + //m_bPresentSample = true; } if (m_bPresentSample) @@ -394,11 +406,6 @@ } } - 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 (m_pTsReaderFilter->m_ShowBufferVideo || fTime < 0.030) { int cntA, cntV; @@ -468,10 +475,10 @@ } // Check for discontinuity - if (stsDiff > (m_fMTDMean + (500 * 10000))) // diff - mean > 500ms + if (stsDiff > (m_fMTDMean + (800 * 10000))) // diff - mean > 800ms { 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); + //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