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; @@ -366,6 +370,7 @@ void CDeMultiplexer::FlushVideo() { LogDebug("demux:flush video"); + CAutoLock flock (&m_sectionFlushVideo); CAutoLock lock (&m_sectionVideo); delete m_pCurrentVideoBuffer; m_pCurrentVideoBuffer = NULL; @@ -397,19 +402,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 +444,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 +468,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 +488,11 @@ SetHoldAudio(holdAudio); SetHoldVideo(holdVideo); SetHoldSubtitle(holdSubtitle); + m_bFlushDelegated = false; + m_bFlushDelgNow = false; + m_bReadAheadFromFile = false; + + m_bHoldFileRead = false; } /// @@ -532,6 +550,13 @@ if (ReadFromFile(false,true)INITIAL_READ_SIZE || GetAudioStreamCount()>0) { #ifdef USE_DYNAMIC_PINS @@ -647,6 +684,7 @@ Flush(); m_streamPcr.Reset(); m_bStarting=false; + LogDebug("demux:Start() end1 BytesProcessed:%d", dwBytesProcessed); return; } dwBytesProcessed+=BytesRead; @@ -654,6 +692,7 @@ m_streamPcr.Reset(); m_iAudioReadCount=0; m_bStarting=false; + LogDebug("demux:Start() end2 BytesProcessed:%d", dwBytesProcessed); } void CDeMultiplexer::SetEndOfFile(bool bEndOfFile) @@ -666,6 +705,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 +721,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]; @@ -773,6 +821,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); @@ -847,10 +896,16 @@ 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 +934,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 +946,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 +973,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 +993,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 +1042,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 +1089,7 @@ delete *it; m_t_vecAudioBuffers.erase(it); } - m_bSetAudioDiscontinuity = true; + m_bSetAudioDiscontinuity = true; //Next good packet will be discontinuous } } m_AudioValidPES = true; @@ -1056,7 +1133,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 +1152,6 @@ } else { - //ParseVideoH264(header, tsPacket); FillVideoH264(header, tsPacket); } } @@ -1120,13 +1199,23 @@ 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"); 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 +1226,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 +1239,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; } } @@ -1323,26 +1412,45 @@ 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) { - 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 the unchanged, but there may be other format changes { if (m_mpegParserTriggerFormatChange && Gop) { - 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); + if (m_audioChanged || m_videoChanged) + { + SetMediaChanging(true); + LogDebug("DeMultiplexer: Got GOP after channel change detected, trigger format change, aud %d, vid %d", m_audioChanged, m_videoChanged); + 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 +1529,23 @@ 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"); + 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 +1556,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 +1569,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 +1654,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(); @@ -1635,29 +1753,45 @@ 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) { - 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 the unchanged, but there may be other format changes { if (m_mpegParserTriggerFormatChange && Gop) { - 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); + if (m_audioChanged || m_videoChanged) + { + SetMediaChanging(true); + LogDebug("DeMultiplexer: Got GOP after channel change detected, trigger format change, aud %d, vid %d", m_audioChanged, m_videoChanged); + 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 +1906,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 +1921,10 @@ 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 +1999,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 +2043,7 @@ if (m_filter.GetAudioPin()->IsConnected()) { changed=true; - audioChanged=true; + m_audioChanged=true; } } @@ -1899,10 +2051,10 @@ 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; m_mpegParserTriggerFormatChange=true; SetMediaChanging(true); @@ -1922,10 +2074,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: 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.20 XXX -------------------"); m_fileReader=NULL; m_fileDuration=NULL; @@ -1136,14 +1137,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 +1158,141 @@ //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)) + { + 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) + //no, then get the duration from the local file + CTsDuration duration; + duration.SetFileReader(m_fileDuration); + duration.SetVideoPid(m_duration.GetPid()); + duration.UpdateDuration(); + + //did we find a duration? + if (duration.Duration().Millisecs()>0) { - //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) + //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 send a EC_LENGTH_CHANGED event to the graph - NotifyEvent(EC_LENGTH_CHANGED, NULL, NULL); - SetDuration(); + //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) + { + //yes, then send a EC_LENGTH_CHANGED event to the graph + NotifyEvent(EC_LENGTH_CHANGED, NULL, NULL); + SetDuration(); + } } } - } - if (m_bLiveTv && (m_State == State_Paused)) - { - // After 10 secs Pause, for sure, liveTv is cancelled. - PauseDuration++ ; - if (PauseDuration > 10) + if (m_bLiveTv && (m_State == State_Paused)) { - m_bLiveTv=false; - LogDebug("CTsReaderFilter, Live Tv is paused for more than 10 secs => m_bLiveTv=false."); + // 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 ; } 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()) ; + + 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(); } - } - //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(); } } } } - while (!ThreadIsStopping(1000)) ; + while (!ThreadIsStopping(300)) ; 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) @@ -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) { + m_bPresentSample = true; Sleep(1); // 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;