123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929 |
- /*
- * 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 - 2004. All Rights Reserved.
- *
- * 3GPP features implementation is based on 3GPP's TS26.234-v5.60,
- * and was contributed by Ximpo Group Ltd.
- *
- * Portions created by Ximpo Group Ltd. are
- * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved.
- *
- * Contributor(s):
- * Dave Mackie [email protected]
- * Alix Marchandise-Franquet [email protected]
- * Ximpo Group Ltd. [email protected]
- */
- #include "mp4common.h"
- #include <limits.h>
- #define AMR_UNINITIALIZED -1
- #define AMR_TRUE 0
- #define AMR_FALSE 1
- static uint32_t SafeMultiply(uint32_t bytesPerSample, uint32_t numSamples)
- {
- if (_UI32_MAX/bytesPerSample < numSamples)
- return 0;
- else
- return numSamples * bytesPerSample;
- }
- static bool TrySafeMultiply(uint32_t bytesPerSample, uint32_t numSamples, uint32_t *value)
- {
- if (_UI32_MAX/bytesPerSample < numSamples)
- return false;
- else
- *value = numSamples * bytesPerSample;
- return true;
- }
- static bool TrySafeAdd(uint32_t val1, uint32_t val2, uint32_t *value)
- {
- if (_UI32_MAX - val1 < val2)
- return false;
- else
- *value = val1 + val2;
- return true;
- }
- MP4Track::MP4Track(MP4File* pFile, MP4Atom* pTrakAtom)
- {
- m_pFile = pFile;
- m_pTrakAtom = pTrakAtom;
- m_pTypeProperty = NULL;
- 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_bytesPerSample should be set to 1, except for the
- // quicktime audio constant bit rate samples, which have non-1 values
- m_bytesPerSample = 1;
- m_samplesPerChunk = 0;
- m_durationPerChunk = 0;
- m_isAmr = AMR_UNINITIALIZED;
- m_curMode = 0;
- m_pTimeScaleProperty = NULL;
- m_pTrackDurationProperty = NULL;
- m_pMediaDurationProperty = NULL;
- m_pTrackModificationProperty = NULL;
- m_pMediaModificationProperty = NULL;
- m_pStszFixedSampleSizeProperty = NULL;
- m_pStszSampleCountProperty = NULL;
- m_pStszSampleSizeProperty = NULL;
- m_pStscCountProperty = NULL;
- m_pStscFirstChunkProperty = NULL;
- m_pStscSamplesPerChunkProperty = NULL;
- m_pStscSampleDescrIndexProperty = NULL;
- m_pStscFirstSampleProperty = NULL;
- m_pChunkCountProperty = NULL;
- m_pChunkOffsetProperty = NULL;
- m_pSttsCountProperty = NULL;
- m_pSttsSampleCountProperty = NULL;
- m_pSttsSampleDeltaProperty = NULL;
- m_pCttsCountProperty = NULL;
- m_pCttsSampleCountProperty = NULL;
- m_pCttsSampleOffsetProperty = NULL;
- m_pStssCountProperty = NULL;
- m_pStssSampleProperty = NULL;
- m_pElstCountProperty = NULL;
- m_pElstMediaTimeProperty = NULL;
- m_pElstDurationProperty = NULL;
- m_pElstRateProperty = NULL;
- m_pElstReservedProperty = NULL;
- m_cachedSttsIndex = 0;
- m_cachedSttsElapsed = 0;
- m_cachedSttsSid = MP4_INVALID_SAMPLE_ID;
- 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
- m_pStszFixedSampleSizeProperty = NULL;
- bool have_stsz =
- m_pTrakAtom->FindProperty("trak.mdia.minf.stbl.stsz.sampleSize",
- (MP4Property**)&m_pStszFixedSampleSizeProperty);
-
- if (have_stsz) {
- success &= m_pTrakAtom->FindProperty(
- "trak.mdia.minf.stbl.stsz.sampleCount",
- (MP4Property**)&m_pStszSampleCountProperty);
- success &= m_pTrakAtom->FindProperty(
- "trak.mdia.minf.stbl.stsz.entries.entrySize",
- (MP4Property**)&m_pStszSampleSizeProperty);
- m_stsz_sample_bits = 32;
- } else {
- success &= m_pTrakAtom->FindProperty(
- "trak.mdia.minf.stbl.stz2.sampleCount",
- (MP4Property**)&m_pStszSampleCountProperty);
- success &= m_pTrakAtom->FindProperty(
- "trak.mdia.minf.stbl.stz2.entries.entrySize",
- (MP4Property**)&m_pStszSampleSizeProperty);
- MP4Integer8Property *stz2_field_size;
- if (m_pTrakAtom->FindProperty(
- "trak.mdia.minf.stbl.stz2.fieldSize",
- (MP4Property **)&stz2_field_size)) {
- m_stsz_sample_bits = stz2_field_size->GetValue();
- m_have_stz2_4bit_sample = false;
- } else success = false;
- }
-
- // 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
- (void)InitEditListProperties();
- // was everything found?
- if (!success) {
- throw new MP4Error("invalid track", "MP4Track::MP4Track");
- }
- CalculateBytesPerSample();
- }
- 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(MP4NormalizeTrackType(type,
- m_pFile->GetVerbosity()));
- }
- 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 = 0;
- try {
- pFile = GetSampleFile(sampleId);
-
- }
- catch (MP4Error* e)
- {
- // PRINT_ERROR(e);
- delete e;
- pFile = 0;
- }
- 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"X64" 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 "U64" duration "D64"\n",
- (pStartTime ? *pStartTime : 0),
- (pDuration ? *pDuration : 0)));
- }
- if (pRenderingOffset) {
- *pRenderingOffset = GetSampleRenderingOffset(sampleId);
- VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
- printf("ReadSample: renderingOffset "D64"\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)
- {
- u_int8_t curMode = 0;
- 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 (m_isAmr == AMR_UNINITIALIZED ) {
- // figure out if this is an AMR audio track
- if (m_pTrakAtom->FindAtomMP4("trak.mdia.minf.stbl.stsd.samr") ||
- m_pTrakAtom->FindAtomMP4("trak.mdia.minf.stbl.stsd.sawb")) {
- m_isAmr = AMR_TRUE;
- m_curMode = (pBytes[0] >> 3) & 0x000F;
- } else {
- m_isAmr = AMR_FALSE;
- }
- }
-
- if (m_isAmr == AMR_TRUE) {
- curMode = (pBytes[0] >> 3) &0x000F; // The mode is in the first byte
- }
- if (duration == MP4_INVALID_DURATION) {
- duration = GetFixedSampleDuration();
- }
- VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
- printf("duration "U64"\n", duration));
- if ((m_isAmr == AMR_TRUE) &&
- (m_curMode != curMode)) {
- WriteChunkBuffer();
- m_curMode = curMode;
- }
- // append sample bytes to chunk buffer
- m_pChunkBuffer = (u_int8_t*)MP4Realloc(m_pChunkBuffer,
- m_chunkBufferSize + numBytes);
- if (m_pChunkBuffer == NULL) return;
- 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();
- m_curMode = curMode;
- }
- 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"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();
- if (m_pStszFixedSampleSizeProperty == NULL &&
- m_stsz_sample_bits == 4) {
- if (m_have_stz2_4bit_sample) {
- ((MP4Integer8Property *)m_pStszSampleSizeProperty)->AddValue(m_stz2_4bit_sample_value);
- m_pStszSampleSizeProperty->IncrementValue();
- }
- }
- // 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)
- {
- if (m_pStszFixedSampleSizeProperty != NULL)
- {
- u_int32_t fixedSampleSize =
- m_pStszFixedSampleSizeProperty->GetValue();
- if (fixedSampleSize != 0)
- {
- return SafeMultiply(m_bytesPerSample, fixedSampleSize);
- }
- }
- // will have to check for 4 bit sample size here
- if (m_stsz_sample_bits == 4) {
- uint8_t value = m_pStszSampleSizeProperty->GetValue((sampleId - 1) / 2);
- if ((sampleId - 1) / 2 == 0) {
- value >>= 4;
- } else value &= 0xf;
- return SafeMultiply(m_bytesPerSample, value);
- }
- return SafeMultiply(m_bytesPerSample, m_pStszSampleSizeProperty->GetValue(sampleId - 1));
- }
- u_int32_t MP4Track::GetMaxSampleSize()
- {
- if (m_pStszFixedSampleSizeProperty != NULL)
- {
- u_int32_t fixedSampleSize =
- m_pStszFixedSampleSizeProperty->GetValue();
- if (fixedSampleSize != 0)
- {
- return SafeMultiply(m_bytesPerSample, 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 SafeMultiply(m_bytesPerSample, maxSampleSize);
- }
- u_int64_t MP4Track::GetTotalOfSampleSizes()
- {
- uint64_t retval;
- if (m_pStszFixedSampleSizeProperty != NULL) {
- 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::SampleSizePropertyAddValue (uint32_t size)
- {
- // this has to deal with different sample size values
- switch (m_pStszSampleSizeProperty->GetType()) {
- case Integer32Property:
- ((MP4Integer32Property *)m_pStszSampleSizeProperty)->AddValue(size);
- break;
- case Integer16Property:
- ((MP4Integer16Property *)m_pStszSampleSizeProperty)->AddValue(size);
- break;
- case Integer8Property:
- if (m_stsz_sample_bits == 4) {
- if (m_have_stz2_4bit_sample == false) {
- m_have_stz2_4bit_sample = true;
- m_stz2_4bit_sample_value = size << 4;
- return;
- } else {
- m_have_stz2_4bit_sample = false;
- size &= 0xf;
- size |= m_stz2_4bit_sample_value;
- }
- }
- ((MP4Integer8Property *)m_pStszSampleSizeProperty)->AddValue(size);
- break;
- default: break;
- }
- // m_pStszSampleSizeProperty->IncrementValue();
- }
- 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 (m_pStszFixedSampleSizeProperty == NULL ||
- numBytes == 0) {
- // special case of first sample is zero bytes in length
- // leave m_pStszFixedSampleSizeProperty at 0
- // start recording variable sample sizes
- if (m_pStszFixedSampleSizeProperty != NULL)
- m_pStszFixedSampleSizeProperty->SetValue(0);
- SampleSizePropertyAddValue(0);
- } else {
- // presume sample size is fixed
- m_pStszFixedSampleSizeProperty->SetValue(numBytes);
- }
- } else { // sampleId > 1
- u_int32_t fixedSampleSize = 0;
- if (m_pStszFixedSampleSizeProperty != NULL) {
- fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue();
- }
- // if we don't have a fixed size, or the current sample size
- // doesn't match our sample size, we need to write the current
- // sample size into the table
- if (fixedSampleSize == 0 || numBytes != fixedSampleSize) {
- if (fixedSampleSize != 0) {
- // fixed size was set; we need to clear fixed sample size
- if (m_pStszFixedSampleSizeProperty != NULL) {
- m_pStszFixedSampleSizeProperty->SetValue(0);
- }
-
- // and create sizes for all previous samples
- for (MP4SampleId sid = 1; sid < sampleId; sid++) {
- SampleSizePropertyAddValue(fixedSampleSize);
- }
- }
- // add size value for this sample
- SampleSizePropertyAddValue(numBytes);
- }
- }
- // either way, we increment the number of samples.
- 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->FindAtomMP4("trak.mdia.minf.stbl.stsd");
- ASSERT(pStsdAtom);
- MP4Atom* pStsdEntryAtom =
- pStsdAtom->GetChildAtom(stsdIndex - 1);
- ASSERT(pStsdEntryAtom);
- MP4Integer16Property* pDrefIndexProperty = NULL;
- if (!pStsdEntryAtom->FindProperty(
- "*.dataReferenceIndex",
- (MP4Property**)&pDrefIndexProperty) ||
-
- pDrefIndexProperty == NULL) {
- return 0;
- }
- u_int32_t drefIndex =
- pDrefIndexProperty->GetValue();
- MP4Atom* pDrefAtom =
- m_pTrakAtom->FindAtomMP4("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;
- ASSERT(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_int64_t firstChunk = m_pStscFirstChunkProperty->GetValue(stscIndex);
- MP4SampleId firstSample = m_pStscFirstSampleProperty->GetValue(stscIndex);
- u_int64_t samplesPerChunk = m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
- // chunkId tells which is the absolute chunk number that this sample
- // is stored in.
- MP4ChunkId chunkId = firstChunk + ((static_cast<unsigned long long>(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 - ((static_cast<unsigned long long>(sampleId) - firstSample) % samplesPerChunk);
- // need cumulative samples sizes from firstSample to sampleId - 1
- u_int64_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();
- }
- }
- void MP4Track::UpdateChunkOffsets(u_int64_t chunkOffset)
- {
- if (m_pChunkOffsetProperty->GetType() == Integer32Property) {
- ((MP4Integer32Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
- } else {
- ((MP4Integer64Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
- }
- m_pChunkCountProperty->IncrementValue();
- }
- MP4Duration MP4Track::GetFixedSampleDuration()
- {
- u_int32_t numStts = m_pSttsCountProperty->GetValue();
- if (numStts == 0) {
- return m_fixedSampleDuration;
- }
- if (numStts != 1) {
- return MP4_INVALID_DURATION; // sample duration is not fixed
- }
- return m_pSttsSampleDeltaProperty->GetValue(0);
- }
- void MP4Track::SetFixedSampleDuration(MP4Duration duration)
- {
- u_int32_t numStts = m_pSttsCountProperty->GetValue();
- // setting this is only allowed before samples have been written
- if (numStts != 0) {
- return;
- }
- m_fixedSampleDuration = duration;
- return;
- }
- void MP4Track::GetSampleTimes(MP4SampleId sampleId,
- MP4Timestamp* pStartTime, MP4Duration* pDuration)
- {
- u_int32_t numStts = m_pSttsCountProperty->GetValue();
- MP4SampleId sid;
- MP4Duration elapsed;
-
-
- if (m_cachedSttsSid != MP4_INVALID_SAMPLE_ID && sampleId >= m_cachedSttsSid) {
- sid = m_cachedSttsSid;
- elapsed = m_cachedSttsElapsed;
- } else {
- m_cachedSttsIndex = 0;
- sid = 1;
- elapsed = 0;
- }
- for (u_int32_t sttsIndex = m_cachedSttsIndex; sttsIndex < numStts; sttsIndex++) {
- MP4SampleId sampleCount =
- m_pSttsSampleCountProperty->GetValue(sttsIndex);
- MP4Duration sampleDelta =
- m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
- if (sampleId <= sid + sampleCount - 1) {
- if (pStartTime) {
- *pStartTime = (static_cast<MP4Timestamp>(sampleId) - sid);
- *pStartTime *= sampleDelta;
- *pStartTime += elapsed;
- }
- if (pDuration) {
- *pDuration = sampleDelta;
- }
- m_cachedSttsIndex = sttsIndex;
- m_cachedSttsSid = sid;
- m_cachedSttsElapsed = elapsed;
- return;
- }
- sid += sampleCount;
- elapsed += sampleCount * sampleDelta;
- }
- throw new MP4Error("sample id out of range",
- "MP4Track::GetSampleTimes");
- }
- MP4SampleId MP4Track::GetSampleIdFromTime(
- MP4Timestamp when,
- bool wantSyncSample,
- bool rewind)
- {
- u_int32_t numStts = m_pSttsCountProperty->GetValue();
- MP4SampleId sid = 1;
- MP4Duration elapsed = 0;
- for (u_int32_t sttsIndex = 0; sttsIndex < numStts; sttsIndex++) {
- MP4SampleId sampleCount =
- m_pSttsSampleCountProperty->GetValue(sttsIndex);
- MP4Duration sampleDelta =
- m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
- if (sampleDelta == 0 && sttsIndex < numStts - 1) {
- VERBOSE_READ(m_pFile->GetVerbosity(),
- printf("Warning: Zero sample duration, stts entry %u\n",
- sttsIndex));
- }
- MP4Duration d = when - elapsed;
- if (d <= sampleCount * sampleDelta) {
- MP4SampleId sampleId = sid;
- if (sampleDelta) {
- sampleId += (d / sampleDelta);
- }
- if (wantSyncSample) {
- return GetSyncSample(sampleId, rewind);
- }
- return sampleId;
- }
- sid += sampleCount;
- elapsed += sampleCount * sampleDelta;
- }
- throw new MP4Error("time out of range",
- "MP4Track::GetSampleIdFromTime");
- return 0; // satisfy MS compiler
- }
- MP4ChunkId MP4Track::GetChunkIdFromTime(
- MP4Timestamp when)
- {
- MP4ChunkId numChunks = GetNumberOfChunks();
- for (MP4ChunkId chunk = 1; chunk <= numChunks; chunk++)
- {
- MP4Timestamp d = GetChunkTime(chunk);
- if (d == when)
- return chunk;
- else if (d > when)
- return chunk==1?1:(chunk-1);
- }
- return numChunks;
- }
- void MP4Track::UpdateSampleTimes(MP4Duration duration)
- {
- u_int32_t numStts = m_pSttsCountProperty->GetValue();
- // if duration == duration of last entry
- if (numStts
- && duration == m_pSttsSampleDeltaProperty->GetValue(numStts-1)) {
- // increment last entry sampleCount
- m_pSttsSampleCountProperty->IncrementValue(1, numStts-1);
- } else {
- // add stts entry, sampleCount = 1, sampleDuration = duration
- m_pSttsSampleCountProperty->AddValue(1);
- m_pSttsSampleDeltaProperty->AddValue(duration);
- m_pSttsCountProperty->IncrementValue();;
- }
- }
- u_int32_t MP4Track::GetSampleCttsIndex(MP4SampleId sampleId,
- MP4SampleId* pFirstSampleId)
- {
- u_int32_t numCtts = m_pCttsCountProperty->GetValue();
- MP4SampleId sid = 1;
-
- for (u_int32_t cttsIndex = 0; cttsIndex < numCtts; cttsIndex++) {
- u_int32_t sampleCount =
- m_pCttsSampleCountProperty->GetValue(cttsIndex);
- if (sampleId <= sid + sampleCount - 1) {
- if (pFirstSampleId) {
- *pFirstSampleId = sid;
- }
- return cttsIndex;
- }
- sid += sampleCount;
- }
- throw new MP4Error("sample id out of range",
- "MP4Track::GetSampleCttsIndex");
- return 0; // satisfy MS compiler
- }
- MP4Duration MP4Track::GetSampleRenderingOffset(MP4SampleId sampleId)
- {
- if (m_pCttsCountProperty == NULL) {
- return 0;
- }
- if (m_pCttsCountProperty->GetValue() == 0) {
- return 0;
- }
- u_int32_t cttsIndex = GetSampleCttsIndex(sampleId);
- return m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
- }
- void MP4Track::UpdateRenderingOffsets(MP4SampleId sampleId,
- MP4Duration renderingOffset)
- {
- // if ctts atom doesn't exist
- if (m_pCttsCountProperty == NULL) {
- // no rendering offset, so nothing to do
- if (renderingOffset == 0) {
- return;
- }
- // else create a ctts atom
- MP4Atom* pCttsAtom = AddAtom("trak.mdia.minf.stbl", "ctts");
- // and get handles on the properties
- ASSERT(pCttsAtom->FindProperty(
- "ctts.entryCount",
- (MP4Property**)&m_pCttsCountProperty));
- ASSERT(pCttsAtom->FindProperty(
- "ctts.entries.sampleCount",
- (MP4Property**)&m_pCttsSampleCountProperty));
- ASSERT(pCttsAtom->FindProperty(
- "ctts.entries.sampleOffset",
- (MP4Property**)&m_pCttsSampleOffsetProperty));
- // if this is not the first sample
- if (sampleId > 1) {
- // add a ctts entry for all previous samples
- // with rendering offset equal to zero
- m_pCttsSampleCountProperty->AddValue(sampleId - 1);
- m_pCttsSampleOffsetProperty->AddValue(0);
- m_pCttsCountProperty->IncrementValue();;
- }
- }
- // ctts atom exists (now)
- u_int32_t numCtts = m_pCttsCountProperty->GetValue();
- // if renderingOffset == renderingOffset of last entry
- if (numCtts && renderingOffset
- == m_pCttsSampleOffsetProperty->GetValue(numCtts-1)) {
- // increment last entry sampleCount
- m_pCttsSampleCountProperty->IncrementValue(1, numCtts-1);
- } else {
- // add ctts entry, sampleCount = 1, sampleOffset = renderingOffset
- m_pCttsSampleCountProperty->AddValue(1);
- m_pCttsSampleOffsetProperty->AddValue(renderingOffset);
- m_pCttsCountProperty->IncrementValue();
- }
- }
- void MP4Track::SetSampleRenderingOffset(MP4SampleId sampleId,
- MP4Duration renderingOffset)
- {
- // check if any ctts entries exist
- if (m_pCttsCountProperty == NULL
- || m_pCttsCountProperty->GetValue() == 0) {
- // if not then Update routine can be used
- // to create a ctts entry for samples before this one
- // and a ctts entry for this sample
- UpdateRenderingOffsets(sampleId, renderingOffset);
- // but we also need a ctts entry
- // for all samples after this one
- u_int32_t afterSamples = GetNumberOfSamples() - sampleId;
- if (afterSamples) {
- m_pCttsSampleCountProperty->AddValue(afterSamples);
- m_pCttsSampleOffsetProperty->AddValue(0);
- m_pCttsCountProperty->IncrementValue();;
- }
- return;
- }
- MP4SampleId firstSampleId;
- u_int32_t cttsIndex = GetSampleCttsIndex(sampleId, &firstSampleId);
- // do nothing in the degenerate case
- if (renderingOffset ==
- m_pCttsSampleOffsetProperty->GetValue(cttsIndex)) {
- return;
- }
- u_int32_t sampleCount =
- m_pCttsSampleCountProperty->GetValue(cttsIndex);
- // if this sample has it's own ctts entry
- if (sampleCount == 1) {
- // then just set the value,
- // note we don't attempt to collapse entries
- m_pCttsSampleOffsetProperty->SetValue(renderingOffset, cttsIndex);
- return;
- }
- MP4SampleId lastSampleId = firstSampleId + sampleCount - 1;
- // else we share this entry with other samples
- // we need to insert our own entry
- if (sampleId == firstSampleId) {
- // our sample is the first one
- m_pCttsSampleCountProperty->
- InsertValue(1, cttsIndex);
- m_pCttsSampleOffsetProperty->
- InsertValue(renderingOffset, cttsIndex);
- m_pCttsSampleCountProperty->
- SetValue(sampleCount - 1, cttsIndex + 1);
- m_pCttsCountProperty->IncrementValue();
- } else if (sampleId == lastSampleId) {
- // our sample is the last one
- m_pCttsSampleCountProperty->
- InsertValue(1, cttsIndex + 1);
- m_pCttsSampleOffsetProperty->
- InsertValue(renderingOffset, cttsIndex + 1);
- m_pCttsSampleCountProperty->
- SetValue(sampleCount - 1, cttsIndex);
- m_pCttsCountProperty->IncrementValue();
- } else {
- // our sample is in the middle, UGH!
- // insert our new entry
- m_pCttsSampleCountProperty->
- InsertValue(1, cttsIndex + 1);
- m_pCttsSampleOffsetProperty->
- InsertValue(renderingOffset, cttsIndex + 1);
- // adjust count of previous entry
- m_pCttsSampleCountProperty->
- SetValue(sampleId - firstSampleId, cttsIndex);
- // insert new entry for those samples beyond our sample
- m_pCttsSampleCountProperty->
- InsertValue(lastSampleId - sampleId, cttsIndex + 2);
- u_int32_t oldRenderingOffset =
- m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
- m_pCttsSampleOffsetProperty->
- InsertValue(oldRenderingOffset, cttsIndex + 2);
- m_pCttsCountProperty->IncrementValue(2);
- }
- }
- bool MP4Track::IsSyncSample(MP4SampleId sampleId)
- {
- if (m_pStssCountProperty == NULL) {
- return true;
- }
- u_int32_t numStss = m_pStssCountProperty->GetValue();
- u_int32_t stssLIndex = 0;
- u_int32_t stssRIndex = numStss - 1;
-
- while (stssRIndex >= stssLIndex){
- u_int32_t stssIndex = (stssRIndex + stssLIndex) >> 1;
- MP4SampleId syncSampleId =
- m_pStssSampleProperty->GetValue(stssIndex);
- if (sampleId == syncSampleId) {
- return true;
- }
- if (sampleId > syncSampleId) {
- stssLIndex = stssIndex + 1;
- } else {
- stssRIndex = stssIndex - 1;
- }
- }
- return false;
- }
- // N.B. "next" is inclusive of this sample id
- MP4SampleId MP4Track::GetNextSyncSample(MP4SampleId sampleId)
- {
- if (m_pStssCountProperty == NULL) {
- return sampleId;
- }
- u_int32_t numStss = m_pStssCountProperty->GetValue();
- for (uint32_t stssIndex = 0; stssIndex < numStss; stssIndex++)
- {
- MP4SampleId syncSampleId = m_pStssSampleProperty->GetValue(stssIndex);
- if (sampleId > syncSampleId) {
- continue;
- }
- return syncSampleId;
- }
- // LATER check stsh for alternate sample
- return MP4_INVALID_SAMPLE_ID;
- }
- MP4SampleId MP4Track::GetSyncSample(MP4SampleId sampleId, bool rewind)
- {
- if (m_pStssCountProperty == NULL) {
- return sampleId;
- }
- u_int32_t numStss = m_pStssCountProperty->GetValue();
- MP4SampleId prevSampleId = 1;
- for (uint32_t stssIndex = 0; stssIndex < numStss; stssIndex++)
- {
- MP4SampleId syncSampleId = m_pStssSampleProperty->GetValue(stssIndex);
- if (sampleId > syncSampleId) {
- prevSampleId = syncSampleId;
- continue;
- }
- return rewind ? prevSampleId : syncSampleId;
- }
- // LATER check stsh for alternate sample
- return MP4_INVALID_SAMPLE_ID;
- }
- void MP4Track::UpdateSyncSamples(MP4SampleId sampleId, bool isSyncSample)
- {
- if (isSyncSample) {
- // if stss atom exists, add entry
- if (m_pStssCountProperty) {
- m_pStssSampleProperty->AddValue(sampleId);
- m_pStssCountProperty->IncrementValue();
- } // else nothing to do (yet)
- } else { // !isSyncSample
- // if stss atom doesn't exist, create one
- if (m_pStssCountProperty == NULL) {
- MP4Atom* pStssAtom = AddAtom("trak.mdia.minf.stbl", "stss");
- ASSERT(pStssAtom->FindProperty(
- "stss.entryCount",
- (MP4Property**)&m_pStssCountProperty));
-
- ASSERT(pStssAtom->FindProperty(
- "stss.entries.sampleNumber",
- (MP4Property**)&m_pStssSampleProperty));
- // set values for all samples that came before this one
- for (MP4SampleId sid = 1; sid < sampleId; sid++) {
- m_pStssSampleProperty->AddValue(sid);
- m_pStssCountProperty->IncrementValue();
- }
- } // else nothing to do
- }
- }
- MP4Atom* MP4Track::AddAtom(char* parentName, char* childName)
- {
- MP4Atom* pChildAtom = MP4Atom::CreateAtom(childName);
- MP4Atom* pParentAtom = m_pTrakAtom->FindAtomMP4(parentName);
- ASSERT(pParentAtom);
- pParentAtom->AddChildAtom(pChildAtom);
- pChildAtom->Generate();
- return pChildAtom;
- }
- u_int64_t MP4Track::GetDuration()
- {
- return m_pMediaDurationProperty->GetValue();
- }
- u_int32_t MP4Track::GetTimeScale()
- {
- return m_pTimeScaleProperty->GetValue();
- }
- void MP4Track::UpdateDurations(MP4Duration duration)
- {
- // update media, track, and movie durations
- m_pMediaDurationProperty->SetValue(
- m_pMediaDurationProperty->GetValue() + duration);
- MP4Duration movieDuration = ToMovieDuration(duration);
- m_pTrackDurationProperty->SetValue(
- m_pTrackDurationProperty->GetValue() + movieDuration);
- m_pFile->UpdateDuration(m_pTrackDurationProperty->GetValue());
- }
- MP4Duration MP4Track::ToMovieDuration(MP4Duration trackDuration)
- {
- return (trackDuration * m_pFile->GetTimeScale())
- / m_pTimeScaleProperty->GetValue();
- }
- void MP4Track::UpdateModificationTimes()
- {
- // update media and track modification times
- MP4Timestamp now = MP4GetAbsTimestamp();
- m_pMediaModificationProperty->SetValue(now);
- m_pTrackModificationProperty->SetValue(now);
- }
- u_int32_t MP4Track::GetNumberOfChunks()
- {
- return m_pChunkOffsetProperty->GetCount();
- }
- u_int32_t MP4Track::GetChunkStscIndex(MP4ChunkId chunkId)
- {
- u_int32_t stscIndex;
- u_int32_t numStscs = m_pStscCountProperty->GetValue();
- ASSERT(chunkId);
- ASSERT(numStscs > 0);
- for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
- if (chunkId < m_pStscFirstChunkProperty->GetValue(stscIndex)) {
- ASSERT(stscIndex != 0);
- break;
- }
- }
- return stscIndex - 1;
- }
- 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::GetChunkSize(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);
- uint32_t chunkOffsetBytes;
- if (!TrySafeMultiply(samplesPerChunk, chunkId - firstChunkId, &chunkOffsetBytes))
- return 0;
- MP4SampleId firstSampleInChunk;
- if (!TrySafeAdd(firstSample, chunkOffsetBytes, &firstSampleInChunk))
- return 0;
- // need cumulative sizes of samples in chunk
- u_int32_t chunkSize = 0;
- for (u_int32_t i = 0; i < samplesPerChunk; i++)
- {
- if (!TrySafeAdd(chunkSize, GetSampleSize(firstSampleInChunk + i), &chunkSize))
- return 0;
- }
- return chunkSize;
- }
- void MP4Track::ReadChunk(MP4ChunkId chunkId,
- u_int8_t** ppChunk, u_int32_t* pChunkSize,
- MP4Timestamp* pStartTime, MP4Duration* pDuration)
- {
- ASSERT(chunkId);
- ASSERT(ppChunk);
- ASSERT(pChunkSize);
- bool do_free=false;
- u_int64_t chunkOffset =
- m_pChunkOffsetProperty->GetValue(chunkId - 1);
- *pChunkSize = GetChunkSize(chunkId);
- if (!*ppChunk)
- {
- do_free=true;
- *ppChunk = (u_int8_t*)MP4Malloc(*pChunkSize);
- }
- VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
- printf("ReadChunk: track %u id %u offset 0x"X64" size %u (0x%x)\n",
- m_trackId, chunkId, chunkOffset, *pChunkSize, *pChunkSize));
- u_int64_t oldPos = m_pFile->GetPosition(); // only used in mode == 'w'
- try {
- m_pFile->SetPosition(chunkOffset);
- m_pFile->ReadBytes(*ppChunk, *pChunkSize);
- if (pStartTime)
- *pStartTime = GetChunkTime(chunkId);
- if (pDuration)
- *pDuration = m_durationPerChunk;
- }
- catch (MP4Error* e) {
- // let's not leak memory
- if (do_free)
- MP4Free(*ppChunk);
- *ppChunk = NULL;
- if (m_pFile->GetMode() == 'w') {
- m_pFile->SetPosition(oldPos);
- }
- throw e;
- }
- if (m_pFile->GetMode() == 'w') {
- m_pFile->SetPosition(oldPos);
- }
- }
- void MP4Track::RewriteChunk(MP4ChunkId chunkId,
- u_int8_t* pChunk, u_int32_t chunkSize)
- {
- u_int64_t chunkOffset = m_pFile->GetPosition();
- m_pFile->WriteBytes(pChunk, chunkSize);
- m_pChunkOffsetProperty->SetValue(chunkOffset, chunkId - 1);
- VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
- printf("RewriteChunk: track %u id %u offset 0x"X64" size %u (0x%x)\n",
- m_trackId, chunkId, chunkOffset, chunkSize, chunkSize));
- }
- // map track type name aliases to official names
- bool MP4Track::InitEditListProperties()
- {
- m_pElstCountProperty = NULL;
- m_pElstMediaTimeProperty = NULL;
- m_pElstDurationProperty = NULL;
- m_pElstRateProperty = NULL;
- m_pElstReservedProperty = NULL;
- MP4Atom* pElstAtom =
- m_pTrakAtom->FindAtomMP4("trak.edts.elst");
- if (!pElstAtom) {
- return false;
- }
- (void)pElstAtom->FindProperty(
- "elst.entryCount",
- (MP4Property**)&m_pElstCountProperty);
- (void)pElstAtom->FindProperty(
- "elst.entries.mediaTime",
- (MP4Property**)&m_pElstMediaTimeProperty);
- (void)pElstAtom->FindProperty(
- "elst.entries.segmentDuration",
- (MP4Property**)&m_pElstDurationProperty);
- (void)pElstAtom->FindProperty(
- "elst.entries.mediaRate",
- (MP4Property**)&m_pElstRateProperty);
- (void)pElstAtom->FindProperty(
- "elst.entries.reserved",
- (MP4Property**)&m_pElstReservedProperty);
- return m_pElstCountProperty
- && m_pElstMediaTimeProperty
- && m_pElstDurationProperty
- && m_pElstRateProperty
- && m_pElstReservedProperty;
- }
- MP4EditId MP4Track::AddEdit(MP4EditId editId)
- {
- if (!m_pElstCountProperty) {
- (void)m_pFile->AddDescendantAtoms(m_pTrakAtom, "edts.elst");
- if (InitEditListProperties() == false) return MP4_INVALID_EDIT_ID;
- }
- if (editId == MP4_INVALID_EDIT_ID) {
- editId = m_pElstCountProperty->GetValue() + 1;
- }
- m_pElstMediaTimeProperty->InsertValue(0, editId - 1);
- m_pElstDurationProperty->InsertValue(0, editId - 1);
- m_pElstRateProperty->InsertValue(1, editId - 1);
- m_pElstReservedProperty->InsertValue(0, editId - 1);
- m_pElstCountProperty->IncrementValue();
- return editId;
- }
- void MP4Track::DeleteEdit(MP4EditId editId)
- {
- if (editId == MP4_INVALID_EDIT_ID) {
- throw new MP4Error("edit id can't be zero",
- "MP4Track::DeleteEdit");
- }
- if (!m_pElstCountProperty
- || m_pElstCountProperty->GetValue() == 0) {
- throw new MP4Error("no edits exist",
- "MP4Track::DeleteEdit");
- }
- m_pElstMediaTimeProperty->DeleteValue(editId - 1);
- m_pElstDurationProperty->DeleteValue(editId - 1);
- m_pElstRateProperty->DeleteValue(editId - 1);
- m_pElstReservedProperty->DeleteValue(editId - 1);
- m_pElstCountProperty->IncrementValue(-1);
- // clean up if last edit is deleted
- if (m_pElstCountProperty->GetValue() == 0) {
- m_pElstCountProperty = NULL;
- m_pElstMediaTimeProperty = NULL;
- m_pElstDurationProperty = NULL;
- m_pElstRateProperty = NULL;
- m_pElstReservedProperty = NULL;
- m_pTrakAtom->DeleteChildAtom(
- m_pTrakAtom->FindAtomMP4("trak.edts"));
- }
- }
- MP4Timestamp MP4Track::GetEditStart(
- MP4EditId editId)
- {
- if (editId == MP4_INVALID_EDIT_ID) {
- return MP4_INVALID_TIMESTAMP;
- } else if (editId == 1) {
- return 0;
- }
- return (MP4Timestamp)GetEditTotalDuration(editId - 1);
- }
- MP4Duration MP4Track::GetEditTotalDuration(
- MP4EditId editId)
- {
- u_int32_t numEdits = 0;
- if (m_pElstCountProperty) {
- numEdits = m_pElstCountProperty->GetValue();
- }
- if (editId == MP4_INVALID_EDIT_ID) {
- editId = numEdits;
- }
- if (numEdits == 0 || editId > numEdits) {
- return MP4_INVALID_DURATION;
- }
- MP4Duration totalDuration = 0;
- for (MP4EditId eid = 1; eid <= editId; eid++) {
- totalDuration +=
- m_pElstDurationProperty->GetValue(eid - 1);
- }
- return totalDuration;
- }
- 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 "U64" "
- "sampleId %u start "U64" duration "D64"\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;
- }
- void MP4Track::CalculateBytesPerSample ()
- {
- MP4Atom *pMedia = m_pTrakAtom->FindAtomMP4("trak.mdia.minf.stbl.stsd");
- MP4Atom *pMediaData;
- const char *media_data_name;
- if (pMedia == NULL) return;
- if (pMedia->GetNumberOfChildAtoms() != 1) return;
-
- pMediaData = pMedia->GetChildAtom(0);
- media_data_name = pMediaData->GetType();
- if ((ATOMID(media_data_name) == ATOMID("twos")) ||
- (ATOMID(media_data_name) == ATOMID("sowt"))) {
- MP4IntegerProperty *chan, *sampleSize;
- chan = (MP4IntegerProperty *)pMediaData->GetProperty(4);
- sampleSize = (MP4IntegerProperty *)pMediaData->GetProperty(5);
- m_bytesPerSample = chan->GetValue() * (sampleSize->GetValue() / 8);
- }
- }
-
|