📄 mp4track.cpp
字号:
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2001. All Rights Reserved. * * Contributor(s): * Dave Mackie dmackie@cisco.com */#include "mp4common.h"MP4Track::MP4Track(MP4File* pFile, MP4Atom* pTrakAtom) { m_pFile = pFile; m_pTrakAtom = pTrakAtom; m_lastStsdIndex = 0; m_lastSampleFile = NULL; m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID; m_pCachedReadSample = NULL; m_cachedReadSampleSize = 0; m_writeSampleId = 1; m_fixedSampleDuration = 0; m_pChunkBuffer = NULL; m_chunkBufferSize = 0; m_chunkSamples = 0; m_chunkDuration = 0; m_samplesPerChunk = 0; m_durationPerChunk = 0; bool success = true; MP4Integer32Property* pTrackIdProperty; success &= m_pTrakAtom->FindProperty( "trak.tkhd.trackId", (MP4Property**)&pTrackIdProperty); if (success) { m_trackId = pTrackIdProperty->GetValue(); } success &= m_pTrakAtom->FindProperty( "trak.mdia.mdhd.timeScale", (MP4Property**)&m_pTimeScaleProperty); if (success) { // default chunking is 1 second of samples m_durationPerChunk = m_pTimeScaleProperty->GetValue(); } success &= m_pTrakAtom->FindProperty( "trak.tkhd.duration", (MP4Property**)&m_pTrackDurationProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.mdhd.duration", (MP4Property**)&m_pMediaDurationProperty); success &= m_pTrakAtom->FindProperty( "trak.tkhd.modificationTime", (MP4Property**)&m_pTrackModificationProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.mdhd.modificationTime", (MP4Property**)&m_pMediaModificationProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.hdlr.handlerType", (MP4Property**)&m_pTypeProperty); // get handles on sample size information success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsz.sampleSize", (MP4Property**)&m_pStszFixedSampleSizeProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsz.sampleCount", (MP4Property**)&m_pStszSampleCountProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsz.entries.sampleSize", (MP4Property**)&m_pStszSampleSizeProperty); // get handles on information needed to map sample id's to file offsets success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsc.entryCount", (MP4Property**)&m_pStscCountProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsc.entries.firstChunk", (MP4Property**)&m_pStscFirstChunkProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsc.entries.samplesPerChunk", (MP4Property**)&m_pStscSamplesPerChunkProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsc.entries.sampleDescriptionIndex", (MP4Property**)&m_pStscSampleDescrIndexProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stsc.entries.firstSample", (MP4Property**)&m_pStscFirstSampleProperty); bool haveStco = m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stco.entryCount", (MP4Property**)&m_pChunkCountProperty); if (haveStco) { success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stco.entries.chunkOffset", (MP4Property**)&m_pChunkOffsetProperty); } else { success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.co64.entryCount", (MP4Property**)&m_pChunkCountProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.co64.entries.chunkOffset", (MP4Property**)&m_pChunkOffsetProperty); } // get handles on sample timing info success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stts.entryCount", (MP4Property**)&m_pSttsCountProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stts.entries.sampleCount", (MP4Property**)&m_pSttsSampleCountProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stts.entries.sampleDelta", (MP4Property**)&m_pSttsSampleDeltaProperty); // get handles on rendering offset info if it exists m_pCttsCountProperty = NULL; m_pCttsSampleCountProperty = NULL; m_pCttsSampleOffsetProperty = NULL; bool haveCtts = m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.ctts.entryCount", (MP4Property**)&m_pCttsCountProperty); if (haveCtts) { success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.ctts.entries.sampleCount", (MP4Property**)&m_pCttsSampleCountProperty); success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.ctts.entries.sampleOffset", (MP4Property**)&m_pCttsSampleOffsetProperty); } // get handles on sync sample info if it exists m_pStssCountProperty = NULL; m_pStssSampleProperty = NULL; bool haveStss = m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stss.entryCount", (MP4Property**)&m_pStssCountProperty); if (haveStss) { success &= m_pTrakAtom->FindProperty( "trak.mdia.minf.stbl.stss.entries.sampleNumber", (MP4Property**)&m_pStssSampleProperty); } // edit list InitEditListProperties(); // was everything found? if (!success) { throw new MP4Error("invalid track", "MP4Track::MP4Track"); }}MP4Track::~MP4Track(){ MP4Free(m_pCachedReadSample); MP4Free(m_pChunkBuffer);}const char* MP4Track::GetType(){ return m_pTypeProperty->GetValue();}void MP4Track::SetType(const char* type) { m_pTypeProperty->SetValue(NormalizeTrackType(type));}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); }}void MP4Track::ReadSampleFragment( MP4SampleId sampleId, u_int32_t sampleOffset, u_int16_t sampleLength, u_int8_t* pDest){ if (sampleId == MP4_INVALID_SAMPLE_ID) { throw new MP4Error("invalid sample id", "MP4Track::ReadSampleFragment"); } if (sampleId != m_cachedReadSampleId) { MP4Free(m_pCachedReadSample); m_pCachedReadSample = NULL; m_cachedReadSampleSize = 0; m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID; ReadSample( sampleId, &m_pCachedReadSample, &m_cachedReadSampleSize); m_cachedReadSampleId = sampleId; } if (sampleOffset + sampleLength > m_cachedReadSampleSize) { throw new MP4Error("offset and/or length are too large", "MP4Track::ReadSampleFragment"); } memcpy(pDest, &m_pCachedReadSample[sampleOffset], sampleLength);}void MP4Track::WriteSample( const u_int8_t* pBytes, u_int32_t numBytes, MP4Duration duration, MP4Duration renderingOffset, bool isSyncSample){ VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(), printf("WriteSample: track %u id %u size %u (0x%x) ", m_trackId, m_writeSampleId, numBytes, numBytes)); if (pBytes == NULL && numBytes > 0) { throw new MP4Error("no sample data", "MP4WriteSample"); } if (duration == MP4_INVALID_DURATION) { duration = GetFixedSampleDuration(); } VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(), printf("duration "LLU"\n", duration)); // append sample bytes to chunk buffer m_pChunkBuffer = (u_int8_t*)MP4Realloc(m_pChunkBuffer, m_chunkBufferSize + numBytes); memcpy(&m_pChunkBuffer[m_chunkBufferSize], pBytes, numBytes); m_chunkBufferSize += numBytes; m_chunkSamples++; m_chunkDuration += duration; UpdateSampleSizes(m_writeSampleId, numBytes); UpdateSampleTimes(duration); UpdateRenderingOffsets(m_writeSampleId, renderingOffset); UpdateSyncSamples(m_writeSampleId, isSyncSample); if (IsChunkFull(m_writeSampleId)) { WriteChunkBuffer(); } UpdateDurations(duration); UpdateModificationTimes(); m_writeSampleId++;}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"LLX" 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; } return m_pStszSampleSizeProperty->GetValue(sampleId - 1);}u_int32_t MP4Track::GetMaxSampleSize(){ u_int32_t fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue(); if (fixedSampleSize != 0) { return fixedSampleSize; } 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;}u_int64_t MP4Track::GetTotalOfSampleSizes(){ u_int32_t fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue(); // if fixed sample size, just need to multiply by number of samples if (fixedSampleSize != 0) { return fixedSampleSize * GetNumberOfSamples(); } // 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;}void MP4Track::UpdateSampleSizes(MP4SampleId sampleId, u_int32_t numBytes){ // 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_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); } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -