📄 mp4track.cpp
字号:
void MP4Track::WriteChunkBuffer(){ if (m_chunkBufferSize == 0) { return; } u_int64_t chunkOffset = m_pFile->GetPosition(); // write chunk buffer m_pFile->WriteBytes(m_pChunkBuffer, m_chunkBufferSize); VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(), printf("WriteChunk: track %u offset 0x"X64" size %u (0x%x) numSamples %u\n", m_trackId, chunkOffset, m_chunkBufferSize, m_chunkBufferSize, m_chunkSamples)); UpdateSampleToChunk(m_writeSampleId, m_pChunkCountProperty->GetValue() + 1, m_chunkSamples); UpdateChunkOffsets(chunkOffset); // clean up chunk buffer MP4Free(m_pChunkBuffer); m_pChunkBuffer = NULL; m_chunkBufferSize = 0; m_chunkSamples = 0; m_chunkDuration = 0;}void MP4Track::FinishWrite(){ // write out any remaining samples in chunk buffer WriteChunkBuffer(); // record buffer size and bitrates MP4BitfieldProperty* pBufferSizeProperty; if (m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.bufferSizeDB", (MP4Property**)&pBufferSizeProperty)) { pBufferSizeProperty->SetValue(GetMaxSampleSize()); } MP4Integer32Property* pBitrateProperty; if (m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.maxBitrate", (MP4Property**)&pBitrateProperty)) { pBitrateProperty->SetValue(GetMaxBitrate()); } if (m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate", (MP4Property**)&pBitrateProperty)) { pBitrateProperty->SetValue(GetAvgBitrate()); }}bool MP4Track::IsChunkFull(MP4SampleId sampleId){ if (m_samplesPerChunk) { return m_chunkSamples >= m_samplesPerChunk; } ASSERT(m_durationPerChunk); return m_chunkDuration >= m_durationPerChunk;}u_int32_t MP4Track::GetNumberOfSamples(){ return m_pStszSampleCountProperty->GetValue();}u_int32_t MP4Track::GetSampleSize(MP4SampleId sampleId){ u_int32_t fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue(); if (fixedSampleSize != 0) { return fixedSampleSize * m_bytesPerSample; } return m_bytesPerSample * m_pStszSampleSizeProperty->GetValue(sampleId - 1);}u_int32_t MP4Track::GetMaxSampleSize(){ u_int32_t fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue(); if (fixedSampleSize != 0) { return fixedSampleSize * m_bytesPerSample; } u_int32_t maxSampleSize = 0; u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount(); for (MP4SampleId sid = 1; sid <= numSamples; sid++) { u_int32_t sampleSize = m_pStszSampleSizeProperty->GetValue(sid - 1); if (sampleSize > maxSampleSize) { maxSampleSize = sampleSize; } } return maxSampleSize * m_bytesPerSample;}u_int64_t MP4Track::GetTotalOfSampleSizes(){ uint64_t retval; u_int32_t fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue(); // if fixed sample size, just need to multiply by number of samples if (fixedSampleSize != 0) { retval = m_bytesPerSample; retval *= fixedSampleSize; retval *= GetNumberOfSamples(); return retval; } // else non-fixed sample size, sum them u_int64_t totalSampleSizes = 0; u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount(); for (MP4SampleId sid = 1; sid <= numSamples; sid++) { u_int32_t sampleSize = m_pStszSampleSizeProperty->GetValue(sid - 1); totalSampleSizes += sampleSize; } return totalSampleSizes * m_bytesPerSample;}void MP4Track::UpdateSampleSizes(MP4SampleId sampleId, u_int32_t numBytes){ if (m_bytesPerSample > 1) { if ((numBytes % m_bytesPerSample) != 0) { // error VERBOSE_ERROR(m_pFile->GetVerbosity(), printf("UpdateSampleSize: numBytes %u not divisible by bytesPerSample %u sampleId %u\n", numBytes, m_bytesPerSample, sampleId); ); } numBytes /= m_bytesPerSample; } // for first sample if (sampleId == 1) { if (numBytes > 0) { // presume sample size is fixed m_pStszFixedSampleSizeProperty->SetValue(numBytes); } else { // special case of first sample is zero bytes in length // leave m_pStszFixedSampleSizeProperty at 0 // start recording variable sample sizes m_pStszFixedSampleSizeProperty->SetValue(0); m_pStszSampleSizeProperty->AddValue(0); } } else { // sampleId > 1 u_int32_t fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue(); if (fixedSampleSize == 0 || numBytes != fixedSampleSize) { // sample size is not fixed if (fixedSampleSize) { // need to clear fixed sample size m_pStszFixedSampleSizeProperty->SetValue(0); // and create sizes for all previous samples for (MP4SampleId sid = 1; sid < sampleId; sid++) { m_pStszSampleSizeProperty->AddValue(fixedSampleSize); } } // add size value for this sample m_pStszSampleSizeProperty->AddValue(numBytes); } } m_pStszSampleCountProperty->IncrementValue();#if 0 printf("track %u sample id %u bytes %u fixed %u count %u prop %u\n", m_trackId, sampleId, numBytes, m_pStszFixedSampleSizeProperty->GetValue(), m_pStszSampleSizeProperty->GetCount(), m_pStszSampleCountProperty->GetValue());#endif}u_int32_t MP4Track::GetAvgBitrate(){ if (GetDuration() == 0) { return 0; } double calc = UINT64_TO_DOUBLE(GetTotalOfSampleSizes()); // this is a bit better - we use the whole duration calc *= 8.0; calc *= GetTimeScale(); calc /= UINT64_TO_DOUBLE(GetDuration()); // we might want to think about rounding to the next 100 or 1000 return (uint32_t) ceil(calc);}u_int32_t MP4Track::GetMaxBitrate(){ u_int32_t timeScale = GetTimeScale(); MP4SampleId numSamples = GetNumberOfSamples(); u_int32_t maxBytesPerSec = 0; u_int32_t bytesThisSec = 0; MP4Timestamp thisSecStart = 0; MP4Timestamp lastSampleTime = 0; uint32_t lastSampleSize = 0; MP4SampleId thisSecStartSid = 1; for (MP4SampleId sid = 1; sid <= numSamples; sid++) { uint32_t sampleSize; MP4Timestamp sampleTime; sampleSize = GetSampleSize(sid); GetSampleTimes(sid, &sampleTime, NULL); if (sampleTime < thisSecStart + timeScale) { bytesThisSec += sampleSize; lastSampleSize = sampleSize; lastSampleTime = sampleTime; } else { // we've already written the last sample and sampleSize. // this means that we've probably overflowed the last second // calculate the time we've overflowed MP4Duration overflow_dur = (thisSecStart + timeScale) - lastSampleTime; // calculate the duration of the last sample MP4Duration lastSampleDur = sampleTime - lastSampleTime; uint32_t overflow_bytes; // now, calculate the number of bytes we overflowed. Round up. overflow_bytes = ((lastSampleSize * overflow_dur) + (lastSampleDur - 1)) / lastSampleDur; if (bytesThisSec - overflow_bytes > maxBytesPerSec) { maxBytesPerSec = bytesThisSec - overflow_bytes; } // now adjust the values for this sample. Remove the bytes // from the first sample in this time frame lastSampleTime = sampleTime; lastSampleSize = sampleSize; bytesThisSec += sampleSize; bytesThisSec -= GetSampleSize(thisSecStartSid); thisSecStartSid++; GetSampleTimes(thisSecStartSid, &thisSecStart, NULL); } } return maxBytesPerSec * 8;}u_int32_t MP4Track::GetSampleStscIndex(MP4SampleId sampleId){ u_int32_t stscIndex; u_int32_t numStscs = m_pStscCountProperty->GetValue(); if (numStscs == 0) { throw new MP4Error("No data chunks exist", "GetSampleStscIndex"); } for (stscIndex = 0; stscIndex < numStscs; stscIndex++) { if (sampleId < m_pStscFirstSampleProperty->GetValue(stscIndex)) { ASSERT(stscIndex != 0); stscIndex -= 1; break; } } if (stscIndex == numStscs) { ASSERT(stscIndex != 0); stscIndex -= 1; } return stscIndex;}FILE* MP4Track::GetSampleFile(MP4SampleId sampleId){ u_int32_t stscIndex = GetSampleStscIndex(sampleId); u_int32_t stsdIndex = m_pStscSampleDescrIndexProperty->GetValue(stscIndex); // check if the answer will be the same as last time if (m_lastStsdIndex && stsdIndex == m_lastStsdIndex) { return m_lastSampleFile; } MP4Atom* pStsdAtom = m_pTrakAtom->FindAtom("trak.mdia.minf.stbl.stsd"); ASSERT(pStsdAtom); MP4Atom* pStsdEntryAtom = pStsdAtom->GetChildAtom(stsdIndex - 1); ASSERT(pStsdEntryAtom); MP4Integer16Property* pDrefIndexProperty = NULL; pStsdEntryAtom->FindProperty( "*.dataReferenceIndex", (MP4Property**)&pDrefIndexProperty); if (pDrefIndexProperty == NULL) { throw new MP4Error("invalid stsd entry", "GetSampleFile"); } u_int32_t drefIndex = pDrefIndexProperty->GetValue(); MP4Atom* pDrefAtom = m_pTrakAtom->FindAtom("trak.mdia.minf.dinf.dref"); ASSERT(pDrefAtom); MP4Atom* pUrlAtom = pDrefAtom->GetChildAtom(drefIndex - 1); ASSERT(pUrlAtom); FILE* pFile; if (pUrlAtom->GetFlags() & 1) { pFile = NULL; // self-contained } else { MP4StringProperty* pLocationProperty = NULL; pUrlAtom->FindProperty( "*.location", (MP4Property**)&pLocationProperty); ASSERT(pLocationProperty); const char* url = pLocationProperty->GetValue(); VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(), printf("dref url = %s\n", url)); pFile = (FILE*)-1; // attempt to open url if it's a file url // currently this is the only thing we understand if (!strncmp(url, "file:", 5)) { const char* fileName = url + 5; if (!strncmp(fileName, "//", 2)) { fileName = strchr(fileName + 2, '/'); } if (fileName) { pFile = fopen(fileName, "rb"); if (!pFile) { pFile = (FILE*)-1; } } } } if (m_lastSampleFile) { fclose(m_lastSampleFile); } // cache the answer m_lastStsdIndex = stsdIndex; m_lastSampleFile = pFile; return pFile;}u_int64_t MP4Track::GetSampleFileOffset(MP4SampleId sampleId){ u_int32_t stscIndex = GetSampleStscIndex(sampleId); // firstChunk is the chunk index of the first chunk with // samplesPerChunk samples in the chunk. There may be multiples - // ie: several chunks with the same number of samples per chunk. u_int32_t firstChunk = m_pStscFirstChunkProperty->GetValue(stscIndex); MP4SampleId firstSample = m_pStscFirstSampleProperty->GetValue(stscIndex); u_int32_t samplesPerChunk = m_pStscSamplesPerChunkProperty->GetValue(stscIndex); // chunkId tells which is the absolute chunk number that this sample // is stored in. MP4ChunkId chunkId = firstChunk + ((sampleId - firstSample) / samplesPerChunk); // chunkOffset is the file offset (absolute) for the start of the chunk u_int64_t chunkOffset = m_pChunkOffsetProperty->GetValue(chunkId - 1); MP4SampleId firstSampleInChunk = sampleId - ((sampleId - firstSample) % samplesPerChunk); // need cumulative samples sizes from firstSample to sampleId - 1 u_int32_t sampleOffset = 0; for (MP4SampleId i = firstSampleInChunk; i < sampleId; i++) { sampleOffset += GetSampleSize(i); } return chunkOffset + sampleOffset;}void MP4Track::UpdateSampleToChunk(MP4SampleId sampleId, MP4ChunkId chunkId, u_int32_t samplesPerChunk){ u_int32_t numStsc = m_pStscCountProperty->GetValue(); // if samplesPerChunk == samplesPerChunk of last entry if (numStsc && samplesPerChunk == m_pStscSamplesPerChunkProperty->GetValue(numStsc-1)) { // nothing to do } else { // add stsc entry m_pStscFirstChunkProperty->AddValue(chunkId); m_pStscSamplesPerChunkProperty->AddValue(samplesPerChunk); m_pStscSampleDescrIndexProperty->AddValue(1); m_pStscFirstSampleProperty->AddValue(sampleId - samplesPerChunk + 1); m_pStscCountProperty->IncrementValue(); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -