MP4 analyser now supports packet data printing/parsing
This commit is contained in:
parent
16e22eadbe
commit
7cdd58b19f
2 changed files with 230 additions and 3 deletions
|
@ -1,5 +1,114 @@
|
|||
#include "analyser_mp4.h"
|
||||
#include <mist/bitfields.h>
|
||||
#include <mist/mp4_generic.h>
|
||||
#include <mist/h264.h>
|
||||
|
||||
|
||||
class mp4TrackHeader{
|
||||
public:
|
||||
mp4TrackHeader(){
|
||||
initialised = false;
|
||||
stscStart = 0;
|
||||
sampleIndex = 0;
|
||||
deltaIndex = 0;
|
||||
deltaPos = 0;
|
||||
deltaTotal = 0;
|
||||
offsetIndex = 0;
|
||||
offsetPos = 0;
|
||||
sttsBox.clear();
|
||||
hasCTTS = false;
|
||||
cttsBox.clear();
|
||||
stszBox.clear();
|
||||
stcoBox.clear();
|
||||
co64Box.clear();
|
||||
stco64 = false;
|
||||
trackId = 0;
|
||||
}
|
||||
void read(MP4::TRAK &trakBox){
|
||||
initialised = false;
|
||||
std::string tmp; // temporary string for copying box data
|
||||
MP4::Box trakLoopPeek;
|
||||
timeScale = 1;
|
||||
|
||||
MP4::MDIA mdiaBox = trakBox.getChild<MP4::MDIA>();
|
||||
|
||||
timeScale = mdiaBox.getChild<MP4::MDHD>().getTimeScale();
|
||||
trackId = trakBox.getChild<MP4::TKHD>().getTrackID();
|
||||
|
||||
MP4::STBL stblBox = mdiaBox.getChild<MP4::MINF>().getChild<MP4::STBL>();
|
||||
|
||||
sttsBox.copyFrom(stblBox.getChild<MP4::STTS>());
|
||||
cttsBox.copyFrom(stblBox.getChild<MP4::CTTS>());
|
||||
stszBox.copyFrom(stblBox.getChild<MP4::STSZ>());
|
||||
stcoBox.copyFrom(stblBox.getChild<MP4::STCO>());
|
||||
co64Box.copyFrom(stblBox.getChild<MP4::CO64>());
|
||||
stscBox.copyFrom(stblBox.getChild<MP4::STSC>());
|
||||
stco64 = co64Box.isType("co64");
|
||||
hasCTTS = cttsBox.isType("ctts");
|
||||
}
|
||||
size_t trackId;
|
||||
MP4::STCO stcoBox;
|
||||
MP4::CO64 co64Box;
|
||||
MP4::STSZ stszBox;
|
||||
MP4::STTS sttsBox;
|
||||
bool hasCTTS;
|
||||
MP4::CTTS cttsBox;
|
||||
MP4::STSC stscBox;
|
||||
uint64_t timeScale;
|
||||
void getPart(uint64_t index, uint64_t &offset, uint64_t &size){
|
||||
if (index < sampleIndex){
|
||||
sampleIndex = 0;
|
||||
stscStart = 0;
|
||||
}
|
||||
|
||||
uint64_t stscCount = stscBox.getEntryCount();
|
||||
MP4::STSCEntry stscEntry;
|
||||
while (stscStart < stscCount){
|
||||
stscEntry = stscBox.getSTSCEntry(stscStart);
|
||||
// check where the next index starts
|
||||
uint64_t nextSampleIndex;
|
||||
if (stscStart + 1 < stscCount){
|
||||
nextSampleIndex = sampleIndex + (stscBox.getSTSCEntry(stscStart + 1).firstChunk - stscEntry.firstChunk) *
|
||||
stscEntry.samplesPerChunk;
|
||||
}else{
|
||||
nextSampleIndex = stszBox.getSampleCount();
|
||||
}
|
||||
if (nextSampleIndex > index){break;}
|
||||
sampleIndex = nextSampleIndex;
|
||||
++stscStart;
|
||||
}
|
||||
|
||||
if (sampleIndex > index){
|
||||
FAIL_MSG("Could not complete seek - not in file (%" PRIu64 " > %" PRIu64 ")", sampleIndex, index);
|
||||
}
|
||||
|
||||
uint64_t stcoPlace = (stscEntry.firstChunk - 1) + ((index - sampleIndex) / stscEntry.samplesPerChunk);
|
||||
uint64_t stszStart = sampleIndex + (stcoPlace - (stscEntry.firstChunk - 1)) * stscEntry.samplesPerChunk;
|
||||
|
||||
offset = (stco64 ? co64Box.getChunkOffset(stcoPlace) : stcoBox.getChunkOffset(stcoPlace));
|
||||
for (int j = stszStart; j < index; j++){offset += stszBox.getEntrySize(j);}
|
||||
size = stszBox.getEntrySize(index);
|
||||
|
||||
initialised = true;
|
||||
}
|
||||
uint64_t size(){return (stszBox.asBox() ? stszBox.getSampleCount() : 0);}
|
||||
|
||||
private:
|
||||
bool initialised;
|
||||
// next variables are needed for the stsc/stco loop
|
||||
uint64_t stscStart;
|
||||
uint64_t sampleIndex;
|
||||
// next variables are needed for the stts loop
|
||||
uint64_t deltaIndex; ///< Index in STTS box
|
||||
uint64_t deltaPos; ///< Sample counter for STTS box
|
||||
uint64_t deltaTotal; ///< Total timestamp for STTS box
|
||||
// for CTTS box loop
|
||||
uint64_t offsetIndex; ///< Index in CTTS box
|
||||
uint64_t offsetPos; ///< Sample counter for CTTS box
|
||||
|
||||
bool stco64;
|
||||
};
|
||||
|
||||
|
||||
void AnalyserMP4::init(Util::Config &conf){
|
||||
Analyser::init(conf);
|
||||
|
@ -7,6 +116,7 @@ void AnalyserMP4::init(Util::Config &conf){
|
|||
|
||||
AnalyserMP4::AnalyserMP4(Util::Config &conf) : Analyser(conf){
|
||||
curPos = prePos = 0;
|
||||
moofPos = moovPos = 0;
|
||||
}
|
||||
|
||||
bool AnalyserMP4::parsePacket(){
|
||||
|
@ -25,10 +135,34 @@ bool AnalyserMP4::parsePacket(){
|
|||
}
|
||||
}
|
||||
|
||||
if (!isOpen()){
|
||||
if (!mp4Buffer.size()){return false;}
|
||||
uint64_t actSize = mp4Buffer.size();
|
||||
uint64_t nedSize = neededBytes();
|
||||
if (actSize < nedSize){
|
||||
WARN_MSG("Read partial box; appending %" PRIu64 " zeroes to parse remainder!", nedSize - actSize);
|
||||
mp4Buffer.append(nedSize - actSize, (char)0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mp4Data.read(mp4Buffer)){
|
||||
INFO_MSG("Read a box at position %" PRIu64, prePos);
|
||||
if (detail >= 2){std::cout << mp4Data.toPrettyString(0) << std::endl;}
|
||||
///\TODO update mediaTime with the current timestamp
|
||||
INFO_MSG("Read a %" PRIu64 "b %s box at position %" PRIu64, mp4Data.boxedSize(), mp4Data.getType().c_str(), prePos);
|
||||
if (mp4Data.getType() == "mdat"){
|
||||
mdatPos = prePos;
|
||||
analyseData(mp4Data);
|
||||
return true;
|
||||
}
|
||||
if (mp4Data.getType() == "moof"){
|
||||
moof.assign(mp4Data.asBox(), mp4Data.boxedSize());
|
||||
moofPos = prePos;
|
||||
}
|
||||
if (mp4Data.getType() == "moov"){
|
||||
moov.assign(mp4Data.asBox(), mp4Data.boxedSize());
|
||||
moovPos = prePos;
|
||||
}
|
||||
if (detail >= 2){
|
||||
std::cout << mp4Data.toPrettyString(0) << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
FAIL_MSG("Could not read box at position %" PRIu64, prePos);
|
||||
|
@ -44,3 +178,90 @@ uint64_t AnalyserMP4::neededBytes(){
|
|||
size = Bit::btohll(mp4Buffer.data() + 8);
|
||||
return size;
|
||||
}
|
||||
|
||||
void printByteRange(const char * ptr, size_t start, size_t end){
|
||||
size_t byteCounter = 0;
|
||||
for (uint64_t j = start; j < end; ++j){
|
||||
if (byteCounter && (byteCounter % 32) == 0){std::cout << std::endl;}
|
||||
std::cout << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)ptr[j];
|
||||
++byteCounter;
|
||||
}
|
||||
std::cout << std::dec << std::endl;
|
||||
}
|
||||
|
||||
h264::nalUnit * getNalUnit(const char * data, size_t pktLen){
|
||||
switch (data[0] & 0x1F){
|
||||
case 1:
|
||||
case 5:
|
||||
case 19: return new h264::codedSliceUnit(data, pktLen);
|
||||
case 6: return new h264::seiUnit(data, pktLen);
|
||||
case 7: return new h264::spsUnit(data, pktLen);
|
||||
case 8: return new h264::ppsUnit(data, pktLen);
|
||||
default: return new h264::nalUnit(data, pktLen);
|
||||
}
|
||||
}
|
||||
|
||||
void AnalyserMP4::analyseData(MP4::Box & mdatBox){
|
||||
if (moov.size()){
|
||||
MP4::Box globHdr(moov, false);
|
||||
std::deque<MP4::TRAK> traks = ((MP4::MOOV*)&globHdr)->getChildren<MP4::TRAK>();
|
||||
|
||||
size_t trkCounter = 0;
|
||||
for (std::deque<MP4::TRAK>::iterator trakIt = traks.begin(); trakIt != traks.end(); trakIt++){
|
||||
trkCounter++;
|
||||
MP4::MDIA mdiaBox = trakIt->getChild<MP4::MDIA>();
|
||||
|
||||
std::string hdlrType = mdiaBox.getChild<MP4::HDLR>().getHandlerType();
|
||||
if (hdlrType != "vide" && hdlrType != "soun" && hdlrType != "sbtl"){
|
||||
INFO_MSG("Unsupported handler: %s", hdlrType.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string sType = mdiaBox.getChild<MP4::MINF>().getChild<MP4::STBL>().getChild<MP4::STSD>().getEntry(0).getType();
|
||||
if (sType == "avc1" || sType == "h264" || sType == "mp4v"){sType = "H264";}
|
||||
if (sType == "hev1" || sType == "hvc1"){sType = "HEVC";}
|
||||
if (sType == "ac-3"){sType = "AC3";}
|
||||
if (sType == "tx3g"){sType = "subtitle";}
|
||||
INFO_MSG("Detected %s", sType.c_str());
|
||||
mp4TrackHeader tHdr;
|
||||
tHdr.read(*trakIt);
|
||||
size_t noPkts = tHdr.size();
|
||||
for (size_t i = 0; i < noPkts; ++i){
|
||||
uint64_t offset = 0, size = 0;
|
||||
tHdr.getPart(i, offset, size);
|
||||
std::cout << "Packet " << i << " for track " << trkCounter << " (" << sType << "): " << size << " bytes" << std::endl;
|
||||
if (offset < mdatPos){
|
||||
std::cout << "Data is before mdat!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (offset - mdatPos + size > mdatBox.boxedSize()){
|
||||
std::cout << "Data is after mdat!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
const char * ptr = mdatBox.asBox() - mdatPos + offset;
|
||||
if (sType == "H264"){
|
||||
size_t j = 0;
|
||||
while (j+4 <= size){
|
||||
uint32_t len = Bit::btohl(ptr+j);
|
||||
std::cout << len << " bytes: ";
|
||||
printByteRange(ptr, j, 4);
|
||||
if (j+4+len > size){len = size-j-4;}
|
||||
|
||||
h264::nalUnit * nalu = getNalUnit(ptr+j+4, len);
|
||||
nalu->toPrettyString(std::cout);
|
||||
delete nalu;
|
||||
printByteRange(ptr, j+4, j+4+len);
|
||||
j += 4 + len;
|
||||
}
|
||||
}else{
|
||||
printByteRange(ptr, 0, size);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if (moof.size()){
|
||||
}
|
||||
///\TODO update mediaTime with the current timestamp
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,13 @@ public:
|
|||
bool parsePacket();
|
||||
|
||||
private:
|
||||
Util::ResizeablePointer moof;
|
||||
Util::ResizeablePointer moov;
|
||||
uint64_t moovPos;
|
||||
uint64_t moofPos;
|
||||
uint64_t mdatPos;
|
||||
uint64_t neededBytes();
|
||||
void analyseData(MP4::Box & mdatBox);
|
||||
std::string mp4Buffer;
|
||||
MP4::Box mp4Data;
|
||||
uint64_t curPos;
|
||||
|
|
Loading…
Add table
Reference in a new issue