mp4track.cpp
上传用户:sun1608
上传日期:2007-02-02
资源大小:6116k
文件大小:42k
源码类别:

流媒体/Mpeg4/MP4

开发平台:

Visual C++

  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.  All Rights Reserved.
  17.  * 
  18.  * Contributor(s): 
  19.  * Dave Mackie dmackie@cisco.com
  20.  */
  21. #include "mp4common.h"
  22. MP4Track::MP4Track(MP4File* pFile, MP4Atom* pTrakAtom) 
  23. {
  24. m_pFile = pFile;
  25. m_pTrakAtom = pTrakAtom;
  26. m_lastStsdIndex = 0;
  27. m_lastSampleFile = NULL;
  28. m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
  29. m_pCachedReadSample = NULL;
  30. m_cachedReadSampleSize = 0;
  31. m_writeSampleId = 1;
  32. m_fixedSampleDuration = 0;
  33. m_pChunkBuffer = NULL;
  34. m_chunkBufferSize = 0;
  35. m_chunkSamples = 0;
  36. m_chunkDuration = 0;
  37. m_samplesPerChunk = 0;
  38. m_durationPerChunk = 0;
  39. bool success = true;
  40. MP4Integer32Property* pTrackIdProperty;
  41. success &= m_pTrakAtom->FindProperty(
  42. "trak.tkhd.trackId",
  43. (MP4Property**)&pTrackIdProperty);
  44. if (success) {
  45. m_trackId = pTrackIdProperty->GetValue();
  46. }
  47. success &= m_pTrakAtom->FindProperty(
  48. "trak.mdia.mdhd.timeScale", 
  49. (MP4Property**)&m_pTimeScaleProperty);
  50. if (success) {
  51. // default chunking is 1 second of samples
  52. m_durationPerChunk = m_pTimeScaleProperty->GetValue();
  53. }
  54. success &= m_pTrakAtom->FindProperty(
  55. "trak.tkhd.duration", 
  56. (MP4Property**)&m_pTrackDurationProperty);
  57. success &= m_pTrakAtom->FindProperty(
  58. "trak.mdia.mdhd.duration", 
  59. (MP4Property**)&m_pMediaDurationProperty);
  60. success &= m_pTrakAtom->FindProperty(
  61. "trak.tkhd.modificationTime", 
  62. (MP4Property**)&m_pTrackModificationProperty);
  63. success &= m_pTrakAtom->FindProperty(
  64. "trak.mdia.mdhd.modificationTime", 
  65. (MP4Property**)&m_pMediaModificationProperty);
  66. success &= m_pTrakAtom->FindProperty(
  67. "trak.mdia.hdlr.handlerType",
  68. (MP4Property**)&m_pTypeProperty);
  69. // get handles on sample size information
  70. success &= m_pTrakAtom->FindProperty(
  71. "trak.mdia.minf.stbl.stsz.sampleSize",
  72. (MP4Property**)&m_pStszFixedSampleSizeProperty);
  73. success &= m_pTrakAtom->FindProperty(
  74. "trak.mdia.minf.stbl.stsz.sampleCount",
  75. (MP4Property**)&m_pStszSampleCountProperty);
  76. success &= m_pTrakAtom->FindProperty(
  77. "trak.mdia.minf.stbl.stsz.entries.sampleSize",
  78. (MP4Property**)&m_pStszSampleSizeProperty);
  79. // get handles on information needed to map sample id's to file offsets
  80. success &= m_pTrakAtom->FindProperty(
  81. "trak.mdia.minf.stbl.stsc.entryCount",
  82. (MP4Property**)&m_pStscCountProperty);
  83. success &= m_pTrakAtom->FindProperty(
  84. "trak.mdia.minf.stbl.stsc.entries.firstChunk",
  85. (MP4Property**)&m_pStscFirstChunkProperty);
  86. success &= m_pTrakAtom->FindProperty(
  87. "trak.mdia.minf.stbl.stsc.entries.samplesPerChunk",
  88. (MP4Property**)&m_pStscSamplesPerChunkProperty);
  89. success &= m_pTrakAtom->FindProperty(
  90. "trak.mdia.minf.stbl.stsc.entries.sampleDescriptionIndex",
  91. (MP4Property**)&m_pStscSampleDescrIndexProperty);
  92. success &= m_pTrakAtom->FindProperty(
  93. "trak.mdia.minf.stbl.stsc.entries.firstSample",
  94. (MP4Property**)&m_pStscFirstSampleProperty);
  95. bool haveStco = m_pTrakAtom->FindProperty(
  96. "trak.mdia.minf.stbl.stco.entryCount",
  97. (MP4Property**)&m_pChunkCountProperty);
  98. if (haveStco) {
  99. success &= m_pTrakAtom->FindProperty(
  100. "trak.mdia.minf.stbl.stco.entries.chunkOffset",
  101. (MP4Property**)&m_pChunkOffsetProperty);
  102. } else {
  103. success &= m_pTrakAtom->FindProperty(
  104. "trak.mdia.minf.stbl.co64.entryCount",
  105. (MP4Property**)&m_pChunkCountProperty);
  106. success &= m_pTrakAtom->FindProperty(
  107. "trak.mdia.minf.stbl.co64.entries.chunkOffset",
  108. (MP4Property**)&m_pChunkOffsetProperty);
  109. }
  110. // get handles on sample timing info
  111. success &= m_pTrakAtom->FindProperty(
  112. "trak.mdia.minf.stbl.stts.entryCount",
  113. (MP4Property**)&m_pSttsCountProperty);
  114. success &= m_pTrakAtom->FindProperty(
  115. "trak.mdia.minf.stbl.stts.entries.sampleCount",
  116. (MP4Property**)&m_pSttsSampleCountProperty);
  117. success &= m_pTrakAtom->FindProperty(
  118. "trak.mdia.minf.stbl.stts.entries.sampleDelta",
  119. (MP4Property**)&m_pSttsSampleDeltaProperty);
  120. // get handles on rendering offset info if it exists
  121. m_pCttsCountProperty = NULL;
  122. m_pCttsSampleCountProperty = NULL;
  123. m_pCttsSampleOffsetProperty = NULL;
  124. bool haveCtts = m_pTrakAtom->FindProperty(
  125. "trak.mdia.minf.stbl.ctts.entryCount",
  126. (MP4Property**)&m_pCttsCountProperty);
  127. if (haveCtts) {
  128. success &= m_pTrakAtom->FindProperty(
  129. "trak.mdia.minf.stbl.ctts.entries.sampleCount",
  130. (MP4Property**)&m_pCttsSampleCountProperty);
  131. success &= m_pTrakAtom->FindProperty(
  132. "trak.mdia.minf.stbl.ctts.entries.sampleOffset",
  133. (MP4Property**)&m_pCttsSampleOffsetProperty);
  134. }
  135. // get handles on sync sample info if it exists
  136. m_pStssCountProperty = NULL;
  137. m_pStssSampleProperty = NULL;
  138. bool haveStss = m_pTrakAtom->FindProperty(
  139. "trak.mdia.minf.stbl.stss.entryCount",
  140. (MP4Property**)&m_pStssCountProperty);
  141. if (haveStss) {
  142. success &= m_pTrakAtom->FindProperty(
  143. "trak.mdia.minf.stbl.stss.entries.sampleNumber",
  144. (MP4Property**)&m_pStssSampleProperty);
  145. }
  146. // edit list
  147. InitEditListProperties();
  148. // was everything found?
  149. if (!success) {
  150. throw new MP4Error("invalid track", "MP4Track::MP4Track");
  151. }
  152. }
  153. MP4Track::~MP4Track()
  154. {
  155. MP4Free(m_pCachedReadSample);
  156. MP4Free(m_pChunkBuffer);
  157. }
  158. const char* MP4Track::GetType()
  159. {
  160. return m_pTypeProperty->GetValue();
  161. }
  162. void MP4Track::SetType(const char* type) 
  163. {
  164. m_pTypeProperty->SetValue(NormalizeTrackType(type));
  165. }
  166. void MP4Track::ReadSample(
  167. MP4SampleId sampleId,
  168. u_int8_t** ppBytes, 
  169. u_int32_t* pNumBytes, 
  170. MP4Timestamp* pStartTime, 
  171. MP4Duration* pDuration,
  172. MP4Duration* pRenderingOffset, 
  173. bool* pIsSyncSample)
  174. {
  175. if (sampleId == MP4_INVALID_SAMPLE_ID) {
  176. throw new MP4Error("sample id can't be zero", 
  177. "MP4Track::ReadSample");
  178. }
  179. // handle unusual case of wanting to read a sample
  180. // that is still sitting in the write chunk buffer
  181. if (m_pChunkBuffer && sampleId >= m_writeSampleId - m_chunkSamples) {
  182. WriteChunkBuffer();
  183. }
  184. FILE* pFile = GetSampleFile(sampleId);
  185. if (pFile == (FILE*)-1) {
  186. throw new MP4Error("sample is located in an inaccessible file",
  187. "MP4Track::ReadSample");
  188. }
  189. u_int64_t fileOffset = GetSampleFileOffset(sampleId);
  190. u_int32_t sampleSize = GetSampleSize(sampleId);
  191. if (*ppBytes != NULL && *pNumBytes < sampleSize) {
  192. throw new MP4Error("sample buffer is too small",
  193.  "MP4Track::ReadSample");
  194. }
  195. *pNumBytes = sampleSize;
  196. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  197. printf("ReadSample: track %u id %u offset 0x"LLX" size %u (0x%x)n",
  198. m_trackId, sampleId, fileOffset, *pNumBytes, *pNumBytes));
  199. bool bufferMalloc = false;
  200. if (*ppBytes == NULL) {
  201. *ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
  202. bufferMalloc = true;
  203. }
  204. u_int64_t oldPos = m_pFile->GetPosition(pFile); // only used in mode == 'w'
  205. try { 
  206. m_pFile->SetPosition(fileOffset, pFile);
  207. m_pFile->ReadBytes(*ppBytes, *pNumBytes, pFile);
  208. if (pStartTime || pDuration) {
  209. GetSampleTimes(sampleId, pStartTime, pDuration);
  210. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  211. printf("ReadSample:  start "LLU" duration "LLD"n",
  212. (pStartTime ? *pStartTime : 0), 
  213. (pDuration ? *pDuration : 0)));
  214. }
  215. if (pRenderingOffset) {
  216. *pRenderingOffset = GetSampleRenderingOffset(sampleId);
  217. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  218. printf("ReadSample:  renderingOffset "LLD"n",
  219. *pRenderingOffset));
  220. }
  221. if (pIsSyncSample) {
  222. *pIsSyncSample = IsSyncSample(sampleId);
  223. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  224. printf("ReadSample:  isSyncSample %un",
  225. *pIsSyncSample));
  226. }
  227. }
  228. catch (MP4Error* e) {
  229. if (bufferMalloc) {
  230. // let's not leak memory
  231. MP4Free(*ppBytes);
  232. *ppBytes = NULL;
  233. }
  234. if (m_pFile->GetMode() == 'w') {
  235. m_pFile->SetPosition(oldPos, pFile);
  236. }
  237. throw e;
  238. }
  239. if (m_pFile->GetMode() == 'w') {
  240. m_pFile->SetPosition(oldPos, pFile);
  241. }
  242. }
  243. void MP4Track::ReadSampleFragment(
  244. MP4SampleId sampleId,
  245. u_int32_t sampleOffset,
  246. u_int16_t sampleLength,
  247. u_int8_t* pDest)
  248. {
  249. if (sampleId == MP4_INVALID_SAMPLE_ID) {
  250. throw new MP4Error("invalid sample id", 
  251. "MP4Track::ReadSampleFragment");
  252. }
  253. if (sampleId != m_cachedReadSampleId) {
  254. MP4Free(m_pCachedReadSample);
  255. m_pCachedReadSample = NULL;
  256. m_cachedReadSampleSize = 0;
  257. m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
  258. ReadSample(
  259. sampleId,
  260. &m_pCachedReadSample,
  261. &m_cachedReadSampleSize);
  262. m_cachedReadSampleId = sampleId;
  263. }
  264. if (sampleOffset + sampleLength > m_cachedReadSampleSize) {
  265. throw new MP4Error("offset and/or length are too large", 
  266. "MP4Track::ReadSampleFragment");
  267. }
  268. memcpy(pDest, &m_pCachedReadSample[sampleOffset], sampleLength);
  269. }
  270. void MP4Track::WriteSample(
  271. u_int8_t* pBytes, 
  272. u_int32_t numBytes,
  273. MP4Duration duration, 
  274. MP4Duration renderingOffset, 
  275. bool isSyncSample)
  276. {
  277. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  278. printf("WriteSample: track %u id %u size %u (0x%x) ",
  279. m_trackId, m_writeSampleId, numBytes, numBytes));
  280. if (pBytes == NULL && numBytes > 0) {
  281. throw new MP4Error("no sample data", "MP4WriteSample");
  282. }
  283. if (duration == MP4_INVALID_DURATION) {
  284. duration = GetFixedSampleDuration();
  285. }
  286. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  287. printf("duration "LLU"n", duration));
  288. // append sample bytes to chunk buffer
  289. m_pChunkBuffer = (u_int8_t*)MP4Realloc(m_pChunkBuffer, 
  290. m_chunkBufferSize + numBytes);
  291. memcpy(&m_pChunkBuffer[m_chunkBufferSize], pBytes, numBytes);
  292. m_chunkBufferSize += numBytes;
  293. m_chunkSamples++;
  294. m_chunkDuration += duration;
  295. UpdateSampleSizes(m_writeSampleId, numBytes);
  296. UpdateSampleTimes(duration);
  297. UpdateRenderingOffsets(m_writeSampleId, renderingOffset);
  298. UpdateSyncSamples(m_writeSampleId, isSyncSample);
  299. if (IsChunkFull(m_writeSampleId)) {
  300. WriteChunkBuffer();
  301. }
  302. UpdateDurations(duration);
  303. UpdateModificationTimes();
  304. m_writeSampleId++;
  305. }
  306. void MP4Track::WriteChunkBuffer()
  307. {
  308. if (m_chunkBufferSize == 0) {
  309. return;
  310. }
  311. u_int64_t chunkOffset = m_pFile->GetPosition();
  312. // write chunk buffer
  313. m_pFile->WriteBytes(m_pChunkBuffer, m_chunkBufferSize);
  314. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  315. printf("WriteChunk: track %u offset 0x"LLX" size %u (0x%x) numSamples %un",
  316. m_trackId, chunkOffset, m_chunkBufferSize, 
  317. m_chunkBufferSize, m_chunkSamples));
  318. UpdateSampleToChunk(m_writeSampleId, 
  319. m_pChunkCountProperty->GetValue() + 1, 
  320. m_chunkSamples);
  321. UpdateChunkOffsets(chunkOffset);
  322. // clean up chunk buffer
  323. MP4Free(m_pChunkBuffer);
  324. m_pChunkBuffer = NULL;
  325. m_chunkBufferSize = 0;
  326. m_chunkSamples = 0;
  327. m_chunkDuration = 0;
  328. }
  329. void MP4Track::FinishWrite()
  330. {
  331. // write out any remaining samples in chunk buffer
  332. WriteChunkBuffer();
  333. // record buffer size and bitrates
  334. MP4BitfieldProperty* pBufferSizeProperty;
  335. if (m_pTrakAtom->FindProperty(
  336.   "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.bufferSizeDB",
  337.   (MP4Property**)&pBufferSizeProperty)) {
  338. pBufferSizeProperty->SetValue(GetMaxSampleSize());
  339. }
  340. MP4Integer32Property* pBitrateProperty;
  341. if (m_pTrakAtom->FindProperty(
  342.   "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.maxBitrate",
  343.   (MP4Property**)&pBitrateProperty)) {
  344. pBitrateProperty->SetValue(GetMaxBitrate());
  345. }
  346. if (m_pTrakAtom->FindProperty(
  347.   "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate",
  348.   (MP4Property**)&pBitrateProperty)) {
  349. pBitrateProperty->SetValue(GetAvgBitrate());
  350. }
  351. }
  352. bool MP4Track::IsChunkFull(MP4SampleId sampleId)
  353. {
  354. if (m_samplesPerChunk) {
  355. return m_chunkSamples >= m_samplesPerChunk;
  356. }
  357. ASSERT(m_durationPerChunk);
  358. return m_chunkDuration >= m_durationPerChunk;
  359. }
  360. u_int32_t MP4Track::GetNumberOfSamples()
  361. {
  362. return m_pStszSampleCountProperty->GetValue();
  363. }
  364. u_int32_t MP4Track::GetSampleSize(MP4SampleId sampleId)
  365. {
  366. u_int32_t fixedSampleSize = 
  367. m_pStszFixedSampleSizeProperty->GetValue(); 
  368. if (fixedSampleSize != 0) {
  369. return fixedSampleSize;
  370. }
  371. return m_pStszSampleSizeProperty->GetValue(sampleId - 1);
  372. }
  373. u_int32_t MP4Track::GetMaxSampleSize()
  374. {
  375. u_int32_t fixedSampleSize = 
  376. m_pStszFixedSampleSizeProperty->GetValue(); 
  377. if (fixedSampleSize != 0) {
  378. return fixedSampleSize;
  379. }
  380. u_int32_t maxSampleSize = 0;
  381. u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
  382. for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
  383. u_int32_t sampleSize =
  384. m_pStszSampleSizeProperty->GetValue(sid - 1);
  385. if (sampleSize > maxSampleSize) {
  386. maxSampleSize = sampleSize;
  387. }
  388. }
  389. return maxSampleSize;
  390. }
  391. u_int64_t MP4Track::GetTotalOfSampleSizes()
  392. {
  393. u_int32_t fixedSampleSize = 
  394. m_pStszFixedSampleSizeProperty->GetValue(); 
  395. // if fixed sample size, just need to multiply by number of samples
  396. if (fixedSampleSize != 0) {
  397. return fixedSampleSize * GetNumberOfSamples();
  398. }
  399. // else non-fixed sample size, sum them
  400. u_int64_t totalSampleSizes = 0;
  401. u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
  402. for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
  403. u_int32_t sampleSize =
  404. m_pStszSampleSizeProperty->GetValue(sid - 1);
  405. totalSampleSizes += sampleSize;
  406. }
  407. return totalSampleSizes;
  408. }
  409. void MP4Track::UpdateSampleSizes(MP4SampleId sampleId, u_int32_t numBytes)
  410. {
  411. // for first sample
  412. if (sampleId == 1) {
  413. // presume sample size is fixed
  414. m_pStszFixedSampleSizeProperty->SetValue(numBytes); 
  415. } else { // sampleId > 1
  416. u_int32_t fixedSampleSize = 
  417. m_pStszFixedSampleSizeProperty->GetValue(); 
  418. if (fixedSampleSize == 0 || numBytes != fixedSampleSize) {
  419. // sample size is not fixed
  420. if (fixedSampleSize) {
  421. // need to clear fixed sample size
  422. m_pStszFixedSampleSizeProperty->SetValue(0); 
  423. // and create sizes for all previous samples
  424. for (MP4SampleId sid = 1; sid < sampleId; sid++) {
  425. m_pStszSampleSizeProperty->AddValue(fixedSampleSize);
  426. }
  427. }
  428. // add size value for this sample
  429. m_pStszSampleSizeProperty->AddValue(numBytes);
  430. }
  431. }
  432. m_pStszSampleCountProperty->IncrementValue();
  433. }
  434. u_int32_t MP4Track::GetAvgBitrate()
  435. {
  436. if (GetDuration() == 0) {
  437. return 0;
  438. }
  439. u_int64_t durationSecs =
  440. MP4ConvertTime(GetDuration(), GetTimeScale(), MP4_SECS_TIME_SCALE);
  441. if (GetDuration() % GetTimeScale() != 0) {
  442. durationSecs++;
  443. }
  444. return (GetTotalOfSampleSizes() * 8) / durationSecs;
  445. }
  446. u_int32_t MP4Track::GetMaxBitrate()
  447. {
  448. u_int32_t timeScale = GetTimeScale();
  449. MP4SampleId numSamples = GetNumberOfSamples();
  450. u_int32_t maxBytesPerSec = 0;
  451. u_int32_t bytesThisSec = 0;
  452. MP4Timestamp thisSec = 0;
  453. for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
  454. u_int32_t sampleSize;
  455. MP4Timestamp sampleTime;
  456. sampleSize = GetSampleSize(sid);
  457. GetSampleTimes(sid, &sampleTime, NULL);
  458. // sample counts for current second
  459. if (sampleTime < thisSec + timeScale) {
  460. bytesThisSec += sampleSize;
  461. } else { // sample is in a future second
  462. if (bytesThisSec > maxBytesPerSec) {
  463. maxBytesPerSec = bytesThisSec;
  464. }
  465. thisSec = sampleTime - (sampleTime % timeScale);
  466. bytesThisSec = sampleSize;
  467. }
  468. }
  469. // last second (or partial second) 
  470. if (bytesThisSec > maxBytesPerSec) {
  471. maxBytesPerSec = bytesThisSec;
  472. }
  473. return maxBytesPerSec * 8;
  474. }
  475. u_int32_t MP4Track::GetSampleStscIndex(MP4SampleId sampleId)
  476. {
  477. u_int32_t stscIndex;
  478. u_int32_t numStscs = m_pStscCountProperty->GetValue();
  479. if (numStscs == 0) {
  480. throw new MP4Error("No data chunks exist", "GetSampleStscIndex");
  481. }
  482. for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
  483. if (sampleId < m_pStscFirstSampleProperty->GetValue(stscIndex)) {
  484. ASSERT(stscIndex != 0);
  485. stscIndex -= 1;
  486. break;
  487. }
  488. }
  489. if (stscIndex == numStscs) {
  490. ASSERT(stscIndex != 0);
  491. stscIndex -= 1;
  492. }
  493. return stscIndex;
  494. }
  495. FILE* MP4Track::GetSampleFile(MP4SampleId sampleId)
  496. {
  497. u_int32_t stscIndex =
  498. GetSampleStscIndex(sampleId);
  499. u_int32_t stsdIndex = 
  500. m_pStscSampleDescrIndexProperty->GetValue(stscIndex);
  501. // check if the answer will be the same as last time
  502. if (m_lastStsdIndex && stsdIndex == m_lastStsdIndex) {
  503. return m_lastSampleFile;
  504. }
  505. MP4Atom* pStsdAtom = 
  506. m_pTrakAtom->FindAtom("trak.mdia.minf.stbl.stsd");
  507. ASSERT(pStsdAtom);
  508. MP4Atom* pStsdEntryAtom = 
  509. pStsdAtom->GetChildAtom(stsdIndex - 1);
  510. ASSERT(pStsdEntryAtom);
  511. MP4Integer16Property* pDrefIndexProperty = NULL;
  512. pStsdEntryAtom->FindProperty(
  513. "*.dataReferenceIndex",
  514. (MP4Property**)&pDrefIndexProperty);
  515. if (pDrefIndexProperty == NULL) {
  516. throw new MP4Error("invalid stsd entry", "GetSampleFile");
  517. }
  518. u_int32_t drefIndex =
  519. pDrefIndexProperty->GetValue();
  520. MP4Atom* pDrefAtom =
  521. m_pTrakAtom->FindAtom("trak.mdia.minf.dinf.dref");
  522. ASSERT(pDrefAtom);
  523. MP4Atom* pUrlAtom =
  524. pDrefAtom->GetChildAtom(drefIndex - 1);
  525. ASSERT(pUrlAtom);
  526. FILE* pFile;
  527. if (pUrlAtom->GetFlags() & 1) {
  528. pFile = NULL; // self-contained
  529. } else {
  530. MP4StringProperty* pLocationProperty = NULL;
  531. pUrlAtom->FindProperty(
  532. "*.location", 
  533. (MP4Property**)&pLocationProperty);
  534. ASSERT(pLocationProperty);
  535. const char* url = pLocationProperty->GetValue();
  536. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  537. printf("dref url = %sn", url));
  538. pFile = (FILE*)-1;
  539. // attempt to open url if it's a file url 
  540. // currently this is the only thing we understand
  541. if (!strncmp(url, "file:", 5)) {
  542. const char* fileName = url + 5;
  543. if (!strncmp(fileName, "//", 2)) {
  544. fileName = strchr(fileName + 2, '/');
  545. }
  546. if (fileName) {
  547. pFile = fopen(fileName, "rb");
  548. if (!pFile) {
  549. pFile = (FILE*)-1;
  550. }
  551. }
  552. }
  553. if (m_lastSampleFile) {
  554. fclose(m_lastSampleFile);
  555. }
  556. // cache the answer
  557. m_lastStsdIndex = stsdIndex;
  558. m_lastSampleFile = pFile;
  559. return pFile;
  560. }
  561. u_int64_t MP4Track::GetSampleFileOffset(MP4SampleId sampleId)
  562. {
  563. u_int32_t stscIndex =
  564. GetSampleStscIndex(sampleId);
  565. u_int32_t firstChunk = 
  566. m_pStscFirstChunkProperty->GetValue(stscIndex);
  567. MP4SampleId firstSample = 
  568. m_pStscFirstSampleProperty->GetValue(stscIndex);
  569. u_int32_t samplesPerChunk = 
  570. m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
  571. MP4ChunkId chunkId = firstChunk +
  572. ((sampleId - firstSample) / samplesPerChunk);
  573. u_int64_t chunkOffset = m_pChunkOffsetProperty->GetValue(chunkId - 1);
  574. MP4SampleId firstSampleInChunk = 
  575. sampleId - ((sampleId - firstSample) % samplesPerChunk);
  576. // need cumulative samples sizes from firstSample to sampleId - 1
  577. u_int32_t sampleOffset = 0;
  578. for (MP4SampleId i = firstSampleInChunk; i < sampleId; i++) {
  579. sampleOffset += GetSampleSize(i);
  580. }
  581. return chunkOffset + sampleOffset;
  582. }
  583. void MP4Track::UpdateSampleToChunk(MP4SampleId sampleId,
  584.  MP4ChunkId chunkId, u_int32_t samplesPerChunk)
  585. {
  586. u_int32_t numStsc = m_pStscCountProperty->GetValue();
  587. // if samplesPerChunk == samplesPerChunk of last entry
  588. if (numStsc && samplesPerChunk == 
  589.   m_pStscSamplesPerChunkProperty->GetValue(numStsc-1)) {
  590. // nothing to do
  591. } else {
  592. // add stsc entry
  593. m_pStscFirstChunkProperty->AddValue(chunkId);
  594. m_pStscSamplesPerChunkProperty->AddValue(samplesPerChunk);
  595. m_pStscSampleDescrIndexProperty->AddValue(1);
  596. m_pStscFirstSampleProperty->AddValue(sampleId - samplesPerChunk + 1);
  597. m_pStscCountProperty->IncrementValue();
  598. }
  599. }
  600. void MP4Track::UpdateChunkOffsets(u_int64_t chunkOffset)
  601. {
  602. if (m_pChunkOffsetProperty->GetType() == Integer32Property) {
  603. ((MP4Integer32Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
  604. } else {
  605. ((MP4Integer64Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
  606. }
  607. m_pChunkCountProperty->IncrementValue();
  608. }
  609. MP4Duration MP4Track::GetFixedSampleDuration()
  610. {
  611. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  612. if (numStts == 0) {
  613. return m_fixedSampleDuration;
  614. }
  615. if (numStts != 1) {
  616. return MP4_INVALID_DURATION; // sample duration is not fixed
  617. }
  618. return m_pSttsSampleDeltaProperty->GetValue(0);
  619. }
  620. bool MP4Track::SetFixedSampleDuration(MP4Duration duration)
  621. {
  622. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  623. // setting this is only allowed before samples have been written
  624. if (numStts != 0) {
  625. return false;
  626. }
  627. m_fixedSampleDuration = duration;
  628. return true;
  629. }
  630. void MP4Track::GetSampleTimes(MP4SampleId sampleId,
  631. MP4Timestamp* pStartTime, MP4Duration* pDuration)
  632. {
  633. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  634. MP4SampleId sid = 1;
  635. MP4Duration elapsed = 0;
  636. for (u_int32_t sttsIndex = 0; sttsIndex < numStts; sttsIndex++) {
  637. u_int32_t sampleCount = 
  638. m_pSttsSampleCountProperty->GetValue(sttsIndex);
  639. u_int32_t sampleDelta = 
  640. m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
  641. if (sampleId <= sid + sampleCount - 1) {
  642. if (pStartTime) {
  643. *pStartTime = elapsed + ((sampleId - sid) * sampleDelta);
  644. }
  645. if (pDuration) {
  646. *pDuration = sampleDelta;
  647. }
  648. return;
  649. }
  650. sid += sampleCount;
  651. elapsed += sampleCount * sampleDelta;
  652. }
  653. throw new MP4Error("sample id out of range", 
  654. "MP4Track::GetSampleTimes");
  655. }
  656. MP4SampleId MP4Track::GetSampleIdFromTime(
  657. MP4Timestamp when, 
  658. bool wantSyncSample) 
  659. {
  660. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  661. MP4SampleId sid = 1;
  662. MP4Duration elapsed = 0;
  663. for (u_int32_t sttsIndex = 0; sttsIndex < numStts; sttsIndex++) {
  664. u_int32_t sampleCount = 
  665. m_pSttsSampleCountProperty->GetValue(sttsIndex);
  666. u_int32_t sampleDelta = 
  667. m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
  668. if (sampleDelta == 0 && sttsIndex < numStts - 1) {
  669. VERBOSE_READ(m_pFile->GetVerbosity(),
  670. printf("Warning: Zero sample duration, stts entry %un",
  671. sttsIndex));
  672. }
  673. MP4Duration d = when - elapsed;
  674. if (d <= sampleCount * sampleDelta) {
  675. MP4SampleId sampleId = sid;
  676. if (sampleDelta) {
  677. sampleId += (d / sampleDelta);
  678. }
  679. if (wantSyncSample) {
  680. return GetNextSyncSample(sampleId);
  681. }
  682. return sampleId;
  683. }
  684. sid += sampleCount;
  685. elapsed += sampleCount * sampleDelta;
  686. }
  687. throw new MP4Error("time out of range", 
  688. "MP4Track::GetSampleIdFromTime");
  689. return 0; // satisfy MS compiler
  690. }
  691. void MP4Track::UpdateSampleTimes(MP4Duration duration)
  692. {
  693. u_int32_t numStts = m_pSttsCountProperty->GetValue();
  694. // if duration == duration of last entry
  695. if (numStts 
  696.   && duration == m_pSttsSampleDeltaProperty->GetValue(numStts-1)) {
  697. // increment last entry sampleCount
  698. m_pSttsSampleCountProperty->IncrementValue(1, numStts-1);
  699. } else {
  700. // add stts entry, sampleCount = 1, sampleDuration = duration
  701. m_pSttsSampleCountProperty->AddValue(1);
  702. m_pSttsSampleDeltaProperty->AddValue(duration);
  703. m_pSttsCountProperty->IncrementValue();;
  704. }
  705. }
  706. u_int32_t MP4Track::GetSampleCttsIndex(MP4SampleId sampleId, 
  707. MP4SampleId* pFirstSampleId)
  708. {
  709. u_int32_t numCtts = m_pCttsCountProperty->GetValue();
  710. MP4SampleId sid = 1;
  711. for (u_int32_t cttsIndex = 0; cttsIndex < numCtts; cttsIndex++) {
  712. u_int32_t sampleCount = 
  713. m_pCttsSampleCountProperty->GetValue(cttsIndex);
  714. if (sampleId <= sid + sampleCount - 1) {
  715. if (pFirstSampleId) {
  716. *pFirstSampleId = sid;
  717. }
  718. return cttsIndex;
  719. }
  720. sid += sampleCount;
  721. }
  722. throw new MP4Error("sample id out of range", 
  723. "MP4Track::GetSampleCttsIndex");
  724. return 0; // satisfy MS compiler
  725. }
  726. MP4Duration MP4Track::GetSampleRenderingOffset(MP4SampleId sampleId)
  727. {
  728. if (m_pCttsCountProperty == NULL) {
  729. return 0;
  730. }
  731. if (m_pCttsCountProperty->GetValue() == 0) {
  732. return 0;
  733. }
  734. u_int32_t cttsIndex = GetSampleCttsIndex(sampleId);
  735. return m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
  736. }
  737. void MP4Track::UpdateRenderingOffsets(MP4SampleId sampleId, 
  738. MP4Duration renderingOffset)
  739. {
  740. // if ctts atom doesn't exist
  741. if (m_pCttsCountProperty == NULL) {
  742. // no rendering offset, so nothing to do
  743. if (renderingOffset == 0) {
  744. return;
  745. }
  746. // else create a ctts atom
  747. MP4Atom* pCttsAtom = AddAtom("trak.mdia.minf.stbl", "ctts");
  748. // and get handles on the properties
  749. pCttsAtom->FindProperty(
  750. "ctts.entryCount",
  751. (MP4Property**)&m_pCttsCountProperty);
  752. pCttsAtom->FindProperty(
  753. "ctts.entries.sampleCount",
  754. (MP4Property**)&m_pCttsSampleCountProperty);
  755. pCttsAtom->FindProperty(
  756. "ctts.entries.sampleOffset",
  757. (MP4Property**)&m_pCttsSampleOffsetProperty);
  758. // if this is not the first sample
  759. if (sampleId > 1) {
  760. // add a ctts entry for all previous samples
  761. // with rendering offset equal to zero
  762. m_pCttsSampleCountProperty->AddValue(sampleId - 1);
  763. m_pCttsSampleOffsetProperty->AddValue(0);
  764. m_pCttsCountProperty->IncrementValue();;
  765. }
  766. }
  767. // ctts atom exists (now)
  768. u_int32_t numCtts = m_pCttsCountProperty->GetValue();
  769. // if renderingOffset == renderingOffset of last entry
  770. if (numCtts && renderingOffset
  771.    == m_pCttsSampleOffsetProperty->GetValue(numCtts-1)) {
  772. // increment last entry sampleCount
  773. m_pCttsSampleCountProperty->IncrementValue(1, numCtts-1);
  774. } else {
  775. // add ctts entry, sampleCount = 1, sampleOffset = renderingOffset
  776. m_pCttsSampleCountProperty->AddValue(1);
  777. m_pCttsSampleOffsetProperty->AddValue(renderingOffset);
  778. m_pCttsCountProperty->IncrementValue();
  779. }
  780. }
  781. void MP4Track::SetSampleRenderingOffset(MP4SampleId sampleId,
  782.  MP4Duration renderingOffset)
  783. {
  784. // check if any ctts entries exist
  785. if (m_pCttsCountProperty == NULL
  786.   || m_pCttsCountProperty->GetValue() == 0) {
  787. // if not then Update routine can be used 
  788. // to create a ctts entry for samples before this one
  789. // and a ctts entry for this sample 
  790. UpdateRenderingOffsets(sampleId, renderingOffset);
  791. // but we also need a ctts entry 
  792. // for all samples after this one
  793. u_int32_t afterSamples = GetNumberOfSamples() - sampleId;
  794. if (afterSamples) {
  795. m_pCttsSampleCountProperty->AddValue(afterSamples);
  796. m_pCttsSampleOffsetProperty->AddValue(0);
  797. m_pCttsCountProperty->IncrementValue();;
  798. }
  799. return;
  800. }
  801. MP4SampleId firstSampleId;
  802. u_int32_t cttsIndex = GetSampleCttsIndex(sampleId, &firstSampleId);
  803. // do nothing in the degenerate case
  804. if (renderingOffset == 
  805.   m_pCttsSampleOffsetProperty->GetValue(cttsIndex)) {
  806. return;
  807. }
  808. u_int32_t sampleCount =
  809. m_pCttsSampleCountProperty->GetValue(cttsIndex);
  810. // if this sample has it's own ctts entry
  811. if (sampleCount == 1) {
  812. // then just set the value, 
  813. // note we don't attempt to collapse entries
  814. m_pCttsSampleOffsetProperty->SetValue(renderingOffset, cttsIndex);
  815. return;
  816. }
  817. MP4SampleId lastSampleId = firstSampleId + sampleCount - 1;
  818. // else we share this entry with other samples
  819. // we need to insert our own entry
  820. if (sampleId == firstSampleId) {
  821. // our sample is the first one
  822. m_pCttsSampleCountProperty->
  823. InsertValue(1, cttsIndex);
  824. m_pCttsSampleOffsetProperty->
  825. InsertValue(renderingOffset, cttsIndex);
  826. m_pCttsSampleCountProperty->
  827. SetValue(sampleCount - 1, cttsIndex + 1);
  828. m_pCttsCountProperty->IncrementValue();
  829. } else if (sampleId == lastSampleId) {
  830. // our sample is the last one
  831. m_pCttsSampleCountProperty->
  832. InsertValue(1, cttsIndex + 1);
  833. m_pCttsSampleOffsetProperty->
  834. InsertValue(renderingOffset, cttsIndex + 1);
  835. m_pCttsSampleCountProperty->
  836. SetValue(sampleCount - 1, cttsIndex);
  837. m_pCttsCountProperty->IncrementValue();
  838. } else {
  839. // our sample is in the middle, UGH!
  840. // insert our new entry
  841. m_pCttsSampleCountProperty->
  842. InsertValue(1, cttsIndex + 1);
  843. m_pCttsSampleOffsetProperty->
  844. InsertValue(renderingOffset, cttsIndex + 1);
  845. // adjust count of previous entry
  846. m_pCttsSampleCountProperty->
  847. SetValue(sampleId - firstSampleId, cttsIndex);
  848. // insert new entry for those samples beyond our sample
  849. m_pCttsSampleCountProperty->
  850. InsertValue(lastSampleId - sampleId, cttsIndex + 2);
  851. u_int32_t oldRenderingOffset =
  852. m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
  853. m_pCttsSampleOffsetProperty->
  854. InsertValue(oldRenderingOffset, cttsIndex + 2);
  855. m_pCttsCountProperty->IncrementValue(2);
  856. }
  857. }
  858. bool MP4Track::IsSyncSample(MP4SampleId sampleId)
  859. {
  860. if (m_pStssCountProperty == NULL) {
  861. return true;
  862. }
  863. u_int32_t numStss = m_pStssCountProperty->GetValue();
  864. for (u_int32_t stssIndex = 0; stssIndex < numStss; stssIndex++) {
  865. MP4SampleId syncSampleId = 
  866. m_pStssSampleProperty->GetValue(stssIndex);
  867. if (sampleId == syncSampleId) {
  868. return true;
  869. if (sampleId < syncSampleId) {
  870. break;
  871. }
  872. }
  873. return false;
  874. }
  875. // N.B. "next" is inclusive of this sample id
  876. MP4SampleId MP4Track::GetNextSyncSample(MP4SampleId sampleId)
  877. {
  878. if (m_pStssCountProperty == NULL) {
  879. return sampleId;
  880. }
  881. u_int32_t numStss = m_pStssCountProperty->GetValue();
  882. for (u_int32_t stssIndex = 0; stssIndex < numStss; stssIndex++) {
  883. MP4SampleId syncSampleId = 
  884. m_pStssSampleProperty->GetValue(stssIndex);
  885. if (sampleId > syncSampleId) {
  886. continue;
  887. }
  888. return syncSampleId;
  889. }
  890. // LATER check stsh for alternate sample
  891. return MP4_INVALID_SAMPLE_ID;
  892. }
  893. void MP4Track::UpdateSyncSamples(MP4SampleId sampleId, bool isSyncSample)
  894. {
  895. if (isSyncSample) {
  896. // if stss atom exists, add entry
  897. if (m_pStssCountProperty) {
  898. m_pStssSampleProperty->AddValue(sampleId);
  899. m_pStssCountProperty->IncrementValue();
  900. } // else nothing to do (yet)
  901. } else { // !isSyncSample
  902. // if stss atom doesn't exist, create one
  903. if (m_pStssCountProperty == NULL) {
  904. MP4Atom* pStssAtom = AddAtom("trak.mdia.minf.stbl", "stss");
  905. pStssAtom->FindProperty(
  906. "stss.entryCount",
  907. (MP4Property**)&m_pStssCountProperty);
  908. pStssAtom->FindProperty(
  909. "stss.entries.sampleNumber",
  910. (MP4Property**)&m_pStssSampleProperty);
  911. // set values for all samples that came before this one
  912. for (MP4SampleId sid = 1; sid < sampleId; sid++) {
  913. m_pStssSampleProperty->AddValue(sid);
  914. m_pStssCountProperty->IncrementValue();
  915. }
  916. } // else nothing to do
  917. }
  918. }
  919. MP4Atom* MP4Track::AddAtom(char* parentName, char* childName)
  920. {
  921. MP4Atom* pChildAtom = MP4Atom::CreateAtom(childName);
  922. MP4Atom* pParentAtom = m_pTrakAtom->FindAtom(parentName);
  923. ASSERT(pParentAtom);
  924. pParentAtom->AddChildAtom(pChildAtom);
  925. pChildAtom->Generate();
  926. return pChildAtom;
  927. }
  928. u_int64_t MP4Track::GetDuration()
  929. {
  930. return m_pMediaDurationProperty->GetValue();
  931. }
  932. u_int32_t MP4Track::GetTimeScale()
  933. {
  934. return m_pTimeScaleProperty->GetValue();
  935. }
  936. void MP4Track::UpdateDurations(MP4Duration duration)
  937. {
  938. // update media, track, and movie durations
  939. m_pMediaDurationProperty->SetValue(
  940. m_pMediaDurationProperty->GetValue() + duration);
  941. MP4Duration movieDuration = ToMovieDuration(duration);
  942. m_pTrackDurationProperty->SetValue(
  943. m_pTrackDurationProperty->GetValue() + movieDuration);
  944. m_pFile->UpdateDuration(m_pTrackDurationProperty->GetValue());
  945. }
  946. MP4Duration MP4Track::ToMovieDuration(MP4Duration trackDuration)
  947. {
  948. return (trackDuration * m_pFile->GetTimeScale()) 
  949. / m_pTimeScaleProperty->GetValue();
  950. }
  951. void MP4Track::UpdateModificationTimes()
  952. {
  953. // update media and track modification times
  954. MP4Timestamp now = MP4GetAbsTimestamp();
  955. m_pMediaModificationProperty->SetValue(now);
  956. m_pTrackModificationProperty->SetValue(now);
  957. }
  958. u_int32_t MP4Track::GetNumberOfChunks()
  959. {
  960. return m_pChunkOffsetProperty->GetCount();
  961. }
  962. u_int32_t MP4Track::GetChunkStscIndex(MP4ChunkId chunkId)
  963. {
  964. u_int32_t stscIndex;
  965. u_int32_t numStscs = m_pStscCountProperty->GetValue();
  966. ASSERT(chunkId);
  967. ASSERT(numStscs > 0);
  968. for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
  969. if (chunkId < m_pStscFirstChunkProperty->GetValue(stscIndex)) {
  970. ASSERT(stscIndex != 0);
  971. break;
  972. }
  973. }
  974. return stscIndex - 1;
  975. }
  976. MP4Timestamp MP4Track::GetChunkTime(MP4ChunkId chunkId)
  977. {
  978. u_int32_t stscIndex = GetChunkStscIndex(chunkId);
  979. MP4ChunkId firstChunkId = 
  980. m_pStscFirstChunkProperty->GetValue(stscIndex);
  981. MP4SampleId firstSample = 
  982. m_pStscFirstSampleProperty->GetValue(stscIndex);
  983. u_int32_t samplesPerChunk = 
  984. m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
  985. MP4SampleId firstSampleInChunk = 
  986. firstSample + ((chunkId - firstChunkId) * samplesPerChunk);
  987. MP4Timestamp chunkTime;
  988. GetSampleTimes(firstSampleInChunk, &chunkTime, NULL);
  989. return chunkTime;
  990. }
  991. u_int32_t MP4Track::GetChunkSize(MP4ChunkId chunkId)
  992. {
  993. u_int32_t stscIndex = GetChunkStscIndex(chunkId);
  994. MP4ChunkId firstChunkId = 
  995. m_pStscFirstChunkProperty->GetValue(stscIndex);
  996. MP4SampleId firstSample = 
  997. m_pStscFirstSampleProperty->GetValue(stscIndex);
  998. u_int32_t samplesPerChunk = 
  999. m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
  1000. MP4SampleId firstSampleInChunk = 
  1001. firstSample + ((chunkId - firstChunkId) * samplesPerChunk);
  1002. // need cumulative sizes of samples in chunk 
  1003. u_int32_t chunkSize = 0;
  1004. for (u_int32_t i = 0; i < samplesPerChunk; i++) {
  1005. chunkSize += GetSampleSize(firstSampleInChunk + i);
  1006. }
  1007. return chunkSize;
  1008. }
  1009. void MP4Track::ReadChunk(MP4ChunkId chunkId, 
  1010. u_int8_t** ppChunk, u_int32_t* pChunkSize)
  1011. {
  1012. ASSERT(chunkId);
  1013. ASSERT(ppChunk);
  1014. ASSERT(pChunkSize);
  1015. u_int64_t chunkOffset = 
  1016. m_pChunkOffsetProperty->GetValue(chunkId - 1);
  1017. *pChunkSize = GetChunkSize(chunkId);
  1018. *ppChunk = (u_int8_t*)MP4Malloc(*pChunkSize);
  1019. VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
  1020. printf("ReadChunk: track %u id %u offset 0x"LLX" size %u (0x%x)n",
  1021. m_trackId, chunkId, chunkOffset, *pChunkSize, *pChunkSize));
  1022. u_int64_t oldPos = m_pFile->GetPosition(); // only used in mode == 'w'
  1023. try {
  1024. m_pFile->SetPosition(chunkOffset);
  1025. m_pFile->ReadBytes(*ppChunk, *pChunkSize);
  1026. }
  1027. catch (MP4Error* e) {
  1028. // let's not leak memory
  1029. MP4Free(*ppChunk);
  1030. *ppChunk = NULL;
  1031. if (m_pFile->GetMode() == 'w') {
  1032. m_pFile->SetPosition(oldPos);
  1033. }
  1034. throw e;
  1035. }
  1036. if (m_pFile->GetMode() == 'w') {
  1037. m_pFile->SetPosition(oldPos);
  1038. }
  1039. }
  1040. void MP4Track::RewriteChunk(MP4ChunkId chunkId, 
  1041. u_int8_t* pChunk, u_int32_t chunkSize)
  1042. {
  1043. u_int64_t chunkOffset = m_pFile->GetPosition();
  1044. m_pFile->WriteBytes(pChunk, chunkSize);
  1045. m_pChunkOffsetProperty->SetValue(chunkOffset, chunkId - 1);
  1046. VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
  1047. printf("RewriteChunk: track %u id %u offset 0x"LLX" size %u (0x%x)n",
  1048. m_trackId, chunkId, chunkOffset, chunkSize, chunkSize)); 
  1049. }
  1050. // map track type name aliases to official names
  1051. const char* MP4Track::NormalizeTrackType(const char* type)
  1052. {
  1053. if (!strcasecmp(type, "vide")
  1054.   || !strcasecmp(type, "video")
  1055.   || !strcasecmp(type, "mp4v")) {
  1056. return MP4_VIDEO_TRACK_TYPE;
  1057. }
  1058. if (!strcasecmp(type, "soun")
  1059.   || !strcasecmp(type, "sound")
  1060.   || !strcasecmp(type, "audio")
  1061.   || !strcasecmp(type, "mp4a")) {
  1062. return MP4_AUDIO_TRACK_TYPE;
  1063. }
  1064. if (!strcasecmp(type, "sdsm")
  1065.   || !strcasecmp(type, "scene")
  1066.   || !strcasecmp(type, "bifs")) {
  1067. return MP4_SCENE_TRACK_TYPE;
  1068. }
  1069. if (!strcasecmp(type, "odsm")
  1070.   || !strcasecmp(type, "od")) {
  1071. return MP4_OD_TRACK_TYPE;
  1072. }
  1073. return type;
  1074. }
  1075. bool MP4Track::InitEditListProperties()
  1076. {
  1077. m_pElstCountProperty = NULL;
  1078. m_pElstMediaTimeProperty = NULL;
  1079. m_pElstDurationProperty = NULL;
  1080. m_pElstRateProperty = NULL;
  1081. m_pElstReservedProperty = NULL;
  1082. MP4Atom* pElstAtom =
  1083. m_pTrakAtom->FindAtom("trak.edts.elst");
  1084. if (!pElstAtom) {
  1085. return false;
  1086. }
  1087. pElstAtom->FindProperty(
  1088. "elst.entryCount",
  1089. (MP4Property**)&m_pElstCountProperty);
  1090. pElstAtom->FindProperty(
  1091. "elst.entries.mediaTime",
  1092. (MP4Property**)&m_pElstMediaTimeProperty);
  1093. pElstAtom->FindProperty(
  1094. "elst.entries.segmentDuration",
  1095. (MP4Property**)&m_pElstDurationProperty);
  1096. pElstAtom->FindProperty(
  1097. "elst.entries.mediaRate",
  1098. (MP4Property**)&m_pElstRateProperty);
  1099. pElstAtom->FindProperty(
  1100. "elst.entries.reserved",
  1101. (MP4Property**)&m_pElstReservedProperty);
  1102. return m_pElstCountProperty
  1103. && m_pElstMediaTimeProperty
  1104. && m_pElstDurationProperty
  1105. && m_pElstRateProperty
  1106. && m_pElstReservedProperty;
  1107. }
  1108. MP4EditId MP4Track::AddEdit(MP4EditId editId)
  1109. {
  1110. if (!m_pElstCountProperty) {
  1111. m_pFile->AddDescendantAtoms(m_pTrakAtom, "edts.elst");
  1112. InitEditListProperties();
  1113. }
  1114. if (editId == MP4_INVALID_EDIT_ID) {
  1115. editId = m_pElstCountProperty->GetValue() + 1;
  1116. }
  1117. m_pElstMediaTimeProperty->InsertValue(0, editId - 1);
  1118. m_pElstDurationProperty->InsertValue(0, editId - 1);
  1119. m_pElstRateProperty->InsertValue(1, editId - 1);
  1120. m_pElstReservedProperty->InsertValue(0, editId - 1);
  1121. m_pElstCountProperty->IncrementValue();
  1122. return editId;
  1123. }
  1124. void MP4Track::DeleteEdit(MP4EditId editId)
  1125. {
  1126. if (editId == MP4_INVALID_EDIT_ID) {
  1127. throw new MP4Error("edit id can't be zero", 
  1128. "MP4Track::DeleteEdit");
  1129. }
  1130. if (!m_pElstCountProperty
  1131.   || m_pElstCountProperty->GetValue() == 0) {
  1132. throw new MP4Error("no edits exist", 
  1133. "MP4Track::DeleteEdit");
  1134. }
  1135. m_pElstMediaTimeProperty->DeleteValue(editId - 1);
  1136. m_pElstDurationProperty->DeleteValue(editId - 1);
  1137. m_pElstRateProperty->DeleteValue(editId - 1);
  1138. m_pElstReservedProperty->DeleteValue(editId - 1);
  1139. m_pElstCountProperty->IncrementValue(-1);
  1140. // clean up if last edit is deleted
  1141. if (m_pElstCountProperty->GetValue() == 0) {
  1142. m_pElstCountProperty = NULL;
  1143. m_pElstMediaTimeProperty = NULL;
  1144. m_pElstDurationProperty = NULL;
  1145. m_pElstRateProperty = NULL;
  1146. m_pElstReservedProperty = NULL;
  1147. m_pTrakAtom->DeleteChildAtom(
  1148. m_pTrakAtom->FindAtom("trak.edts"));
  1149. }
  1150. }
  1151. MP4Timestamp MP4Track::GetEditStart(
  1152. MP4EditId editId) 
  1153. {
  1154. if (editId == MP4_INVALID_EDIT_ID) {
  1155. return MP4_INVALID_TIMESTAMP;
  1156. } else if (editId == 1) {
  1157. return 0;
  1158. }
  1159. return (MP4Timestamp)GetEditTotalDuration(editId - 1);
  1160. }
  1161. MP4Duration MP4Track::GetEditTotalDuration(
  1162. MP4EditId editId)
  1163. {
  1164. u_int32_t numEdits = 0;
  1165. if (m_pElstCountProperty) {
  1166. numEdits = m_pElstCountProperty->GetValue();
  1167. }
  1168. if (editId == MP4_INVALID_EDIT_ID) {
  1169. editId = numEdits;
  1170. }
  1171. if (numEdits == 0 || editId > numEdits) {
  1172. return MP4_INVALID_DURATION;
  1173. }
  1174. MP4Duration totalDuration = 0;
  1175. for (MP4EditId eid = 1; eid <= editId; eid++) {
  1176. totalDuration += 
  1177. m_pElstDurationProperty->GetValue(eid - 1);
  1178. }
  1179. return totalDuration;
  1180. }
  1181. MP4SampleId MP4Track::GetSampleIdFromEditTime(
  1182. MP4Timestamp editWhen, 
  1183. MP4Timestamp* pStartTime, 
  1184. MP4Duration* pDuration)
  1185. {
  1186. MP4SampleId sampleId = MP4_INVALID_SAMPLE_ID;
  1187. u_int32_t numEdits = 0;
  1188. if (m_pElstCountProperty) {
  1189. numEdits = m_pElstCountProperty->GetValue();
  1190. }
  1191. if (numEdits) {
  1192. MP4Duration editElapsedDuration = 0;
  1193. for (MP4EditId editId = 1; editId <= numEdits; editId++) {
  1194. // remember edit segment's start time (in edit timeline)
  1195. MP4Timestamp editStartTime = 
  1196. (MP4Timestamp)editElapsedDuration;
  1197. // accumulate edit segment's duration
  1198. editElapsedDuration += 
  1199. m_pElstDurationProperty->GetValue(editId - 1);
  1200. // calculate difference between the specified edit time
  1201. // and the end of this edit segment
  1202. if (editElapsedDuration - editWhen <= 0) {
  1203. // the specified time has not yet been reached
  1204. continue;
  1205. }
  1206. // 'editWhen' is within this edit segment
  1207. // calculate the specified edit time
  1208. // relative to just this edit segment
  1209. MP4Duration editOffset =
  1210. editWhen - editStartTime;
  1211. // calculate the media (track) time that corresponds
  1212. // to the specified edit time based on the edit list
  1213. MP4Timestamp mediaWhen = 
  1214. m_pElstMediaTimeProperty->GetValue(editId - 1)
  1215. + editOffset;
  1216. // lookup the sample id for the media time
  1217. sampleId = GetSampleIdFromTime(mediaWhen, false);
  1218. // lookup the sample's media start time and duration
  1219. MP4Timestamp sampleStartTime;
  1220. MP4Duration sampleDuration;
  1221. GetSampleTimes(sampleId, &sampleStartTime, &sampleDuration);
  1222. // calculate the difference if any between when the sample
  1223. // would naturally start and when it starts in the edit timeline 
  1224. MP4Duration sampleStartOffset =
  1225. mediaWhen - sampleStartTime;
  1226. // calculate the start time for the sample in the edit time line
  1227. MP4Timestamp editSampleStartTime =
  1228. editWhen - MIN(editOffset, sampleStartOffset);
  1229. MP4Duration editSampleDuration = 0;
  1230. // calculate how long this sample lasts in the edit list timeline
  1231. if (m_pElstRateProperty->GetValue(editId - 1) == 0) {
  1232. // edit segment is a "dwell"
  1233. // so sample duration is that of the edit segment
  1234. editSampleDuration =
  1235. m_pElstDurationProperty->GetValue(editId - 1);
  1236. } else {
  1237. // begin with the natural sample duration
  1238. editSampleDuration = sampleDuration;
  1239. // now shorten that if the edit segment starts
  1240. // after the sample would naturally start 
  1241. if (editOffset < sampleStartOffset) {
  1242. editSampleDuration -= sampleStartOffset - editOffset;
  1243. }
  1244. // now shorten that if the edit segment ends
  1245. // before the sample would naturally end
  1246. if (editElapsedDuration 
  1247.   < editSampleStartTime + sampleDuration) {
  1248. editSampleDuration -= (editSampleStartTime + sampleDuration) 
  1249. - editElapsedDuration;
  1250. }
  1251. }
  1252. if (pStartTime) {
  1253. *pStartTime = editSampleStartTime;
  1254. }
  1255. if (pDuration) {
  1256. *pDuration = editSampleDuration;
  1257. }
  1258. VERBOSE_EDIT(m_pFile->GetVerbosity(),
  1259. printf("GetSampleIdFromEditTime: when %llu "
  1260. "sampleId %u start %llu duration %lldn", 
  1261. editWhen, sampleId, 
  1262. editSampleStartTime, editSampleDuration));
  1263. return sampleId;
  1264. }
  1265. throw new MP4Error("time out of range", 
  1266. "MP4Track::GetSampleIdFromEditTime");
  1267. } else { // no edit list
  1268. sampleId = GetSampleIdFromTime(editWhen, false);
  1269. if (pStartTime || pDuration) {
  1270. GetSampleTimes(sampleId, pStartTime, pDuration);
  1271. }
  1272. }
  1273. return sampleId;
  1274. }