mp4track.cpp 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929
  1. /*
  2. * The contents of this file are subject to the Mozilla Public
  3. * License Version 1.1 (the "License"); you may not use this file
  4. * except in compliance with the License. You may obtain a copy of
  5. * the License at http://www.mozilla.org/MPL/
  6. *
  7. * Software distributed under the License is distributed on an "AS
  8. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9. * implied. See the License for the specific language governing
  10. * rights and limitations under the License.
  11. *
  12. * The Original Code is MPEG4IP.
  13. *
  14. * The Initial Developer of the Original Code is Cisco Systems Inc.
  15. * Portions created by Cisco Systems Inc. are
  16. * Copyright (C) Cisco Systems Inc. 2001 - 2004. All Rights Reserved.
  17. *
  18. * 3GPP features implementation is based on 3GPP's TS26.234-v5.60,
  19. * and was contributed by Ximpo Group Ltd.
  20. *
  21. * Portions created by Ximpo Group Ltd. are
  22. * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved.
  23. *
  24. * Contributor(s):
  25. * Dave Mackie [email protected]
  26. * Alix Marchandise-Franquet [email protected]
  27. * Ximpo Group Ltd. [email protected]
  28. */
  29. #include "mp4common.h"
  30. #include <limits.h>
  31. #define AMR_UNINITIALIZED -1
  32. #define AMR_TRUE 0
  33. #define AMR_FALSE 1
  34. static uint32_t SafeMultiply(uint32_t bytesPerSample, uint32_t numSamples)
  35. {
  36. if (_UI32_MAX/bytesPerSample < numSamples)
  37. return 0;
  38. else
  39. return numSamples * bytesPerSample;
  40. }
  41. static bool TrySafeMultiply(uint32_t bytesPerSample, uint32_t numSamples, uint32_t *value)
  42. {
  43. if (_UI32_MAX/bytesPerSample < numSamples)
  44. return false;
  45. else
  46. *value = numSamples * bytesPerSample;
  47. return true;
  48. }
  49. static bool TrySafeAdd(uint32_t val1, uint32_t val2, uint32_t *value)
  50. {
  51. if (_UI32_MAX - val1 < val2)
  52. return false;
  53. else
  54. *value = val1 + val2;
  55. return true;
  56. }
  57. MP4Track::MP4Track(MP4File* pFile, MP4Atom* pTrakAtom)
  58. {
  59. m_pFile = pFile;
  60. m_pTrakAtom = pTrakAtom;
  61. m_pTypeProperty = NULL;
  62. m_lastStsdIndex = 0;
  63. m_lastSampleFile = NULL;
  64. m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
  65. m_pCachedReadSample = NULL;
  66. m_cachedReadSampleSize = 0;
  67. m_writeSampleId = 1;
  68. m_fixedSampleDuration = 0;
  69. m_pChunkBuffer = NULL;
  70. m_chunkBufferSize = 0;
  71. m_chunkSamples = 0;
  72. m_chunkDuration = 0;
  73. // m_bytesPerSample should be set to 1, except for the
  74. // quicktime audio constant bit rate samples, which have non-1 values
  75. m_bytesPerSample = 1;
  76. m_samplesPerChunk = 0;
  77. m_durationPerChunk = 0;
  78. m_isAmr = AMR_UNINITIALIZED;
  79. m_curMode = 0;
  80. m_pTimeScaleProperty = NULL;
  81. m_pTrackDurationProperty = NULL;
  82. m_pMediaDurationProperty = NULL;
  83. m_pTrackModificationProperty = NULL;
  84. m_pMediaModificationProperty = NULL;
  85. m_pStszFixedSampleSizeProperty = NULL;
  86. m_pStszSampleCountProperty = NULL;
  87. m_pStszSampleSizeProperty = NULL;
  88. m_pStscCountProperty = NULL;
  89. m_pStscFirstChunkProperty = NULL;
  90. m_pStscSamplesPerChunkProperty = NULL;
  91. m_pStscSampleDescrIndexProperty = NULL;
  92. m_pStscFirstSampleProperty = NULL;
  93. m_pChunkCountProperty = NULL;
  94. m_pChunkOffsetProperty = NULL;
  95. m_pSttsCountProperty = NULL;
  96. m_pSttsSampleCountProperty = NULL;
  97. m_pSttsSampleDeltaProperty = NULL;
  98. m_pCttsCountProperty = NULL;
  99. m_pCttsSampleCountProperty = NULL;
  100. m_pCttsSampleOffsetProperty = NULL;
  101. m_pStssCountProperty = NULL;
  102. m_pStssSampleProperty = NULL;
  103. m_pElstCountProperty = NULL;
  104. m_pElstMediaTimeProperty = NULL;
  105. m_pElstDurationProperty = NULL;
  106. m_pElstRateProperty = NULL;
  107. m_pElstReservedProperty = NULL;
  108. m_cachedSttsIndex = 0;
  109. m_cachedSttsElapsed = 0;
  110. m_cachedSttsSid = MP4_INVALID_SAMPLE_ID;
  111. bool success = true;
  112. MP4Integer32Property* pTrackIdProperty;
  113. success &= m_pTrakAtom->FindProperty(
  114. "trak.tkhd.trackId",
  115. (MP4Property**)&pTrackIdProperty);
  116. if (success) {
  117. m_trackId = pTrackIdProperty->GetValue();
  118. }
  119. success &= m_pTrakAtom->FindProperty(
  120. "trak.mdia.mdhd.timeScale",
  121. (MP4Property**)&m_pTimeScaleProperty);
  122. if (success) {
  123. // default chunking is 1 second of samples
  124. m_durationPerChunk = m_pTimeScaleProperty->GetValue();
  125. }
  126. success &= m_pTrakAtom->FindProperty(
  127. "trak.tkhd.duration",
  128. (MP4Property**)&m_pTrackDurationProperty);
  129. success &= m_pTrakAtom->FindProperty(
  130. "trak.mdia.mdhd.duration",
  131. (MP4Property**)&m_pMediaDurationProperty);
  132. success &= m_pTrakAtom->FindProperty(
  133. "trak.tkhd.modificationTime",
  134. (MP4Property**)&m_pTrackModificationProperty);
  135. success &= m_pTrakAtom->FindProperty(
  136. "trak.mdia.mdhd.modificationTime",
  137. (MP4Property**)&m_pMediaModificationProperty);
  138. success &= m_pTrakAtom->FindProperty(
  139. "trak.mdia.hdlr.handlerType",
  140. (MP4Property**)&m_pTypeProperty);
  141. // get handles on sample size information
  142. m_pStszFixedSampleSizeProperty = NULL;
  143. bool have_stsz =
  144. m_pTrakAtom->FindProperty("trak.mdia.minf.stbl.stsz.sampleSize",
  145. (MP4Property**)&m_pStszFixedSampleSizeProperty);
  146. if (have_stsz) {
  147. success &= m_pTrakAtom->FindProperty(
  148. "trak.mdia.minf.stbl.stsz.sampleCount",
  149. (MP4Property**)&m_pStszSampleCountProperty);
  150. success &= m_pTrakAtom->FindProperty(
  151. "trak.mdia.minf.stbl.stsz.entries.entrySize",
  152. (MP4Property**)&m_pStszSampleSizeProperty);
  153. m_stsz_sample_bits = 32;
  154. } else {
  155. success &= m_pTrakAtom->FindProperty(
  156. "trak.mdia.minf.stbl.stz2.sampleCount",
  157. (MP4Property**)&m_pStszSampleCountProperty);
  158. success &= m_pTrakAtom->FindProperty(
  159. "trak.mdia.minf.stbl.stz2.entries.entrySize",
  160. (MP4Property**)&m_pStszSampleSizeProperty);
  161. MP4Integer8Property *stz2_field_size;
  162. if (m_pTrakAtom->FindProperty(
  163. "trak.mdia.minf.stbl.stz2.fieldSize",
  164. (MP4Property **)&stz2_field_size)) {
  165. m_stsz_sample_bits = stz2_field_size->GetValue();
  166. m_have_stz2_4bit_sample = false;
  167. } else success = false;
  168. }
  169. // get handles on information needed to map sample id's to file offsets
  170. success &= m_pTrakAtom->FindProperty(
  171. "trak.mdia.minf.stbl.stsc.entryCount",
  172. (MP4Property**)&m_pStscCountProperty);
  173. success &= m_pTrakAtom->FindProperty(
  174. "trak.mdia.minf.stbl.stsc.entries.firstChunk",
  175. (MP4Property**)&m_pStscFirstChunkProperty);
  176. success &= m_pTrakAtom->FindProperty(
  177. "trak.mdia.minf.stbl.stsc.entries.samplesPerChunk",
  178. (MP4Property**)&m_pStscSamplesPerChunkProperty);
  179. success &= m_pTrakAtom->FindProperty(
  180. "trak.mdia.minf.stbl.stsc.entries.sampleDescriptionIndex",
  181. (MP4Property**)&m_pStscSampleDescrIndexProperty);
  182. success &= m_pTrakAtom->FindProperty(
  183. "trak.mdia.minf.stbl.stsc.entries.firstSample",
  184. (MP4Property**)&m_pStscFirstSampleProperty);
  185. bool haveStco = m_pTrakAtom->FindProperty(
  186. "trak.mdia.minf.stbl.stco.entryCount",
  187. (MP4Property**)&m_pChunkCountProperty);
  188. if (haveStco) {
  189. success &= m_pTrakAtom->FindProperty(
  190. "trak.mdia.minf.stbl.stco.entries.chunkOffset",
  191. (MP4Property**)&m_pChunkOffsetProperty);
  192. } else {
  193. success &= m_pTrakAtom->FindProperty(
  194. "trak.mdia.minf.stbl.co64.entryCount",
  195. (MP4Property**)&m_pChunkCountProperty);
  196. success &= m_pTrakAtom->FindProperty(
  197. "trak.mdia.minf.stbl.co64.entries.chunkOffset",
  198. (MP4Property**)&m_pChunkOffsetProperty);
  199. }
  200. // get handles on sample timing info
  201. success &= m_pTrakAtom->FindProperty(
  202. "trak.mdia.minf.stbl.stts.entryCount",
  203. (MP4Property**)&m_pSttsCountProperty);
  204. success &= m_pTrakAtom->FindProperty(
  205. "trak.mdia.minf.stbl.stts.entries.sampleCount",
  206. (MP4Property**)&m_pSttsSampleCountProperty);
  207. success &= m_pTrakAtom->FindProperty(
  208. "trak.mdia.minf.stbl.stts.entries.sampleDelta",
  209. (MP4Property**)&m_pSttsSampleDeltaProperty);
  210. // get handles on rendering offset info if it exists
  211. m_pCttsCountProperty = NULL;
  212. m_pCttsSampleCountProperty = NULL;
  213. m_pCttsSampleOffsetProperty = NULL;
  214. bool haveCtts = m_pTrakAtom->FindProperty(
  215. "trak.mdia.minf.stbl.ctts.entryCount",
  216. (MP4Property**)&m_pCttsCountProperty);
  217. if (haveCtts) {
  218. success &= m_pTrakAtom->FindProperty(
  219. "trak.mdia.minf.stbl.ctts.entries.sampleCount",
  220. (MP4Property**)&m_pCttsSampleCountProperty);
  221. success &= m_pTrakAtom->FindProperty(
  222. "trak.mdia.minf.stbl.ctts.entries.sampleOffset",
  223. (MP4Property**)&m_pCttsSampleOffsetProperty);
  224. }
  225. // get handles on sync sample info if it exists
  226. m_pStssCountProperty = NULL;
  227. m_pStssSampleProperty = NULL;
  228. bool haveStss = m_pTrakAtom->FindProperty(
  229. "trak.mdia.minf.stbl.stss.entryCount",
  230. (MP4Property**)&m_pStssCountProperty);
  231. if (haveStss) {
  232. success &= m_pTrakAtom->FindProperty(
  233. "trak.mdia.minf.stbl.stss.entries.sampleNumber",
  234. (MP4Property**)&m_pStssSampleProperty);
  235. }
  236. // edit list
  237. (void)InitEditListProperties();
  238. // was everything found?
  239. if (!success) {
  240. throw new MP4Error("invalid track", "MP4Track::MP4Track");
  241. }
  242. CalculateBytesPerSample();
  243. }
  244. MP4Track::~MP4Track()
  245. {
  246. MP4Free(m_pCachedReadSample);
  247. MP4Free(m_pChunkBuffer);
  248. }
  249. const char* MP4Track::GetType()
  250. {
  251. return m_pTypeProperty->GetValue();
  252. }
  253. void MP4Track::SetType(const char* type)
  254. {
  255. m_pTypeProperty->SetValue(MP4NormalizeTrackType(type,
  256. m_pFile->GetVerbosity()));
  257. }
  258. void MP4Track::ReadSample(
  259. MP4SampleId sampleId,
  260. u_int8_t** ppBytes,
  261. u_int32_t* pNumBytes,
  262. MP4Timestamp* pStartTime,
  263. MP4Duration* pDuration,
  264. MP4Duration* pRenderingOffset,
  265. bool* pIsSyncSample)
  266. {
  267. if (sampleId == MP4_INVALID_SAMPLE_ID) {
  268. throw new MP4Error("sample id can't be zero",
  269. "MP4Track::ReadSample");
  270. }
  271. // handle unusual case of wanting to read a sample
  272. // that is still sitting in the write chunk buffer
  273. if (m_pChunkBuffer && sampleId >= m_writeSampleId - m_chunkSamples) {
  274. WriteChunkBuffer();
  275. }
  276. FILE *pFile = 0;
  277. try {
  278. pFile = GetSampleFile(sampleId);
  279. }
  280. catch (MP4Error* e)
  281. {
  282. // PRINT_ERROR(e);
  283. delete e;
  284. pFile = 0;
  285. }
  286. if (pFile == (FILE*)-1) {
  287. throw new MP4Error("sample is located in an inaccessible file",
  288. "MP4Track::ReadSample");
  289. }
  290. u_int64_t fileOffset = GetSampleFileOffset(sampleId);
  291. u_int32_t sampleSize = GetSampleSize(sampleId);
  292. if (*ppBytes != NULL && *pNumBytes < sampleSize) {
  293. throw new MP4Error("sample buffer is too small",
  294. "MP4Track::ReadSample");
  295. }
  296. *pNumBytes = sampleSize;
  297. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  298. printf("ReadSample: track %u id %u offset 0x"X64" size %u (0x%x)\n",
  299. m_trackId, sampleId, fileOffset, *pNumBytes, *pNumBytes));
  300. bool bufferMalloc = false;
  301. if (*ppBytes == NULL) {
  302. *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
  303. bufferMalloc = true;
  304. }
  305. u_int64_t oldPos = m_pFile->GetPosition(pFile); // only used in mode == 'w'
  306. try {
  307. m_pFile->SetPosition(fileOffset, pFile);
  308. m_pFile->ReadBytes(*ppBytes, *pNumBytes, pFile);
  309. if (pStartTime || pDuration) {
  310. GetSampleTimes(sampleId, pStartTime, pDuration);
  311. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  312. printf("ReadSample: start "U64" duration "D64"\n",
  313. (pStartTime ? *pStartTime : 0),
  314. (pDuration ? *pDuration : 0)));
  315. }
  316. if (pRenderingOffset) {
  317. *pRenderingOffset = GetSampleRenderingOffset(sampleId);
  318. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  319. printf("ReadSample: renderingOffset "D64"\n",
  320. *pRenderingOffset));
  321. }
  322. if (pIsSyncSample) {
  323. *pIsSyncSample = IsSyncSample(sampleId);
  324. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  325. printf("ReadSample: isSyncSample %u\n",
  326. *pIsSyncSample));
  327. }
  328. }
  329. catch (MP4Error* e) {
  330. if (bufferMalloc) {
  331. // let's not leak memory
  332. MP4Free(*ppBytes);
  333. *ppBytes = NULL;
  334. }
  335. if (m_pFile->GetMode() == 'w') {
  336. m_pFile->SetPosition(oldPos, pFile);
  337. }
  338. throw e;
  339. }
  340. if (m_pFile->GetMode() == 'w') {
  341. m_pFile->SetPosition(oldPos, pFile);
  342. }
  343. }
  344. void MP4Track::ReadSampleFragment(
  345. MP4SampleId sampleId,
  346. u_int32_t sampleOffset,
  347. u_int16_t sampleLength,
  348. u_int8_t* pDest)
  349. {
  350. if (sampleId == MP4_INVALID_SAMPLE_ID) {
  351. throw new MP4Error("invalid sample id",
  352. "MP4Track::ReadSampleFragment");
  353. }
  354. if (sampleId != m_cachedReadSampleId) {
  355. MP4Free(m_pCachedReadSample);
  356. m_pCachedReadSample = NULL;
  357. m_cachedReadSampleSize = 0;
  358. m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
  359. ReadSample(
  360. sampleId,
  361. &m_pCachedReadSample,
  362. &m_cachedReadSampleSize);
  363. m_cachedReadSampleId = sampleId;
  364. }
  365. if (sampleOffset + sampleLength > m_cachedReadSampleSize) {
  366. throw new MP4Error("offset and/or length are too large",
  367. "MP4Track::ReadSampleFragment");
  368. }
  369. memcpy(pDest, &m_pCachedReadSample[sampleOffset], sampleLength);
  370. }
  371. void MP4Track::WriteSample(
  372. const u_int8_t* pBytes,
  373. u_int32_t numBytes,
  374. MP4Duration duration,
  375. MP4Duration renderingOffset,
  376. bool isSyncSample)
  377. {
  378. u_int8_t curMode = 0;
  379. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  380. printf("WriteSample: track %u id %u size %u (0x%x) ",
  381. m_trackId, m_writeSampleId, numBytes, numBytes));
  382. if (pBytes == NULL && numBytes > 0) {
  383. throw new MP4Error("no sample data", "MP4WriteSample");
  384. }
  385. if (m_isAmr == AMR_UNINITIALIZED ) {
  386. // figure out if this is an AMR audio track
  387. if (m_pTrakAtom->FindAtomMP4("trak.mdia.minf.stbl.stsd.samr") ||
  388. m_pTrakAtom->FindAtomMP4("trak.mdia.minf.stbl.stsd.sawb")) {
  389. m_isAmr = AMR_TRUE;
  390. m_curMode = (pBytes[0] >> 3) & 0x000F;
  391. } else {
  392. m_isAmr = AMR_FALSE;
  393. }
  394. }
  395. if (m_isAmr == AMR_TRUE) {
  396. curMode = (pBytes[0] >> 3) &0x000F; // The mode is in the first byte
  397. }
  398. if (duration == MP4_INVALID_DURATION) {
  399. duration = GetFixedSampleDuration();
  400. }
  401. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  402. printf("duration "U64"\n", duration));
  403. if ((m_isAmr == AMR_TRUE) &&
  404. (m_curMode != curMode)) {
  405. WriteChunkBuffer();
  406. m_curMode = curMode;
  407. }
  408. // append sample bytes to chunk buffer
  409. m_pChunkBuffer = (u_int8_t*)MP4Realloc(m_pChunkBuffer,
  410. m_chunkBufferSize + numBytes);
  411. if (m_pChunkBuffer == NULL) return;
  412. memcpy(&m_pChunkBuffer[m_chunkBufferSize], pBytes, numBytes);
  413. m_chunkBufferSize += numBytes;
  414. m_chunkSamples++;
  415. m_chunkDuration += duration;
  416. UpdateSampleSizes(m_writeSampleId, numBytes);
  417. UpdateSampleTimes(duration);
  418. UpdateRenderingOffsets(m_writeSampleId, renderingOffset);
  419. UpdateSyncSamples(m_writeSampleId, isSyncSample);
  420. if (IsChunkFull(m_writeSampleId)) {
  421. WriteChunkBuffer();
  422. m_curMode = curMode;
  423. }
  424. UpdateDurations(duration);
  425. UpdateModificationTimes();
  426. m_writeSampleId++;
  427. }
  428. void MP4Track::WriteChunkBuffer()
  429. {
  430. if (m_chunkBufferSize == 0) {
  431. return;
  432. }
  433. u_int64_t chunkOffset = m_pFile->GetPosition();
  434. // write chunk buffer
  435. m_pFile->WriteBytes(m_pChunkBuffer, m_chunkBufferSize);
  436. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  437. printf("WriteChunk: track %u offset 0x"X64" size %u (0x%x) numSamples %u\n",
  438. m_trackId, chunkOffset, m_chunkBufferSize,
  439. m_chunkBufferSize, m_chunkSamples));
  440. UpdateSampleToChunk(m_writeSampleId,
  441. m_pChunkCountProperty->GetValue() + 1,
  442. m_chunkSamples);
  443. UpdateChunkOffsets(chunkOffset);
  444. // clean up chunk buffer
  445. MP4Free(m_pChunkBuffer);
  446. m_pChunkBuffer = NULL;
  447. m_chunkBufferSize = 0;
  448. m_chunkSamples = 0;
  449. m_chunkDuration = 0;
  450. }
  451. void MP4Track::FinishWrite()
  452. {
  453. // write out any remaining samples in chunk buffer
  454. WriteChunkBuffer();
  455. if (m_pStszFixedSampleSizeProperty == NULL &&
  456. m_stsz_sample_bits == 4) {
  457. if (m_have_stz2_4bit_sample) {
  458. ((MP4Integer8Property *)m_pStszSampleSizeProperty)->AddValue(m_stz2_4bit_sample_value);
  459. m_pStszSampleSizeProperty->IncrementValue();
  460. }
  461. }
  462. // record buffer size and bitrates
  463. MP4BitfieldProperty* pBufferSizeProperty;
  464. if (m_pTrakAtom->FindProperty(
  465. "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.bufferSizeDB",
  466. (MP4Property**)&pBufferSizeProperty)) {
  467. pBufferSizeProperty->SetValue(GetMaxSampleSize());
  468. }
  469. MP4Integer32Property* pBitrateProperty;
  470. if (m_pTrakAtom->FindProperty(
  471. "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.maxBitrate",
  472. (MP4Property**)&pBitrateProperty)) {
  473. pBitrateProperty->SetValue(GetMaxBitrate());
  474. }
  475. if (m_pTrakAtom->FindProperty(
  476. "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate",
  477. (MP4Property**)&pBitrateProperty)) {
  478. pBitrateProperty->SetValue(GetAvgBitrate());
  479. }
  480. }
  481. bool MP4Track::IsChunkFull(MP4SampleId sampleId)
  482. {
  483. if (m_samplesPerChunk) {
  484. return m_chunkSamples >= m_samplesPerChunk;
  485. }
  486. ASSERT(m_durationPerChunk);
  487. return m_chunkDuration >= m_durationPerChunk;
  488. }
  489. u_int32_t MP4Track::GetNumberOfSamples()
  490. {
  491. return m_pStszSampleCountProperty->GetValue();
  492. }
  493. u_int32_t MP4Track::GetSampleSize(MP4SampleId sampleId)
  494. {
  495. if (m_pStszFixedSampleSizeProperty != NULL)
  496. {
  497. u_int32_t fixedSampleSize =
  498. m_pStszFixedSampleSizeProperty->GetValue();
  499. if (fixedSampleSize != 0)
  500. {
  501. return SafeMultiply(m_bytesPerSample, fixedSampleSize);
  502. }
  503. }
  504. // will have to check for 4 bit sample size here
  505. if (m_stsz_sample_bits == 4) {
  506. uint8_t value = m_pStszSampleSizeProperty->GetValue((sampleId - 1) / 2);
  507. if ((sampleId - 1) / 2 == 0) {
  508. value >>= 4;
  509. } else value &= 0xf;
  510. return SafeMultiply(m_bytesPerSample, value);
  511. }
  512. return SafeMultiply(m_bytesPerSample, m_pStszSampleSizeProperty->GetValue(sampleId - 1));
  513. }
  514. u_int32_t MP4Track::GetMaxSampleSize()
  515. {
  516. if (m_pStszFixedSampleSizeProperty != NULL)
  517. {
  518. u_int32_t fixedSampleSize =
  519. m_pStszFixedSampleSizeProperty->GetValue();
  520. if (fixedSampleSize != 0)
  521. {
  522. return SafeMultiply(m_bytesPerSample, fixedSampleSize);
  523. }
  524. }
  525. u_int32_t maxSampleSize = 0;
  526. u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
  527. for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
  528. u_int32_t sampleSize =
  529. m_pStszSampleSizeProperty->GetValue(sid - 1);
  530. if (sampleSize > maxSampleSize) {
  531. maxSampleSize = sampleSize;
  532. }
  533. }
  534. return SafeMultiply(m_bytesPerSample, maxSampleSize);
  535. }
  536. u_int64_t MP4Track::GetTotalOfSampleSizes()
  537. {
  538. uint64_t retval;
  539. if (m_pStszFixedSampleSizeProperty != NULL) {
  540. u_int32_t fixedSampleSize =
  541. m_pStszFixedSampleSizeProperty->GetValue();
  542. // if fixed sample size, just need to multiply by number of samples
  543. if (fixedSampleSize != 0) {
  544. retval = m_bytesPerSample;
  545. retval *= fixedSampleSize;
  546. retval *= GetNumberOfSamples();
  547. return retval;
  548. }
  549. }
  550. // else non-fixed sample size, sum them
  551. u_int64_t totalSampleSizes = 0;
  552. u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
  553. for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
  554. u_int32_t sampleSize =
  555. m_pStszSampleSizeProperty->GetValue(sid - 1);
  556. totalSampleSizes += sampleSize;
  557. }
  558. return totalSampleSizes * m_bytesPerSample;
  559. }
  560. void MP4Track::SampleSizePropertyAddValue (uint32_t size)
  561. {
  562. // this has to deal with different sample size values
  563. switch (m_pStszSampleSizeProperty->GetType()) {
  564. case Integer32Property:
  565. ((MP4Integer32Property *)m_pStszSampleSizeProperty)->AddValue(size);
  566. break;
  567. case Integer16Property:
  568. ((MP4Integer16Property *)m_pStszSampleSizeProperty)->AddValue(size);
  569. break;
  570. case Integer8Property:
  571. if (m_stsz_sample_bits == 4) {
  572. if (m_have_stz2_4bit_sample == false) {
  573. m_have_stz2_4bit_sample = true;
  574. m_stz2_4bit_sample_value = size << 4;
  575. return;
  576. } else {
  577. m_have_stz2_4bit_sample = false;
  578. size &= 0xf;
  579. size |= m_stz2_4bit_sample_value;
  580. }
  581. }
  582. ((MP4Integer8Property *)m_pStszSampleSizeProperty)->AddValue(size);
  583. break;
  584. default: break;
  585. }
  586. // m_pStszSampleSizeProperty->IncrementValue();
  587. }
  588. void MP4Track::UpdateSampleSizes(MP4SampleId sampleId, u_int32_t numBytes)
  589. {
  590. if (m_bytesPerSample > 1) {
  591. if ((numBytes % m_bytesPerSample) != 0) {
  592. // error
  593. VERBOSE_ERROR(m_pFile->GetVerbosity(),
  594. printf("UpdateSampleSize: numBytes %u not divisible by bytesPerSample %u sampleId %u\n",
  595. numBytes, m_bytesPerSample, sampleId);
  596. );
  597. }
  598. numBytes /= m_bytesPerSample;
  599. }
  600. // for first sample
  601. if (sampleId == 1) {
  602. if (m_pStszFixedSampleSizeProperty == NULL ||
  603. numBytes == 0) {
  604. // special case of first sample is zero bytes in length
  605. // leave m_pStszFixedSampleSizeProperty at 0
  606. // start recording variable sample sizes
  607. if (m_pStszFixedSampleSizeProperty != NULL)
  608. m_pStszFixedSampleSizeProperty->SetValue(0);
  609. SampleSizePropertyAddValue(0);
  610. } else {
  611. // presume sample size is fixed
  612. m_pStszFixedSampleSizeProperty->SetValue(numBytes);
  613. }
  614. } else { // sampleId > 1
  615. u_int32_t fixedSampleSize = 0;
  616. if (m_pStszFixedSampleSizeProperty != NULL) {
  617. fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue();
  618. }
  619. // if we don't have a fixed size, or the current sample size
  620. // doesn't match our sample size, we need to write the current
  621. // sample size into the table
  622. if (fixedSampleSize == 0 || numBytes != fixedSampleSize) {
  623. if (fixedSampleSize != 0) {
  624. // fixed size was set; we need to clear fixed sample size
  625. if (m_pStszFixedSampleSizeProperty != NULL) {
  626. m_pStszFixedSampleSizeProperty->SetValue(0);
  627. }
  628. // and create sizes for all previous samples
  629. for (MP4SampleId sid = 1; sid < sampleId; sid++) {
  630. SampleSizePropertyAddValue(fixedSampleSize);
  631. }
  632. }
  633. // add size value for this sample
  634. SampleSizePropertyAddValue(numBytes);
  635. }
  636. }
  637. // either way, we increment the number of samples.
  638. m_pStszSampleCountProperty->IncrementValue();
  639. #if 0
  640. printf("track %u sample id %u bytes %u fixed %u count %u prop %u\n",
  641. m_trackId, sampleId, numBytes,
  642. m_pStszFixedSampleSizeProperty->GetValue(),
  643. m_pStszSampleSizeProperty->GetCount(),
  644. m_pStszSampleCountProperty->GetValue());
  645. #endif
  646. }
  647. u_int32_t MP4Track::GetAvgBitrate()
  648. {
  649. if (GetDuration() == 0) {
  650. return 0;
  651. }
  652. double calc = UINT64_TO_DOUBLE(GetTotalOfSampleSizes());
  653. // this is a bit better - we use the whole duration
  654. calc *= 8.0;
  655. calc *= GetTimeScale();
  656. calc /= UINT64_TO_DOUBLE(GetDuration());
  657. // we might want to think about rounding to the next 100 or 1000
  658. return (uint32_t) ceil(calc);
  659. }
  660. u_int32_t MP4Track::GetMaxBitrate()
  661. {
  662. u_int32_t timeScale = GetTimeScale();
  663. MP4SampleId numSamples = GetNumberOfSamples();
  664. u_int32_t maxBytesPerSec = 0;
  665. u_int32_t bytesThisSec = 0;
  666. MP4Timestamp thisSecStart = 0;
  667. MP4Timestamp lastSampleTime = 0;
  668. uint32_t lastSampleSize = 0;
  669. MP4SampleId thisSecStartSid = 1;
  670. for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
  671. uint32_t sampleSize;
  672. MP4Timestamp sampleTime;
  673. sampleSize = GetSampleSize(sid);
  674. GetSampleTimes(sid, &sampleTime, NULL);
  675. if (sampleTime < thisSecStart + timeScale) {
  676. bytesThisSec += sampleSize;
  677. lastSampleSize = sampleSize;
  678. lastSampleTime = sampleTime;
  679. } else {
  680. // we've already written the last sample and sampleSize.
  681. // this means that we've probably overflowed the last second
  682. // calculate the time we've overflowed
  683. MP4Duration overflow_dur =
  684. (thisSecStart + timeScale) - lastSampleTime;
  685. // calculate the duration of the last sample
  686. MP4Duration lastSampleDur = sampleTime - lastSampleTime;
  687. uint32_t overflow_bytes;
  688. // now, calculate the number of bytes we overflowed. Round up.
  689. overflow_bytes =
  690. ((lastSampleSize * overflow_dur) + (lastSampleDur - 1)) / lastSampleDur;
  691. if (bytesThisSec - overflow_bytes > maxBytesPerSec) {
  692. maxBytesPerSec = bytesThisSec - overflow_bytes;
  693. }
  694. // now adjust the values for this sample. Remove the bytes
  695. // from the first sample in this time frame
  696. lastSampleTime = sampleTime;
  697. lastSampleSize = sampleSize;
  698. bytesThisSec += sampleSize;
  699. bytesThisSec -= GetSampleSize(thisSecStartSid);
  700. thisSecStartSid++;
  701. GetSampleTimes(thisSecStartSid, &thisSecStart, NULL);
  702. }
  703. }
  704. return maxBytesPerSec * 8;
  705. }
  706. u_int32_t MP4Track::GetSampleStscIndex(MP4SampleId sampleId)
  707. {
  708. u_int32_t stscIndex;
  709. u_int32_t numStscs = m_pStscCountProperty->GetValue();
  710. if (numStscs == 0) {
  711. throw new MP4Error("No data chunks exist", "GetSampleStscIndex");
  712. }
  713. for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
  714. if (sampleId < m_pStscFirstSampleProperty->GetValue(stscIndex)) {
  715. ASSERT(stscIndex != 0);
  716. stscIndex -= 1;
  717. break;
  718. }
  719. }
  720. if (stscIndex == numStscs) {
  721. ASSERT(stscIndex != 0);
  722. stscIndex -= 1;
  723. }
  724. return stscIndex;
  725. }
  726. FILE* MP4Track::GetSampleFile(MP4SampleId sampleId)
  727. {
  728. u_int32_t stscIndex =
  729. GetSampleStscIndex(sampleId);
  730. u_int32_t stsdIndex =
  731. m_pStscSampleDescrIndexProperty->GetValue(stscIndex);
  732. // check if the answer will be the same as last time
  733. if (m_lastStsdIndex && stsdIndex == m_lastStsdIndex) {
  734. return m_lastSampleFile;
  735. }
  736. MP4Atom* pStsdAtom =
  737. m_pTrakAtom->FindAtomMP4("trak.mdia.minf.stbl.stsd");
  738. ASSERT(pStsdAtom);
  739. MP4Atom* pStsdEntryAtom =
  740. pStsdAtom->GetChildAtom(stsdIndex - 1);
  741. ASSERT(pStsdEntryAtom);
  742. MP4Integer16Property* pDrefIndexProperty = NULL;
  743. if (!pStsdEntryAtom->FindProperty(
  744. "*.dataReferenceIndex",
  745. (MP4Property**)&pDrefIndexProperty) ||
  746. pDrefIndexProperty == NULL) {
  747. return 0;
  748. }
  749. u_int32_t drefIndex =
  750. pDrefIndexProperty->GetValue();
  751. MP4Atom* pDrefAtom =
  752. m_pTrakAtom->FindAtomMP4("trak.mdia.minf.dinf.dref");
  753. ASSERT(pDrefAtom);
  754. MP4Atom* pUrlAtom =
  755. pDrefAtom->GetChildAtom(drefIndex - 1);
  756. ASSERT(pUrlAtom);
  757. FILE* pFile;
  758. if (pUrlAtom->GetFlags() & 1) {
  759. pFile = NULL; // self-contained
  760. } else {
  761. MP4StringProperty* pLocationProperty = NULL;
  762. ASSERT(pUrlAtom->FindProperty(
  763. "*.location",
  764. (MP4Property**)&pLocationProperty));
  765. ASSERT(pLocationProperty);
  766. const char* url = pLocationProperty->GetValue();
  767. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  768. printf("dref url = %s\n", url));
  769. pFile = (FILE*)-1;
  770. // attempt to open url if it's a file url
  771. // currently this is the only thing we understand
  772. if (!strncmp(url, "file:", 5)) {
  773. const char* fileName = url + 5;
  774. if (!strncmp(fileName, "//", 2)) {
  775. fileName = strchr(fileName + 2, '/');
  776. }
  777. if (fileName) {
  778. pFile = fopen(fileName, "rb");
  779. if (!pFile) {
  780. pFile = (FILE*)-1;
  781. }
  782. }
  783. }
  784. }
  785. if (m_lastSampleFile) {
  786. fclose(m_lastSampleFile);
  787. }
  788. // cache the answer
  789. m_lastStsdIndex = stsdIndex;
  790. m_lastSampleFile = pFile;
  791. return pFile;
  792. }
  793. u_int64_t MP4Track::GetSampleFileOffset(MP4SampleId sampleId)
  794. {
  795. u_int32_t stscIndex = GetSampleStscIndex(sampleId);
  796. // firstChunk is the chunk index of the first chunk with
  797. // samplesPerChunk samples in the chunk. There may be multiples -
  798. // ie: several chunks with the same number of samples per chunk.
  799. u_int64_t firstChunk = m_pStscFirstChunkProperty->GetValue(stscIndex);
  800. MP4SampleId firstSample = m_pStscFirstSampleProperty->GetValue(stscIndex);
  801. u_int64_t samplesPerChunk = m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
  802. // chunkId tells which is the absolute chunk number that this sample
  803. // is stored in.
  804. MP4ChunkId chunkId = firstChunk + ((static_cast<unsigned long long>(sampleId) - firstSample) / samplesPerChunk);
  805. // chunkOffset is the file offset (absolute) for the start of the chunk
  806. u_int64_t chunkOffset = m_pChunkOffsetProperty->GetValue(chunkId - 1);
  807. MP4SampleId firstSampleInChunk = sampleId - ((static_cast<unsigned long long>(sampleId) - firstSample) % samplesPerChunk);
  808. // need cumulative samples sizes from firstSample to sampleId - 1
  809. u_int64_t sampleOffset = 0;
  810. for (MP4SampleId i = firstSampleInChunk; i < sampleId; i++) {
  811. sampleOffset += GetSampleSize(i);
  812. }
  813. return chunkOffset + sampleOffset;
  814. }
  815. void MP4Track::UpdateSampleToChunk(MP4SampleId sampleId,
  816. MP4ChunkId chunkId, u_int32_t samplesPerChunk)
  817. {
  818. u_int32_t numStsc = m_pStscCountProperty->GetValue();
  819. // if samplesPerChunk == samplesPerChunk of last entry
  820. if (numStsc && samplesPerChunk ==
  821. m_pStscSamplesPerChunkProperty->GetValue(numStsc-1)) {
  822. // nothing to do
  823. } else {
  824. // add stsc entry
  825. m_pStscFirstChunkProperty->AddValue(chunkId);
  826. m_pStscSamplesPerChunkProperty->AddValue(samplesPerChunk);
  827. m_pStscSampleDescrIndexProperty->AddValue(1);
  828. m_pStscFirstSampleProperty->AddValue(sampleId - samplesPerChunk + 1);
  829. m_pStscCountProperty->IncrementValue();
  830. }
  831. }
  832. void MP4Track::UpdateChunkOffsets(u_int64_t chunkOffset)
  833. {
  834. if (m_pChunkOffsetProperty->GetType() == Integer32Property) {
  835. ((MP4Integer32Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
  836. } else {
  837. ((MP4Integer64Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
  838. }
  839. m_pChunkCountProperty->IncrementValue();
  840. }
  841. MP4Duration MP4Track::GetFixedSampleDuration()
  842. {
  843. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  844. if (numStts == 0) {
  845. return m_fixedSampleDuration;
  846. }
  847. if (numStts != 1) {
  848. return MP4_INVALID_DURATION; // sample duration is not fixed
  849. }
  850. return m_pSttsSampleDeltaProperty->GetValue(0);
  851. }
  852. void MP4Track::SetFixedSampleDuration(MP4Duration duration)
  853. {
  854. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  855. // setting this is only allowed before samples have been written
  856. if (numStts != 0) {
  857. return;
  858. }
  859. m_fixedSampleDuration = duration;
  860. return;
  861. }
  862. void MP4Track::GetSampleTimes(MP4SampleId sampleId,
  863. MP4Timestamp* pStartTime, MP4Duration* pDuration)
  864. {
  865. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  866. MP4SampleId sid;
  867. MP4Duration elapsed;
  868. if (m_cachedSttsSid != MP4_INVALID_SAMPLE_ID && sampleId >= m_cachedSttsSid) {
  869. sid = m_cachedSttsSid;
  870. elapsed = m_cachedSttsElapsed;
  871. } else {
  872. m_cachedSttsIndex = 0;
  873. sid = 1;
  874. elapsed = 0;
  875. }
  876. for (u_int32_t sttsIndex = m_cachedSttsIndex; sttsIndex < numStts; sttsIndex++) {
  877. MP4SampleId sampleCount =
  878. m_pSttsSampleCountProperty->GetValue(sttsIndex);
  879. MP4Duration sampleDelta =
  880. m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
  881. if (sampleId <= sid + sampleCount - 1) {
  882. if (pStartTime) {
  883. *pStartTime = (static_cast<MP4Timestamp>(sampleId) - sid);
  884. *pStartTime *= sampleDelta;
  885. *pStartTime += elapsed;
  886. }
  887. if (pDuration) {
  888. *pDuration = sampleDelta;
  889. }
  890. m_cachedSttsIndex = sttsIndex;
  891. m_cachedSttsSid = sid;
  892. m_cachedSttsElapsed = elapsed;
  893. return;
  894. }
  895. sid += sampleCount;
  896. elapsed += sampleCount * sampleDelta;
  897. }
  898. throw new MP4Error("sample id out of range",
  899. "MP4Track::GetSampleTimes");
  900. }
  901. MP4SampleId MP4Track::GetSampleIdFromTime(
  902. MP4Timestamp when,
  903. bool wantSyncSample,
  904. bool rewind)
  905. {
  906. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  907. MP4SampleId sid = 1;
  908. MP4Duration elapsed = 0;
  909. for (u_int32_t sttsIndex = 0; sttsIndex < numStts; sttsIndex++) {
  910. MP4SampleId sampleCount =
  911. m_pSttsSampleCountProperty->GetValue(sttsIndex);
  912. MP4Duration sampleDelta =
  913. m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
  914. if (sampleDelta == 0 && sttsIndex < numStts - 1) {
  915. VERBOSE_READ(m_pFile->GetVerbosity(),
  916. printf("Warning: Zero sample duration, stts entry %u\n",
  917. sttsIndex));
  918. }
  919. MP4Duration d = when - elapsed;
  920. if (d <= sampleCount * sampleDelta) {
  921. MP4SampleId sampleId = sid;
  922. if (sampleDelta) {
  923. sampleId += (d / sampleDelta);
  924. }
  925. if (wantSyncSample) {
  926. return GetSyncSample(sampleId, rewind);
  927. }
  928. return sampleId;
  929. }
  930. sid += sampleCount;
  931. elapsed += sampleCount * sampleDelta;
  932. }
  933. throw new MP4Error("time out of range",
  934. "MP4Track::GetSampleIdFromTime");
  935. return 0; // satisfy MS compiler
  936. }
  937. MP4ChunkId MP4Track::GetChunkIdFromTime(
  938. MP4Timestamp when)
  939. {
  940. MP4ChunkId numChunks = GetNumberOfChunks();
  941. for (MP4ChunkId chunk = 1; chunk <= numChunks; chunk++)
  942. {
  943. MP4Timestamp d = GetChunkTime(chunk);
  944. if (d == when)
  945. return chunk;
  946. else if (d > when)
  947. return chunk==1?1:(chunk-1);
  948. }
  949. return numChunks;
  950. }
  951. void MP4Track::UpdateSampleTimes(MP4Duration duration)
  952. {
  953. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  954. // if duration == duration of last entry
  955. if (numStts
  956. && duration == m_pSttsSampleDeltaProperty->GetValue(numStts-1)) {
  957. // increment last entry sampleCount
  958. m_pSttsSampleCountProperty->IncrementValue(1, numStts-1);
  959. } else {
  960. // add stts entry, sampleCount = 1, sampleDuration = duration
  961. m_pSttsSampleCountProperty->AddValue(1);
  962. m_pSttsSampleDeltaProperty->AddValue(duration);
  963. m_pSttsCountProperty->IncrementValue();;
  964. }
  965. }
  966. u_int32_t MP4Track::GetSampleCttsIndex(MP4SampleId sampleId,
  967. MP4SampleId* pFirstSampleId)
  968. {
  969. u_int32_t numCtts = m_pCttsCountProperty->GetValue();
  970. MP4SampleId sid = 1;
  971. for (u_int32_t cttsIndex = 0; cttsIndex < numCtts; cttsIndex++) {
  972. u_int32_t sampleCount =
  973. m_pCttsSampleCountProperty->GetValue(cttsIndex);
  974. if (sampleId <= sid + sampleCount - 1) {
  975. if (pFirstSampleId) {
  976. *pFirstSampleId = sid;
  977. }
  978. return cttsIndex;
  979. }
  980. sid += sampleCount;
  981. }
  982. throw new MP4Error("sample id out of range",
  983. "MP4Track::GetSampleCttsIndex");
  984. return 0; // satisfy MS compiler
  985. }
  986. MP4Duration MP4Track::GetSampleRenderingOffset(MP4SampleId sampleId)
  987. {
  988. if (m_pCttsCountProperty == NULL) {
  989. return 0;
  990. }
  991. if (m_pCttsCountProperty->GetValue() == 0) {
  992. return 0;
  993. }
  994. u_int32_t cttsIndex = GetSampleCttsIndex(sampleId);
  995. return m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
  996. }
  997. void MP4Track::UpdateRenderingOffsets(MP4SampleId sampleId,
  998. MP4Duration renderingOffset)
  999. {
  1000. // if ctts atom doesn't exist
  1001. if (m_pCttsCountProperty == NULL) {
  1002. // no rendering offset, so nothing to do
  1003. if (renderingOffset == 0) {
  1004. return;
  1005. }
  1006. // else create a ctts atom
  1007. MP4Atom* pCttsAtom = AddAtom("trak.mdia.minf.stbl", "ctts");
  1008. // and get handles on the properties
  1009. ASSERT(pCttsAtom->FindProperty(
  1010. "ctts.entryCount",
  1011. (MP4Property**)&m_pCttsCountProperty));
  1012. ASSERT(pCttsAtom->FindProperty(
  1013. "ctts.entries.sampleCount",
  1014. (MP4Property**)&m_pCttsSampleCountProperty));
  1015. ASSERT(pCttsAtom->FindProperty(
  1016. "ctts.entries.sampleOffset",
  1017. (MP4Property**)&m_pCttsSampleOffsetProperty));
  1018. // if this is not the first sample
  1019. if (sampleId > 1) {
  1020. // add a ctts entry for all previous samples
  1021. // with rendering offset equal to zero
  1022. m_pCttsSampleCountProperty->AddValue(sampleId - 1);
  1023. m_pCttsSampleOffsetProperty->AddValue(0);
  1024. m_pCttsCountProperty->IncrementValue();;
  1025. }
  1026. }
  1027. // ctts atom exists (now)
  1028. u_int32_t numCtts = m_pCttsCountProperty->GetValue();
  1029. // if renderingOffset == renderingOffset of last entry
  1030. if (numCtts && renderingOffset
  1031. == m_pCttsSampleOffsetProperty->GetValue(numCtts-1)) {
  1032. // increment last entry sampleCount
  1033. m_pCttsSampleCountProperty->IncrementValue(1, numCtts-1);
  1034. } else {
  1035. // add ctts entry, sampleCount = 1, sampleOffset = renderingOffset
  1036. m_pCttsSampleCountProperty->AddValue(1);
  1037. m_pCttsSampleOffsetProperty->AddValue(renderingOffset);
  1038. m_pCttsCountProperty->IncrementValue();
  1039. }
  1040. }
  1041. void MP4Track::SetSampleRenderingOffset(MP4SampleId sampleId,
  1042. MP4Duration renderingOffset)
  1043. {
  1044. // check if any ctts entries exist
  1045. if (m_pCttsCountProperty == NULL
  1046. || m_pCttsCountProperty->GetValue() == 0) {
  1047. // if not then Update routine can be used
  1048. // to create a ctts entry for samples before this one
  1049. // and a ctts entry for this sample
  1050. UpdateRenderingOffsets(sampleId, renderingOffset);
  1051. // but we also need a ctts entry
  1052. // for all samples after this one
  1053. u_int32_t afterSamples = GetNumberOfSamples() - sampleId;
  1054. if (afterSamples) {
  1055. m_pCttsSampleCountProperty->AddValue(afterSamples);
  1056. m_pCttsSampleOffsetProperty->AddValue(0);
  1057. m_pCttsCountProperty->IncrementValue();;
  1058. }
  1059. return;
  1060. }
  1061. MP4SampleId firstSampleId;
  1062. u_int32_t cttsIndex = GetSampleCttsIndex(sampleId, &firstSampleId);
  1063. // do nothing in the degenerate case
  1064. if (renderingOffset ==
  1065. m_pCttsSampleOffsetProperty->GetValue(cttsIndex)) {
  1066. return;
  1067. }
  1068. u_int32_t sampleCount =
  1069. m_pCttsSampleCountProperty->GetValue(cttsIndex);
  1070. // if this sample has it's own ctts entry
  1071. if (sampleCount == 1) {
  1072. // then just set the value,
  1073. // note we don't attempt to collapse entries
  1074. m_pCttsSampleOffsetProperty->SetValue(renderingOffset, cttsIndex);
  1075. return;
  1076. }
  1077. MP4SampleId lastSampleId = firstSampleId + sampleCount - 1;
  1078. // else we share this entry with other samples
  1079. // we need to insert our own entry
  1080. if (sampleId == firstSampleId) {
  1081. // our sample is the first one
  1082. m_pCttsSampleCountProperty->
  1083. InsertValue(1, cttsIndex);
  1084. m_pCttsSampleOffsetProperty->
  1085. InsertValue(renderingOffset, cttsIndex);
  1086. m_pCttsSampleCountProperty->
  1087. SetValue(sampleCount - 1, cttsIndex + 1);
  1088. m_pCttsCountProperty->IncrementValue();
  1089. } else if (sampleId == lastSampleId) {
  1090. // our sample is the last one
  1091. m_pCttsSampleCountProperty->
  1092. InsertValue(1, cttsIndex + 1);
  1093. m_pCttsSampleOffsetProperty->
  1094. InsertValue(renderingOffset, cttsIndex + 1);
  1095. m_pCttsSampleCountProperty->
  1096. SetValue(sampleCount - 1, cttsIndex);
  1097. m_pCttsCountProperty->IncrementValue();
  1098. } else {
  1099. // our sample is in the middle, UGH!
  1100. // insert our new entry
  1101. m_pCttsSampleCountProperty->
  1102. InsertValue(1, cttsIndex + 1);
  1103. m_pCttsSampleOffsetProperty->
  1104. InsertValue(renderingOffset, cttsIndex + 1);
  1105. // adjust count of previous entry
  1106. m_pCttsSampleCountProperty->
  1107. SetValue(sampleId - firstSampleId, cttsIndex);
  1108. // insert new entry for those samples beyond our sample
  1109. m_pCttsSampleCountProperty->
  1110. InsertValue(lastSampleId - sampleId, cttsIndex + 2);
  1111. u_int32_t oldRenderingOffset =
  1112. m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
  1113. m_pCttsSampleOffsetProperty->
  1114. InsertValue(oldRenderingOffset, cttsIndex + 2);
  1115. m_pCttsCountProperty->IncrementValue(2);
  1116. }
  1117. }
  1118. bool MP4Track::IsSyncSample(MP4SampleId sampleId)
  1119. {
  1120. if (m_pStssCountProperty == NULL) {
  1121. return true;
  1122. }
  1123. u_int32_t numStss = m_pStssCountProperty->GetValue();
  1124. u_int32_t stssLIndex = 0;
  1125. u_int32_t stssRIndex = numStss - 1;
  1126. while (stssRIndex >= stssLIndex){
  1127. u_int32_t stssIndex = (stssRIndex + stssLIndex) >> 1;
  1128. MP4SampleId syncSampleId =
  1129. m_pStssSampleProperty->GetValue(stssIndex);
  1130. if (sampleId == syncSampleId) {
  1131. return true;
  1132. }
  1133. if (sampleId > syncSampleId) {
  1134. stssLIndex = stssIndex + 1;
  1135. } else {
  1136. stssRIndex = stssIndex - 1;
  1137. }
  1138. }
  1139. return false;
  1140. }
  1141. // N.B. "next" is inclusive of this sample id
  1142. MP4SampleId MP4Track::GetNextSyncSample(MP4SampleId sampleId)
  1143. {
  1144. if (m_pStssCountProperty == NULL) {
  1145. return sampleId;
  1146. }
  1147. u_int32_t numStss = m_pStssCountProperty->GetValue();
  1148. for (uint32_t stssIndex = 0; stssIndex < numStss; stssIndex++)
  1149. {
  1150. MP4SampleId syncSampleId = m_pStssSampleProperty->GetValue(stssIndex);
  1151. if (sampleId > syncSampleId) {
  1152. continue;
  1153. }
  1154. return syncSampleId;
  1155. }
  1156. // LATER check stsh for alternate sample
  1157. return MP4_INVALID_SAMPLE_ID;
  1158. }
  1159. MP4SampleId MP4Track::GetSyncSample(MP4SampleId sampleId, bool rewind)
  1160. {
  1161. if (m_pStssCountProperty == NULL) {
  1162. return sampleId;
  1163. }
  1164. u_int32_t numStss = m_pStssCountProperty->GetValue();
  1165. MP4SampleId prevSampleId = 1;
  1166. for (uint32_t stssIndex = 0; stssIndex < numStss; stssIndex++)
  1167. {
  1168. MP4SampleId syncSampleId = m_pStssSampleProperty->GetValue(stssIndex);
  1169. if (sampleId > syncSampleId) {
  1170. prevSampleId = syncSampleId;
  1171. continue;
  1172. }
  1173. return rewind ? prevSampleId : syncSampleId;
  1174. }
  1175. // LATER check stsh for alternate sample
  1176. return MP4_INVALID_SAMPLE_ID;
  1177. }
  1178. void MP4Track::UpdateSyncSamples(MP4SampleId sampleId, bool isSyncSample)
  1179. {
  1180. if (isSyncSample) {
  1181. // if stss atom exists, add entry
  1182. if (m_pStssCountProperty) {
  1183. m_pStssSampleProperty->AddValue(sampleId);
  1184. m_pStssCountProperty->IncrementValue();
  1185. } // else nothing to do (yet)
  1186. } else { // !isSyncSample
  1187. // if stss atom doesn't exist, create one
  1188. if (m_pStssCountProperty == NULL) {
  1189. MP4Atom* pStssAtom = AddAtom("trak.mdia.minf.stbl", "stss");
  1190. ASSERT(pStssAtom->FindProperty(
  1191. "stss.entryCount",
  1192. (MP4Property**)&m_pStssCountProperty));
  1193. ASSERT(pStssAtom->FindProperty(
  1194. "stss.entries.sampleNumber",
  1195. (MP4Property**)&m_pStssSampleProperty));
  1196. // set values for all samples that came before this one
  1197. for (MP4SampleId sid = 1; sid < sampleId; sid++) {
  1198. m_pStssSampleProperty->AddValue(sid);
  1199. m_pStssCountProperty->IncrementValue();
  1200. }
  1201. } // else nothing to do
  1202. }
  1203. }
  1204. MP4Atom* MP4Track::AddAtom(char* parentName, char* childName)
  1205. {
  1206. MP4Atom* pChildAtom = MP4Atom::CreateAtom(childName);
  1207. MP4Atom* pParentAtom = m_pTrakAtom->FindAtomMP4(parentName);
  1208. ASSERT(pParentAtom);
  1209. pParentAtom->AddChildAtom(pChildAtom);
  1210. pChildAtom->Generate();
  1211. return pChildAtom;
  1212. }
  1213. u_int64_t MP4Track::GetDuration()
  1214. {
  1215. return m_pMediaDurationProperty->GetValue();
  1216. }
  1217. u_int32_t MP4Track::GetTimeScale()
  1218. {
  1219. return m_pTimeScaleProperty->GetValue();
  1220. }
  1221. void MP4Track::UpdateDurations(MP4Duration duration)
  1222. {
  1223. // update media, track, and movie durations
  1224. m_pMediaDurationProperty->SetValue(
  1225. m_pMediaDurationProperty->GetValue() + duration);
  1226. MP4Duration movieDuration = ToMovieDuration(duration);
  1227. m_pTrackDurationProperty->SetValue(
  1228. m_pTrackDurationProperty->GetValue() + movieDuration);
  1229. m_pFile->UpdateDuration(m_pTrackDurationProperty->GetValue());
  1230. }
  1231. MP4Duration MP4Track::ToMovieDuration(MP4Duration trackDuration)
  1232. {
  1233. return (trackDuration * m_pFile->GetTimeScale())
  1234. / m_pTimeScaleProperty->GetValue();
  1235. }
  1236. void MP4Track::UpdateModificationTimes()
  1237. {
  1238. // update media and track modification times
  1239. MP4Timestamp now = MP4GetAbsTimestamp();
  1240. m_pMediaModificationProperty->SetValue(now);
  1241. m_pTrackModificationProperty->SetValue(now);
  1242. }
  1243. u_int32_t MP4Track::GetNumberOfChunks()
  1244. {
  1245. return m_pChunkOffsetProperty->GetCount();
  1246. }
  1247. u_int32_t MP4Track::GetChunkStscIndex(MP4ChunkId chunkId)
  1248. {
  1249. u_int32_t stscIndex;
  1250. u_int32_t numStscs = m_pStscCountProperty->GetValue();
  1251. ASSERT(chunkId);
  1252. ASSERT(numStscs > 0);
  1253. for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
  1254. if (chunkId < m_pStscFirstChunkProperty->GetValue(stscIndex)) {
  1255. ASSERT(stscIndex != 0);
  1256. break;
  1257. }
  1258. }
  1259. return stscIndex - 1;
  1260. }
  1261. MP4Timestamp MP4Track::GetChunkTime(MP4ChunkId chunkId)
  1262. {
  1263. u_int32_t stscIndex = GetChunkStscIndex(chunkId);
  1264. MP4ChunkId firstChunkId =
  1265. m_pStscFirstChunkProperty->GetValue(stscIndex);
  1266. MP4SampleId firstSample =
  1267. m_pStscFirstSampleProperty->GetValue(stscIndex);
  1268. u_int32_t samplesPerChunk =
  1269. m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
  1270. MP4SampleId firstSampleInChunk =
  1271. firstSample + ((chunkId - firstChunkId) * samplesPerChunk);
  1272. MP4Timestamp chunkTime;
  1273. GetSampleTimes(firstSampleInChunk, &chunkTime, NULL);
  1274. return chunkTime;
  1275. }
  1276. u_int32_t MP4Track::GetChunkSize(MP4ChunkId chunkId)
  1277. {
  1278. u_int32_t stscIndex = GetChunkStscIndex(chunkId);
  1279. MP4ChunkId firstChunkId =
  1280. m_pStscFirstChunkProperty->GetValue(stscIndex);
  1281. MP4SampleId firstSample =
  1282. m_pStscFirstSampleProperty->GetValue(stscIndex);
  1283. u_int32_t samplesPerChunk =
  1284. m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
  1285. uint32_t chunkOffsetBytes;
  1286. if (!TrySafeMultiply(samplesPerChunk, chunkId - firstChunkId, &chunkOffsetBytes))
  1287. return 0;
  1288. MP4SampleId firstSampleInChunk;
  1289. if (!TrySafeAdd(firstSample, chunkOffsetBytes, &firstSampleInChunk))
  1290. return 0;
  1291. // need cumulative sizes of samples in chunk
  1292. u_int32_t chunkSize = 0;
  1293. for (u_int32_t i = 0; i < samplesPerChunk; i++)
  1294. {
  1295. if (!TrySafeAdd(chunkSize, GetSampleSize(firstSampleInChunk + i), &chunkSize))
  1296. return 0;
  1297. }
  1298. return chunkSize;
  1299. }
  1300. void MP4Track::ReadChunk(MP4ChunkId chunkId,
  1301. u_int8_t** ppChunk, u_int32_t* pChunkSize,
  1302. MP4Timestamp* pStartTime, MP4Duration* pDuration)
  1303. {
  1304. ASSERT(chunkId);
  1305. ASSERT(ppChunk);
  1306. ASSERT(pChunkSize);
  1307. bool do_free=false;
  1308. u_int64_t chunkOffset =
  1309. m_pChunkOffsetProperty->GetValue(chunkId - 1);
  1310. *pChunkSize = GetChunkSize(chunkId);
  1311. if (!*ppChunk)
  1312. {
  1313. do_free=true;
  1314. *ppChunk = (u_int8_t*)MP4Malloc(*pChunkSize);
  1315. }
  1316. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  1317. printf("ReadChunk: track %u id %u offset 0x"X64" size %u (0x%x)\n",
  1318. m_trackId, chunkId, chunkOffset, *pChunkSize, *pChunkSize));
  1319. u_int64_t oldPos = m_pFile->GetPosition(); // only used in mode == 'w'
  1320. try {
  1321. m_pFile->SetPosition(chunkOffset);
  1322. m_pFile->ReadBytes(*ppChunk, *pChunkSize);
  1323. if (pStartTime)
  1324. *pStartTime = GetChunkTime(chunkId);
  1325. if (pDuration)
  1326. *pDuration = m_durationPerChunk;
  1327. }
  1328. catch (MP4Error* e) {
  1329. // let's not leak memory
  1330. if (do_free)
  1331. MP4Free(*ppChunk);
  1332. *ppChunk = NULL;
  1333. if (m_pFile->GetMode() == 'w') {
  1334. m_pFile->SetPosition(oldPos);
  1335. }
  1336. throw e;
  1337. }
  1338. if (m_pFile->GetMode() == 'w') {
  1339. m_pFile->SetPosition(oldPos);
  1340. }
  1341. }
  1342. void MP4Track::RewriteChunk(MP4ChunkId chunkId,
  1343. u_int8_t* pChunk, u_int32_t chunkSize)
  1344. {
  1345. u_int64_t chunkOffset = m_pFile->GetPosition();
  1346. m_pFile->WriteBytes(pChunk, chunkSize);
  1347. m_pChunkOffsetProperty->SetValue(chunkOffset, chunkId - 1);
  1348. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  1349. printf("RewriteChunk: track %u id %u offset 0x"X64" size %u (0x%x)\n",
  1350. m_trackId, chunkId, chunkOffset, chunkSize, chunkSize));
  1351. }
  1352. // map track type name aliases to official names
  1353. bool MP4Track::InitEditListProperties()
  1354. {
  1355. m_pElstCountProperty = NULL;
  1356. m_pElstMediaTimeProperty = NULL;
  1357. m_pElstDurationProperty = NULL;
  1358. m_pElstRateProperty = NULL;
  1359. m_pElstReservedProperty = NULL;
  1360. MP4Atom* pElstAtom =
  1361. m_pTrakAtom->FindAtomMP4("trak.edts.elst");
  1362. if (!pElstAtom) {
  1363. return false;
  1364. }
  1365. (void)pElstAtom->FindProperty(
  1366. "elst.entryCount",
  1367. (MP4Property**)&m_pElstCountProperty);
  1368. (void)pElstAtom->FindProperty(
  1369. "elst.entries.mediaTime",
  1370. (MP4Property**)&m_pElstMediaTimeProperty);
  1371. (void)pElstAtom->FindProperty(
  1372. "elst.entries.segmentDuration",
  1373. (MP4Property**)&m_pElstDurationProperty);
  1374. (void)pElstAtom->FindProperty(
  1375. "elst.entries.mediaRate",
  1376. (MP4Property**)&m_pElstRateProperty);
  1377. (void)pElstAtom->FindProperty(
  1378. "elst.entries.reserved",
  1379. (MP4Property**)&m_pElstReservedProperty);
  1380. return m_pElstCountProperty
  1381. && m_pElstMediaTimeProperty
  1382. && m_pElstDurationProperty
  1383. && m_pElstRateProperty
  1384. && m_pElstReservedProperty;
  1385. }
  1386. MP4EditId MP4Track::AddEdit(MP4EditId editId)
  1387. {
  1388. if (!m_pElstCountProperty) {
  1389. (void)m_pFile->AddDescendantAtoms(m_pTrakAtom, "edts.elst");
  1390. if (InitEditListProperties() == false) return MP4_INVALID_EDIT_ID;
  1391. }
  1392. if (editId == MP4_INVALID_EDIT_ID) {
  1393. editId = m_pElstCountProperty->GetValue() + 1;
  1394. }
  1395. m_pElstMediaTimeProperty->InsertValue(0, editId - 1);
  1396. m_pElstDurationProperty->InsertValue(0, editId - 1);
  1397. m_pElstRateProperty->InsertValue(1, editId - 1);
  1398. m_pElstReservedProperty->InsertValue(0, editId - 1);
  1399. m_pElstCountProperty->IncrementValue();
  1400. return editId;
  1401. }
  1402. void MP4Track::DeleteEdit(MP4EditId editId)
  1403. {
  1404. if (editId == MP4_INVALID_EDIT_ID) {
  1405. throw new MP4Error("edit id can't be zero",
  1406. "MP4Track::DeleteEdit");
  1407. }
  1408. if (!m_pElstCountProperty
  1409. || m_pElstCountProperty->GetValue() == 0) {
  1410. throw new MP4Error("no edits exist",
  1411. "MP4Track::DeleteEdit");
  1412. }
  1413. m_pElstMediaTimeProperty->DeleteValue(editId - 1);
  1414. m_pElstDurationProperty->DeleteValue(editId - 1);
  1415. m_pElstRateProperty->DeleteValue(editId - 1);
  1416. m_pElstReservedProperty->DeleteValue(editId - 1);
  1417. m_pElstCountProperty->IncrementValue(-1);
  1418. // clean up if last edit is deleted
  1419. if (m_pElstCountProperty->GetValue() == 0) {
  1420. m_pElstCountProperty = NULL;
  1421. m_pElstMediaTimeProperty = NULL;
  1422. m_pElstDurationProperty = NULL;
  1423. m_pElstRateProperty = NULL;
  1424. m_pElstReservedProperty = NULL;
  1425. m_pTrakAtom->DeleteChildAtom(
  1426. m_pTrakAtom->FindAtomMP4("trak.edts"));
  1427. }
  1428. }
  1429. MP4Timestamp MP4Track::GetEditStart(
  1430. MP4EditId editId)
  1431. {
  1432. if (editId == MP4_INVALID_EDIT_ID) {
  1433. return MP4_INVALID_TIMESTAMP;
  1434. } else if (editId == 1) {
  1435. return 0;
  1436. }
  1437. return (MP4Timestamp)GetEditTotalDuration(editId - 1);
  1438. }
  1439. MP4Duration MP4Track::GetEditTotalDuration(
  1440. MP4EditId editId)
  1441. {
  1442. u_int32_t numEdits = 0;
  1443. if (m_pElstCountProperty) {
  1444. numEdits = m_pElstCountProperty->GetValue();
  1445. }
  1446. if (editId == MP4_INVALID_EDIT_ID) {
  1447. editId = numEdits;
  1448. }
  1449. if (numEdits == 0 || editId > numEdits) {
  1450. return MP4_INVALID_DURATION;
  1451. }
  1452. MP4Duration totalDuration = 0;
  1453. for (MP4EditId eid = 1; eid <= editId; eid++) {
  1454. totalDuration +=
  1455. m_pElstDurationProperty->GetValue(eid - 1);
  1456. }
  1457. return totalDuration;
  1458. }
  1459. MP4SampleId MP4Track::GetSampleIdFromEditTime(
  1460. MP4Timestamp editWhen,
  1461. MP4Timestamp* pStartTime,
  1462. MP4Duration* pDuration)
  1463. {
  1464. MP4SampleId sampleId = MP4_INVALID_SAMPLE_ID;
  1465. u_int32_t numEdits = 0;
  1466. if (m_pElstCountProperty) {
  1467. numEdits = m_pElstCountProperty->GetValue();
  1468. }
  1469. if (numEdits) {
  1470. MP4Duration editElapsedDuration = 0;
  1471. for (MP4EditId editId = 1; editId <= numEdits; editId++) {
  1472. // remember edit segment's start time (in edit timeline)
  1473. MP4Timestamp editStartTime =
  1474. (MP4Timestamp)editElapsedDuration;
  1475. // accumulate edit segment's duration
  1476. editElapsedDuration +=
  1477. m_pElstDurationProperty->GetValue(editId - 1);
  1478. // calculate difference between the specified edit time
  1479. // and the end of this edit segment
  1480. if (editElapsedDuration - editWhen <= 0) {
  1481. // the specified time has not yet been reached
  1482. continue;
  1483. }
  1484. // 'editWhen' is within this edit segment
  1485. // calculate the specified edit time
  1486. // relative to just this edit segment
  1487. MP4Duration editOffset =
  1488. editWhen - editStartTime;
  1489. // calculate the media (track) time that corresponds
  1490. // to the specified edit time based on the edit list
  1491. MP4Timestamp mediaWhen =
  1492. m_pElstMediaTimeProperty->GetValue(editId - 1)
  1493. + editOffset;
  1494. // lookup the sample id for the media time
  1495. sampleId = GetSampleIdFromTime(mediaWhen, false);
  1496. // lookup the sample's media start time and duration
  1497. MP4Timestamp sampleStartTime;
  1498. MP4Duration sampleDuration;
  1499. GetSampleTimes(sampleId, &sampleStartTime, &sampleDuration);
  1500. // calculate the difference if any between when the sample
  1501. // would naturally start and when it starts in the edit timeline
  1502. MP4Duration sampleStartOffset =
  1503. mediaWhen - sampleStartTime;
  1504. // calculate the start time for the sample in the edit time line
  1505. MP4Timestamp editSampleStartTime =
  1506. editWhen - MIN(editOffset, sampleStartOffset);
  1507. MP4Duration editSampleDuration = 0;
  1508. // calculate how long this sample lasts in the edit list timeline
  1509. if (m_pElstRateProperty->GetValue(editId - 1) == 0) {
  1510. // edit segment is a "dwell"
  1511. // so sample duration is that of the edit segment
  1512. editSampleDuration =
  1513. m_pElstDurationProperty->GetValue(editId - 1);
  1514. } else {
  1515. // begin with the natural sample duration
  1516. editSampleDuration = sampleDuration;
  1517. // now shorten that if the edit segment starts
  1518. // after the sample would naturally start
  1519. if (editOffset < sampleStartOffset) {
  1520. editSampleDuration -= sampleStartOffset - editOffset;
  1521. }
  1522. // now shorten that if the edit segment ends
  1523. // before the sample would naturally end
  1524. if (editElapsedDuration
  1525. < editSampleStartTime + sampleDuration) {
  1526. editSampleDuration -= (editSampleStartTime + sampleDuration)
  1527. - editElapsedDuration;
  1528. }
  1529. }
  1530. if (pStartTime) {
  1531. *pStartTime = editSampleStartTime;
  1532. }
  1533. if (pDuration) {
  1534. *pDuration = editSampleDuration;
  1535. }
  1536. VERBOSE_EDIT(m_pFile->GetVerbosity(),
  1537. printf("GetSampleIdFromEditTime: when "U64" "
  1538. "sampleId %u start "U64" duration "D64"\n",
  1539. editWhen, sampleId,
  1540. editSampleStartTime, editSampleDuration));
  1541. return sampleId;
  1542. }
  1543. throw new MP4Error("time out of range",
  1544. "MP4Track::GetSampleIdFromEditTime");
  1545. } else { // no edit list
  1546. sampleId = GetSampleIdFromTime(editWhen, false);
  1547. if (pStartTime || pDuration) {
  1548. GetSampleTimes(sampleId, pStartTime, pDuration);
  1549. }
  1550. }
  1551. return sampleId;
  1552. }
  1553. void MP4Track::CalculateBytesPerSample ()
  1554. {
  1555. MP4Atom *pMedia = m_pTrakAtom->FindAtomMP4("trak.mdia.minf.stbl.stsd");
  1556. MP4Atom *pMediaData;
  1557. const char *media_data_name;
  1558. if (pMedia == NULL) return;
  1559. if (pMedia->GetNumberOfChildAtoms() != 1) return;
  1560. pMediaData = pMedia->GetChildAtom(0);
  1561. media_data_name = pMediaData->GetType();
  1562. if ((ATOMID(media_data_name) == ATOMID("twos")) ||
  1563. (ATOMID(media_data_name) == ATOMID("sowt"))) {
  1564. MP4IntegerProperty *chan, *sampleSize;
  1565. chan = (MP4IntegerProperty *)pMediaData->GetProperty(4);
  1566. sampleSize = (MP4IntegerProperty *)pMediaData->GetProperty(5);
  1567. m_bytesPerSample = chan->GetValue() * (sampleSize->GetValue() / 8);
  1568. }
  1569. }