BOOL CMpcAudioRenderer::ScheduleSample(IMediaSample *pMediaSample) { REFERENCE_TIME StartSample; REFERENCE_TIME EndSample; // Is someone pulling our leg if (pMediaSample == NULL) { return FALSE; } // Get the next sample due up for rendering. If there aren't any ready // then GetNextSampleTimes returns an error. If there is one to be done // then it succeeds and yields the sample times. If it is due now then // it returns S_OK other if it's to be done when due it returns S_FALSE HRESULT hr = GetSampleTimes(pMediaSample, &StartSample, &EndSample); if (FAILED(hr)) { return FALSE; } // If we don't have a reference clock then we cannot set up the advise // time so we simply set the event indicating an image to render. This // will cause us to run flat out without any timing or synchronisation if (hr == S_OK) { EXECUTE_ASSERT(SetEvent((HANDLE) m_RenderEvent)); return TRUE; } if (m_dRate <= 1.1) { ASSERT(m_dwAdvise == 0); ASSERT(m_pClock); WaitForSingleObject((HANDLE)m_RenderEvent,0); hr = m_pClock->AdviseTime( (REFERENCE_TIME) m_tStart, StartSample, (HEVENT)(HANDLE) m_RenderEvent, &m_dwAdvise); if (SUCCEEDED(hr)) { return TRUE; } } else { hr = DoRenderSample (pMediaSample); } // We could not schedule the next sample for rendering despite the fact // we have a valid sample here. This is a fair indication that either // the system clock is wrong or the time stamp for the sample is duff ASSERT(m_dwAdvise == 0); return FALSE; }
MP4Timestamp MP4Track::GetChunkTime(MP4ChunkId chunkId) { u_int32_t stscIndex = GetChunkStscIndex(chunkId); MP4ChunkId firstChunkId = m_pStscFirstChunkProperty->GetValue(stscIndex); MP4SampleId firstSample = m_pStscFirstSampleProperty->GetValue(stscIndex); u_int32_t samplesPerChunk = m_pStscSamplesPerChunkProperty->GetValue(stscIndex); MP4SampleId firstSampleInChunk = firstSample + ((chunkId - firstChunkId) * samplesPerChunk); MP4Timestamp chunkTime; GetSampleTimes(firstSampleInChunk, &chunkTime, NULL); return chunkTime; }
u_int32_t MP4Track::GetMaxBitrate() { u_int32_t timeScale = GetTimeScale(); MP4SampleId numSamples = GetNumberOfSamples(); u_int32_t maxBytesPerSec = 0; u_int32_t bytesThisSec = 0; MP4Timestamp thisSec = 0; for (MP4SampleId sid = 1; sid <= numSamples; sid++) { u_int32_t sampleSize; MP4Timestamp sampleTime; sampleSize = GetSampleSize(sid); GetSampleTimes(sid, &sampleTime, NULL); // sample counts for current second if (sampleTime < thisSec + timeScale) { bytesThisSec += sampleSize; } else { // sample is in a future second if (bytesThisSec > maxBytesPerSec) { maxBytesPerSec = bytesThisSec; } thisSec = sampleTime - (sampleTime % timeScale); bytesThisSec = sampleSize; } } // last second (or partial second) if (bytesThisSec > maxBytesPerSec) { maxBytesPerSec = bytesThisSec; } return maxBytesPerSec * 8; }
void MP4Track::ReadSample( MP4SampleId sampleId, u_int8_t** ppBytes, u_int32_t* pNumBytes, MP4Timestamp* pStartTime, MP4Duration* pDuration, MP4Duration* pRenderingOffset, bool* pIsSyncSample) { if (sampleId == MP4_INVALID_SAMPLE_ID) { throw new MP4Error("sample id can't be zero", "MP4Track::ReadSample"); } // handle unusual case of wanting to read a sample // that is still sitting in the write chunk buffer if (m_pChunkBuffer && sampleId >= m_writeSampleId - m_chunkSamples) { WriteChunkBuffer(); } FILE* pFile = GetSampleFile(sampleId); if (pFile == (FILE*)-1) { throw new MP4Error("sample is located in an inaccessible file", "MP4Track::ReadSample"); } u_int64_t fileOffset = GetSampleFileOffset(sampleId); u_int32_t sampleSize = GetSampleSize(sampleId); if (*ppBytes != NULL && *pNumBytes < sampleSize) { throw new MP4Error("sample buffer is too small", "MP4Track::ReadSample"); } *pNumBytes = sampleSize; VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: track %u id %u offset 0x"LLX" size %u (0x%x)\n", m_trackId, sampleId, fileOffset, *pNumBytes, *pNumBytes)); bool bufferMalloc = false; if (*ppBytes == NULL) { *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes); bufferMalloc = true; } u_int64_t oldPos = m_pFile->GetPosition(pFile); // only used in mode == 'w' try { m_pFile->SetPosition(fileOffset, pFile); m_pFile->ReadBytes(*ppBytes, *pNumBytes, pFile); if (pStartTime || pDuration) { GetSampleTimes(sampleId, pStartTime, pDuration); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: start "LLU" duration "LLD"\n", (pStartTime ? *pStartTime : 0), (pDuration ? *pDuration : 0))); } if (pRenderingOffset) { *pRenderingOffset = GetSampleRenderingOffset(sampleId); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: renderingOffset "LLD"\n", *pRenderingOffset)); } if (pIsSyncSample) { *pIsSyncSample = IsSyncSample(sampleId); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("ReadSample: isSyncSample %u\n", *pIsSyncSample)); } } catch (MP4Error* e) { if (bufferMalloc) { // let's not leak memory MP4Free(*ppBytes); *ppBytes = NULL; } if (m_pFile->GetMode() == 'w') { m_pFile->SetPosition(oldPos, pFile); } throw e; } if (m_pFile->GetMode() == 'w') { m_pFile->SetPosition(oldPos, pFile); } }
MP4SampleId MP4Track::GetSampleIdFromEditTime( MP4Timestamp editWhen, MP4Timestamp* pStartTime, MP4Duration* pDuration) { MP4SampleId sampleId = MP4_INVALID_SAMPLE_ID; u_int32_t numEdits = 0; if (m_pElstCountProperty) { numEdits = m_pElstCountProperty->GetValue(); } if (numEdits) { MP4Duration editElapsedDuration = 0; for (MP4EditId editId = 1; editId <= numEdits; editId++) { // remember edit segment's start time (in edit timeline) MP4Timestamp editStartTime = (MP4Timestamp)editElapsedDuration; // accumulate edit segment's duration editElapsedDuration += m_pElstDurationProperty->GetValue(editId - 1); // calculate difference between the specified edit time // and the end of this edit segment if (editElapsedDuration - editWhen <= 0) { // the specified time has not yet been reached continue; } // 'editWhen' is within this edit segment // calculate the specified edit time // relative to just this edit segment MP4Duration editOffset = editWhen - editStartTime; // calculate the media (track) time that corresponds // to the specified edit time based on the edit list MP4Timestamp mediaWhen = m_pElstMediaTimeProperty->GetValue(editId - 1) + editOffset; // lookup the sample id for the media time sampleId = GetSampleIdFromTime(mediaWhen, false); // lookup the sample's media start time and duration MP4Timestamp sampleStartTime; MP4Duration sampleDuration; GetSampleTimes(sampleId, &sampleStartTime, &sampleDuration); // calculate the difference if any between when the sample // would naturally start and when it starts in the edit timeline MP4Duration sampleStartOffset = mediaWhen - sampleStartTime; // calculate the start time for the sample in the edit time line MP4Timestamp editSampleStartTime = editWhen - MIN(editOffset, sampleStartOffset); MP4Duration editSampleDuration = 0; // calculate how long this sample lasts in the edit list timeline if (m_pElstRateProperty->GetValue(editId - 1) == 0) { // edit segment is a "dwell" // so sample duration is that of the edit segment editSampleDuration = m_pElstDurationProperty->GetValue(editId - 1); } else { // begin with the natural sample duration editSampleDuration = sampleDuration; // now shorten that if the edit segment starts // after the sample would naturally start if (editOffset < sampleStartOffset) { editSampleDuration -= sampleStartOffset - editOffset; } // now shorten that if the edit segment ends // before the sample would naturally end if (editElapsedDuration < editSampleStartTime + sampleDuration) { editSampleDuration -= (editSampleStartTime + sampleDuration) - editElapsedDuration; } } if (pStartTime) { *pStartTime = editSampleStartTime; } if (pDuration) { *pDuration = editSampleDuration; } VERBOSE_EDIT(m_pFile->GetVerbosity(), printf("GetSampleIdFromEditTime: when %llu " "sampleId %u start %llu duration %lld\n", editWhen, sampleId, editSampleStartTime, editSampleDuration)); return sampleId; } throw new MP4Error("time out of range", "MP4Track::GetSampleIdFromEditTime"); } else { // no edit list sampleId = GetSampleIdFromTime(editWhen, false); if (pStartTime || pDuration) { GetSampleTimes(sampleId, pStartTime, pDuration); } } return sampleId; }