void CRTSPClient::FillBuffer(DWORD byteCount) { LogDebug("CRTSPClient::Fillbuffer...%d\n",byteCount); DWORD tickCount=GET_TIME_NOW(); while ( IsRunning() && m_buffer.Size() < byteCount) { Sleep(5); if (GET_TIME_NOW()-tickCount > 3000) break; } LogDebug("CRTSPClient::Fillbuffer...%d/%d\n",byteCount,m_buffer.Size() ); }
//****************************************************** /// Called when thread is about to start delivering data to the codec /// HRESULT CVideoPin::OnThreadStartPlay() { DWORD thrdID = GetCurrentThreadId(); LogDebug("vidPin:OnThreadStartPlay(%f), rate:%02.2f, threadID:0x%x, GET_TIME_NOW:0x%x", (float)m_rtStart.Millisecs()/1000.0f, m_dRateSeeking, thrdID, GET_TIME_NOW()); //set discontinuity flag indicating to codec that the new data //is not belonging to any previous data m_bDiscontinuity=TRUE; m_bPresentSample=false; m_delayedDiscont = 0; m_FillBuffSleepTime = 1; m_LastFillBuffTime = GET_TIME_NOW(); m_sampleCount = 0; m_bInFillBuffer=false; m_pTsReaderFilter->m_ShowBufferVideo = INIT_SHOWBUFFERVIDEO; m_llLastComp = 0; m_llLastMTDts = 0; m_nNextMTD = 0; m_fMTDMean = 0; m_llMTDSumAvg = 0; ZeroMemory((void*)&m_pllMTD, sizeof(REFERENCE_TIME) * NB_MTDSIZE); //get file-duration and set m_rtDuration GetDuration(NULL); if( !m_bPinNoNewSegFlush ) //MS DTV video decoder can hang if we flush here... { //Downstream flush DeliverBeginFlush(); DeliverEndFlush(); } //start playing DeliverNewSegment(m_rtStart, m_rtStop, m_dRateSeeking); return CSourceStream::OnThreadStartPlay( ); }
//****************************************************** /// Called when thread is about to start delivering data to the codec /// HRESULT CAudioPin::OnThreadStartPlay() { DWORD thrdID = GetCurrentThreadId(); LogDebug("audPin:OnThreadStartPlay(%f), rate:%02.2f, threadID:0x%x, GET_TIME_NOW:0x%x", (float)m_rtStart.Millisecs()/1000.0f, m_dRateSeeking, thrdID, GET_TIME_NOW()); //set flag to compensate any differences in the stream time & file time m_pTsReaderFilter->m_bStreamCompensated = false; m_pTsReaderFilter->m_bForcePosnUpdate = true; m_pTsReaderFilter->WakeThread(); m_pTsReaderFilter->m_ShowBufferAudio = INIT_SHOWBUFFERAUDIO; //set discontinuity flag indicating to codec that the new data //is not belonging to any previous data m_bDiscontinuity = TRUE; m_bPresentSample = false; m_sampleCount = 0; m_bInFillBuffer=false; m_pTsReaderFilter->m_audioReady = false; m_FillBuffSleepTime = 1; m_LastFillBuffTime = GET_TIME_NOW(); ClearAverageFtime(); //get file-duration and set m_rtDuration GetDuration(NULL); //Downstream flush DeliverBeginFlush(); DeliverEndFlush(); //start playing DeliverNewSegment(m_rtStart, m_rtStop, m_dRateSeeking); return CSourceStream::OnThreadStartPlay( ); }
HRESULT CAudioPin::FillBuffer(IMediaSample *pSample) { try { CDeMultiplexer& demux=m_pTsReaderFilter->GetDemultiplexer(); CBuffer* buffer=NULL; bool earlyStall = false; //get file-duration and set m_rtDuration GetDuration(NULL); do { //Check if we need to wait for a while DWORD timeNow = GET_TIME_NOW(); while (timeNow < (m_LastFillBuffTime + m_FillBuffSleepTime)) { Sleep(1); timeNow = GET_TIME_NOW(); } m_LastFillBuffTime = timeNow; //did we reach the end of the file if (demux.EndOfFile()) { int ACnt, VCnt; demux.GetBufferCounts(&ACnt, &VCnt); if (ACnt <= 0 && VCnt <= 0) //have we used all the data ? { LogDebug("audPin:set eof"); m_FillBuffSleepTime = 5; CreateEmptySample(pSample); m_bInFillBuffer = false; return S_FALSE; //S_FALSE will notify the graph that end of file has been reached } } //if the filter is currently seeking to a new position //or this pin is currently seeking to a new position then //we dont try to read any packets, but simply return... if (m_pTsReaderFilter->IsSeeking() || m_pTsReaderFilter->IsStopping() || demux.m_bFlushRunning || !m_pTsReaderFilter->m_bStreamCompensated) { m_FillBuffSleepTime = 5; CreateEmptySample(pSample); m_bInFillBuffer = false; if (demux.m_bFlushRunning || !m_pTsReaderFilter->m_bStreamCompensated) { //Force discon on next good sample m_sampleCount = 0; m_bDiscontinuity=true; } if (!m_pTsReaderFilter->m_bStreamCompensated && (m_nNextAFT != 0)) { ClearAverageFtime(); } return NOERROR; } else { m_FillBuffSleepTime = 1; m_bInFillBuffer = true; } // Get next audio buffer from demultiplexer buffer=demux.GetAudio(earlyStall, m_rtStart); if (buffer==NULL) { m_FillBuffSleepTime = 5; } else { m_bPresentSample = true ; if (buffer->GetForcePMT()) { m_bAddPMT = true; } if (buffer->GetDiscontinuity()) { m_bDiscontinuity = true; } CRefTime RefTime,cRefTime ; bool HasTimestamp ; double fTime = 0.0; double clock = 0.0; double stallPoint = AUDIO_STALL_POINT; //check if it has a timestamp if ((HasTimestamp=buffer->MediaTime(RefTime))) { cRefTime = RefTime ; cRefTime -= m_rtStart ; //adjust the timestamp with the compensation cRefTime-= m_pTsReaderFilter->GetCompensation() ; //Check if total compensation offset is more than +/-10ms if (abs(m_pTsReaderFilter->GetTotalDeltaComp()) > 100000) { if (!m_bDisableSlowPlayDiscontinuity) { //Force downstream filters to resync by setting discontinuity flag pSample->SetDiscontinuity(TRUE); } m_pTsReaderFilter->ClearTotalDeltaComp(); } REFERENCE_TIME RefClock = 0; m_pTsReaderFilter->GetMediaPosition(&RefClock) ; clock = (double)(RefClock-m_rtStart.m_time)/10000000.0 ; fTime = ((double)cRefTime.m_time/10000000.0) - clock ; //Calculate a mean 'fTime' value using 'raw' fTime data CalcAverageFtime(fTime); if (timeNow < (m_pTsReaderFilter->m_lastPauseRun + (30*1000))) { //do this for 30s after start of play, a flush or pause m_fAFTMeanRef = m_fAFTMean; } //Add compensation time for external downstream audio delay //to stop samples becoming 'late' (note: this does NOT change the actual sample timestamps) fTime -= m_fAFTMeanRef; //remove the 'mean' offset fTime += ((AUDIO_STALL_POINT/2.0) + 0.2); //re-centre the timing //Discard late samples at start of play, //and samples outside a sensible timing window during play //(helps with signal corruption recovery) cRefTime -= m_pTsReaderFilter->m_ClockOnStart.m_time; if (fTime < -2.0) { if ((m_dRateSeeking == 1.0) && (m_pTsReaderFilter->State() == State_Running) && (clock > 8.0) && !demux.m_bFlushDelegated) { //Very late - request internal flush and re-sync to stream demux.DelegatedFlush(false, false); LogDebug("audPin : Audio to render very late, flushing") ; } } if ((cRefTime.m_time >= PRESENT_DELAY) && (fTime > ((cRefTime.m_time >= FS_TIM_LIM) ? -0.3 : -0.5)) && (fTime < 2.5)) { if ((fTime > stallPoint) && (m_sampleCount > 2)) { //Too early - stall to avoid over-filling of audio decode/renderer buffers, //but don't enable at start of play to make sure graph starts properly m_FillBuffSleepTime = 10; buffer = NULL; earlyStall = true; continue; } } else //Don't drop samples normally - it upsets the rate matching in the audio renderer { // Sample is too late. m_bPresentSample = false ; } cRefTime += m_pTsReaderFilter->m_ClockOnStart.m_time; } if (m_bPresentSample && (m_dRateSeeking == 1.0) && (buffer->Length() > 0)) { //do we need to set the discontinuity flag? if (m_bDiscontinuity) { //ifso, set it pSample->SetDiscontinuity(TRUE); LogDebug("audPin: Set discontinuity L:%d B:%d fTime:%03.3f SampCnt:%d", m_bDiscontinuity, buffer->GetDiscontinuity(), (float)fTime, m_sampleCount); m_bDiscontinuity=FALSE; } if (m_bAddPMT && !m_pTsReaderFilter->m_bDisableAddPMT && !m_bPinNoAddPMT) { //Add MediaType info to sample CMediaType mt; int audioIndex = 0; demux.GetAudioStream(audioIndex); demux.GetAudioStreamType(audioIndex, mt, m_iPosition); pSample->SetMediaType(&mt); SetMediaType(&mt); WAVEFORMATEX* wfe = (WAVEFORMATEX*)mt.Format(); LogDebug("audPin: Add pmt, fTime:%03.3f SampCnt:%d, Ch:%d, Sr:%d", (float)fTime, m_sampleCount, wfe->nChannels, wfe->nSamplesPerSec); m_bAddPMT = false; //Only add once } if (HasTimestamp) { //now we have the final timestamp, set timestamp in sample REFERENCE_TIME refTime=(REFERENCE_TIME)cRefTime; refTime = (REFERENCE_TIME)((double)refTime/m_dRateSeeking); refTime += m_pTsReaderFilter->m_regAudioDelay; //add offset (to produce delay relative to video) pSample->SetSyncPoint(TRUE); pSample->SetTime(&refTime,&refTime); if (m_pTsReaderFilter->m_ShowBufferAudio || fTime < 0.02 || (m_sampleCount < 3)) { int cntA, cntV; CRefTime firstAudio, lastAudio; CRefTime firstVideo, lastVideo, zeroVideo; cntA = demux.GetAudioBufferPts(firstAudio, lastAudio); cntV = demux.GetVideoBufferPts(firstVideo, lastVideo, zeroVideo); LogDebug("Aud/Ref : %03.3f, Compensated = %03.3f ( %0.3f A/V buffers=%02d/%02d), Clk : %f, SampCnt %d, Sleep %d ms, stallPt %03.3f", (float)RefTime.Millisecs()/1000.0f, (float)cRefTime.Millisecs()/1000.0f, fTime,cntA,cntV, clock, m_sampleCount, m_FillBuffSleepTime, (float)stallPoint); } if (m_pTsReaderFilter->m_ShowBufferAudio) m_pTsReaderFilter->m_ShowBufferAudio--; // CalcAverageFtime(fTime); if (((float)cRefTime.Millisecs()/1000.0f) > AUDIO_READY_POINT) { m_pTsReaderFilter->m_audioReady = true; } } else { //buffer has no timestamp pSample->SetTime(NULL,NULL); pSample->SetSyncPoint(FALSE); } //copy buffer in sample BYTE* pSampleBuffer; pSample->SetActualDataLength(buffer->Length()); pSample->GetPointer(&pSampleBuffer); memcpy(pSampleBuffer,buffer->Data(),buffer->Length()); //delete the buffer and return delete buffer; demux.EraseAudioBuff(); } else { // Buffer was not displayed because it was out of date, search for next. delete buffer; demux.EraseAudioBuff(); buffer=NULL ; m_FillBuffSleepTime = (m_dRateSeeking == 1.0) ? 1 : 2; m_bDiscontinuity = TRUE; //Next good sample will be discontinuous } } earlyStall = false; } while (buffer==NULL); m_bInFillBuffer = false; return NOERROR; } // Should we return something else than NOERROR when hitting an exception? catch(int e) { LogDebug("audPin:fillbuffer exception %d", e); } catch(...) { LogDebug("audPin:fillbuffer exception ..."); } m_FillBuffSleepTime = 5; CreateEmptySample(pSample); m_bDiscontinuity = TRUE; //Next good sample will be discontinuous m_bInFillBuffer = false; return NOERROR; }
HRESULT CVideoPin::FillBuffer(IMediaSample *pSample) { try { CDeMultiplexer& demux = m_pTsReaderFilter->GetDemultiplexer(); CBuffer* buffer = NULL; bool earlyStall = false; //get file-duration and set m_rtDuration GetDuration(NULL); do { //Check if we need to wait for a while DWORD timeNow = GET_TIME_NOW(); while (timeNow < (m_LastFillBuffTime + m_FillBuffSleepTime)) { Sleep(1); timeNow = GET_TIME_NOW(); } m_LastFillBuffTime = timeNow; //did we reach the end of the file if (demux.EndOfFile()) { int ACnt, VCnt; demux.GetBufferCounts(&ACnt, &VCnt); if (ACnt <= 0 && VCnt <= 0) //have we used all the data ? { LogDebug("vidPin:set eof"); m_FillBuffSleepTime = 5; CreateEmptySample(pSample); m_bInFillBuffer = false; return S_FALSE; //S_FALSE will notify the graph that end of file has been reached } } //if the filter is currently seeking to a new position //or this pin is currently seeking to a new position then //we dont try to read any packets, but simply return... if (m_pTsReaderFilter->IsSeeking() || m_pTsReaderFilter->IsStopping() || demux.m_bFlushRunning || !m_pTsReaderFilter->m_bStreamCompensated) { m_FillBuffSleepTime = 5; CreateEmptySample(pSample); m_bInFillBuffer = false; if (demux.m_bFlushRunning || !m_pTsReaderFilter->m_bStreamCompensated) { //Force discon on next good sample m_sampleCount = 0; m_bDiscontinuity=true; } return NOERROR; } else { m_FillBuffSleepTime = 1; m_bInFillBuffer = true; } // Get next video buffer from demultiplexer buffer=demux.GetVideo(earlyStall); if (buffer == NULL) { m_FillBuffSleepTime = 5; } else if (buffer->Length() > m_bufferSize) { //discard buffer delete buffer; demux.EraseVideoBuff(); m_bDiscontinuity = TRUE; //Next good sample will be discontinuous buffer = NULL; m_FillBuffSleepTime = 1; LogDebug("vidPin : Error - buffer too large for sample") ; } else { m_bPresentSample = true ; CRefTime RefTime, cRefTime; double fTime = 0.0; double clock = 0.0; double stallPoint = VIDEO_STALL_POINT; //check if it has a timestamp bool HasTimestamp=buffer->MediaTime(RefTime); if (HasTimestamp) { bool ForcePresent = false; CRefTime compTemp = m_pTsReaderFilter->GetCompensation(); if (m_pTsReaderFilter->m_bFastSyncFFDShow && (compTemp != m_llLastComp)) { m_bDiscontinuity = true; } m_llLastComp = compTemp; cRefTime = RefTime; cRefTime -= m_rtStart; //adjust the timestamp with the compensation cRefTime -= compTemp; cRefTime -= m_pTsReaderFilter->m_ClockOnStart.m_time; // 'fast start' timestamp modification, during first (AddVideoComp + 1 sec) of play double fsAdjLimit = (1.0 * (double)m_pTsReaderFilter->AddVideoComp.m_time) + (double)FS_ADDON_LIM; //(1 * vid comp) + 1 second if (m_pTsReaderFilter->m_EnableSlowMotionOnZapping && ((double)cRefTime.m_time < fsAdjLimit) ) { //float startCref = (float)cRefTime.m_time/(1000*10000); //used in LogDebug below only //Assume desired timestamp span is zero to fsAdjLimit, actual span is AddVideoComp to fsAdjLimit double offsetRatio = fsAdjLimit/(double)FS_ADDON_LIM; // == fsAdjLimit/(fsAdjLimit - (double)m_pTsReaderFilter->AddVideoComp.m_time); double currOffset = fsAdjLimit - (double)cRefTime.m_time; double newOffset = currOffset * offsetRatio; cRefTime = (fsAdjLimit > newOffset) ? (REFERENCE_TIME)(fsAdjLimit - newOffset) : 0; //Don't allow negative cRefTime ForcePresent = true; //LogDebug("VFS cOfs %03.3f, nOfs %03.3f, cRefTimeS %03.3f, cRefTimeN %03.3f", (float)currOffset/(1000*10000), (float)newOffset/(1000*10000), startCref, (float)cRefTime.m_time/(1000*10000)); if (m_pTsReaderFilter->m_bFastSyncFFDShow) { m_delayedDiscont = 2; //Force I-frame timestamp updates for FFDShow } } REFERENCE_TIME RefClock = 0; m_pTsReaderFilter->GetMediaPosition(&RefClock) ; clock = (double)(RefClock-m_rtStart.m_time)/10000000.0 ; fTime = ((double)(cRefTime.m_time + m_pTsReaderFilter->m_ClockOnStart.m_time)/10000000.0) - clock ; if (m_dRateSeeking == 1.0) { if ((fTime < -2.0) && (m_pTsReaderFilter->State() == State_Running) && (clock > 8.0) && !ForcePresent && !demux.m_bFlushDelegated) { //Very late - request internal flush and re-sync to stream demux.DelegatedFlush(false, false); LogDebug("vidPin : Video to render very late, flushing") ; } //Discard late samples at start of play, //and samples outside a sensible timing window during play //(helps with signal corruption recovery) if ((fTime > (ForcePresent ? -1.0 : -0.3)) && (fTime < (demux.m_dVidPTSJumpLimit + 1.0)) ) { if ((fTime > stallPoint) && (m_sampleCount > 10)) { //Too early - stall for a while to avoid over-filling of video pipeline buffers, //but don't enable at start of play to make sure graph starts properly m_FillBuffSleepTime = 10; buffer = NULL; earlyStall = true; continue; } } else { // Sample is too late. m_bPresentSample = false ; } } else if ((fTime < -1.0) || (fTime > 3.0)) //Fast-forward limits { // Sample is too late. m_bPresentSample = false ; } cRefTime += m_pTsReaderFilter->m_ClockOnStart.m_time; } if (m_bPresentSample && (buffer->Length() > 0)) { //do we need to set the discontinuity flag? if (m_bDiscontinuity || buffer->GetDiscontinuity()) { if ((m_sampleCount == 0) && m_bAddPMT && !m_pTsReaderFilter->m_bDisableAddPMT && !m_bPinNoAddPMT) { //Add MediaType info to first sample after OnThreadStartPlay() CMediaType mt; if (demux.GetVideoStreamType(mt)) { pSample->SetMediaType(&mt); SetMediaType(&mt); LogDebug("vidPin: Add pmt and set discontinuity L:%d B:%d fTime:%03.3f SampCnt:%d", m_bDiscontinuity, buffer->GetDiscontinuity(), (float)fTime, m_sampleCount); } else { LogDebug("vidPin: Add pmt failed - set discontinuity L:%d B:%d fTime:%03.3f SampCnt:%d", m_bDiscontinuity, buffer->GetDiscontinuity(), (float)fTime, m_sampleCount); } m_bAddPMT = false; //Only add once each time } else { LogDebug("vidPin: Set discontinuity L:%d B:%d fTime:%03.3f SampCnt:%d", m_bDiscontinuity, buffer->GetDiscontinuity(), (float)fTime, m_sampleCount); } pSample->SetDiscontinuity(TRUE); m_bDiscontinuity=FALSE; } //LogDebug("vidPin: video buffer type = %d", buffer->GetVideoServiceType()); if (HasTimestamp) { //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 = (REFERENCE_TIME)((double)refTime/m_dRateSeeking); pSample->SetTime(&refTime,&refTime); if (m_pTsReaderFilter->m_bFastSyncFFDShow && (m_dRateSeeking == 1.0)) { 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("vidPin:set I-frame discontinuity, count %d", m_delayedDiscont); } else { m_delayedDiscont = 0; } } } if (m_pTsReaderFilter->m_ShowBufferVideo || ((fTime < 0.02) && (m_dRateSeeking == 1.0)) || (m_sampleCount < 3)) { int cntA, cntV; CRefTime firstAudio, lastAudio; CRefTime firstVideo, lastVideo, zeroVideo; cntA = demux.GetAudioBufferPts(firstAudio, lastAudio); cntV = demux.GetVideoBufferPts(firstVideo, lastVideo, zeroVideo); LogDebug("Vid/Ref : %03.3f, %c-frame(%02d), Compensated = %03.3f ( %0.3f A/V buffers=%02d/%02d), Clk : %f, SampCnt %d, stallPt %03.3f", (float)RefTime.Millisecs()/1000.0f,buffer->GetFrameType(),buffer->GetFrameCount(), (float)cRefTime.Millisecs()/1000.0f, fTime, cntA,cntV,clock, m_sampleCount, (float)stallPoint); } if (m_pTsReaderFilter->m_ShowBufferVideo) m_pTsReaderFilter->m_ShowBufferVideo--; } else { //buffer has no timestamp pSample->SetTime(NULL,NULL); pSample->SetSyncPoint(FALSE); } // copy buffer into the sample BYTE* pSampleBuffer; pSample->SetActualDataLength(buffer->Length()); pSample->GetPointer(&pSampleBuffer); memcpy(pSampleBuffer,buffer->Data(),buffer->Length()); // delete the buffer delete buffer; demux.EraseVideoBuff(); //m_sampleCount++ ; } else { // Buffer was not displayed because it was out of date, search for next. delete buffer; demux.EraseVideoBuff(); m_bDiscontinuity = TRUE; //Next good sample will be discontinuous buffer = NULL; m_FillBuffSleepTime = 1; } } earlyStall = false; } while (buffer == NULL); m_bInFillBuffer = false; return NOERROR; } catch(...) { LogDebug("vidPin:fillbuffer exception"); } m_FillBuffSleepTime = 5; CreateEmptySample(pSample); m_bDiscontinuity = TRUE; //Next good sample will be discontinuous m_bInFillBuffer = false; return NOERROR; }