void DurationIndex::AppendCTTSMode(REFERENCE_TIME tStart, REFERENCE_TIME tEnd) { // if the frames are out of order and the end time is invalid, // we must use the frame rate to work out the difference REFERENCE_TIME dur; if (m_bUseFrameRate) { dur = m_tFrame; } else { dur = (tEnd - tStart); } // ToScale will round down, and this truncation causes the // diff between decode and presentation time to get larger and larger // so we sum both reference time and scaled totals and use the difference. // That way the rounding error does not build up. // the simpler version of these two lines is: cThis = long(ToScale(tEnd - tStart)); m_refDuration += dur; long cThis = long(ToScale(m_refDuration) - m_TotalDuration); // difference between sum of durations to here and actual CTS // -- note: do this before adding current sample to total duration long cDiff = long(ToScale(tStart) - m_TotalDuration); AddDuration(cThis); m_CTTS.Append(cDiff); }
HRESULT DurationIndex::WriteTable(Atom* patm) { // do nothing if no samples at all HRESULT hr = S_OK; if (m_nSamples <= mode_decide_count) { ModeDecide(); } if (m_nSamples > 0) { if (!m_bCTTS) { // the final sample duration has not been recorded -- use the // stop time if (ToScale(m_tStopLast) > m_TotalDuration) { AddDuration(long(ToScale(m_tStopLast) - m_TotalDuration)); } else // NOTE: We still need some duration recorded, to avoid stts/stsz discrepancy at the very least AddDuration(1); } // create atom and write table smart_ptr<Atom> pstts = patm->CreateAtom('stts'); m_STTS.Write(pstts); pstts->Close(); if (m_bCTTS) { // write CTTS table smart_ptr<Atom> pctts = patm->CreateAtom('ctts'); m_CTTS.Write(pctts); pctts->Close(); } } return hr; }
void DurationIndex::Add(REFERENCE_TIME tStart, REFERENCE_TIME tEnd) { // In general it is safer to just use the start time of each sample // since the stop time will be either wrong, or will just be deduced from // the next sample start time. // However, when frame re-ordering is happening, the composition time (== PTS) will // not be the same as the decode time (== DTS) and we need to use both start and // stop time to build the CTTS table. // We save the first few timestamps and then decide which mode to be in. if (m_nSamples < mode_decide_count) { if (m_nSamples == 0) { m_SumDurations = 0; } m_SumDurations += (tEnd - tStart); m_SampleStarts[m_nSamples] = tStart; m_SampleStops[m_nSamples] = tEnd; m_nSamples++; return; } else if (m_nSamples == mode_decide_count) { // this decides on a mode and then processes // all the samples in the table ModeDecide(); } if (m_bCTTS) { AppendCTTSMode(tStart, tEnd); } else { AddDuration(long(ToScale(tStart) - m_TotalDuration)); } m_nSamples++; m_tStartLast = tStart; m_tStopLast = tEnd; return; }
void DurationIndex::ModeDecide() { if (m_nSamples > 0) { bool bReverse = false; bool bDurOk = true; LONGLONG ave = m_SumDurations / m_nSamples; // 70fps is the maximum reasonable frame rate, so anything less than this is // likely to be an error const REFERENCE_TIME min_frame_dur = (UNITS / 70); if (ave < min_frame_dur) { bDurOk = false; } // in the most common case, when converting from TS output, we don't // get either accurate end times or an accuration rate in the media type. // The smallest positive interval between frames should be the frame duration. REFERENCE_TIME tInterval = 0; for (int i = 0; i < m_nSamples; i++) { if (i > 0) { if (m_SampleStarts[i] < m_SampleStarts[i-1]) { bReverse = true; } else { REFERENCE_TIME tThis = m_SampleStarts[i] - m_SampleStarts[i-1]; if (tThis > min_frame_dur) { if ((tInterval == 0) || (tThis < tInterval)) { tInterval = tThis; } } } } } m_tStartFirst = m_SampleStarts[0]; // this interval is a better guess than the media type frame rate if (tInterval > min_frame_dur) { m_tFrame = tInterval; } if (bReverse) { m_bCTTS = true; if (!bDurOk) { m_bUseFrameRate = true; } else { m_bUseFrameRate = false; } // remember that the first frame might not be zero m_TotalDuration = ToScale(m_tStartFirst); m_refDuration = m_tStartFirst; for (int i = 0; i < m_nSamples; i++) { AppendCTTSMode(m_SampleStarts[i], m_SampleStops[i]); } } else { m_bCTTS = false; m_TotalDuration = ToScale(m_tStartFirst); for (int i = 1; i < m_nSamples; i++) { AddDuration(long(ToScale(m_SampleStarts[i]) - m_TotalDuration)); } } } }