📄 mp4track.cpp
字号:
m_pCttsCountProperty->IncrementValue(2);
}
}
bool MP4Track::IsSyncSample(MP4SampleId sampleId)
{
if (m_pStssCountProperty == NULL) {
return true;
}
u_int32_t numStss = m_pStssCountProperty->GetValue();
for (u_int32_t stssIndex = 0; stssIndex < numStss; stssIndex++) {
MP4SampleId syncSampleId =
m_pStssSampleProperty->GetValue(stssIndex);
if (sampleId == syncSampleId) {
return true;
}
if (sampleId < syncSampleId) {
break;
}
}
return false;
}
// N.B. "next" is inclusive of this sample id
MP4SampleId MP4Track::GetNextSyncSample(MP4SampleId sampleId)
{
if (m_pStssCountProperty == NULL) {
return sampleId;
}
u_int32_t numStss = m_pStssCountProperty->GetValue();
for (u_int32_t stssIndex = 0; stssIndex < numStss; stssIndex++) {
MP4SampleId syncSampleId =
m_pStssSampleProperty->GetValue(stssIndex);
if (sampleId > syncSampleId) {
continue;
}
return syncSampleId;
}
// LATER check stsh for alternate sample
return MP4_INVALID_SAMPLE_ID;
}
void MP4Track::UpdateSyncSamples(MP4SampleId sampleId, bool isSyncSample)
{
if (isSyncSample) {
// if stss atom exists, add entry
if (m_pStssCountProperty) {
m_pStssSampleProperty->AddValue(sampleId);
m_pStssCountProperty->IncrementValue();
} // else nothing to do (yet)
} else { // !isSyncSample
// if stss atom doesn't exist, create one
if (m_pStssCountProperty == NULL) {
MP4Atom* pStssAtom = AddAtom("trak.mdia.minf.stbl", "stss");
pStssAtom->FindProperty(
"stss.entryCount",
(MP4Property**)&m_pStssCountProperty);
pStssAtom->FindProperty(
"stss.entries.sampleNumber",
(MP4Property**)&m_pStssSampleProperty);
// set values for all samples that came before this one
for (MP4SampleId sid = 1; sid < sampleId; sid++) {
m_pStssSampleProperty->AddValue(sid);
m_pStssCountProperty->IncrementValue();
}
} // else nothing to do
}
}
MP4Atom* MP4Track::AddAtom(char* parentName, char* childName)
{
MP4Atom* pChildAtom = MP4Atom::CreateAtom(childName);
MP4Atom* pParentAtom = m_pTrakAtom->FindAtom(parentName);
ASSERT(pParentAtom);
pParentAtom->AddChildAtom(pChildAtom);
pChildAtom->Generate();
return pChildAtom;
}
u_int64_t MP4Track::GetDuration()
{
return m_pMediaDurationProperty->GetValue();
}
u_int32_t MP4Track::GetTimeScale()
{
return m_pTimeScaleProperty->GetValue();
}
void MP4Track::UpdateDurations(MP4Duration duration)
{
// update media, track, and movie durations
m_pMediaDurationProperty->SetValue(
m_pMediaDurationProperty->GetValue() + duration);
MP4Duration movieDuration = ToMovieDuration(duration);
m_pTrackDurationProperty->SetValue(
m_pTrackDurationProperty->GetValue() + movieDuration);
m_pFile->UpdateDuration(m_pTrackDurationProperty->GetValue());
}
MP4Duration MP4Track::ToMovieDuration(MP4Duration trackDuration)
{
return (trackDuration * m_pFile->GetTimeScale())
/ m_pTimeScaleProperty->GetValue();
}
void MP4Track::UpdateModificationTimes()
{
// update media and track modification times
MP4Timestamp now = MP4GetAbsTimestamp();
m_pMediaModificationProperty->SetValue(now);
m_pTrackModificationProperty->SetValue(now);
}
u_int32_t MP4Track::GetNumberOfChunks()
{
return m_pChunkOffsetProperty->GetCount();
}
u_int32_t MP4Track::GetChunkStscIndex(MP4ChunkId chunkId)
{
u_int32_t stscIndex;
u_int32_t numStscs = m_pStscCountProperty->GetValue();
ASSERT(chunkId);
ASSERT(numStscs > 0);
for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
if (chunkId < m_pStscFirstChunkProperty->GetValue(stscIndex)) {
ASSERT(stscIndex != 0);
break;
}
}
return stscIndex - 1;
}
MP4Timestamp MP4Track::GetChunkTime(MP4ChunkId chunkId)
{
u_int32_t stscIndex = GetChunkStscIndex(chunkId);
MP4ChunkId firstChunkId =
m_pStscFirstChunkProperty->GetValue(stscIndex);
MP4SampleId firstSample =
m_pStscFirstSampleProperty->GetValue(stscIndex);
u_int32_t samplesPerChunk =
m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
MP4SampleId firstSampleInChunk =
firstSample + ((chunkId - firstChunkId) * samplesPerChunk);
MP4Timestamp chunkTime;
GetSampleTimes(firstSampleInChunk, &chunkTime, NULL);
return chunkTime;
}
u_int32_t MP4Track::GetChunkSize(MP4ChunkId chunkId)
{
u_int32_t stscIndex = GetChunkStscIndex(chunkId);
MP4ChunkId firstChunkId =
m_pStscFirstChunkProperty->GetValue(stscIndex);
MP4SampleId firstSample =
m_pStscFirstSampleProperty->GetValue(stscIndex);
u_int32_t samplesPerChunk =
m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
MP4SampleId firstSampleInChunk =
firstSample + ((chunkId - firstChunkId) * samplesPerChunk);
// need cumulative sizes of samples in chunk
u_int32_t chunkSize = 0;
for (u_int32_t i = 0; i < samplesPerChunk; i++) {
chunkSize += GetSampleSize(firstSampleInChunk + i);
}
return chunkSize;
}
void MP4Track::ReadChunk(MP4ChunkId chunkId,
u_int8_t** ppChunk, u_int32_t* pChunkSize)
{
ASSERT(chunkId);
ASSERT(ppChunk);
ASSERT(pChunkSize);
u_int64_t chunkOffset =
m_pChunkOffsetProperty->GetValue(chunkId - 1);
*pChunkSize = GetChunkSize(chunkId);
*ppChunk = (u_int8_t*)MP4Malloc(*pChunkSize);
VERBOSE_READ_SAMPLE(m_pFile->GetVerbosity(),
printf("ReadChunk: track %u id %u offset 0x"LLX" size %u (0x%x)\n",
m_trackId, chunkId, chunkOffset, *pChunkSize, *pChunkSize));
u_int64_t oldPos = m_pFile->GetPosition(); // only used in mode == 'w'
try {
m_pFile->SetPosition(chunkOffset);
m_pFile->ReadBytes(*ppChunk, *pChunkSize);
}
catch (MP4Error* e) {
// let's not leak memory
MP4Free(*ppChunk);
*ppChunk = NULL;
if (m_pFile->GetMode() == 'w') {
m_pFile->SetPosition(oldPos);
}
throw e;
}
if (m_pFile->GetMode() == 'w') {
m_pFile->SetPosition(oldPos);
}
}
void MP4Track::RewriteChunk(MP4ChunkId chunkId,
u_int8_t* pChunk, u_int32_t chunkSize)
{
u_int64_t chunkOffset = m_pFile->GetPosition();
m_pFile->WriteBytes(pChunk, chunkSize);
m_pChunkOffsetProperty->SetValue(chunkOffset, chunkId - 1);
VERBOSE_WRITE_SAMPLE(m_pFile->GetVerbosity(),
printf("RewriteChunk: track %u id %u offset 0x"LLX" size %u (0x%x)\n",
m_trackId, chunkId, chunkOffset, chunkSize, chunkSize));
}
// map track type name aliases to official names
const char* MP4Track::NormalizeTrackType(const char* type)
{
if (!strcasecmp(type, "vide")
|| !strcasecmp(type, "video")
|| !strcasecmp(type, "mp4v")
|| !strcasecmp(type, "encv")) {
return MP4_VIDEO_TRACK_TYPE;
}
if (!strcasecmp(type, "soun")
|| !strcasecmp(type, "sound")
|| !strcasecmp(type, "audio")
|| !strcasecmp(type, "enca") // ismacrypt
|| !strcasecmp(type, "mp4a")) {
return MP4_AUDIO_TRACK_TYPE;
}
if (!strcasecmp(type, "sdsm")
|| !strcasecmp(type, "scene")
|| !strcasecmp(type, "bifs")) {
return MP4_SCENE_TRACK_TYPE;
}
if (!strcasecmp(type, "odsm")
|| !strcasecmp(type, "od")) {
return MP4_OD_TRACK_TYPE;
}
return type;
}
bool MP4Track::InitEditListProperties()
{
m_pElstCountProperty = NULL;
m_pElstMediaTimeProperty = NULL;
m_pElstDurationProperty = NULL;
m_pElstRateProperty = NULL;
m_pElstReservedProperty = NULL;
MP4Atom* pElstAtom =
m_pTrakAtom->FindAtom("trak.edts.elst");
if (!pElstAtom) {
return false;
}
pElstAtom->FindProperty(
"elst.entryCount",
(MP4Property**)&m_pElstCountProperty);
pElstAtom->FindProperty(
"elst.entries.mediaTime",
(MP4Property**)&m_pElstMediaTimeProperty);
pElstAtom->FindProperty(
"elst.entries.segmentDuration",
(MP4Property**)&m_pElstDurationProperty);
pElstAtom->FindProperty(
"elst.entries.mediaRate",
(MP4Property**)&m_pElstRateProperty);
pElstAtom->FindProperty(
"elst.entries.reserved",
(MP4Property**)&m_pElstReservedProperty);
return m_pElstCountProperty
&& m_pElstMediaTimeProperty
&& m_pElstDurationProperty
&& m_pElstRateProperty
&& m_pElstReservedProperty;
}
MP4EditId MP4Track::AddEdit(MP4EditId editId)
{
if (!m_pElstCountProperty) {
m_pFile->AddDescendantAtoms(m_pTrakAtom, "edts.elst");
InitEditListProperties();
}
if (editId == MP4_INVALID_EDIT_ID) {
editId = m_pElstCountProperty->GetValue() + 1;
}
m_pElstMediaTimeProperty->InsertValue(0, editId - 1);
m_pElstDurationProperty->InsertValue(0, editId - 1);
m_pElstRateProperty->InsertValue(1, editId - 1);
m_pElstReservedProperty->InsertValue(0, editId - 1);
m_pElstCountProperty->IncrementValue();
return editId;
}
void MP4Track::DeleteEdit(MP4EditId editId)
{
if (editId == MP4_INVALID_EDIT_ID) {
throw new MP4Error("edit id can't be zero",
"MP4Track::DeleteEdit");
}
if (!m_pElstCountProperty
|| m_pElstCountProperty->GetValue() == 0) {
throw new MP4Error("no edits exist",
"MP4Track::DeleteEdit");
}
m_pElstMediaTimeProperty->DeleteValue(editId - 1);
m_pElstDurationProperty->DeleteValue(editId - 1);
m_pElstRateProperty->DeleteValue(editId - 1);
m_pElstReservedProperty->DeleteValue(editId - 1);
m_pElstCountProperty->IncrementValue(-1);
// clean up if last edit is deleted
if (m_pElstCountProperty->GetValue() == 0) {
m_pElstCountProperty = NULL;
m_pElstMediaTimeProperty = NULL;
m_pElstDurationProperty = NULL;
m_pElstRateProperty = NULL;
m_pElstReservedProperty = NULL;
m_pTrakAtom->DeleteChildAtom(
m_pTrakAtom->FindAtom("trak.edts"));
}
}
MP4Timestamp MP4Track::GetEditStart(
MP4EditId editId)
{
if (editId == MP4_INVALID_EDIT_ID) {
return MP4_INVALID_TIMESTAMP;
} else if (editId == 1) {
return 0;
}
return (MP4Timestamp)GetEditTotalDuration(editId - 1);
}
MP4Duration MP4Track::GetEditTotalDuration(
MP4EditId editId)
{
u_int32_t numEdits = 0;
if (m_pElstCountProperty) {
numEdits = m_pElstCountProperty->GetValue();
}
if (editId == MP4_INVALID_EDIT_ID) {
editId = numEdits;
}
if (numEdits == 0 || editId > numEdits) {
return MP4_INVALID_DURATION;
}
MP4Duration totalDuration = 0;
for (MP4EditId eid = 1; eid <= editId; eid++) {
totalDuration +=
m_pElstDurationProperty->GetValue(eid - 1);
}
return totalDuration;
}
MP4SampleId MP4Track::GetSampleIdFromEditTime(
MP4Timestamp editWhen,
MP4Timestamp* pStartTime,
MP4Duration* pDuration)
{
MP4SampleId sampleId = MP4_INVALID_SAMPLE_ID;
u_int32_t numEdits = 0;
if (m_pElstCountProperty) {
numEdits = m_pElstCountProperty->GetValue();
}
if (numEdits) {
MP4Duration editElapsedDuration = 0;
for (MP4EditId editId = 1; editId <= numEdits; editId++) {
// remember edit segment's start time (in edit timeline)
MP4Timestamp editStartTime =
(MP4Timestamp)editElapsedDuration;
// accumulate edit segment's duration
editElapsedDuration +=
m_pElstDurationProperty->GetValue(editId - 1);
// calculate difference between the specified edit time
// and the end of this edit segment
if (editElapsedDuration - editWhen <= 0) {
// the specified time has not yet been reached
continue;
}
// 'editWhen' is within this edit segment
// calculate the specified edit time
// relative to just this edit segment
MP4Duration editOffset =
editWhen - editStartTime;
// calculate the media (track) time that corresponds
// to the specified edit time based on the edit list
MP4Timestamp mediaWhen =
m_pElstMediaTimeProperty->GetValue(editId - 1)
+ editOffset;
// lookup the sample id for the media time
sampleId = GetSampleIdFromTime(mediaWhen, false);
// lookup the sample's media start time and duration
MP4Timestamp sampleStartTime;
MP4Duration sampleDuration;
GetSampleTimes(sampleId, &sampleStartTime, &sampleDuration);
// calculate the difference if any between when the sample
// would naturally start and when it starts in the edit timeline
MP4Duration sampleStartOffset =
mediaWhen - sampleStartTime;
// calculate the start time for the sample in the edit time line
MP4Timestamp editSampleStartTime =
editWhen - MIN(editOffset, sampleStartOffset);
MP4Duration editSampleDuration = 0;
// calculate how long this sample lasts in the edit list timeline
if (m_pElstRateProperty->GetValue(editId - 1) == 0) {
// edit segment is a "dwell"
// so sample duration is that of the edit segment
editSampleDuration =
m_pElstDurationProperty->GetValue(editId - 1);
} else {
// begin with the natural sample duration
editSampleDuration = sampleDuration;
// now shorten that if the edit segment starts
// after the sample would naturally start
if (editOffset < sampleStartOffset) {
editSampleDuration -= sampleStartOffset - editOffset;
}
// now shorten that if the edit segment ends
// before the sample would naturally end
if (editElapsedDuration
< editSampleStartTime + sampleDuration) {
editSampleDuration -= (editSampleStartTime + sampleDuration)
- editElapsedDuration;
}
}
if (pStartTime) {
*pStartTime = editSampleStartTime;
}
if (pDuration) {
*pDuration = editSampleDuration;
}
VERBOSE_EDIT(m_pFile->GetVerbosity(),
printf("GetSampleIdFromEditTime: when %llu "
"sampleId %u start %llu duration %lld\n",
editWhen, sampleId,
editSampleStartTime, editSampleDuration));
return sampleId;
}
throw new MP4Error("time out of range",
"MP4Track::GetSampleIdFromEditTime");
} else { // no edit list
sampleId = GetSampleIdFromTime(editWhen, false);
if (pStartTime || pDuration) {
GetSampleTimes(sampleId, pStartTime, pDuration);
}
}
return sampleId;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -