📄 mp4track.cpp
字号:
}
}
m_pStszSampleCountProperty->IncrementValue();
}
u_int32_t MP4Track::GetAvgBitrate()
{
if (GetDuration() == 0) {
return 0;
}
u_int64_t durationSecs =
MP4ConvertTime(GetDuration(), GetTimeScale(), MP4_SECS_TIME_SCALE);
if (GetDuration() % GetTimeScale() != 0) {
durationSecs++;
}
return (GetTotalOfSampleSizes() * 8) / durationSecs;
}
u_int32_t MP4Track::GetMaxBitrate()
{
u_int32_t timeScale = GetTimeScale();
MP4SampleId numSamples = GetNumberOfSamples();
u_int32_t maxBytesPerSec = 0;
u_int32_t bytesThisSec = 0;
MP4Timestamp thisSec = 0;
for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
u_int32_t sampleSize;
MP4Timestamp sampleTime;
sampleSize = GetSampleSize(sid);
GetSampleTimes(sid, &sampleTime, NULL);
// sample counts for current second
if (sampleTime < thisSec + timeScale) {
bytesThisSec += sampleSize;
} else { // sample is in a future second
if (bytesThisSec > maxBytesPerSec) {
maxBytesPerSec = bytesThisSec;
}
thisSec = sampleTime - (sampleTime % timeScale);
bytesThisSec = sampleSize;
}
}
// last second (or partial second)
if (bytesThisSec > maxBytesPerSec) {
maxBytesPerSec = bytesThisSec;
}
return maxBytesPerSec * 8;
}
u_int32_t MP4Track::GetSampleStscIndex(MP4SampleId sampleId)
{
u_int32_t stscIndex;
u_int32_t numStscs = m_pStscCountProperty->GetValue();
if (numStscs == 0) {
throw new MP4Error("No data chunks exist", "GetSampleStscIndex");
}
for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
if (sampleId < m_pStscFirstSampleProperty->GetValue(stscIndex)) {
ASSERT(stscIndex != 0);
stscIndex -= 1;
break;
}
}
if (stscIndex == numStscs) {
ASSERT(stscIndex != 0);
stscIndex -= 1;
}
return stscIndex;
}
FILE* MP4Track::GetSampleFile(MP4SampleId sampleId)
{
u_int32_t stscIndex =
GetSampleStscIndex(sampleId);
u_int32_t stsdIndex =
m_pStscSampleDescrIndexProperty->GetValue(stscIndex);
// check if the answer will be the same as last time
if (m_lastStsdIndex && stsdIndex == m_lastStsdIndex) {
return m_lastSampleFile;
}
MP4Atom* pStsdAtom =
m_pTrakAtom->FindAtom("trak.mdia.minf.stbl.stsd");
ASSERT(pStsdAtom);
MP4Atom* pStsdEntryAtom =
pStsdAtom->GetChildAtom(stsdIndex - 1);
ASSERT(pStsdEntryAtom);
MP4Integer16Property* pDrefIndexProperty = NULL;
pStsdEntryAtom->FindProperty(
"*.dataReferenceIndex",
(MP4Property**)&pDrefIndexProperty);
if (pDrefIndexProperty == NULL) {
throw new MP4Error("invalid stsd entry", "GetSampleFile");
}
u_int32_t drefIndex =
pDrefIndexProperty->GetValue();
MP4Atom* pDrefAtom =
m_pTrakAtom->FindAtom("trak.mdia.minf.dinf.dref");
ASSERT(pDrefAtom);
MP4Atom* pUrlAtom =
pDrefAtom->GetChildAtom(drefIndex - 1);
ASSERT(pUrlAtom);
FILE* pFile;
if (pUrlAtom->GetFlags() & 1) {
pFile = NULL; // self-contained
} else {
#ifndef USE_FILE_CALLBACKS
MP4StringProperty* pLocationProperty = NULL;
pUrlAtom->FindProperty(
"*.location",
(MP4Property**)&pLocationProperty);
ASSERT(pLocationProperty);
const char* url = pLocationProperty->GetValue();
VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
printf("dref url = %s\n", url));
pFile = (FILE*)-1;
// attempt to open url if it's a file url
// currently this is the only thing we understand
if (!strncmp(url, "file:", 5)) {
const char* fileName = url + 5;
if (!strncmp(fileName, "//", 2)) {
fileName = strchr(fileName + 2, '/');
}
if (fileName) {
pFile = fopen(fileName, "rb");
if (!pFile) {
pFile = (FILE*)-1;
}
}
}
#else
throw new MP4Error(errno, "Function not supported when using callbacks", "GetSampleFile");
#endif
}
if (m_lastSampleFile) {
#ifndef USE_FILE_CALLBACKS
fclose(m_lastSampleFile);
#else
throw new MP4Error(errno, "Function not supported when using callbacks", "GetSampleFile");
#endif
}
// cache the answer
m_lastStsdIndex = stsdIndex;
m_lastSampleFile = pFile;
return pFile;
}
u_int64_t MP4Track::GetSampleFileOffset(MP4SampleId sampleId)
{
u_int32_t stscIndex =
GetSampleStscIndex(sampleId);
u_int32_t firstChunk =
m_pStscFirstChunkProperty->GetValue(stscIndex);
MP4SampleId firstSample =
m_pStscFirstSampleProperty->GetValue(stscIndex);
u_int32_t samplesPerChunk =
m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
MP4ChunkId chunkId = firstChunk +
((sampleId - firstSample) / samplesPerChunk);
u_int64_t chunkOffset = m_pChunkOffsetProperty->GetValue(chunkId - 1);
MP4SampleId firstSampleInChunk =
sampleId - ((sampleId - firstSample) % samplesPerChunk);
// need cumulative samples sizes from firstSample to sampleId - 1
u_int32_t sampleOffset = 0;
for (MP4SampleId i = firstSampleInChunk; i < sampleId; i++) {
sampleOffset += GetSampleSize(i);
}
return chunkOffset + sampleOffset;
}
void MP4Track::UpdateSampleToChunk(MP4SampleId sampleId,
MP4ChunkId chunkId, u_int32_t samplesPerChunk)
{
u_int32_t numStsc = m_pStscCountProperty->GetValue();
// if samplesPerChunk == samplesPerChunk of last entry
if (numStsc && samplesPerChunk ==
m_pStscSamplesPerChunkProperty->GetValue(numStsc-1)) {
// nothing to do
} else {
// add stsc entry
m_pStscFirstChunkProperty->AddValue(chunkId);
m_pStscSamplesPerChunkProperty->AddValue(samplesPerChunk);
m_pStscSampleDescrIndexProperty->AddValue(1);
m_pStscFirstSampleProperty->AddValue(sampleId - samplesPerChunk + 1);
m_pStscCountProperty->IncrementValue();
}
}
void MP4Track::UpdateChunkOffsets(u_int64_t chunkOffset)
{
if (m_pChunkOffsetProperty->GetType() == Integer32Property) {
((MP4Integer32Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
} else {
((MP4Integer64Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
}
m_pChunkCountProperty->IncrementValue();
}
MP4Duration MP4Track::GetFixedSampleDuration()
{
u_int32_t numStts = m_pSttsCountProperty->GetValue();
if (numStts == 0) {
return m_fixedSampleDuration;
}
if (numStts != 1) {
return MP4_INVALID_DURATION; // sample duration is not fixed
}
return m_pSttsSampleDeltaProperty->GetValue(0);
}
bool MP4Track::SetFixedSampleDuration(MP4Duration duration)
{
u_int32_t numStts = m_pSttsCountProperty->GetValue();
// setting this is only allowed before samples have been written
if (numStts != 0) {
return false;
}
m_fixedSampleDuration = duration;
return true;
}
void MP4Track::GetSampleTimes(MP4SampleId sampleId,
MP4Timestamp* pStartTime, MP4Duration* pDuration)
{
u_int32_t numStts = m_pSttsCountProperty->GetValue();
MP4SampleId sid = 1;
MP4Duration elapsed = 0;
for (u_int32_t sttsIndex = 0; sttsIndex < numStts; sttsIndex++) {
u_int32_t sampleCount =
m_pSttsSampleCountProperty->GetValue(sttsIndex);
u_int32_t sampleDelta =
m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
if (sampleId <= sid + sampleCount - 1) {
if (pStartTime) {
*pStartTime = (sampleId - sid);
*pStartTime *= sampleDelta;
*pStartTime += elapsed;
}
if (pDuration) {
*pDuration = sampleDelta;
}
return;
}
sid += sampleCount;
elapsed += sampleCount * sampleDelta;
}
throw new MP4Error("sample id out of range",
"MP4Track::GetSampleTimes");
}
MP4SampleId MP4Track::GetSampleIdFromTime(
MP4Timestamp when,
bool wantSyncSample)
{
u_int32_t numStts = m_pSttsCountProperty->GetValue();
MP4SampleId sid = 1;
MP4Duration elapsed = 0;
for (u_int32_t sttsIndex = 0; sttsIndex < numStts; sttsIndex++) {
u_int32_t sampleCount =
m_pSttsSampleCountProperty->GetValue(sttsIndex);
u_int32_t sampleDelta =
m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
if (sampleDelta == 0 && sttsIndex < numStts - 1) {
VERBOSE_READ(m_pFile->GetVerbosity(),
printf("Warning: Zero sample duration, stts entry %u\n",
sttsIndex));
}
MP4Duration d = when - elapsed;
if (d <= sampleCount * sampleDelta) {
MP4SampleId sampleId = sid;
if (sampleDelta) {
sampleId += (d / sampleDelta);
}
if (wantSyncSample) {
return GetNextSyncSample(sampleId);
}
return sampleId;
}
sid += sampleCount;
elapsed += sampleCount * sampleDelta;
}
throw new MP4Error("time out of range",
"MP4Track::GetSampleIdFromTime");
return 0; // satisfy MS compiler
}
void MP4Track::UpdateSampleTimes(MP4Duration duration)
{
u_int32_t numStts = m_pSttsCountProperty->GetValue();
// if duration == duration of last entry
if (numStts
&& duration == m_pSttsSampleDeltaProperty->GetValue(numStts-1)) {
// increment last entry sampleCount
m_pSttsSampleCountProperty->IncrementValue(1, numStts-1);
} else {
// add stts entry, sampleCount = 1, sampleDuration = duration
m_pSttsSampleCountProperty->AddValue(1);
m_pSttsSampleDeltaProperty->AddValue(duration);
m_pSttsCountProperty->IncrementValue();;
}
}
u_int32_t MP4Track::GetSampleCttsIndex(MP4SampleId sampleId,
MP4SampleId* pFirstSampleId)
{
u_int32_t numCtts = m_pCttsCountProperty->GetValue();
MP4SampleId sid = 1;
for (u_int32_t cttsIndex = 0; cttsIndex < numCtts; cttsIndex++) {
u_int32_t sampleCount =
m_pCttsSampleCountProperty->GetValue(cttsIndex);
if (sampleId <= sid + sampleCount - 1) {
if (pFirstSampleId) {
*pFirstSampleId = sid;
}
return cttsIndex;
}
sid += sampleCount;
}
throw new MP4Error("sample id out of range",
"MP4Track::GetSampleCttsIndex");
return 0; // satisfy MS compiler
}
MP4Duration MP4Track::GetSampleRenderingOffset(MP4SampleId sampleId)
{
if (m_pCttsCountProperty == NULL) {
return 0;
}
if (m_pCttsCountProperty->GetValue() == 0) {
return 0;
}
u_int32_t cttsIndex = GetSampleCttsIndex(sampleId);
return m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
}
void MP4Track::UpdateRenderingOffsets(MP4SampleId sampleId,
MP4Duration renderingOffset)
{
// if ctts atom doesn't exist
if (m_pCttsCountProperty == NULL) {
// no rendering offset, so nothing to do
if (renderingOffset == 0) {
return;
}
// else create a ctts atom
MP4Atom* pCttsAtom = AddAtom("trak.mdia.minf.stbl", "ctts");
// and get handles on the properties
pCttsAtom->FindProperty(
"ctts.entryCount",
(MP4Property**)&m_pCttsCountProperty);
pCttsAtom->FindProperty(
"ctts.entries.sampleCount",
(MP4Property**)&m_pCttsSampleCountProperty);
pCttsAtom->FindProperty(
"ctts.entries.sampleOffset",
(MP4Property**)&m_pCttsSampleOffsetProperty);
// if this is not the first sample
if (sampleId > 1) {
// add a ctts entry for all previous samples
// with rendering offset equal to zero
m_pCttsSampleCountProperty->AddValue(sampleId - 1);
m_pCttsSampleOffsetProperty->AddValue(0);
m_pCttsCountProperty->IncrementValue();;
}
}
// ctts atom exists (now)
u_int32_t numCtts = m_pCttsCountProperty->GetValue();
// if renderingOffset == renderingOffset of last entry
if (numCtts && renderingOffset
== m_pCttsSampleOffsetProperty->GetValue(numCtts-1)) {
// increment last entry sampleCount
m_pCttsSampleCountProperty->IncrementValue(1, numCtts-1);
} else {
// add ctts entry, sampleCount = 1, sampleOffset = renderingOffset
m_pCttsSampleCountProperty->AddValue(1);
m_pCttsSampleOffsetProperty->AddValue(renderingOffset);
m_pCttsCountProperty->IncrementValue();
}
}
void MP4Track::SetSampleRenderingOffset(MP4SampleId sampleId,
MP4Duration renderingOffset)
{
// check if any ctts entries exist
if (m_pCttsCountProperty == NULL
|| m_pCttsCountProperty->GetValue() == 0) {
// if not then Update routine can be used
// to create a ctts entry for samples before this one
// and a ctts entry for this sample
UpdateRenderingOffsets(sampleId, renderingOffset);
// but we also need a ctts entry
// for all samples after this one
u_int32_t afterSamples = GetNumberOfSamples() - sampleId;
if (afterSamples) {
m_pCttsSampleCountProperty->AddValue(afterSamples);
m_pCttsSampleOffsetProperty->AddValue(0);
m_pCttsCountProperty->IncrementValue();;
}
return;
}
MP4SampleId firstSampleId;
u_int32_t cttsIndex = GetSampleCttsIndex(sampleId, &firstSampleId);
// do nothing in the degenerate case
if (renderingOffset ==
m_pCttsSampleOffsetProperty->GetValue(cttsIndex)) {
return;
}
u_int32_t sampleCount =
m_pCttsSampleCountProperty->GetValue(cttsIndex);
// if this sample has it's own ctts entry
if (sampleCount == 1) {
// then just set the value,
// note we don't attempt to collapse entries
m_pCttsSampleOffsetProperty->SetValue(renderingOffset, cttsIndex);
return;
}
MP4SampleId lastSampleId = firstSampleId + sampleCount - 1;
// else we share this entry with other samples
// we need to insert our own entry
if (sampleId == firstSampleId) {
// our sample is the first one
m_pCttsSampleCountProperty->
InsertValue(1, cttsIndex);
m_pCttsSampleOffsetProperty->
InsertValue(renderingOffset, cttsIndex);
m_pCttsSampleCountProperty->
SetValue(sampleCount - 1, cttsIndex + 1);
m_pCttsCountProperty->IncrementValue();
} else if (sampleId == lastSampleId) {
// our sample is the last one
m_pCttsSampleCountProperty->
InsertValue(1, cttsIndex + 1);
m_pCttsSampleOffsetProperty->
InsertValue(renderingOffset, cttsIndex + 1);
m_pCttsSampleCountProperty->
SetValue(sampleCount - 1, cttsIndex);
m_pCttsCountProperty->IncrementValue();
} else {
// our sample is in the middle, UGH!
// insert our new entry
m_pCttsSampleCountProperty->
InsertValue(1, cttsIndex + 1);
m_pCttsSampleOffsetProperty->
InsertValue(renderingOffset, cttsIndex + 1);
// adjust count of previous entry
m_pCttsSampleCountProperty->
SetValue(sampleId - firstSampleId, cttsIndex);
// insert new entry for those samples beyond our sample
m_pCttsSampleCountProperty->
InsertValue(lastSampleId - sampleId, cttsIndex + 2);
u_int32_t oldRenderingOffset =
m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
m_pCttsSampleOffsetProperty->
InsertValue(oldRenderingOffset, cttsIndex + 2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -