Support for TREX boxes in MP4::Stream parser and all binaries that use it

This commit is contained in:
Thulinma 2024-04-22 15:02:11 +02:00
parent 57655f1b21
commit 0e26d49bec
7 changed files with 71 additions and 25 deletions

View file

@ -125,7 +125,7 @@ namespace MP4{
if (getSampleInformationCount() < no + 1){setInt32(no + 1, 4);} if (getSampleInformationCount() < no + 1){setInt32(no + 1, 4);}
} }
trunSampleInformation TRUN::getSampleInformation(uint32_t no, TFHD * tfhd) const{ trunSampleInformation TRUN::getSampleInformation(uint32_t no, TFHD * tfhd, TREX * trex) const{
trunSampleInformation ret; trunSampleInformation ret;
ret.sampleDuration = 0; ret.sampleDuration = 0;
ret.sampleSize = 0; ret.sampleSize = 0;
@ -148,22 +148,34 @@ namespace MP4{
if (flags & trunsampleDuration){ if (flags & trunsampleDuration){
ret.sampleDuration = getInt32(offset + no * sampInfoSize + innerOffset); ret.sampleDuration = getInt32(offset + no * sampInfoSize + innerOffset);
innerOffset += 4; innerOffset += 4;
}else if (tfhd){ }else if (tfhd && (tfhd->getFlags() & tfhdSampleDura)){
ret.sampleDuration = tfhd->getDefaultSampleDuration(); ret.sampleDuration = tfhd->getDefaultSampleDuration();
}else if (trex){
ret.sampleDuration = trex->getDefaultSampleDuration();
}else{
WARN_MSG("Could not get sample duration from TRUN, TFHD or TREX box(es)!");
} }
if (flags & trunsampleSize){ if (flags & trunsampleSize){
ret.sampleSize = getInt32(offset + no * sampInfoSize + innerOffset); ret.sampleSize = getInt32(offset + no * sampInfoSize + innerOffset);
innerOffset += 4; innerOffset += 4;
}else if (tfhd){ }else if (tfhd && (tfhd->getFlags() & tfhdSampleSize)){
ret.sampleSize = tfhd->getDefaultSampleSize(); ret.sampleSize = tfhd->getDefaultSampleSize();
}else if (trex){
ret.sampleSize = trex->getDefaultSampleSize();
}else{
WARN_MSG("Could not get sample size from TRUN, TFHD or TREX box(es)!");
} }
if (flags & trunsampleFlags){ if (flags & trunsampleFlags){
ret.sampleFlags = getInt32(offset + no * sampInfoSize + innerOffset); ret.sampleFlags = getInt32(offset + no * sampInfoSize + innerOffset);
innerOffset += 4; innerOffset += 4;
}else if ((flags & trunfirstSampleFlags) && !no){ }else if ((flags & trunfirstSampleFlags) && !no){
ret.sampleFlags = getFirstSampleFlags(); ret.sampleFlags = getFirstSampleFlags();
}else if (tfhd){ }else if (tfhd && (tfhd->getFlags() & tfhdSampleFlag)){
ret.sampleFlags = tfhd->getDefaultSampleFlags(); ret.sampleFlags = tfhd->getDefaultSampleFlags();
}else if (trex){
ret.sampleFlags = trex->getDefaultSampleFlags();
}else{
WARN_MSG("Could not get sample flags from TRUN, TFHD or TREX box(es)!");
} }
if (flags & trunsampleOffsets){ if (flags & trunsampleOffsets){
ret.sampleOffset = getInt32(offset + no * sampInfoSize + innerOffset); ret.sampleOffset = getInt32(offset + no * sampInfoSize + innerOffset);

View file

@ -69,6 +69,23 @@ namespace MP4{
std::string toPrettyString(uint32_t indent = 0); std::string toPrettyString(uint32_t indent = 0);
}; };
class TREX : public fullBox{
public:
TREX(unsigned int trackId = 0);
void setTrackID(uint32_t newTrackID);
uint32_t getTrackID();
void setDefaultSampleDescriptionIndex(uint32_t newDefaultSampleDescriptionIndex);
uint32_t getDefaultSampleDescriptionIndex();
void setDefaultSampleDuration(uint32_t newDefaultSampleDuration);
uint32_t getDefaultSampleDuration();
void setDefaultSampleSize(uint32_t newDefaultSampleSize);
uint32_t getDefaultSampleSize();
void setDefaultSampleFlags(uint32_t newDefaultSampleFlags);
uint32_t getDefaultSampleFlags();
std::string toPrettyString(uint32_t indent = 0);
};
struct trunSampleInformation { struct trunSampleInformation {
uint32_t sampleDuration; uint32_t sampleDuration;
uint32_t sampleSize; uint32_t sampleSize;
@ -106,7 +123,7 @@ namespace MP4{
uint32_t getFirstSampleFlags() const; uint32_t getFirstSampleFlags() const;
uint32_t getSampleInformationCount() const; uint32_t getSampleInformationCount() const;
void setSampleInformation(trunSampleInformation newSample, uint32_t no); void setSampleInformation(trunSampleInformation newSample, uint32_t no);
trunSampleInformation getSampleInformation(uint32_t no, TFHD * tfhd = 0) const; trunSampleInformation getSampleInformation(uint32_t no, TFHD * tfhd = 0, TREX * trex = 0) const;
std::string toPrettyString(uint32_t indent = 0) const; std::string toPrettyString(uint32_t indent = 0) const;
}; };
@ -312,22 +329,6 @@ namespace MP4{
MVEX(); MVEX();
}; };
class TREX : public fullBox{
public:
TREX(unsigned int trackId = 0);
void setTrackID(uint32_t newTrackID);
uint32_t getTrackID();
void setDefaultSampleDescriptionIndex(uint32_t newDefaultSampleDescriptionIndex);
uint32_t getDefaultSampleDescriptionIndex();
void setDefaultSampleDuration(uint32_t newDefaultSampleDuration);
uint32_t getDefaultSampleDuration();
void setDefaultSampleSize(uint32_t newDefaultSampleSize);
uint32_t getDefaultSampleSize();
void setDefaultSampleFlags(uint32_t newDefaultSampleFlags);
uint32_t getDefaultSampleFlags();
std::string toPrettyString(uint32_t indent = 0);
};
class MFRA : public containerBox{ class MFRA : public containerBox{
public: public:
MFRA(); MFRA();

View file

@ -69,6 +69,8 @@ namespace MP4{
co64Box.clear(); co64Box.clear();
stscBox.clear(); stscBox.clear();
stssBox.clear(); stssBox.clear();
trexBox.clear();
trexPtr = 0;
stco64 = false; stco64 = false;
trafMode = false; trafMode = false;
trackId = 0; trackId = 0;
@ -94,6 +96,11 @@ namespace MP4{
trafs.clear(); trafs.clear();
} }
void TrackHeader::read(TREX &_trexBox){
trexBox.copyFrom(_trexBox);
if (trexBox.isType("trex")){trexPtr = &trexBox;}
}
void TrackHeader::read(TRAK &trakBox){ void TrackHeader::read(TRAK &trakBox){
vidWidth = vidHeight = audChannels = audRate = audSize = 0; vidWidth = vidHeight = audChannels = audRate = audSize = 0;
codec.clear(); codec.clear();
@ -428,7 +435,7 @@ namespace MP4{
// Okay, our index is inside this TRUN! // Okay, our index is inside this TRUN!
// Let's pull the TFHD box into this as well... // Let's pull the TFHD box into this as well...
TFHD tfhd = ((TRAF)(*t)).getChild<TFHD>(); TFHD tfhd = ((TRAF)(*t)).getChild<TFHD>();
trunSampleInformation si = r->getSampleInformation(index - skipped, &tfhd); trunSampleInformation si = r->getSampleInformation(index - skipped, &tfhd, trexPtr);
if (byteOffset){ if (byteOffset){
size_t offset = 0; size_t offset = 0;
if (tfhd.getDefaultBaseIsMoof()){ if (tfhd.getDefaultBaseIsMoof()){
@ -438,7 +445,7 @@ namespace MP4{
offset += r->getDataOffset(); offset += r->getDataOffset();
size_t target = index - skipped; size_t target = index - skipped;
for (size_t i = 0; i < target; ++i){ for (size_t i = 0; i < target; ++i){
offset += r->getSampleInformation(i, &tfhd).sampleSize; offset += r->getSampleInformation(i, &tfhd, trexPtr).sampleSize;
} }
}else{ }else{
FAIL_MSG("Unimplemented: trun box does not contain a data offset!"); FAIL_MSG("Unimplemented: trun box does not contain a data offset!");
@ -458,7 +465,7 @@ namespace MP4{
while (timeSample < index){ while (timeSample < index){
// Most common case: timeSample is in the current TRUN box // Most common case: timeSample is in the current TRUN box
if (timeSample >= skipped && timeSample < skipped + count){ if (timeSample >= skipped && timeSample < skipped + count){
trunSampleInformation i = r->getSampleInformation(timeSample - skipped, &tfhd); trunSampleInformation i = r->getSampleInformation(timeSample - skipped, &tfhd, trexPtr);
increaseTime(i.sampleDuration); increaseTime(i.sampleDuration);
continue; continue;
} }
@ -475,7 +482,7 @@ namespace MP4{
break; break;
} }
// Cool, now we know it's valid, increase the time accordingly. // Cool, now we know it's valid, increase the time accordingly.
trunSampleInformation i = runIt->getSampleInformation(timeSample - locSkipped, &tfhd); trunSampleInformation i = runIt->getSampleInformation(timeSample - locSkipped, &tfhd, trexPtr);
increaseTime(i.sampleDuration); increaseTime(i.sampleDuration);
} }
*time = (timeTotal * 1000) / timeScale; *time = (timeTotal * 1000) / timeScale;

View file

@ -31,6 +31,8 @@ namespace MP4{
/// Reads (new) track header information for processing /// Reads (new) track header information for processing
void read(TRAK &trakBox); void read(TRAK &trakBox);
/// Reads (new) track header information for processing /// Reads (new) track header information for processing
void read(TREX &trexBox);
/// Reads (new) track header information for processing
void read(TRAF &trafBox); void read(TRAF &trafBox);
/// Signal that we're going to be reading the next moof box now. /// Signal that we're going to be reading the next moof box now.
@ -87,6 +89,8 @@ namespace MP4{
STTS sttsBox; ///< packet durations STTS sttsBox; ///< packet durations
CTTS cttsBox; ///< packet time offsets (optional) CTTS cttsBox; ///< packet time offsets (optional)
STSC stscBox; ///< packet count per chunk STSC stscBox; ///< packet count per chunk
TREX trexBox; ///< packet count per chunk
TREX * trexPtr; ///< Either 0 or pointer to trexBox member
std::deque<TRAF> trafs; ///< Current traf boxes, if any std::deque<TRAF> trafs; ///< Current traf boxes, if any
bool stco64; // 64 bit chunk offsets? bool stco64; // 64 bit chunk offsets?
bool hasOffsets; ///< Are time offsets present? bool hasOffsets; ///< Are time offsets present?

View file

@ -200,6 +200,12 @@ namespace Mist{
mp4Headers.rbegin()->read(*trakIt); mp4Headers.rbegin()->read(*trakIt);
mp4PacksLeft += mp4Headers.rbegin()->size(); mp4PacksLeft += mp4Headers.rbegin()->size();
} }
std::deque<MP4::TREX> trex = ((MP4::MOOV*)&moovBox)->getChild<MP4::MVEX>().getChildren<MP4::TREX>();
for (std::deque<MP4::TREX>::iterator trexIt = trex.begin(); trexIt != trex.end(); trexIt++){
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
if (it->trackId == trexIt->getTrackID()){it->read(*trexIt);}
}
}
MEDIUM_MSG("Read moov box"); MEDIUM_MSG("Read moov box");
} }
if (boxType == "moof"){ if (boxType == "moof"){
@ -262,6 +268,12 @@ namespace Mist{
mp4Headers.rbegin()->read(*trakIt); mp4Headers.rbegin()->read(*trakIt);
mp4PacksLeft += mp4Headers.rbegin()->size(); mp4PacksLeft += mp4Headers.rbegin()->size();
} }
std::deque<MP4::TREX> trex = ((MP4::MOOV*)&moovBox)->getChild<MP4::MVEX>().getChildren<MP4::TREX>();
for (std::deque<MP4::TREX>::iterator trexIt = trex.begin(); trexIt != trex.end(); trexIt++){
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
if (it->trackId == trexIt->getTrackID()){it->read(*trexIt);}
}
}
MEDIUM_MSG("Read moov box"); MEDIUM_MSG("Read moov box");
} }
offset += boxSize; offset += boxSize;

View file

@ -97,6 +97,10 @@ bool AnalyserMP4::parsePacket(){
// Regardless of support, we now put it in our track header array (after all, even unsupported tracks can be analysed!) // Regardless of support, we now put it in our track header array (after all, even unsupported tracks can be analysed!)
hdrs[tHdr.trackId].read(*trakIt); hdrs[tHdr.trackId].read(*trakIt);
} }
std::deque<MP4::TREX> trex = ((MP4::MOOV*)&mp4Data)->getChild<MP4::MVEX>().getChildren<MP4::TREX>();
for (std::deque<MP4::TREX>::iterator trexIt = trex.begin(); trexIt != trex.end(); trexIt++){
hdrs[trexIt->getTrackID()].read(*trexIt);
}
// If we stored an mdat earlier, we can now analyse and then wipe it // If we stored an mdat earlier, we can now analyse and then wipe it
if (mdat.size()){ if (mdat.size()){
MP4::Box mdatBox(mdat, false); MP4::Box mdatBox(mdat, false);

View file

@ -130,6 +130,12 @@ namespace Mist{
trackHeaders.push_back(MP4::TrackHeader()); trackHeaders.push_back(MP4::TrackHeader());
trackHeaders.rbegin()->read(*trakIt); trackHeaders.rbegin()->read(*trakIt);
} }
std::deque<MP4::TREX> trex = ((MP4::MOOV*)&moovBox)->getChild<MP4::MVEX>().getChildren<MP4::TREX>();
for (std::deque<MP4::TREX>::iterator trexIt = trex.begin(); trexIt != trex.end(); trexIt++){
for (std::deque<MP4::TrackHeader>::iterator it = trackHeaders.begin(); it != trackHeaders.end(); it++){
if (it->trackId == trexIt->getTrackID()){it->read(*trexIt);}
}
}
hasMoov = true; hasMoov = true;
} }
activityCounter = Util::bootSecs(); activityCounter = Util::bootSecs();