Upgrade segmentreader to support (f)MP4 segments, add such support to HLS input

This commit is contained in:
Thulinma 2024-04-20 02:11:57 +02:00
parent b01df1f3f1
commit 57655f1b21
4 changed files with 159 additions and 57 deletions

View file

@ -29,6 +29,8 @@ namespace Mist{
#endif
currBuf = 0;
packetPtr = 0;
mp4PacksLeft = 0;
lastMoof = 0;
}
void SegmentReader::onProgress(bool (*callback)(uint8_t)){
@ -49,6 +51,9 @@ namespace Mist{
// Buffered? Just return false - we can't download more.
if (buffered){return false;}
// Past end of file? Always return false.
if (_offset > currBuf->rsize()){return false;}
#ifdef SSL
// Encrypted? Round up to nearest multiple of 16
if (encrypted && _offset % 16){
@ -86,7 +91,34 @@ namespace Mist{
}
void SegmentReader::initializeMetadata(DTSC::Meta &meta, size_t tid, size_t mappingId){
tsStream.initializeMetadata(meta, tid, mappingId);
if (parser == STRM_TS){
tsStream.initializeMetadata(meta, tid, mappingId);
return;
}
if (parser == STRM_MP4){
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
if (it->trackId != tid){continue;}
size_t tNumber = meta.addTrack();
INFO_MSG("Found track %zu of type %s -> %s", tNumber, it->sType.c_str(), it->codec.c_str());
meta.setID(tNumber, mappingId);
meta.setCodec(tNumber, it->codec);
meta.setInit(tNumber, it->initData);
meta.setLang(tNumber, it->lang);
if (it->trackType == "video"){
meta.setType(tNumber, "video");
meta.setWidth(tNumber, it->vidWidth);
meta.setHeight(tNumber, it->vidHeight);
}
if (it->trackType == "audio"){
meta.setType(tNumber, "audio");
meta.setChannels(tNumber, it->audChannels);
meta.setRate(tNumber, it->audRate);
meta.setSize(tNumber, it->audSize);
}
}
return;
}
}
/// Attempts to read a single TS packet from the current segment, setting packetPtr on success
@ -101,7 +133,7 @@ namespace Mist{
parser = STRM_TS;
continue;
}
if (!memcmp(*currBuf + 4, "ftyp", 4) || !memcmp(*currBuf + 4, "moof", 4) || !memcmp(*currBuf + 4, "moov", 4)){
if (!memcmp(*currBuf + 4, "ftyp", 4) || !memcmp(*currBuf + 4, "styp", 4) || !memcmp(*currBuf + 4, "moof", 4) || !memcmp(*currBuf + 4, "moov", 4)){
parser = STRM_MP4;
continue;
}
@ -122,34 +154,118 @@ namespace Mist{
}
if (parser == STRM_MP4){
/// \TODO Implement parsing MP4 data
if (mp4PacksLeft){
std::deque<size_t>::iterator pIt = mp4PackNo.begin();
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
if (*pIt < it->size()){
uint64_t prtBpos = 0, prtTime = 0;
uint32_t prtBlen = 0;
int32_t prtTimeOff = 0;
bool prtKey = false;
it->getPart(*pIt, &prtBpos, &prtBlen, &prtTime, &prtTimeOff, &prtKey, lastMoof);
// Increase/decrease counters
--mp4PacksLeft;
++(*pIt);
// Abort reading if we cannot read this part, try the next part
if (!readTo(prtBpos + prtBlen)){continue;}
// Fill the packet and return true
thisPacket.genericFill(prtTime, prtTimeOff, it->trackId, *currBuf + prtBpos, prtBlen, bytePos, prtKey);
return true;
}
++pIt;
}
}
if (!mp4PacksLeft){
// Read more boxes!
if (offset >= currBuf->rsize()){return false;}
if (!readTo(offset + 12)){
INFO_MSG("Could not read next MP4 box!");
return false;
}
std::string boxType = std::string(*currBuf+offset+4, 4);
uint64_t boxSize = MP4::calcBoxSize(*currBuf+offset);
if (!readTo(offset + boxSize)){
INFO_MSG("Could not read next MP4 box!");
return false;
}
if (boxType == "moov"){
mp4PacksLeft = 0;
mp4Headers.clear();
mp4PackNo.clear();
MP4::Box moovBox(*currBuf+offset, false);
std::deque<MP4::TRAK> trak = ((MP4::MOOV*)&moovBox)->getChildren<MP4::TRAK>();
for (std::deque<MP4::TRAK>::iterator trakIt = trak.begin(); trakIt != trak.end(); trakIt++){
mp4Headers.push_back(MP4::TrackHeader());
mp4PackNo.push_back(0);
mp4Headers.rbegin()->read(*trakIt);
mp4PacksLeft += mp4Headers.rbegin()->size();
}
MEDIUM_MSG("Read moov box");
}
if (boxType == "moof"){
if (!mp4Headers.size()){
FAIL_MSG("Attempting to read moof box without reading moov box first!");
return false;
}
lastMoof = offset;
MP4::Box moofBox(*currBuf+offset, false);
// Indicate that we're reading the next moof box to all track headers
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
it->nextMoof();
}
// Loop over traf boxes inside the moof box, but them in our header parser
std::deque<MP4::TRAF> trafs = ((MP4::MOOF*)&moofBox)->getChildren<MP4::TRAF>();
for (std::deque<MP4::TRAF>::iterator t = trafs.begin(); t != trafs.end(); ++t){
if (!(t->getChild<MP4::TFHD>())){
WARN_MSG("Could not find thfd box inside traf box!");
continue;
}
uint32_t trackId = t->getChild<MP4::TFHD>().getTrackID();
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
if (it->trackId == trackId){
it->read(*t);
break;
}
}
}
mp4PacksLeft = 0;
std::deque<size_t>::iterator pIt = mp4PackNo.begin();
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
mp4PacksLeft += it->size();
(*pIt) = 0;
++pIt;
}
}
offset += boxSize;
}
}
}
return false;
}
void SegmentReader::setInit(const std::string & data){
/// \TODO Implement detecting/parsing MP4 init data
/*
std::string boxType = std::string(readBuffer+4, 4);
uint64_t boxSize = MP4::calcBoxSize(readBuffer);
char * ptr = (char *)data.data();
size_t len = data.size();
size_t offset = 0;
while (offset + 8 <= len){
std::string boxType = std::string(ptr+offset+4, 4);
uint64_t boxSize = MP4::calcBoxSize(ptr+offset);
if (boxType == "moov"){
while (readBuffer.size() < boxSize && inFile && keepRunning()){inFile.readSome(boxSize-readBuffer.size(), *this);}
if (readBuffer.size() < boxSize){
Util::logExitReason(ER_FORMAT_SPECIFIC, "Could not read entire MOOV box into memory");
break;
}
MP4::Box moovBox(readBuffer, false);
// for all box in moov
mp4PacksLeft = 0;
mp4Headers.clear();
mp4PackNo.clear();
MP4::Box moovBox(ptr+offset, false);
std::deque<MP4::TRAK> trak = ((MP4::MOOV*)&moovBox)->getChildren<MP4::TRAK>();
for (std::deque<MP4::TRAK>::iterator trakIt = trak.begin(); trakIt != trak.end(); trakIt++){
trackHeaders.push_back(MP4::TrackHeader());
trackHeaders.rbegin()->read(*trakIt);
mp4Headers.push_back(MP4::TrackHeader());
mp4PackNo.push_back(0);
mp4Headers.rbegin()->read(*trakIt);
mp4PacksLeft += mp4Headers.rbegin()->size();
}
hasMoov = true;
MEDIUM_MSG("Read moov box");
}
*/
offset += boxSize;
}
}
/// Stores data in currBuf, decodes if/as necessary, in whole 16-byte blocks
@ -212,6 +328,11 @@ namespace Mist{
/// Loads the given segment URL into the segment buffer.
bool SegmentReader::load(const std::string &path, uint64_t startAt, uint64_t stopAt, const char * ivec, const char * keyAES, Util::ResizeablePointer * bufPtr){
tsStream.partialClear();
lastMoof = 0;
for (std::deque<MP4::TrackHeader>::iterator it = mp4Headers.begin(); it != mp4Headers.end(); ++it){
it->nextMoof();
}
isOpen = false;
parser = STRM_UNKN;
if (ivec && keyAES && memcmp(keyAES, "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", 16)){

View file

@ -42,6 +42,9 @@ namespace Mist{
streamType parser;
TS::Stream tsStream;
std::deque<MP4::TrackHeader> mp4Headers;
std::deque<size_t> mp4PackNo;
size_t mp4PacksLeft;
uint64_t lastMoof;
#ifdef SSL