mistserver/lib/dtscmeta.cpp

2199 lines
73 KiB
C++

#include "dtsc.h"
#include "defines.h"
#include "bitfields.h"
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <fstream>
namespace DTSC {
/// Default constructor for packets - sets a null pointer and invalid packet.
Packet::Packet() {
data = NULL;
bufferLen = 0;
dataLen = 0;
master = false;
version = DTSC_INVALID;
}
/// Copy constructor for packets, copies an existing packet with same noCopy flag as original.
Packet::Packet(const Packet & rhs) {
master = false;
bufferLen = 0;
data = NULL;
if (rhs.data && rhs.dataLen){
reInit(rhs.data, rhs.dataLen, !rhs.master);
}else{
null();
}
}
/// Data constructor for packets, either references or copies a packet from raw data.
Packet::Packet(const char * data_, unsigned int len, bool noCopy) {
master = false;
bufferLen = 0;
data = NULL;
reInit(data_, len, noCopy);
}
/// This destructor clears frees the data pointer if the packet was not a reference.
Packet::~Packet() {
if (master && data) {
free(data);
}
}
/// Copier for packets, copies an existing packet with same noCopy flag as original.
/// If going from copy to noCopy, this will free the data pointer first.
void Packet::operator = (const Packet & rhs) {
if (master && !rhs.master) {
null();
}
if (rhs && rhs.data && rhs.dataLen) {
reInit(rhs.data, rhs.dataLen, !rhs.master);
} else {
null();
}
}
/// Returns true if the packet is deemed valid, false otherwise.
/// Valid packets have a length of at least 8, known header type, and length equal to the length set in the header.
Packet::operator bool() const {
if (!data) {
DONTEVEN_MSG("No data");
return false;
}
if (dataLen < 8) {
VERYHIGH_MSG("Datalen < 8");
return false;
}
if (version == DTSC_INVALID) {
VERYHIGH_MSG("No valid version");
return false;
}
if (ntohl(((int *)data)[1]) + 8 > dataLen) {
VERYHIGH_MSG("Length mismatch");
return false;
}
return true;
}
/// Returns the recognized packet type.
/// This type is set by reInit and all constructors, and then just referenced from there on.
packType Packet::getVersion() const {
return version;
}
/// Resets this packet back to the same state as if it had just been freshly constructed.
/// If needed, this frees the data pointer.
void Packet::null() {
if (master && data) {
free(data);
}
master = false;
data = NULL;
bufferLen = 0;
dataLen = 0;
version = DTSC_INVALID;
}
/// Internally used resize function for when operating in copy mode and the internal buffer is too small.
/// It will only resize up, never down.
///\param len The length th scale the buffer up to if necessary
void Packet::resize(size_t len) {
if (master && len > bufferLen) {
char * tmp = (char *)realloc(data, len);
if (tmp) {
data = tmp;
bufferLen = len;
} else {
FAIL_MSG("Out of memory on parsing a packet");
}
}
}
void Packet::reInit(Socket::Connection & src) {
int sleepCount = 0;
null();
int toReceive = 0;
while (src.connected()){
if (!toReceive && src.Received().available(8)){
if (src.Received().copy(2) != "DT"){
WARN_MSG("Invalid DTSC Packet header encountered (%s)", src.Received().copy(4).c_str());
break;
}
toReceive = Bit::btohl(src.Received().copy(8).data() + 4);
}
if (toReceive && src.Received().available(toReceive + 8)){
std::string dataBuf = src.Received().remove(toReceive + 8);
reInit(dataBuf.data(), dataBuf.size());
return;
}
if(!src.spool()){
if (sleepCount++ > 150){
WARN_MSG("Waiting for packet on connection timed out");
return;
}
Util::wait(100);
}
}
}
///\brief Initializes a packet with new data
///\param data_ The new data for the packet
///\param len The length of the data pointed to by data_
///\param noCopy Determines whether to make a copy or not
void Packet::reInit(const char * data_, unsigned int len, bool noCopy) {
if (!data_) {
WARN_MSG("ReInit received a null pointer with len %d, nulling", len);
null();
return;
}
if (!data_[0] && !data_[1] && !data_[2] && !data_[3]){
null();
return;
}
if (data_[0] != 'D' || data_[1] != 'T') {
unsigned int twlen = len;
if (twlen > 20){twlen = 20;}
HIGH_MSG("ReInit received a pointer that didn't start with 'DT' but with %s (%u) - data corruption?", JSON::Value(std::string(data_, twlen)).toString().c_str(), len);
null();
return;
}
if (len <= 0) {
len = ntohl(((int *)data_)[1]) + 8;
}
//clear any existing controlled contents
if (master && noCopy) {
null();
}
//set control flag to !noCopy
master = !noCopy;
//either copy the data, or only the pointer, depending on flag
if (noCopy) {
data = (char *)data_;
} else {
resize(len);
memcpy(data, data_, len);
}
//check header type and store packet length
dataLen = len;
version = DTSC_INVALID;
if (len < 4) {
FAIL_MSG("ReInit received a packet with size < 4");
return;
}
if (!memcmp(data, Magic_Packet2, 4)) {
version = DTSC_V2;
}
if (!memcmp(data, Magic_Packet, 4)) {
version = DTSC_V1;
}
if (!memcmp(data, Magic_Header, 4)) {
version = DTSC_HEAD;
}
if (!memcmp(data, Magic_Command, 4)) {
version = DTCM;
}
if (version == DTSC_INVALID){
FAIL_MSG("ReInit received a packet with invalid header");
}
}
/// Re-initializes this Packet to contain a generic DTSC packet with the given data fields.
/// When given a NULL pointer, the data is reserved and memset to 0
/// If given a NULL pointer and a zero size, an empty packet is created.
void Packet::genericFill(long long packTime, long long packOffset, long long packTrack, const char * packData, long long packDataSize, uint64_t packBytePos, bool isKeyframe, int64_t bootMsOffset){
null();
master = true;
//time and trackID are part of the 20-byte header.
//the container object adds 4 bytes (plus 2+namelen for each content, see below)
//offset, if non-zero, adds 9 bytes (integer type) and 8 bytes (2+namelen)
//bpos, if >= 0, adds 9 bytes (integer type) and 6 bytes (2+namelen)
//keyframe, if true, adds 9 bytes (integer type) and 10 bytes (2+namelen)
//bootmsoffset, if != 0, adds 9 bytes (integer type) and 5 bytes (2+namelen)
//data adds packDataSize+5 bytes (string type) and 6 bytes (2+namelen)
if (packData && packDataSize < 1){
FAIL_MSG("Attempted to fill a packet with %lli bytes!", packDataSize);
return;
}
unsigned int sendLen = 24 + (packOffset?17:0) + (packBytePos?15:0) + (isKeyframe?19:0) + (bootMsOffset?14:0) + packDataSize+11;
resize(sendLen);
//set internal variables
version = DTSC_V2;
dataLen = sendLen;
//write the first 20 bytes
memcpy(data, "DTP2", 4);
unsigned int tmpLong = htonl(sendLen - 8);
memcpy(data+4, (char *)&tmpLong, 4);
tmpLong = htonl(packTrack);
memcpy(data+8, (char *)&tmpLong, 4);
tmpLong = htonl((int)(packTime >> 32));
memcpy(data+12, (char *)&tmpLong, 4);
tmpLong = htonl((int)(packTime & 0xFFFFFFFF));
memcpy(data+16, (char *)&tmpLong, 4);
data[20] = 0xE0;//start container object
unsigned int offset = 21;
if (packOffset){
memcpy(data+offset, "\000\006offset\001", 9);
tmpLong = htonl((int)(packOffset >> 32));
memcpy(data+offset+9, (char *)&tmpLong, 4);
tmpLong = htonl((int)(packOffset & 0xFFFFFFFF));
memcpy(data+offset+13, (char *)&tmpLong, 4);
offset += 17;
}
if (packBytePos){
memcpy(data+offset, "\000\004bpos\001", 7);
tmpLong = htonl((int)(packBytePos >> 32));
memcpy(data+offset+7, (char *)&tmpLong, 4);
tmpLong = htonl((int)(packBytePos & 0xFFFFFFFF));
memcpy(data+offset+11, (char *)&tmpLong, 4);
offset += 15;
}
if (isKeyframe){
memcpy(data+offset, "\000\010keyframe\001\000\000\000\000\000\000\000\001", 19);
offset += 19;
}
if (bootMsOffset){
memcpy(data+offset, "\000\003bmo\001", 6);
tmpLong = htonl((int)(bootMsOffset >> 32));
memcpy(data+offset+6, (char *)&tmpLong, 4);
tmpLong = htonl((int)(bootMsOffset & 0xFFFFFFFF));
memcpy(data+offset+10, (char *)&tmpLong, 4);
offset += 14;
}
memcpy(data+offset, "\000\004data\002", 7);
tmpLong = htonl(packDataSize);
memcpy(data+offset+7, (char *)&tmpLong, 4);
if (packData){
memcpy(data+offset+11, packData, packDataSize);
}else{
memset(data+offset+11, 0, packDataSize);
}
//finish container with 0x0000EE
memcpy(data+offset+11+packDataSize, "\000\000\356", 3);
}
///sets the keyframe byte.
void Packet::setKeyFrame(bool kf){
uint32_t offset = 23;
while (data[offset] != 'd' && data[offset] != 'k' && data[offset] != 'K'){
switch (data[offset]){
case 'o': offset += 17; break;
case 'b': offset += 15; break;
default:
FAIL_MSG("Unknown field: %c", data[offset]);
}
}
if(data[offset] == 'k' || data[offset] == 'K'){
data[offset] = (kf?'k':'K');
data[offset+16] = (kf?1:0);
}else{
ERROR_MSG("Could not set keyframe - field not found!");
}
}
void Packet::appendData(const char * appendData, uint32_t appendLen){
resize(dataLen + appendLen);
memcpy(data + dataLen-3, appendData, appendLen);
memcpy(data + dataLen-3 + appendLen, "\000\000\356", 3); //end container
dataLen += appendLen;
Bit::htobl(data+4, Bit::btohl(data +4)+appendLen);
uint32_t offset = getDataStringLenOffset();
Bit::htobl(data+offset, Bit::btohl(data+offset)+appendLen);
}
void Packet::appendNal(const char * appendData, uint32_t appendLen){
if(appendLen ==0){
return;
}
resize(dataLen + appendLen +4);
Bit::htobl(data+dataLen -3, appendLen);
memcpy(data + dataLen-3+4, appendData, appendLen);
memcpy(data + dataLen-3+4 + appendLen, "\000\000\356", 3); //end container
dataLen += appendLen +4;
Bit::htobl(data+4, Bit::btohl(data +4)+appendLen+4);
uint32_t offset = getDataStringLenOffset();
Bit::htobl(data+offset, Bit::btohl(data+offset)+appendLen+4);
prevNalSize = appendLen;
}
void Packet::upgradeNal(const char * appendData, uint32_t appendLen){
if(appendLen ==0){
return;
}
uint64_t sizeOffset = dataLen - 3 - 4 - prevNalSize;
if (Bit::btohl(data + sizeOffset) != prevNalSize){
FAIL_MSG("PrevNalSize state not correct");
return;
}
resize(dataLen + appendLen);//Not + 4 as size bytes have already been written here.
Bit::htobl(data+sizeOffset, prevNalSize + appendLen);
prevNalSize += appendLen;
memcpy(data + dataLen - 3, appendData, appendLen);
memcpy(data + dataLen - 3 + appendLen, "\000\000\356", 3); //end container
dataLen += appendLen;
Bit::htobl(data+4, Bit::btohl(data +4)+appendLen);
uint32_t offset = getDataStringLenOffset();
Bit::htobl(data+offset, Bit::btohl(data+offset)+appendLen);
}
size_t Packet::getDataStringLen(){
return Bit::btohl(data+getDataStringLenOffset());
}
///Method can only be used when using internal functions to build the data.
size_t Packet::getDataStringLenOffset(){
size_t offset = 23;
while (data[offset] != 'd'){
switch (data[offset]){
case 'o': offset += 17; break;
case 'b': offset += 15; break;
case 'k': offset += 19; break;
case 'K': offset += 19; break;
default:
FAIL_MSG("Unknown field: %c", data[offset]);
return -1;
}
}
return offset +5;
}
/// Helper function for skipping over whole DTSC parts
static char * skipDTSC(char * p, char * max) {
if (p + 1 >= max || p[0] == 0x00) {
return 0;//out of packet! 1 == error
}
if (p[0] == DTSC_INT) {
//int, skip 9 bytes to next value
return p + 9;
}
if (p[0] == DTSC_STR) {
if (p + 4 >= max) {
return 0;//out of packet!
}
return p + 5 + Bit::btohl(p+1);
}
if (p[0] == DTSC_OBJ || p[0] == DTSC_CON) {
p++;
//object, scan contents
while (p < max && p[0] + p[1] != 0) { //while not encountering 0x0000 (we assume 0x0000EE)
if (p + 2 >= max) {
return 0;//out of packet!
}
p += 2 + Bit::btohs(p);//skip size
//otherwise, search through the contents, if needed, and continue
p = skipDTSC(p, max);
if (!p) {
return 0;
}
}
return p + 3;
}
if (p[0] == DTSC_ARR) {
p++;
//array, scan contents
while (p < max && p[0] + p[1] != 0) { //while not encountering 0x0000 (we assume 0x0000EE)
//search through contents...
p = skipDTSC(p, max);
if (!p) {
return 0;
}
}
return p + 3; //skip end marker
}
return 0;//out of packet! 1 == error
}
///\brief Retrieves a single parameter as a string
///\param identifier The name of the parameter
///\param result A location on which the string will be returned
///\param len An integer in which the length of the string will be returned
void Packet::getString(const char * identifier, char *& result, size_t & len) const {
getScan().getMember(identifier).getString(result, len);
}
///\brief Retrieves a single parameter as a string
///\param identifier The name of the parameter
///\param result The string in which to store the result
void Packet::getString(const char * identifier, std::string & result) const {
result = getScan().getMember(identifier).asString();
}
///\brief Retrieves a single parameter as an integer
///\param identifier The name of the parameter
///\param result The result is stored in this integer
void Packet::getInt(const char * identifier, uint64_t & result) const {
result = getScan().getMember(identifier).asInt();
}
///\brief Retrieves a single parameter as an integer
///\param identifier The name of the parameter
///\result The requested parameter as an integer
uint64_t Packet::getInt(const char * identifier) const {
uint64_t result;
getInt(identifier, result);
return result;
}
///\brief Retrieves a single parameter as a boolean
///\param identifier The name of the parameter
///\param result The result is stored in this boolean
void Packet::getFlag(const char * identifier, bool & result) const {
uint64_t result_;
getInt(identifier, result_);
result = result_;
}
///\brief Retrieves a single parameter as a boolean
///\param identifier The name of the parameter
///\result The requested parameter as a boolean
bool Packet::getFlag(const char * identifier) const {
bool result;
getFlag(identifier, result);
return result;
}
///\brief Checks whether a parameter exists
///\param identifier The name of the parameter
///\result Whether the parameter exists or not
bool Packet::hasMember(const char * identifier) const {
return getScan().getMember(identifier).getType() > 0;
}
///\brief Returns the timestamp of the packet.
///\return The timestamp of this packet.
uint64_t Packet::getTime() const {
if (version != DTSC_V2) {
if (!data) {
return 0;
}
return getInt("time");
}
return Bit::btohll(data + 12);
}
void Packet::setTime(uint64_t _time) {
if (!master){
INFO_MSG("Can't set the time for this packet, as it is not master.");
return;
}
Bit::htobll(data + 12, _time);
}
///\brief Returns the track id of the packet.
///\return The track id of this packet.
size_t Packet::getTrackId() const {
if (version != DTSC_V2) {
return getInt("trackid");
}
return Bit::btohl(data+8);
}
///\brief Returns a pointer to the payload of this packet.
///\return A pointer to the payload of this packet.
char * Packet::getData() const {
return data;
}
///\brief Returns the size of this packet.
///\return The size of this packet.
size_t Packet::getDataLen() const {
return dataLen;
}
///\brief Returns the size of the payload of this packet.
///\return The size of the payload of this packet.
size_t Packet::getPayloadLen() const {
if (version == DTSC_V2) {
return dataLen - 20;
} else {
return dataLen - 8;
}
}
/// Returns a DTSC::Scan instance to the contents of this packet.
/// May return an invalid instance if this packet is invalid.
Scan Packet::getScan() const {
if (!*this || !getDataLen() || !getPayloadLen() || getDataLen() <= getPayloadLen()){
return Scan();
}
return Scan(data + (getDataLen() - getPayloadLen()), getPayloadLen());
}
///\brief Converts the packet into a JSON value
///\return A JSON::Value representation of this packet.
JSON::Value Packet::toJSON() const {
JSON::Value result;
uint32_t i = 8;
if (getVersion() == DTSC_V1) {
JSON::fromDTMI(data, dataLen, i, result);
}
if (getVersion() == DTSC_V2) {
JSON::fromDTMI2(data, dataLen, i, result);
}
return result;
}
std::string Packet::toSummary() const {
std::stringstream out;
char * res = 0;
size_t len = 0;
getString("data", res, len);
out << getTrackId() << "@" << getTime() << ": " << len << " bytes";
if (hasMember("keyframe")){
out << " (keyframe)";
}
return out.str();
}
/// Create an invalid DTSC::Scan object by default.
Scan::Scan() {
p = 0;
len = 0;
}
/// Create a DTSC::Scan object from memory pointer.
Scan::Scan(char * pointer, size_t length) {
p = pointer;
len = length;
}
/// Returns whether the DTSC::Scan object contains valid data.
Scan::operator bool() const {
return (p && len);
}
/// Returns an object representing the named indice of this object.
/// Returns an invalid object if this indice doesn't exist or this isn't an object type.
Scan Scan::getMember(const std::string & indice) const {
return getMember(indice.data(), indice.size());
}
/// Returns an object representing the named indice of this object.
/// Returns an invalid object if this indice doesn't exist or this isn't an object type.
Scan Scan::getMember(const char * indice, const size_t ind_len) const {
if (getType() != DTSC_OBJ && getType() != DTSC_CON) {
return Scan();
}
char * i = p + 1;
//object, scan contents
while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
if (i + 2 >= p + len) {
return Scan();//out of packet!
}
uint16_t strlen = Bit::btohs(i);
i += 2;
if (ind_len == strlen && strncmp(indice, i, strlen) == 0) {
return Scan(i + strlen, len - (i - p));
}
i = skipDTSC(i + strlen, p + len);
if (!i) {
return Scan();
}
}
return Scan();
}
/// Returns an object representing the named indice of this object.
/// Returns an invalid object if this indice doesn't exist or this isn't an object type.
bool Scan::hasMember(const std::string & indice) const{
return getMember(indice.data(), indice.size());
}
/// Returns whether an object representing the named indice of this object exists.
/// Returns false if this indice doesn't exist or this isn't an object type.
bool Scan::hasMember(const char * indice, const size_t ind_len) const {
return getMember(indice, ind_len);
}
/// Returns an object representing the named indice of this object.
/// Returns an invalid object if this indice doesn't exist or this isn't an object type.
Scan Scan::getMember(const char * indice) const {
return getMember(indice, strlen(indice));
}
/// Returns the amount of indices if an array, the amount of members if an object, or zero otherwise.
size_t Scan::getSize() const {
uint32_t arr_indice = 0;
if (getType() == DTSC_ARR) {
char * i = p + 1;
//array, scan contents
while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
//search through contents...
arr_indice++;
i = skipDTSC(i, p + len);
if (!i) {
break;
}
}
}
if (getType() == DTSC_OBJ || getType() == DTSC_CON) {
char * i = p + 1;
//object, scan contents
while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
if (i + 2 >= p + len) {
return Scan();//out of packet!
}
uint16_t strlen = Bit::btohs(i);
i += 2;
arr_indice++;
i = skipDTSC(i + strlen, p + len);
if (!i) {
break;
}
}
}
return arr_indice;
}
/// Returns an object representing the num-th indice of this array.
/// If not an array but an object, it returns the num-th member, instead.
/// Returns an invalid object if this indice doesn't exist or this isn't an array or object type.
Scan Scan::getIndice(size_t num) const {
if (getType() == DTSC_ARR) {
char * i = p + 1;
unsigned int arr_indice = 0;
//array, scan contents
while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
//search through contents...
if (arr_indice == num) {
return Scan(i, len - (i - p));
} else {
arr_indice++;
i = skipDTSC(i, p + len);
if (!i) {
return Scan();
}
}
}
}
if (getType() == DTSC_OBJ || getType() == DTSC_CON) {
char * i = p + 1;
unsigned int arr_indice = 0;
//object, scan contents
while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
if (i + 2 >= p + len) {
return Scan();//out of packet!
}
unsigned int strlen = Bit::btohs(i);
i += 2;
if (arr_indice == num) {
return Scan(i + strlen, len - (i - p));
} else {
arr_indice++;
i = skipDTSC(i + strlen, p + len);
if (!i) {
return Scan();
}
}
}
}
return Scan();
}
/// Returns the name of the num-th member of this object.
/// Returns an empty string on error or when not an object.
std::string Scan::getIndiceName(size_t num) const {
if (getType() == DTSC_OBJ || getType() == DTSC_CON) {
char * i = p + 1;
unsigned int arr_indice = 0;
//object, scan contents
while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
if (i + 2 >= p + len) {
return "";//out of packet!
}
unsigned int strlen = Bit::btohs(i);
i += 2;
if (arr_indice == num) {
return std::string(i, strlen);
} else {
arr_indice++;
i = skipDTSC(i + strlen, p + len);
if (!i) {
return "";
}
}
}
}
return "";
}
/// Returns the first byte of this DTSC value, or 0 on error.
char Scan::getType() const {
if (!p) {
return 0;
}
return p[0];
}
/// Returns the boolean value of this DTSC value.
/// Numbers are compared to 0.
/// Strings are checked for non-zero length.
/// Objects and arrays are checked for content.
/// Returns false on error or in other cases.
bool Scan::asBool() const {
switch (getType()) {
case DTSC_STR:
return (p[1] | p[2] | p[3] | p[4]);
case DTSC_INT:
return (asInt() != 0);
case DTSC_OBJ:
case DTSC_CON:
case DTSC_ARR:
return (p[1] | p[2]);
default:
return false;
}
}
/// Returns the long long value of this DTSC number value.
/// Will convert string values to numbers, taking octal and hexadecimal types into account.
/// Illegal or invalid values return 0.
int64_t Scan::asInt() const {
switch (getType()) {
case DTSC_INT:
return Bit::btohll(p+1);
case DTSC_STR:
char * str;
size_t strlen;
getString(str, strlen);
if (!strlen) {
return 0;
}
return strtoll(str, 0, 0);
default:
return 0;
}
}
/// Returns the string value of this DTSC string value.
/// Uses getString internally, if a string.
/// Converts integer values to strings.
/// Returns an empty string on error.
std::string Scan::asString() const {
switch (getType()) {
case DTSC_INT:{
std::stringstream st;
st << asInt();
return st.str();
}
break;
case DTSC_STR:{
char * str;
size_t strlen;
getString(str, strlen);
return std::string(str, strlen);
}
break;
}
return "";
}
/// Sets result to a pointer to the string, and strlen to the length of it.
/// Sets both to zero if this isn't a DTSC string value.
/// Attempts absolutely no conversion.
void Scan::getString(char *& result, size_t & strlen) const {
if (getType() == DTSC_STR){
result = p + 5;
strlen = Bit::btohl(p+1);
return;
}
result = 0;
strlen = 0;
}
/// Returns the DTSC scan object as a JSON value
/// Returns an empty object on error.
JSON::Value Scan::asJSON() const {
JSON::Value result;
unsigned int i = 0;
JSON::fromDTMI(p, len, i, result);
return result;
}
/// \todo Move this function to some generic area. Duplicate from json.cpp
static inline char hex2c(char c) {
if (c < 10) {
return '0' + c;
}
if (c < 16) {
return 'A' + (c - 10);
}
return '0';
}
/// \todo Move this function to some generic area. Duplicate from json.cpp
static std::string string_escape(const std::string val) {
std::stringstream out;
out << "\"";
for (size_t i = 0; i < val.size(); ++i) {
switch (val.data()[i]) {
case '"':
out << "\\\"";
break;
case '\\':
out << "\\\\";
break;
case '\n':
out << "\\n";
break;
case '\b':
out << "\\b";
break;
case '\f':
out << "\\f";
break;
case '\r':
out << "\\r";
break;
case '\t':
out << "\\t";
break;
default:
if (val.data()[i] < 32 || val.data()[i] > 126) {
out << "\\u00";
out << hex2c((val.data()[i] >> 4) & 0xf);
out << hex2c(val.data()[i] & 0xf);
} else {
out << val.data()[i];
}
break;
}
}
out << "\"";
return out.str();
}
std::string Scan::toPrettyString(size_t indent) const {
switch (getType()) {
case DTSC_STR: {
uint32_t strlen = Bit::btohl(p+1);
if (strlen > 250) {
std::stringstream ret;
ret << "\"" << strlen << " bytes of data\"";
return ret.str();
}
return string_escape(asString());
}
case DTSC_INT: {
std::stringstream ret;
ret << asInt();
return ret.str();
}
case DTSC_OBJ:
case DTSC_CON: {
std::stringstream ret;
ret << "{" << std::endl;
indent += 2;
char * i = p + 1;
bool first = true;
//object, scan contents
while (i[0] + i[1] != 0 && i < p + len) { //while not encountering 0x0000 (we assume 0x0000EE)
if (i + 2 >= p + len) {
indent -= 2;
ret << std::string(indent, ' ') << "} //walked out of object here";
return ret.str();
}
if (!first){
ret << "," << std::endl;
}
first = false;
uint16_t strlen = Bit::btohs(i);
i += 2;
ret << std::string(indent, ' ') << "\"" << std::string(i, strlen) << "\": " << Scan(i + strlen, len - (i - p)).toPrettyString(indent);
i = skipDTSC(i + strlen, p + len);
if (!i) {
indent -= 2;
ret << std::string(indent, ' ') << "} //could not locate next object";
return ret.str();
}
}
indent -= 2;
ret << std::endl << std::string(indent, ' ') << "}";
return ret.str();
}
case DTSC_ARR: {
std::stringstream ret;
ret << "[" << std::endl;
indent += 2;
Scan tmpScan;
unsigned int i = 0;
bool first = true;
do {
tmpScan = getIndice(i++);
if (tmpScan.getType()) {
if (!first){
ret << "," << std::endl;
}
first = false;
ret << std::string(indent, ' ') << tmpScan.toPrettyString(indent);
}
} while (tmpScan.getType());
indent -= 2;
ret << std::endl << std::string(indent, ' ') << "]";
return ret.str();
}
default:
return "Error";
}
}
///\brief Returns the payloadsize of a part
uint32_t Part::getSize() {
return Bit::btoh24(data);
}
///\brief Sets the payloadsize of a part
void Part::setSize(uint32_t newSize) {
Bit::htob24(data, newSize);
}
///\brief Returns the duration of a part
uint32_t Part::getDuration() {
return Bit::btoh24(data + 3);
}
///\brief Sets the duration of a part
void Part::setDuration(uint32_t newDuration) {
Bit::htob24(data + 3, newDuration);
}
///\brief returns the offset of a part
///Assumes the offset is actually negative if bit 0x800000 is set.
uint32_t Part::getOffset() {
uint32_t ret = Bit::btoh24(data + 6);
if (ret & 0x800000){
return ret | 0xff000000ul;
}else{
return ret;
}
}
///\brief Sets the offset of a part
void Part::setOffset(uint32_t newOffset) {
Bit::htob24(data + 6, newOffset);
}
///\brief Returns the data of a part
char * Part::getData() {
return data;
}
///\brief Converts a part to a human readable string
///\param str The stringstream to append to
///\param indent the amount of indentation needed
void Part::toPrettyString(std::ostream & str, int indent) {
str << std::string(indent, ' ') << "Part: Size(" << getSize() << "), Dur(" << getDuration() << "), Offset(" << getOffset() << ")" << std::endl;
}
///\brief Returns the byteposition of a keyframe
unsigned long long Key::getBpos() {
return Bit::btohll(data);
}
void Key::setBpos(unsigned long long newBpos) {
Bit::htobll(data, newBpos);
}
unsigned long Key::getLength() {
return Bit::btoh24(data+8);
}
void Key::setLength(unsigned long newLength) {
Bit::htob24(data+8, newLength);
}
///\brief Returns the number of a keyframe
unsigned long Key::getNumber() {
return Bit::btohl(data + 11);
}
///\brief Sets the number of a keyframe
void Key::setNumber(unsigned long newNumber) {
Bit::htobl(data + 11, newNumber);
}
///\brief Returns the number of parts of a keyframe
unsigned short Key::getParts() {
return Bit::btohs(data + 15);
}
///\brief Sets the number of parts of a keyframe
void Key::setParts(unsigned short newParts) {
Bit::htobs(data + 15, newParts);
}
///\brief Returns the timestamp of a keyframe
unsigned long long Key::getTime() {
return Bit::btohll(data + 17);
}
///\brief Sets the timestamp of a keyframe
void Key::setTime(unsigned long long newTime) {
Bit::htobll(data + 17, newTime);
}
///\brief Returns the data of this keyframe struct
char * Key::getData() {
return data;
}
///\brief Converts a keyframe to a human readable string
///\param str The stringstream to append to
///\param indent the amount of indentation needed
void Key::toPrettyString(std::ostream & str, int indent) {
str << std::string(indent, ' ') << "Key " << getNumber() << ": Pos(" << getBpos() << "), Dur(" << getLength() << "), Parts(" << getParts() << "), Time(" << getTime() << ")" << std::endl;
}
///\brief Returns the duration of this fragment
unsigned long Fragment::getDuration() {
return Bit::btohl(data);
}
///\brief Sets the duration of this fragment
void Fragment::setDuration(unsigned long newDuration) {
Bit::htobl(data, newDuration);
}
///\brief Returns the length of this fragment
char Fragment::getLength() {
return data[4];
}
///\brief Sets the length of this fragment
void Fragment::setLength(char newLength) {
data[4] = newLength;
}
///\brief Returns the number of the first keyframe in this fragment
unsigned long Fragment::getNumber() {
return Bit::btohl(data + 5);
}
///\brief Sets the number of the first keyframe in this fragment
void Fragment::setNumber(unsigned long newNumber) {
Bit::htobl(data + 5, newNumber);
}
///\brief Returns the size of a fragment
unsigned long Fragment::getSize() {
return Bit::btohl(data + 9);
}
///\brief Sets the size of a fragement
void Fragment::setSize(unsigned long newSize) {
Bit::htobl(data + 9, newSize);
}
///\brief Returns thte data of this fragment structure
char * Fragment::getData() {
return data;
}
///\brief Converts a fragment to a human readable string
///\param str The stringstream to append to
///\param indent the amount of indentation needed
void Fragment::toPrettyString(std::ostream & str, int indent) {
str << std::string(indent, ' ') << "Fragment " << getNumber() << ": Dur(" << getDuration() << "), Len(" << (int)getLength() << "), Size(" << getSize() << ")" << std::endl;
}
///\brief Constructs an empty track
Track::Track() {
trackID = 0;
firstms = 0;
lastms = 0;
bps = 0;
max_bps = 0;
missedFrags = 0;
rate = 0;
size = 0;
channels = 0;
width = 0;
height = 0;
fpks = 0;
minKeepAway = 0;
}
///\brief Constructs a track from a JSON::Value
Track::Track(JSON::Value & trackRef) {
if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) {
Fragment * tmp = (Fragment *)trackRef["fragments"].asStringRef().data();
fragments = std::deque<Fragment>(tmp, tmp + (trackRef["fragments"].asStringRef().size() / PACKED_FRAGMENT_SIZE));
}
if (trackRef.isMember("keys") && trackRef["keys"].isString()) {
Key * tmp = (Key *)trackRef["keys"].asStringRef().data();
keys = std::deque<Key>(tmp, tmp + (trackRef["keys"].asStringRef().size() / PACKED_KEY_SIZE));
}
if (trackRef.isMember("parts") && trackRef["parts"].isString()) {
Part * tmp = (Part *)trackRef["parts"].asStringRef().data();
parts = std::deque<Part>(tmp, tmp + (trackRef["parts"].asStringRef().size() / 9));
}
trackID = trackRef["trackid"].asInt();
firstms = trackRef["firstms"].asInt();
lastms = trackRef["lastms"].asInt();
bps = trackRef["bps"].asInt();
max_bps = trackRef["maxbps"].asInt();
missedFrags = trackRef["missed_frags"].asInt();
codec = trackRef["codec"].asStringRef();
type = trackRef["type"].asStringRef();
init = trackRef["init"].asStringRef();
if (trackRef.isMember("lang") && trackRef["lang"].asStringRef().size()){
lang = trackRef["lang"].asStringRef();
}
if (type == "audio") {
rate = trackRef["rate"].asInt();
size = trackRef["size"].asInt();
channels = trackRef["channels"].asInt();
}
if (type == "video") {
width = trackRef["width"].asInt();
height = trackRef["height"].asInt();
fpks = trackRef["fpks"].asInt();
}
if (trackRef.isMember("keysizes") && trackRef["keysizes"].isString()) {
std::string tmp = trackRef["keysizes"].asStringRef();
for (unsigned int i = 0; i < tmp.size(); i += 4){
keySizes.push_back((((long unsigned)tmp[i]) << 24) | (((long unsigned)tmp[i+1]) << 16) | (((long unsigned int)tmp[i+2]) << 8) | tmp[i+3]);
}
}
if (trackRef.isMember("keepaway") && trackRef["keepaway"].isInt()){
minKeepAway = trackRef["keepaway"].asInt();
}else{
minKeepAway = 0;
}
}
///\brief Constructs a track from a JSON::Value
Track::Track(Scan & trackRef) {
if (trackRef.getMember("fragments").getType() == DTSC_STR) {
char * tmp = 0;
size_t tmplen = 0;
trackRef.getMember("fragments").getString(tmp, tmplen);
fragments = std::deque<Fragment>((Fragment *)tmp, ((Fragment *)tmp) + (tmplen / PACKED_FRAGMENT_SIZE));
}
if (trackRef.getMember("keys").getType() == DTSC_STR) {
char * tmp = 0;
size_t tmplen = 0;
trackRef.getMember("keys").getString(tmp, tmplen);
keys = std::deque<Key>((Key *)tmp, ((Key *)tmp) + (tmplen / PACKED_KEY_SIZE));
}
if (trackRef.getMember("parts").getType() == DTSC_STR) {
char * tmp = 0;
size_t tmplen = 0;
trackRef.getMember("parts").getString(tmp, tmplen);
parts = std::deque<Part>((Part *)tmp, ((Part *)tmp) + (tmplen / 9));
}
trackID = trackRef.getMember("trackid").asInt();
firstms = trackRef.getMember("firstms").asInt();
lastms = trackRef.getMember("lastms").asInt();
bps = trackRef.getMember("bps").asInt();
max_bps = trackRef.getMember("maxbps").asInt();
missedFrags = trackRef.getMember("missed_frags").asInt();
codec = trackRef.getMember("codec").asString();
type = trackRef.getMember("type").asString();
init = trackRef.getMember("init").asString();
if (trackRef.getMember("lang")){
lang = trackRef.getMember("lang").asString();
}
if (type == "audio") {
rate = trackRef.getMember("rate").asInt();
size = trackRef.getMember("size").asInt();
channels = trackRef.getMember("channels").asInt();
}
if (type == "video") {
width = trackRef.getMember("width").asInt();
height = trackRef.getMember("height").asInt();
fpks = trackRef.getMember("fpks").asInt();
}
if (trackRef.getMember("keysizes").getType() == DTSC_STR) {
char * tmp = 0;
size_t tmplen = 0;
trackRef.getMember("keysizes").getString(tmp, tmplen);
for (unsigned int i = 0; i < tmplen; i += 4){
keySizes.push_back((((long unsigned)tmp[i]) << 24) | (((long unsigned)tmp[i+1]) << 16) | (((long unsigned int)tmp[i+2]) << 8) | tmp[i+3]);
}
}
if (trackRef.getMember("keepaway").getType() == DTSC_INT){
minKeepAway = trackRef.getMember("keepaway").asInt();
}else{
minKeepAway = 0;
}
}
///\brief Updates a track and its metadata given new packet properties.
///Will also insert keyframes on non-video tracks, and creates fragments
void Track::update(long long packTime, long long packOffset, long long packDataSize, uint64_t packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size) {
if ((unsigned long long)packTime < lastms) {
static bool warned = false;
if (!warned){
ERROR_MSG("Received packets for track %u in wrong order (%lld < %llu) - ignoring! Further messages on HIGH level.", trackID, packTime, lastms);
warned = true;
}else{
HIGH_MSG("Received packets for track %u in wrong order (%lld < %llu) - ignoring! Further messages on HIGH level.", trackID, packTime, lastms);
}
return;
}
Part newPart;
newPart.setSize(packDataSize);
newPart.setOffset(packOffset);
if (parts.size()) {
parts[parts.size() - 1].setDuration(packTime - lastms);
newPart.setDuration(packTime - lastms);
} else {
newPart.setDuration(0);
}
parts.push_back(newPart);
lastms = packTime;
if (isKeyframe || !keys.size() || (type != "video" && packTime >= AUDIO_KEY_INTERVAL && packTime - (unsigned long long)keys[keys.size() - 1].getTime() >= AUDIO_KEY_INTERVAL)){
Key newKey;
newKey.setTime(packTime);
newKey.setParts(0);
newKey.setLength(0);
if (keys.size()) {
newKey.setNumber(keys[keys.size() - 1].getNumber() + 1);
keys[keys.size() - 1].setLength(packTime - keys[keys.size() - 1].getTime());
} else {
newKey.setNumber(1);
}
if (packBytePos) { //For VoD
newKey.setBpos(packBytePos);
} else {
newKey.setBpos(0);
}
keys.push_back(newKey);
keySizes.push_back(0);
firstms = keys[0].getTime();
if (!fragments.size() || ((unsigned long long)packTime > segment_size && (unsigned long long)packTime - segment_size >= (unsigned long long)getKey(fragments.rbegin()->getNumber()).getTime())) {
//new fragment
Fragment newFrag;
newFrag.setDuration(0);
newFrag.setLength(1);
newFrag.setNumber(keys[keys.size() - 1].getNumber());
if (fragments.size()) {
fragments[fragments.size() - 1].setDuration(packTime - getKey(fragments[fragments.size() - 1].getNumber()).getTime());
uint64_t totalBytes = 0;
uint64_t totalDuration = 0;
for (std::deque<DTSC::Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++){
totalBytes += it->getSize();
totalDuration += it->getDuration();
}
bps = totalDuration ? (totalBytes * 1000) / totalDuration : 0;
max_bps = std::max(max_bps, (int)((fragments.rbegin()->getSize() * 1000) / fragments.rbegin()->getDuration()));
}
newFrag.setDuration(0);
newFrag.setSize(0);
fragments.push_back(newFrag);
//We set the insert time lastms-firstms in the future, to prevent unstable playback
fragInsertTime.push_back(Util::bootSecs() + ((lastms - firstms)/1000));
} else {
Fragment & lastFrag = fragments[fragments.size() - 1];
lastFrag.setLength(lastFrag.getLength() + 1);
}
}
keys.rbegin()->setParts(keys.rbegin()->getParts() + 1);
(*keySizes.rbegin()) += packSendSize;
fragments.rbegin()->setSize(fragments.rbegin()->getSize() + packDataSize);
}
void Track::clearParts(){
while (fragments.size() > 1){removeFirstKey();}
}
/// Removes the first buffered key, including any fragments it was part of
void Track::removeFirstKey(){
HIGH_MSG("Erasing key %d:%lu", trackID, keys[0].getNumber());
//remove all parts of this key
for (int i = 0; i < keys[0].getParts(); i++) {
parts.pop_front();
}
//remove the key itself
keys.pop_front();
keySizes.pop_front();
//update firstms
firstms = keys[0].getTime();
//delete any fragments no longer fully buffered
while (fragments.size() && keys.size() && fragments[0].getNumber() < keys[0].getNumber()) {
fragments.pop_front();
fragInsertTime.pop_front();
//and update the missed fragment counter
++missedFrags;
}
}
/// Returns the amount of whole seconds since the first fragment was inserted into the buffer.
/// This assumes playback from the start of the buffer at time of insert, meaning that
/// the time is offset by that difference. E.g.: if a buffer is 50s long, the newest fragment
/// will have a value of 0 until 50s have passed, after which it will increase at a rate of
/// 1 per second.
uint32_t Track::secsSinceFirstFragmentInsert(){
uint32_t bs = Util::bootSecs();
if (bs > fragInsertTime.front()){
return bs - fragInsertTime.front();
}else{
return 0;
}
}
void Track::finalize(){
keys.rbegin()->setLength(lastms - keys.rbegin()->getTime() + parts.rbegin()->getDuration());
}
/// Returns the duration in ms of the longest-duration fragment.
uint32_t Track::biggestFragment(){
uint32_t ret = 0;
for (unsigned int i = 0; i<fragments.size(); i++){
if (fragments[i].getDuration() > ret){
ret = fragments[i].getDuration();
}
}
return ret;
}
///\brief Returns a key given its number, or an empty key if the number is out of bounds
Key & Track::getKey(unsigned int keyNum) {
static Key empty;
if (!keys.size() || keyNum < keys[0].getNumber()) {
return empty;
}
if ((keyNum - keys[0].getNumber()) > keys.size()) {
return empty;
}
return keys[keyNum - keys[0].getNumber()];
}
///\brief Returns a fragment given its number, or an empty fragment if the number is out of bounds
Fragment & Track::getFrag(unsigned int fragNum) {
static Fragment empty;
if (!fragments.size() || fragNum < fragments[0].getNumber() || fragNum > fragments.rbegin()->getNumber()) {
return empty;
}
for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); ++it){
if (fragNum >= it->getNumber() && fragNum <= it->getNumber() + it->getLength()){
return *it;
}
}
return empty;
}
/// Returns the number of the key containing timestamp, or last key if nowhere.
unsigned int Track::timeToKeynum(unsigned int timestamp){
unsigned int result = 0;
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++){
if (it->getTime() > timestamp){
break;
}
result = it->getNumber();
}
return result;
}
/// Gets indice of the fragment containing timestamp, or last fragment if nowhere.
uint32_t Track::timeToFragnum(uint64_t timestamp){
uint32_t i = 0;
for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); ++it){
if (timestamp < getKey(it->getNumber()).getTime() + it->getDuration()){
return i;
}
++i;
}
return fragments.size()-1;
}
///\brief Resets a track, clears all meta values
void Track::reset() {
fragments.clear();
fragInsertTime.clear();
parts.clear();
keySizes.clear();
keys.clear();
bps = 0;
max_bps = 0;
firstms = 0;
lastms = 0;
}
///\brief Creates an empty meta object
Meta::Meta() {
vod = false;
live = false;
version = DTSH_VERSION;
moreheader = 0;
merged = false;
bufferWindow = 0;
bootMsOffset = 0;
}
Meta::Meta(const DTSC::Packet & source) {
reinit(source);
}
void Meta::reinit(const DTSC::Packet & source) {
tracks.clear();
vod = source.getFlag("vod");
live = source.getFlag("live");
version = source.getInt("version");
merged = source.getFlag("merged");
bufferWindow = source.getInt("buffer_window");
moreheader = source.getInt("moreheader");
bootMsOffset = source.getInt("bootoffset");
source.getString("source", sourceURI);
if (source.getScan().hasMember("inputlocalvars")){
inputLocalVars = source.getScan().getMember("inputlocalvars").asJSON();
}
Scan tmpTracks = source.getScan().getMember("tracks");
unsigned int num = 0;
Scan tmpTrack;
do {
tmpTrack = tmpTracks.getIndice(num);
if (tmpTrack.asBool()) {
unsigned int trackId = tmpTrack.getMember("trackid").asInt();
if (trackId) {
tracks[trackId] = Track(tmpTrack);
}
num++;
}
} while (tmpTrack.asBool());
}
///\brief Creates a meta object from a JSON::Value
Meta::Meta(JSON::Value & meta) {
vod = meta.isMember("vod") && meta["vod"];
live = meta.isMember("live") && meta["live"];
sourceURI = meta.isMember("source") ? meta["source"].asStringRef() : "";
version = meta.isMember("version") ? meta["version"].asInt() : 0;
bootMsOffset = meta.isMember("bootoffset") ? meta["bootoffset"].asInt() : 0;
merged = meta.isMember("merged") && meta["merged"];
bufferWindow = 0;
if (meta.isMember("buffer_window")) {
bufferWindow = meta["buffer_window"].asInt();
}
//for (JSON::ObjIter it = meta["tracks"].ObjBegin(); it != meta["tracks"].ObjEnd(); it++) {
jsonForEach(meta["tracks"], it) {
if ((*it)["trackid"].asInt()) {
tracks[(*it)["trackid"].asInt()] = Track((*it));
}
}
if (meta.isMember("moreheader")) {
moreheader = meta["moreheader"].asInt();
} else {
moreheader = 0;
}
}
///\brief Updates a meta object given a JSON::Value
void Meta::update(JSON::Value & pack, unsigned long segment_size) {
update(pack["time"].asInt(), pack.isMember("offset")?pack["offset"].asInt():0, pack["trackid"].asInt(), pack["data"].asStringRef().size(), pack.isMember("bpos")?pack["bpos"].asInt():0, pack.isMember("keyframe"), pack.packedSize(), segment_size);
}
///\brief Updates a meta object given a DTSC::Packet
void Meta::update(const DTSC::Packet & pack, unsigned long segment_size) {
char * data;
size_t dataLen;
pack.getString("data", data, dataLen);
update(pack.getTime(), pack.hasMember("offset")?pack.getInt("offset"):0, pack.getTrackId(), dataLen, pack.hasMember("bpos")?pack.getInt("bpos"):0, pack.hasMember("keyframe"), pack.getDataLen(), segment_size);
if (!bootMsOffset && pack.hasMember("bmo")){
bootMsOffset = pack.getInt("bmo");
}
}
///\brief Updates a meta object given a DTSC::Packet with byte position override.
void Meta::updatePosOverride(DTSC::Packet & pack, uint64_t bpos) {
char * data;
size_t dataLen;
pack.getString("data", data, dataLen);
update(pack.getTime(), pack.hasMember("offset")?pack.getInt("offset"):0, pack.getTrackId(), dataLen, bpos, pack.hasMember("keyframe"), pack.getDataLen());
}
void Meta::update(long long packTime, long long packOffset, long long packTrack, long long packDataSize, uint64_t packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size){
DONTEVEN_MSG("Updating meta with: t=%lld, o=%lld, s=%lld, t=%lld, p=%lld", packTime, packOffset, packDataSize, packTrack, packBytePos);
if (!packSendSize){
//time and trackID are part of the 20-byte header.
//the container object adds 4 bytes (plus 2+namelen for each content, see below)
//offset, if non-zero, adds 9 bytes (integer type) and 8 bytes (2+namelen)
//bpos, if >= 0, adds 9 bytes (integer type) and 6 bytes (2+namelen)
//keyframe, if true, adds 9 bytes (integer type) and 10 bytes (2+namelen)
//data adds packDataSize+5 bytes (string type) and 6 bytes (2+namelen)
packSendSize = 24 + (packOffset?17:0) + (packBytePos?15:0) + (isKeyframe?19:0) + packDataSize+11;
}
if (vod != (packBytePos > 0)){
INFO_MSG("Changing stream from %s to %s (bPos=%lld)", vod?"VoD":"live", packBytePos?"Vod":"live", packBytePos);
}
vod = (packBytePos > 0);
live = !vod;
EXTREME_MSG("Updating meta with %lld@%lld+%lld", packTrack, packTime, packOffset);
if (packTrack > 0 && tracks.count(packTrack)){
tracks[packTrack].update(packTime, packOffset, packDataSize, packBytePos, isKeyframe, packSendSize, segment_size);
}
}
/// Returns a reference to the first video track, or the first track.
/// Beware: returns a reference to invalid memory if there are no tracks!
/// Will print a WARN-level message if this is the case.
Track & Meta::mainTrack(){
if (!tracks.size()){
WARN_MSG("Returning nonsense reference - crashing is likely");
return tracks.begin()->second;
}
for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
if (it->second.type == "video"){
return it->second;
}
}
return tracks.begin()->second;
}
/// Returns 0 if there are no tracks, otherwise calls mainTrack().biggestFragment().
uint32_t Meta::biggestFragment(){
if (!tracks.size()){return 0;}
return mainTrack().biggestFragment();
}
///\brief Converts a track to a human readable string
///\param str The stringstream to append to
///\param indent the amount of indentation needed
///\param verbosity How verbose the output needs to be
void Track::toPrettyString(std::ostream & str, int indent, int verbosity) {
str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl;
str << std::string(indent + 2, ' ') << "ID: " << trackID << std::endl;
str << std::string(indent + 2, ' ') << "Firstms: " << firstms << std::endl;
str << std::string(indent + 2, ' ') << "Lastms: " << lastms << std::endl;
str << std::string(indent + 2, ' ') << "Bps: " << bps << std::endl;
str << std::string(indent + 2, ' ') << "Peak bps: " << max_bps << std::endl;
if (missedFrags) {
str << std::string(indent + 2, ' ') << "missedFrags: " << missedFrags << std::endl;
}
str << std::string(indent + 2, ' ') << "Codec: " << codec << std::endl;
str << std::string(indent + 2, ' ') << "Type: " << type << std::endl;
str << std::string(indent + 2, ' ') << "Init: ";
for (unsigned int i = 0; i < init.size(); ++i) {
str << std::hex << std::setw(2) << std::setfill('0') << (int)init[i];
}
str << std::dec << std::endl;
if (lang.size()){
str << std::string(indent + 2, ' ') << "Language: " << lang << std::endl;
}
if (type == "audio") {
str << std::string(indent + 2, ' ') << "Rate: " << rate << std::endl;
str << std::string(indent + 2, ' ') << "Size: " << size << std::endl;
str << std::string(indent + 2, ' ') << "Channel: " << channels << std::endl;
} else if (type == "video") {
str << std::string(indent + 2, ' ') << "Width: " << width << std::endl;
str << std::string(indent + 2, ' ') << "Height: " << height << std::endl;
str << std::string(indent + 2, ' ') << "Fpks: " << fpks << std::endl;
}
str << std::string(indent + 2, ' ') << "Fragments: " << fragments.size() << std::endl;
if (verbosity & 0x01) {
for (unsigned int i = 0; i < fragments.size(); i++) {
fragments[i].toPrettyString(str, indent + 4);
}
}
str << std::string(indent + 2, ' ') << "Keys: " << keys.size() << std::endl;
if (verbosity & 0x02) {
for (unsigned int i = 0; i < keys.size(); i++) {
keys[i].toPrettyString(str, indent + 4);
}
}
str << std::string(indent + 2, ' ') << "KeySizes: " << keySizes.size() << std::endl;
if (keySizes.size() && verbosity & 0x02){
for (unsigned int i = 0; i < keySizes.size(); i++){
str << std::string(indent + 4, ' ') << "[" << i << "] " << keySizes[i] << std::endl;
}
}
str << std::string(indent + 2, ' ') << "Parts: " << parts.size() << std::endl;
if (verbosity & 0x04) {
for (unsigned int i = 0; i < parts.size(); i++) {
parts[i].toPrettyString(str, indent + 4);
}
}
}
///\brief Converts a short to a char*
char * convertShort(short input) {
static char result[2];
result[0] = (input >> 8) & 0xFF;
result[1] = (input) & 0xFF;
return result;
}
///\brief Converts an integer to a char*
char * convertInt(int input) {
static char result[4];
result[0] = (input >> 24) & 0xFF;
result[1] = (input >> 16) & 0xFF;
result[2] = (input >> 8) & 0xFF;
result[3] = (input) & 0xFF;
return result;
}
///\brief Converts a long long to a char*
char * convertLongLong(long long int input) {
static char result[8];
result[0] = (input >> 56) & 0xFF;
result[1] = (input >> 48) & 0xFF;
result[2] = (input >> 40) & 0xFF;
result[3] = (input >> 32) & 0xFF;
result[4] = (input >> 24) & 0xFF;
result[5] = (input >> 16) & 0xFF;
result[6] = (input >> 8) & 0xFF;
result[7] = (input) & 0xFF;
return result;
}
///\brief Returns a unique identifier for a track
std::string Track::getIdentifier() {
std::stringstream result;
if (type == "") {
result << "metadata_" << trackID;
return result.str();
}else{
result << type << "_";
}
result << codec << "_";
if (type == "audio") {
result << channels << "ch_";
result << rate << "hz";
} else if (type == "video") {
result << width << "x" << height << "_";
result << (double)fpks / 1000 << "fps";
}
if (lang.size() && lang != "und"){
result << "_" << lang;
}
return result.str();
}
///\brief Returns a writable identifier for a track, to prevent overwrites on readout
std::string Track::getWritableIdentifier() {
if (cachedIdent.size()){return cachedIdent;}
std::stringstream result;
result << getIdentifier() << "_" << trackID;
cachedIdent = result.str();
return cachedIdent;
}
///\brief Determines the "packed" size of a track
int Track::getSendLen(bool skipDynamic) {
int result = 124 + init.size() + codec.size() + type.size() + getWritableIdentifier().size();
if (!skipDynamic){
result += fragments.size() * PACKED_FRAGMENT_SIZE + 16;
result += keys.size() * PACKED_KEY_SIZE + 11;
result += (keySizes.size() * 4) + 15;
result += parts.size() * 9 + 12;
}
if (lang.size() && lang != "und"){
result += 11 + lang.size();
}
if (type == "audio") {
result += 49;
} else if (type == "video") {
result += 48;
}
if (!skipDynamic && missedFrags) {
result += 23;
}
if (minKeepAway){
result += 19;
}
return result;
}
///\brief Writes a pointer to the specified destination
///
///Does a memcpy and increases the destination pointer accordingly
static void writePointer(char *& p, const char * src, unsigned int len) {
memcpy(p, src, len);
p += len;
}
///\brief Writes a pointer to the specified destination
///
///Does a memcpy and increases the destination pointer accordingly
static void writePointer(char *& p, const std::string & src) {
writePointer(p, src.data(), src.size());
}
///\brief Writes a track to a pointer
void Track::writeTo(char *& p) {
std::deque<Fragment>::iterator firstFrag = fragments.begin();
if (fragments.size() && (&firstFrag) == 0){
return;
}
std::string trackIdent = getWritableIdentifier();
writePointer(p, convertShort(trackIdent.size()), 2);
writePointer(p, trackIdent);
writePointer(p, "\340", 1);//Begin track object
writePointer(p, "\000\011fragments\002", 12);
writePointer(p, convertInt(fragments.size() * PACKED_FRAGMENT_SIZE), 4);
for (; firstFrag != fragments.end(); ++firstFrag) {
writePointer(p, firstFrag->getData(), PACKED_FRAGMENT_SIZE);
}
writePointer(p, "\000\004keys\002", 7);
writePointer(p, convertInt(keys.size() * PACKED_KEY_SIZE), 4);
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
writePointer(p, it->getData(), PACKED_KEY_SIZE);
}
writePointer(p, "\000\010keysizes\002,", 11);
writePointer(p, convertInt(keySizes.size() * 4), 4);
std::string tmp;
tmp.reserve(keySizes.size() * 4);
for (unsigned int i = 0; i < keySizes.size(); i++){
tmp += (char)(keySizes[i] >> 24);
tmp += (char)(keySizes[i] >> 16);
tmp += (char)(keySizes[i] >> 8);
tmp += (char)(keySizes[i]);
}
writePointer(p, tmp.data(), tmp.size());
writePointer(p, "\000\005parts\002", 8);
writePointer(p, convertInt(parts.size() * 9), 4);
for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
writePointer(p, it->getData(), 9);
}
writePointer(p, "\000\007trackid\001", 10);
writePointer(p, convertLongLong(trackID), 8);
if (missedFrags) {
writePointer(p, "\000\014missed_frags\001", 15);
writePointer(p, convertLongLong(missedFrags), 8);
}
writePointer(p, "\000\007firstms\001", 10);
writePointer(p, convertLongLong(firstms), 8);
writePointer(p, "\000\006lastms\001", 9);
writePointer(p, convertLongLong(lastms), 8);
writePointer(p, "\000\003bps\001", 6);
writePointer(p, convertLongLong(bps), 8);
writePointer(p, "\000\006maxbps\001", 9);
writePointer(p, convertLongLong(max_bps), 8);
writePointer(p, "\000\004init\002", 7);
writePointer(p, convertInt(init.size()), 4);
writePointer(p, init);
writePointer(p, "\000\005codec\002", 8);
writePointer(p, convertInt(codec.size()), 4);
writePointer(p, codec);
writePointer(p, "\000\004type\002", 7);
writePointer(p, convertInt(type.size()), 4);
writePointer(p, type);
if (lang.size() && lang != "und"){
writePointer(p, "\000\004lang\002", 7);
writePointer(p, convertInt(lang.size()), 4);
writePointer(p, lang);
}
if (type == "audio") {
writePointer(p, "\000\004rate\001", 7);
writePointer(p, convertLongLong(rate), 8);
writePointer(p, "\000\004size\001", 7);
writePointer(p, convertLongLong(size), 8);
writePointer(p, "\000\010channels\001", 11);
writePointer(p, convertLongLong(channels), 8);
} else if (type == "video") {
writePointer(p, "\000\005width\001", 8);
writePointer(p, convertLongLong(width), 8);
writePointer(p, "\000\006height\001", 9);
writePointer(p, convertLongLong(height), 8);
writePointer(p, "\000\004fpks\001", 7);
writePointer(p, convertLongLong(fpks), 8);
}
if (minKeepAway){
writePointer(p, "\000\010keepaway\001", 11);
writePointer(p, convertLongLong(minKeepAway), 8);
}
writePointer(p, "\000\000\356", 3);//End this track Object
}
///\brief Writes a track to a socket
void Track::send(Socket::Connection & conn, bool skipDynamic) {
conn.SendNow(convertShort(getWritableIdentifier().size()), 2);
conn.SendNow(getWritableIdentifier());
conn.SendNow("\340", 1);//Begin track object
if (!skipDynamic){
conn.SendNow("\000\011fragments\002", 12);
conn.SendNow(convertInt(fragments.size() * PACKED_FRAGMENT_SIZE), 4);
for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++) {
conn.SendNow(it->getData(), PACKED_FRAGMENT_SIZE);
}
conn.SendNow("\000\004keys\002", 7);
conn.SendNow(convertInt(keys.size() * PACKED_KEY_SIZE), 4);
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
conn.SendNow(it->getData(), PACKED_KEY_SIZE);
}
conn.SendNow("\000\010keysizes\002,", 11);
conn.SendNow(convertInt(keySizes.size() * 4), 4);
std::string tmp;
tmp.reserve(keySizes.size() * 4);
for (unsigned int i = 0; i < keySizes.size(); i++){
tmp += (char)(keySizes[i] >> 24);
tmp += (char)(keySizes[i] >> 16);
tmp += (char)(keySizes[i] >> 8);
tmp += (char)(keySizes[i]);
}
conn.SendNow(tmp.data(), tmp.size());
conn.SendNow("\000\005parts\002", 8);
conn.SendNow(convertInt(parts.size() * 9), 4);
for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
conn.SendNow(it->getData(), 9);
}
}
conn.SendNow("\000\007trackid\001", 10);
conn.SendNow(convertLongLong(trackID), 8);
if (!skipDynamic && missedFrags) {
conn.SendNow("\000\014missed_frags\001", 15);
conn.SendNow(convertLongLong(missedFrags), 8);
}
conn.SendNow("\000\007firstms\001", 10);
conn.SendNow(convertLongLong(firstms), 8);
conn.SendNow("\000\006lastms\001", 9);
conn.SendNow(convertLongLong(lastms), 8);
conn.SendNow("\000\003bps\001", 6);
conn.SendNow(convertLongLong(bps), 8);
conn.SendNow("\000\006maxbps\001", 9);
conn.SendNow(convertLongLong(max_bps), 8);
conn.SendNow("\000\004init\002", 7);
conn.SendNow(convertInt(init.size()), 4);
conn.SendNow(init);
conn.SendNow("\000\005codec\002", 8);
conn.SendNow(convertInt(codec.size()), 4);
conn.SendNow(codec);
conn.SendNow("\000\004type\002", 7);
conn.SendNow(convertInt(type.size()), 4);
conn.SendNow(type);
if (lang.size() && lang != "und"){
conn.SendNow("\000\004lang\002", 7);
conn.SendNow(convertInt(lang.size()), 4);
conn.SendNow(lang);
}
if (type == "audio") {
conn.SendNow("\000\004rate\001", 7);
conn.SendNow(convertLongLong(rate), 8);
conn.SendNow("\000\004size\001", 7);
conn.SendNow(convertLongLong(size), 8);
conn.SendNow("\000\010channels\001", 11);
conn.SendNow(convertLongLong(channels), 8);
} else if (type == "video") {
conn.SendNow("\000\005width\001", 8);
conn.SendNow(convertLongLong(width), 8);
conn.SendNow("\000\006height\001", 9);
conn.SendNow(convertLongLong(height), 8);
conn.SendNow("\000\004fpks\001", 7);
conn.SendNow(convertLongLong(fpks), 8);
}
if (minKeepAway){
conn.SendNow("\000\010keepaway\001", 11);
conn.SendNow(convertLongLong(minKeepAway), 8);
}
conn.SendNow("\000\000\356", 3);//End this track Object
}
///\brief Determines the "packed" size of a meta object
unsigned int Meta::getSendLen(bool skipDynamic, std::set<unsigned long> selectedTracks) {
unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21;
for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
if (!selectedTracks.size() || selectedTracks.count(it->first)){
dataLen += it->second.getSendLen(skipDynamic);
}
}
if (version){dataLen += 18;}
if (bootMsOffset){dataLen += 21;}
if (sourceURI.size()){dataLen += 13+sourceURI.size();}
return dataLen + 8; //add 8 bytes header
}
///\brief Writes a meta object to a pointer
void Meta::writeTo(char * p) {
int dataLen = getSendLen() - 8; //strip 8 bytes header
writePointer(p, DTSC::Magic_Header, 4);
writePointer(p, convertInt(dataLen), 4);
writePointer(p, "\340\000\006tracks\340", 10);
for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
it->second.writeTo(p);
}
writePointer(p, "\000\000\356", 3);//End tracks object
if (vod) {
writePointer(p, "\000\003vod\001", 6);
writePointer(p, convertLongLong(1), 8);
}
if (live) {
writePointer(p, "\000\004live\001", 7);
writePointer(p, convertLongLong(1), 8);
}
if (merged) {
writePointer(p, "\000\006merged\001", 9);
writePointer(p, convertLongLong(1), 8);
}
if (version) {
writePointer(p, "\000\007version\001", 10);
writePointer(p, convertLongLong(version), 8);
}
if (bootMsOffset) {
writePointer(p, "\000\012bootoffset\001", 13);
writePointer(p, convertLongLong(bootMsOffset), 8);
}
if (sourceURI.size()) {
writePointer(p, "\000\006source\002", 9);
writePointer(p, convertInt(sourceURI.size()), 4);
writePointer(p, sourceURI);
}
if (bufferWindow) {
writePointer(p, "\000\015buffer_window\001", 16);
writePointer(p, convertLongLong(bufferWindow), 8);
}
writePointer(p, "\000\012moreheader\001", 13);
writePointer(p, convertLongLong(moreheader), 8);
writePointer(p, "\000\000\356", 3);//End global object
}
///\brief Writes a meta object to a socket
void Meta::send(Socket::Connection & conn, bool skipDynamic, std::set<unsigned long> selectedTracks) {
int dataLen = getSendLen(skipDynamic, selectedTracks) - 8; //strip 8 bytes header
conn.SendNow(DTSC::Magic_Header, 4);
conn.SendNow(convertInt(dataLen), 4);
conn.SendNow("\340\000\006tracks\340", 10);
for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
if (!selectedTracks.size() || selectedTracks.count(it->first)){
it->second.send(conn, skipDynamic);
}
}
conn.SendNow("\000\000\356", 3);//End tracks object
if (vod) {
conn.SendNow("\000\003vod\001", 6);
conn.SendNow(convertLongLong(1), 8);
}
if (live) {
conn.SendNow("\000\004live\001", 7);
conn.SendNow(convertLongLong(1), 8);
}
if (merged) {
conn.SendNow("\000\006merged\001", 9);
conn.SendNow(convertLongLong(1), 8);
}
if (version) {
conn.SendNow("\000\007version\001", 10);
conn.SendNow(convertLongLong(version), 8);
}
if (bootMsOffset) {
conn.SendNow("\000\012bootoffset\001", 13);
conn.SendNow(convertLongLong(bootMsOffset), 8);
}
if (sourceURI.size()) {
conn.SendNow("\000\006source\002", 9);
conn.SendNow(convertInt(sourceURI.size()), 4);
conn.SendNow(sourceURI);
}
if (bufferWindow) {
conn.SendNow("\000\015buffer_window\001", 16);
conn.SendNow(convertLongLong(bufferWindow), 8);
}
conn.SendNow("\000\012moreheader\001", 13);
conn.SendNow(convertLongLong(moreheader), 8);
conn.SendNow("\000\000\356", 3);//End global object
}
///\brief Converts a track to a JSON::Value
JSON::Value Track::toJSON(bool skipDynamic) {
JSON::Value result;
std::string tmp;
if (!skipDynamic) {
tmp.reserve(fragments.size() * PACKED_FRAGMENT_SIZE);
for (std::deque<Fragment>::iterator it = fragments.begin(); it != fragments.end(); it++) {
tmp.append(it->getData(), PACKED_FRAGMENT_SIZE);
}
result["fragments"] = tmp;
tmp = "";
tmp.reserve(keys.size() * PACKED_KEY_SIZE);
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++) {
tmp.append(it->getData(), PACKED_KEY_SIZE);
}
result["keys"] = tmp;
tmp = "";
tmp.reserve(keySizes.size() * 4);
for (unsigned int i = 0; i < keySizes.size(); i++){
tmp += (char)((keySizes[i] >> 24));
tmp += (char)((keySizes[i] >> 16));
tmp += (char)((keySizes[i] >> 8));
tmp += (char)(keySizes[i]);
}
result["keysizes"] = tmp;
tmp = "";
tmp.reserve(parts.size() * 9);
for (std::deque<Part>::iterator it = parts.begin(); it != parts.end(); it++) {
tmp.append(it->getData(), 9);
}
result["parts"] = tmp;
}
result["init"] = init;
if (lang.size() && lang != "und"){
result["lang"] = lang;
}
result["trackid"] = trackID;
result["firstms"] = firstms;
result["lastms"] = lastms;
result["bps"] = bps;
result["maxbps"] = max_bps;
if (missedFrags) {
result["missed_frags"] = missedFrags;
}
result["codec"] = codec;
result["type"] = type;
if (type == "audio") {
result["rate"] = rate;
result["size"] = size;
result["channels"] = channels;
} else if (type == "video") {
result["width"] = width;
result["height"] = height;
result["fpks"] = fpks;
}
if(minKeepAway){
result["keepaway"] = minKeepAway;
}
return result;
}
///\brief Converts a meta object to a JSON::Value
JSON::Value Meta::toJSON() {
JSON::Value result;
for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
result["tracks"][it->second.getWritableIdentifier()] = it->second.toJSON();
}
if (vod) {
result["vod"] = 1;
}
if (live) {
result["live"] = 1;
}
if (merged) {
result["merged"] = 1;
}
if (bufferWindow) {
result["buffer_window"] = bufferWindow;
}
if (version) {
result["version"] = version;
}
if (bootMsOffset){
result["bootoffset"] = bootMsOffset;
}
if (sourceURI.size()){
result["source"] = sourceURI;
}
result["moreheader"] = moreheader;
if (inputLocalVars){
result["inputlocalvars"] = inputLocalVars;
}
return result;
}
///\brief Writes metadata to a filename. Wipes existing contents, if any.
bool Meta::toFile(const std::string & fileName){
std::ofstream oFile(fileName.c_str());
oFile << toJSON().toNetPacked();
if (!oFile.good()){return false;}
oFile.close();
return true;
}
///\brief Converts a meta object to a human readable string
///\param str The stringstream to append to
///\param indent the amount of indentation needed
///\param verbosity How verbose the output needs to be
void Meta::toPrettyString(std::ostream & str, int indent, int verbosity) {
for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
it->second.toPrettyString(str, indent, verbosity);
}
if (vod) {
str << std::string(indent, ' ') << "Video on Demand" << std::endl;
}
if (live) {
str << std::string(indent, ' ') << "Live" << std::endl;
}
if (merged) {
str << std::string(indent, ' ') << "Merged file" << std::endl;
}
if (bufferWindow) {
str << std::string(indent, ' ') << "Buffer Window: " << bufferWindow << std::endl;
}
if (sourceURI.size()){
str << std::string(indent, ' ') << "Source: " << sourceURI << std::endl;
}
if (bootMsOffset){
str << std::string(indent, ' ') << "Boot MS offset: " << bootMsOffset << std::endl;
}
str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl;
}
///\brief Resets a meta object, removes all unimportant meta values
void Meta::reset() {
for (std::map<unsigned int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
it->second.reset();
}
bootMsOffset = 0;
}
PartIter::PartIter(Track & Trk, Fragment & frag){
tRef = &Trk;
pIt = tRef->parts.begin();
kIt = tRef->keys.begin();
uint32_t fragNum = frag.getNumber();
while (kIt->getNumber() < fragNum && kIt != tRef->keys.end()){
uint32_t kParts = kIt->getParts();
for (uint32_t pCount = 0; pCount < kParts && pIt != tRef->parts.end(); ++pCount){
++pIt;
}
++kIt;
}
if (kIt == tRef->keys.end()){tRef = 0;}
currInKey = 0;
lastKey = fragNum + frag.getLength();
}
/// Dereferences into a Value reference.
/// If invalid iterator, returns an empty reference and prints a warning message.
Part & PartIter::operator*() const{
if (tRef && pIt != tRef->parts.end()){
return *pIt;
}
static Part error;
WARN_MSG("Dereferenced invalid Part iterator");
return error;
}
/// Dereferences into a Value reference.
/// If invalid iterator, returns an empty reference and prints a warning message.
Part* PartIter::operator->() const{
return &(operator*());
}
/// True if not done iterating.
PartIter::operator bool() const{
return (tRef && pIt != tRef->parts.end());
}
PartIter & PartIter::operator++(){
if (*this){
++pIt;
if (++currInKey >= kIt->getParts()){
currInKey = 0;
//check if we're done iterating - we assume done if past the last key or arrived past the fragment
if (++kIt == tRef->keys.end() || kIt->getNumber() >= lastKey){
tRef = 0;
}
}
}
return *this;
}
}