📄 mp4track.cpp
字号:
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is MPEG4IP.
*
* The Initial Developer of the Original Code is Cisco Systems Inc.
* Portions created by Cisco Systems Inc. are
* Copyright (C) Cisco Systems Inc. 2001. All Rights Reserved.
*
* Contributor(s):
* Dave Mackie dmackie@cisco.com
* Alix Marchandise-Franquet alix@cisco.com
*/
#include "mp4common.h"
MP4Track::MP4Track(MP4File* pFile, MP4Atom* pTrakAtom)
{
m_pFile = pFile;
m_pTrakAtom = pTrakAtom;
m_lastStsdIndex = 0;
m_lastSampleFile = NULL;
m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
m_pCachedReadSample = NULL;
m_cachedReadSampleSize = 0;
m_writeSampleId = 1;
m_fixedSampleDuration = 0;
m_pChunkBuffer = NULL;
m_chunkBufferSize = 0;
m_chunkSamples = 0;
m_chunkDuration = 0;
m_samplesPerChunk = 0;
m_durationPerChunk = 0;
bool success = true;
MP4Integer32Property* pTrackIdProperty;
success &= m_pTrakAtom->FindProperty(
"trak.tkhd.trackId",
(MP4Property**)&pTrackIdProperty);
if (success) {
m_trackId = pTrackIdProperty->GetValue();
}
success &= m_pTrakAtom->FindProperty(
"trak.mdia.mdhd.timeScale",
(MP4Property**)&m_pTimeScaleProperty);
if (success) {
// default chunking is 1 second of samples
m_durationPerChunk = m_pTimeScaleProperty->GetValue();
}
success &= m_pTrakAtom->FindProperty(
"trak.tkhd.duration",
(MP4Property**)&m_pTrackDurationProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.mdhd.duration",
(MP4Property**)&m_pMediaDurationProperty);
success &= m_pTrakAtom->FindProperty(
"trak.tkhd.modificationTime",
(MP4Property**)&m_pTrackModificationProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.mdhd.modificationTime",
(MP4Property**)&m_pMediaModificationProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.hdlr.handlerType",
(MP4Property**)&m_pTypeProperty);
// get handles on sample size information
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsz.sampleSize",
(MP4Property**)&m_pStszFixedSampleSizeProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsz.sampleCount",
(MP4Property**)&m_pStszSampleCountProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsz.entries.sampleSize",
(MP4Property**)&m_pStszSampleSizeProperty);
// get handles on information needed to map sample id's to file offsets
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsc.entryCount",
(MP4Property**)&m_pStscCountProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsc.entries.firstChunk",
(MP4Property**)&m_pStscFirstChunkProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsc.entries.samplesPerChunk",
(MP4Property**)&m_pStscSamplesPerChunkProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsc.entries.sampleDescriptionIndex",
(MP4Property**)&m_pStscSampleDescrIndexProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsc.entries.firstSample",
(MP4Property**)&m_pStscFirstSampleProperty);
bool haveStco = m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stco.entryCount",
(MP4Property**)&m_pChunkCountProperty);
if (haveStco) {
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stco.entries.chunkOffset",
(MP4Property**)&m_pChunkOffsetProperty);
} else {
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.co64.entryCount",
(MP4Property**)&m_pChunkCountProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.co64.entries.chunkOffset",
(MP4Property**)&m_pChunkOffsetProperty);
}
// get handles on sample timing info
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stts.entryCount",
(MP4Property**)&m_pSttsCountProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stts.entries.sampleCount",
(MP4Property**)&m_pSttsSampleCountProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stts.entries.sampleDelta",
(MP4Property**)&m_pSttsSampleDeltaProperty);
// get handles on rendering offset info if it exists
m_pCttsCountProperty = NULL;
m_pCttsSampleCountProperty = NULL;
m_pCttsSampleOffsetProperty = NULL;
bool haveCtts = m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.ctts.entryCount",
(MP4Property**)&m_pCttsCountProperty);
if (haveCtts) {
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.ctts.entries.sampleCount",
(MP4Property**)&m_pCttsSampleCountProperty);
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.ctts.entries.sampleOffset",
(MP4Property**)&m_pCttsSampleOffsetProperty);
}
// get handles on sync sample info if it exists
m_pStssCountProperty = NULL;
m_pStssSampleProperty = NULL;
bool haveStss = m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stss.entryCount",
(MP4Property**)&m_pStssCountProperty);
if (haveStss) {
success &= m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stss.entries.sampleNumber",
(MP4Property**)&m_pStssSampleProperty);
}
// edit list
InitEditListProperties();
// was everything found?
if (!success) {
throw new MP4Error("invalid track", "MP4Track::MP4Track");
}
}
MP4Track::~MP4Track()
{
MP4Free(m_pCachedReadSample);
MP4Free(m_pChunkBuffer);
}
const char* MP4Track::GetType()
{
return m_pTypeProperty->GetValue();
}
void MP4Track::SetType(const char* type)
{
m_pTypeProperty->SetValue(NormalizeTrackType(type));
}
void MP4Track::ReadSample(
MP4SampleId sampleId,
u_int8_t** ppBytes,
u_int32_t* pNumBytes,
MP4Timestamp* pStartTime,
MP4Duration* pDuration,
MP4Duration* pRenderingOffset,
bool* pIsSyncSample)
{
if (sampleId == MP4_INVALID_SAMPLE_ID) {
throw new MP4Error("sample id can't be zero",
"MP4Track::ReadSample");
}
// handle unusual case of wanting to read a sample
// that is still sitting in the write chunk buffer
if (m_pChunkBuffer && sampleId >= m_writeSampleId - m_chunkSamples) {
WriteChunkBuffer();
}
FILE* pFile = GetSampleFile(sampleId);
if (pFile == (FILE*)-1) {
throw new MP4Error("sample is located in an inaccessible file",
"MP4Track::ReadSample");
}
u_int64_t fileOffset = GetSampleFileOffset(sampleId);
u_int32_t sampleSize = GetSampleSize(sampleId);
if (*ppBytes != NULL && *pNumBytes < sampleSize) {
throw new MP4Error("sample buffer is too small",
"MP4Track::ReadSample");
}
*pNumBytes = sampleSize;
VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
printf("ReadSample: track %u id %u offset 0x"LLX" size %u (0x%x)\n",
m_trackId, sampleId, fileOffset, *pNumBytes, *pNumBytes));
bool bufferMalloc = false;
if (*ppBytes == NULL) {
*ppBytes = (u_int8_t*)MP4Malloc(*pNumBytes);
bufferMalloc = true;
}
u_int64_t oldPos = m_pFile->GetPosition(pFile); // only used in mode == 'w'
try {
m_pFile->SetPosition(fileOffset, pFile);
m_pFile->ReadBytes(*ppBytes, *pNumBytes, pFile);
if (pStartTime || pDuration) {
GetSampleTimes(sampleId, pStartTime, pDuration);
VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
printf("ReadSample: start "LLU" duration "LLD"\n",
(pStartTime ? *pStartTime : 0),
(pDuration ? *pDuration : 0)));
}
if (pRenderingOffset) {
*pRenderingOffset = GetSampleRenderingOffset(sampleId);
VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
printf("ReadSample: renderingOffset "LLD"\n",
*pRenderingOffset));
}
if (pIsSyncSample) {
*pIsSyncSample = IsSyncSample(sampleId);
VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
printf("ReadSample: isSyncSample %u\n",
*pIsSyncSample));
}
}
catch (MP4Error* e) {
if (bufferMalloc) {
// let's not leak memory
MP4Free(*ppBytes);
*ppBytes = NULL;
}
if (m_pFile->GetMode() == 'w') {
m_pFile->SetPosition(oldPos, pFile);
}
throw e;
}
if (m_pFile->GetMode() == 'w') {
m_pFile->SetPosition(oldPos, pFile);
}
}
void MP4Track::ReadSampleFragment(
MP4SampleId sampleId,
u_int32_t sampleOffset,
u_int16_t sampleLength,
u_int8_t* pDest)
{
if (sampleId == MP4_INVALID_SAMPLE_ID) {
throw new MP4Error("invalid sample id",
"MP4Track::ReadSampleFragment");
}
if (sampleId != m_cachedReadSampleId) {
MP4Free(m_pCachedReadSample);
m_pCachedReadSample = NULL;
m_cachedReadSampleSize = 0;
m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
ReadSample(
sampleId,
&m_pCachedReadSample,
&m_cachedReadSampleSize);
m_cachedReadSampleId = sampleId;
}
if (sampleOffset + sampleLength > m_cachedReadSampleSize) {
throw new MP4Error("offset and/or length are too large",
"MP4Track::ReadSampleFragment");
}
memcpy(pDest, &m_pCachedReadSample[sampleOffset], sampleLength);
}
void MP4Track::WriteSample(
const u_int8_t* pBytes,
u_int32_t numBytes,
MP4Duration duration,
MP4Duration renderingOffset,
bool isSyncSample)
{
VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
printf("WriteSample: track %u id %u size %u (0x%x) ",
m_trackId, m_writeSampleId, numBytes, numBytes));
if (pBytes == NULL && numBytes > 0) {
throw new MP4Error("no sample data", "MP4WriteSample");
}
if (duration == MP4_INVALID_DURATION) {
duration = GetFixedSampleDuration();
}
VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
printf("duration "LLU"\n", duration));
// append sample bytes to chunk buffer
m_pChunkBuffer = (u_int8_t*)MP4Realloc(m_pChunkBuffer,
m_chunkBufferSize + numBytes);
memcpy(&m_pChunkBuffer[m_chunkBufferSize], pBytes, numBytes);
m_chunkBufferSize += numBytes;
m_chunkSamples++;
m_chunkDuration += duration;
UpdateSampleSizes(m_writeSampleId, numBytes);
UpdateSampleTimes(duration);
UpdateRenderingOffsets(m_writeSampleId, renderingOffset);
UpdateSyncSamples(m_writeSampleId, isSyncSample);
if (IsChunkFull(m_writeSampleId)) {
WriteChunkBuffer();
}
UpdateDurations(duration);
UpdateModificationTimes();
m_writeSampleId++;
}
void MP4Track::WriteChunkBuffer()
{
if (m_chunkBufferSize == 0) {
return;
}
u_int64_t chunkOffset = m_pFile->GetPosition();
// write chunk buffer
m_pFile->WriteBytes(m_pChunkBuffer, m_chunkBufferSize);
VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
printf("WriteChunk: track %u offset 0x"LLX" size %u (0x%x) numSamples %u\n",
m_trackId, chunkOffset, m_chunkBufferSize,
m_chunkBufferSize, m_chunkSamples));
UpdateSampleToChunk(m_writeSampleId,
m_pChunkCountProperty->GetValue() + 1,
m_chunkSamples);
UpdateChunkOffsets(chunkOffset);
// clean up chunk buffer
MP4Free(m_pChunkBuffer);
m_pChunkBuffer = NULL;
m_chunkBufferSize = 0;
m_chunkSamples = 0;
m_chunkDuration = 0;
}
void MP4Track::FinishWrite()
{
// write out any remaining samples in chunk buffer
WriteChunkBuffer();
// record buffer size and bitrates
MP4BitfieldProperty* pBufferSizeProperty;
if (m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.bufferSizeDB",
(MP4Property**)&pBufferSizeProperty)) {
pBufferSizeProperty->SetValue(GetMaxSampleSize());
}
MP4Integer32Property* pBitrateProperty;
if (m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.maxBitrate",
(MP4Property**)&pBitrateProperty)) {
pBitrateProperty->SetValue(GetMaxBitrate());
}
if (m_pTrakAtom->FindProperty(
"trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate",
(MP4Property**)&pBitrateProperty)) {
pBitrateProperty->SetValue(GetAvgBitrate());
}
}
bool MP4Track::IsChunkFull(MP4SampleId sampleId)
{
if (m_samplesPerChunk) {
return m_chunkSamples >= m_samplesPerChunk;
}
ASSERT(m_durationPerChunk);
return m_chunkDuration >= m_durationPerChunk;
}
u_int32_t MP4Track::GetNumberOfSamples()
{
return m_pStszSampleCountProperty->GetValue();
}
u_int32_t MP4Track::GetSampleSize(MP4SampleId sampleId)
{
u_int32_t fixedSampleSize =
m_pStszFixedSampleSizeProperty->GetValue();
if (fixedSampleSize != 0) {
return fixedSampleSize;
}
return m_pStszSampleSizeProperty->GetValue(sampleId - 1);
}
u_int32_t MP4Track::GetMaxSampleSize()
{
u_int32_t fixedSampleSize =
m_pStszFixedSampleSizeProperty->GetValue();
if (fixedSampleSize != 0) {
return fixedSampleSize;
}
u_int32_t maxSampleSize = 0;
u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
u_int32_t sampleSize =
m_pStszSampleSizeProperty->GetValue(sid - 1);
if (sampleSize > maxSampleSize) {
maxSampleSize = sampleSize;
}
}
return maxSampleSize;
}
u_int64_t MP4Track::GetTotalOfSampleSizes()
{
u_int32_t fixedSampleSize =
m_pStszFixedSampleSizeProperty->GetValue();
// if fixed sample size, just need to multiply by number of samples
if (fixedSampleSize != 0) {
return fixedSampleSize * GetNumberOfSamples();
}
// else non-fixed sample size, sum them
u_int64_t totalSampleSizes = 0;
u_int32_t numSamples = m_pStszSampleSizeProperty->GetCount();
for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
u_int32_t sampleSize =
m_pStszSampleSizeProperty->GetValue(sid - 1);
totalSampleSizes += sampleSize;
}
return totalSampleSizes;
}
void MP4Track::UpdateSampleSizes(MP4SampleId sampleId, u_int32_t numBytes)
{
// for first sample
if (sampleId == 1) {
if (numBytes > 0) {
// presume sample size is fixed
m_pStszFixedSampleSizeProperty->SetValue(numBytes);
} else {
// special case of first sample is zero bytes in length
// leave m_pStszFixedSampleSizeProperty at 0
// start recording variable sample sizes
m_pStszSampleSizeProperty->AddValue(0);
}
} else { // sampleId > 1
u_int32_t fixedSampleSize =
m_pStszFixedSampleSizeProperty->GetValue();
if (fixedSampleSize == 0 || numBytes != fixedSampleSize) {
// sample size is not fixed
if (fixedSampleSize) {
// need to clear fixed sample size
m_pStszFixedSampleSizeProperty->SetValue(0);
// and create sizes for all previous samples
for (MP4SampleId sid = 1; sid < sampleId; sid++) {
m_pStszSampleSizeProperty->AddValue(fixedSampleSize);
}
}
// add size value for this sample
m_pStszSampleSizeProperty->AddValue(numBytes);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -