#include "dtsc.h" #include "defines.h" #include #include #include /// Retrieves a short in network order from the pointer p. static short btohs(char * p) { return (p[0] << 8) + p[1]; } /// Stores a short value of val in network order to the pointer p. static void htobs(char * p, short val) { p[0] = (val >> 8) & 0xFF; p[1] = val & 0xFF; } /// Retrieves a long in network order from the pointer p. static long btohl(char * p) { return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; } /// Stores a long value of val in network order to the pointer p. static void htobl(char * p, long val) { p[0] = (val >> 24) & 0xFF; p[1] = (val >> 16) & 0xFF; p[2] = (val >> 8) & 0xFF; p[3] = val & 0xFF; } 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) { Packet(rhs.data, rhs.dataLen, !rhs.master); } /// 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) { 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) { DEBUG_MSG(DLVL_DONTEVEN, "No data"); return false; } if (dataLen < 8) { DEBUG_MSG(DLVL_DONTEVEN, "Datalen < 8"); return false; } if (version == DTSC_INVALID) { DEBUG_MSG(DLVL_DONTEVEN, "No valid version"); return false; } if (ntohl(((int *)data)[1]) + 8 != dataLen) { DEBUG_MSG(DLVL_DONTEVEN, "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(unsigned int len) { if (master && len > bufferLen) { char * tmp = (char *)realloc(data, len); if (tmp) { data = tmp; bufferLen = len; } else { DEBUG_MSG(DLVL_FAIL, "Out of memory on parsing a packet"); } } } ///\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_) { DEBUG_MSG(DLVL_DEVEL, "ReInit received a null pointer with len %d, ignoring", len); null(); return; } if (data_[0] != 'D' || data_[1] != 'T') { DEBUG_MSG(DLVL_HIGH, "ReInit received a pointer that didn't start with 'DT' - data corruption?"); 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 > 3) { if (!memcmp(data, Magic_Packet2, 4)) { version = DTSC_V2; } else { if (!memcmp(data, Magic_Packet, 4)) { version = DTSC_V1; } else { if (!memcmp(data, Magic_Header, 4)) { version = DTSC_HEAD; } else { DEBUG_MSG(DLVL_FAIL, "ReInit received a packet with invalid header"); return; } } } } else { DEBUG_MSG(DLVL_FAIL, "ReInit received a packet with size < 4"); return; } } /// 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 + p[1] * 256 * 256 * 256 + p[2] * 256 * 256 + p[3] * 256 + p[4]; } if (p[0] == DTSC_OBJ || p[0] == DTSC_CON) { p++; //object, scan contents while (p[0] + p[1] != 0 && p < max) { //while not encountering 0x0000 (we assume 0x0000EE) if (p + 2 >= max) { return 0;//out of packet! } p += 2 + (p[0] * 256 + p[1]);//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[0] + p[1] != 0 && p < max) { //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, unsigned int & 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, int & 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 int Packet::getInt(const char * identifier) const { int 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 { int result_; getInt(identifier, result_); result = (bool)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. long long unsigned int Packet::getTime() const { if (version != DTSC_V2) { if (!data) { return 0; } return getInt("time"); } return ((long long int)ntohl(((int *)(data + 12))[0]) << 32) | ntohl(((int *)(data + 12))[1]); } ///\brief Returns the track id of the packet. ///\return The track id of this packet. long int Packet::getTrackId() const { if (version != DTSC_V2) { return getInt("trackid"); } return ntohl(((int *)data)[2]); } ///\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. int Packet::getDataLen() const { return dataLen; } ///\brief Returns the size of the payload of this packet. ///\return The size of the payload of this packet. int 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 { 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; unsigned int i = 8; if (getVersion() == DTSC_V1) { JSON::fromDTMI((const unsigned char *)data, dataLen, i, result); } if (getVersion() == DTSC_V2) { JSON::fromDTMI2((const unsigned char *)data, dataLen, i, result); } return result; } /// 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 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(std::string indice) { 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 unsigned int ind_len) { 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! } unsigned int strlen = i[0] * 256 + i[1]; i += 2; if (ind_len == strlen && strncmp(indice, i, strlen) == 0) { return Scan(i + strlen, len - (i - p)); } else { 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. Scan Scan::getMember(const char * indice) { return getMember(indice, strlen(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(unsigned int num) { 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 = i[0] * 256 + i[1]; 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 first byte of this DTSC value, or 0 on error. char Scan::getType() { 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() { 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. long long Scan::asInt() { switch (getType()) { case DTSC_INT: return ((long long int)p[1] << 56) | ((long long int)p[2] << 48) | ((long long int)p[3] << 40) | ((long long int)p[4] << 32) | ((long long int)p[5] << 24) | ((long long int)p[6] << 16) | ((long long int)p[7] << 8) | p[8]; case DTSC_STR: char * str; unsigned int 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, does no conversion. /// Returns an empty string on error. std::string Scan::asString() { char * str; unsigned int strlen; getString(str, strlen); return std::string(str, strlen); } /// Sets result to a pointer to the string, and strlen to the lenght of it. /// Sets both to zero if this isn't a DTSC string value. /// Attempts absolutely no conversion. void Scan::getString(char *& result, unsigned int & strlen) { switch (getType()) { case DTSC_STR: result = p + 5; strlen = p[1] * 256 * 256 * 256 + p[2] * 256 * 256 + p[3] * 256 + p[4]; return; default: result = 0; strlen = 0; return; } } /// \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 (unsigned int 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(unsigned int indent) { switch (getType()) { case DTSC_STR: { unsigned int strlen = p[1] * 256 * 256 * 256 + p[2] * 256 * 256 + p[3] * 256 + p[4]; 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((size_t)indent, ' ') << "} //walked out of object here"; return ret.str(); } if (!first){ ret << "," << std::endl; } first = false; unsigned int strlen = i[0] * 256 + i[1]; i += 2; ret << std::string((size_t)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((size_t)indent, ' ') << "} //could not locate next object"; return ret.str(); } } indent -= 2; ret << std::endl << std::string((size_t)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((size_t)indent, ' ') << tmpScan.toPrettyString(indent); } } while (tmpScan.getType()); indent -= 2; ret << std::endl << std::string((size_t)indent, ' ') << "]"; return ret.str(); } default: return "Error"; } } ///\brief Returns the payloadsize of a part long Part::getSize() { return ((long)data[0] << 16) | ((long)data[1] << 8) | data[2]; } ///\brief Sets the payloadsize of a part void Part::setSize(long newSize) { data[0] = (newSize & 0xFF0000) >> 16; data[1] = (newSize & 0x00FF00) >> 8; data[2] = (newSize & 0x0000FF); } ///\brief Retruns the duration of a part short Part::getDuration() { return btohs(data + 3); } ///\brief Sets the duration of a part void Part::setDuration(short newDuration) { htobs(data + 3, newDuration); } ///\brief returns the offset of a part long Part::getOffset() { return btohl(data + 5); } ///\brief Sets the offset of a part void Part::setOffset(long newOffset) { htobl(data + 5, 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 long long unsigned int Key::getBpos() { return (((long long unsigned int)data[0] << 32) | (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]); } ///\brief Returns the byteposition of a keyframe void Key::setBpos(long long unsigned int newBpos) { data[4] = newBpos & 0xFF; data[3] = (newBpos >> 8) & 0xFF; data[2] = (newBpos >> 16) & 0xFF; data[1] = (newBpos >> 24) & 0xFF; data[0] = (newBpos >> 32) & 0xFF; } ///\brief Returns the byteposition of a keyframe long Key::getLength() { return ((data[5] << 16) | (data[6] << 8) | data[7]); } ///\brief Sets the byteposition of a keyframe void Key::setLength(long newLength) { data[7] = newLength & 0xFF; data[6] = (newLength >> 8) & 0xFF; data[5] = (newLength >> 16) & 0xFF; } ///\brief Returns the number of a keyframe unsigned short Key::getNumber() { return btohs(data + 8); } ///\brief Sets the number of a keyframe void Key::setNumber(unsigned short newNumber) { htobs(data + 8, newNumber); } ///\brief Returns the number of parts of a keyframe short Key::getParts() { return btohs(data + 10); } ///\brief Sets the number of parts of a keyframe void Key::setParts(short newParts) { htobs(data + 10, newParts); } ///\brief Returns the timestamp of a keyframe long Key::getTime() { return btohl(data + 12); } ///\brief Sets the timestamp of a keyframe void Key::setTime(long newTime) { htobl(data + 12, 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 long Fragment::getDuration() { return btohl(data); } ///\brief Sets the duration of this fragment void Fragment::setDuration(long newDuration) { 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 short Fragment::getNumber() { return btohs(data + 5); } ///\brief Sets the number of the first keyframe in this fragment void Fragment::setNumber(short newNumber) { htobs(data + 5, newNumber); } ///\brief Returns the size of a fragment long Fragment::getSize() { return btohl(data + 7); } ///\brief Sets the size of a fragement void Fragment::setSize(long newSize) { htobl(data + 7, 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 readOnlyTrack readOnlyTrack::readOnlyTrack() { fragments = NULL; fragLen = 0; keys = NULL; keyLen = 0; parts = NULL; partLen = 0; missedFrags = 0; firstms = 0; lastms = 0; bps = 0; rate = 0; size = 0; channels = 0; width = 0; height = 0; fpks = 0; } ///\brief Constructs a readOnlyTrack from a JSON::Value readOnlyTrack::readOnlyTrack(JSON::Value & trackRef) { if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) { fragments = (Fragment *)trackRef["fragments"].asStringRef().data(); fragLen = trackRef["fragments"].asStringRef().size() / 11; } else { fragments = 0; fragLen = 0; } if (trackRef.isMember("keys") && trackRef["keys"].isString()) { keys = (Key *)trackRef["keys"].asStringRef().data(); keyLen = trackRef["keys"].asStringRef().size() / 16; } else { keys = 0; keyLen = 0; } if (trackRef.isMember("parts") && trackRef["parts"].isString()) { parts = (Part *)trackRef["parts"].asStringRef().data(); partLen = trackRef["parts"].asStringRef().size() / 9; } else { parts = 0; partLen = 0; } trackID = trackRef["trackid"].asInt(); firstms = trackRef["firstms"].asInt(); lastms = trackRef["lastms"].asInt(); bps = trackRef["bps"].asInt(); missedFrags = trackRef["missed_frags"].asInt(); codec = trackRef["codec"].asStringRef(); type = trackRef["type"].asStringRef(); init = trackRef["init"].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 (codec == "vorbis" || codec == "theora") { idHeader = trackRef["idheader"].asStringRef(); commentHeader = trackRef["commentheader"].asStringRef(); } } ///\brief Constructs an empty track Track::Track() { trackID = 0; firstms = 0; lastms = 0; bps = 0; missedFrags = 0; rate = 0; size = 0; channels = 0; width = 0; height = 0; fpks = 0; } ///\brief Constructs a track from a readOnlyTrack Track::Track(const readOnlyTrack & rhs) { trackID = rhs.trackID; firstms = rhs.firstms; lastms = rhs.lastms; bps = rhs.bps; missedFrags = rhs.missedFrags; init = rhs.init; codec = rhs.codec; type = rhs.type; rate = rhs.rate; size = rhs.size; channels = rhs.channels; width = rhs.width; height = rhs.height; fpks = rhs.fpks; idHeader = rhs.idHeader; commentHeader = rhs.commentHeader; if (rhs.fragments && rhs.fragLen) { fragments = std::deque(rhs.fragments, rhs.fragments + rhs.fragLen); } if (rhs.keys && rhs.keyLen) { keys = std::deque(rhs.keys, rhs.keys + rhs.keyLen); } if (rhs.parts && rhs.partLen) { parts = std::deque(rhs.parts, rhs.parts + rhs.partLen); } } ///\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(tmp, tmp + (trackRef["fragments"].asStringRef().size() / 11)); } if (trackRef.isMember("keys") && trackRef["keys"].isString()) { Key * tmp = (Key *)trackRef["keys"].asStringRef().data(); keys = std::deque(tmp, tmp + (trackRef["keys"].asStringRef().size() / 16)); } if (trackRef.isMember("parts") && trackRef["parts"].isString()) { Part * tmp = (Part *)trackRef["parts"].asStringRef().data(); parts = std::deque(tmp, tmp + (trackRef["parts"].asStringRef().size() / 9)); } trackID = trackRef["trackid"].asInt(); firstms = trackRef["firstms"].asInt(); lastms = trackRef["lastms"].asInt(); bps = trackRef["bps"].asInt(); missedFrags = trackRef["missed_frags"].asInt(); codec = trackRef["codec"].asStringRef(); type = trackRef["type"].asStringRef(); init = trackRef["init"].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 (codec == "vorbis" || codec == "theora") { idHeader = trackRef["idheader"].asStringRef(); commentHeader = trackRef["commentheader"].asStringRef(); } } ///\brief Constructs a track from a JSON::Value Track::Track(Scan & trackRef) { if (trackRef.getMember("fragments").getType() == DTSC_STR) { char * tmp = 0; unsigned int tmplen = 0; trackRef.getMember("fragments").getString(tmp, tmplen); fragments = std::deque((Fragment *)tmp, ((Fragment *)tmp) + (tmplen / 11)); } if (trackRef.getMember("keys").getType() == DTSC_STR) { char * tmp = 0; unsigned int tmplen = 0; trackRef.getMember("keys").getString(tmp, tmplen); keys = std::deque((Key *)tmp, ((Key *)tmp) + (tmplen / 16)); } if (trackRef.getMember("parts").getType() == DTSC_STR) { char * tmp = 0; unsigned int tmplen = 0; trackRef.getMember("parts").getString(tmp, tmplen); parts = std::deque((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(); missedFrags = trackRef.getMember("missed_frags").asInt(); codec = trackRef.getMember("codec").asString(); type = trackRef.getMember("type").asString(); init = trackRef.getMember("init").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(); } } ///\brief Updates a track and its metadata given a DTSC::Packet. /// ///Will also insert keyframes on non-video tracks, and creates fragments void Track::update(DTSC::Packet & pack) { if (pack.getTime() < lastms) { DEBUG_MSG(DLVL_WARN, "Received packets for track %d in wrong order (%d < %d) - ignoring!", (int)trackID, (int)pack.getTime(), (int)lastms); return; } Part newPart; char * data; unsigned int dataLen; pack.getString("data", data, dataLen); newPart.setSize(dataLen); newPart.setOffset(pack.getInt("offset")); if (parts.size()) { parts[parts.size() - 1].setDuration(pack.getTime() - lastms); newPart.setDuration(pack.getTime() - lastms); } else { newPart.setDuration(0); } parts.push_back(newPart); lastms = pack.getTime(); if (pack.getFlag("keyframe") || !keys.size() || (type != "video" && pack.getTime() > 5000 && pack.getTime() - 5000 > (unsigned long long)keys[keys.size() - 1].getTime())) { Key newKey; newKey.setTime(pack.getTime()); newKey.setParts(0); newKey.setLength(0); if (keys.size()) { newKey.setNumber(keys[keys.size() - 1].getNumber() + 1); keys[keys.size() - 1].setLength(pack.getTime() - keys[keys.size() - 1].getTime()); } else { newKey.setNumber(1); } if (pack.hasMember("bpos")) { //For VoD newKey.setBpos(pack.getInt("bpos")); } else { newKey.setBpos(0); } keys.push_back(newKey); firstms = keys[0].getTime(); if (!fragments.size() || pack.getTime() - 5000 >= (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(pack.getTime() - getKey(fragments[fragments.size() - 1].getNumber()).getTime()); if (!bps && fragments[fragments.size() - 1].getDuration() > 1000) { bps = (fragments[fragments.size() - 1].getSize() * 1000) / fragments[fragments.size() - 1].getDuration(); } } newFrag.setDuration(0); newFrag.setSize(0); fragments.push_back(newFrag); } else { Fragment & lastFrag = fragments[fragments.size() - 1]; lastFrag.setLength(lastFrag.getLength() + 1); } } keys.rbegin()->setParts(keys.rbegin()->getParts() + 1); fragments.rbegin()->setSize(fragments.rbegin()->getSize() + dataLen); } ///\brief Updates a track and its metadata given a JSON::Value /// ///Will also insert keyframes on non-video tracks, and creates fragments void Track::update(JSON::Value & pack) { if ((unsigned long long)pack["time"].asInt() < lastms) { DEBUG_MSG(DLVL_WARN, "Received packets for track %d in wrong order (%d < %d) - ignoring!", (int)trackID, (int)pack["time"].asInt(), (int)lastms); return; } Part newPart; newPart.setSize(pack["data"].asStringRef().size()); newPart.setOffset(pack["offset"].asInt()); if (parts.size()) { parts[parts.size() - 1].setDuration(pack["time"].asInt() - lastms); newPart.setDuration(pack["time"].asInt() - lastms); } else { newPart.setDuration(0); } parts.push_back(newPart); lastms = pack["time"].asInt(); if (pack.isMember("keyframe") || !keys.size() || (type != "video" && pack["time"].asInt() - 5000 > keys[keys.size() - 1].getTime())) { Key newKey; newKey.setTime(pack["time"].asInt()); newKey.setParts(0); newKey.setLength(0); if (keys.size()) { newKey.setNumber(keys[keys.size() - 1].getNumber() + 1); keys[keys.size() - 1].setLength(pack["time"].asInt() - keys[keys.size() - 1].getTime()); } else { newKey.setNumber(1); } if (pack.isMember("bpos")) { //For VoD newKey.setBpos(pack["bpos"].asInt()); } else { newKey.setBpos(0); } keys.push_back(newKey); firstms = keys[0].getTime(); if (!fragments.size() || pack["time"].asInt() - 5000 >= 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(pack["time"].asInt() - getKey(fragments[fragments.size() - 1].getNumber()).getTime()); if (!bps && fragments[fragments.size() - 1].getDuration() > 1000) { bps = (fragments[fragments.size() - 1].getSize() * 1000) / fragments[fragments.size() - 1].getDuration(); } } newFrag.setDuration(0); newFrag.setSize(0); fragments.push_back(newFrag); } else { Fragment & lastFrag = fragments[fragments.size() - 1]; lastFrag.setLength(lastFrag.getLength() + 1); } } keys.rbegin()->setParts(keys.rbegin()->getParts() + 1); fragments.rbegin()->setSize(fragments.rbegin()->getSize() + pack["data"].asStringRef().size()); } ///\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 (keyNum < keys[0].getNumber()) { return empty; } if ((keyNum - keys[0].getNumber()) > keys.size()) { return empty; } return keys[keyNum - keys[0].getNumber()]; } ///\brief Returns a unique identifier for a track std::string readOnlyTrack::getIdentifier() { std::stringstream result; if (type == "") { result << "metadata_" << trackID; return result.str(); } 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"; } return result.str(); } ///\brief Returns a writable identifier for a track, to prevent overwrites on readout std::string readOnlyTrack::getWritableIdentifier() { std::stringstream result; result << getIdentifier() << "_" << trackID; return result.str(); } ///\brief Resets a track, clears all meta values void Track::reset() { fragments.clear(); parts.clear(); keys.clear(); bps = 0; firstms = 0; lastms = 0; } ///\brief Creates an empty read-only meta object readOnlyMeta::readOnlyMeta() { vod = false; live = false; merged = false; moreheader = 0; merged = false; bufferWindow = 0; } ///\brief Creates a read-only meta object from a given JSON::Value readOnlyMeta::readOnlyMeta(JSON::Value & meta) { vod = meta.isMember("vod") && meta["vod"]; live = meta.isMember("live") && meta["live"]; 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++) { if (it->second.isMember("trackid") && it->second["trackid"]) { tracks[it->second["trackid"].asInt()] = readOnlyTrack(it->second); } } if (meta.isMember("moreheader")) { moreheader = meta["moreheader"].asInt(); } else { moreheader = 0; } } ///\brief Converts a read-only 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 readOnlyTrack::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; 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 (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; } if (codec == "vorbis" || codec == "theora") { str << std::string(indent + 2, ' ') << "IdHeader: " << idHeader << std::endl; str << std::string(indent + 2, ' ') << "CommentHeader: " << commentHeader << std::endl; } str << std::string(indent + 2, ' ') << "Fragments: " << fragLen << std::endl; if (fragments && verbosity & 0x01) { for (unsigned int i = 0; i < fragLen; i++) { fragments[i].toPrettyString(str, indent + 4); } } str << std::string(indent + 2, ' ') << "Keys: " << keyLen << std::endl; if (keys && verbosity & 0x02) { for (unsigned int i = 0; i < keyLen; i++) { keys[i].toPrettyString(str, indent + 4); } } str << std::string(indent + 2, ' ') << "Parts: " << partLen << std::endl; if (parts && verbosity & 0x04) { for (unsigned int i = 0; i < partLen; i++) { parts[i].toPrettyString(str, indent + 4); } } } ///\brief Creates an empty meta object Meta::Meta() { vod = false; live = false; moreheader = 0; merged = false; bufferWindow = 0; } ///\brief Creates a meta object from a read-only meta object Meta::Meta(const readOnlyMeta & rhs) { vod = rhs.vod; live = rhs.live; merged = rhs.merged; bufferWindow = rhs.bufferWindow; for (std::map::const_iterator it = rhs.tracks.begin(); it != rhs.tracks.end(); it++) { tracks[it->first] = it->second; } moreheader = rhs.moreheader; } 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"); merged = source.getFlag("merged"); bufferWindow = source.getInt("buffer_window"); moreheader = source.getInt("moreheader"); Scan tmpTracks = source.getScan().getMember("tracks"); unsigned int num = 0; Scan tmpTrack; do { tmpTrack = tmpTracks.getIndice(num); if (tmpTrack.asBool()) { 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"]; 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++) { if (it->second["trackid"].asInt()) { tracks[it->second["trackid"].asInt()] = Track(it->second); } } 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) { vod = pack.isMember("bpos"); live = !vod; if (pack["trackid"].asInt() && tracks.count(pack["trackid"].asInt())) { tracks[pack["trackid"].asInt()].update(pack); } } ///\brief Updates a meta object given a DTSC::Packet void Meta::update(DTSC::Packet & pack) { vod = pack.hasMember("bpos"); live = !vod; if (pack.getTrackId() && tracks.count(pack.getTrackId())) { tracks[pack.getTrackId()].update(pack); } } ///\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; 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 (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; } if (codec == "vorbis" || codec == "theora") { str << std::string(indent + 2, ' ') << "IdHeader: " << idHeader << std::endl; str << std::string(indent + 2, ' ') << "CommentHeader: " << commentHeader << 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, ' ') << "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 Determines the "packed" size of a read-only track int readOnlyTrack::getSendLen() { int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size(); result += fragLen * 11; result += keyLen * 16; result += partLen * 9; if (type == "audio") { result += 49; } else if (type == "video") { result += 48; } if (codec == "vorbis" || codec == "theora") { result += 15 + idHeader.size();//idheader result += 20 + commentHeader.size();//commentheader } if (missedFrags) { result += 23; } return result; } ///\brief Determines the "packed" size of a track int Track::getSendLen() { int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size(); result += fragments.size() * 11; result += keys.size() * 16; result += parts.size() * 9; if (type == "audio") { result += 49; } else if (type == "video") { result += 48; } if (codec == "vorbis" || codec == "theora") { result += 15 + idHeader.size();//idheader result += 20 + commentHeader.size();//commentheader } if (missedFrags) { result += 23; } 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 read-only track to a pointer void readOnlyTrack::writeTo(char *& p) { std::string iden = getWritableIdentifier(); writePointer(p, convertShort(iden.size()), 2); writePointer(p, iden); writePointer(p, "\340", 1);//Begin track object writePointer(p, "\000\011fragments\002", 12); writePointer(p, convertInt(fragLen * 11), 4); writePointer(p, (char *)fragments, fragLen * 11); writePointer(p, "\000\004keys\002", 7); writePointer(p, convertInt(keyLen * 16), 4); writePointer(p, (char *)keys, keyLen * 16); writePointer(p, "\000\005parts\002", 8); writePointer(p, convertInt(partLen * 9), 4); writePointer(p, (char *)parts, partLen * 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\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 (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 (codec == "vorbis" || codec == "theora") { writePointer(p, "\000\010idheader\002", 11); writePointer(p, convertInt(idHeader.size()), 4); writePointer(p, idHeader); writePointer(p, "\000\015commentheader\002", 16); writePointer(p, convertInt(commentHeader.size()), 4); writePointer(p, commentHeader); } writePointer(p, "\000\000\356", 3);//End this track Object } ///\brief Writes a read-only track to a socket void readOnlyTrack::send(Socket::Connection & conn) { conn.SendNow(convertShort(getWritableIdentifier().size()), 2); conn.SendNow(getWritableIdentifier()); conn.SendNow("\340", 1);//Begin track object conn.SendNow("\000\011fragments\002", 12); conn.SendNow(convertInt(fragLen * 11), 4); conn.SendNow((char *)fragments, fragLen * 11); conn.SendNow("\000\004keys\002", 7); conn.SendNow(convertInt(keyLen * 16), 4); conn.SendNow((char *)keys, keyLen * 16); conn.SendNow("\000\005parts\002", 8); conn.SendNow(convertInt(partLen * 9), 4); conn.SendNow((char *)parts, partLen * 9); conn.SendNow("\000\007trackid\001", 10); conn.SendNow(convertLongLong(trackID), 8); if (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\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 (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 (codec == "vorbis" || codec == "theora") { conn.SendNow("\000\010idheader\002", 11); conn.SendNow(convertInt(idHeader.size()), 4); conn.SendNow(idHeader); conn.SendNow("\000\015commentheader\002", 16); conn.SendNow(convertInt(commentHeader.size()), 4); conn.SendNow(commentHeader); } conn.SendNow("\000\000\356", 3);//End this track Object } ///\brief Writes a track to a pointer void Track::writeTo(char *& p) { writePointer(p, convertShort(getWritableIdentifier().size()), 2); writePointer(p, getWritableIdentifier()); writePointer(p, "\340", 1);//Begin track object writePointer(p, "\000\011fragments\002", 12); writePointer(p, convertInt(fragments.size() * 11), 4); for (std::deque::iterator it = fragments.begin(); it != fragments.end(); it++) { writePointer(p, it->getData(), 11); } writePointer(p, "\000\004keys\002", 7); writePointer(p, convertInt(keys.size() * 16), 4); for (std::deque::iterator it = keys.begin(); it != keys.end(); it++) { writePointer(p, it->getData(), 16); } writePointer(p, "\000\005parts\002", 8); writePointer(p, convertInt(parts.size() * 9), 4); for (std::deque::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\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 (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 (codec == "vorbis" || codec == "theora") { writePointer(p, "\000\010idheader\002", 11); writePointer(p, convertInt(idHeader.size()), 4); writePointer(p, idHeader); writePointer(p, "\000\015commentheader\002", 16); writePointer(p, convertInt(commentHeader.size()), 4); writePointer(p, commentHeader); } writePointer(p, "\000\000\356", 3);//End this track Object } ///\brief Writes a track to a socket void Track::send(Socket::Connection & conn) { conn.SendNow(convertShort(getWritableIdentifier().size()), 2); conn.SendNow(getWritableIdentifier()); conn.SendNow("\340", 1);//Begin track object conn.SendNow("\000\011fragments\002", 12); conn.SendNow(convertInt(fragments.size() * 11), 4); for (std::deque::iterator it = fragments.begin(); it != fragments.end(); it++) { conn.SendNow(it->getData(), 11); } conn.SendNow("\000\004keys\002", 7); conn.SendNow(convertInt(keys.size() * 16), 4); for (std::deque::iterator it = keys.begin(); it != keys.end(); it++) { conn.SendNow(it->getData(), 16); } conn.SendNow("\000\005parts\002", 8); conn.SendNow(convertInt(parts.size() * 9), 4); for (std::deque::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 (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\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 (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 (codec == "vorbis" || codec == "theora") { conn.SendNow("\000\010idheader\002", 11); conn.SendNow(convertInt(idHeader.size()), 4); conn.SendNow(idHeader); conn.SendNow("\000\015commentheader\002", 16); conn.SendNow(convertInt(commentHeader.size()), 4); conn.SendNow(commentHeader); } conn.SendNow("\000\000\356", 3);//End this track Object } ///\brief Determines the "packed" size of a read-only meta object unsigned int readOnlyMeta::getSendLen() { unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++) { dataLen += it->second.getSendLen(); } return dataLen + 8; //add 8 bytes header length } ///\brief Writes a read-only meta object to a pointer void readOnlyMeta::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::iterator it = tracks.begin(); it != tracks.end(); it++) { it->second.writeTo(p); } writePointer(p, "\000\000\356", 3); 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 (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 read-only meta object to a socket void readOnlyMeta::send(Socket::Connection & conn) { int dataLen = getSendLen() - 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::iterator it = tracks.begin(); it != tracks.end(); it++) { it->second.send(conn); } conn.SendNow("\000\000\356", 3); 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 (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 Determines the "packed" size of a meta object unsigned int Meta::getSendLen() { unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++) { dataLen += it->second.getSendLen(); } 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::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 (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) { int dataLen = getSendLen() - 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::iterator it = tracks.begin(); it != tracks.end(); it++) { it->second.send(conn); } 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 (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 read-only track to a JSON::Value JSON::Value readOnlyTrack::toJSON() { JSON::Value result; if (fragments) { result["fragments"] = std::string((char *)fragments, fragLen * 11); } if (keys) { result["keys"] = std::string((char *)keys, keyLen * 16); } if (parts) { result["parts"] = std::string((char *)parts, partLen * 9); } result["trackid"] = trackID; result["firstms"] = (long long)firstms; result["lastms"] = (long long)lastms; result["bps"] = bps; if (missedFrags) { result["missed_frags"] = missedFrags; } result["codec"] = codec; result["type"] = type; result["init"] = init; 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 (codec == "vorbis" || codec == "theora") { result["idheader"] = idHeader; result["commentheader"] = commentHeader; } return result; } ///\brief Converts a track to a JSON::Value JSON::Value Track::toJSON() { JSON::Value result; std::string tmp; tmp.reserve(fragments.size() * 11); for (std::deque::iterator it = fragments.begin(); it != fragments.end(); it++) { tmp.append(it->getData(), 11); } result["fragments"] = tmp; tmp = ""; tmp.reserve(keys.size() * 16); for (std::deque::iterator it = keys.begin(); it != keys.end(); it++) { tmp.append(it->getData(), 16); } result["keys"] = tmp; tmp = ""; tmp.reserve(parts.size() * 9); for (std::deque::iterator it = parts.begin(); it != parts.end(); it++) { tmp.append(it->getData(), 9); } result["parts"] = tmp; result["trackid"] = trackID; result["firstms"] = (long long)firstms; result["lastms"] = (long long)lastms; result["bps"] = bps; if (missedFrags) { result["missed_frags"] = missedFrags; } result["codec"] = codec; result["type"] = type; result["init"] = init; 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 (codec == "vorbis" || codec == "theora") { result["idheader"] = idHeader; result["commentheader"] = commentHeader; } return result; } ///\brief Converts a meta object to a JSON::Value JSON::Value Meta::toJSON() { JSON::Value result; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++) { result["tracks"][it->second.getWritableIdentifier()] = it->second.toJSON(); } if (vod) { result["vod"] = 1ll; } if (live) { result["live"] = 1ll; } if (merged) { result["merged"] = 1ll; } if (bufferWindow) { result["buffer_window"] = bufferWindow; } result["moreheader"] = moreheader; return result; } ///\brief Converts a read-only 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 readOnlyMeta::toPrettyString(std::ostream & str, int indent, int verbosity) { for (std::map::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; } str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl; } ///\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::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; } str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl; } ///\brief Converts a read-only meta object to a JSON::Value JSON::Value readOnlyMeta::toJSON() { JSON::Value result; for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++) { result["tracks"][it->second.getWritableIdentifier()] = it->second.toJSON(); } if (vod) { result["vod"] = 1ll; } if (live) { result["live"] = 1ll; } if (merged) { result["merged"] = 1ll; } result["moreheader"] = moreheader; if (bufferWindow) { result["buffer_window"] = bufferWindow; } return result; } ///\brief Resets a meta object, removes all unimportant meta values void Meta::reset() { for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++) { it->second.reset(); } } ///\brief Returns whether a read-only meta object is fixed or not bool readOnlyMeta::isFixed() { for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++) { if (!it->second.keyLen || !(it->second.keys[it->second.keyLen - 1].getBpos())) { return false; } } return true; } ///\brief Returns whether a meta object is fixed or not bool Meta::isFixed() { for (std::map::iterator it = tracks.begin(); it != tracks.end(); it++) { if (it->second.type == "meta" || it->second.type == "") { continue; } if (!it->second.keys.size() || !(it->second.keys.rbegin()->getBpos())) { return false; } } return true; } }