From 770b6e35673da9a5f65e4aba0c5fc242b1de3233 Mon Sep 17 00:00:00 2001 From: Erik Zandvliet Date: Mon, 24 Sep 2012 15:31:41 +0200 Subject: [PATCH] rewritten abst and asrt, including pretty-printing --- lib/mp4.cpp | 705 ++++++++++++++++++++++++++++++---------------------- lib/mp4.h | 110 ++++---- 2 files changed, 465 insertions(+), 350 deletions(-) diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 8190762f..d921b3e5 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -4,6 +4,8 @@ #include "mp4.h" #include "json.h" +#define Int64 long long int + /// Contains all MP4 format related code. namespace MP4{ @@ -49,7 +51,7 @@ namespace MP4{ void * ret = malloc(size); if (!ret){return false;} free(data); - data = ret; + data = (char*)ret; memcpy(data, newData.c_str(), size); newData.erase( 0, size ); return true; @@ -80,7 +82,7 @@ namespace MP4{ void Box::clear() { if (data && managed){free(data);} managed = true; - data = malloc(8); + data = (char*)malloc(8); if (data){ data_size = 8; ((int*)data)[0] = htonl(data_size); @@ -92,13 +94,13 @@ namespace MP4{ /// Attempts to typecast this Box to a more specific type and call the toPrettyString() function of that type. /// If this failed, it will print out a message saying pretty-printing is not implemented for . std::string Box::toPrettyString(int indent){ - switch( ntohl(((int*)data.c_str())[1]) ) { //type is at this address - case 0x6D666864: return ((MFHD*)this)->toPrettyString(indent); break; - case 0x6D6F6F66: return ((MOOF*)this)->toPrettyString(indent); break; + switch (ntohl( *((int*)(data+4)) )){ //type is at this address + //case 0x6D666864: return ((MFHD*)this)->toPrettyString(indent); break; + //case 0x6D6F6F66: return ((MOOF*)this)->toPrettyString(indent); break; case 0x61627374: return ((ABST*)this)->toPrettyString(indent); break; - case 0x61667274: return ((AFRT*)this)->toPrettyString(indent); break; + //case 0x61667274: return ((AFRT*)this)->toPrettyString(indent); break; case 0x61737274: return ((ASRT*)this)->toPrettyString(indent); break; - default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data,4,4)+"\n"; break; + default: return std::string(indent, ' ')+"Unimplemented pretty-printing for box "+std::string(data+4,4)+"\n"; break; } } @@ -133,7 +135,7 @@ namespace MP4{ if (!reserve(index, 0, 2)){return;} } newData = htons( newData ); - memcpy( (void*)(data.c_str() + index), (void*)&newData, 2 ); + memcpy( data + index, (char*)&newData, 2 ); } /// Gets the 16 bits integer at the given index. @@ -145,7 +147,7 @@ namespace MP4{ if (!reserve(index, 0, 2)){return 0;} } short result; - memcpy( (void*)&result, (void*)(data.c_str() + index), 2 ); + memcpy( (char*)&result, data + index, 2 ); return ntohs(result); } @@ -187,7 +189,7 @@ namespace MP4{ if (!reserve(index, 0, 4)){return;} } newData = htonl( newData ); - memcpy( (char*)data.c_str() + index, (char*)&newData, 4 ); + memcpy( data + index, (char*)&newData, 4 ); } /// Gets the 32 bits integer at the given index. @@ -199,14 +201,14 @@ namespace MP4{ if (!reserve(index, 0, 4)){return 0;} } long result; - memcpy( (char*)&result, (char*)data.c_str() + index, 4 ); + memcpy( (char*)&result, data + index, 4 ); return ntohl(result); } /// Sets the 64 bits integer at the given index. /// Attempts to resize the data pointer if the index is out of range. /// Fails silently if resizing failed. - void Box::setInt64( long long int newData, size_t index ) { + void Box::setInt64( Int64 newData, size_t index ) { index += 8; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return;} @@ -224,7 +226,7 @@ namespace MP4{ /// Gets the 64 bits integer at the given index. /// Attempts to resize the data pointer if the index is out of range. /// Returns zero if resizing failed. - long long int Box::getInt64( size_t index ) { + Int64 Box::getInt64( size_t index ) { index += 8; if (index+7 >= boxedSize()){ if (!reserve(index, 0, 8)){return 0;} @@ -285,7 +287,7 @@ namespace MP4{ /// Attempts to reserve enough space for wanted bytes of data at given position, where current bytes of data is now reserved. /// This will move any existing data behind the currently reserved space to the proper location after reserving. /// \returns True on success, false otherwise. - bool reserve(size_t position, size_t current, size_t wanted){ + bool Box::reserve(size_t position, size_t current, size_t wanted){ if (current == wanted){return true;} if (current < wanted){ //make bigger @@ -294,12 +296,12 @@ namespace MP4{ if (!managed){return false;} void * ret = realloc(data, boxedSize() + (wanted-current)); if (!ret){return false;} - data = ret; + data = (char*)ret; data_size = boxedSize() + (wanted-current); } //move data behind backward, if any if (boxedSize() - (position+current) > 0){ - memmove(position+wanted, position+current, boxedSize() - (position+current)); + memmove(data+position+wanted, data+position+current, boxedSize() - (position+current)); } //calculate and set new size int newSize = boxedSize() + (wanted-current); @@ -309,7 +311,7 @@ namespace MP4{ //make smaller //move data behind forward, if any if (boxedSize() - (position+current) > 0){ - memmove(position+wanted, position+current, boxedSize() - (position+current)); + memmove(data+position+wanted, data+position+current, boxedSize() - (position+current)); } //calculate and set new size int newSize = boxedSize() - (current-wanted); @@ -318,7 +320,8 @@ namespace MP4{ } } - ABST::ABST( ) : Box("abst") { + ABST::ABST( ) { + memcpy(data + 4, "abst", 4); setVersion( 0 ); setFlags( 0 ); setBootstrapinfoVersion( 0 ); @@ -328,186 +331,340 @@ namespace MP4{ setTimeScale( 1000 ); setCurrentMediaTime( 0 ); setSmpteTimeCodeOffset( 0 ); - setMovieIdentifier( "" ); - setDrmData( "" ); - setMetaData( "" ); + std::string empty; + setMovieIdentifier( empty ); + setDrmData( empty ); + setMetaData( empty ); } - void ABST::setVersion( char newVersion ) { - setInt8( newVersion, 0 ); + void ABST::setVersion(char newVersion){setInt8(newVersion, 0);} + + char ABST::getVersion(){return getInt8(0);} + + void ABST::setFlags(long newFlags){setInt24(newFlags, 1);} + + long ABST::getFlags(){return getInt24(1);} + + void ABST::setBootstrapinfoVersion(long newVersion){setInt32(newVersion, 4);} + + long ABST::getBootstrapinfoVersion(){return getInt32(4);} + + void ABST::setProfile(char newProfile){ + //profile = bit 1 and 2 of byte 8. + setInt8((getInt8(8) & 0x3F) + ((newProfile & 0x03) << 6), 8); } - void ABST::setFlags( long newFlags ) { - setInt24( newFlags, 1 ); - } + char ABST::getProfile(){return (getInt8(8) & 0xC0);}; - void ABST::setBootstrapinfoVersion( long newVersion ) { - setInt32( newVersion, 4 ); - } - - void ABST::setProfile( char newProfile ) { - setInt8((getInt8(8) & 0x3F) + ((newProfile & 0x02) << 6), 8); - } - - - void ABST::setLive( char newLive ) { - setInt8((getInt8(8) & 0xDF) + ((newLive & 0x01) << 5), 8); - } - - - void ABST::setUpdate( char newUpdate ) { - setInt8((getInt8(8) & 0xEF) + ((newUpdate & 0x01) << 4), 8); - } - - void ABST::setTimeScale( long newScale ) { - setInt32(newScale, 9); + void ABST::setLive(bool newLive){ + //live = bit 4 of byte 8. + setInt8((getInt8(8) & 0xDF) + (newLive ? 0x10 : 0 ), 8); } - void ABST::setCurrentMediaTime( long long int newTime ) { - setInt64( newTime, 13); - } + bool ABST::getLive(){return (getInt8(8) & 0x10);} - void ABST::setSmpteTimeCodeOffset( long long int newTime ) { - setInt64( newTime, 21); - } - - void ABST::setMovieIdentifier( std::string newIdentifier ) { - movieIdentifier = newIdentifier; - isUpdated = true; - } - - void ABST::addServerEntry( std::string newEntry ) { - if( std::find( Servers.begin(), Servers.end(), newEntry ) == Servers.end() ) { - Servers.push_back( newEntry ); - isUpdated = true; - } + void ABST::setUpdate(bool newUpdate) { + //update = bit 5 of byte 8. + setInt8((getInt8(8) & 0xEF) + (newUpdate ? 0x08 : 0), 8); } - void ABST::delServerEntry( std::string delEntry ) { - for( std::deque::iterator it = Servers.begin(); it != Servers.end(); it++ ) { - if( (*it) == delEntry ) { - Servers.erase( it ); - isUpdated = true; + bool ABST::getUpdate(){return (getInt8(8) & 0x08);} + + void ABST::setTimeScale(long newScale){setInt32(newScale, 9);} + + long ABST::getTimeScale(){return getInt32(9);} + + void ABST::setCurrentMediaTime(Int64 newTime){setInt64(newTime, 13);} + + Int64 ABST::getCurrentMediaTime(){return getInt64(13);} + + void ABST::setSmpteTimeCodeOffset(Int64 newTime){setInt64(newTime, 21);} + + Int64 ABST::getSmpteTimeCodeOffset(){return getInt64(21);} + + void ABST::setMovieIdentifier(std::string & newIdentifier){setString(newIdentifier, 29);} + + char* ABST::getMovieIdentifier(){return getString(29);} + + long ABST::getServerEntryCount(){ + int countLoc = 29 + getStringLen(29)+1; + return getInt8(countLoc); + } + + void ABST::setServerEntry(std::string & newEntry, long no){ + int countLoc = 29 + getStringLen(29)+1; + int tempLoc = countLoc+1; + for (int i = 0; i < no; i++){ + if (i < getServerEntryCount()){ + tempLoc += getStringLen(tempLoc)+1; + } else { + if(!reserve(tempLoc, 0, no - getServerEntryCount())){return;}; + memset(data+tempLoc, 0, no - getServerEntryCount()); + tempLoc += no - getServerEntryCount(); + setInt8(no, countLoc);//set new serverEntryCount break; } } + setString(newEntry, tempLoc); } - void ABST::addQualityEntry( std::string newEntry ) { - if( std::find( Qualities.begin(), Qualities.end(), newEntry ) == Qualities.end() ) { - Servers.push_back( newEntry ); - isUpdated = true; + ///\return Empty string if no > serverEntryCount(), serverEntry[no] otherwise. + const char* ABST::getServerEntry(long no){ + if (no > getServerEntryCount()){return "";} + int tempLoc = 29+getStringLen(29)+1 + 1;//position of entry count; + for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); + } + + long ABST::getQualityEntryCount(){ + int countLoc = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + countLoc += getStringLen(countLoc)+1; } + return getInt8(countLoc); } - void ABST::delQualityEntry( std::string delEntry ) { - for( std::deque::iterator it = Qualities.begin(); it != Qualities.end(); it++ ) { - if( (*it) == delEntry ) { - Qualities.erase( it ); - isUpdated = true; + void ABST::setQualityEntry(std::string & newEntry, long no){ + int countLoc = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + countLoc += getStringLen(countLoc)+1; + } + int tempLoc = countLoc+1; + for (int i = 0; i < no; i++){ + if (i < getQualityEntryCount()){ + tempLoc += getStringLen(tempLoc)+1; + } else { + if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; + memset(data+tempLoc, 0, no - getQualityEntryCount()); + tempLoc += no - getQualityEntryCount(); + setInt8(no, countLoc);//set new qualityEntryCount break; } } + setString(newEntry, tempLoc); } - + + const char* ABST::getQualityEntry(long no){ + if (no > getQualityEntryCount()){return "";} + int tempLoc = 29+getStringLen(29)+1 + 1;//position of serverentry count; + for (int i = 0; i < getServerEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + tempLoc += 1; + for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); + } + void ABST::setDrmData( std::string newDrm ) { - drmData = newDrm; - isUpdated = true; + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + setString(newDrm, offset); + } + + char* ABST::getDrmData() { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + return getString(offset); } void ABST::setMetaData( std::string newMetaData ) { - metaData = newMetaData; - isUpdated = true; - } - - void ABST::addSegmentRunTable( Box * newSegment ) { - segmentTables.push_back(newSegment); - isUpdated = true; - } - - void ABST::addFragmentRunTable( Box * newFragment ) { - fragmentTables.push_back(newFragment); - isUpdated = true; + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1; + setString(newMetaData, offset); } - void ABST::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize(29); - int myOffset = 29; - //0-terminated movieIdentifier - memcpy( (char*)data.c_str() + myOffset, movieIdentifier.c_str(), movieIdentifier.size() + 1); - myOffset += movieIdentifier.size() + 1; - //8-bit server amount - setInt8( Servers.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = Servers.begin(); it != Servers.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; + char* ABST::getMetaData() { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; } - //8-bit quality amount - setInt8( Qualities.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = Qualities.begin();it != Qualities.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; } - //0-terminated DrmData - memcpy( (char*)data.c_str() + myOffset, drmData.c_str(), drmData.size() + 1); - myOffset += drmData.size() + 1; - //0-terminated MetaData - memcpy( (char*)data.c_str() + myOffset, metaData.c_str(), metaData.size() + 1); - myOffset += metaData.size() + 1; - //8-bit segment run amount - setInt8( segmentTables.size(), myOffset ); - myOffset ++; - //retrieve box for each entry - for( std::deque::iterator it = segmentTables.begin(); it != segmentTables.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() ); - myOffset += (*it)->boxedSize(); - } - //8-bit fragment run amount - setInt8( fragmentTables.size(), myOffset ); - myOffset ++; - //retrieve box for each entry - for( std::deque::iterator it = fragmentTables.begin(); it != fragmentTables.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() + 1); - myOffset += (*it)->boxedSize(); - } - isUpdated = false; + offset+=getStringLen(offset)+1; + return getString(offset); } - std::string ABST::toPrettyString( int indent ) { - regenerate(); - std::stringbuffer r; - r << std::string(indent, ' ') << "Bootstrap Info" << std::endl; - r << std::string(indent+1, ' ') << "Version " << getInt8(0) << std::endl; - if( getInt8(5) & 0x10 ) { - r << std::string(indent+1, ' ') << "Update" << std::endl; - } else { - r << std::string(indent+1, ' ') << "Replacement or new table" << std::endl; + long ABST::getSegmentRunTableCount(){ + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; } - if( getInt8(5) & 0x20 ) { + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + return getInt8(offset); + } + + void ABST::setSegmentRunTable( ASRT newSegment, long no ) { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < no; i++){ + if (i < getSegmentRunTableCount()){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } else { + if(!reserve(tempLoc, 0, 8 * (no - getSegmentRunTableCount()))){return;}; + for( int j = 0; j < (no - getSegmentRunTableCount())*8; j += 8 ) { + setInt32(8,tempLoc+j); + } + tempLoc += (no - getServerEntryCount() ) * 8; + setInt8(no, countLoc);//set new serverEntryCount + break; + } + } + } + + ASRT & ABST::getSegmentRunTable( long no ) { + static Box result; + if( no > getSegmentRunTableCount() ) { + static Box res; + return (ASRT&)res; + } + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < no; i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + result = Box(data+8+tempLoc,false); + return (ASRT&)result; + } + + long ABST::getFragmentRunTableCount() { + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < getSegmentRunTableCount(); i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + return getInt8( tempLoc ); + } + + //tot hier + + AFRT & ABST::getFragmentRunTable( long no ) { + static Box result; + if( no > getFragmentRunTableCount() ) { + static Box res; + return (AFRT&)res; + } + long offset = 29 + getStringLen(29)+1 + 1; + for( int i = 0; i< getServerEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset++; + for( int i = 0; i< getQualityEntryCount(); i++ ) { + offset += getStringLen(offset)+1; + } + offset+=getStringLen(offset)+1;//DrmData + offset+=getStringLen(offset)+1;//MetaData + int countLoc = offset; + int tempLoc = countLoc + 1;//segmentRuntableCount + for (int i = 0; i < getSegmentRunTableCount(); i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + tempLoc ++;//segmentRuntableCount + for (int i = 0; i < no; i++){ + tempLoc += Box(data+8+tempLoc,false).boxedSize(); + } + result = Box(data+8+tempLoc,false); + return (AFRT&)result; + } + + std::string ABST::toPrettyString( long indent ) { + std::stringstream r; + r << std::string(indent, ' ') << "[abst] Bootstrap Info" << std::endl; + r << std::string(indent+1, ' ') << "Version " << getVersion() << std::endl; + r << std::string(indent+1, ' ') << "BootstrapinfoVersion " << getBootstrapinfoVersion() << std::endl; + r << std::string(indent+1, ' ') << "Profile " << getProfile() << std::endl; + if( getLive() ) { r << std::string(indent+1, ' ' ) << "Live" << std::endl; }else{ r << std::string(indent+1, ' ' ) << "Recorded" << std::endl; } - r << std::string(indent+1, ' ') << "Profile " << ( getInt8(5) & 0xC0 ) << std::endl; - r << std::string(indent+1, ' ') << "Timescale " << getInt64(10) << std::endl; - r << std::string(indent+1, ' ') << "CurrMediaTime " << getInt32(6) << std::endl; - r << std::string(indent+1, ' ') << "Segment Run Tables " << segmentTables.size() << std::endl; - for( uint32_t i = 0; i < segmentTables.size(); i++ ) { - r += segmentTables[i]->toPrettyString(indent+2); + if( getUpdate() ) { + r << std::string(indent+1, ' ') << "Update" << std::endl; + } else { + r << std::string(indent+1, ' ') << "Replacement or new table" << std::endl; } - r += std::string(indent+1, ' ')+"Fragment Run Tables "+JSON::Value((long long int)fragmentTables.size()).asString()+"\n"; - for( uint32_t i = 0; i < fragmentTables.size(); i++ ) { - r += fragmentTables[i]->toPrettyString(indent+2); + r << std::string(indent+1, ' ') << "Timescale " << getTimeScale() << std::endl; + r << std::string(indent+1, ' ') << "CurrMediaTime " << getCurrentMediaTime() << std::endl; + r << std::string(indent+1, ' ') << "SmpteTimeCodeOffset " << getSmpteTimeCodeOffset() << std::endl; + r << std::string(indent+1, ' ') << "MovieIdentifier " << getMovieIdentifier() << std::endl; + r << std::string(indent+1, ' ') << "ServerEntryTable (" << getServerEntryCount() << ")" << std::endl; + for( int i = 0; i < getServerEntryCount(); i++ ) { + r << std::string(indent+2, ' ') << getServerEntry(i) << std::endl; + } + r << std::string(indent+1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; + for( int i = 0; i < getQualityEntryCount(); i++ ) { + r << std::string(indent+2, ' ') << getQualityEntry(i) << std::endl; + } + + r << std::string(indent+1, ' ') << "DrmData " << getDrmData() << std::endl; + r << std::string(indent+1, ' ') << "MetaData " << getMetaData() << std::endl; + + r << std::string(indent+1, ' ') << "SegmentRunTableEntries (" << getSegmentRunTableCount() << ")" << std::endl; + for( uint32_t i = 0; i < getSegmentRunTableCount(); i++ ) { + r << ((Box)getSegmentRunTable(i)).toPrettyString(indent+2); + } + r << std::string(indent+1, ' ')+"FragmentRunTableEntries (" << getFragmentRunTableCount() << ")" << std::endl; + for( uint32_t i = 0; i < getFragmentRunTableCount(); i++ ) { + r << ((Box)getFragmentRunTable(i)).toPrettyString(indent+2); } return r.str(); } - AFRT::AFRT() : Box("afrt"){ + AFRT::AFRT(){ + memcpy(data + 4, "afrt", 4); setVersion( 0 ); setUpdate( 0 ); setTimeScale( 1000 ); @@ -527,46 +684,17 @@ namespace MP4{ void AFRT::addQualityEntry( std::string newQuality ) { qualityModifiers.push_back( newQuality ); - isUpdated = true; } - void AFRT::addFragmentRun( long firstFragment, long long int firstTimestamp, long duration, char discontinuity ) { + void AFRT::addFragmentRun( long firstFragment, Int64 firstTimestamp, long duration, char discontinuity ) { fragmentRun newRun; newRun.firstFragment = firstFragment; newRun.firstTimestamp = firstTimestamp; newRun.duration = duration; newRun.discontinuity = discontinuity; fragmentRunTable.push_back( newRun ); - isUpdated = true; } - void AFRT::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize( 8 ); - int myOffset = 8; - setInt8( qualityModifiers.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = qualityModifiers.begin();it != qualityModifiers.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; - } - setInt32( fragmentRunTable.size(), myOffset ); - myOffset += 4; - //table values for each entry - for( std::deque::iterator it = fragmentRunTable.begin();it != fragmentRunTable.end(); it++ ) { - setInt32( (*it).firstFragment, myOffset ); - myOffset += 4; - setInt64( (*it).firstTimestamp, myOffset ); - myOffset += 8; - setInt32( (*it).duration, myOffset ); - myOffset += 4; - setInt8( (*it).discontinuity, myOffset ); - myOffset += 1; - } - isUpdated = false; - } - std::string AFRT::toPrettyString(int indent){ std::string r; r += std::string(indent, ' ')+"Fragment Run Table\n"; @@ -575,19 +703,20 @@ namespace MP4{ }else{ r += std::string(indent+1, ' ')+"Replacement or new table\n"; } - r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((long long int)getInt32(4)).asString()+"\n"; - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)qualityModifiers.size()).asString()+"\n"; + r += std::string(indent+1, ' ')+"Timescale "+JSON::Value((Int64)getInt32(4)).asString()+"\n"; + r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((Int64)qualityModifiers.size()).asString()+"\n"; for( uint32_t i = 0; i < qualityModifiers.size(); i++ ) { r += std::string(indent+2, ' ')+"\""+qualityModifiers[i]+"\"\n"; } - r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((long long int)fragmentRunTable.size()).asString()+"\n"; + r += std::string(indent+1, ' ')+"Fragments "+JSON::Value((Int64)fragmentRunTable.size()).asString()+"\n"; for( uint32_t i = 0; i < fragmentRunTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+"Duration "+JSON::Value((long long int)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((long long int)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((long long int)fragmentRunTable[i].firstTimestamp).asString()+"\n"; + r += std::string(indent+2, ' ')+"Duration "+JSON::Value((Int64)fragmentRunTable[i].duration).asString()+", starting at "+JSON::Value((Int64)fragmentRunTable[i].firstFragment).asString()+" @ "+JSON::Value((Int64)fragmentRunTable[i].firstTimestamp).asString()+"\n"; } return r; } - ASRT::ASRT() : Box("asrt") { + ASRT::ASRT(){ + memcpy(data + 4, "asrt", 4); setVersion( 0 ); setUpdate( 0 ); } @@ -596,67 +725,104 @@ namespace MP4{ setInt8( newVersion, 0 ); } + long ASRT::getVersion(){return getInt8(0);} + void ASRT::setUpdate( long newUpdate ) { setInt24( newUpdate, 1 ); } - void ASRT::addQualityEntry( std::string newQuality ) { - qualityModifiers.push_back( newQuality ); - isUpdated = true; + long ASRT::getUpdate(){return getInt24(1);} + + long ASRT::getQualityEntryCount(){ + return getInt8(4); } - void ASRT::addSegmentRun( long firstSegment, long fragmentsPerSegment ) { - segmentRun newRun; - newRun.firstSegment = firstSegment; - newRun.fragmentsPerSegment = fragmentsPerSegment; - segmentRunTable.push_back( newRun ); - isUpdated = true; + void ASRT::setQualityEntry(std::string & newEntry, long no){ + int countLoc = 4; + int tempLoc = countLoc+1; + for (int i = 0; i < no; i++){ + if (i < getQualityEntryCount()){ + tempLoc += getStringLen(tempLoc)+1; + } else { + if(!reserve(tempLoc, 0, no - getQualityEntryCount())){return;}; + memset(data+tempLoc, 0, no - getQualityEntryCount()); + tempLoc += no - getQualityEntryCount(); + setInt8(no, countLoc);//set new qualityEntryCount + break; + } + } + setString(newEntry, tempLoc); } - void ASRT::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize( 4 ); - int myOffset = 4; - setInt8( qualityModifiers.size(), myOffset ); - myOffset ++; - //0-terminated string for each entry - for( std::deque::iterator it = qualityModifiers.begin();it != qualityModifiers.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it).c_str(), (*it).size() + 1); - myOffset += (*it).size() + 1; - } - setInt32( segmentRunTable.size(), myOffset ); - myOffset += 4; - //table values for each entry - for( std::deque::iterator it = segmentRunTable.begin();it != segmentRunTable.end(); it++ ) { - setInt32( (*it).firstSegment, myOffset ); - myOffset += 4; - setInt32( (*it).fragmentsPerSegment, myOffset ); - myOffset += 4; - } - isUpdated = false; + const char* ASRT::getQualityEntry(long no){ + if (no > getQualityEntryCount()){return "";} + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < no; i++){tempLoc += getStringLen(tempLoc)+1;} + return getString(tempLoc); } - + + long ASRT::getSegmentRunEntryCount() { + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < getQualityEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + return getInt32(tempLoc); + } + + void ASRT::setSegmentRun( long firstSegment, long fragmentsPerSegment, long no ) { + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < getSegmentRunEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + int countLoc = tempLoc; + tempLoc += 4; + for (int i = 0; i < no; i++){ + if (i < getSegmentRunEntryCount()){ + tempLoc += 8; + } else { + if(!reserve(tempLoc, 0, (no - getQualityEntryCount())*8)){return;}; + memset(data+tempLoc, 0, (no - getQualityEntryCount())*8); + tempLoc += (no - getQualityEntryCount())*8; + setInt32(no, countLoc);//set new qualityEntryCount + break; + } + } + setInt32(firstSegment,tempLoc); + setInt32(fragmentsPerSegment,tempLoc+4); + } + + asrt_runtable ASRT::getSegmentRun( long no ) { + int tempLoc = 5;//position of qualityentry count; + for (int i = 0; i < getSegmentRunEntryCount(); i++){tempLoc += getStringLen(tempLoc)+1;} + int countLoc = tempLoc; + tempLoc += 4; + for (int i = 0; i < no; i++){tempLoc += 8;} + asrt_runtable res; + res.firstSegment = getInt32(tempLoc); + res.fragmentsPerSegment = getInt32(tempLoc+4); + return res; + } + std::string ASRT::toPrettyString(int indent){ - std::string r; - r += std::string(indent, ' ')+"Segment Run Table\n"; - if (getInt24(1)){ - r += std::string(indent+1, ' ')+"Update\n"; + std::stringstream r; + r << std::string(indent, ' ') << "[asrt] Segment Run Table" << std::endl; + r << std::string(indent+1, ' ') << "Version " << getVersion() << std::endl; + if (getUpdate()){ + r << std::string(indent+1, ' ') << "Update" << std::endl; }else{ - r += std::string(indent+1, ' ')+"Replacement or new table\n"; + r << std::string(indent+1, ' ') << "Replacement or new table" << std::endl; } - r += std::string(indent+1, ' ')+"Qualities "+JSON::Value((long long int)qualityModifiers.size()).asString()+"\n"; - for( uint32_t i = 0; i < qualityModifiers.size(); i++ ) { - r += std::string(indent+2, ' ')+"\""+qualityModifiers[i]+"\"\n"; + r << std::string(indent+1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl; + for( int i = 0; i < getQualityEntryCount(); i++ ) { + r << std::string(indent+2, ' ') << getQualityEntry(i) << std::endl; } - r += std::string(indent+1, ' ')+"Segments "+JSON::Value((long long int)segmentRunTable.size()).asString()+"\n"; - for( uint32_t i = 0; i < segmentRunTable.size(); i ++ ) { - r += std::string(indent+2, ' ')+JSON::Value((long long int)segmentRunTable[i].fragmentsPerSegment).asString()+" fragments per, starting at "+JSON::Value((long long int)segmentRunTable[i].firstSegment).asString()+"\n"; + r << std::string(indent+1, ' ') << "SegmentRunEntryTable (" << getSegmentRunEntryCount()<< ")" << std::endl; + for( int i = 0; i < getSegmentRunEntryCount(); i ++ ) { + r << std::string(indent+2, ' ') << "FirstSegment " << getSegmentRun(i).firstSegment << std::endl; + r << std::string(indent+2, ' ') << "FragmentsPerSegment " << getSegmentRun(i).fragmentsPerSegment << std::endl; } - return r; + return r.str(); } - MFHD::MFHD() : Box("mfhd") { + MFHD::MFHD(){ setInt32(0,0); + memcpy(data + 4, "mfhd", 4); } void MFHD::setSequenceNumber( long newSequenceNumber ) { @@ -666,54 +832,35 @@ namespace MP4{ std::string MFHD::toPrettyString( int indent ) { std::string r; r += std::string(indent, ' ')+"Movie Fragment Header\n"; - r += std::string(indent+1, ' ')+"SequenceNumber: "+JSON::Value((long long int)getInt32(4)).asString()+"\n"; + r += std::string(indent+1, ' ')+"SequenceNumber: "+JSON::Value((Int64)getInt32(4)).asString()+"\n"; } - MOOF::MOOF() : Box("moof") {} + MOOF::MOOF(){ + memcpy(data + 4, "moof", 4); + } void MOOF::addContent( Box* newContent ) { content.push_back( newContent ); - isUpdated = true; - } - - void MOOF::regenerate() { - if (!isUpdated){return;}//skip if already up to date - data.resize( 0 ); - int myOffset = 0; - //retrieve box for each entry - for( std::deque::iterator it = content.begin(); it != content.end(); it++ ) { - memcpy( (char*)data.c_str() + myOffset, (*it)->asBox().c_str(), (*it)->boxedSize() + 1); - myOffset += (*it)->boxedSize(); - } - isUpdated = false; } std::string MOOF::toPrettyString( int indent ) { - std::string r; - r += std::string(indent, ' ')+"Movie Fragment\n"; - for( uint32_t i = 0; i < content.size(); i++ ) { - r += content[i]->toPrettyString(indent+2); - } - return r; + } - TRUN::TRUN() : Box("trun") { - setInt8(0,0); + TRUN::TRUN(){ + memcpy(data + 4, "trun", 4); } void TRUN::setFlags( long newFlags ) { setInt24(newFlags,1); - isUpdated = true; } void TRUN::setDataOffset( long newOffset ) { dataOffset = newOffset; - isUpdated = true; } void TRUN::setFirstSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { firstSampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); - isUpdated = true; } void TRUN::addSampleInformation( long newDuration, long newSize, char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy,char sampleIsDifferenceSample, long newCompositionTimeOffset ) { @@ -723,7 +870,6 @@ namespace MP4{ newSample.sampleFlags = getSampleFlags( sampleDependsOn, sampleIsDependedOn, sampleHasRedundancy, sampleIsDifferenceSample ); newSample.sampleCompositionTimeOffset = newCompositionTimeOffset; allSamples.push_back( newSample ); - isUpdated = true; } long TRUN::getSampleFlags( char sampleDependsOn, char sampleIsDependedOn, char sampleHasRedundancy, char sampleIsDifferenceSample ) { @@ -738,39 +884,4 @@ namespace MP4{ sampleFlags += 0x0000FFFF; return sampleFlags; } - - void TRUN::regenerate( ) { - if (!isUpdated){return;}//skip if already up to date - data.resize( 4 ); - int myOffset = 4; - setInt32( allSamples.size(), myOffset ); - myOffset += 4; - if( getInt24( 1 ) & 0x000001 ) { - setInt32( dataOffset, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000004 ) { - setInt32( firstSampleFlags, myOffset ); - myOffset += 4; - } - for( std::deque::iterator it = allSamples.begin(); it != allSamples.end(); it++ ) { - if( getInt24( 1 ) & 0x000100 ) { - setInt32( (*it).sampleDuration, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000200 ) { - setInt32( (*it).sampleSize, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000400 ) { - setInt32( (*it).sampleFlags, myOffset ); - myOffset += 4; - } - if( getInt24( 1 ) & 0x000800 ) { - setInt32( (*it).sampleCompositionTimeOffset, myOffset ); - myOffset += 4; - } - } - isUpdated = false; - } }; diff --git a/lib/mp4.h b/lib/mp4.h index 0f1cf56a..450b63a9 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -1,7 +1,9 @@ #pragma once #include #include +#include #include +#include #include #include #include "json.h" @@ -46,49 +48,6 @@ namespace MP4{ bool managed; ///< If false, will not attempt to resize/free the data pointer. };//Box Class - /// ABST Box class - class ABST: public Box { - public: - ABST(); - void setVersion(char newVersion); - char getVersion(); - void setFlags(long newFlags); - long getFlags(); - void setBootstrapinfoVersion(long newVersion); - long getBootstrapinfoVersion(); - void setProfile(char newProfile); - char getProfile(); - void setLive(char newLive); - char getLive(); - void setUpdate(char newUpdate); - char getUpdate(); - void setTimeScale(long newTimeScale); - long getTimeScale(); - void setCurrentMediaTime(long long int newTime); - long long int getCurrentMediaTime(); - void setSmpteTimeCodeOffset(long long int newTime); - long long int getSmpteTimeCodeOffset(); - void setMovieIdentifier(std::string & newIdentifier); - char * getMovieIdentifier(); - void setServerEntry(std::string & entry, int no); - char * getServerEntry(int no); - int getServerEntryCount(); - void setQualityEntry(std::string & entry, int no); - char * getQualityEntry(int no); - int getQualityEntryCount(); - void setDrmData(std::string newDrm); - char * getDrmData(); - void setMetaData(std::string newMetaData); - char * getMetaData(); - void setSegmentRunTable(ASRT table, int no); - ASRT & getSegmentRunTable(int no); - int getSegmentRunTableCount(); - void setFragmentRunTables(AFRT table, int no); - AFRT & getFragmentRunTable(int no); - int getFragmentRunTableCount(); - std::string toPrettyString(int indent = 0); - };//ABST Box - struct fragmentRun { long firstFragment; long long int firstTimestamp; @@ -113,24 +72,26 @@ namespace MP4{ std::deque fragmentRunTable; };//AFRT Box - struct segmentRun { - uint32_t firstSegment; - uint32_t fragmentsPerSegment; - };//segmentRun + struct asrt_runtable{ + long firstSegment; + long fragmentsPerSegment; + }; /// ASRT Box class class ASRT : public Box { public: ASRT(); void setVersion( char newVersion ); + long getVersion(); void setUpdate( long newUpdate ); - void addQualityEntry( std::string newQuality ); - void addSegmentRun( long firstSegment, long fragmentsPerSegment ); - void regenerate(); + long getUpdate(); + long getQualityEntryCount(); + void setQualityEntry( std::string & newQuality, long no ); + const char* getQualityEntry( long no ); + long getSegmentRunEntryCount(); + void setSegmentRun( long firstSegment, long fragmentsPerSegment, long no ); + asrt_runtable getSegmentRun( long no ); std::string toPrettyString(int indent = 0); - private: - std::deque qualityModifiers; - std::deque segmentRunTable; };//ASRT Box class MFHD : public Box { @@ -150,6 +111,49 @@ namespace MP4{ std::deque content; };//MOOF Box + /// ABST Box class + class ABST: public Box { + public: + ABST(); + void setVersion(char newVersion); + char getVersion(); + void setFlags(long newFlags); + long getFlags(); + void setBootstrapinfoVersion(long newVersion); + long getBootstrapinfoVersion(); + void setProfile(char newProfile); + char getProfile(); + void setLive(bool newLive); + bool getLive(); + void setUpdate(bool newUpdate); + bool getUpdate(); + void setTimeScale(long newTimeScale); + long getTimeScale(); + void setCurrentMediaTime(long long int newTime); + long long int getCurrentMediaTime(); + void setSmpteTimeCodeOffset(long long int newTime); + long long int getSmpteTimeCodeOffset(); + void setMovieIdentifier(std::string & newIdentifier); + char * getMovieIdentifier(); + long getServerEntryCount(); + void setServerEntry(std::string & entry, long no); + const char * getServerEntry(long no); + long getQualityEntryCount(); + void setQualityEntry(std::string & entry, long no); + const char * getQualityEntry(long no); + void setDrmData(std::string newDrm); + char * getDrmData(); + void setMetaData(std::string newMetaData); + char * getMetaData(); + long getSegmentRunTableCount(); + void setSegmentRunTable(ASRT table, long no); + ASRT & getSegmentRunTable(long no); + long getFragmentRunTableCount(); + void setFragmentRunTable(AFRT table, long no); + AFRT & getFragmentRunTable(long no); + std::string toPrettyString(long indent = 0); + };//ABST Box + struct trunSampleInformation { long sampleDuration; long sampleSize;