LTS Commits
This commit is contained in:
parent
f24d97b510
commit
4bdbd82f66
72 changed files with 8245 additions and 105 deletions
55
lib/dtsc.cpp
55
lib/dtsc.cpp
|
@ -356,6 +356,11 @@ void DTSC::Stream::cutOneBuffer() {
|
|||
metadata.tracks[trid].fragments.clear();
|
||||
}
|
||||
}
|
||||
/*LTS-START*/
|
||||
if (!recordPath.empty()) {
|
||||
recordPacket(buffers.begin()->second);
|
||||
}
|
||||
/*LTS-END*/
|
||||
deletionCallback(buffers.begin()->first);
|
||||
buffers.erase(buffers.begin());
|
||||
}
|
||||
|
@ -453,6 +458,35 @@ DTSC::Ring * DTSC::Stream::getRing() {
|
|||
return new DTSC::Ring(tmp);
|
||||
}
|
||||
|
||||
/*LTS-START*/
|
||||
/// Sets the recording path and writes the file header in preperation for the recording.
|
||||
/// If the file cannot be opened the path is assumed to be invalid and an error is written.
|
||||
void DTSC::Stream::setRecord(std::string path) {
|
||||
if (path.empty()) {
|
||||
return;
|
||||
}
|
||||
recordFile = new File(path, true);
|
||||
if (!recordFile) {
|
||||
DEBUG_MSG(DLVL_ERROR, "Failed to create file: %s", path.c_str());
|
||||
}
|
||||
headerRecorded = false;
|
||||
recordPath = path;
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
/*LTS-START*/
|
||||
/// Writes a packet to file, if the header was not yet written it writes that first.
|
||||
void DTSC::Stream::recordPacket(JSON::Value & packet) {
|
||||
if (!headerRecorded) {
|
||||
metadata.moreheader = 0;
|
||||
std::string header = metadata.toJSON().toPacked();
|
||||
recordFile->writeHeader(header, true);
|
||||
headerRecorded = true;
|
||||
}
|
||||
recordFile->writePacket(packet);
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
/// Deletes a given out Ring class from memory and internal Ring list.
|
||||
/// Checks for NULL pointers and invalid pointers, silently discarding them.
|
||||
void DTSC::Stream::dropRing(DTSC::Ring * ptr) {
|
||||
|
@ -764,16 +798,13 @@ void DTSC::File::seekNext() {
|
|||
myPack.null();
|
||||
return;
|
||||
}
|
||||
fseek(F, currentPositions.begin()->bytePos, SEEK_SET);
|
||||
seekPos thisPos = *currentPositions.begin();
|
||||
fseek(F, thisPos.bytePos, SEEK_SET);
|
||||
if (reachedEOF()) {
|
||||
myPack.null();
|
||||
return;
|
||||
}
|
||||
clearerr(F);
|
||||
if (!metadata.merged) {
|
||||
seek_time(currentPositions.begin()->seekTime + 1, currentPositions.begin()->trackID);
|
||||
fseek(F, currentPositions.begin()->bytePos, SEEK_SET);
|
||||
}
|
||||
currentPositions.erase(currentPositions.begin());
|
||||
lastreadpos = ftell(F);
|
||||
if (fread(buffer, 4, 1, F) != 1) {
|
||||
|
@ -786,7 +817,7 @@ void DTSC::File::seekNext() {
|
|||
return;
|
||||
}
|
||||
if (memcmp(buffer, DTSC::Magic_Header, 4) == 0) {
|
||||
seek_time(myPack.getTime() + 1, myPack.getTrackId(), true);
|
||||
seek_time(myPack.getTime(), myPack.getTrackId(), true);
|
||||
return seekNext();
|
||||
}
|
||||
long long unsigned int version = 0;
|
||||
|
@ -864,9 +895,12 @@ void DTSC::File::seekNext() {
|
|||
}
|
||||
currentPositions.insert(tmpPos);
|
||||
} else {
|
||||
seek_time(myPack.getTime() + 1, myPack.getTrackId(), true);
|
||||
seek_time(myPack.getTime(), myPack.getTrackId(), true);
|
||||
}
|
||||
seek_bpos(tempLoc);
|
||||
}else{
|
||||
seek_time(thisPos.seekTime, thisPos.trackID);
|
||||
fseek(F, thisPos.bytePos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -954,9 +988,14 @@ DTSC::Packet & DTSC::File::getPacket() {
|
|||
bool DTSC::File::seek_time(unsigned int ms, unsigned int trackNo, bool forceSeek) {
|
||||
seekPos tmpPos;
|
||||
tmpPos.trackID = trackNo;
|
||||
if (!forceSeek && myPack && ms > myPack.getTime() && trackNo >= myPack.getTrackId()) {
|
||||
if (!forceSeek && myPack && ms >= myPack.getTime() && trackNo >= myPack.getTrackId()) {
|
||||
tmpPos.seekTime = myPack.getTime();
|
||||
tmpPos.bytePos = getBytePos();
|
||||
/*
|
||||
if (trackNo == myPack.getTrackId()){
|
||||
tmpPos.bytePos += myPack.getDataLen();
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
tmpPos.seekTime = 0;
|
||||
tmpPos.bytePos = 0;
|
||||
|
|
26
lib/dtsc.h
26
lib/dtsc.h
|
@ -179,6 +179,26 @@ namespace DTSC {
|
|||
volatile int playCount;
|
||||
};
|
||||
|
||||
/*LTS-START*/
|
||||
///\brief Basic class supporting initialization Vectors.
|
||||
///
|
||||
///These are used for encryption of data.
|
||||
class Ivec {
|
||||
public:
|
||||
Ivec();
|
||||
Ivec(long long int iVec);
|
||||
void setIvec(long long int iVec);
|
||||
void setIvec(std::string iVec);
|
||||
void setIvec(char * iVec, int len);
|
||||
long long int asInt();
|
||||
char * getData();
|
||||
private:
|
||||
///\brief Data storage for this initialization vector.
|
||||
///
|
||||
/// - 8 bytes: MSB storage of the initialization vector.
|
||||
char data[8];
|
||||
};
|
||||
/*LTS-END*/
|
||||
|
||||
///\brief Basic class for storage of data associated with single DTSC packets, a.k.a. parts.
|
||||
class Part {
|
||||
|
@ -270,6 +290,7 @@ namespace DTSC {
|
|||
std::deque<Key> keys;
|
||||
std::deque<unsigned long> keySizes;
|
||||
std::deque<Part> parts;
|
||||
std::deque<Ivec> ivecs; /*LTS*/
|
||||
Key & getKey(unsigned int keyNum);
|
||||
unsigned int timeToKeynum(unsigned int timestamp);
|
||||
unsigned int timeToFragnum(unsigned int timestamp);
|
||||
|
@ -394,6 +415,7 @@ namespace DTSC {
|
|||
std::string & outPacket(livePos num);
|
||||
std::string & outHeader();
|
||||
Ring * getRing();
|
||||
void setRecord(std::string path); /*LTS*/
|
||||
unsigned int getTime();
|
||||
void dropRing(Ring * ptr);
|
||||
int canSeekms(unsigned int ms);
|
||||
|
@ -411,9 +433,13 @@ namespace DTSC {
|
|||
std::map<int, std::set<livePos> > keyframes;
|
||||
virtual void addPacket(JSON::Value & newPack);
|
||||
virtual void addMeta(JSON::Value & newMeta);
|
||||
void recordPacket(JSON::Value & packet); /*LTS*/
|
||||
datatype datapointertype;
|
||||
unsigned int buffercount;
|
||||
unsigned int buffertime;
|
||||
std::string recordPath; /*LTS*/
|
||||
File * recordFile; /*LTS*/
|
||||
bool headerRecorded; /*LTS*/
|
||||
std::map<unsigned int, std::string> trackMapping;
|
||||
virtual void deletionCallback(livePos deleting);
|
||||
};
|
||||
|
|
|
@ -790,7 +790,37 @@ namespace DTSC {
|
|||
}
|
||||
}
|
||||
|
||||
/*LTS-START*/
|
||||
Ivec::Ivec() {
|
||||
setIvec(0);
|
||||
}
|
||||
|
||||
Ivec::Ivec(long long int iVec) {
|
||||
setIvec(iVec);
|
||||
}
|
||||
|
||||
void Ivec::setIvec(long long int iVec) {
|
||||
Bit::htobll(data, iVec);
|
||||
}
|
||||
|
||||
void Ivec::setIvec(std::string iVec) {
|
||||
memset(data, 0, 8);
|
||||
memcpy(data, iVec.data(), std::min(8, (int)iVec.size()));
|
||||
}
|
||||
|
||||
void Ivec::setIvec(char * iVec, int len) {
|
||||
memset(data, 0, 8);
|
||||
memcpy(data, iVec, std::min(8, len));
|
||||
}
|
||||
|
||||
long long int Ivec::asInt() {
|
||||
return Bit::btohll(data);
|
||||
}
|
||||
|
||||
char * Ivec::getData() {
|
||||
return data;
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
///\brief Returns the payloadsize of a part
|
||||
long Part::getSize() {
|
||||
|
@ -978,6 +1008,12 @@ namespace DTSC {
|
|||
Part * tmp = (Part *)trackRef["parts"].asStringRef().data();
|
||||
parts = std::deque<Part>(tmp, tmp + (trackRef["parts"].asStringRef().size() / 9));
|
||||
}
|
||||
/*LTS-START*/
|
||||
if (trackRef.isMember("ivecs") && trackRef["ivecs"].isString()) {
|
||||
Ivec * tmp = (Ivec *)trackRef["ivecs"].asString().data();
|
||||
ivecs = std::deque<Ivec>(tmp, tmp + (trackRef["ivecs"].asString().size() / 8));
|
||||
}
|
||||
/*LTS-END*/
|
||||
trackID = trackRef["trackid"].asInt();
|
||||
firstms = trackRef["firstms"].asInt();
|
||||
lastms = trackRef["lastms"].asInt();
|
||||
|
@ -1024,6 +1060,14 @@ namespace DTSC {
|
|||
trackRef.getMember("parts").getString(tmp, tmplen);
|
||||
parts = std::deque<Part>((Part *)tmp, ((Part *)tmp) + (tmplen / 9));
|
||||
}
|
||||
/*LTS-START*/
|
||||
if (trackRef.getMember("ivecs").getType() == DTSC_STR) {
|
||||
char * tmp = 0;
|
||||
unsigned int tmplen = 0;
|
||||
trackRef.getMember("ivecs").getString(tmp, tmplen);
|
||||
ivecs = std::deque<Ivec>((Ivec *)tmp, ((Ivec *)tmp) + (tmplen / 8));
|
||||
}
|
||||
/*LTS-END*/
|
||||
trackID = trackRef.getMember("trackid").asInt();
|
||||
firstms = trackRef.getMember("firstms").asInt();
|
||||
lastms = trackRef.getMember("lastms").asInt();
|
||||
|
@ -1086,6 +1130,13 @@ namespace DTSC {
|
|||
} else {
|
||||
newKey.setBpos(0);
|
||||
}
|
||||
/*LTS-START
|
||||
if (pack.isMember("ivec")) {
|
||||
Ivec newIvec;
|
||||
newIvec.setIvec((char *)pack["ivec"].asString().data(), 8);
|
||||
ivecs.push_back(newIvec);
|
||||
}
|
||||
LTS-END*/
|
||||
keys.push_back(newKey);
|
||||
keySizes.push_back(0);
|
||||
firstms = keys[0].getTime();
|
||||
|
@ -1376,6 +1427,7 @@ namespace DTSC {
|
|||
result += 11 + (keySizes.size() * 4) + 4;
|
||||
}
|
||||
result += parts.size() * 9;
|
||||
result += (ivecs.size() * 8) + 12; /*LTS*/
|
||||
if (type == "audio") {
|
||||
result += 49;
|
||||
} else if (type == "video") {
|
||||
|
@ -1433,6 +1485,13 @@ namespace DTSC {
|
|||
for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
|
||||
writePointer(p, it->getData(), 9);
|
||||
}
|
||||
/*LTS-START*/
|
||||
writePointer(p, "\000\005ivecs\002", 8);
|
||||
writePointer(p, convertInt(ivecs.size() * 8), 4);
|
||||
for (std::deque<Ivec>::iterator it = ivecs.begin(); it != ivecs.end(); it++) {
|
||||
writePointer(p, it->getData(), 8);
|
||||
}
|
||||
/*LTS-END*/
|
||||
writePointer(p, "\000\007trackid\001", 10);
|
||||
writePointer(p, convertLongLong(trackID), 8);
|
||||
if (missedFrags) {
|
||||
|
@ -1503,6 +1562,13 @@ namespace DTSC {
|
|||
for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
|
||||
conn.SendNow(it->getData(), 9);
|
||||
}
|
||||
/*LTS-START*/
|
||||
conn.SendNow("\000\005ivecs\002", 8);
|
||||
conn.SendNow(convertInt(ivecs.size() * 8), 4);
|
||||
for (std::deque<Ivec>::iterator it = ivecs.begin(); it != ivecs.end(); it++) {
|
||||
conn.SendNow(it->getData(), 8);
|
||||
}
|
||||
/*LTS-END*/
|
||||
conn.SendNow("\000\007trackid\001", 10);
|
||||
conn.SendNow(convertLongLong(trackID), 8);
|
||||
if (missedFrags) {
|
||||
|
@ -1643,6 +1709,14 @@ namespace DTSC {
|
|||
tmp.append(it->getData(), 9);
|
||||
}
|
||||
result["parts"] = tmp;
|
||||
/*LTS-START*/
|
||||
tmp = "";
|
||||
tmp.reserve(ivecs.size() * 8);
|
||||
for (std::deque<Ivec>::iterator it = ivecs.begin(); it != ivecs.end(); it++) {
|
||||
tmp.append(it->getData(), 8);
|
||||
}
|
||||
result["ivecs"] = tmp;
|
||||
/*LTS-END*/
|
||||
result["trackid"] = trackID;
|
||||
result["firstms"] = (long long)firstms;
|
||||
result["lastms"] = (long long)lastms;
|
||||
|
|
62
lib/encryption.cpp
Normal file
62
lib/encryption.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include "encryption.h"
|
||||
#include "auth.h"
|
||||
//#include <openssl/aes.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <cstdio>
|
||||
#include <cstdio>
|
||||
|
||||
namespace Encryption {
|
||||
std::string AES_Crypt(const std::string & data, const std::string & key, std::string & ivec) {
|
||||
unsigned char * outData = (unsigned char *)malloc(data.size() * sizeof(char));
|
||||
//unsigned int stateNum = 0;
|
||||
unsigned char stateEcount[16];
|
||||
unsigned char stateIvec[16];
|
||||
memset(stateEcount, 0, 16);
|
||||
memcpy(stateIvec, ivec.c_str(), 8);
|
||||
memset(stateIvec + 8, 0, 8);
|
||||
/// \todo Implement this ^_^
|
||||
//AES_KEY cryptKey;
|
||||
//if (AES_set_encrypt_key((unsigned char *)key.c_str(), 128, &cryptKey)) {
|
||||
// abort();
|
||||
//}
|
||||
/// \todo loop per 128 bytes....
|
||||
//AES_ctr128_encrypt((unsigned char *)data.c_str(), outData, data.size(), &cryptKey, stateIvec, stateEcount, &stateNum);
|
||||
std::string result = std::string((char *)outData, data.size());
|
||||
free(outData);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string PR_GenerateContentKey(std::string & keyseed, std::string & keyid) {
|
||||
char contentKey[16];
|
||||
char dataBlob[92];
|
||||
char keyA[32], keyB[32], keyC[32];
|
||||
std::string keyidBytes = PR_GuidToByteArray(keyid);
|
||||
memcpy(dataBlob, keyseed.c_str(), 30);
|
||||
memcpy(dataBlob+30, keyidBytes.data(), 16);
|
||||
memcpy(dataBlob+46, keyseed.c_str(), 30);
|
||||
memcpy(dataBlob+76, keyidBytes.data(), 16);
|
||||
Secure::sha256bin(dataBlob, 46, keyA);
|
||||
Secure::sha256bin(dataBlob, 76, keyB);
|
||||
Secure::sha256bin(dataBlob, 92, keyC);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
contentKey[i] = keyA[i] ^ keyA[i + 16] ^ keyB[i] ^ keyB[i + 16] ^ keyC[i] ^ keyC[i + 16];
|
||||
}
|
||||
return std::string(contentKey, 16);
|
||||
}
|
||||
|
||||
std::string PR_GuidToByteArray(std::string & guid) {
|
||||
std::string result;
|
||||
result = guid[3];
|
||||
result += guid[2];
|
||||
result += guid[1];
|
||||
result += guid[0];
|
||||
result += guid[5];
|
||||
result += guid[4];
|
||||
result += guid[7];
|
||||
result += guid[6];
|
||||
result += guid.substr(8);
|
||||
return result;
|
||||
}
|
||||
}
|
8
lib/encryption.h
Normal file
8
lib/encryption.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <string>
|
||||
|
||||
namespace Encryption {
|
||||
std::string AES_Crypt(const std::string & data, const std::string & key, std::string & ivec);
|
||||
|
||||
std::string PR_GenerateContentKey(std::string & keyseed, std::string & keyid);
|
||||
std::string PR_GuidToByteArray(std::string & guid);
|
||||
}
|
61
lib/mp4.cpp
61
lib/mp4.cpp
|
@ -4,7 +4,9 @@
|
|||
#include "mp4.h"
|
||||
#include "mp4_adobe.h"
|
||||
#include "mp4_ms.h"
|
||||
#include "mp4_dash.h"
|
||||
#include "mp4_generic.h"
|
||||
#include "mp4_encryption.h" // /*LTS*/
|
||||
#include "json.h"
|
||||
|
||||
#include "defines.h"
|
||||
|
@ -104,6 +106,7 @@ namespace MP4 {
|
|||
}
|
||||
} else if (size == 0) {
|
||||
fseek(newData, 0, SEEK_END);
|
||||
return true;
|
||||
}
|
||||
DONTEVEN_MSG("skipping size 0x%.8lX", size);
|
||||
if (fseek(newData, pos + size, SEEK_SET) == 0) {
|
||||
|
@ -132,6 +135,10 @@ namespace MP4 {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (size == 0){//no else if, because the extended size MAY be 0...
|
||||
fseek(newData, 0, SEEK_END);
|
||||
size = ftell(newData) - pos;
|
||||
}
|
||||
fseek(newData, pos, SEEK_SET);
|
||||
data = (char *)realloc(data, size);
|
||||
data_size = size;
|
||||
|
@ -160,6 +167,9 @@ namespace MP4 {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (size == 0){
|
||||
size = newData.size();
|
||||
}
|
||||
if (newData.size() >= size) {
|
||||
data = (char *)realloc(data, size);
|
||||
data_size = size;
|
||||
|
@ -243,6 +253,9 @@ namespace MP4 {
|
|||
case 0x74666864:
|
||||
return ((TFHD *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x68766343:
|
||||
return ((HVCC *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x61766343:
|
||||
return ((AVCC *)this)->toPrettyString(indent);
|
||||
break;
|
||||
|
@ -252,6 +265,9 @@ namespace MP4 {
|
|||
case 0x66747970:
|
||||
return ((FTYP *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x73747970:
|
||||
return ((STYP*)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x6D6F6F76:
|
||||
return ((MOOV *)this)->toPrettyString(indent);
|
||||
break;
|
||||
|
@ -344,11 +360,18 @@ namespace MP4 {
|
|||
break;
|
||||
case 0x6D703461://mp4a
|
||||
case 0x656E6361://enca
|
||||
case 0x61632D33://ac-3
|
||||
return ((MP4A *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x64616333:
|
||||
return ((DAC3 *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x61616320:
|
||||
return ((AAC *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x68657631:
|
||||
return ((HEV1 *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x61766331:
|
||||
return ((AVC1 *)this)->toPrettyString(indent);
|
||||
break;
|
||||
|
@ -356,6 +379,15 @@ namespace MP4 {
|
|||
case 0x656E6376://encv
|
||||
return ((H264 *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x6669656C:
|
||||
return ((FIEL *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x74726566:
|
||||
return ((TREF *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x676D6864:
|
||||
return ((GMHD *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x65647473:
|
||||
return ((EDTS *)this)->toPrettyString(indent);
|
||||
break;
|
||||
|
@ -377,12 +409,36 @@ namespace MP4 {
|
|||
case 0x75756964:
|
||||
return ((UUID *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x73696478:
|
||||
return ((SIDX*)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x74666474:
|
||||
return ((TFDT*)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x696F6473:
|
||||
return ((IODS*)this)->toPrettyString(indent);
|
||||
break;
|
||||
/*LTS-START*/
|
||||
case 0x73696E66:
|
||||
return ((SINF *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x66726D61:
|
||||
return ((FRMA *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x7363686D:
|
||||
return ((SCHM *)this)->toPrettyString(indent);
|
||||
break;
|
||||
case 0x73636869:
|
||||
return ((SCHI *)this)->toPrettyString(indent);
|
||||
break;
|
||||
/*LTS-END*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
std::string retval = std::string(indent, ' ') + "Unimplemented pretty-printing for box " + std::string(data + 4, 4) + "\n";
|
||||
std::stringstream retval;
|
||||
retval << std::string(indent, ' ') << "Unimplemented pretty-printing for box " << std::string(data + 4, 4) << " (" << ntohl(((int*)data)[0]) << ")\n";
|
||||
/// \todo Implement hexdump for unimplemented boxes?
|
||||
return retval;
|
||||
return retval.str();
|
||||
}
|
||||
|
||||
/// Sets the 8 bits integer at the given index.
|
||||
|
@ -804,4 +860,5 @@ namespace MP4 {
|
|||
}
|
||||
return r.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
240
lib/mp4_dash.cpp
Normal file
240
lib/mp4_dash.cpp
Normal file
|
@ -0,0 +1,240 @@
|
|||
#include "mp4_dash.h"
|
||||
#include "defines.h"
|
||||
|
||||
namespace MP4 {
|
||||
SIDX::SIDX() {
|
||||
memcpy(data + 4, "sidx", 4);
|
||||
setVersion(0);
|
||||
setFlags(0);
|
||||
}
|
||||
|
||||
void SIDX::setReferenceID(uint32_t newReferenceID) {
|
||||
setInt32(newReferenceID, 4);
|
||||
}
|
||||
|
||||
uint32_t SIDX::getReferenceID() {
|
||||
return getInt32(4);
|
||||
}
|
||||
|
||||
void SIDX::setTimescale(uint32_t newTimescale) {
|
||||
setInt32(newTimescale, 8);
|
||||
}
|
||||
|
||||
uint32_t SIDX::getTimescale() {
|
||||
return getInt32(8);
|
||||
}
|
||||
|
||||
void SIDX::setEarliestPresentationTime(uint64_t newEarliestPresentationTime) {
|
||||
if (getVersion() == 0) {
|
||||
setInt32(newEarliestPresentationTime, 12);
|
||||
} else {
|
||||
setInt64(newEarliestPresentationTime, 12);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t SIDX::getEarliestPresentationTime() {
|
||||
if (getVersion() == 0) {
|
||||
return getInt32(12);
|
||||
}
|
||||
return getInt64(12);
|
||||
}
|
||||
|
||||
void SIDX::setFirstOffset(uint64_t newFirstOffset) {
|
||||
if (getVersion() == 0) {
|
||||
setInt32(newFirstOffset, 16);
|
||||
} else {
|
||||
setInt64(newFirstOffset, 20);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t SIDX::getFirstOffset() {
|
||||
if (getVersion() == 0) {
|
||||
return getInt32(16);
|
||||
}
|
||||
return getInt64(20);
|
||||
}
|
||||
|
||||
uint16_t SIDX::getReferenceCount() {
|
||||
if (getVersion() == 0) {
|
||||
return getInt16(22);
|
||||
}
|
||||
return getInt16(30);
|
||||
}
|
||||
|
||||
void SIDX::setReference(sidxReference & newRef, size_t index) {
|
||||
if (index >= getReferenceCount()) {
|
||||
setInt16(index + 1, (getVersion() == 0 ? 22 : 30));
|
||||
}
|
||||
uint32_t offset = 24 + (index * 12) + (getVersion() == 0 ? 0 : 8);
|
||||
uint32_t tmp = (newRef.referenceType ? 0x80000000 : 0) | newRef.referencedSize;
|
||||
setInt32(tmp, offset);
|
||||
setInt32(newRef.subSegmentDuration, offset + 4);
|
||||
tmp = (newRef.sapStart ? 0x80000000 : 0) | ((newRef.sapType & 0x7) << 24) | newRef.sapDeltaTime;
|
||||
setInt32(tmp, offset + 8);
|
||||
}
|
||||
|
||||
sidxReference SIDX::getReference(size_t index) {
|
||||
sidxReference result;
|
||||
if (index >= getReferenceCount()) {
|
||||
DEBUG_MSG(DLVL_DEVEL, "Warning, attempt to obtain reference out of bounds");
|
||||
return result;
|
||||
}
|
||||
uint32_t offset = 24 + (index * 12) + (getVersion() == 0 ? 0 : 8);
|
||||
uint32_t tmp = getInt32(offset);
|
||||
result.referenceType = tmp & 0x80000000;
|
||||
result.referencedSize = tmp & 0x7FFFFFFF;
|
||||
result.subSegmentDuration = getInt32(offset + 4);
|
||||
tmp = getInt32(offset + 8);
|
||||
result.sapStart = tmp & 0x80000000;
|
||||
result.sapType = (tmp & 0x70000000) >> 24;
|
||||
result.sapDeltaTime = (tmp & 0x0FFFFFFF);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string SIDX::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[sidx] Segment Index Box (" << boxedSize() << ")" << std::endl;
|
||||
r << fullBox::toPrettyString(indent);
|
||||
r << std::string(indent + 1, ' ') << "ReferenceID " << getReferenceID() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Timescale " << getTimescale() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "EarliestPresentationTime " << getEarliestPresentationTime() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "FirstOffset " << getFirstOffset() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "References [" << getReferenceCount() << "]" << std::endl;
|
||||
for (int i = 0; i < getReferenceCount(); i++) {
|
||||
sidxReference tmp = getReference(i);
|
||||
r << std::string(indent + 2, ' ') << "[" << i << "]" << std::endl;
|
||||
r << std::string(indent + 3, ' ') << "ReferenceType " << (int)tmp.referenceType << std::endl;
|
||||
r << std::string(indent + 3, ' ') << "ReferencedSize " << tmp.referencedSize << std::endl;
|
||||
r << std::string(indent + 3, ' ') << "SubSegmentDuration " << tmp.subSegmentDuration << std::endl;
|
||||
r << std::string(indent + 3, ' ') << "StartsWithSAP " << (int)tmp.sapStart << std::endl;
|
||||
r << std::string(indent + 3, ' ') << "SAP Type " << (int)tmp.sapType << std::endl;
|
||||
r << std::string(indent + 3, ' ') << "SAP DeltaTime " << tmp.sapDeltaTime << std::endl;
|
||||
}
|
||||
return r.str();
|
||||
}
|
||||
|
||||
TFDT::TFDT() {
|
||||
memcpy(data + 4, "tfdt", 4);
|
||||
setVersion(0);
|
||||
setFlags(0);
|
||||
}
|
||||
|
||||
void TFDT::setBaseMediaDecodeTime(uint64_t newBaseMediaDecodeTime) {
|
||||
if (getVersion() == 1) {
|
||||
setInt64(newBaseMediaDecodeTime, 4);
|
||||
} else {
|
||||
setInt32(newBaseMediaDecodeTime, 4);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t TFDT::getBaseMediaDecodeTime() {
|
||||
if (getVersion() == 1) {
|
||||
return getInt64(4);
|
||||
}
|
||||
return getInt32(4);
|
||||
}
|
||||
|
||||
std::string TFDT::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[tfdt] Track Fragment Base Media Decode Time Box (" << boxedSize() << ")" << std::endl;
|
||||
r << fullBox::toPrettyString(indent);
|
||||
r << std::string(indent + 1, ' ') << "BaseMediaDecodeTime " << getBaseMediaDecodeTime() << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
|
||||
IODS::IODS() {
|
||||
memcpy(data + 4, "iods", 4);
|
||||
setVersion(0);
|
||||
setFlags(0);
|
||||
setIODTypeTag(0x10);
|
||||
setDescriptorTypeLength(0x07);
|
||||
setODID(0x004F);
|
||||
setODProfileLevel(0xFF);
|
||||
setODSceneLevel(0xFF);
|
||||
setODAudioLevel(0xFF);
|
||||
setODVideoLevel(0xFF);
|
||||
setODGraphicsLevel(0xFF);
|
||||
}
|
||||
|
||||
void IODS::setIODTypeTag(char value) {
|
||||
setInt8(value, 4);
|
||||
}
|
||||
|
||||
char IODS::getIODTypeTag() {
|
||||
return getInt8(4);
|
||||
}
|
||||
|
||||
void IODS::setDescriptorTypeLength(char length) {
|
||||
setInt8(length, 5);
|
||||
}
|
||||
|
||||
char IODS::getDescriptorTypeLength() {
|
||||
return getInt8(5);
|
||||
}
|
||||
|
||||
void IODS::setODID(short id) {
|
||||
setInt16(id, 6);
|
||||
}
|
||||
|
||||
short IODS::getODID() {
|
||||
return getInt16(6);
|
||||
}
|
||||
|
||||
void IODS::setODProfileLevel(char value) {
|
||||
setInt8(value, 8);
|
||||
}
|
||||
|
||||
char IODS::getODProfileLevel() {
|
||||
return getInt8(8);
|
||||
}
|
||||
|
||||
void IODS::setODSceneLevel(char value) {
|
||||
setInt8(value, 9);
|
||||
}
|
||||
|
||||
char IODS::getODSceneLevel() {
|
||||
return getInt8(9);
|
||||
}
|
||||
|
||||
void IODS::setODAudioLevel(char value) {
|
||||
setInt8(value, 10);
|
||||
}
|
||||
|
||||
char IODS::getODAudioLevel() {
|
||||
return getInt8(10);
|
||||
}
|
||||
|
||||
void IODS::setODVideoLevel(char value) {
|
||||
setInt8(value, 11);
|
||||
}
|
||||
|
||||
char IODS::getODVideoLevel() {
|
||||
return getInt8(11);
|
||||
}
|
||||
|
||||
void IODS::setODGraphicsLevel(char value) {
|
||||
setInt8(value, 12);
|
||||
}
|
||||
|
||||
char IODS::getODGraphicsLevel() {
|
||||
return getInt8(12);
|
||||
}
|
||||
|
||||
|
||||
std::string IODS::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[iods] IODS Box (" << boxedSize() << ")" << std::endl;
|
||||
r << fullBox::toPrettyString(indent);
|
||||
r << std::string(indent + 2, ' ') << "IOD Type Tag: " << std::hex << std::setw(2) << std::setfill('0') << (int)getIODTypeTag() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "DescriptorTypeLength: " << std::hex << std::setw(2) << std::setfill('0') << (int)getDescriptorTypeLength() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "OD ID: " << std::hex << std::setw(4) << std::setfill('0') << (int)getODID() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "OD Profile Level: " << std::hex << std::setw(2) << std::setfill('0') << (int)getODProfileLevel() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "OD Scene Level: " << std::hex << std::setw(2) << std::setfill('0') << (int)getODSceneLevel() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "OD Audio Level: " << std::hex << std::setw(2) << std::setfill('0') << (int)getODAudioLevel() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "OD Video Level: " << std::hex << std::setw(2) << std::setfill('0') << (int)getODVideoLevel() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "OD Graphics Level: " << std::hex << std::setw(2) << std::setfill('0') << (int)getODGraphicsLevel() << std::dec << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
}
|
||||
|
||||
|
73
lib/mp4_dash.h
Normal file
73
lib/mp4_dash.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
#include "mp4.h"
|
||||
|
||||
namespace MP4 {
|
||||
struct sidxReference {
|
||||
bool referenceType;
|
||||
uint32_t referencedSize;
|
||||
uint32_t subSegmentDuration;
|
||||
bool sapStart;
|
||||
uint8_t sapType;
|
||||
uint32_t sapDeltaTime;
|
||||
};
|
||||
|
||||
class SIDX: public fullBox {
|
||||
public:
|
||||
SIDX();
|
||||
void setReferenceID(uint32_t newReferenceID);
|
||||
uint32_t getReferenceID();
|
||||
void setTimescale(uint32_t newTimescale);
|
||||
uint32_t getTimescale();
|
||||
|
||||
void setEarliestPresentationTime(uint64_t newEarliestPresentationTime);
|
||||
uint64_t getEarliestPresentationTime();
|
||||
void setFirstOffset(uint64_t newFirstOffset);
|
||||
uint64_t getFirstOffset();
|
||||
|
||||
uint16_t getReferenceCount();
|
||||
void setReference(sidxReference & newRef, size_t index);
|
||||
sidxReference getReference(size_t index);
|
||||
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class TFDT: public fullBox {
|
||||
public:
|
||||
TFDT();
|
||||
void setBaseMediaDecodeTime(uint64_t newBaseMediaDecodeTime);
|
||||
uint64_t getBaseMediaDecodeTime();
|
||||
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class IODS: public fullBox {
|
||||
public:
|
||||
IODS();
|
||||
void setIODTypeTag(char value);
|
||||
char getIODTypeTag();
|
||||
|
||||
void setDescriptorTypeLength(char length);
|
||||
char getDescriptorTypeLength();
|
||||
|
||||
void setODID(short id);
|
||||
short getODID();
|
||||
|
||||
void setODProfileLevel(char value);
|
||||
char getODProfileLevel();
|
||||
|
||||
void setODSceneLevel(char value);
|
||||
char getODSceneLevel();
|
||||
|
||||
void setODAudioLevel(char value);
|
||||
char getODAudioLevel();
|
||||
|
||||
void setODVideoLevel(char value);
|
||||
char getODVideoLevel();
|
||||
|
||||
void setODGraphicsLevel(char value);
|
||||
char getODGraphicsLevel();
|
||||
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
}
|
||||
|
456
lib/mp4_encryption.cpp
Normal file
456
lib/mp4_encryption.cpp
Normal file
|
@ -0,0 +1,456 @@
|
|||
#include "mp4_encryption.h"
|
||||
|
||||
namespace MP4 {
|
||||
|
||||
UUID_SampleEncryption::UUID_SampleEncryption() {
|
||||
setUUID((std::string)"a2394f52-5a9b-4f14-a244-6c427c648df4");
|
||||
}
|
||||
|
||||
void UUID_SampleEncryption::setVersion(uint32_t newVersion) {
|
||||
setInt8(newVersion, 16);
|
||||
}
|
||||
|
||||
uint32_t UUID_SampleEncryption::getVersion() {
|
||||
return getInt8(16);
|
||||
}
|
||||
|
||||
void UUID_SampleEncryption::setFlags(uint32_t newFlags) {
|
||||
setInt24(newFlags, 17);
|
||||
}
|
||||
|
||||
uint32_t UUID_SampleEncryption::getFlags() {
|
||||
return getInt24(17);
|
||||
}
|
||||
|
||||
void UUID_SampleEncryption::setAlgorithmID(uint32_t newAlgorithmID) {
|
||||
if (getFlags() & 0x01) {
|
||||
setInt24(newAlgorithmID, 20);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t UUID_SampleEncryption::getAlgorithmID() {
|
||||
if (getFlags() & 0x01) {
|
||||
return getInt24(20);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UUID_SampleEncryption::setIVSize(uint32_t newIVSize) {
|
||||
if (getFlags() & 0x01) {
|
||||
setInt8(newIVSize, 23);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t UUID_SampleEncryption::getIVSize() {
|
||||
if (getFlags() & 0x01) {
|
||||
return getInt8(23);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UUID_SampleEncryption::setKID(std::string newKID) {
|
||||
if (newKID == "") {
|
||||
return;
|
||||
}
|
||||
if (getFlags() & 0x01) {
|
||||
while (newKID.size() < 16) {
|
||||
newKID += (char)0x00;
|
||||
}
|
||||
for (int i = 0; i < 16; i++) {
|
||||
setInt8(newKID[i], 24 + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string UUID_SampleEncryption::getKID() {
|
||||
if (getFlags() & 0x01) {
|
||||
std::string result;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
result += (char)getInt8(24 + i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
uint32_t UUID_SampleEncryption::getSampleCount() {
|
||||
int myOffset = 20;
|
||||
if (getFlags() & 0x01) {
|
||||
myOffset += 20;
|
||||
}
|
||||
return getInt32(myOffset);
|
||||
}
|
||||
|
||||
#define IV_SIZE 8
|
||||
void UUID_SampleEncryption::setSample(UUID_SampleEncryption_Sample newSample, size_t index) {
|
||||
int myOffset = 20;
|
||||
myOffset += 20 * (getFlags() & 0x01);
|
||||
myOffset += 4;//sampleCount is here;
|
||||
for (unsigned int i = 0; i < std::min(index, (size_t)getSampleCount()); i++) {
|
||||
myOffset += IV_SIZE;
|
||||
if (getFlags() & 0x02) {
|
||||
int entryCount = getInt16(myOffset);
|
||||
myOffset += 2 + (entryCount * 6);
|
||||
}
|
||||
}
|
||||
if (index >= getSampleCount()) {
|
||||
//we are now at the end of currently reserved space, reserve more and adapt offset accordingly.
|
||||
int reserveSize = ((index - getSampleCount())) * (IV_SIZE + (getFlags() & 0x02));
|
||||
reserveSize += IV_SIZE;
|
||||
if (getFlags() & 0x02) {
|
||||
reserveSize += 2 + newSample.Entries.size();
|
||||
}
|
||||
if (!reserve(myOffset, 0, reserveSize)) {
|
||||
return;//Memory errors...
|
||||
}
|
||||
myOffset += (index - getSampleCount()) * (IV_SIZE + (getFlags() & 0x02));
|
||||
}
|
||||
//write box.
|
||||
for (int i = 0; i < IV_SIZE; i++) {
|
||||
setInt8(newSample.InitializationVector[i], myOffset ++);//set and skip
|
||||
}
|
||||
if (getFlags() & 0x02) {
|
||||
setInt16(newSample.Entries.size(), myOffset);
|
||||
myOffset += 2;
|
||||
for (std::vector<UUID_SampleEncryption_Sample_Entry>::iterator it = newSample.Entries.begin(); it != newSample.Entries.end(); it++) {
|
||||
setInt16(it->BytesClear, myOffset);
|
||||
myOffset += 2;
|
||||
setInt32(it->BytesEncrypted, myOffset);
|
||||
myOffset += 4;
|
||||
}
|
||||
}
|
||||
if (index >= getSampleCount()) {
|
||||
setInt32(index + 1, 20 + (20 * (getFlags() & 0x01)));
|
||||
}
|
||||
}
|
||||
|
||||
UUID_SampleEncryption_Sample UUID_SampleEncryption::getSample(size_t index) {
|
||||
if (index >= getSampleCount()) {
|
||||
return UUID_SampleEncryption_Sample();
|
||||
}
|
||||
int myOffset = 20;
|
||||
myOffset += 20 * (getFlags() & 0x01);
|
||||
myOffset += 4;//sampleCount is here
|
||||
for (unsigned int i = 0; i < index; i++) {
|
||||
myOffset += IV_SIZE;
|
||||
if (getFlags() & 0x02) {
|
||||
int entryCount = getInt16(myOffset);
|
||||
myOffset += 2;//skip over entrycount
|
||||
myOffset += entryCount * 6;//skip entryCount sample entries
|
||||
}
|
||||
}
|
||||
UUID_SampleEncryption_Sample result;
|
||||
for (int i = 0; i < IV_SIZE; i++) {
|
||||
result.InitializationVector += getInt8(myOffset++);//read and skip
|
||||
}
|
||||
if (getFlags() & 0x02) {
|
||||
result.NumberOfEntries = getInt16(myOffset);
|
||||
myOffset += 2;
|
||||
for (unsigned int i = 0; i < result.NumberOfEntries; i++) {
|
||||
result.Entries.push_back(UUID_SampleEncryption_Sample_Entry());
|
||||
result.Entries[i].BytesClear = getInt16(myOffset);
|
||||
myOffset += 2;
|
||||
result.Entries[i].BytesEncrypted = getInt32(myOffset);
|
||||
myOffset += 4;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string UUID_SampleEncryption::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[a2394f52-5a9b-4f14-a244-6c427c648df4] Sample Encryption Box (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Version: " << getVersion() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Flags: " << getFlags() << std::endl;
|
||||
if (getFlags() & 0x01) {
|
||||
r << std::string(indent + 1, ' ') << "Algorithm ID: " << getAlgorithmID() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "IV Size: " << getIVSize() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Key ID: " << getKID() << std::endl;
|
||||
}
|
||||
r << std::string(indent + 1, ' ') << "Sample Count: " << getSampleCount() << std::endl;
|
||||
for (unsigned int i = 0; i < getSampleCount(); i++) {
|
||||
UUID_SampleEncryption_Sample tmpSample = getSample(i);
|
||||
r << std::string(indent + 1, ' ') << "[" << i << "]" << std::endl;
|
||||
r << std::string(indent + 3, ' ') << "Initialization Vector: 0x";
|
||||
for (unsigned int j = 0; j < tmpSample.InitializationVector.size(); j++) {
|
||||
r << std::hex << std::setw(2) << std::setfill('0') << (int)tmpSample.InitializationVector[j] << std::dec;
|
||||
}
|
||||
r << std::endl;
|
||||
if (getFlags() & 0x02) {
|
||||
r << std::string(indent + 3, ' ') << "Number of entries: " << tmpSample.NumberOfEntries << std::endl;
|
||||
for (unsigned int j = 0; j < tmpSample.NumberOfEntries; j++) {
|
||||
r << std::string(indent + 3, ' ') << "[" << j << "]" << std::endl;
|
||||
r << std::string(indent + 5, ' ') << "Bytes clear: " << tmpSample.Entries[j].BytesClear << std::endl;
|
||||
r << std::string(indent + 5, ' ') << "Bytes encrypted: " << tmpSample.Entries[j].BytesEncrypted << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r.str();
|
||||
}
|
||||
|
||||
UUID_TrackEncryption::UUID_TrackEncryption() {
|
||||
setUUID((std::string)"8974dbce-7be7-4c51-84f9-7148f9882554");
|
||||
}
|
||||
|
||||
void UUID_TrackEncryption::setVersion(uint32_t newVersion) {
|
||||
setInt8(newVersion, 16);
|
||||
}
|
||||
|
||||
uint32_t UUID_TrackEncryption::getVersion() {
|
||||
return getInt8(16);
|
||||
}
|
||||
|
||||
void UUID_TrackEncryption::setFlags(uint32_t newFlags) {
|
||||
setInt24(newFlags, 17);
|
||||
}
|
||||
|
||||
uint32_t UUID_TrackEncryption::getFlags() {
|
||||
return getInt24(17);
|
||||
}
|
||||
|
||||
void UUID_TrackEncryption::setDefaultAlgorithmID(uint32_t newID) {
|
||||
setInt24(newID, 20);
|
||||
}
|
||||
|
||||
uint32_t UUID_TrackEncryption::getDefaultAlgorithmID() {
|
||||
return getInt24(20);
|
||||
}
|
||||
|
||||
void UUID_TrackEncryption::setDefaultIVSize(uint8_t newIVSize) {
|
||||
setInt8(newIVSize, 23);
|
||||
}
|
||||
|
||||
uint8_t UUID_TrackEncryption::getDefaultIVSize() {
|
||||
return getInt8(23);
|
||||
}
|
||||
|
||||
void UUID_TrackEncryption::setDefaultKID(std::string newKID) {
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
if (i < newKID.size()) {
|
||||
setInt8(newKID[i], 24 + i);
|
||||
} else {
|
||||
setInt8(0x00, 24 + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string UUID_TrackEncryption::getDefaultKID() {
|
||||
std::string result;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
result += getInt8(24 + i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string UUID_TrackEncryption::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[8974dbce-7be7-4c51-84f9-7148f9882554] Track Encryption Box (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Version: " << getVersion() << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Flags: " << getFlags() << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Default Algorithm ID: " << std::hex << getDefaultAlgorithmID() << std::dec << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Default IV Size: " << (int)getDefaultIVSize() << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Default KID: 16 bytes of binary data." << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
|
||||
UUID_ProtectionSystemSpecificHeader::UUID_ProtectionSystemSpecificHeader() {
|
||||
setUUID((std::string)"d08a4f18-10f3-4a82-b6c8-32d8aba183d3");
|
||||
}
|
||||
|
||||
void UUID_ProtectionSystemSpecificHeader::setVersion(uint32_t newVersion) {
|
||||
setInt8(newVersion, 16);
|
||||
}
|
||||
|
||||
uint32_t UUID_ProtectionSystemSpecificHeader::getVersion() {
|
||||
return getInt8(16);
|
||||
}
|
||||
|
||||
void UUID_ProtectionSystemSpecificHeader::setFlags(uint32_t newFlags) {
|
||||
setInt24(newFlags, 17);
|
||||
}
|
||||
|
||||
uint32_t UUID_ProtectionSystemSpecificHeader::getFlags() {
|
||||
return getInt24(17);
|
||||
}
|
||||
|
||||
void UUID_ProtectionSystemSpecificHeader::setSystemID(std::string newID) {
|
||||
for (unsigned int i = 0; i < 16; i++) {
|
||||
if (i < newID.size()) {
|
||||
setInt8(newID[i], 20 + i);
|
||||
} else {
|
||||
setInt8(0x00, 20 + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string UUID_ProtectionSystemSpecificHeader::getSystemID() {
|
||||
std::string result;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
result += getInt8(20 + i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t UUID_ProtectionSystemSpecificHeader::getDataSize() {
|
||||
return getInt32(36);
|
||||
}
|
||||
|
||||
void UUID_ProtectionSystemSpecificHeader::setData(std::string newData) {
|
||||
setInt32(newData.size(), 36);
|
||||
for (unsigned int i = 0; i < newData.size(); i++) {
|
||||
setInt8(newData[i], 40 + i);
|
||||
}
|
||||
}
|
||||
|
||||
std::string UUID_ProtectionSystemSpecificHeader::getData() {
|
||||
std::string result;
|
||||
for (unsigned int i = 0; i < getDataSize(); i++) {
|
||||
result += getInt8(40 + i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string UUID_ProtectionSystemSpecificHeader::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[d08a4f18-10f3-4a82-b6c8-32d8aba183d3] Protection System Specific Header Box (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Version: " << getVersion() << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Flags: " << getFlags() << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "System ID: " << getSystemID() << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Data Size: " << getDataSize() << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Data: " << getData().size() << " bytes of data." << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
|
||||
SINF::SINF() {
|
||||
memcpy(data + 4, "sinf", 4);
|
||||
}
|
||||
|
||||
void SINF::setEntry(Box & newEntry, uint32_t no) {
|
||||
if (no > 4) {
|
||||
return;
|
||||
}
|
||||
int tempLoc = 0;
|
||||
for (unsigned int i = 0; i < no; i++) {
|
||||
tempLoc += Box(getBox(tempLoc).asBox(), false).boxedSize();
|
||||
}
|
||||
setBox(newEntry, tempLoc);
|
||||
}
|
||||
|
||||
Box & SINF::getEntry(uint32_t no) {
|
||||
static Box ret = Box((char *)"\000\000\000\010erro", false);
|
||||
if (no > 4) {
|
||||
ret = Box((char *)"\000\000\000\010erro", false);
|
||||
return ret;
|
||||
}
|
||||
int tempLoc = 0;
|
||||
for (unsigned int i = 0; i < no; i++) {
|
||||
tempLoc += Box(getBox(tempLoc).asBox(), false).boxedSize();
|
||||
}
|
||||
ret = Box(getBox(tempLoc).asBox(), false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string SINF::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
std::cerr << payloadOffset << std::endl;
|
||||
r << std::string(indent, ' ') << "[sinf] Protection Scheme Info Box (" << boxedSize() << ")" << std::endl;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!getEntry(i).isType("erro")) {
|
||||
r << getEntry(i).toPrettyString(indent + 2);
|
||||
}
|
||||
}
|
||||
r << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
|
||||
FRMA::FRMA() {
|
||||
memcpy(data + 4, "frma", 4);
|
||||
}
|
||||
|
||||
void FRMA::setOriginalFormat(std::string newFormat) {
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
if (i < newFormat.size()) {
|
||||
setInt8(newFormat[i], i);
|
||||
} else {
|
||||
setInt8(0x00, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string FRMA::getOriginalFormat() {
|
||||
return std::string(payload(), 4);
|
||||
}
|
||||
|
||||
std::string FRMA::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[frma] Original Format Box (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "Original Format: " << getOriginalFormat() << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
|
||||
SCHM::SCHM() {
|
||||
memcpy(data + 4, "schm", 4);
|
||||
}
|
||||
|
||||
void SCHM::setSchemeType(uint32_t newType) {
|
||||
setInt32(htonl(newType), 4);
|
||||
}
|
||||
|
||||
uint32_t SCHM::getSchemeType() {
|
||||
return ntohl(getInt32(4));
|
||||
}
|
||||
|
||||
void SCHM::setSchemeVersion(uint32_t newVersion) {
|
||||
setInt32(htonl(newVersion), 8);
|
||||
}
|
||||
|
||||
uint32_t SCHM::getSchemeVersion() {
|
||||
return ntohl(getInt32(8));
|
||||
}
|
||||
|
||||
void SCHM::setSchemeURI(std::string newURI) {
|
||||
setFlags(getFlags() | 0x01);
|
||||
setString(newURI, 12);
|
||||
}
|
||||
|
||||
std::string SCHM::getSchemeURI() {
|
||||
if (getFlags() & 0x01) {
|
||||
return getString(12);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string SCHM::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[schm] Scheme Type Box (" << boxedSize() << ")" << std::endl;
|
||||
char tmpStr[10];
|
||||
int tmpInt = getSchemeType();
|
||||
sprintf(tmpStr, "%.4s", (char *)&tmpInt);
|
||||
r << std::string(indent + 2, ' ') << "SchemeType: " << std::string(tmpStr, 4) << std::endl;
|
||||
r << std::string(indent + 2, ' ') << "SchemeVersion: 0x" << std::hex << std::setw(8) << std::setfill('0') << getSchemeVersion() << std::dec << std::endl;
|
||||
if (getFlags() & 0x01) {
|
||||
r << std::string(indent + 2, ' ') << "SchemeURI: " << getSchemeURI() << std::endl;
|
||||
}
|
||||
return r.str();
|
||||
}
|
||||
|
||||
SCHI::SCHI() {
|
||||
memcpy(data + 4, "schi", 4);
|
||||
}
|
||||
|
||||
void SCHI::setContent(Box & newContent) {
|
||||
setBox(newContent, 0);
|
||||
}
|
||||
|
||||
Box & SCHI::getContent() {
|
||||
return getBox(0);
|
||||
}
|
||||
|
||||
std::string SCHI::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[schi] Scheme Information Box (" << boxedSize() << ")" << std::endl;
|
||||
r << getContent().toPrettyString(indent + 2);
|
||||
return r.str();
|
||||
}
|
||||
|
||||
}
|
104
lib/mp4_encryption.h
Normal file
104
lib/mp4_encryption.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
#include "mp4.h"
|
||||
#include "mp4_ms.h"
|
||||
|
||||
namespace MP4 {
|
||||
|
||||
struct UUID_SampleEncryption_Sample_Entry {
|
||||
uint32_t BytesClear;
|
||||
uint32_t BytesEncrypted;
|
||||
};
|
||||
|
||||
struct UUID_SampleEncryption_Sample {
|
||||
std::string InitializationVector;
|
||||
uint32_t NumberOfEntries;
|
||||
std::vector<UUID_SampleEncryption_Sample_Entry> Entries;
|
||||
};
|
||||
|
||||
class UUID_SampleEncryption: public UUID {
|
||||
public:
|
||||
UUID_SampleEncryption();
|
||||
void setVersion(uint32_t newVersion);
|
||||
uint32_t getVersion();
|
||||
void setFlags(uint32_t newFlags);
|
||||
uint32_t getFlags();
|
||||
void setAlgorithmID(uint32_t newAlgorithmID);
|
||||
uint32_t getAlgorithmID();
|
||||
void setIVSize(uint32_t newIVSize);
|
||||
uint32_t getIVSize();
|
||||
void setKID(std::string newKID);
|
||||
std::string getKID();
|
||||
uint32_t getSampleCount();
|
||||
void setSample(UUID_SampleEncryption_Sample newSample, size_t index);
|
||||
UUID_SampleEncryption_Sample getSample(size_t index);
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class UUID_TrackEncryption: public UUID {
|
||||
public:
|
||||
UUID_TrackEncryption();
|
||||
void setVersion(uint32_t newVersion);
|
||||
uint32_t getVersion();
|
||||
void setFlags(uint32_t newFlags);
|
||||
uint32_t getFlags();
|
||||
void setDefaultAlgorithmID(uint32_t newAlgorithmID);
|
||||
uint32_t getDefaultAlgorithmID();
|
||||
void setDefaultIVSize(uint8_t newIVSize);
|
||||
uint8_t getDefaultIVSize();
|
||||
void setDefaultKID(std::string newKID);
|
||||
std::string getDefaultKID();
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class UUID_ProtectionSystemSpecificHeader: public UUID {
|
||||
public:
|
||||
UUID_ProtectionSystemSpecificHeader();
|
||||
void setVersion(uint32_t newVersion);
|
||||
uint32_t getVersion();
|
||||
void setFlags(uint32_t newFlags);
|
||||
uint32_t getFlags();
|
||||
void setSystemID(std::string newID);
|
||||
std::string getSystemID();
|
||||
void setDataSize(uint32_t newDataSize);
|
||||
uint32_t getDataSize();
|
||||
void setData(std::string newData);
|
||||
std::string getData();
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class SINF: public Box {
|
||||
public:
|
||||
SINF();
|
||||
void setEntry(Box & newEntry, uint32_t no);
|
||||
Box & getEntry(uint32_t no);
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class FRMA: public Box {
|
||||
public:
|
||||
FRMA();
|
||||
void setOriginalFormat(std::string newFormat);
|
||||
std::string getOriginalFormat();
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class SCHM: public fullBox {
|
||||
public:
|
||||
SCHM();
|
||||
void setSchemeType(uint32_t newType);
|
||||
uint32_t getSchemeType();
|
||||
void setSchemeVersion(uint32_t newVersion);
|
||||
uint32_t getSchemeVersion();
|
||||
void setSchemeURI(std::string newURI);
|
||||
std::string getSchemeURI();
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class SCHI: public Box {
|
||||
public:
|
||||
SCHI();
|
||||
void setContent(Box & newContent);
|
||||
Box & getContent();
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
}
|
|
@ -639,6 +639,192 @@ namespace MP4 {
|
|||
memcpy((char *)payload(), (char *)newPayload.c_str(), newPayload.size());
|
||||
}
|
||||
|
||||
HVCC::HVCC() {
|
||||
memcpy(data + 4, "hvcC", 4);
|
||||
}
|
||||
|
||||
void HVCC::setConfigurationVersion(char newVersion) {
|
||||
setInt8(newVersion, 0);
|
||||
}
|
||||
char HVCC::getConfigurationVersion() {
|
||||
return getInt8(0);
|
||||
}
|
||||
|
||||
void HVCC::setGeneralProfileSpace(char newGeneral) {
|
||||
setInt8(((newGeneral << 6) & 0xC0) | (getInt8(1) & 0x3F), 1);
|
||||
}
|
||||
char HVCC::getGeneralProfileSpace(){
|
||||
return ((getInt8(1) >> 6) & 0x03);
|
||||
}
|
||||
|
||||
void HVCC::setGeneralTierFlag(char newGeneral){
|
||||
setInt8(((newGeneral << 5) & 0x20) | (getInt8(1) & 0xDF), 1);
|
||||
}
|
||||
char HVCC::getGeneralTierFlag(){
|
||||
return ((getInt8(1) >> 5) & 0x01);
|
||||
}
|
||||
|
||||
void HVCC::setGeneralProfileIdc(char newGeneral){
|
||||
setInt8((newGeneral & 0x1F) | (getInt8(1) & 0xE0), 1);
|
||||
}
|
||||
char HVCC::getGeneralProfileIdc(){
|
||||
return getInt8(1) & 0x1F;
|
||||
}
|
||||
|
||||
void HVCC::setGeneralProfileCompatibilityFlags(unsigned long newGeneral){
|
||||
setInt32(newGeneral, 2);
|
||||
}
|
||||
unsigned long HVCC::getGeneralProfileCompatibilityFlags(){
|
||||
return getInt32(2);
|
||||
}
|
||||
|
||||
void HVCC::setGeneralConstraintIndicatorFlags(unsigned long long newGeneral){
|
||||
setInt32((newGeneral >> 16) & 0x0000FFFF,6);
|
||||
setInt16(newGeneral & 0xFFFFFF, 10);
|
||||
}
|
||||
unsigned long long HVCC::getGeneralConstraintIndicatorFlags(){
|
||||
unsigned long long result = getInt32(6);
|
||||
result <<= 16;
|
||||
return result | getInt16(10);
|
||||
}
|
||||
|
||||
void HVCC::setGeneralLevelIdc(char newGeneral){
|
||||
setInt8(newGeneral, 12);
|
||||
}
|
||||
char HVCC::getGeneralLevelIdc(){
|
||||
return getInt8(12);
|
||||
}
|
||||
|
||||
void HVCC::setMinSpatialSegmentationIdc(short newIdc){
|
||||
setInt16(newIdc | 0xF000, 13);
|
||||
}
|
||||
short HVCC::getMinSpatialSegmentationIdc(){
|
||||
return getInt16(13) & 0x0FFF;
|
||||
}
|
||||
|
||||
void HVCC::setParallelismType(char newType){
|
||||
setInt8(newType | 0xFC, 15);
|
||||
}
|
||||
char HVCC::getParallelismType(){
|
||||
return getInt8(15) & 0x03;
|
||||
}
|
||||
|
||||
void HVCC::setChromaFormat(char newFormat){
|
||||
setInt8(newFormat | 0xFC, 16);
|
||||
}
|
||||
char HVCC::getChromaFormat(){
|
||||
return getInt8(16) & 0x03;
|
||||
}
|
||||
|
||||
void HVCC::setBitDepthLumaMinus8(char newBitDepthLumaMinus8){
|
||||
setInt8(newBitDepthLumaMinus8 | 0xF8, 17);
|
||||
}
|
||||
char HVCC::getBitDepthLumaMinus8(){
|
||||
return getInt8(17) & 0x07;
|
||||
}
|
||||
|
||||
void HVCC::setBitDepthChromaMinus8(char newBitDepthChromaMinus8){
|
||||
setInt8(newBitDepthChromaMinus8 | 0xF8, 18);
|
||||
}
|
||||
char HVCC::getBitDepthChromaMinus8(){
|
||||
return getInt8(18) & 0x07;
|
||||
}
|
||||
|
||||
void setAverageFramerate(short newFramerate);
|
||||
short HVCC::getAverageFramerate(){
|
||||
return getInt16(19);
|
||||
}
|
||||
void setConstantFramerate(char newFramerate);
|
||||
char HVCC::getConstantFramerate(){
|
||||
return (getInt8(21) >> 6) & 0x03;
|
||||
}
|
||||
void setNumberOfTemporalLayers(char newNumber);
|
||||
char HVCC::getNumberOfTemporalLayers(){
|
||||
return (getInt8(21) >> 3) & 0x07;
|
||||
}
|
||||
void setTemporalIdNested(char newNested);
|
||||
char HVCC::getTemporalIdNested(){
|
||||
return (getInt8(21) >> 2) & 0x01;
|
||||
}
|
||||
void setLengthSizeMinus1(char newLengthSizeMinus1);
|
||||
char HVCC::getLengthSizeMinus1(){
|
||||
return getInt8(21) & 0x03;
|
||||
}
|
||||
|
||||
std::deque<HVCCArrayEntry> HVCC::getArrays(){
|
||||
std::deque<HVCCArrayEntry> r;
|
||||
char arrayCount = getInt8(22);
|
||||
int offset = 23;
|
||||
for(int i = 0; i < arrayCount; i++){
|
||||
HVCCArrayEntry entry;
|
||||
entry.arrayCompleteness = ((getInt8(offset) >> 7) & 0x01);
|
||||
entry.nalUnitType = (getInt8(offset) & 0x3F);
|
||||
offset++;
|
||||
short naluCount = getInt16(offset);
|
||||
offset += 2;
|
||||
for (int j = 0; j < naluCount; j++){
|
||||
short naluSize = getInt16(offset);
|
||||
offset += 2;
|
||||
std::string nalu;
|
||||
for (int k = 0; k < naluSize; k++){
|
||||
nalu += (char)getInt8(offset++);
|
||||
}
|
||||
entry.nalUnits.push_back(nalu);
|
||||
}
|
||||
r.push_back(entry);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string HVCC::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[hvcC] H.265 Init Data (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Configuration Version: " << (int)getConfigurationVersion() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "General Profile Space: " << (int)getGeneralProfileSpace() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "General Tier Flag: " << (int)getGeneralTierFlag() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "General Profile IDC: " << (int)getGeneralProfileIdc() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "General Profile Compatibility Flags: 0x" << std::hex << std::setw(8) << std::setfill('0') << getGeneralProfileCompatibilityFlags() << std::dec << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "General Constraint Indicator Flags: 0x" << std::hex << std::setw(12) << std::setfill('0') << getGeneralConstraintIndicatorFlags() << std::dec << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "General Level IDC: " << (int)getGeneralLevelIdc() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Minimum Spatial Segmentation IDC: " << (int)getMinSpatialSegmentationIdc() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Parallelism Type: " << (int)getParallelismType() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Chroma Format: " << (int)getChromaFormat() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Bit Depth Luma - 8: " << (int)getBitDepthLumaMinus8() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Average Framerate: " << (int)getAverageFramerate() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Constant Framerate: " << (int)getConstantFramerate() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Number of Temporal Layers: " << (int)getNumberOfTemporalLayers() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Temporal ID Nested: " << (int)getTemporalIdNested() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Length Size - 1: " << (int)getLengthSizeMinus1() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Arrays:" << std::endl;
|
||||
std::deque<HVCCArrayEntry> arrays = getArrays();
|
||||
for (unsigned int i = 0; i < arrays.size(); i++){
|
||||
r << std::string(indent + 2, ' ') << "Array with type " << (int)arrays[i].nalUnitType << std::endl;
|
||||
for (unsigned int j = 0; j < arrays[i].nalUnits.size(); j++){
|
||||
r << std::string(indent + 3, ' ') << "Nal unit of " << arrays[i].nalUnits[j].size() << " bytes" << std::endl;
|
||||
}
|
||||
}
|
||||
return r.str();
|
||||
}
|
||||
|
||||
void HVCC::setPayload(std::string newPayload) {
|
||||
if (!reserve(0, payloadSize(), newPayload.size())) {
|
||||
DEBUG_MSG(DLVL_ERROR, "Cannot allocate enough memory for payload");
|
||||
return;
|
||||
}
|
||||
memcpy((char *)payload(), (char *)newPayload.c_str(), newPayload.size());
|
||||
}
|
||||
|
||||
std::string HVCC::asAnnexB() {
|
||||
std::deque<HVCCArrayEntry> arrays = getArrays();
|
||||
std::stringstream r;
|
||||
for (unsigned int i = 0; i < arrays.size(); i++){
|
||||
for (unsigned int j = 0; j < arrays[i].nalUnits.size(); j++){
|
||||
r << (char)0x00 << (char)0x00 << (char)0x00 << (char)0x01 << arrays[i].nalUnits[j];
|
||||
}
|
||||
}
|
||||
return r.str();
|
||||
}
|
||||
|
||||
Descriptor::Descriptor(){
|
||||
data = (char*)malloc(2);
|
||||
data[0] = 0;
|
||||
|
@ -967,19 +1153,85 @@ namespace MP4 {
|
|||
return r.str();
|
||||
}
|
||||
|
||||
FTYP::FTYP() {
|
||||
memcpy(data + 4, "ftyp", 4);
|
||||
setMajorBrand("mp41");
|
||||
setMinorVersion("Mist");
|
||||
setCompatibleBrands("isom", 0);
|
||||
setCompatibleBrands("iso2", 1);
|
||||
setCompatibleBrands("avc1", 2);
|
||||
setCompatibleBrands("mp41", 3);
|
||||
DAC3::DAC3(){
|
||||
memcpy(data + 4, "dac3", 4);
|
||||
setInt24(0,0);
|
||||
}
|
||||
|
||||
void FTYP::setMajorBrand(const char * newMajorBrand) {
|
||||
if (payloadOffset + 3 >= boxedSize()) {
|
||||
if (!reserve(payloadOffset, 0, 4)) {
|
||||
char DAC3::getSampleRateCode(){
|
||||
return getInt8(0) >> 6;
|
||||
}
|
||||
|
||||
void DAC3::setSampleRateCode(char newVal){
|
||||
setInt8(((newVal << 6) & 0xC0) | (getInt8(0) & 0x3F), 0);
|
||||
}
|
||||
|
||||
char DAC3::getBitStreamIdentification(){
|
||||
return (getInt8(0) >> 1) & 0x1F;
|
||||
}
|
||||
|
||||
void DAC3::setBitStreamIdentification(char newVal){
|
||||
setInt8(((newVal << 1) & 0x3E) | (getInt8(0) & 0xC1), 0);
|
||||
}
|
||||
|
||||
char DAC3::getBitStreamMode(){
|
||||
return (getInt16(0) >> 6) & 0x7;
|
||||
}
|
||||
|
||||
void DAC3::setBitStreamMode(char newVal){
|
||||
setInt16(((newVal & 0x7) << 6) | (getInt16(0) & 0xFE3F), 0);
|
||||
}
|
||||
|
||||
char DAC3::getAudioConfigMode(){
|
||||
return (getInt8(1) >> 3) & 0x7;
|
||||
}
|
||||
|
||||
void DAC3::setAudioConfigMode(char newVal){
|
||||
setInt8(((newVal & 0x7) << 3) | (getInt8(1) & 0x38),1);
|
||||
}
|
||||
|
||||
bool DAC3::getLowFrequencyEffectsChannelOn(){
|
||||
return (getInt8(1) >> 2) & 0x1;
|
||||
}
|
||||
|
||||
void DAC3::setLowFrequencyEffectsChannelOn(bool newVal){
|
||||
setInt8(((unsigned int)(newVal & 0x1) << 2) | (getInt8(1) & 0x4),1);
|
||||
}
|
||||
|
||||
char DAC3::getFrameSizeCode(){
|
||||
return ((getInt8(1) & 0x3) << 4) | ((getInt8(2) & 0xF0) >> 4);
|
||||
}
|
||||
|
||||
void DAC3::setFrameSizeCode(char newVal){
|
||||
setInt16(((newVal & 0x3F) << 4) | (getInt16(1) & 0x03F0),1);
|
||||
}
|
||||
|
||||
std::string DAC3::toPrettyString(uint32_t indent){
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[dac3] D-AC3 box (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "FSCOD: " << (int)getSampleRateCode() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "BSID: " << (int)getBitStreamIdentification() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "BSMOD: " << (int)getBitStreamMode() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "ACMOD: " << (int)getAudioConfigMode() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "LFEON: " << (int)getLowFrequencyEffectsChannelOn() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "FrameSizeCode: " << (int)getFrameSizeCode() << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
|
||||
FTYP::FTYP(bool fillDefaults){
|
||||
memcpy(data + 4, "ftyp", 4);
|
||||
if (fillDefaults){
|
||||
setMajorBrand("mp41");
|
||||
setMinorVersion("Mist");
|
||||
setCompatibleBrands("isom",0);
|
||||
setCompatibleBrands("iso2",1);
|
||||
setCompatibleBrands("avc1",2);
|
||||
}
|
||||
}
|
||||
|
||||
void FTYP::setMajorBrand(const char * newMajorBrand){
|
||||
if (payloadOffset + 3 >= boxedSize()){
|
||||
if ( !reserve(payloadOffset, 0, 4)){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -996,11 +1248,16 @@ namespace MP4 {
|
|||
return;
|
||||
}
|
||||
}
|
||||
memset(data + payloadOffset + 4, 0, 4);
|
||||
memcpy(data + payloadOffset + 4, newMinorVersion, 4);
|
||||
}
|
||||
|
||||
std::string FTYP::getMinorVersion() {
|
||||
return std::string(data + payloadOffset + 4, 4);
|
||||
|
||||
std::string FTYP::getMinorVersion(){
|
||||
static char zero[4] = {0,0,0,0};
|
||||
if (memcmp(zero, data+payloadOffset+4, 4) == 0){
|
||||
return "";
|
||||
}
|
||||
return std::string(data+payloadOffset+4, 4);
|
||||
}
|
||||
|
||||
size_t FTYP::getCompatibleBrandsCount() {
|
||||
|
@ -1035,6 +1292,22 @@ namespace MP4 {
|
|||
return r.str();
|
||||
}
|
||||
|
||||
STYP::STYP(bool fillDefaults) : FTYP(fillDefaults) {
|
||||
memcpy(data + 4, "styp", 4);
|
||||
}
|
||||
|
||||
std::string STYP::toPrettyString(uint32_t indent){
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[styp] Fragment Type (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "MajorBrand: " << getMajorBrand() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "MinorVersion: " << getMinorVersion() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "CompatibleBrands (" << getCompatibleBrandsCount() << "):" << std::endl;
|
||||
for (unsigned int i = 0; i < getCompatibleBrandsCount(); i++){
|
||||
r << std::string(indent + 2, ' ') << "[" << i << "] CompatibleBrand: " << getCompatibleBrands(i) << std::endl;
|
||||
}
|
||||
return r.str();
|
||||
}
|
||||
|
||||
MOOV::MOOV() {
|
||||
memcpy(data + 4, "moov", 4);
|
||||
}
|
||||
|
@ -1045,47 +1318,52 @@ namespace MP4 {
|
|||
|
||||
TREX::TREX() {
|
||||
memcpy(data + 4, "trex", 4);
|
||||
setTrackID(0);
|
||||
setDefaultSampleDescriptionIndex(1);
|
||||
setDefaultSampleDuration(0);
|
||||
setDefaultSampleSize(0);
|
||||
setDefaultSampleFlags(0);
|
||||
}
|
||||
|
||||
void TREX::setTrackID(uint32_t newTrackID) {
|
||||
setInt32(newTrackID, 0);
|
||||
|
||||
void TREX::setTrackID(uint32_t newTrackID){
|
||||
setInt32(newTrackID, 4);
|
||||
}
|
||||
|
||||
uint32_t TREX::getTrackID() {
|
||||
return getInt32(0);
|
||||
}
|
||||
|
||||
void TREX::setDefaultSampleDescriptionIndex(uint32_t newDefaultSampleDescriptionIndex) {
|
||||
setInt32(newDefaultSampleDescriptionIndex, 4);
|
||||
}
|
||||
|
||||
uint32_t TREX::getDefaultSampleDescriptionIndex() {
|
||||
|
||||
uint32_t TREX::getTrackID(){
|
||||
return getInt32(4);
|
||||
}
|
||||
|
||||
void TREX::setDefaultSampleDuration(uint32_t newDefaultSampleDuration) {
|
||||
setInt32(newDefaultSampleDuration, 8);
|
||||
|
||||
void TREX::setDefaultSampleDescriptionIndex(uint32_t newDefaultSampleDescriptionIndex){
|
||||
setInt32(newDefaultSampleDescriptionIndex,8);
|
||||
}
|
||||
|
||||
uint32_t TREX::getDefaultSampleDuration() {
|
||||
|
||||
uint32_t TREX::getDefaultSampleDescriptionIndex(){
|
||||
return getInt32(8);
|
||||
}
|
||||
|
||||
void TREX::setDefaultSampleSize(uint32_t newDefaultSampleSize) {
|
||||
setInt32(newDefaultSampleSize, 12);
|
||||
|
||||
void TREX::setDefaultSampleDuration(uint32_t newDefaultSampleDuration){
|
||||
setInt32(newDefaultSampleDuration,12);
|
||||
}
|
||||
|
||||
uint32_t TREX::getDefaultSampleSize() {
|
||||
|
||||
uint32_t TREX::getDefaultSampleDuration(){
|
||||
return getInt32(12);
|
||||
}
|
||||
|
||||
void TREX::setDefaultSampleFlags(uint32_t newDefaultSampleFlags) {
|
||||
setInt32(newDefaultSampleFlags, 16);
|
||||
|
||||
void TREX::setDefaultSampleSize(uint32_t newDefaultSampleSize){
|
||||
setInt32(newDefaultSampleSize,16);
|
||||
}
|
||||
|
||||
uint32_t TREX::getDefaultSampleFlags() {
|
||||
|
||||
uint32_t TREX::getDefaultSampleSize(){
|
||||
return getInt32(16);
|
||||
}
|
||||
|
||||
void TREX::setDefaultSampleFlags(uint32_t newDefaultSampleFlags){
|
||||
setInt32(newDefaultSampleFlags,20);
|
||||
}
|
||||
|
||||
uint32_t TREX::getDefaultSampleFlags(){
|
||||
return getInt32(20);
|
||||
}
|
||||
|
||||
std::string TREX::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
|
@ -1576,28 +1854,28 @@ namespace MP4 {
|
|||
|
||||
int32_t MVHD::getMatrix(size_t index) {
|
||||
int offset = 0;
|
||||
if (getVersion() == 0) {
|
||||
offset = 24 + 2 + 10;
|
||||
} else {
|
||||
offset = 36 + 2 + 10;
|
||||
if (getVersion() == 0){
|
||||
offset = 36;
|
||||
}else{
|
||||
offset = 48;
|
||||
}
|
||||
return getInt32(offset + index * 4);
|
||||
}
|
||||
|
||||
//24 bytes of pre-defined in between
|
||||
void MVHD::setTrackID(uint32_t newTrackID) {
|
||||
if (getVersion() == 0) {
|
||||
setInt32(newTrackID, 86);
|
||||
} else {
|
||||
setInt32(newTrackID, 98);
|
||||
void MVHD::setTrackID(uint32_t newTrackID){
|
||||
if (getVersion() == 0){
|
||||
setInt32(newTrackID, 96);
|
||||
}else{
|
||||
setInt32(newTrackID, 108);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MVHD::getTrackID() {
|
||||
if (getVersion() == 0) {
|
||||
return getInt32(86);
|
||||
} else {
|
||||
return getInt32(98);
|
||||
|
||||
uint32_t MVHD::getTrackID(){
|
||||
if (getVersion() == 0){
|
||||
return getInt32(96);
|
||||
}else{
|
||||
return getInt32(108);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2775,6 +3053,13 @@ namespace MP4 {
|
|||
return getBox(28);
|
||||
}
|
||||
|
||||
/*LTS-START*/
|
||||
Box & AudioSampleEntry::getSINFBox() {
|
||||
static Box ret = Box(getBox(28 + getBoxLen(28)).asBox(), false);
|
||||
return ret;
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
std::string AudioSampleEntry::toPrettyAudioString(uint32_t indent, std::string name) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << name << " (" << boxedSize() << ")" << std::endl;
|
||||
|
@ -2784,6 +3069,11 @@ namespace MP4 {
|
|||
r << std::string(indent + 1, ' ') << "PreDefined: " << getPreDefined() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "SampleRate: " << getSampleRate() << std::endl;
|
||||
r << getCodecBox().toPrettyString(indent + 1) << std::endl;
|
||||
/*LTS-START*/
|
||||
if (isType("enca")) {
|
||||
r << getSINFBox().toPrettyString(indent + 1);
|
||||
}
|
||||
/*LTS-END*/
|
||||
return r.str();
|
||||
}
|
||||
|
||||
|
@ -2803,6 +3093,14 @@ namespace MP4 {
|
|||
return toPrettyAudioString(indent, "[aac ] Advanced Audio Codec");
|
||||
}
|
||||
|
||||
HEV1::HEV1() {
|
||||
memcpy(data + 4, "hev1", 4);
|
||||
}
|
||||
|
||||
std::string HEV1::toPrettyString(uint32_t indent) {
|
||||
return toPrettyVisualString(indent, "[hev1] High Efficiency Video Codec 1");
|
||||
}
|
||||
|
||||
AVC1::AVC1() {
|
||||
memcpy(data + 4, "avc1", 4);
|
||||
}
|
||||
|
@ -2819,6 +3117,34 @@ namespace MP4 {
|
|||
return toPrettyVisualString(indent, "[h264] H.264/MPEG-4 AVC");
|
||||
}
|
||||
|
||||
FIEL::FIEL(){
|
||||
memcpy(data + 4, "fiel", 4);
|
||||
}
|
||||
|
||||
void FIEL::setTotal(char newTotal){
|
||||
setInt8(newTotal, 0);
|
||||
}
|
||||
|
||||
char FIEL::getTotal(){
|
||||
return getInt8(0);
|
||||
}
|
||||
|
||||
void FIEL::setOrder(char newOrder){
|
||||
setInt8(newOrder, 1);
|
||||
}
|
||||
|
||||
char FIEL::getOrder(){
|
||||
return getInt8(1);
|
||||
}
|
||||
|
||||
std::string FIEL::toPrettyString(uint32_t indent) {
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[fiel] Video Field Order Box (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Total: " << (int)getTotal() << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "Order: " << (int)getOrder() << std::endl;
|
||||
return r.str();
|
||||
}
|
||||
|
||||
STSD::STSD(char v, uint32_t f) {
|
||||
memcpy(data + 4, "stsd", 4);
|
||||
setVersion(v);
|
||||
|
@ -2881,6 +3207,14 @@ namespace MP4 {
|
|||
return r.str();
|
||||
}
|
||||
|
||||
GMHD::GMHD() {
|
||||
memcpy(data + 4, "gmhd", 4);
|
||||
}
|
||||
|
||||
TREF::TREF() {
|
||||
memcpy(data + 4, "tref", 4);
|
||||
}
|
||||
|
||||
EDTS::EDTS() {
|
||||
memcpy(data + 4, "edts", 4);
|
||||
}
|
||||
|
@ -3022,3 +3356,4 @@ namespace MP4 {
|
|||
return r.str();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#pragma once
|
||||
#include "mp4.h"
|
||||
|
||||
namespace MP4 {
|
||||
|
@ -122,6 +123,56 @@ namespace MP4 {
|
|||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
struct HVCCArrayEntry {
|
||||
char arrayCompleteness;
|
||||
char nalUnitType;
|
||||
std::deque<std::string> nalUnits;
|
||||
};
|
||||
|
||||
class HVCC: public Box {
|
||||
public:
|
||||
HVCC();
|
||||
void setConfigurationVersion(char newVersion);
|
||||
char getConfigurationVersion();
|
||||
void setGeneralProfileSpace(char newGeneral);
|
||||
char getGeneralProfileSpace();
|
||||
void setGeneralTierFlag(char newGeneral);
|
||||
char getGeneralTierFlag();
|
||||
void setGeneralProfileIdc(char newGeneral);
|
||||
char getGeneralProfileIdc();
|
||||
void setGeneralProfileCompatibilityFlags(unsigned long newGeneral);
|
||||
unsigned long getGeneralProfileCompatibilityFlags();
|
||||
void setGeneralConstraintIndicatorFlags(unsigned long long newGeneral);
|
||||
unsigned long long getGeneralConstraintIndicatorFlags();
|
||||
void setGeneralLevelIdc(char newGeneral);
|
||||
char getGeneralLevelIdc();
|
||||
void setMinSpatialSegmentationIdc(short newIdc);
|
||||
short getMinSpatialSegmentationIdc();
|
||||
void setParallelismType(char newType);
|
||||
char getParallelismType();
|
||||
void setChromaFormat(char newFormat);
|
||||
char getChromaFormat();
|
||||
void setBitDepthLumaMinus8(char newBitDepthLumaMinus8);
|
||||
char getBitDepthLumaMinus8();
|
||||
void setBitDepthChromaMinus8(char newBitDepthChromaMinus8);
|
||||
char getBitDepthChromaMinus8();
|
||||
void setAverageFramerate(short newFramerate);
|
||||
short getAverageFramerate();
|
||||
void setConstantFramerate(char newFramerate);
|
||||
char getConstantFramerate();
|
||||
void setNumberOfTemporalLayers(char newNumber);
|
||||
char getNumberOfTemporalLayers();
|
||||
void setTemporalIdNested(char newNested);
|
||||
char getTemporalIdNested();
|
||||
void setLengthSizeMinus1(char newLengthSizeMinus1);
|
||||
char getLengthSizeMinus1();
|
||||
///\todo Add setter for the array entries
|
||||
std::deque<HVCCArrayEntry> getArrays();
|
||||
std::string asAnnexB();
|
||||
void setPayload(std::string newPayload);
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class Descriptor{
|
||||
public:
|
||||
Descriptor();
|
||||
|
@ -182,9 +233,27 @@ namespace MP4 {
|
|||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class DAC3: public Box {
|
||||
public:
|
||||
DAC3();
|
||||
char getSampleRateCode();//2bits
|
||||
void setSampleRateCode(char newVal);//2bits
|
||||
char getBitStreamIdentification();//5bits
|
||||
void setBitStreamIdentification(char newVal);//5bits
|
||||
char getBitStreamMode();//3 bits
|
||||
void setBitStreamMode(char newVal);//3 bits
|
||||
char getAudioConfigMode();//3 bits
|
||||
void setAudioConfigMode(char newVal);//3 bits
|
||||
bool getLowFrequencyEffectsChannelOn();//1bit
|
||||
void setLowFrequencyEffectsChannelOn(bool newVal);//1bit
|
||||
char getFrameSizeCode();//6bits
|
||||
void setFrameSizeCode(char newVal);//6bits
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class FTYP: public Box {
|
||||
public:
|
||||
FTYP();
|
||||
FTYP(bool fillDefaults = true);
|
||||
void setMajorBrand(const char * newMajorBrand);
|
||||
std::string getMajorBrand();
|
||||
void setMinorVersion(const char * newMinorVersion);
|
||||
|
@ -195,6 +264,12 @@ namespace MP4 {
|
|||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class STYP: public FTYP {
|
||||
public:
|
||||
STYP(bool fillDefaults = true);
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class MOOV: public containerBox {
|
||||
public:
|
||||
MOOV();
|
||||
|
@ -205,7 +280,7 @@ namespace MP4 {
|
|||
MVEX();
|
||||
};
|
||||
|
||||
class TREX: public Box {
|
||||
class TREX: public fullBox {
|
||||
public:
|
||||
TREX();
|
||||
void setTrackID(uint32_t newTrackID);
|
||||
|
@ -600,6 +675,7 @@ namespace MP4 {
|
|||
uint32_t getSampleRate();
|
||||
void setCodecBox(Box & newBox);
|
||||
Box & getCodecBox();
|
||||
Box & getSINFBox(); /*LTS*/
|
||||
std::string toPrettyAudioString(uint32_t indent = 0, std::string name = "");
|
||||
};
|
||||
|
||||
|
@ -615,6 +691,12 @@ namespace MP4 {
|
|||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class HEV1: public VisualSampleEntry {
|
||||
public:
|
||||
HEV1();
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class AVC1: public VisualSampleEntry {
|
||||
public:
|
||||
AVC1();
|
||||
|
@ -627,6 +709,16 @@ namespace MP4 {
|
|||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class FIEL: public Box {
|
||||
public:
|
||||
FIEL();
|
||||
void setTotal(char newTotal);
|
||||
char getTotal();
|
||||
void setOrder(char newOrder);
|
||||
char getOrder();
|
||||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class STSD: public fullBox {
|
||||
public:
|
||||
STSD(char v = 1, uint32_t f = 0);
|
||||
|
@ -637,6 +729,16 @@ namespace MP4 {
|
|||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
|
||||
class GMHD: public containerBox {
|
||||
public:
|
||||
GMHD();
|
||||
};
|
||||
|
||||
class TREF: public containerBox {
|
||||
public:
|
||||
TREF();
|
||||
};
|
||||
|
||||
class EDTS: public containerBox {
|
||||
public:
|
||||
EDTS();
|
||||
|
@ -677,3 +779,4 @@ namespace MP4 {
|
|||
std::string toPrettyString(uint32_t indent = 0);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "mp4_ms.h"
|
||||
#include "mp4_encryption.h" /*LTS*/
|
||||
|
||||
namespace MP4 {
|
||||
|
||||
|
@ -127,6 +128,17 @@ namespace MP4 {
|
|||
if (UUID == "d4807ef2-ca39-4695-8e54-26cb9e46a79f") {
|
||||
return ((UUID_TrackFragmentReference *)this)->toPrettyString(indent);
|
||||
}
|
||||
/*LTS-START*/
|
||||
if (UUID == "a2394f52-5a9b-4f14-a244-6c427c648df4") {
|
||||
return ((UUID_SampleEncryption *)this)->toPrettyString(indent);
|
||||
}
|
||||
if (UUID == "8974dbce-7be7-4c51-84f9-7148f9882554") {
|
||||
return ((UUID_TrackEncryption *)this)->toPrettyString(indent);
|
||||
}
|
||||
if (UUID == "d08a4f18-10f3-4a82-b6c8-32d8aba183d3") {
|
||||
return ((UUID_ProtectionSystemSpecificHeader *)this)->toPrettyString(indent);
|
||||
}
|
||||
/*LTS-END*/
|
||||
std::stringstream r;
|
||||
r << std::string(indent, ' ') << "[uuid] Extension box (" << boxedSize() << ")" << std::endl;
|
||||
r << std::string(indent + 1, ' ') << "UUID: " << UUID << std::endl;
|
||||
|
|
239
lib/rtp.cpp
Normal file
239
lib/rtp.cpp
Normal file
|
@ -0,0 +1,239 @@
|
|||
#include <arpa/inet.h>
|
||||
#include "rtp.h"
|
||||
#include "timing.h"
|
||||
#include "defines.h"
|
||||
|
||||
#define MAX_SEND 1024*4
|
||||
|
||||
namespace RTP {
|
||||
double Packet::startRTCP = 0;
|
||||
|
||||
unsigned int Packet::getHsize() const {
|
||||
return 12 + 4 * getContribCount();
|
||||
}
|
||||
|
||||
unsigned int Packet::getVersion() const {
|
||||
return (data[0] >> 6) & 0x3;
|
||||
}
|
||||
|
||||
unsigned int Packet::getPadding() const {
|
||||
return (data[0] >> 5) & 0x1;
|
||||
}
|
||||
|
||||
unsigned int Packet::getExtension() const {
|
||||
return (data[0] >> 4) & 0x1;
|
||||
}
|
||||
|
||||
unsigned int Packet::getContribCount() const {
|
||||
return (data[0]) & 0xE;
|
||||
}
|
||||
|
||||
unsigned int Packet::getMarker() const {
|
||||
return (data[1] >> 7) & 0x1;
|
||||
}
|
||||
|
||||
unsigned int Packet::getPayloadType() const {
|
||||
return (data[1]) & 0x7F;
|
||||
}
|
||||
|
||||
unsigned int Packet::getSequence() const {
|
||||
return (((((unsigned int)data[2]) << 8) + data[3]));
|
||||
}
|
||||
|
||||
unsigned int Packet::getTimeStamp() const {
|
||||
return ntohl(*((unsigned int *)(data + 4)));
|
||||
}
|
||||
|
||||
unsigned int Packet::getSSRC() const {
|
||||
return ntohl(*((unsigned int *)(data + 8)));
|
||||
}
|
||||
|
||||
char * Packet::getData() {
|
||||
return data + 8 + 4 * getContribCount() + getExtension();
|
||||
}
|
||||
|
||||
void Packet::setTimestamp(unsigned int t) {
|
||||
*((unsigned int *)(data + 4)) = htonl(t);
|
||||
}
|
||||
|
||||
void Packet::setSequence(unsigned int seq) {
|
||||
*((short *)(data + 2)) = htons(seq);
|
||||
}
|
||||
|
||||
void Packet::setSSRC(unsigned long ssrc) {
|
||||
*((int *)(data + 8)) = htonl(ssrc);
|
||||
}
|
||||
|
||||
void Packet::increaseSequence() {
|
||||
*((short *)(data + 2)) = htons(getSequence() + 1);
|
||||
}
|
||||
|
||||
void Packet::sendH264(void * socket, void callBack(void *, char *, unsigned int, unsigned int), const char * payload, unsigned int payloadlen, unsigned int channel) {
|
||||
/// \todo This function probably belongs in DMS somewhere.
|
||||
if (payloadlen <= MAX_SEND) {
|
||||
data[1] |= 0x80;//setting the RTP marker bit to 1
|
||||
memcpy(data + getHsize(), payload, payloadlen);
|
||||
callBack(socket, data, getHsize() + payloadlen, channel);
|
||||
sentPackets++;
|
||||
sentBytes += payloadlen;
|
||||
increaseSequence();
|
||||
} else {
|
||||
data[1] &= 0x7F;//setting the RTP marker bit to 0
|
||||
unsigned int sent = 0;
|
||||
unsigned int sending = MAX_SEND;//packages are of size MAX_SEND, except for the final one
|
||||
char initByte = (payload[0] & 0xE0) | 0x1C;
|
||||
char serByte = payload[0] & 0x1F; //ser is now 000
|
||||
data[getHsize()] = initByte;
|
||||
while (sent < payloadlen) {
|
||||
if (sent == 0) {
|
||||
serByte |= 0x80;//set first bit to 1
|
||||
} else {
|
||||
serByte &= 0x7F;//set first bit to 0
|
||||
}
|
||||
if (sent + MAX_SEND >= payloadlen) {
|
||||
//last package
|
||||
serByte |= 0x40;
|
||||
sending = payloadlen - sent;
|
||||
data[1] |= 0x80;//setting the RTP marker bit to 1
|
||||
}
|
||||
data[getHsize() + 1] = serByte;
|
||||
memcpy(data + getHsize() + 2, payload + 1 + sent, sending); //+1 because
|
||||
callBack(socket, data, getHsize() + 2 + sending, channel);
|
||||
sentPackets++;
|
||||
sentBytes += sending;
|
||||
sent += sending;
|
||||
increaseSequence();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Packet::sendAAC(void * socket, void callBack(void *, char *, unsigned int, unsigned int), const char * payload, unsigned int payloadlen, unsigned int channel) {
|
||||
/// \todo This function probably belongs in DMS somewhere.
|
||||
data[1] |= 0x80;//setting the RTP marker bit to 1
|
||||
/// \todo This 0x100000 value - What is it? Why is it hardcoded?
|
||||
/// \todo The least significant 3 bits are used to signal some stuff from RFC 3640. Why do we send them always as 000?
|
||||
*((int *)(data + getHsize())) = htonl(((payloadlen << 3) & 0x0010fff8) | 0x00100000);
|
||||
memcpy(data + getHsize() + 4, payload, payloadlen);
|
||||
callBack(socket, data, getHsize() + 4 + payloadlen, channel);
|
||||
sentPackets++;
|
||||
sentBytes += payloadlen;
|
||||
increaseSequence();
|
||||
}
|
||||
|
||||
void Packet::sendRaw(void * socket, void callBack(void *, char *, unsigned int, unsigned int), const char * payload, unsigned int payloadlen, unsigned int channel) {
|
||||
/// \todo This function probably belongs in DMS somewhere.
|
||||
data[1] |= 0x80;//setting the RTP marker bit to 1
|
||||
memcpy(data + getHsize(), payload, payloadlen);
|
||||
callBack(socket, data, getHsize() + payloadlen, channel);
|
||||
sentPackets++;
|
||||
sentBytes += payloadlen;
|
||||
increaseSequence();
|
||||
}
|
||||
|
||||
/// Stores a long long (64 bits) value of val in network order to the pointer p.
|
||||
inline void Packet::htobll(char * p, long long val) {
|
||||
p[0] = (val >> 56) & 0xFF;
|
||||
p[1] = (val >> 48) & 0xFF;
|
||||
p[2] = (val >> 40) & 0xFF;
|
||||
p[3] = (val >> 32) & 0xFF;
|
||||
p[4] = (val >> 24) & 0xFF;
|
||||
p[5] = (val >> 16) & 0xFF;
|
||||
p[6] = (val >> 8) & 0xFF;
|
||||
p[7] = val & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Packet::sendRTCP(long long & connectedAt, void * socket, unsigned int tid , DTSC::Meta & metadata, void callBack(void *, char *, unsigned int, unsigned int)) {
|
||||
void * rtcpData = malloc(32);
|
||||
if (!rtcpData){
|
||||
FAIL_MSG("Could not allocate 32 bytes. Something is seriously messed up.");
|
||||
return;
|
||||
}
|
||||
((int *)rtcpData)[0] = htonl(0x80C80006);
|
||||
((int *)rtcpData)[1] = htonl(getSSRC());
|
||||
// unsigned int tid = packet["trackid"].asInt();
|
||||
//timestamp in ms
|
||||
double ntpTime = 2208988800UL + Util::epoch() + (Util::getMS() % 1000) / 1000.0;
|
||||
if (startRTCP < 1 && startRTCP > -1) {
|
||||
startRTCP = ntpTime;
|
||||
}
|
||||
ntpTime -= startRTCP;
|
||||
|
||||
((int *)rtcpData)[2] = htonl(2208988800UL + Util::epoch()); //epoch is in seconds
|
||||
((int *)rtcpData)[3] = htonl((Util::getMS() % 1000) * 4294967.295);
|
||||
if (metadata.tracks[tid].codec == "H264") {
|
||||
((int *)rtcpData)[4] = htonl((ntpTime - 0) * 90000); //rtpts
|
||||
} else if (metadata.tracks[tid].codec == "AAC") {
|
||||
((int *)rtcpData)[4] = htonl((ntpTime - 0) * metadata.tracks[tid].rate); //rtpts
|
||||
} else {
|
||||
DEBUG_MSG(DLVL_FAIL, "Unsupported codec");
|
||||
return;
|
||||
}
|
||||
//it should be the time packet was sent maybe, after all?
|
||||
//*((int *)(rtcpData+16) ) = htonl(getTimeStamp());//rtpts
|
||||
((int *)rtcpData)[5] = htonl(sentPackets);//packet
|
||||
((int *)rtcpData)[6] = htonl(sentBytes);//octet
|
||||
callBack(socket, (char*)rtcpData , 28 , 0);
|
||||
free(rtcpData);
|
||||
}
|
||||
|
||||
Packet::Packet() {
|
||||
managed = false;
|
||||
data = 0;
|
||||
}
|
||||
|
||||
Packet::Packet(unsigned int payloadType, unsigned int sequence, unsigned int timestamp, unsigned int ssrc, unsigned int csrcCount) {
|
||||
managed = true;
|
||||
data = new char[12 + 4 * csrcCount + 2 + MAX_SEND]; //headerSize, 2 for FU-A, MAX_SEND for maximum sent size
|
||||
data[0] = ((2) << 6) | ((0 & 1) << 5) | ((0 & 1) << 4) | (csrcCount & 15); //version, padding, extension, csrc count
|
||||
data[1] = payloadType & 0x7F; //marker and payload type
|
||||
setSequence(sequence - 1); //we automatically increase the sequence each time when p
|
||||
setTimestamp(timestamp);
|
||||
setSSRC(ssrc);
|
||||
sentBytes = 0;
|
||||
sentPackets = 0;
|
||||
}
|
||||
|
||||
Packet::Packet(const Packet & o) {
|
||||
managed = true;
|
||||
if (o.data) {
|
||||
data = new char[o.getHsize() + 2 + MAX_SEND]; //headerSize, 2 for FU-A, MAX_SEND for maximum sent size
|
||||
if (data) {
|
||||
memcpy(data, o.data, o.getHsize() + 2 + MAX_SEND);
|
||||
}
|
||||
} else {
|
||||
data = new char[14 + MAX_SEND];//headerSize, 2 for FU-A, MAX_SEND for maximum sent size
|
||||
if (data) {
|
||||
memset(data, 0, 14 + MAX_SEND);
|
||||
}
|
||||
}
|
||||
sentBytes = o.sentBytes;
|
||||
sentPackets = o.sentPackets;
|
||||
}
|
||||
|
||||
void Packet::operator=(const Packet & o) {
|
||||
managed = true;
|
||||
if (data) {
|
||||
delete[] data;
|
||||
}
|
||||
data = new char[o.getHsize() + 2 + MAX_SEND];
|
||||
if (data) {
|
||||
memcpy(data, o.data, o.getHsize() + 2 + MAX_SEND);
|
||||
}
|
||||
sentBytes = o.sentBytes;
|
||||
sentPackets = o.sentPackets;
|
||||
}
|
||||
|
||||
Packet::~Packet() {
|
||||
if (managed) {
|
||||
delete [] data;
|
||||
}
|
||||
}
|
||||
Packet::Packet(const char * dat, unsigned int len) {
|
||||
managed = false;
|
||||
datalen = len;
|
||||
data = (char *) dat;
|
||||
}
|
||||
|
||||
}
|
62
lib/rtp.h
Normal file
62
lib/rtp.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cstdio>
|
||||
#include <stdint.h>
|
||||
#include <sstream>
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
#include "socket.h"
|
||||
#include "json.h"
|
||||
#include "dtsc.h"
|
||||
#include "mp4.h"
|
||||
#include "mp4_generic.h"
|
||||
|
||||
/// This namespace holds all RTP-parsing and sending related functionality.
|
||||
namespace RTP {
|
||||
|
||||
/// This class is used to make RTP packets. Currently, H264, and AAC are supported. RTP mechanisms, like increasing sequence numbers and setting timestamps are all taken care of in here.
|
||||
class Packet {
|
||||
private:
|
||||
bool managed;
|
||||
char * data; ///<The actual RTP packet that is being sent
|
||||
unsigned int datalen; ///<Size of rtp packet
|
||||
int sentPackets;
|
||||
int sentBytes;//Because ugly is beautiful
|
||||
inline void htobll(char * p, long long val);
|
||||
public:
|
||||
static double startRTCP;
|
||||
unsigned int getHsize() const;
|
||||
unsigned int getVersion() const;
|
||||
unsigned int getPadding() const;
|
||||
unsigned int getExtension() const;
|
||||
unsigned int getContribCount() const;
|
||||
unsigned int getMarker() const;
|
||||
unsigned int getPayloadType() const;
|
||||
unsigned int getSequence() const;
|
||||
unsigned int getTimeStamp() const;
|
||||
void setSequence(unsigned int seq);
|
||||
unsigned int getSSRC() const;
|
||||
void setSSRC(unsigned long ssrc);
|
||||
|
||||
void setTimestamp(unsigned int t);
|
||||
void increaseSequence();
|
||||
void sendH264(void * socket, void callBack(void *, char *, unsigned int, unsigned int), const char * payload, unsigned int payloadlen, unsigned int channel);
|
||||
void sendAAC(void * socket, void callBack(void *, char *, unsigned int, unsigned int), const char * payload, unsigned int payloadlen, unsigned int channel);
|
||||
void sendRaw(void * socket, void callBack(void *, char *, unsigned int, unsigned int), const char * payload, unsigned int payloadlen, unsigned int channel);
|
||||
void sendRTCP(long long & connectedAt, void * socket, unsigned int tid, DTSC::Meta & metadata, void callBack(void *, char *, unsigned int, unsigned int));
|
||||
|
||||
|
||||
Packet();
|
||||
Packet(unsigned int pt, unsigned int seq, unsigned int ts, unsigned int ssr, unsigned int csrcCount = 0);
|
||||
Packet(const Packet & o);
|
||||
void operator=(const Packet & o);
|
||||
~Packet();
|
||||
Packet(const char * dat, unsigned int len);
|
||||
char * getData();
|
||||
};
|
||||
|
||||
}
|
|
@ -79,6 +79,12 @@ bool Util::startInput(std::string streamname, std::string filename, bool forkFir
|
|||
IPC::semaphore configLock("!mistConfLock", O_CREAT | O_RDWR, ACCESSPERMS, 1);
|
||||
configLock.wait();
|
||||
DTSC::Scan config = DTSC::Scan(mistConfOut.mapped, mistConfOut.len);
|
||||
|
||||
/*LTS-START*/
|
||||
if (config.getMember("hardlimit_active")) {
|
||||
return false;
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
sanitizeName(streamname);
|
||||
std::string smp = streamname.substr(0, streamname.find_first_of("+ "));
|
||||
|
@ -89,6 +95,13 @@ bool Util::startInput(std::string streamname, std::string filename, bool forkFir
|
|||
configLock.post();//unlock the config semaphore
|
||||
return false;
|
||||
}
|
||||
|
||||
/*LTS-START*/
|
||||
if (stream_cfg.getMember("hardlimit_active")) {
|
||||
return false;
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
|
||||
//If starting without filename parameter, check if the stream is already active.
|
||||
//If yes, don't activate again to prevent duplicate inputs.
|
||||
|
|
|
@ -157,14 +157,14 @@ namespace tthread {
|
|||
// Get thread startup information
|
||||
_thread_start_info * ti = (_thread_start_info *) aArg;
|
||||
|
||||
try {
|
||||
//try {
|
||||
// Call the actual client thread function
|
||||
ti->mFunction(ti->mArg);
|
||||
} catch (...) {
|
||||
//} catch (...) {
|
||||
// Uncaught exceptions will terminate the application (default behavior
|
||||
// according to C++11)
|
||||
std::terminate();
|
||||
}
|
||||
//std::terminate();
|
||||
//}
|
||||
|
||||
// The thread is no longer executing
|
||||
if (ti->mThread) {
|
||||
|
|
|
@ -1022,10 +1022,14 @@ namespace TS {
|
|||
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
|
||||
if (myMeta.tracks[*it].codec == "H264"){
|
||||
PMT.setStreamType(0x1B,id);
|
||||
}else if (myMeta.tracks[*it].codec == "HEVC"){
|
||||
PMT.setStreamType(0x06,id);
|
||||
}else if (myMeta.tracks[*it].codec == "AAC"){
|
||||
PMT.setStreamType(0x0F,id);
|
||||
}else if (myMeta.tracks[*it].codec == "MP3"){
|
||||
PMT.setStreamType(0x03,id);
|
||||
}else if (myMeta.tracks[*it].codec == "AC3"){
|
||||
PMT.setStreamType(0x81,id);
|
||||
}
|
||||
PMT.setElementaryPID(0x100 + (*it) - 1, id);
|
||||
PMT.setESInfoLength(0,id);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue