diff --git a/lib/amf.cpp b/lib/amf.cpp index 3d3fa9bc..db12e679 100644 --- a/lib/amf.cpp +++ b/lib/amf.cpp @@ -6,69 +6,66 @@ #include /// Returns the std::string Indice for the current object, if available. /// Returns an empty string if no indice exists. -std::string AMF::Object::Indice() { +std::string AMF::Object::Indice(){ return myIndice; } /// Returns the AMF::obj0type AMF0 object type for this object. -AMF::obj0type AMF::Object::GetType() { +AMF::obj0type AMF::Object::GetType(){ return myType; } /// Returns the numeric value of this object, if available. /// If this object holds no numeric value, 0 is returned. -double AMF::Object::NumValue() { +double AMF::Object::NumValue(){ return numval; } /// Returns the std::string value of this object, if available. /// If this object holds no string value, an empty string is returned. -std::string AMF::Object::StrValue() { +std::string AMF::Object::StrValue(){ return strval; } /// Returns the C-string value of this object, if available. /// If this object holds no string value, an empty C-string is returned. -const char * AMF::Object::Str() { +const char *AMF::Object::Str(){ return strval.c_str(); } /// Returns a count of the amount of objects this object currently holds. /// If this object is not a container type, this function will always return 0. -int AMF::Object::hasContent() { +int AMF::Object::hasContent(){ return contents.size(); } -/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. -void AMF::Object::addContent(AMF::Object c) { +/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container +/// types. +void AMF::Object::addContent(AMF::Object c){ contents.push_back(c); } /// Returns a pointer to the object held at indice i. /// Returns AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object * AMF::Object::getContentP(unsigned int i) { - if (i >= contents.size()) { - return 0; - } +AMF::Object *AMF::Object::getContentP(unsigned int i){ + if (i >= contents.size()){return 0;} return &contents.at(i); } /// Returns a copy of the object held at indice i. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object AMF::Object::getContent(unsigned int i) { +AMF::Object AMF::Object::getContent(unsigned int i){ return contents.at(i); } /// Returns a pointer to the object held at indice s. /// Returns NULL if no object is held at this indice. /// \param s The indice of the object in this container. -AMF::Object * AMF::Object::getContentP(std::string s) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - if (it->Indice() == s) { - return &(*it); - } +AMF::Object *AMF::Object::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} } return 0; } @@ -76,27 +73,25 @@ AMF::Object * AMF::Object::getContentP(std::string s) { /// Returns a copy of the object held at indice s. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param s The indice of the object in this container. -AMF::Object AMF::Object::getContent(std::string s) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - if (it->Indice() == s) { - return *it; - } +AMF::Object AMF::Object::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} } return AMF::Object("error", AMF0_DDV_CONTAINER); } /// Default constructor. /// Simply fills the data with AMF::Object("error", AMF0_DDV_CONTAINER) -AMF::Object::Object() { +AMF::Object::Object(){ *this = AMF::Object("error", AMF0_DDV_CONTAINER); -} //default constructor +}// default constructor /// Constructor for numeric objects. /// The object type is by default AMF::AMF0_NUMBER, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The numeric value of this object. Numeric AMF0 objects only support double-type values. -/// \param setType The object type to force this object to. -AMF::Object::Object(std::string indice, double val, AMF::obj0type setType) { //num type initializer +/// \param indice The string indice of this object in its container, or empty string if none. +/// Numeric indices are automatic. \param val The numeric value of this object. Numeric AMF0 objects +/// only support double-type values. \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, double val, AMF::obj0type setType){// num type initializer myIndice = indice; myType = setType; strval = ""; @@ -105,11 +100,26 @@ AMF::Object::Object(std::string indice, double val, AMF::obj0type setType) { //n /// Constructor for string objects. /// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. -/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The string value of this object. -/// \param setType The object type to force this object to. -AMF::Object::Object(std::string indice, std::string val, AMF::obj0type setType) { //str type initializer +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done +/// automatically. \param indice The string indice of this object in its container, or empty string +/// if none. Numeric indices are automatic. \param val The string value of this object. \param +/// setType The object type to force this object to. +AMF::Object::Object(std::string indice, std::string val, + AMF::obj0type setType){// str type initializer + myIndice = indice; + myType = setType; + strval = val; + numval = 0; +} + +/// Constructor for string objects. +/// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done +/// automatically. \param indice The string indice of this object in its container, or empty string +/// if none. Numeric indices are automatic. \param val The string value of this object. \param +/// setType The object type to force this object to. +AMF::Object::Object(std::string indice, const char *val, + AMF::obj0type setType){// str type initializer myIndice = indice; myType = setType; strval = val; @@ -118,9 +128,9 @@ AMF::Object::Object(std::string indice, std::string val, AMF::obj0type setType) /// Constructor for container objects. /// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param setType The object type to force this object to. -AMF::Object::Object(std::string indice, AMF::obj0type setType) { //object type initializer +/// \param indice The string indice of this object in its container, or empty string if none. +/// Numeric indices are automatic. \param setType The object type to force this object to. +AMF::Object::Object(std::string indice, AMF::obj0type setType){// object type initializer myIndice = indice; myType = setType; strval = ""; @@ -130,234 +140,186 @@ AMF::Object::Object(std::string indice, AMF::obj0type setType) { //object type i /// Return the contents as a human-readable string. /// If this object contains other objects, it will call itself recursively /// and print all nested content as well. -std::string AMF::Object::Print(std::string indent) { +std::string AMF::Object::Print(std::string indent){ std::stringstream st; st << indent; // print my type - switch (myType) { - case AMF::AMF0_NUMBER: - st << "Number"; - break; - case AMF::AMF0_BOOL: - st << "Bool"; - break; - case AMF::AMF0_STRING: //short string - case AMF::AMF0_LONGSTRING: - st << "String"; - break; - case AMF::AMF0_OBJECT: - st << "Object"; - break; - case AMF::AMF0_MOVIECLIP: - st << "MovieClip"; - break; - case AMF::AMF0_NULL: - st << "Null"; - break; - case AMF::AMF0_UNDEFINED: - st << "Undefined"; - break; - case AMF::AMF0_REFERENCE: - st << "Reference"; - break; - case AMF::AMF0_ECMA_ARRAY: - st << "ECMA Array"; - break; - case AMF::AMF0_OBJ_END: - st << "Object end"; - break; - case AMF::AMF0_STRICT_ARRAY: - st << "Strict Array"; - break; - case AMF::AMF0_DATE: - st << "Date"; - break; - case AMF::AMF0_UNSUPPORTED: - st << "Unsupported"; - break; - case AMF::AMF0_RECORDSET: - st << "Recordset"; - break; - case AMF::AMF0_XMLDOC: - st << "XML Document"; - break; - case AMF::AMF0_TYPED_OBJ: - st << "Typed Object"; - break; - case AMF::AMF0_UPGRADE: - st << "Upgrade to AMF3"; - break; - case AMF::AMF0_DDV_CONTAINER: - st << "DDVTech Container"; - break; + switch (myType){ + case AMF::AMF0_NUMBER: st << "Number"; break; + case AMF::AMF0_BOOL: st << "Bool"; break; + case AMF::AMF0_STRING: // short string + case AMF::AMF0_LONGSTRING: st << "String"; break; + case AMF::AMF0_OBJECT: st << "Object"; break; + case AMF::AMF0_MOVIECLIP: st << "MovieClip"; break; + case AMF::AMF0_NULL: st << "Null"; break; + case AMF::AMF0_UNDEFINED: st << "Undefined"; break; + case AMF::AMF0_REFERENCE: st << "Reference"; break; + case AMF::AMF0_ECMA_ARRAY: st << "ECMA Array"; break; + case AMF::AMF0_OBJ_END: st << "Object end"; break; + case AMF::AMF0_STRICT_ARRAY: st << "Strict Array"; break; + case AMF::AMF0_DATE: st << "Date"; break; + case AMF::AMF0_UNSUPPORTED: st << "Unsupported"; break; + case AMF::AMF0_RECORDSET: st << "Recordset"; break; + case AMF::AMF0_XMLDOC: st << "XML Document"; break; + case AMF::AMF0_TYPED_OBJ: st << "Typed Object"; break; + case AMF::AMF0_UPGRADE: st << "Upgrade to AMF3"; break; + case AMF::AMF0_DDV_CONTAINER: st << "DDVTech Container"; break; } // print my string indice, if available st << " " << myIndice << " "; // print my numeric or string contents - switch (myType) { - case AMF::AMF0_NUMBER: - case AMF::AMF0_BOOL: - case AMF::AMF0_REFERENCE: - case AMF::AMF0_DATE: - st << numval; - break; - case AMF::AMF0_STRING: - case AMF::AMF0_LONGSTRING: - case AMF::AMF0_XMLDOC: - case AMF::AMF0_TYPED_OBJ: - st << strval; - break; - default: - break; //we don't care about the rest, and don't want a compiler warning... + switch (myType){ + case AMF::AMF0_NUMBER: + case AMF::AMF0_BOOL: + case AMF::AMF0_REFERENCE: + case AMF::AMF0_DATE: st << numval; break; + case AMF::AMF0_STRING: + case AMF::AMF0_LONGSTRING: + case AMF::AMF0_XMLDOC: + case AMF::AMF0_TYPED_OBJ: st << strval; break; + default: break; // we don't care about the rest, and don't want a compiler warning... } st << std::endl; // if I hold other objects, print those too, recursively. - if (contents.size() > 0) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ st << it->Print(indent + " "); } } return st.str(); -} //print +}// print /// Packs the AMF object to a std::string for transfer over the network. -/// If the object is a container type, this function will call itself recursively and contain all contents. -/// Tip: When sending multiple AMF objects in one go, put them in a single AMF::AMF0_DDV_CONTAINER for easy transfer. -std::string AMF::Object::Pack() { +/// If the object is a container type, this function will call itself recursively and contain all +/// contents. Tip: When sending multiple AMF objects in one go, put them in a single +/// AMF::AMF0_DDV_CONTAINER for easy transfer. +std::string AMF::Object::Pack(){ std::string r = ""; - //check for string/longstring conversion - if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)) { - myType = AMF::AMF0_LONGSTRING; - } - //skip output of DDV container types, they do not exist. Only output their contents. - if (myType != AMF::AMF0_DDV_CONTAINER) { - r += myType; - } - //output the properly formatted AMF0 data stream for this object's contents. - switch (myType) { - case AMF::AMF0_NUMBER: - r += *(((char *) &numval) + 7); - r += *(((char *) &numval) + 6); - r += *(((char *) &numval) + 5); - r += *(((char *) &numval) + 4); - r += *(((char *) &numval) + 3); - r += *(((char *) &numval) + 2); - r += *(((char *) &numval) + 1); - r += *(((char *) &numval)); - break; - case AMF::AMF0_DATE: - r += *(((char *) &numval) + 7); - r += *(((char *) &numval) + 6); - r += *(((char *) &numval) + 5); - r += *(((char *) &numval) + 4); - r += *(((char *) &numval) + 3); - r += *(((char *) &numval) + 2); - r += *(((char *) &numval) + 1); - r += *(((char *) &numval)); - r += (char)0; //timezone always 0 - r += (char)0; //timezone always 0 - break; - case AMF::AMF0_BOOL: - r += (char)numval; - break; - case AMF::AMF0_STRING: - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case AMF::AMF0_LONGSTRING: - case AMF::AMF0_XMLDOC: //is always a longstring - r += strval.size() / (256 * 256 * 256); - r += strval.size() / (256 * 256); - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case AMF::AMF0_TYPED_OBJ: //is an object, with the classname first - r += Indice().size() / 256; - r += Indice().size() % 256; - r += Indice(); - /* no break */ - case AMF::AMF0_OBJECT: - if (contents.size() > 0) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } + // check for string/longstring conversion + if ((myType == AMF::AMF0_STRING) && (strval.size() > 0xFFFF)){myType = AMF::AMF0_LONGSTRING;} + // skip output of DDV container types, they do not exist. Only output their contents. + if (myType != AMF::AMF0_DDV_CONTAINER){r += myType;} + // output the properly formatted AMF0 data stream for this object's contents. + switch (myType){ + case AMF::AMF0_NUMBER: + r += *(((char *)&numval) + 7); + r += *(((char *)&numval) + 6); + r += *(((char *)&numval) + 5); + r += *(((char *)&numval) + 4); + r += *(((char *)&numval) + 3); + r += *(((char *)&numval) + 2); + r += *(((char *)&numval) + 1); + r += *(((char *)&numval)); + break; + case AMF::AMF0_DATE: + r += *(((char *)&numval) + 7); + r += *(((char *)&numval) + 6); + r += *(((char *)&numval) + 5); + r += *(((char *)&numval) + 4); + r += *(((char *)&numval) + 3); + r += *(((char *)&numval) + 2); + r += *(((char *)&numval) + 1); + r += *(((char *)&numval)); + r += (char)0; // timezone always 0 + r += (char)0; // timezone always 0 + break; + case AMF::AMF0_BOOL: r += (char)numval; break; + case AMF::AMF0_STRING: + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF::AMF0_LONGSTRING: + case AMF::AMF0_XMLDOC: // is always a longstring + r += strval.size() / (256 * 256 * 256); + r += strval.size() / (256 * 256); + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case AMF::AMF0_TYPED_OBJ: // is an object, with the classname first + r += Indice().size() / 256; + r += Indice().size() % 256; + r += Indice(); + /* no break */ + case AMF::AMF0_OBJECT: + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); } + } + r += (char)0; + r += (char)0; + r += (char)9; + break; + case AMF::AMF0_MOVIECLIP: + case AMF::AMF0_OBJ_END: + case AMF::AMF0_UPGRADE: + case AMF::AMF0_NULL: + case AMF::AMF0_UNDEFINED: + case AMF::AMF0_RECORDSET: + case AMF::AMF0_UNSUPPORTED: + // no data to add + break; + case AMF::AMF0_REFERENCE: + r += (char)((int)numval / 256); + r += (char)((int)numval % 256); + break; + case AMF::AMF0_ECMA_ARRAY:{ + int arrlen = 0; + if (contents.size() > 0){ + arrlen = contents.size(); + r += arrlen / (256 * 256 * 256); + r += arrlen / (256 * 256); + r += arrlen / 256; + r += arrlen % 256; + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); + } + }else{ r += (char)0; r += (char)0; - r += (char)9; - break; - case AMF::AMF0_MOVIECLIP: - case AMF::AMF0_OBJ_END: - case AMF::AMF0_UPGRADE: - case AMF::AMF0_NULL: - case AMF::AMF0_UNDEFINED: - case AMF::AMF0_RECORDSET: - case AMF::AMF0_UNSUPPORTED: - //no data to add - break; - case AMF::AMF0_REFERENCE: - r += (char)((int)numval / 256); - r += (char)((int)numval % 256); - break; - case AMF::AMF0_ECMA_ARRAY: { - int arrlen = 0; - if (contents.size() > 0) { - arrlen = contents.size(); - r += arrlen / (256 * 256 * 256); - r += arrlen / (256 * 256); - r += arrlen / 256; - r += arrlen % 256; - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - r += it->Indice().size() / 256; - r += it->Indice().size() % 256; - r += it->Indice(); - r += it->Pack(); - } - } else { - r += (char)0; - r += (char)0; - r += (char)0; - r += (char)0; - } - r += (char)0; - r += (char)0; - r += (char)9; + r += (char)0; + r += (char)0; + } + r += (char)0; + r += (char)0; + r += (char)9; + }break; + case AMF::AMF0_STRICT_ARRAY:{ + int arrlen = 0; + if (contents.size() > 0){ + arrlen = contents.size(); + r += arrlen / (256 * 256 * 256); + r += arrlen / (256 * 256); + r += arrlen / 256; + r += arrlen % 256; + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); } - break; - case AMF::AMF0_STRICT_ARRAY: { - int arrlen = 0; - if (contents.size() > 0) { - arrlen = contents.size(); - r += arrlen / (256 * 256 * 256); - r += arrlen / (256 * 256); - r += arrlen / 256; - r += arrlen % 256; - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - r += it->Pack(); - } - } else { - r += (char)0; - r += (char)0; - r += (char)0; - r += (char)0; - } + }else{ + r += (char)0; + r += (char)0; + r += (char)0; + r += (char)0; + } + }break; + case AMF::AMF0_DDV_CONTAINER: // only send contents + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + r += it->Pack(); } - break; - case AMF::AMF0_DDV_CONTAINER: //only send contents - if (contents.size() > 0) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - r += it->Pack(); - } - } - break; + } + break; } return r; -} //pack +}// pack /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. /// This function updates i every call with the new position in the data. @@ -366,232 +328,238 @@ std::string AMF::Object::Pack() { /// \param i Current parsing position in the raw data. /// \param name Indice name for any new object created. /// \returns A single AMF::Object, parsed from the raw data. -AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int & len, unsigned int & i, std::string name) { +AMF::Object AMF::parseOne(const unsigned char *&data, unsigned int &len, unsigned int &i, + std::string name){ std::string tmpstr; unsigned int tmpi = 0; unsigned char tmpdbl[8]; - double * d; // hack to work around strict aliasing - switch (data[i]) { - case AMF::AMF0_NUMBER: - tmpdbl[7] = data[i + 1]; - tmpdbl[6] = data[i + 2]; - tmpdbl[5] = data[i + 3]; - tmpdbl[4] = data[i + 4]; - tmpdbl[3] = data[i + 5]; - tmpdbl[2] = data[i + 6]; - tmpdbl[1] = data[i + 7]; - tmpdbl[0] = data[i + 8]; - i += 9; //skip 8(a double)+1 forwards - d = (double *)tmpdbl; - return AMF::Object(name, *d, AMF::AMF0_NUMBER); - break; - case AMF::AMF0_DATE: - tmpdbl[7] = data[i + 1]; - tmpdbl[6] = data[i + 2]; - tmpdbl[5] = data[i + 3]; - tmpdbl[4] = data[i + 4]; - tmpdbl[3] = data[i + 5]; - tmpdbl[2] = data[i + 6]; - tmpdbl[1] = data[i + 7]; - tmpdbl[0] = data[i + 8]; - i += 11; //skip 8(a double)+1+timezone(2) forwards - d = (double *)tmpdbl; - return AMF::Object(name, *d, AMF::AMF0_DATE); - break; - case AMF::AMF0_BOOL: - i += 2; //skip bool+1 forwards - if (data[i - 1] == 0) { - return AMF::Object(name, (double)0, AMF::AMF0_BOOL); - } else { - return AMF::Object(name, (double)1, AMF::AMF0_BOOL); - } - break; - case AMF::AMF0_REFERENCE: - tmpi = data[i + 1] * 256 + data[i + 2]; //get the ref number value as a double - i += 3; //skip ref+1 forwards - return AMF::Object(name, (double)tmpi, AMF::AMF0_REFERENCE); - break; - case AMF::AMF0_XMLDOC: - tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i + 5, (size_t)tmpi); //add the string data - i += tmpi + 5; //skip length+size+1 forwards - return AMF::Object(name, tmpstr, AMF::AMF0_XMLDOC); - break; - case AMF::AMF0_LONGSTRING: - tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i + 5, (size_t)tmpi); //add the string data - i += tmpi + 5; //skip length+size+1 forwards - return AMF::Object(name, tmpstr, AMF::AMF0_LONGSTRING); - break; - case AMF::AMF0_STRING: - tmpi = data[i + 1] * 256 + data[i + 2]; //set tmpi to UTF-8 length - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i + 3, (size_t)tmpi); //add the string data - i += tmpi + 3; //skip length+size+1 forwards - return AMF::Object(name, tmpstr, AMF::AMF0_STRING); - break; - case AMF::AMF0_NULL: - case AMF::AMF0_UNDEFINED: - case AMF::AMF0_UNSUPPORTED: - ++i; - return AMF::Object(name, (double)0, (AMF::obj0type)data[i - 1]); - break; - case AMF::AMF0_OBJECT: { - ++i; - AMF::Object ret(name, AMF::AMF0_OBJECT); - while (data[i] + data[i + 1] != 0) { //while not encountering 0x0000 (we assume 0x000009) - tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i + 2, (size_t)tmpi); //add the string data - i += tmpi + 2; //skip length+size forwards - ret.addContent(AMF::parseOne(data, len, i, tmpstr)); //add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3; //skip 0x000009 - return ret; - } - break; - case AMF::AMF0_TYPED_OBJ: { - ++i; - tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i + 2, (size_t)tmpi); //add the string data - AMF::Object ret(tmpstr, AMF::AMF0_TYPED_OBJ); //the object is not named "name" but tmpstr - while (data[i] + data[i + 1] != 0) { //while not encountering 0x0000 (we assume 0x000009) - tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i + 2, (size_t)tmpi); //add the string data - i += tmpi + 2; //skip length+size forwards - ret.addContent(AMF::parseOne(data, len, i, tmpstr)); //add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3; //skip 0x000009 - return ret; - } - break; - case AMF::AMF0_ECMA_ARRAY: { - ++i; - AMF::Object ret(name, AMF::AMF0_ECMA_ARRAY); - i += 4; //ignore the array length, we re-calculate it - while (data[i] + data[i + 1] != 0) { //while not encountering 0x0000 (we assume 0x000009) - tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i + 2, (size_t)tmpi); //add the string data - i += tmpi + 2; //skip length+size forwards - ret.addContent(AMF::parseOne(data, len, i, tmpstr)); //add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3; //skip 0x000009 - return ret; - } - break; - case AMF::AMF0_STRICT_ARRAY: { - AMF::Object ret(name, AMF::AMF0_STRICT_ARRAY); - tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to array length - i += 5; //skip size+1 forwards - while (tmpi > 0) { //while not done parsing array - ret.addContent(AMF::parseOne(data, len, i, "arrVal")); //add content, recursively parsed, updating i - --tmpi; - } - return ret; - } - break; + double *d; // hack to work around strict aliasing + switch (data[i]){ + case AMF::AMF0_NUMBER: + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 9; // skip 8(a double)+1 forwards + d = (double *)tmpdbl; + return AMF::Object(name, *d, AMF::AMF0_NUMBER); + break; + case AMF::AMF0_DATE: + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 11; // skip 8(a double)+1+timezone(2) forwards + d = (double *)tmpdbl; + return AMF::Object(name, *d, AMF::AMF0_DATE); + break; + case AMF::AMF0_BOOL: + i += 2; // skip bool+1 forwards + if (data[i - 1] == 0){ + return AMF::Object(name, (double)0, AMF::AMF0_BOOL); + }else{ + return AMF::Object(name, (double)1, AMF::AMF0_BOOL); + } + break; + case AMF::AMF0_REFERENCE: + tmpi = data[i + 1] * 256 + data[i + 2]; // get the ref number value as a double + i += 3; // skip ref+1 forwards + return AMF::Object(name, (double)tmpi, AMF::AMF0_REFERENCE); + break; + case AMF::AMF0_XMLDOC: + tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + + data[i + 4]; // set tmpi to UTF-8-long length + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 5, (size_t)tmpi); // add the string data + i += tmpi + 5; // skip length+size+1 forwards + return AMF::Object(name, tmpstr, AMF::AMF0_XMLDOC); + break; + case AMF::AMF0_LONGSTRING: + tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + + data[i + 4]; // set tmpi to UTF-8-long length + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 5, (size_t)tmpi); // add the string data + i += tmpi + 5; // skip length+size+1 forwards + return AMF::Object(name, tmpstr, AMF::AMF0_LONGSTRING); + break; + case AMF::AMF0_STRING: + tmpi = data[i + 1] * 256 + data[i + 2]; // set tmpi to UTF-8 length + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 3, (size_t)tmpi); // add the string data + i += tmpi + 3; // skip length+size+1 forwards + return AMF::Object(name, tmpstr, AMF::AMF0_STRING); + break; + case AMF::AMF0_NULL: + case AMF::AMF0_UNDEFINED: + case AMF::AMF0_UNSUPPORTED: + ++i; + return AMF::Object(name, (double)0, (AMF::obj0type)data[i - 1]); + break; + case AMF::AMF0_OBJECT:{ + ++i; + AMF::Object ret(name, AMF::AMF0_OBJECT); + while (data[i] + data[i + 1] != 0){// while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i] * 256 + data[i + 1]; // set tmpi to the UTF-8 length + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 2, (size_t)tmpi); // add the string data + i += tmpi + 2; // skip length+size forwards + ret.addContent(AMF::parseOne( + data, len, i, + tmpstr)); // add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3; // skip 0x000009 + return ret; + }break; + case AMF::AMF0_TYPED_OBJ:{ + ++i; + tmpi = data[i] * 256 + data[i + 1]; // set tmpi to the UTF-8 length + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 2, (size_t)tmpi); // add the string data + AMF::Object ret(tmpstr, AMF::AMF0_TYPED_OBJ); // the object is not named "name" but tmpstr + while (data[i] + data[i + 1] != 0){// while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i] * 256 + data[i + 1]; // set tmpi to the UTF-8 length + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 2, (size_t)tmpi); // add the string data + i += tmpi + 2; // skip length+size forwards + ret.addContent(AMF::parseOne( + data, len, i, + tmpstr)); // add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3; // skip 0x000009 + return ret; + }break; + case AMF::AMF0_ECMA_ARRAY:{ + ++i; + AMF::Object ret(name, AMF::AMF0_ECMA_ARRAY); + i += 4; // ignore the array length, we re-calculate it + while (data[i] + data[i + 1] != 0){// while not encountering 0x0000 (we assume 0x000009) + tmpi = data[i] * 256 + data[i + 1]; // set tmpi to the UTF-8 length + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i + 2, (size_t)tmpi); // add the string data + i += tmpi + 2; // skip length+size forwards + ret.addContent(AMF::parseOne( + data, len, i, + tmpstr)); // add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3; // skip 0x000009 + return ret; + }break; + case AMF::AMF0_STRICT_ARRAY:{ + AMF::Object ret(name, AMF::AMF0_STRICT_ARRAY); + tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + + data[i + 4]; // set tmpi to array length + i += 5; // skip size+1 forwards + while (tmpi > 0){// while not done parsing array + ret.addContent( + AMF::parseOne(data, len, i, "arrVal")); // add content, recursively parsed, updating i + --tmpi; + } + return ret; + }break; } - DEBUG_MSG(DLVL_ERROR, "Error: Unimplemented AMF type %hhx - returning.", data[i]); + ERROR_MSG("Error: Unimplemented AMF type %hhx - returning.", data[i]); return AMF::Object("error", AMF::AMF0_DDV_CONTAINER); -} //parseOne +}// parseOne /// Parses a C-string to a valid AMF::Object. /// This function will find all AMF objects in the string and return /// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. -AMF::Object AMF::parse(const unsigned char * data, unsigned int len) { - AMF::Object ret("returned", AMF::AMF0_DDV_CONTAINER); //container type +AMF::Object AMF::parse(const unsigned char *data, unsigned int len){ + AMF::Object ret("returned", AMF::AMF0_DDV_CONTAINER); // container type unsigned int i = 0, j = 0; - while (i < len) { + while (i < len){ ret.addContent(AMF::parseOne(data, len, i, "")); - if (i > j) { + if (i > j){ j = i; - } else { + }else{ return ret; } } return ret; -} //parse +}// parse /// Parses a std::string to a valid AMF::Object. /// This function will find all AMF objects in the string and return /// them all packed in a single AMF::AMF0_DDV_CONTAINER AMF::Object. -AMF::Object AMF::parse(std::string data) { +AMF::Object AMF::parse(std::string data){ return AMF::parse((const unsigned char *)data.c_str(), data.size()); -} //parse +}// parse /// Returns the std::string Indice for the current object, if available. /// Returns an empty string if no indice exists. -std::string AMF::Object3::Indice() { +std::string AMF::Object3::Indice(){ return myIndice; } /// Returns the AMF::obj0type AMF0 object type for this object. -AMF::obj3type AMF::Object3::GetType() { +AMF::obj3type AMF::Object3::GetType(){ return myType; } /// Returns the double value of this object, if available. /// If this object holds no double value, 0 is returned. -double AMF::Object3::DblValue() { +double AMF::Object3::DblValue(){ return dblval; } /// Returns the integer value of this object, if available. /// If this object holds no integer value, 0 is returned. -int AMF::Object3::IntValue() { +int AMF::Object3::IntValue(){ return intval; } /// Returns the std::string value of this object, if available. /// If this object holds no string value, an empty string is returned. -std::string AMF::Object3::StrValue() { +std::string AMF::Object3::StrValue(){ return strval; } /// Returns the C-string value of this object, if available. /// If this object holds no string value, an empty C-string is returned. -const char * AMF::Object3::Str() { +const char *AMF::Object3::Str(){ return strval.c_str(); } /// Returns a count of the amount of objects this object currently holds. /// If this object is not a container type, this function will always return 0. -int AMF::Object3::hasContent() { +int AMF::Object3::hasContent(){ return contents.size(); } -/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container types. -void AMF::Object3::addContent(AMF::Object3 c) { +/// Adds an AMF::Object to this object. Works for all types, but only makes sense for container +/// types. +void AMF::Object3::addContent(AMF::Object3 c){ contents.push_back(c); } /// Returns a pointer to the object held at indice i. /// Returns AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object3 * AMF::Object3::getContentP(int i) { +AMF::Object3 *AMF::Object3::getContentP(int i){ return &contents.at(i); } /// Returns a copy of the object held at indice i. /// Returns a AMF::AMF3_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param i The indice of the object in this container. -AMF::Object3 AMF::Object3::getContent(int i) { +AMF::Object3 AMF::Object3::getContent(int i){ return contents.at(i); } /// Returns a pointer to the object held at indice s. /// Returns NULL if no object is held at this indice. /// \param s The indice of the object in this container. -AMF::Object3 * AMF::Object3::getContentP(std::string s) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - if (it->Indice() == s) { - return &(*it); - } +AMF::Object3 *AMF::Object3::getContentP(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return &(*it);} } return 0; } @@ -599,27 +567,26 @@ AMF::Object3 * AMF::Object3::getContentP(std::string s) { /// Returns a copy of the object held at indice s. /// Returns a AMF::AMF0_DDV_CONTAINER of indice "error" if no object is held at this indice. /// \param s The indice of the object in this container. -AMF::Object3 AMF::Object3::getContent(std::string s) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { - if (it->Indice() == s) { - return *it; - } +AMF::Object3 AMF::Object3::getContent(std::string s){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ + if (it->Indice() == s){return *it;} } return AMF::Object3("error", AMF3_DDV_CONTAINER); } /// Default constructor. /// Simply fills the data with AMF::Object3("error", AMF3_DDV_CONTAINER) -AMF::Object3::Object3() { +AMF::Object3::Object3(){ *this = AMF::Object3("error", AMF3_DDV_CONTAINER); -} //default constructor +}// default constructor /// Constructor for double objects. /// The object type is by default AMF::AMF3_DOUBLE, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The numeric value of this object. Double AMF3 objects only support double-type values. -/// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, double val, AMF::obj3type setType) { //num type initializer +/// \param indice The string indice of this object in its container, or empty string if none. +/// Numeric indices are automatic. \param val The numeric value of this object. Double AMF3 objects +/// only support double-type values. \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, double val, + AMF::obj3type setType){// num type initializer myIndice = indice; myType = setType; strval = ""; @@ -629,10 +596,10 @@ AMF::Object3::Object3(std::string indice, double val, AMF::obj3type setType) { / /// Constructor for integer objects. /// The object type is by default AMF::AMF3_INTEGER, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The numeric value of this object. Integer AMF3 objects only support integer-type values. -/// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, int val, AMF::obj3type setType) { //num type initializer +/// \param indice The string indice of this object in its container, or empty string if none. +/// Numeric indices are automatic. \param val The numeric value of this object. Integer AMF3 objects +/// only support integer-type values. \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, int val, AMF::obj3type setType){// num type initializer myIndice = indice; myType = setType; strval = ""; @@ -642,11 +609,12 @@ AMF::Object3::Object3(std::string indice, int val, AMF::obj3type setType) { //nu /// Constructor for string objects. /// The object type is by default AMF::AMF0_STRING, but this can be forced to a different value. -/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done automatically. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param val The string value of this object. -/// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType) { //str type initializer +/// There is no need to manually change the type to AMF::AMF0_LONGSTRING, this will be done +/// automatically. \param indice The string indice of this object in its container, or empty string +/// if none. Numeric indices are automatic. \param val The string value of this object. \param +/// setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, std::string val, + AMF::obj3type setType){// str type initializer myIndice = indice; myType = setType; strval = val; @@ -656,9 +624,9 @@ AMF::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType /// Constructor for container objects. /// The object type is by default AMF::AMF0_OBJECT, but this can be forced to a different value. -/// \param indice The string indice of this object in its container, or empty string if none. Numeric indices are automatic. -/// \param setType The object type to force this object to. -AMF::Object3::Object3(std::string indice, AMF::obj3type setType) { //object type initializer +/// \param indice The string indice of this object in its container, or empty string if none. +/// Numeric indices are automatic. \param setType The object type to force this object to. +AMF::Object3::Object3(std::string indice, AMF::obj3type setType){// object type initializer myIndice = indice; myType = setType; strval = ""; @@ -669,107 +637,73 @@ AMF::Object3::Object3(std::string indice, AMF::obj3type setType) { //object type /// Return the contents as a human-readable string. /// If this object contains other objects, it will call itself recursively /// and print all nested content as well. -std::string AMF::Object3::Print(std::string indent) { +std::string AMF::Object3::Print(std::string indent){ std::stringstream st; st << indent; // print my type - switch (myType) { - case AMF::AMF3_UNDEFINED: - st << "Undefined"; - break; - case AMF::AMF3_NULL: - st << "Null"; - break; - case AMF::AMF3_FALSE: - st << "False"; - break; - case AMF::AMF3_TRUE: - st << "True"; - break; - case AMF::AMF3_INTEGER: - st << "Integer"; - break; - case AMF::AMF3_DOUBLE: - st << "Double"; - break; - case AMF::AMF3_STRING: - st << "String"; - break; - case AMF::AMF3_XMLDOC: - st << "XML Doc"; - break; - case AMF::AMF3_DATE: - st << "Date"; - break; - case AMF::AMF3_ARRAY: - st << "Array"; - break; - case AMF::AMF3_OBJECT: - st << "Object"; - break; - case AMF::AMF3_XML: - st << "XML"; - break; - case AMF::AMF3_BYTES: - st << "ByteArray"; - break; - case AMF::AMF3_DDV_CONTAINER: - st << "DDVTech Container"; - break; + switch (myType){ + case AMF::AMF3_UNDEFINED: st << "Undefined"; break; + case AMF::AMF3_NULL: st << "Null"; break; + case AMF::AMF3_FALSE: st << "False"; break; + case AMF::AMF3_TRUE: st << "True"; break; + case AMF::AMF3_INTEGER: st << "Integer"; break; + case AMF::AMF3_DOUBLE: st << "Double"; break; + case AMF::AMF3_STRING: st << "String"; break; + case AMF::AMF3_XMLDOC: st << "XML Doc"; break; + case AMF::AMF3_DATE: st << "Date"; break; + case AMF::AMF3_ARRAY: st << "Array"; break; + case AMF::AMF3_OBJECT: st << "Object"; break; + case AMF::AMF3_XML: st << "XML"; break; + case AMF::AMF3_BYTES: st << "ByteArray"; break; + case AMF::AMF3_DDV_CONTAINER: st << "DDVTech Container"; break; } // print my string indice, if available st << " " << myIndice << " "; // print my numeric or string contents - switch (myType) { - case AMF::AMF3_INTEGER: - st << intval; - break; - case AMF::AMF3_DOUBLE: + switch (myType){ + case AMF::AMF3_INTEGER: st << intval; break; + case AMF::AMF3_DOUBLE: st << dblval; break; + case AMF::AMF3_STRING: + case AMF::AMF3_XMLDOC: + case AMF::AMF3_XML: + case AMF::AMF3_BYTES: + if (intval > 0){ + st << "REF" << intval; + }else{ + st << strval; + } + break; + case AMF::AMF3_DATE: + if (intval > 0){ + st << "REF" << intval; + }else{ st << dblval; - break; - case AMF::AMF3_STRING: - case AMF::AMF3_XMLDOC: - case AMF::AMF3_XML: - case AMF::AMF3_BYTES: - if (intval > 0) { - st << "REF" << intval; - } else { - st << strval; - } - break; - case AMF::AMF3_DATE: - if (intval > 0) { - st << "REF" << intval; - } else { - st << dblval; - } - break; - case AMF::AMF3_ARRAY: - case AMF::AMF3_OBJECT: - if (intval > 0) { - st << "REF" << intval; - } - break; - default: - break; //we don't care about the rest, and don't want a compiler warning... + } + break; + case AMF::AMF3_ARRAY: + case AMF::AMF3_OBJECT: + if (intval > 0){st << "REF" << intval;} + break; + default: break; // we don't care about the rest, and don't want a compiler warning... } st << std::endl; // if I hold other objects, print those too, recursively. - if (contents.size() > 0) { - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++) { + if (contents.size() > 0){ + for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ st << it->Print(indent + " "); } } return st.str(); -} //print +}// print /// Packs the AMF object to a std::string for transfer over the network. -/// If the object is a container type, this function will call itself recursively and contain all contents. -/// Tip: When sending multiple AMF objects in one go, put them in a single AMF::AMF0_DDV_CONTAINER for easy transfer. -std::string AMF::Object3::Pack() { +/// If the object is a container type, this function will call itself recursively and contain all +/// contents. Tip: When sending multiple AMF objects in one go, put them in a single +/// AMF::AMF0_DDV_CONTAINER for easy transfer. +std::string AMF::Object3::Pack(){ std::string r = ""; return r; -} //pack +}// pack /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. /// This function updates i every call with the new position in the data. @@ -778,370 +712,373 @@ std::string AMF::Object3::Pack() { /// \param i Current parsing position in the raw data. /// \param name Indice name for any new object created. /// \returns A single AMF::Object3, parsed from the raw data. -AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int & len, unsigned int & i, std::string name) { +AMF::Object3 AMF::parseOne3(const unsigned char *&data, unsigned int &len, unsigned int &i, + std::string name){ std::string tmpstr; unsigned int tmpi = 0; unsigned int arrsize = 0; unsigned char tmpdbl[8]; - double * d; // hack to work around strict aliasing - switch (data[i]) { - case AMF::AMF3_UNDEFINED: - case AMF::AMF3_NULL: - case AMF::AMF3_FALSE: - case AMF::AMF3_TRUE: - ++i; - return AMF::Object3(name, (AMF::obj3type)data[i - 1]); - break; - case AMF::AMF3_INTEGER: - if (data[i + 1] < 0x80) { + double *d; // hack to work around strict aliasing + switch (data[i]){ + case AMF::AMF3_UNDEFINED: + case AMF::AMF3_NULL: + case AMF::AMF3_FALSE: + case AMF::AMF3_TRUE: + ++i; + return AMF::Object3(name, (AMF::obj3type)data[i - 1]); + break; + case AMF::AMF3_INTEGER: + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; + } + } + } + tmpi = (tmpi << 3) >> 3; // fix sign bit + return AMF::Object3(name, (int)tmpi, AMF::AMF3_INTEGER); + break; + case AMF::AMF3_DOUBLE: + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 9; // skip 8(a double)+1 forwards + d = (double *)tmpdbl; + return AMF::Object3(name, *d, AMF::AMF3_DOUBLE); + break; + case AMF::AMF3_STRING: + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; + } + } + } + tmpi = (tmpi << 3) >> 3; // fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_STRING); // reference type + } + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); // add the string data + i += (tmpi >> 1); // skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_STRING); // normal type + break; + case AMF::AMF3_XMLDOC: + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; + } + } + } + tmpi = (tmpi << 3) >> 3; // fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XMLDOC); // reference type + } + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); // add the string data + i += (tmpi >> 1); // skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XMLDOC); // normal type + break; + case AMF::AMF3_XML: + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; + } + } + } + tmpi = (tmpi << 3) >> 3; // fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XML); // reference type + } + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); // add the string data + i += (tmpi >> 1); // skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_XML); // normal type + break; + case AMF::AMF3_BYTES: + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + tmpi = (tmpi << 3) >> 3; // fix sign bit + i += 5; + } + } + } + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES); // reference type + } + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); // add the string data + i += (tmpi >> 1); // skip length+size+1 forwards + return AMF::Object3(name, tmpstr, AMF::AMF3_BYTES); // normal type + break; + case AMF::AMF3_DATE: + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; + } + } + } + tmpi = (tmpi << 3) >> 3; // fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_DATE); // reference type + } + tmpdbl[7] = data[i]; + tmpdbl[6] = data[i + 1]; + tmpdbl[5] = data[i + 2]; + tmpdbl[4] = data[i + 3]; + tmpdbl[3] = data[i + 4]; + tmpdbl[2] = data[i + 5]; + tmpdbl[1] = data[i + 6]; + tmpdbl[0] = data[i + 7]; + d = (double *)tmpdbl; + i += 8; // skip a double forwards + return AMF::Object3(name, *d, AMF::AMF3_DATE); + break; + case AMF::AMF3_ARRAY:{ + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; + } + } + } + tmpi = (tmpi << 3) >> 3; // fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_ARRAY); // reference type + } + AMF::Object3 ret(name, AMF::AMF3_ARRAY); + arrsize = tmpi >> 1; + do{ + if (data[i + 1] < 0x80){ tmpi = data[i + 1]; i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ tmpi |= data[i + 2]; i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ tmpi |= data[i + 3]; i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. tmpi |= data[i + 4]; i += 5; } } } - tmpi = (tmpi << 3) >> 3; //fix sign bit - return AMF::Object3(name, (int)tmpi, AMF::AMF3_INTEGER); - break; - case AMF::AMF3_DOUBLE: - tmpdbl[7] = data[i + 1]; - tmpdbl[6] = data[i + 2]; - tmpdbl[5] = data[i + 3]; - tmpdbl[4] = data[i + 4]; - tmpdbl[3] = data[i + 5]; - tmpdbl[2] = data[i + 6]; - tmpdbl[1] = data[i + 7]; - tmpdbl[0] = data[i + 8]; - i += 9; //skip 8(a double)+1 forwards - d = (double *)tmpdbl; - return AMF::Object3(name, *d, AMF::AMF3_DOUBLE); - break; - case AMF::AMF3_STRING: - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - i += 5; - } + tmpi = (tmpi << 3) >> 4; // fix sign bit, ignore references for now... + /// \todo Fix references? + if (tmpi > 0){ + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)tmpi); // add the string data + ret.addContent( + AMF::parseOne3(data, len, i, tmpstr)); // add content, recursively parsed, updating i + } + }while (tmpi > 0); + while (arrsize > 0){// while not done parsing array + ret.addContent( + AMF::parseOne3(data, len, i, "arrVal")); // add content, recursively parsed, updating i + --arrsize; + } + return ret; + }break; + case AMF::AMF3_OBJECT:{ + if (data[i + 1] < 0x80){ + tmpi = data[i + 1]; + i += 2; + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ + tmpi |= data[i + 2]; + i += 3; + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ + tmpi |= data[i + 3]; + i += 4; + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. + tmpi |= data[i + 4]; + i += 5; } } - tmpi = (tmpi << 3) >> 3; //fix sign bit - if ((tmpi & 1) == 0) { - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_STRING); //reference type - } - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data - i += (tmpi >> 1); //skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_STRING); //normal type - break; - case AMF::AMF3_XMLDOC: - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - i += 5; - } - } - } - tmpi = (tmpi << 3) >> 3; //fix sign bit - if ((tmpi & 1) == 0) { - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XMLDOC); //reference type - } - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data - i += (tmpi >> 1); //skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_XMLDOC); //normal type - break; - case AMF::AMF3_XML: - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - i += 5; - } - } - } - tmpi = (tmpi << 3) >> 3; //fix sign bit - if ((tmpi & 1) == 0) { - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_XML); //reference type - } - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data - i += (tmpi >> 1); //skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_XML); //normal type - break; - case AMF::AMF3_BYTES: - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - tmpi = (tmpi << 3) >> 3; //fix sign bit - i += 5; - } - } - } - if ((tmpi & 1) == 0) { - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_BYTES); //reference type - } - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i, (size_t)(tmpi >> 1)); //add the string data - i += (tmpi >> 1); //skip length+size+1 forwards - return AMF::Object3(name, tmpstr, AMF::AMF3_BYTES); //normal type - break; - case AMF::AMF3_DATE: - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - i += 5; - } - } - } - tmpi = (tmpi << 3) >> 3; //fix sign bit - if ((tmpi & 1) == 0) { - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_DATE); //reference type - } - tmpdbl[7] = data[i]; - tmpdbl[6] = data[i + 1]; - tmpdbl[5] = data[i + 2]; - tmpdbl[4] = data[i + 3]; - tmpdbl[3] = data[i + 4]; - tmpdbl[2] = data[i + 5]; - tmpdbl[1] = data[i + 6]; - tmpdbl[0] = data[i + 7]; - d = (double *)tmpdbl; - i += 8; //skip a double forwards - return AMF::Object3(name, *d, AMF::AMF3_DATE); - break; - case AMF::AMF3_ARRAY: { - if (data[i + 1] < 0x80) { + } + tmpi = (tmpi << 3) >> 3; // fix sign bit + if ((tmpi & 1) == 0){ + return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_OBJECT); // reference type + } + AMF::Object3 ret(name, AMF::AMF3_OBJECT); + bool isdynamic = false; + if ((tmpi & 2) == 0){// traits by reference, skip for now + /// \todo Implement traits by reference. Or references in general, of course... + }else{ + isdynamic = ((tmpi & 8) == 8); + arrsize = tmpi >> 4; // count of sealed members + /// \todo Read in arrsize sealed member names, then arrsize sealed members. + } + if (isdynamic){ + do{ + if (data[i + 1] < 0x80){ tmpi = data[i + 1]; i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { + }else{ + tmpi = (data[i + 1] & 0x7F) << 7; // strip the upper bit, shift 7 up. + if (data[i + 2] < 0x80){ tmpi |= data[i + 2]; i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { + }else{ + tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; // strip the upper bit, shift 7 up. + if (data[i + 3] < 0x80){ tmpi |= data[i + 3]; i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. + }else{ + tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; // strip the upper bit, shift 7 up. tmpi |= data[i + 4]; i += 5; } } } - tmpi = (tmpi << 3) >> 3; //fix sign bit - if ((tmpi & 1) == 0) { - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_ARRAY); //reference type + tmpi = (tmpi << 3) >> 4; // fix sign bit, ignore references for now... + /// \todo Fix references? + if (tmpi > 0){ + tmpstr.clear(); // clean tmpstr, just to be sure + tmpstr.append((const char *)data + i, (size_t)tmpi); // add the string data + ret.addContent( + AMF::parseOne3(data, len, i, tmpstr)); // add content, recursively parsed, updating i } - AMF::Object3 ret(name, AMF::AMF3_ARRAY); - arrsize = tmpi >> 1; - do { - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - i += 5; - } - } - } - tmpi = (tmpi << 3) >> 4; //fix sign bit, ignore references for now... - /// \todo Fix references? - if (tmpi > 0) { - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i, (size_t)tmpi); //add the string data - ret.addContent(AMF::parseOne3(data, len, i, tmpstr)); //add content, recursively parsed, updating i - } - } while (tmpi > 0); - while (arrsize > 0) { //while not done parsing array - ret.addContent(AMF::parseOne3(data, len, i, "arrVal")); //add content, recursively parsed, updating i - --arrsize; - } - return ret; - } - break; - case AMF::AMF3_OBJECT: { - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - i += 5; - } - } - } - tmpi = (tmpi << 3) >> 3; //fix sign bit - if ((tmpi & 1) == 0) { - return AMF::Object3(name, (int)((tmpi >> 1) + 1), AMF::AMF3_OBJECT); //reference type - } - AMF::Object3 ret(name, AMF::AMF3_OBJECT); - bool isdynamic = false; - if ((tmpi & 2) == 0) { //traits by reference, skip for now - /// \todo Implement traits by reference. Or references in general, of course... - } else { - isdynamic = ((tmpi & 8) == 8); - arrsize = tmpi >> 4; //count of sealed members - /// \todo Read in arrsize sealed member names, then arrsize sealed members. - } - if (isdynamic) { - do { - if (data[i + 1] < 0x80) { - tmpi = data[i + 1]; - i += 2; - } else { - tmpi = (data[i + 1] & 0x7F) << 7; //strip the upper bit, shift 7 up. - if (data[i + 2] < 0x80) { - tmpi |= data[i + 2]; - i += 3; - } else { - tmpi = (tmpi | (data[i + 2] & 0x7F)) << 7; //strip the upper bit, shift 7 up. - if (data[i + 3] < 0x80) { - tmpi |= data[i + 3]; - i += 4; - } else { - tmpi = (tmpi | (data[i + 3] & 0x7F)) << 8; //strip the upper bit, shift 7 up. - tmpi |= data[i + 4]; - i += 5; - } - } - } - tmpi = (tmpi << 3) >> 4; //fix sign bit, ignore references for now... - /// \todo Fix references? - if (tmpi > 0) { - tmpstr.clear(); //clean tmpstr, just to be sure - tmpstr.append((const char *)data + i, (size_t)tmpi); //add the string data - ret.addContent(AMF::parseOne3(data, len, i, tmpstr)); //add content, recursively parsed, updating i - } - } while (tmpi > 0); //keep reading dynamic values until empty string - } //dynamic types - return ret; - } - break; + }while (tmpi > 0); // keep reading dynamic values until empty string + }// dynamic types + return ret; + }break; } - DEBUG_MSG(DLVL_ERROR, "Error: Unimplemented AMF3 type %hhx - returning.", data[i]); + ERROR_MSG("Error: Unimplemented AMF3 type %hhx - returning.", data[i]); return AMF::Object3("error", AMF::AMF3_DDV_CONTAINER); -} //parseOne +}// parseOne /// Parses a C-string to a valid AMF::Object3. /// This function will find all AMF3 objects in the string and return /// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. -AMF::Object3 AMF::parse3(const unsigned char * data, unsigned int len) { - AMF::Object3 ret("returned", AMF::AMF3_DDV_CONTAINER); //container type +AMF::Object3 AMF::parse3(const unsigned char *data, unsigned int len){ + AMF::Object3 ret("returned", AMF::AMF3_DDV_CONTAINER); // container type unsigned int i = 0, j = 0; - while (i < len) { + while (i < len){ ret.addContent(AMF::parseOne3(data, len, i, "")); - if (i > j) { + if (i > j){ j = i; - } else { + }else{ return ret; } } return ret; -} //parse +}// parse /// Parses a std::string to a valid AMF::Object3. /// This function will find all AMF3 objects in the string and return /// them all packed in a single AMF::AMF3_DDV_CONTAINER AMF::Object3. -AMF::Object3 AMF::parse3(std::string data) { +AMF::Object3 AMF::parse3(std::string data){ return AMF::parse3((const unsigned char *)data.c_str(), data.size()); -} //parse +}// parse + diff --git a/lib/amf.h b/lib/amf.h index 4d560bdd..37483601 100644 --- a/lib/amf.h +++ b/lib/amf.h @@ -2,15 +2,15 @@ /// Holds all headers for the AMF namespace. #pragma once -#include #include #include +#include /// Holds all AMF parsing and creation related functions and classes. -namespace AMF { +namespace AMF{ /// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use. - enum obj0type { + enum obj0type{ AMF0_NUMBER = 0x00, AMF0_BOOL = 0x01, AMF0_STRING = 0x02, @@ -33,7 +33,7 @@ namespace AMF { }; /// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use. - enum obj3type { + enum obj3type{ AMF3_UNDEFINED = 0x00, AMF3_NULL = 0x01, AMF3_FALSE = 0x02, @@ -51,80 +51,87 @@ namespace AMF { }; /// Recursive class that holds AMF0 objects. - /// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type. - class Object { - public: - std::string Indice(); - obj0type GetType(); - double NumValue(); - std::string StrValue(); - const char * Str(); - int hasContent(); - void addContent(AMF::Object c); - Object * getContentP(unsigned int i); - Object getContent(unsigned int i); - Object * getContentP(std::string s); - Object getContent(std::string s); - Object(); - Object(std::string indice, double val, obj0type setType = AMF0_NUMBER); - Object(std::string indice, std::string val, obj0type setType = AMF0_STRING); - Object(std::string indice, obj0type setType = AMF0_OBJECT); - std::string Print(std::string indent = ""); - std::string Pack(); - protected: - std::string myIndice; ///< Holds this objects indice, if any. - obj0type myType; ///< Holds this objects AMF0 type. - std::string strval; ///< Holds this objects string value, if any. - double numval; ///< Holds this objects numeric value, if any. - std::vector contents; ///< Holds this objects contents, if any (for container types). + /// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH + /// container type. + class Object{ + public: + std::string Indice(); + obj0type GetType(); + double NumValue(); + std::string StrValue(); + const char *Str(); + int hasContent(); + void addContent(AMF::Object c); + Object *getContentP(unsigned int i); + Object getContent(unsigned int i); + Object *getContentP(std::string s); + Object getContent(std::string s); + Object(); + Object(std::string indice, double val, obj0type setType = AMF0_NUMBER); + Object(std::string indice, std::string val, obj0type setType = AMF0_STRING); + Object(std::string indice, const char *val, obj0type setType = AMF0_STRING); + Object(std::string indice, obj0type setType = AMF0_OBJECT); + std::string Print(std::string indent = ""); + std::string Pack(); + + protected: + std::string myIndice; ///< Holds this objects indice, if any. + obj0type myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + double numval; ///< Holds this objects numeric value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). }; - //AMFType + // AMFType /// Parses a C-string to a valid AMF::Object. - Object parse(const unsigned char * data, unsigned int len); + Object parse(const unsigned char *data, unsigned int len); /// Parses a std::string to a valid AMF::Object. Object parse(std::string data); /// Parses a single AMF0 type - used recursively by the AMF::parse() functions. - Object parseOne(const unsigned char *& data, unsigned int & len, unsigned int & i, std::string name); + Object parseOne(const unsigned char *&data, unsigned int &len, unsigned int &i, std::string name); /// Recursive class that holds AMF3 objects. - /// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH container type. - class Object3 { - public: - std::string Indice(); - obj3type GetType(); - double DblValue(); - int IntValue(); - std::string StrValue(); - const char * Str(); - int hasContent(); - void addContent(AMF::Object3 c); - Object3 * getContentP(int i); - Object3 getContent(int i); - Object3 * getContentP(std::string s); - Object3 getContent(std::string s); - Object3(); - Object3(std::string indice, int val, obj3type setType = AMF3_INTEGER); - Object3(std::string indice, double val, obj3type setType = AMF3_DOUBLE); - Object3(std::string indice, std::string val, obj3type setType = AMF3_STRING); - Object3(std::string indice, obj3type setType = AMF3_OBJECT); - std::string Print(std::string indent = ""); - std::string Pack(); - protected: - std::string myIndice; ///< Holds this objects indice, if any. - obj3type myType; ///< Holds this objects AMF0 type. - std::string strval; ///< Holds this objects string value, if any. - double dblval; ///< Holds this objects double value, if any. - int intval; ///< Holds this objects int value, if any. - std::vector contents; ///< Holds this objects contents, if any (for container types). + /// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH + /// container type. + class Object3{ + public: + std::string Indice(); + obj3type GetType(); + double DblValue(); + int IntValue(); + std::string StrValue(); + const char *Str(); + int hasContent(); + void addContent(AMF::Object3 c); + Object3 *getContentP(int i); + Object3 getContent(int i); + Object3 *getContentP(std::string s); + Object3 getContent(std::string s); + Object3(); + Object3(std::string indice, int val, obj3type setType = AMF3_INTEGER); + Object3(std::string indice, double val, obj3type setType = AMF3_DOUBLE); + Object3(std::string indice, std::string val, obj3type setType = AMF3_STRING); + Object3(std::string indice, obj3type setType = AMF3_OBJECT); + std::string Print(std::string indent = ""); + std::string Pack(); + + protected: + std::string myIndice; ///< Holds this objects indice, if any. + obj3type myType; ///< Holds this objects AMF0 type. + std::string strval; ///< Holds this objects string value, if any. + double dblval; ///< Holds this objects double value, if any. + int intval; ///< Holds this objects int value, if any. + std::vector contents; ///< Holds this objects contents, if any (for container types). }; - //AMFType + // AMFType /// Parses a C-string to a valid AMF::Object3. - Object3 parse3(const unsigned char * data, unsigned int len); + Object3 parse3(const unsigned char *data, unsigned int len); /// Parses a std::string to a valid AMF::Object3. Object3 parse3(std::string data); /// Parses a single AMF3 type - used recursively by the AMF::parse3() functions. - Object3 parseOne3(const unsigned char *& data, unsigned int & len, unsigned int & i, std::string name); + Object3 parseOne3(const unsigned char *&data, unsigned int &len, unsigned int &i, + std::string name); + +}// namespace AMF -} //AMF namespace diff --git a/lib/bitfields.cpp b/lib/bitfields.cpp index 10bc7e3b..2586878d 100644 --- a/lib/bitfields.cpp +++ b/lib/bitfields.cpp @@ -1,33 +1,32 @@ #include "bitfields.h" #include -/// Takes a pointer, offset bitcount and data bitcount, returning the unsigned int read from the givens. -/// offsetBits may be > 7, in which case offsetBits / 8 is added to the pointer automatically. -/// This function assumes Most Significant Bits first. -/// If dataBits > 64, only the last 64 bits are returned. -unsigned long long Bit::getMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits){ - //If the offset is a whole byte or more, add the whole bytes to the pointer instead. +/// Takes a pointer, offset bitcount and data bitcount, returning the unsigned int read from the +/// givens. offsetBits may be > 7, in which case offsetBits / 8 is added to the pointer +/// automatically. This function assumes Most Significant Bits first. If dataBits > 64, only the +/// last 64 bits are returned. +unsigned long long Bit::getMSB(char *pointer, unsigned int offsetBits, unsigned int dataBits){ + // If the offset is a whole byte or more, add the whole bytes to the pointer instead. pointer += offsetBits >> 3; - //The offset is now guaranteed less than a whole byte. + // The offset is now guaranteed less than a whole byte. offsetBits &= 0x07; unsigned long long retVal = 0; - //Now we parse the remaining bytes + // Now we parse the remaining bytes while (dataBits){ - //Calculate how many bits we're reading from this byte - //We assume all except for the offset + // Calculate how many bits we're reading from this byte + // We assume all except for the offset unsigned int curBits = 8 - offsetBits; - //If that is too much, we use the remainder instead - if (curBits > dataBits){ - curBits = dataBits; - } - //First, shift the current return value by the amount of bits we're adding + // If that is too much, we use the remainder instead + if (curBits > dataBits){curBits = dataBits;} + // First, shift the current return value by the amount of bits we're adding retVal <<= curBits; - //Next, add those bits from the current pointer position at the correct offset, increasing the pointer by one + // Next, add those bits from the current pointer position at the correct offset, increasing the + // pointer by one retVal |= ((int)(*(pointer++)) << offsetBits) >> (8 - curBits); - //Finally, set the offset to zero and remove curBits from dataBits. + // Finally, set the offset to zero and remove curBits from dataBits. offsetBits = 0; dataBits -= curBits; - }//Loop until we run out of dataBits, then return the result + }// Loop until we run out of dataBits, then return the result return retVal; } @@ -36,45 +35,42 @@ unsigned long long Bit::getMSB(char * pointer, unsigned int offsetBits, unsigned /// This function assumes Most Significant Bits first. /// WARNING: UNFINISHED. DO NOT USE. /// \todo Finish writing this - untested atm. -void Bit::setMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits, unsigned long long value){ - //Set the pointer to the last byte we need to be setting +void Bit::setMSB(char *pointer, unsigned int offsetBits, unsigned int dataBits, + unsigned long long value){ + // Set the pointer to the last byte we need to be setting pointer += (offsetBits + dataBits) >> 3; - //The offset is now guaranteed less than a whole byte. + // The offset is now guaranteed less than a whole byte. offsetBits = (offsetBits + dataBits) & 0x07; unsigned long long retVal = 0; - //Now we set the remaining bytes + // Now we set the remaining bytes while (dataBits){ - //Calculate how many bits we're setting in this byte - //We assume all that will fit in the current byte + // Calculate how many bits we're setting in this byte + // We assume all that will fit in the current byte unsigned int curBits = offsetBits; - //If that is too much, we use the remainder instead - if (curBits > dataBits){ - curBits = dataBits; - } - //Set the current pointer position at the correct offset, increasing the pointer by one + // If that is too much, we use the remainder instead + if (curBits > dataBits){curBits = dataBits;} + // Set the current pointer position at the correct offset, increasing the pointer by one retVal |= ((int)(*(pointer++)) << offsetBits) >> (8 - curBits); *pointer = (((*pointer) << offsetBits) >> offsetBits) | ((value & 0xFF) << (8 - offsetBits)); --pointer; - //Finally, shift the current value by the amount of bits we're adding + // Finally, shift the current value by the amount of bits we're adding value >>= offsetBits; //... and set the offset to eight and remove curBits from dataBits. offsetBits = 8; dataBits -= curBits; - }//Loop until we run out of dataBits, then return the result + }// Loop until we run out of dataBits, then return the result } /// Parses a string reference to a boolean. -/// Returns true if the string, with whitespace removed and converted to lowercase, prefix-matches any of: "1", "yes", "true", "cont". -/// Returns false otherwise. -bool Util::stringToBool(std::string & str){ +/// Returns true if the string, with whitespace removed and converted to lowercase, prefix-matches +/// any of: "1", "yes", "true", "cont". Returns false otherwise. +bool Util::stringToBool(std::string &str){ std::string tmp; tmp.reserve(4); for (unsigned int i = 0; i < str.size() && tmp.size() < 4; ++i){ - if (!::isspace(str[i])){ - tmp.push_back((char)tolower(str[i])); - } + if (!::isspace(str[i])){tmp.push_back((char)tolower(str[i]));} } - return (strncmp(tmp.c_str(), "1", 1) == 0 || strncmp(tmp.c_str(), "yes", 3) == 0 || strncmp(tmp.c_str(), "true", 4) == 0 || strncmp(tmp.c_str(), "cont", 4) == 0); + return (strncmp(tmp.c_str(), "1", 1) == 0 || strncmp(tmp.c_str(), "yes", 3) == 0 || + strncmp(tmp.c_str(), "true", 4) == 0 || strncmp(tmp.c_str(), "cont", 4) == 0); } - diff --git a/lib/bitfields.h b/lib/bitfields.h index f8d5c0af..e4e68ed6 100644 --- a/lib/bitfields.h +++ b/lib/bitfields.h @@ -1,39 +1,38 @@ #pragma once +#include "defines.h" #include -#include namespace Util{ - bool stringToBool(std::string & str); + bool stringToBool(std::string &str); } namespace Bit{ - //bitfield getters - unsigned long long getMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits); - unsigned long long getByName(char * pointer); - //bitfield setters - void setMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits, unsigned long long value); - void setByName(char * pointer); + // bitfield getters + unsigned long long getMSB(char *pointer, unsigned int offsetBits, unsigned int dataBits); + unsigned long long getByName(char *pointer); + // bitfield setters + void setMSB(char *pointer, unsigned int offsetBits, unsigned int dataBits, + unsigned long long value); + void setByName(char *pointer); - //Host to binary/binary to host functions - similar to kernel ntoh/hton functions. + // Host to binary/binary to host functions - similar to kernel ntoh/hton functions. /// Retrieves a short in network order from the pointer p. - inline unsigned short btohs(const char * p) { - return ((unsigned short)p[0] << 8) | p[1]; - } + inline uint16_t btohs(const char *p){return ((uint16_t)p[0] << 8) | p[1];} /// Stores a short value of val in network order to the pointer p. - inline void htobs(char * p, unsigned short val) { + inline void htobs(char *p, uint16_t val){ p[0] = (val >> 8) & 0xFF; p[1] = val & 0xFF; } /// Retrieves a long in network order from the pointer p. - inline unsigned long btohl(const char * p) { - return ((unsigned long)p[0] << 24) | ((unsigned long)p[1] << 16) | ((unsigned long)p[2] << 8) | p[3]; + inline uint32_t btohl(const char *p){ + return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3]; } /// Stores a long value of val in network order to the pointer p. - inline void htobl(char * p, unsigned long val) { + inline void htobl(char *p, uint32_t val){ p[0] = (val >> 24) & 0xFF; p[1] = (val >> 16) & 0xFF; p[2] = (val >> 8) & 0xFF; @@ -41,24 +40,25 @@ namespace Bit{ } /// Retrieves a long in network order from the pointer p. - inline unsigned long btoh24(const char * p) { + inline unsigned long btoh24(const char *p){ return ((unsigned long)p[0] << 16) | ((unsigned long)p[1] << 8) | p[2]; } /// Stores a long value of val in network order to the pointer p. - inline void htob24(char * p, unsigned long val) { + inline void htob24(char *p, unsigned long val){ p[0] = (val >> 16) & 0xFF; p[1] = (val >> 8) & 0xFF; p[2] = val & 0xFF; } /// Retrieves a 40-bit uint in network order from the pointer p. - inline uint64_t btoh40(const char * p) { - return ((uint64_t)p[0] << 32) | ((uint64_t)p[1] << 24) | ((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 8) | p[4]; + inline uint64_t btoh40(const char *p){ + return ((uint64_t)p[0] << 32) | ((uint64_t)p[1] << 24) | ((uint64_t)p[2] << 16) | + ((uint64_t)p[3] << 8) | p[4]; } /// Stores a 40-bit uint value of val in network order to the pointer p. - inline void htob40(char * p, uint64_t val) { + inline void htob40(char *p, uint64_t val){ p[0] = (val >> 32) & 0xFF; p[1] = (val >> 24) & 0xFF; p[2] = (val >> 16) & 0xFF; @@ -67,12 +67,13 @@ namespace Bit{ } /// Retrieves a 48-bit uint in network order from the pointer p. - inline uint64_t btoh48(const char * p) { - return ((uint64_t)p[0] << 40) | ((uint64_t)p[1] << 32) | ((uint64_t)p[2] << 24) | ((uint64_t)p[3] << 16) | ((uint64_t)p[4] << 8) | p[5]; + inline uint64_t btoh48(const char *p){ + return ((uint64_t)p[0] << 40) | ((uint64_t)p[1] << 32) | ((uint64_t)p[2] << 24) | + ((uint64_t)p[3] << 16) | ((uint64_t)p[4] << 8) | p[5]; } /// Stores a 48-bit uint value of val in network order to the pointer p. - inline void htob48(char * p, uint64_t val) { + inline void htob48(char *p, uint64_t val){ p[0] = (val >> 40) & 0xFF; p[1] = (val >> 32) & 0xFF; p[2] = (val >> 24) & 0xFF; @@ -82,12 +83,13 @@ namespace Bit{ } /// Retrieves a 56-bit uint in network order from the pointer p. - inline uint64_t btoh56(const char * p) { - return ((uint64_t)p[0] << 48) | ((uint64_t)p[1] << 40) | ((uint64_t)p[2] << 32) | ((uint64_t)p[3] << 24) | ((uint64_t)p[4] << 16) | ((uint64_t)p[5] << 8) | p[6]; + inline uint64_t btoh56(const char *p){ + return ((uint64_t)p[0] << 48) | ((uint64_t)p[1] << 40) | ((uint64_t)p[2] << 32) | + ((uint64_t)p[3] << 24) | ((uint64_t)p[4] << 16) | ((uint64_t)p[5] << 8) | p[6]; } /// Stores a 56-bit uint value of val in network order to the pointer p. - inline void htob56(char * p, uint64_t val) { + inline void htob56(char *p, uint64_t val){ p[0] = (val >> 48) & 0xFF; p[1] = (val >> 40) & 0xFF; p[2] = (val >> 32) & 0xFF; @@ -98,12 +100,14 @@ namespace Bit{ } /// Retrieves a long long in network order from the pointer p. - inline unsigned long long btohll(const char * p) { - return ((unsigned long long)p[0] << 56) | ((unsigned long long)p[1] << 48) | ((unsigned long long)p[2] << 40) | ((unsigned long long)p[3] << 32) | ((unsigned long)p[4] << 24) | ((unsigned long)p[5] << 16) | ((unsigned long)p[6] << 8) | p[7]; + inline uint64_t btohll(const char *p){ + return ((uint64_t)p[0] << 56) | ((uint64_t)p[1] << 48) | ((uint64_t)p[2] << 40) | + ((uint64_t)p[3] << 32) | ((uint64_t)p[4] << 24) | ((uint64_t)p[5] << 16) | + ((uint64_t)p[6] << 8) | p[7]; } /// Stores a long value of val in network order to the pointer p. - inline void htobll(char * p, unsigned long long val) { + inline void htobll(char *p, unsigned long long val){ p[0] = (val >> 56) & 0xFF; p[1] = (val >> 48) & 0xFF; p[2] = (val >> 40) & 0xFF; @@ -114,42 +118,37 @@ namespace Bit{ p[7] = val & 0xFF; } - inline float btohf(const char * p){ + inline float btohf(const char *p){ uint32_t tmp = btohl(p); - return *reinterpret_cast(&tmp); + return *reinterpret_cast(&tmp); } - inline void htobf(char * p, float val){ - htobl(p, *reinterpret_cast(&val)); - } + inline void htobf(char *p, float val){htobl(p, *reinterpret_cast(&val));} - inline double btohd(const char * p){ + inline double btohd(const char *p){ uint64_t tmp = btohll(p); - return *reinterpret_cast(&tmp); + return *reinterpret_cast(&tmp); } - inline void htobd(char * p, double val){ - htobll(p, *reinterpret_cast(&val)); - } + inline void htobd(char *p, double val){htobll(p, *reinterpret_cast(&val));} /// Retrieves a short in little endian from the pointer p. - inline unsigned short btohs_le(const char * p) { - return ((unsigned short)p[1] << 8) | p[0]; - } + inline uint16_t btohs_le(const char *p){return ((uint16_t)p[1] << 8) | p[0];} /// Stores a short value of val in little endian to the pointer p. - inline void htobs_le(char * p, unsigned short val) { + inline void htobs_le(char *p, unsigned short val){ p[1] = (val >> 8) & 0xFF; p[0] = val & 0xFF; } /// Retrieves a long in network order from the pointer p. - inline unsigned long btohl_le(const char * p) { - return ((unsigned long)p[3] << 24) | ((unsigned long)p[2] << 16) | ((unsigned long)p[1] << 8) | p[0]; + inline unsigned long btohl_le(const char *p){ + return ((unsigned long)p[3] << 24) | ((unsigned long)p[2] << 16) | ((unsigned long)p[1] << 8) | + p[0]; } /// Stores a long value of val in little endian to the pointer p. - inline void htobl_le(char * p, unsigned long val) { + inline void htobl_le(char *p, unsigned long val){ p[3] = (val >> 24) & 0xFF; p[2] = (val >> 16) & 0xFF; p[1] = (val >> 8) & 0xFF; @@ -157,24 +156,27 @@ namespace Bit{ } /// Retrieves a long in little endian from the pointer p. - inline unsigned long btoh24_le(const char * p) { + inline unsigned long btoh24_le(const char *p){ return ((unsigned long)p[2] << 16) | ((unsigned long)p[1] << 8) | p[0]; } /// Stores a long value of val in network order to the pointer p. - inline void htob24_le(char * p, unsigned long val) { + inline void htob24_le(char *p, unsigned long val){ p[2] = (val >> 16) & 0xFF; p[1] = (val >> 8) & 0xFF; p[0] = val & 0xFF; } /// Retrieves a long long in little endian from the pointer p. - inline unsigned long long btohll_le(const char * p) { - return ((unsigned long long)p[7] << 56) | ((unsigned long long)p[6] << 48) | ((unsigned long long)p[5] << 40) | ((unsigned long long)p[4] << 32) | ((unsigned long)p[3] << 24) | ((unsigned long)p[2] << 16) | ((unsigned long)p[1] << 8) | p[0]; + inline unsigned long long btohll_le(const char *p){ + return ((unsigned long long)p[7] << 56) | ((unsigned long long)p[6] << 48) | + ((unsigned long long)p[5] << 40) | ((unsigned long long)p[4] << 32) | + ((unsigned long)p[3] << 24) | ((unsigned long)p[2] << 16) | ((unsigned long)p[1] << 8) | + p[0]; } /// Stores a long value of val in little endian to the pointer p. - inline void htobll_le(char * p, unsigned long long val) { + inline void htobll_le(char *p, unsigned long long val){ p[7] = (val >> 56) & 0xFF; p[6] = (val >> 48) & 0xFF; p[5] = (val >> 40) & 0xFF; @@ -185,5 +187,5 @@ namespace Bit{ p[0] = val & 0xFF; } -} +}// namespace Bit diff --git a/lib/bitstream.cpp b/lib/bitstream.cpp index b7617814..faa82b13 100644 --- a/lib/bitstream.cpp +++ b/lib/bitstream.cpp @@ -3,52 +3,55 @@ #include #include -namespace Utils { - bitstream::bitstream() { +namespace Utils{ + bitstream::bitstream(){ data = NULL; offset = 0; dataSize = 0; bufferSize = 0; } - bool bitstream::checkBufferSize(unsigned int size) { - if (size > bufferSize) { - void * temp = realloc(data, size); - if (temp) { - data = (char *) temp; - bufferSize = size; - return true; - } else { - return false; - } - } else { - return true; - } + bitstream::~bitstream(){ + if (!data){return;} + free(data); + bufferSize = 0; + dataSize = 0; + offset = 0; } - void bitstream::append(const char * input, size_t bytes) { - if (checkBufferSize(dataSize + bytes)) { + bool bitstream::checkBufferSize(unsigned int size){ + if (size <= bufferSize){return true;} + + void *temp = realloc(data, size); + if (!temp){return false;} + + data = (char *)temp; + bufferSize = size; + return true; + } + + void bitstream::append(const char *input, size_t bytes){ + if (checkBufferSize(dataSize + bytes)){ memcpy(data + dataSize, input, bytes); dataSize += bytes; } } - void bitstream::append(const std::string & input) { - append((char *)input.c_str(), input.size()); - } + void bitstream::append(const std::string &input){append((char *)input.c_str(), input.size());} - bool bitstream::peekOffset(size_t peekOffset) { + bool bitstream::peekOffset(size_t peekOffset){ peekOffset += offset; return ((data[peekOffset >> 3]) >> (7 - (peekOffset & 7))) & 1; } - long long unsigned int bitstream::peek(size_t count) { - if (count > 64) { + long long unsigned int bitstream::peek(size_t count){ + if (count > 64){ DEBUG_MSG(DLVL_WARN, "Can not read %d bits into a long long unsigned int!", (int)count); - //return 0; + // return 0; } - if (count > size()) { - DEBUG_MSG(DLVL_ERROR, "Not enough bits left in stream. Left: %d requested: %d", (int)size(), (int)count); + if (count > size()){ + DEBUG_MSG(DLVL_ERROR, "Not enough bits left in stream. Left: %d requested: %d", (int)size(), + (int)count); return 0; } long long unsigned int retval = 0; @@ -56,21 +59,21 @@ namespace Utils { size_t readSize; size_t readOff; char readBuff; - while (curPlace < count) { + while (curPlace < count){ readBuff = data[(int)((offset + curPlace) / 8)]; readSize = 8; - readOff = (offset + curPlace) % 8; //the reading offset within the byte - if (readOff != 0) { - //if we start our read not on the start of a byte - //curplace and retval should both be 0 - //this should be the first read that aligns reading to bytes, if we read over the end of read byte - //we cut the MSb off of the buffer by bit mask - readSize -= readOff;//defining starting bit - readBuff = readBuff & ((1 << readSize) - 1);//bitmasking + readOff = (offset + curPlace) % 8; // the reading offset within the byte + if (readOff != 0){ + // if we start our read not on the start of a byte + // curplace and retval should both be 0 + // this should be the first read that aligns reading to bytes, if we read over the end of + // read byte we cut the MSb off of the buffer by bit mask + readSize -= readOff; // defining starting bit + readBuff = readBuff & ((1 << readSize) - 1); // bitmasking } - //up until here we assume we read to the end of the byte - if (count - curPlace < readSize) { //if we do not read to the end of the byte - //we cut off the LSb off of the read buffer by bitshift + // up until here we assume we read to the end of the byte + if (count - curPlace < readSize){// if we do not read to the end of the byte + // we cut off the LSb off of the read buffer by bitshift readSize = count - curPlace; readBuff = readBuff >> (8 - readSize - readOff); } @@ -80,99 +83,170 @@ namespace Utils { return retval; } - long long unsigned int bitstream::get(size_t count) { - if (count <= size()) { + long long unsigned int bitstream::get(size_t count){ + if (count <= size()){ long long unsigned int retVal; retVal = peek(count); skip(count); return retVal; - } else { + }else{ return 0; } } - void bitstream::skip(size_t count) { - if (count <= size()) { + void bitstream::skip(size_t count){ + if (count <= size()){ offset += count; - } else { + }else{ offset = dataSize * 8; } - } - long long unsigned int bitstream::size() { - return (dataSize * 8) - offset; - } + long long unsigned int bitstream::size(){return (dataSize * 8) - offset;} - void bitstream::clear() { + void bitstream::clear(){ dataSize = 0; offset = 0; } - void bitstream::flush() { + void bitstream::flush(){ memmove(data, data + (offset / 8), dataSize - (offset / 8)); dataSize -= offset / 8; offset %= 8; } - long long unsigned int bitstream::golombPeeker() { - for (size_t i = 0; i < 64 && i < size(); i++) { - if (peekOffset(i)) { - return peek((i * 2) + 1); - } + long long unsigned int bitstream::golombPeeker(){ + for (size_t i = 0; i < 64 && i < size(); i++){ + if (peekOffset(i)){return peek((i * 2) + 1);} } return 0; } - long long unsigned int bitstream::golombGetter() { - for (size_t i = 0; i < 64 && i < size(); i++) { - if (peekOffset(i)) { - return get((i * 2) + 1); - } + long long unsigned int bitstream::golombGetter(){ + for (size_t i = 0; i < 64 && i < size(); i++){ + if (peekOffset(i)){return get((i * 2) + 1);} } return 0; } - long long int bitstream::getExpGolomb() { + long long int bitstream::getExpGolomb(){ long long unsigned int temp = golombGetter(); - return (temp >> 1) * (1 - ((temp & 1) << 1)); //Is actually return (temp / 2) * (1 - (temp & 1) * 2); + return (temp >> 1) * + (1 - ((temp & 1) << 1)); // Is actually return (temp / 2) * (1 - (temp & 1) * 2); } - long long unsigned int bitstream::getUExpGolomb() { - return golombGetter() - 1; - } + long long unsigned int bitstream::getUExpGolomb(){return golombGetter() - 1;} - long long int bitstream::peekExpGolomb() { + long long int bitstream::peekExpGolomb(){ long long unsigned int temp = golombPeeker(); - return (temp >> 1) * (1 - ((temp & 1) << 1)); //Is actually return (temp / 2) * (1 - (temp & 1) * 2); + return (temp >> 1) * + (1 - ((temp & 1) << 1)); // Is actually return (temp / 2) * (1 - (temp & 1) * 2); } - long long unsigned int bitstream::peekUExpGolomb() { - return golombPeeker() - 1; + long long unsigned int bitstream::peekUExpGolomb(){return golombPeeker() - 1;} + + bitWriter::bitWriter(){ + dataBuffer = NULL; + bufferSize = 0; + reallocate(0); + dataSize = 0; } -//Note: other bitstream here - bitstreamLSBF::bitstreamLSBF() { + bitWriter::~bitWriter(){ + if (dataBuffer != NULL){free(dataBuffer);} + } + + void bitWriter::reallocate(size_t newSize){ + size_t sizeBefore = bufferSize / 8; + char *tmp; + if (dataBuffer != NULL){ + tmp = (char *)realloc(dataBuffer, (newSize / 8) + 1); + }else{ + tmp = (char *)malloc((newSize / 8) + 1); + } + if (tmp){ + dataBuffer = tmp; + bufferSize = ((newSize / 8) + 1) * 8; + memset(dataBuffer + sizeBefore, 0x00, (bufferSize / 8) - sizeBefore); + }else{ + FAIL_MSG("Could not reallocate!!"); + } + } + + size_t bitWriter::size(){return dataSize;} + + void bitWriter::append(uint64_t value, size_t bitLength){ + if (dataSize + bitLength > bufferSize){reallocate(dataSize + bitLength);} + + int64_t fullShift = (bitLength / 8) * 8; + uint64_t firstMask = ((0x01ull << (bitLength % 8)) - 1) << fullShift; + + appendData(((value & firstMask) >> fullShift), bitLength - fullShift); + while (fullShift > 0){ + fullShift -= 8; + uint64_t mask = (0xFFull) << fullShift; + appendData((value & mask) >> fullShift, 8); + } + } + + void bitWriter::appendData(uint8_t data, size_t len){ + size_t byteOffset = dataSize / 8; + size_t bitOffset = dataSize % 8; + if (len <= 8 - bitOffset){ + dataBuffer[byteOffset] |= (data << (8 - bitOffset - len)); + dataSize += len; + }else{ + size_t shift = (len - (8 - bitOffset)); + dataBuffer[byteOffset] |= (data >> shift); + dataSize += (len - shift); + appendData(data, shift); + } + } + + size_t bitWriter::UExpGolombEncodedSize(uint64_t value){ + value++; + size_t res = 1; + size_t maxVal = 1; + while (value > maxVal){ + maxVal = (maxVal << 1 | 0x01); + res += 1; + } + return 2 * res - 1; + } + + void bitWriter::appendExpGolomb(int64_t value){ + if (value < 0){ + value = value * -2; + }else if (value > 0){ + value = (value * 2) - 1; + } + appendUExpGolomb(value); + } + + void bitWriter::appendUExpGolomb(uint64_t value){ + append(value + 1, UExpGolombEncodedSize(value)); + } + + // Note: other bitstream here + bitstreamLSBF::bitstreamLSBF(){ readBufferOffset = 0; readBuffer = 0; } - void bitstreamLSBF::append(char * input, size_t bytes) { + void bitstreamLSBF::append(char *input, size_t bytes){ data.append(input, bytes); fixData(); } - void bitstreamLSBF::append(std::string & input) { + void bitstreamLSBF::append(std::string &input){ data += input; fixData(); } - long long unsigned int bitstreamLSBF::size() { - return data.size() * 8 + readBufferOffset; - } + long long unsigned int bitstreamLSBF::size(){return data.size() * 8 + readBufferOffset;} - long long unsigned int bitstreamLSBF::get(size_t count) { - if (count <= 32 && count <= readBufferOffset) { + long long unsigned int bitstreamLSBF::get(size_t count){ + if (count <= 32 && count <= readBufferOffset){ long long unsigned int retval = readBuffer & (((long long unsigned int)1 << count) - 1); readBuffer = readBuffer >> count; readBufferOffset -= count; @@ -182,34 +256,33 @@ namespace Utils { return 42; } - void bitstreamLSBF::skip(size_t count) { - if (count <= 32 && count <= readBufferOffset) { + void bitstreamLSBF::skip(size_t count){ + if (count <= 32 && count <= readBufferOffset){ readBuffer = readBuffer >> count; readBufferOffset -= count; fixData(); } } - long long unsigned int bitstreamLSBF::peek(size_t count) { - if (count <= 32 && count <= readBufferOffset) { - return readBuffer & ((1 << count) - 1); - } + long long unsigned int bitstreamLSBF::peek(size_t count){ + if (count <= 32 && count <= readBufferOffset){return readBuffer & ((1 << count) - 1);} return 0; } - void bitstreamLSBF::clear() { + void bitstreamLSBF::clear(){ data = ""; readBufferOffset = 0; readBuffer = 0; } - void bitstreamLSBF::fixData() { - unsigned int pos=0; - while (readBufferOffset <= 32 && data.size() != 0) { + void bitstreamLSBF::fixData(){ + unsigned int pos = 0; + while (readBufferOffset <= 32 && data.size() != 0){ readBuffer |= (((long long unsigned int)data[pos]) << readBufferOffset); pos++; readBufferOffset += 8; } data.erase(0, pos); } -} +}// namespace Utils + diff --git a/lib/bitstream.h b/lib/bitstream.h index bd623480..e16b580f 100644 --- a/lib/bitstream.h +++ b/lib/bitstream.h @@ -1,61 +1,96 @@ #pragma once -#include +#include "defines.h" +#include -namespace Utils { - class bitstream { - public: - bitstream(); - bitstream & operator<< (std::string input) { - append(input); - return *this; - }; - bitstream & operator<< (char input) { - append(std::string(input, 1)); - return *this; - }; - void append(const char * input, size_t bytes); - void append(const std::string & input); - long long unsigned int size(); - void skip(size_t count); - long long unsigned int get(size_t count); - long long unsigned int peek(size_t count); - bool peekOffset(size_t peekOffset); - void flush(); - void clear(); - long long int getExpGolomb(); - long long unsigned int getUExpGolomb(); - long long int peekExpGolomb(); - long long unsigned int peekUExpGolomb(); - private: - bool checkBufferSize(unsigned int size); - long long unsigned int golombGetter(); - long long unsigned int golombPeeker(); - char * data; - size_t offset; - size_t dataSize; - size_t bufferSize; +namespace Utils{ + class bitstream{ + public: + bitstream(); + bitstream &operator<<(std::string input){ + append(input); + return *this; + } + bitstream &operator<<(char input){ + append(std::string(input, 1)); + return *this; + } + ~bitstream(); + void append(const char *input, size_t bytes); + void append(const std::string &input); + long long unsigned int size(); + void skip(size_t count); + long long unsigned int get(size_t count); + long long unsigned int peek(size_t count); + bool peekOffset(size_t peekOffset); + void flush(); + void clear(); + long long int getExpGolomb(); + long long unsigned int getUExpGolomb(); + long long int peekExpGolomb(); + long long unsigned int peekUExpGolomb(); + + static size_t bitSizeUExpGolomb(size_t value){ + size_t i = 1; + size_t power = 2; + while (power - 2 < value){ + i += 2; + power *= 2; + } + return i; + } + + private: + bool checkBufferSize(unsigned int size); + long long unsigned int golombGetter(); + long long unsigned int golombPeeker(); + char *data; + size_t offset; + size_t dataSize; + size_t bufferSize; }; - class bitstreamLSBF { - public: - bitstreamLSBF(); - bitstreamLSBF & operator<< (std::string input) { - append(input); - return *this; - }; - void append(char * input, size_t bytes); - void append(std::string & input); - long long unsigned int size(); - void skip(size_t count); - long long unsigned int get(size_t count); - long long unsigned int peek(size_t count); - void clear(); - std::string data; - private: - long long unsigned int readBuffer; - unsigned int readBufferOffset; - void fixData(); + class bitWriter{ + public: + bitWriter(); + ~bitWriter(); + size_t size(); + void append(uint64_t value, size_t bitLength); + void appendExpGolomb(int64_t value); + void appendUExpGolomb(uint64_t value); + static size_t UExpGolombEncodedSize(uint64_t value); + std::string str(){return std::string(dataBuffer, (dataSize / 8) + (dataSize % 8 ? 1 : 0));} + + protected: + void reallocate(size_t newSize); + void appendData(uint8_t data, size_t len); + + char *dataBuffer; + // NOTE: ALL SIZES IN BITS! + size_t bufferSize; + + size_t dataSize; }; -} + class bitstreamLSBF{ + public: + bitstreamLSBF(); + bitstreamLSBF &operator<<(std::string input){ + append(input); + return *this; + } + void append(char *input, size_t bytes); + void append(std::string &input); + long long unsigned int size(); + void skip(size_t count); + long long unsigned int get(size_t count); + long long unsigned int peek(size_t count); + void clear(); + std::string data; + + private: + long long unsigned int readBuffer; + unsigned int readBufferOffset; + void fixData(); + }; +}// namespace Utils diff --git a/lib/config.cpp b/lib/config.cpp index e5889e19..ff18b6fc 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -3,11 +3,11 @@ #include "config.h" #include "defines.h" +#include "stream.h" #include "timing.h" #include "tinythread.h" -#include "stream.h" -#include #include +#include #ifdef __CYGWIN__ #include @@ -21,35 +21,35 @@ #if defined(__APPLE__) #include #endif +#include "procs.h" +#include //for getMyExec #include +#include +#include +#include #include +#include #include +#include #include #include -#include -#include -#include -#include -#include -#include //for getMyExec -#include "procs.h" bool Util::Config::is_active = false; -static Socket::Server * serv_sock_pointer = 0; -unsigned int Util::Config::printDebugLevel = DEBUG;// +static Socket::Server *serv_sock_pointer = 0; +uint32_t Util::Config::printDebugLevel = DEBUG; // std::string Util::Config::streamName; -Util::Config::Config() { - //global options here +Util::Config::Config(){ + // global options here vals["debug"]["long"] = "debug"; vals["debug"]["short"] = "g"; vals["debug"]["arg"] = "integer"; vals["debug"]["help"] = "The debug level at which messages need to be printed."; - vals["debug"]["value"].append((long long)DEBUG); + vals["debug"]["value"].append((int64_t)DEBUG); } /// Creates a new configuration manager. -Util::Config::Config(std::string cmd) { +Util::Config::Config(std::string cmd){ vals.null(); long_count = 2; vals["cmd"]["value"].append(cmd); @@ -63,173 +63,153 @@ Util::Config::Config(std::string cmd) { vals["debug"]["short"] = "g"; vals["debug"]["arg"] = "integer"; vals["debug"]["help"] = "The debug level at which messages need to be printed."; - vals["debug"]["value"].append((long long)DEBUG); + vals["debug"]["value"].append((int64_t)DEBUG); } /// Adds an option to the configuration parser. -/// The option needs an unique name (doubles will overwrite the previous) and can contain the following in the option itself: +/// The option needs an unique name (doubles will overwrite the previous) and can contain the +/// following in the option itself: ///\code -/// { +///{ /// "short":"o", //The short option letter /// "long":"onName", //The long option /// "arg":"integer", //The type of argument, if required. -/// "value":[], //The default value(s) for this option if it is not given on the commandline. -/// "arg_num":1, //The count this value has on the commandline, after all the options have been processed. -/// "help":"Blahblahblah" //The helptext for this option. -/// } +/// "value":[], //The default value(s) for this option if it is not given on the +/// commandline. "arg_num":1, //The count this value has on the commandline, after all +/// the options have been processed. "help":"Blahblahblah" //The helptext for this option. +///} ///\endcode -void Util::Config::addOption(std::string optname, JSON::Value option) { +void Util::Config::addOption(std::string optname, JSON::Value option){ vals[optname] = option; - if (!vals[optname].isMember("value") && vals[optname].isMember("default")) { + if (!vals[optname].isMember("value") && vals[optname].isMember("default")){ vals[optname]["value"].append(vals[optname]["default"]); vals[optname].removeMember("default"); } long_count = 0; - jsonForEach(vals, it) { - if (it->isMember("long")) { - long_count++; - } + jsonForEach(vals, it){ + if (it->isMember("long")){long_count++;} } } /// Prints a usage message to the given output. -void Util::Config::printHelp(std::ostream & output) { +void Util::Config::printHelp(std::ostream &output){ unsigned int longest = 0; std::map args; - jsonForEach(vals, it) { + jsonForEach(vals, it){ unsigned int current = 0; - if (it->isMember("long")) { - current += (*it)["long"].asString().size() + 4; - } - if (it->isMember("short")) { - current += (*it)["short"].asString().size() + 3; - } - if (current > longest) { - longest = current; - } + if (it->isMember("long")){current += (*it)["long"].asString().size() + 4;} + if (it->isMember("short")){current += (*it)["short"].asString().size() + 3;} + if (current > longest){longest = current;} current = 0; - if (current > longest) { - longest = current; - } - if (it->isMember("arg_num")) { + if (current > longest){longest = current;} + if (it->isMember("arg_num")){ current = it.key().size() + 3; - if (current > longest) { - longest = current; - } + if (current > longest){longest = current;} args[(*it)["arg_num"].asInt()] = it.key(); } } output << "Usage: " << getString("cmd") << " [options]"; - for (std::map::iterator i = args.begin(); i != args.end(); i++) { - if (vals[i->second].isMember("value") && vals[i->second]["value"].size()) { + for (std::map::iterator i = args.begin(); i != args.end(); i++){ + if (vals[i->second].isMember("value") && vals[i->second]["value"].size()){ output << " [" << i->second << "]"; - } else { + }else{ output << " " << i->second; } } output << std::endl << std::endl; - jsonForEach(vals, it) { + jsonForEach(vals, it){ std::string f; - if (it->isMember("long") || it->isMember("short")) { - if (it->isMember("long") && it->isMember("short")) { + if (it->isMember("long") || it->isMember("short")){ + if (it->isMember("long") && it->isMember("short")){ f = "--" + (*it)["long"].asString() + ", -" + (*it)["short"].asString(); - } else { - if (it->isMember("long")) { - f = "--" + (*it)["long"].asString(); - } - if (it->isMember("short")) { - f = "-" + (*it)["short"].asString(); - } + }else{ + if (it->isMember("long")){f = "--" + (*it)["long"].asString();} + if (it->isMember("short")){f = "-" + (*it)["short"].asString();} } - while (f.size() < longest) { - f.append(" "); - } - if (it->isMember("arg")) { - output << f << "(" << (*it)["arg"].asString() << ") " << (*it)["help"].asString() << std::endl; - } else { + while (f.size() < longest){f.append(" ");} + if (it->isMember("arg")){ + output << f << "(" << (*it)["arg"].asString() << ") " << (*it)["help"].asString() + << std::endl; + }else{ output << f << (*it)["help"].asString() << std::endl; } } - if (it->isMember("arg_num")) { + if (it->isMember("arg_num")){ f = it.key(); - while (f.size() < longest) { - f.append(" "); - } - output << f << "(" << (*it)["arg"].asString() << ") " << (*it)["help"].asString() << std::endl; + while (f.size() < longest){f.append(" ");} + output << f << "(" << (*it)["arg"].asString() << ") " << (*it)["help"].asString() + << std::endl; } } } /// Parses commandline arguments. /// Calls exit if an unknown option is encountered, printing a help message. -bool Util::Config::parseArgs(int & argc, char ** & argv) { +bool Util::Config::parseArgs(int &argc, char **&argv){ int opt = 0; std::string shortopts; - struct option * longOpts = (struct option *)calloc(long_count + 1, sizeof(struct option)); + struct option *longOpts = (struct option *)calloc(long_count + 1, sizeof(struct option)); int long_i = 0; int arg_count = 0; - if (vals.size()) { - jsonForEach(vals, it) { - if (it->isMember("short")) { + if (vals.size()){ + jsonForEach(vals, it){ + if (it->isMember("short")){ shortopts += (*it)["short"].asString(); - if (it->isMember("arg")) { - shortopts += ":"; - } + if (it->isMember("arg")){shortopts += ":";} } - if (it->isMember("long")) { + if (it->isMember("long")){ longOpts[long_i].name = (*it)["long"].asStringRef().c_str(); longOpts[long_i].val = (*it)["short"].asString()[0]; - if (it->isMember("arg")) { - longOpts[long_i].has_arg = 1; - } + if (it->isMember("arg")){longOpts[long_i].has_arg = 1;} long_i++; } - if (it->isMember("arg_num") && !(it->isMember("value") && (*it)["value"].size())) { - if ((*it)["arg_num"].asInt() > arg_count) { - arg_count = (*it)["arg_num"].asInt(); - } + if (it->isMember("arg_num") && !(it->isMember("value") && (*it)["value"].size())){ + if ((*it)["arg_num"].asInt() > arg_count){arg_count = (*it)["arg_num"].asInt();} } } } - - while ((opt = getopt_long(argc, argv, shortopts.c_str(), longOpts, 0)) != -1) { - switch (opt) { - case 'h': - case '?': - printHelp(std::cout); - case 'v': - std::cout << "Version: " PACKAGE_VERSION ", release " RELEASE << std::endl; - #ifdef NOCRASHCHECK - std::cout << "- Flag: No crash check. Will not attempt to detect and kill crashed processes." << std::endl; - #endif - #ifndef SHM_ENABLED - std::cout << "- Flag: Shared memory disabled. Will use shared files in stead of shared memory as IPC method." << std::endl; - #endif - #ifdef WITH_THREADNAMES - std::cout << "- Flag: With threadnames. Debuggers will show sensible human-readable thread names." << std::endl; - #endif - std::cout << "Built on " __DATE__ ", " __TIME__ << std::endl; - exit(0); - break; - default: - jsonForEach(vals, it) { - if (it->isMember("short") && (*it)["short"].asString()[0] == opt) { - if (it->isMember("arg")) { - (*it)["value"].append((std::string)optarg); - } else { - (*it)["value"].append((long long int)1); - } - break; + + while ((opt = getopt_long(argc, argv, shortopts.c_str(), longOpts, 0)) != -1){ + switch (opt){ + case 'h': + case '?': printHelp(std::cout); + case 'v': std::cout << "Version: " PACKAGE_VERSION ", release " RELEASE << std::endl; +#ifdef NOCRASHCHECK + std::cout << "- Flag: No crash check. Will not attempt to detect and kill crashed processes." + << std::endl; +#endif +#ifndef SHM_ENABLED + std::cout << "- Flag: Shared memory disabled. Will use shared files in stead of shared " + "memory as IPC method." + << std::endl; +#endif +#ifdef WITH_THREADNAMES + std::cout + << "- Flag: With threadnames. Debuggers will show sensible human-readable thread names." + << std::endl; +#endif + std::cout << "Built on " __DATE__ ", " __TIME__ << std::endl; + exit(0); + break; + default: + jsonForEach(vals, it){ + if (it->isMember("short") && (*it)["short"].asString()[0] == opt){ + if (it->isMember("arg")){ + (*it)["value"].append(optarg); + }else{ + (*it)["value"].append((int64_t)1); } + break; } - break; + } + break; } - } //commandline options parser - free(longOpts); //free the long options array - long_i = 1; //re-use long_i as an argument counter - while (optind < argc) { //parse all remaining options, ignoring anything unexpected. - jsonForEach(vals, it) { - if (it->isMember("arg_num") && (*it)["arg_num"].asInt() == long_i) { + }// commandline options parser + free(longOpts); // free the long options array + long_i = 1; // re-use long_i as an argument counter + while (optind < argc){// parse all remaining options, ignoring anything unexpected. + jsonForEach(vals, it){ + if (it->isMember("arg_num") && (*it)["arg_num"].asInt() == long_i){ (*it)["value"].append((std::string)argv[optind]); break; } @@ -237,89 +217,85 @@ bool Util::Config::parseArgs(int & argc, char ** & argv) { optind++; long_i++; } - if (long_i <= arg_count) { - return false; - } + if (long_i <= arg_count){return false;} printDebugLevel = getInteger("debug"); return true; } -bool Util::Config::hasOption(const std::string & optname) { +bool Util::Config::hasOption(const std::string &optname){ return vals.isMember(optname); } /// Returns a reference to the current value of an option or default if none was set. /// If the option does not exist, this exits the application with a return code of 37. -JSON::Value & Util::Config::getOption(std::string optname, bool asArray) { - if (!vals.isMember(optname)) { - std::cout << "Fatal error: a non-existent option '" << optname << "' was accessed." << std::endl; +JSON::Value &Util::Config::getOption(std::string optname, bool asArray){ + if (!vals.isMember(optname)){ + std::cout << "Fatal error: a non-existent option '" << optname << "' was accessed." + << std::endl; exit(37); } - if (!vals[optname].isMember("value") || !vals[optname]["value"].isArray()) { + if (!vals[optname].isMember("value") || !vals[optname]["value"].isArray()){ vals[optname]["value"].append(JSON::Value()); vals[optname]["value"].shrink(0); } - if (asArray) { - return vals[optname]["value"]; - } else { - int n = vals[optname]["value"].size(); - if (!n){ - static JSON::Value empty = ""; - return empty; - }else{ - return vals[optname]["value"][n - 1]; - } + if (asArray){return vals[optname]["value"];} + int n = vals[optname]["value"].size(); + if (!n){ + static JSON::Value empty = ""; + return empty; } + return vals[optname]["value"][n - 1]; } /// Returns the current value of an option or default if none was set as a string. /// Calls getOption internally. -std::string Util::Config::getString(std::string optname) { +std::string Util::Config::getString(std::string optname){ return getOption(optname).asString(); } /// Returns the current value of an option or default if none was set as a long long int. /// Calls getOption internally. -long long int Util::Config::getInteger(std::string optname) { +int64_t Util::Config::getInteger(std::string optname){ return getOption(optname).asInt(); } /// Returns the current value of an option or default if none was set as a bool. /// Calls getOption internally. -bool Util::Config::getBool(std::string optname) { +bool Util::Config::getBool(std::string optname){ return getOption(optname).asBool(); } -struct callbackData { - Socket::Connection * sock; +struct callbackData{ + Socket::Connection *sock; int (*cb)(Socket::Connection &); }; -static void callThreadCallback(void * cDataArg) { - DEBUG_MSG(DLVL_INSANE, "Thread for %p started", cDataArg); - callbackData * cData = (callbackData *)cDataArg; +static void callThreadCallback(void *cDataArg){ + INSANE_MSG("Thread for %p started", cDataArg); + callbackData *cData = (callbackData *)cDataArg; cData->cb(*(cData->sock)); cData->sock->close(); delete cData->sock; delete cData; - DEBUG_MSG(DLVL_INSANE, "Thread for %p ended", cDataArg); + INSANE_MSG("Thread for %p ended", cDataArg); } -int Util::Config::threadServer(Socket::Server & server_socket, int (*callback)(Socket::Connection &)) { +int Util::Config::threadServer(Socket::Server &server_socket, + int (*callback)(Socket::Connection &)){ Util::Procs::socketList.insert(server_socket.getSocket()); - while (is_active && server_socket.connected()) { + while (is_active && server_socket.connected()){ Socket::Connection S = server_socket.accept(); - if (S.connected()) { //check if the new connection is valid - callbackData * cData = new callbackData; + if (S.connected()){// check if the new connection is valid + callbackData *cData = new callbackData; cData->sock = new Socket::Connection(S); cData->cb = callback; - //spawn a new thread for this connection + // spawn a new thread for this connection tthread::thread T(callThreadCallback, (void *)cData); - //detach it, no need to keep track of it anymore + // detach it, no need to keep track of it anymore T.detach(); - DEBUG_MSG(DLVL_HIGH, "Spawned new thread for socket %i", S.getSocket()); - } else { - Util::sleep(10); //sleep 10ms + HIGH_MSG("Spawned new thread for socket %i", S.getSocket()); + }else{ + Util::sleep(10); // sleep 10ms } } Util::Procs::socketList.erase(server_socket.getSocket()); @@ -327,21 +303,21 @@ int Util::Config::threadServer(Socket::Server & server_socket, int (*callback)(S return 0; } -int Util::Config::forkServer(Socket::Server & server_socket, int (*callback)(Socket::Connection &)) { +int Util::Config::forkServer(Socket::Server &server_socket, int (*callback)(Socket::Connection &)){ Util::Procs::socketList.insert(server_socket.getSocket()); - while (is_active && server_socket.connected()) { + while (is_active && server_socket.connected()){ Socket::Connection S = server_socket.accept(); - if (S.connected()) { //check if the new connection is valid + if (S.connected()){// check if the new connection is valid pid_t myid = fork(); - if (myid == 0) { //if new child, start MAINHANDLER + if (myid == 0){// if new child, start MAINHANDLER server_socket.drop(); return callback(S); - } else { //otherwise, do nothing or output debugging text - DEBUG_MSG(DLVL_HIGH, "Forked new process %i for socket %i", (int)myid, S.getSocket()); + }else{// otherwise, do nothing or output debugging text + HIGH_MSG("Forked new process %i for socket %i", (int)myid, S.getSocket()); S.drop(); } - } else { - Util::sleep(10); //sleep 10ms + }else{ + Util::sleep(10); // sleep 10ms } } Util::Procs::socketList.erase(server_socket.getSocket()); @@ -349,40 +325,40 @@ int Util::Config::forkServer(Socket::Server & server_socket, int (*callback)(Soc return 0; } -int Util::Config::serveThreadedSocket(int (*callback)(Socket::Connection &)) { +int Util::Config::serveThreadedSocket(int (*callback)(Socket::Connection &)){ Socket::Server server_socket; - if (vals.isMember("socket")) { + if (vals.isMember("socket")){ server_socket = Socket::Server(Util::getTmpFolder() + getString("socket")); } - if (vals.isMember("port") && vals.isMember("interface")) { + if (vals.isMember("port") && vals.isMember("interface")){ server_socket = Socket::Server(getInteger("port"), getString("interface"), false); } - if (!server_socket.connected()) { - DEBUG_MSG(DLVL_DEVEL, "Failure to open socket"); + if (!server_socket.connected()){ + DEVEL_MSG("Failure to open socket"); return 1; } serv_sock_pointer = &server_socket; - DEBUG_MSG(DLVL_DEVEL, "Activating threaded server: %s", getString("cmd").c_str()); + DEVEL_MSG("Activating threaded server: %s", getString("cmd").c_str()); activate(); int r = threadServer(server_socket, callback); serv_sock_pointer = 0; return r; } -int Util::Config::serveForkedSocket(int (*callback)(Socket::Connection & S)) { +int Util::Config::serveForkedSocket(int (*callback)(Socket::Connection &S)){ Socket::Server server_socket; - if (vals.isMember("socket")) { + if (vals.isMember("socket")){ server_socket = Socket::Server(Util::getTmpFolder() + getString("socket")); } - if (vals.isMember("port") && vals.isMember("interface")) { + if (vals.isMember("port") && vals.isMember("interface")){ server_socket = Socket::Server(getInteger("port"), getString("interface"), false); } - if (!server_socket.connected()) { - DEBUG_MSG(DLVL_DEVEL, "Failure to open socket"); + if (!server_socket.connected()){ + DEVEL_MSG("Failure to open socket"); return 1; } serv_sock_pointer = &server_socket; - DEBUG_MSG(DLVL_DEVEL, "Activating forked server: %s", getString("cmd").c_str()); + DEVEL_MSG("Activating forked server: %s", getString("cmd").c_str()); activate(); int r = forkServer(server_socket, callback); serv_sock_pointer = 0; @@ -393,8 +369,8 @@ int Util::Config::serveForkedSocket(int (*callback)(Socket::Connection & S)) { /// - Drop permissions to the stored "username", if any. /// - Set is_active to true. /// - Set up a signal handler to set is_active to false for the SIGINT, SIGHUP and SIGTERM signals. -void Util::Config::activate() { - if (vals.isMember("username")) { +void Util::Config::activate(){ + if (vals.isMember("username")){ setUser(getString("username")); vals.removeMember("username"); } @@ -407,9 +383,9 @@ void Util::Config::activate() { sigaction(SIGHUP, &new_action, NULL); sigaction(SIGTERM, &new_action, NULL); sigaction(SIGPIPE, &new_action, NULL); - //check if a child signal handler isn't set already, if so, set it. + // check if a child signal handler isn't set already, if so, set it. sigaction(SIGCHLD, 0, &cur_action); - if (cur_action.sa_handler == SIG_DFL || cur_action.sa_handler == SIG_IGN) { + if (cur_action.sa_handler == SIG_DFL || cur_action.sa_handler == SIG_IGN){ sigaction(SIGCHLD, &new_action, NULL); } is_active = true; @@ -418,55 +394,51 @@ void Util::Config::activate() { /// Basic signal handler. Sets is_active to false if it receives /// a SIGINT, SIGHUP or SIGTERM signal, reaps children for the SIGCHLD /// signal, and ignores all other signals. -void Util::Config::signal_handler(int signum, siginfo_t * sigInfo, void * ignore) { - switch (signum) { - case SIGINT: //these three signals will set is_active to false. - case SIGHUP: - case SIGTERM: - if (serv_sock_pointer){serv_sock_pointer->close();} +void Util::Config::signal_handler(int signum, siginfo_t *sigInfo, void *ignore){ + switch (signum){ + case SIGINT: // these three signals will set is_active to false. + case SIGHUP: + case SIGTERM: + if (serv_sock_pointer){serv_sock_pointer->close();} #if DEBUG >= DLVL_DEVEL - static int ctr = 0; - if (!is_active && ++ctr > 4){BACKTRACE;} + static int ctr = 0; + if (!is_active && ++ctr > 4){BACKTRACE;} #endif - is_active = false; - default: - switch (sigInfo->si_code){ - case SI_USER: - case SI_QUEUE: - case SI_TIMER: - case SI_ASYNCIO: - case SI_MESGQ: - INFO_MSG("Received signal %s (%d) from process %d", strsignal(signum), signum, sigInfo->si_pid); - break; - default: - INFO_MSG("Received signal %s (%d)", strsignal(signum), signum); - break; - } - break; - case SIGCHLD: { //when a child dies, reap it. - int status; - pid_t ret = -1; - while (ret != 0) { - ret = waitpid(-1, &status, WNOHANG); - if (ret < 0 && errno != EINTR) { - break; - } - } - HIGH_MSG("Received signal %s (%d) from process %d", strsignal(signum), signum, sigInfo->si_pid); - break; - } - case SIGPIPE: - //We ignore SIGPIPE to prevent messages triggering another SIGPIPE. - //Loops are bad, m'kay? + is_active = false; + default: + switch (sigInfo->si_code){ + case SI_USER: + case SI_QUEUE: + case SI_TIMER: + case SI_ASYNCIO: + case SI_MESGQ: + INFO_MSG("Received signal %s (%d) from process %d", strsignal(signum), signum, + sigInfo->si_pid); break; + default: INFO_MSG("Received signal %s (%d)", strsignal(signum), signum); break; + } + break; + case SIGCHLD:{// when a child dies, reap it. + int status; + pid_t ret = -1; + while (ret != 0){ + ret = waitpid(-1, &status, WNOHANG); + if (ret < 0 && errno != EINTR){break;} + } + HIGH_MSG("Received signal %s (%d) from process %d", strsignal(signum), signum, sigInfo->si_pid); + break; } -} //signal_handler - + case SIGPIPE: + // We ignore SIGPIPE to prevent messages triggering another SIGPIPE. + // Loops are bad, m'kay? + break; + } +}// signal_handler /// Adds the options from the given JSON capabilities structure. /// Recurses into optional and required, added options as needed. -void Util::Config::addOptionsFromCapabilities(const JSON::Value & capa){ - //First add the required options. +void Util::Config::addOptionsFromCapabilities(const JSON::Value &capa){ + // First add the required options. if (capa.isMember("required") && capa["required"].size()){ jsonForEachConst(capa["required"], it){ if (!it->isMember("short") || !it->isMember("option") || !it->isMember("type")){ @@ -477,21 +449,19 @@ void Util::Config::addOptionsFromCapabilities(const JSON::Value & capa){ opt["short"] = (*it)["short"]; opt["long"] = (*it)["option"].asStringRef().substr(2); if (it->isMember("type")){ - //int, uint, debug, select, str + // int, uint, debug, select, str if ((*it)["type"].asStringRef() == "int" || (*it)["type"].asStringRef() == "uint"){ opt["arg"] = "integer"; }else{ opt["arg"] = "string"; } } - if (it->isMember("default")){ - opt["value"].append((*it)["default"]); - } + if (it->isMember("default")){opt["value"].append((*it)["default"]);} opt["help"] = (*it)["help"]; addOption(it.key(), opt); } } - //Then, the optionals. + // Then, the optionals. if (capa.isMember("optional") && capa["optional"].size()){ jsonForEachConst(capa["optional"], it){ if (it.key() == "debug"){continue;} @@ -503,31 +473,30 @@ void Util::Config::addOptionsFromCapabilities(const JSON::Value & capa){ opt["short"] = (*it)["short"]; opt["long"] = (*it)["option"].asStringRef().substr(2); if (it->isMember("type")){ - //int, uint, debug, select, str + // int, uint, debug, select, str if ((*it)["type"].asStringRef() == "int" || (*it)["type"].asStringRef() == "uint"){ opt["arg"] = "integer"; }else{ opt["arg"] = "string"; } } - if (it->isMember("default")){ - opt["value"].append((*it)["default"]); - } + if (it->isMember("default")){opt["value"].append((*it)["default"]);} opt["help"] = (*it)["help"]; addOption(it.key(), opt); } } } -/// Adds the default connector options. Also updates the capabilities structure with the default options. -/// Besides the options addBasicConnectorOptions adds, this function also adds port and interface options. -void Util::Config::addConnectorOptions(int port, JSON::Value & capabilities) { +/// Adds the default connector options. Also updates the capabilities structure with the default +/// options. Besides the options addBasicConnectorOptions adds, this function also adds port and +/// interface options. +void Util::Config::addConnectorOptions(int port, JSON::Value &capabilities){ capabilities["optional"]["port"]["name"] = "TCP port"; capabilities["optional"]["port"]["help"] = "TCP port to listen on"; capabilities["optional"]["port"]["type"] = "uint"; capabilities["optional"]["port"]["short"] = "p"; capabilities["optional"]["port"]["option"] = "--port"; - capabilities["optional"]["port"]["default"] = (long long)port; + capabilities["optional"]["port"]["default"] = (int64_t)port; capabilities["optional"]["interface"]["name"] = "Interface"; capabilities["optional"]["interface"]["help"] = "Address of the interface to listen on"; @@ -537,12 +506,14 @@ void Util::Config::addConnectorOptions(int port, JSON::Value & capabilities) { capabilities["optional"]["interface"]["type"] = "str"; addBasicConnectorOptions(capabilities); -} //addConnectorOptions +}// addConnectorOptions -/// Adds the default connector options. Also updates the capabilities structure with the default options. -void Util::Config::addBasicConnectorOptions(JSON::Value & capabilities) { +/// Adds the default connector options. Also updates the capabilities structure with the default +/// options. +void Util::Config::addBasicConnectorOptions(JSON::Value &capabilities){ capabilities["optional"]["username"]["name"] = "Username"; - capabilities["optional"]["username"]["help"] = "Username to drop privileges to - default if unprovided means do not drop privileges"; + capabilities["optional"]["username"]["help"] = + "Username to drop privileges to - default if unprovided means do not drop privileges"; capabilities["optional"]["username"]["option"] = "--username"; capabilities["optional"]["username"]["short"] = "u"; capabilities["optional"]["username"]["default"] = "root"; @@ -554,14 +525,12 @@ void Util::Config::addBasicConnectorOptions(JSON::Value & capabilities) { option["long"] = "json"; option["short"] = "j"; option["help"] = "Output connector info in JSON format, then exit."; - option["value"].append(0ll); + option["value"].append((int64_t)0); addOption("json", option); } - - /// Gets directory the current executable is stored in. -std::string Util::getMyPath() { +std::string Util::getMyPath(){ char mypath[500]; #ifdef __CYGWIN__ GetModuleFileName(0, mypath, 500); @@ -572,69 +541,63 @@ std::string Util::getMyPath() { int ret = _NSGetExecutablePath(mypath, &refSize); #else int ret = readlink("/proc/self/exe", mypath, 500); - if (ret != -1) { + if (ret != -1){ mypath[ret] = 0; - } else { + }else{ mypath[0] = 0; } #endif #endif std::string tPath = mypath; size_t slash = tPath.rfind('/'); - if (slash == std::string::npos) { + if (slash == std::string::npos){ slash = tPath.rfind('\\'); - if (slash == std::string::npos) { - return ""; - } + if (slash == std::string::npos){return "";} } tPath.resize(slash + 1); return tPath; } /// Gets all executables in getMyPath that start with "Mist". -void Util::getMyExec(std::deque & execs) { +void Util::getMyExec(std::deque &execs){ std::string path = Util::getMyPath(); #ifdef __CYGWIN__ path += "\\Mist*"; WIN32_FIND_DATA FindFileData; HANDLE hdl = FindFirstFile(path.c_str(), &FindFileData); - while (hdl != INVALID_HANDLE_VALUE) { + while (hdl != INVALID_HANDLE_VALUE){ execs.push_back(FindFileData.cFileName); - if (!FindNextFile(hdl, &FindFileData)) { + if (!FindNextFile(hdl, &FindFileData)){ FindClose(hdl); hdl = INVALID_HANDLE_VALUE; } } #else - DIR * d = opendir(path.c_str()); - if (!d) { - return; - } - struct dirent * dp; - do { + DIR *d = opendir(path.c_str()); + if (!d){return;} + struct dirent *dp; + do{ errno = 0; - if ((dp = readdir(d))) { - if (strncmp(dp->d_name, "Mist", 4) == 0) { - execs.push_back(dp->d_name); - } + if ((dp = readdir(d))){ + if (strncmp(dp->d_name, "Mist", 4) == 0){execs.push_back(dp->d_name);} } - } while (dp != NULL); + }while (dp != NULL); closedir(d); #endif } /// Sets the current process' running user -void Util::setUser(std::string username) { - if (username != "root") { - struct passwd * user_info = getpwnam(username.c_str()); - if (!user_info) { - DEBUG_MSG(DLVL_ERROR, "Error: could not setuid %s: could not get PID", username.c_str()); +void Util::setUser(std::string username){ + if (username != "root"){ + struct passwd *user_info = getpwnam(username.c_str()); + if (!user_info){ + ERROR_MSG("Error: could not setuid %s: could not get PID", username.c_str()); return; - } else { - if (setuid(user_info->pw_uid) != 0) { - DEBUG_MSG(DLVL_ERROR, "Error: could not setuid %s: not allowed", username.c_str()); - } else { - DEBUG_MSG(DLVL_DEVEL, "Change user to %s", username.c_str()); + }else{ + if (setuid(user_info->pw_uid) != 0){ + ERROR_MSG("Error: could not setuid %s: not allowed", username.c_str()); + }else{ + DEVEL_MSG("Change user to %s", username.c_str()); } } } diff --git a/lib/config.h b/lib/config.h index 35e3e49b..21f3b5e0 100644 --- a/lib/config.h +++ b/lib/config.h @@ -7,53 +7,55 @@ #define PACKAGE_VERSION "unknown" #endif -#include #include "json.h" #include +#include /// Contains utility code, not directly related to streaming media -namespace Util { +namespace Util{ /// Deals with parsing configuration from commandline options. - class Config { - private: - JSON::Value vals; ///< Holds all current config values - int long_count; - static void signal_handler(int signum, siginfo_t * sigInfo, void * ignore); - public: - //variables - static bool is_active; ///< Set to true by activate(), set to false by the signal handler. - static unsigned int printDebugLevel; - static std::string streamName; ///< Used by debug messages to identify the stream name - //functions - Config(); - Config(std::string cmd); - void addOption(std::string optname, JSON::Value option); - void printHelp(std::ostream & output); - bool parseArgs(int & argc, char ** & argv); - bool hasOption(const std::string & optname); - JSON::Value & getOption(std::string optname, bool asArray = false); - std::string getString(std::string optname); - long long int getInteger(std::string optname); - bool getBool(std::string optname); - void activate(); - int threadServer(Socket::Server & server_socket, int (*callback)(Socket::Connection & S)); - int forkServer(Socket::Server & server_socket, int (*callback)(Socket::Connection & S)); - int serveThreadedSocket(int (*callback)(Socket::Connection & S)); - int serveForkedSocket(int (*callback)(Socket::Connection & S)); - int servePlainSocket(int (*callback)(Socket::Connection & S)); - void addOptionsFromCapabilities(const JSON::Value & capabilities); - void addBasicConnectorOptions(JSON::Value & capabilities); - void addConnectorOptions(int port, JSON::Value & capabilities); + class Config{ + private: + JSON::Value vals; ///< Holds all current config values + int long_count; + static void signal_handler(int signum, siginfo_t *sigInfo, void *ignore); + + public: + // variables + static bool is_active; ///< Set to true by activate(), set to false by the signal handler. + static uint32_t printDebugLevel; + static std::string streamName; ///< Used by debug messages to identify the stream name + // functions + Config(); + Config(std::string cmd); + void addOption(std::string optname, JSON::Value option); + void printHelp(std::ostream &output); + bool parseArgs(int &argc, char **&argv); + bool hasOption(const std::string &optname); + JSON::Value &getOption(std::string optname, bool asArray = false); + std::string getString(std::string optname); + int64_t getInteger(std::string optname); + bool getBool(std::string optname); + void activate(); + int threadServer(Socket::Server &server_socket, int (*callback)(Socket::Connection &S)); + int forkServer(Socket::Server &server_socket, int (*callback)(Socket::Connection &S)); + int serveThreadedSocket(int (*callback)(Socket::Connection &S)); + int serveForkedSocket(int (*callback)(Socket::Connection &S)); + int servePlainSocket(int (*callback)(Socket::Connection &S)); + void addOptionsFromCapabilities(const JSON::Value &capabilities); + void addBasicConnectorOptions(JSON::Value &capabilities); + void addConnectorOptions(int port, JSON::Value &capabilities); }; /// Gets directory the current executable is stored in. std::string getMyPath(); /// Gets all executables in getMyPath that start with "Mist". - void getMyExec(std::deque & execs); + void getMyExec(std::deque &execs); /// Will set the active user to the named username. void setUser(std::string user); -} +}// namespace Util + diff --git a/lib/defines.h b/lib/defines.h index c62deef3..a59c7266 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -18,7 +18,9 @@ #define PRETTY_ARG_TIME(t) (int)(t)/86400, ((int)(t)%86400)/3600, ((int)(t)%3600)/60, (int)(t)%60 #if DEBUG > -1 +#define __STDC_FORMAT_MACROS 1 #include +#include #include #include #include "config.h" diff --git a/lib/downloader.cpp b/lib/downloader.cpp index cb48f17a..c45ed90f 100644 --- a/lib/downloader.cpp +++ b/lib/downloader.cpp @@ -1,7 +1,7 @@ #include "downloader.h" #include "defines.h" -#include "timing.h" #include "encode.h" +#include "timing.h" namespace HTTP{ @@ -66,7 +66,8 @@ namespace HTTP{ } /// Sends a request for the given URL, does no waiting. - void Downloader::doRequest(const HTTP::URL &link, const std::string &method, const std::string &body){ + void Downloader::doRequest(const HTTP::URL &link, const std::string &method, + const std::string &body){ if (!canRequest(link)){return;} bool needSSL = (link.protocol == "https"); H.Clean(); @@ -116,9 +117,7 @@ namespace HTTP{ H.SetHeader("Host", link.host); } } - if (method.size()){ - H.method = method; - } + if (method.size()){H.method = method;} H.SetHeader("User-Agent", "MistServer " PACKAGE_VERSION); H.SetHeader("X-Version", PACKAGE_VERSION); H.SetHeader("Accept", "*/*"); @@ -142,9 +141,10 @@ namespace HTTP{ /// Makes at most 5 attempts, and will wait no longer than 5 seconds without receiving data. bool Downloader::get(const HTTP::URL &link, uint8_t maxRecursiveDepth){ if (!canRequest(link)){return false;} - unsigned int loop = retryCount+1; // max 5 attempts + size_t loop = retryCount + 1; // max 5 attempts while (--loop){// loop while we are unsuccessful - MEDIUM_MSG("Retrieving %s (%lu/%lu)", link.getUrl().c_str(), retryCount-loop+1, retryCount); + MEDIUM_MSG("Retrieving %s (%zu/%" PRIu32 ")", link.getUrl().c_str(), retryCount - loop + 1, + retryCount); doRequest(link); uint64_t reqTime = Util::bootSecs(); while (getSocket() && Util::bootSecs() < reqTime + dataTimeout){ @@ -179,24 +179,28 @@ namespace HTTP{ reqTime = Util::bootSecs(); } if (getSocket()){ - FAIL_MSG("Timeout while retrieving %s (%lu/%lu)", link.getUrl().c_str(), retryCount-loop+1, retryCount); + FAIL_MSG("Timeout while retrieving %s (%zu/%" PRIu32 ")", link.getUrl().c_str(), + retryCount - loop + 1, retryCount); getSocket().close(); }else{ - FAIL_MSG("Lost connection while retrieving %s (%lu/%lu)", link.getUrl().c_str(), retryCount-loop+1, retryCount); + FAIL_MSG("Lost connection while retrieving %s (%zu/%" PRIu32 ")", link.getUrl().c_str(), + retryCount - loop + 1, retryCount); } Util::sleep(500); // wait a bit before retrying } FAIL_MSG("Could not retrieve %s", link.getUrl().c_str()); return false; } - - bool Downloader::post(const HTTP::URL &link, const std::string &payload, bool sync, uint8_t maxRecursiveDepth){ + + bool Downloader::post(const HTTP::URL &link, const std::string &payload, bool sync, + uint8_t maxRecursiveDepth){ if (!canRequest(link)){return false;} - unsigned int loop = retryCount; // max 5 attempts + size_t loop = retryCount; // max 5 attempts while (--loop){// loop while we are unsuccessful - MEDIUM_MSG("Posting to %s (%lu/%lu)", link.getUrl().c_str(), retryCount-loop+1, retryCount); + MEDIUM_MSG("Posting to %s (%zu/%" PRIu32 ")", link.getUrl().c_str(), retryCount - loop + 1, + retryCount); doRequest(link, "POST", payload); - //Not synced? Ignore the response and immediately return false. + // Not synced? Ignore the response and immediately return false. if (!sync){return false;} uint64_t reqTime = Util::bootSecs(); while (getSocket() && Util::bootSecs() < reqTime + dataTimeout){ @@ -261,9 +265,7 @@ namespace HTTP{ setHeader("Cookie", cookie.substr(0, cookie.find(';'))); } uint32_t sCode = getStatusCode(); - if (sCode == 401 || sCode == 407 || (sCode >= 300 && sCode < 400)){ - return true; - } + if (sCode == 401 || sCode == 407 || (sCode >= 300 && sCode < 400)){return true;} return false; } @@ -285,9 +287,7 @@ namespace HTTP{ } if (getStatusCode() == 407){ // retry with authentication - if (H.hasHeader("Proxy-Authenticate")){ - proxyAuthStr = H.GetHeader("Proxy-Authenticate"); - } + if (H.hasHeader("Proxy-Authenticate")){proxyAuthStr = H.GetHeader("Proxy-Authenticate");} if (!proxyAuthStr.size()){ FAIL_MSG("Proxy authentication required but no Proxy-Authenticate header present"); return false; diff --git a/lib/downloader.h b/lib/downloader.h index d5895dc0..35eef078 100644 --- a/lib/downloader.h +++ b/lib/downloader.h @@ -7,16 +7,20 @@ namespace HTTP{ Downloader(); std::string &data(); const std::string &const_data() const; - void doRequest(const HTTP::URL &link, const std::string &method="", const std::string &body=""); + void doRequest(const HTTP::URL &link, const std::string &method = "", + const std::string &body = ""); bool get(const std::string &link); bool get(const HTTP::URL &link, uint8_t maxRecursiveDepth = 6); - bool post(const HTTP::URL &link, const std::string &payload, bool sync = true, uint8_t maxRecursiveDepth = 6); + bool post(const HTTP::URL &link, const std::string &payload, bool sync = true, + uint8_t maxRecursiveDepth = 6); std::string getHeader(const std::string &headerName); std::string &getStatusText(); uint32_t getStatusCode(); - bool isOk(); ///< True if the request was successful. - bool shouldContinue(); /// dataLen) { - DEBUG_MSG(DLVL_VERYHIGH, "Length mismatch"); + VERYHIGH_MSG("Length mismatch"); return false; } return true; @@ -100,14 +100,14 @@ namespace DTSC { /// 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) { + void Packet::resize(size_t 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"); + FAIL_MSG("Out of memory on parsing a packet"); } } } @@ -156,7 +156,7 @@ namespace DTSC { if (data_[0] != 'D' || data_[1] != 'T') { unsigned int twlen = len; if (twlen > 20){twlen = 20;} - DEBUG_MSG(DLVL_HIGH, "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); + 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; } @@ -179,29 +179,25 @@ namespace DTSC { //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 { - if (!memcmp(data, Magic_Command, 4)) { - version = DTCM; - } 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"); + 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. @@ -298,12 +294,21 @@ namespace DTSC { } } + 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; } -// INFO_MSG("totallen: %d, appendLen: %d",totalLen,appendLen); resize(dataLen + appendLen +4); Bit::htobl(data+dataLen -3, appendLen); memcpy(data + dataLen-3+4, appendData, appendLen); @@ -336,13 +341,13 @@ namespace DTSC { Bit::htobl(data+offset, Bit::btohl(data+offset)+appendLen); } - uint32_t Packet::getDataStringLen(){ + size_t Packet::getDataStringLen(){ return Bit::btohl(data+getDataStringLenOffset()); } ///Method can only be used when using internal functions to build the data. - uint32_t Packet::getDataStringLenOffset(){ - uint32_t offset = 23; + size_t Packet::getDataStringLenOffset(){ + size_t offset = 23; while (data[offset] != 'd'){ switch (data[offset]){ case 'o': offset += 17; break; @@ -408,7 +413,7 @@ namespace DTSC { ///\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 { + void Packet::getString(const char * identifier, char *& result, size_t & len) const { getScan().getMember(identifier).getString(result, len); } @@ -441,7 +446,7 @@ namespace DTSC { void Packet::getFlag(const char * identifier, bool & result) const { uint64_t result_; getInt(identifier, result_); - result = (bool)result_; + result = result_; } ///\brief Retrieves a single parameter as a boolean @@ -462,7 +467,7 @@ namespace DTSC { ///\brief Returns the timestamp of the packet. ///\return The timestamp of this packet. - long long unsigned int Packet::getTime() const { + uint64_t Packet::getTime() const { if (version != DTSC_V2) { if (!data) { return 0; @@ -472,9 +477,17 @@ namespace DTSC { 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. - long int Packet::getTrackId() const { + size_t Packet::getTrackId() const { if (version != DTSC_V2) { return getInt("trackid"); } @@ -489,13 +502,13 @@ namespace DTSC { ///\brief Returns the size of this packet. ///\return The size of this packet. - int Packet::getDataLen() const { + uint64_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. - int Packet::getPayloadLen() const { + size_t Packet::getPayloadLen() const { if (version == DTSC_V2) { return dataLen - 20; } else { @@ -516,12 +529,12 @@ namespace DTSC { ///\return A JSON::Value representation of this packet. JSON::Value Packet::toJSON() const { JSON::Value result; - unsigned int i = 8; + uint32_t i = 8; if (getVersion() == DTSC_V1) { - JSON::fromDTMI((const unsigned char *)data, dataLen, i, result); + JSON::fromDTMI(data, dataLen, i, result); } if (getVersion() == DTSC_V2) { - JSON::fromDTMI2((const unsigned char *)data, dataLen, i, result); + JSON::fromDTMI2(data, dataLen, i, result); } return result; } @@ -529,7 +542,7 @@ namespace DTSC { std::string Packet::toSummary() const { std::stringstream out; char * res = 0; - unsigned int len = 0; + size_t len = 0; getString("data", res, len); out << getTrackId() << "@" << getTime() << ": " << len << " bytes"; if (hasMember("keyframe")){ @@ -558,13 +571,13 @@ namespace DTSC { /// 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) { + 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 unsigned int ind_len) { + Scan Scan::getMember(const char * indice, const size_t ind_len) const { if (getType() != DTSC_OBJ && getType() != DTSC_CON) { return Scan(); } @@ -574,15 +587,14 @@ namespace DTSC { if (i + 2 >= p + len) { return Scan();//out of packet! } - unsigned int strlen = Bit::btohs(i); + uint16_t strlen = Bit::btohs(i); 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(); - } + } + i = skipDTSC(i + strlen, p + len); + if (!i) { + return Scan(); } } return Scan(); @@ -590,63 +602,60 @@ namespace DTSC { /// 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(std::string indice){ + 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 unsigned int ind_len) { + 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) { + 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. - unsigned int Scan::getSize() { + size_t Scan::getSize() const { + uint32_t arr_indice = 0; 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... arr_indice++; i = skipDTSC(i, p + len); if (!i) { - return arr_indice; + break; } } - return arr_indice; } 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); + uint16_t strlen = Bit::btohs(i); i += 2; arr_indice++; i = skipDTSC(i + strlen, p + len); if (!i) { - return arr_indice; + break; } } - return arr_indice; } - return 0; + 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(unsigned int num) { + Scan Scan::getIndice(size_t num) const { if (getType() == DTSC_ARR) { char * i = p + 1; unsigned int arr_indice = 0; @@ -690,7 +699,7 @@ namespace DTSC { /// 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(unsigned int num) { + std::string Scan::getIndiceName(size_t num) const { if (getType() == DTSC_OBJ || getType() == DTSC_CON) { char * i = p + 1; unsigned int arr_indice = 0; @@ -716,7 +725,7 @@ namespace DTSC { } /// Returns the first byte of this DTSC value, or 0 on error. - char Scan::getType() { + char Scan::getType() const { if (!p) { return 0; } @@ -728,7 +737,7 @@ namespace DTSC { /// 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() { + bool Scan::asBool() const { switch (getType()) { case DTSC_STR: return (p[1] | p[2] | p[3] | p[4]); @@ -746,13 +755,13 @@ namespace DTSC { /// 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() { + int64_t Scan::asInt() const { switch (getType()) { case DTSC_INT: return Bit::btohll(p+1); case DTSC_STR: char * str; - unsigned int strlen; + size_t strlen; getString(str, strlen); if (!strlen) { return 0; @@ -767,7 +776,7 @@ namespace DTSC { /// Uses getString internally, if a string. /// Converts integer values to strings. /// Returns an empty string on error. - std::string Scan::asString() { + std::string Scan::asString() const { switch (getType()) { case DTSC_INT:{ std::stringstream st; @@ -777,7 +786,7 @@ namespace DTSC { break; case DTSC_STR:{ char * str; - unsigned int strlen; + size_t strlen; getString(str, strlen); return std::string(str, strlen); } @@ -789,25 +798,22 @@ namespace DTSC { /// 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, unsigned int & strlen) { - switch (getType()) { - case DTSC_STR: + void Scan::getString(char *& result, size_t & strlen) const { + if (getType() == DTSC_STR){ result = p + 5; strlen = Bit::btohl(p+1); return; - default: - result = 0; - strlen = 0; - return; } + result = 0; + strlen = 0; } /// Returns the DTSC scan object as a JSON value /// Returns an empty object on error. - JSON::Value Scan::asJSON(){ + JSON::Value Scan::asJSON() const { JSON::Value result; unsigned int i = 0; - JSON::fromDTMI((const unsigned char*)p, len, i, result); + JSON::fromDTMI(p, len, i, result); return result; } @@ -826,7 +832,7 @@ namespace DTSC { static std::string string_escape(const std::string val) { std::stringstream out; out << "\""; - for (unsigned int i = 0; i < val.size(); ++i) { + for (size_t i = 0; i < val.size(); ++i) { switch (val.data()[i]) { case '"': out << "\\\""; @@ -864,10 +870,10 @@ namespace DTSC { return out.str(); } - std::string Scan::toPrettyString(unsigned int indent) { + std::string Scan::toPrettyString(size_t indent) const { switch (getType()) { case DTSC_STR: { - unsigned int strlen = Bit::btohl(p+1); + uint32_t strlen = Bit::btohl(p+1); if (strlen > 250) { std::stringstream ret; ret << "\"" << strlen << " bytes of data\""; @@ -891,25 +897,25 @@ namespace DTSC { 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"; + ret << std::string(indent, ' ') << "} //walked out of object here"; return ret.str(); } if (!first){ ret << "," << std::endl; } first = false; - unsigned int strlen = Bit::btohs(i); + uint16_t strlen = Bit::btohs(i); i += 2; - ret << std::string((size_t)indent, ' ') << "\"" << std::string(i, strlen) << "\": " << Scan(i + strlen, len - (i - p)).toPrettyString(indent); + 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((size_t)indent, ' ') << "} //could not locate next object"; + ret << std::string(indent, ' ') << "} //could not locate next object"; return ret.str(); } } indent -= 2; - ret << std::endl << std::string((size_t)indent, ' ') << "}"; + ret << std::endl << std::string(indent, ' ') << "}"; return ret.str(); } case DTSC_ARR: { @@ -926,11 +932,11 @@ namespace DTSC { ret << "," << std::endl; } first = false; - ret << std::string((size_t)indent, ' ') << tmpScan.toPrettyString(indent); + ret << std::string(indent, ' ') << tmpScan.toPrettyString(indent); } } while (tmpScan.getType()); indent -= 2; - ret << std::endl << std::string((size_t)indent, ' ') << "]"; + ret << std::endl << std::string(indent, ' ') << "]"; return ret.str(); } default: @@ -1169,19 +1175,19 @@ namespace DTSC { Track::Track(Scan & trackRef) { if (trackRef.getMember("fragments").getType() == DTSC_STR) { char * tmp = 0; - unsigned int tmplen = 0; + size_t tmplen = 0; trackRef.getMember("fragments").getString(tmp, tmplen); fragments = std::deque((Fragment *)tmp, ((Fragment *)tmp) + (tmplen / PACKED_FRAGMENT_SIZE)); } if (trackRef.getMember("keys").getType() == DTSC_STR) { char * tmp = 0; - unsigned int tmplen = 0; + size_t tmplen = 0; trackRef.getMember("keys").getString(tmp, tmplen); keys = std::deque((Key *)tmp, ((Key *)tmp) + (tmplen / PACKED_KEY_SIZE)); } if (trackRef.getMember("parts").getType() == DTSC_STR) { char * tmp = 0; - unsigned int tmplen = 0; + size_t tmplen = 0; trackRef.getMember("parts").getString(tmp, tmplen); parts = std::deque((Part *)tmp, ((Part *)tmp) + (tmplen / 9)); } @@ -1209,7 +1215,7 @@ namespace DTSC { } if (trackRef.getMember("keysizes").getType() == DTSC_STR) { char * tmp = 0; - unsigned int tmplen = 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]); @@ -1490,7 +1496,7 @@ namespace DTSC { ///\brief Updates a meta object given a DTSC::Packet void Meta::update(const DTSC::Packet & pack, unsigned long segment_size) { char * data; - unsigned int dataLen; + 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")){ @@ -1501,7 +1507,7 @@ namespace DTSC { ///\brief Updates a meta object given a DTSC::Packet with byte position override. void Meta::updatePosOverride(DTSC::Packet & pack, uint64_t bpos) { char * data; - unsigned int dataLen; + 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()); } diff --git a/lib/ebml.cpp b/lib/ebml.cpp index 45a7abad..f5feb5e5 100644 --- a/lib/ebml.cpp +++ b/lib/ebml.cpp @@ -37,11 +37,8 @@ namespace EBML{ uint64_t UniInt::readInt(const char *p){ switch (readSize(p)){ case 1: - if (p[0] == 0xFF){ - return 0xFFFFFFFFFFFFFFFFull; - }else{ - return p[0] & 0x7F; - } + if (p[0] == 0xFF){return 0xFFFFFFFFFFFFFFFFull;} + return p[0] & 0x7F; case 2: return Bit::btohs(p) & 0x3FFFull; case 3: return Bit::btoh24(p) & 0x1FFFFFull; case 4: return Bit::btohl(p) & 0xFFFFFFFull; @@ -66,8 +63,8 @@ namespace EBML{ } } - /// Reads an EBML-encoded singed integer from a pointer. Expects the whole number to be readable without - /// bounds checking. + /// Reads an EBML-encoded singed integer from a pointer. Expects the whole number to be readable + /// without bounds checking. int64_t UniInt::readSInt(const char *p){ switch (readSize(p)){ case 1: return ((int64_t)readInt(p)) - 0x3Fll; @@ -99,9 +96,7 @@ namespace EBML{ // ELEM_MASTER types do not contain payload if minimal is true if (minimal && Element(p, true).getType() == ELEM_MASTER){return needed;} uint64_t pSize = UniInt::readInt(sizeOffset); - if (pSize != 0xFFFFFFFFFFFFFFFFull){ - needed += pSize; - } + if (pSize != 0xFFFFFFFFFFFFFFFFull){needed += pSize;} return needed; } @@ -193,7 +188,7 @@ namespace EBML{ case 0x487: return "TagString"; case 0x23C5: return "TagTrackUID"; case 0x43a770: return "Chapters"; - case 0x3a770: return "Chapters"; + case 0x3a770: return "Chapters"; case 0x941a469: return "Attachments"; case 0x8: return "FlagDefault"; case 0x461: return "DateUTC"; @@ -275,7 +270,7 @@ namespace EBML{ case 0x3373: case 0x23C0: case 0x43a770: - case 0x3a770: + case 0x3a770: case 0x941a469: case 0x21A7: case 0x5B9: @@ -476,7 +471,7 @@ namespace EBML{ case 6: val = Bit::btoh48(payDat); break; case 7: val = Bit::btoh56(payDat); break; case 8: val = Bit::btohll(payDat); break; - default: WARN_MSG("UInt payload size %llu not implemented", getPayloadLen()); + default: WARN_MSG("UInt payload size %" PRIu64 " not implemented", getPayloadLen()); } return val; } @@ -493,7 +488,7 @@ namespace EBML{ case 6: val = (((int64_t)Bit::btoh48(payDat)) << 16) >> 16; break; case 7: val = (((int64_t)Bit::btoh56(payDat)) << 8) >> 8; break; case 8: val = Bit::btohll(payDat); break; - default: WARN_MSG("Int payload size %llu not implemented", getPayloadLen()); + default: WARN_MSG("Int payload size %" PRIu64 " not implemented", getPayloadLen()); } return val; } @@ -504,21 +499,21 @@ namespace EBML{ switch (getPayloadLen()){ case 4: val = Bit::btohf(payDat); break; case 8: val = Bit::btohd(payDat); break; - default: WARN_MSG("Float payload size %llu not implemented", getPayloadLen()); + default: WARN_MSG("Float payload size %" PRIu64 " not implemented", getPayloadLen()); } return val; } std::string Element::getValString() const{ uint64_t strLen = getPayloadLen(); - const char * strPtr = getPayload(); - while (strLen && strPtr[strLen-1] == 0){--strLen;} + const char *strPtr = getPayload(); + while (strLen && strPtr[strLen - 1] == 0){--strLen;} return std::string(strPtr, strLen); } std::string Element::getValStringUntrimmed() const{ uint64_t strLen = getPayloadLen(); - const char * strPtr = getPayload(); + const char *strPtr = getPayload(); return std::string(strPtr, strLen); } @@ -549,50 +544,50 @@ namespace EBML{ uint32_t Block::getFrameSize(uint8_t no) const{ switch (getLacing()){ - case 0://No lacing - return getPayloadLen() - (UniInt::readSize(getPayload()) + 3); - case 1:{//Xiph lacing - uint64_t offset = (UniInt::readSize(getPayload()) + 3) + 1; - uint8_t frames = getFrameCount(); - if (no > frames - 1){return 0;}//out of bounds - uint64_t laceNo = 0; - uint32_t currSize = 0; - uint32_t totSize = 0; - while (laceNo <= no && (laceNo < frames-1) && offset < getPayloadLen()){ - currSize += getPayload()[offset]; - if (getPayload()[offset] != 255){ - totSize += currSize; - if (laceNo == no){return currSize;} - currSize = 0; - ++laceNo; - } - ++offset; - } - return getPayloadLen() - offset - totSize;//last frame is rest of the data - } - case 3:{//EBML lacing - const char * pl = getPayload(); - uint64_t offset = (UniInt::readSize(pl) + 3) + 1; - uint8_t frames = getFrameCount(); - if (no > frames - 1){return 0;}//out of bounds - uint64_t laceNo = 0; - uint32_t currSize = 0; - uint32_t totSize = 0; - while (laceNo <= no && (laceNo < frames-1) && offset < getPayloadLen()){ - if (laceNo == 0){ - currSize = UniInt::readInt(pl + offset); - }else{ - currSize += UniInt::readSInt(pl + offset); - } + case 0: // No lacing + return getPayloadLen() - (UniInt::readSize(getPayload()) + 3); + case 1:{// Xiph lacing + uint64_t offset = (UniInt::readSize(getPayload()) + 3) + 1; + uint8_t frames = getFrameCount(); + if (no > frames - 1){return 0;}// out of bounds + uint64_t laceNo = 0; + uint32_t currSize = 0; + uint32_t totSize = 0; + while (laceNo <= no && (laceNo < frames - 1) && offset < getPayloadLen()){ + currSize += getPayload()[offset]; + if (getPayload()[offset] != 255){ totSize += currSize; if (laceNo == no){return currSize;} + currSize = 0; ++laceNo; - offset += UniInt::readSize(pl + offset); } - return getPayloadLen() - offset - totSize;//last frame is rest of the data + ++offset; } - case 2://Fixed lacing - return (getPayloadLen() - (UniInt::readSize(getPayload()) + 3)) / getFrameCount(); + return getPayloadLen() - offset - totSize; // last frame is rest of the data + } + case 3:{// EBML lacing + const char *pl = getPayload(); + uint64_t offset = (UniInt::readSize(pl) + 3) + 1; + uint8_t frames = getFrameCount(); + if (no > frames - 1){return 0;}// out of bounds + uint64_t laceNo = 0; + uint32_t currSize = 0; + uint32_t totSize = 0; + while (laceNo <= no && (laceNo < frames - 1) && offset < getPayloadLen()){ + if (laceNo == 0){ + currSize = UniInt::readInt(pl + offset); + }else{ + currSize += UniInt::readSInt(pl + offset); + } + totSize += currSize; + if (laceNo == no){return currSize;} + ++laceNo; + offset += UniInt::readSize(pl + offset); + } + return getPayloadLen() - offset - totSize; // last frame is rest of the data + } + case 2: // Fixed lacing + return (getPayloadLen() - (UniInt::readSize(getPayload()) + 3)) / getFrameCount(); } WARN_MSG("Lacing type not yet implemented!"); return 0; @@ -600,49 +595,43 @@ namespace EBML{ const char *Block::getFrameData(uint8_t no) const{ switch (getLacing()){ - case 0://No lacing - return getPayload() + (UniInt::readSize(getPayload()) + 3); - case 1:{//Xiph lacing - uint64_t offset = (UniInt::readSize(getPayload()) + 3) + 1; - uint8_t frames = getFrameCount(); - if (no > frames - 1){return 0;}//out of bounds - uint64_t laceNo = 0; - uint32_t currSize = 0; - while ((laceNo < frames-1) && offset < getPayloadLen()){ - if (laceNo < no){ - currSize += getPayload()[offset]; - } - if (getPayload()[offset] != 255){ - ++laceNo; - } - ++offset; - } - return getPayload() + offset + currSize; + case 0: // No lacing + return getPayload() + (UniInt::readSize(getPayload()) + 3); + case 1:{// Xiph lacing + uint64_t offset = (UniInt::readSize(getPayload()) + 3) + 1; + uint8_t frames = getFrameCount(); + if (no > frames - 1){return 0;}// out of bounds + uint64_t laceNo = 0; + uint32_t currSize = 0; + while ((laceNo < frames - 1) && offset < getPayloadLen()){ + if (laceNo < no){currSize += getPayload()[offset];} + if (getPayload()[offset] != 255){++laceNo;} + ++offset; } - case 3:{//EBML lacing - const char * pl = getPayload(); - uint64_t offset = (UniInt::readSize(pl) + 3) + 1; - uint8_t frames = getFrameCount(); - if (no > frames - 1){return 0;}//out of bounds - uint64_t laceNo = 0; - uint32_t currSize = 0; - uint32_t totSize = 0; - while ((laceNo < frames-1) && offset < getPayloadLen()){ - if (laceNo == 0){ - currSize = UniInt::readInt(pl + offset); - }else{ - currSize += UniInt::readSInt(pl + offset); - } - if (laceNo < no){ - totSize += currSize; - } - ++laceNo; - offset += UniInt::readSize(pl + offset); + return getPayload() + offset + currSize; + } + case 3:{// EBML lacing + const char *pl = getPayload(); + uint64_t offset = (UniInt::readSize(pl) + 3) + 1; + uint8_t frames = getFrameCount(); + if (no > frames - 1){return 0;}// out of bounds + uint64_t laceNo = 0; + uint32_t currSize = 0; + uint32_t totSize = 0; + while ((laceNo < frames - 1) && offset < getPayloadLen()){ + if (laceNo == 0){ + currSize = UniInt::readInt(pl + offset); + }else{ + currSize += UniInt::readSInt(pl + offset); } - return pl + offset + totSize; + if (laceNo < no){totSize += currSize;} + ++laceNo; + offset += UniInt::readSize(pl + offset); } - case 2://Fixed lacing - return getPayload() + (UniInt::readSize(getPayload()) + 3) + 1 + no * getFrameSize(no); + return pl + offset + totSize; + } + case 2: // Fixed lacing + return getPayload() + (UniInt::readSize(getPayload()) + 3) + 1 + no * getFrameSize(no); } WARN_MSG("Lacing type not yet implemented!"); return 0; @@ -657,8 +646,7 @@ namespace EBML{ if (isInvisible()){ret << " [Invisible]";} if (isDiscardable()){ret << " [Discardable]";} switch (getLacing()){ - case 0: - break; // No lacing + case 0: break; // No lacing case 1: ret << " [Lacing: Xiph]"; break; case 3: ret << " [Lacing: EMBL]"; break; case 2: ret << " [Lacing: Fixed]"; break; @@ -668,7 +656,8 @@ namespace EBML{ for (uint32_t frameNo = 0; frameNo < getFrameCount(); ++frameNo){ const char *payDat = getFrameData(frameNo); const uint64_t payLen = getFrameSize(frameNo); - ret << std::dec << std::string(indent + 4, ' ') << "Frame " << (frameNo+1) << " (" << payLen << "b):"; + ret << std::dec << std::string(indent + 4, ' ') << "Frame " << (frameNo + 1) << " (" + << payLen << "b):"; if (!payDat || !payLen || detail < 6){ ret << std::endl; continue; @@ -693,5 +682,5 @@ namespace EBML{ ret << std::endl; return ret.str(); } -} +}// namespace EBML diff --git a/lib/ebml.h b/lib/ebml.h index ff1668b1..ea46301a 100644 --- a/lib/ebml.h +++ b/lib/ebml.h @@ -102,7 +102,7 @@ namespace EBML{ private: const char *data; - bool minimalMode; ///= 0x100ull){ return 2; - }else{ - return 1; } + return 1; } uint32_t sizeElemUInt(uint32_t ID, const uint64_t val){ @@ -66,21 +65,20 @@ namespace EBML{ char tmp[8]; uint8_t wSize = sizeUInt(val); switch (wSize){ - case 8: Bit::htobll(tmp, val); break; - case 7: Bit::htob56(tmp, val); break; - case 6: Bit::htob48(tmp, val); break; - case 5: Bit::htob40(tmp, val); break; - case 4: Bit::htobl(tmp, val); break; - case 3: Bit::htob24(tmp, val); break; - case 2: Bit::htobs(tmp, val); break; - case 1: tmp[0] = val; break; + case 8: Bit::htobll(tmp, val); break; + case 7: Bit::htob56(tmp, val); break; + case 6: Bit::htob48(tmp, val); break; + case 5: Bit::htob40(tmp, val); break; + case 4: Bit::htobl(tmp, val); break; + case 3: Bit::htob24(tmp, val); break; + case 2: Bit::htobs(tmp, val); break; + case 1: tmp[0] = val; break; } sendElemHead(C, ID, wSize); C.SendNow(tmp, wSize); } void sendElemID(Socket::Connection &C, uint32_t ID, const uint64_t val){ - char tmp[8]; uint8_t wSize = UniInt::writeSize(val); sendElemHead(C, ID, wSize); sendUniInt(C, val); @@ -90,8 +88,8 @@ namespace EBML{ char tmp[8]; uint8_t wSize = (val == (float)val) ? 4 : 8; switch (wSize){ - case 4: Bit::htobf(tmp, val); break; - case 8: Bit::htobd(tmp, val); break; + case 4: Bit::htobf(tmp, val); break; + case 8: Bit::htobd(tmp, val); break; } sendElemHead(C, ID, wSize); C.SendNow(tmp, wSize); @@ -119,11 +117,11 @@ namespace EBML{ } void sendElemInfo(Socket::Connection &C, const std::string &appName, double duration){ - sendElemHead(C, EID_INFO, 13 + 2 * appName.size() + (duration>0?sizeElemDbl(EID_DURATION, duration):0)); + sendElemHead(C, EID_INFO, + 13 + 2 * appName.size() + + (duration > 0 ? sizeElemDbl(EID_DURATION, duration) : 0)); sendElemUInt(C, EID_TIMECODESCALE, 1000000); - if (duration > 0){ - sendElemDbl(C, EID_DURATION, duration); - } + if (duration > 0){sendElemDbl(C, EID_DURATION, duration);} sendElemStr(C, EID_MUXINGAPP, appName); sendElemStr(C, EID_WRITINGAPP, appName); } @@ -133,24 +131,23 @@ namespace EBML{ } uint32_t sizeElemInfo(const std::string &appName, double duration){ - return 13 + 2 * appName.size() + (duration>0?sizeElemDbl(EID_DURATION, duration):0) + sizeElemHead(EID_INFO, 13 + 2 * appName.size() + (duration>0?sizeElemDbl(EID_DURATION, duration):0)); + return 13 + 2 * appName.size() + (duration > 0 ? sizeElemDbl(EID_DURATION, duration) : 0) + + sizeElemHead(EID_INFO, 13 + 2 * appName.size() + + (duration > 0 ? sizeElemDbl(EID_DURATION, duration) : 0)); } - void sendSimpleBlock(Socket::Connection &C, DTSC::Packet & pkt, uint64_t clusterTime, bool forceKeyframe){ - unsigned int dataLen = 0; - char * dataPointer = 0; + void sendSimpleBlock(Socket::Connection &C, DTSC::Packet &pkt, uint64_t clusterTime, + bool forceKeyframe){ + size_t dataLen = 0; + char *dataPointer = 0; pkt.getString("data", dataPointer, dataLen); uint32_t blockSize = UniInt::writeSize(pkt.getTrackId()) + 3 + dataLen; sendElemHead(C, EID_SIMPLEBLOCK, blockSize); sendUniInt(C, pkt.getTrackId()); - char blockHead[3] = {0, 0, 0}; - if (pkt.hasMember("keyframe") || forceKeyframe){ - blockHead[2] = 0x80; - } + char blockHead[3] ={0, 0, 0}; + if (pkt.hasMember("keyframe") || forceKeyframe){blockHead[2] = 0x80;} int offset = 0; - if (pkt.hasMember("offset")){ - offset = pkt.getInt("offset"); - } + if (pkt.hasMember("offset")){offset = pkt.getInt("offset");} Bit::htobs(blockHead, (int16_t)(pkt.getTime() + offset - clusterTime)); C.SendNow(blockHead, 3); C.SendNow(dataPointer, dataLen); @@ -173,7 +170,8 @@ namespace EBML{ return sizeElemHead(EID_SEEK, elems) + elems; } - void sendElemCuePoint(Socket::Connection &C, uint64_t time, uint64_t track, uint64_t clusterPos, uint64_t relaPos){ + void sendElemCuePoint(Socket::Connection &C, uint64_t time, uint64_t track, uint64_t clusterPos, + uint64_t relaPos){ uint32_t elemsA = 0, elemsB = 0; elemsA += sizeElemUInt(EID_CUETRACK, track); elemsA += sizeElemUInt(EID_CUECLUSTERPOSITION, clusterPos); @@ -197,6 +195,5 @@ namespace EBML{ return sizeElemHead(EID_CUEPOINT, elems) + elems; } - -} +}// namespace EBML diff --git a/lib/ebml_socketglue.h b/lib/ebml_socketglue.h index a6bf284e..8d6cd3c6 100644 --- a/lib/ebml_socketglue.h +++ b/lib/ebml_socketglue.h @@ -1,10 +1,10 @@ -#include "ebml.h" -#include "socket.h" #include "bitfields.h" #include "dtsc.h" +#include "ebml.h" +#include "socket.h" namespace EBML{ - static void sendUniInt(Socket::Connection &C, const uint64_t val); + void sendUniInt(Socket::Connection &C, const uint64_t val); void sendElemHead(Socket::Connection &C, uint32_t ID, const uint64_t size); void sendElemUInt(Socket::Connection &C, uint32_t ID, const uint64_t val); void sendElemID(Socket::Connection &C, uint32_t ID, const uint64_t val); @@ -17,7 +17,8 @@ namespace EBML{ void sendElemSeek(Socket::Connection &C, uint32_t ID, uint64_t bytePos); uint32_t sizeElemSeek(uint32_t ID, uint64_t bytePos); - void sendElemCuePoint(Socket::Connection &C, uint64_t time, uint64_t track, uint64_t clusterPos, uint64_t relaPos); + void sendElemCuePoint(Socket::Connection &C, uint64_t time, uint64_t track, uint64_t clusterPos, + uint64_t relaPos); uint32_t sizeElemCuePoint(uint64_t time, uint64_t track, uint64_t clusterPos, uint64_t relaPos); uint8_t sizeUInt(const uint64_t val); @@ -27,7 +28,8 @@ namespace EBML{ uint32_t sizeElemDbl(uint32_t ID, const double val); uint32_t sizeElemStr(uint32_t ID, const std::string &val); - void sendSimpleBlock(Socket::Connection &C, DTSC::Packet & pkt, uint64_t clusterTime, bool forceKeyframe = false); + void sendSimpleBlock(Socket::Connection &C, DTSC::Packet &pkt, uint64_t clusterTime, + bool forceKeyframe = false); uint32_t sizeSimpleBlock(uint64_t trackId, uint32_t dataSize); -} +}// namespace EBML diff --git a/lib/encode.cpp b/lib/encode.cpp index 7af00bfb..ea322f2a 100644 --- a/lib/encode.cpp +++ b/lib/encode.cpp @@ -80,6 +80,12 @@ namespace Encodings{ return r; } + std::string Hex::encode(const std::string &in){ + std::string res; + for (size_t i = 0; i < in.size(); i++){res += chr(in[i]);} + return res; + } + /// Decodes a hex-encoded std::string to a raw binary std::string. std::string Hex::decode(const std::string &in){ std::string ret(in.size() / 2, '\000'); @@ -97,8 +103,10 @@ namespace Encodings{ for (int i = 0; i < max; i++){ if (('0' <= c[i] && c[i] <= '9') || ('a' <= c[i] && c[i] <= 'z') || ('A' <= c[i] && c[i] <= 'Z') || - (c[i] == '$' || c[i] == '-' || c[i] == '_' || c[i] == '.' || c[i] == ',' || c[i] == '!' || c[i] == '~' || c[i] == ';' || - c[i] == '*' || c[i] == '(' || c[i] == ')' || c[i] == '\'') || (ign.size() && ign.find(c[i]) != std::string::npos)){ + (c[i] == '$' || c[i] == '-' || c[i] == '_' || c[i] == '.' || c[i] == ',' || c[i] == '!' || + c[i] == '~' || c[i] == ';' || c[i] == '*' || c[i] == '(' || c[i] == ')' || + c[i] == '\'') || + (ign.size() && ign.find(c[i]) != std::string::npos)){ escaped.append(&c[i], 1); }else{ if (c[i] == ' '){ diff --git a/lib/encode.h b/lib/encode.h index 6d4b8028..3eaa65c5 100644 --- a/lib/encode.h +++ b/lib/encode.h @@ -2,41 +2,42 @@ #include /// Namespace for character encoding functions and classes -namespace Encodings { +namespace Encodings{ /// Holds base64 decoding and encoding functions. - class Base64 { - private: - static const std::string chars; - static inline bool is_base64(unsigned char c); - public: - static std::string encode(std::string const input); - static std::string decode(std::string const & encoded_string); + class Base64{ + private: + static const std::string chars; + static inline bool is_base64(unsigned char c); + + public: + static std::string encode(std::string const input); + static std::string decode(std::string const &encoded_string); }; /// urlencoding and urldecoding functions - class URL { - public: - /// urldecodes std::string data, parsing out both %-encoded characters and +-encoded spaces. - static std::string decode(const std::string & in); - /// urlencodes std::string data, leaving only the characters A-Za-z0-9~!&()' alone. - static std::string encode(const std::string & c, const std::string &ign = ""); - + class URL{ + public: + /// urldecodes std::string data, parsing out both %-encoded characters and +-encoded spaces. + static std::string decode(const std::string &in); + /// urlencodes std::string data, leaving only the characters A-Za-z0-9~!&()' alone. + static std::string encode(const std::string &c, const std::string &ign = ""); }; /// Hexadecimal-related functions - class Hex { - public: + class Hex{ + public: /// Decodes a single hexadecimal character to integer, case-insensitive. - static inline int ord(char c){ - return ((c&15) + (((c&64)>>6) | ((c&64)>>3))); - } + static inline int ord(char c){return ((c & 15) + (((c & 64) >> 6) | ((c & 64) >> 3)));} /// Encodes a single character as two hex digits in string form. static std::string chr(char dec); /// Decodes a hex-encoded std::string to a raw binary std::string. - static std::string decode(const std::string & in); + static std::string decode(const std::string &in); + + /// Encodes a raw binary std::string to its hex-encoded form + static std::string encode(const std::string &in); }; -} +}// namespace Encodings diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 4156374b..8f98d0a0 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -1,26 +1,26 @@ /// \file flv_tag.cpp /// Holds all code for the FLV namespace. +#include "flv_tag.h" #include "defines.h" #include "rtmpchunks.h" -#include "flv_tag.h" #include "timing.h" #include "util.h" -#include //for Tag::FileLoader -#include //for Tag::FileLoader #include //for Tag::FileLoader +#include +#include //for Tag::FileLoader #include //malloc #include //memcpy -#include - +#include //for Tag::FileLoader #include "h264.h" //Needed for init data parsing in case of invalid values from FLV init /// Holds the last FLV header parsed. /// Defaults to a audio+video header on FLV version 0x01 if no header received yet. -char FLV::Header[13] = {'F', 'L', 'V', 0x01, 0x05, 0, 0, 0, 0x09, 0, 0, 0, 0}; +char FLV::Header[13] ={'F', 'L', 'V', 0x01, 0x05, 0, 0, 0, 0x09, 0, 0, 0, 0}; -bool FLV::Parse_Error = false; ///< This variable is set to true if a problem is encountered while parsing the FLV. +bool FLV::Parse_Error = + false; ///< This variable is set to true if a problem is encountered while parsing the FLV. std::string FLV::Error_Str = ""; /// Checks a FLV Header for validness. Returns true if the header is valid, false @@ -30,7 +30,7 @@ std::string FLV::Error_Str = ""; /// - The PreviousTagSize is not 0 bytes. /// /// Note that we see PreviousTagSize as part of the FLV header, not part of the tag header! -bool FLV::check_header(char * header) { +bool FLV::check_header(char *header){ if (header[0] != 'F') return false; if (header[1] != 'L') return false; if (header[2] != 'V') return false; @@ -43,52 +43,45 @@ bool FLV::check_header(char * header) { if (header[11] != 0) return false; if (header[12] != 0) return false; return true; -} //FLV::check_header +}// FLV::check_header /// Checks the first 3 bytes for the string "FLV". Implementing a basic FLV header check, /// returning true if it is, false if not. -bool FLV::is_header(char * header) { +bool FLV::is_header(char *header){ if (header[0] != 'F') return false; if (header[1] != 'L') return false; if (header[2] != 'V') return false; return true; -} //FLV::is_header - +}// FLV::is_header /// Helper function that can quickly skip through a file looking for a particular tag type -bool FLV::seekToTagType(FILE * f, uint8_t t){ +bool FLV::seekToTagType(FILE *f, uint8_t t){ long long startPos = Util::ftell(f); DONTEVEN_MSG("Starting seek at %lld", startPos); char buf[4]; if (fread(buf, 4, 1, f) != 1){return false;} while (!feof(f) && !ferror(f)){ switch (buf[0]){ - case 0x09: - case 0x08: - case 0x12: - { - if (t == buf[0]){ - if (fseek(f, -4, SEEK_CUR)){ - WARN_MSG("Could not seek back in FLV stream!"); - } - INSANE_MSG("Found tag of type %u at %lld", t, Util::ftell(f)); - return true; - } - long len = (buf[1] << 16) | (buf[2] << 8) | buf[3]; - if (fseek(f, len+11, SEEK_CUR)){ - WARN_MSG("Could not seek forward in FLV stream!"); - }else{ - DONTEVEN_MSG("Seeking %ld+4 bytes forward, now at %lld", len+11, Util::ftell(f)); - } - if (fread(buf, 4, 1, f) != 1){return false;} - } - break; - default: - WARN_MSG("Invalid FLV tag detected! Aborting search."); - if (fseek(f, -4, SEEK_CUR)){ - WARN_MSG("Could not seek back in FLV stream!"); - } - return false; + case 0x09: + case 0x08: + case 0x12:{ + if (t == buf[0]){ + if (fseek(f, -4, SEEK_CUR)){WARN_MSG("Could not seek back in FLV stream!");} + INSANE_MSG("Found tag of type %u at %" PRIu64, t, Util::ftell(f)); + return true; + } + long len = (buf[1] << 16) | (buf[2] << 8) | buf[3]; + if (fseek(f, len + 11, SEEK_CUR)){ + WARN_MSG("Could not seek forward in FLV stream!"); + }else{ + DONTEVEN_MSG("Seeking %ld+4 bytes forward, now at %" PRIu64, len + 11, Util::ftell(f)); + } + if (fread(buf, 4, 1, f) != 1){return false;} + }break; + default: + WARN_MSG("Invalid FLV tag detected! Aborting search."); + if (fseek(f, -4, SEEK_CUR)){WARN_MSG("Could not seek back in FLV stream!");} + return false; } } return false; @@ -98,122 +91,82 @@ bool FLV::seekToTagType(FILE * f, uint8_t t){ /// Will always return false if the tag type is not 0x08 or 0x09. /// Returns true for H263, AVC (H264), AAC. /// \todo Check if MP3 does or does not require init data... -bool FLV::Tag::needsInitData() { - switch (data[0]) { - case 0x09: - switch (data[11] & 0x0F) { - case 2: - return true; - break; //H263 requires init data - case 7: - return true; - break; //AVC requires init data - default: - return false; - break; //other formats do not - } - break; - case 0x08: - switch (data[11] & 0xF0) { - case 0x20: - return false; - break; //MP3 does not...? Unsure. - case 0xA0: - return true; - break; //AAC requires init data - case 0xE0: - return false; - break; //MP38kHz does not...? - default: - return false; - break; //other formats do not - } - break; +bool FLV::Tag::needsInitData(){ + switch (data[0]){ + case 0x09: + switch (data[11] & 0x0F){ + case 2: return true; break; // H263 requires init data + case 7: return true; break; // AVC requires init data + default: return false; break; // other formats do not + } + break; + case 0x08: + switch (data[11] & 0xF0){ + case 0x20: return false; break; // MP3 does not...? Unsure. + case 0xA0: return true; break; // AAC requires init data + case 0xE0: return false; break; // MP38kHz does not...? + default: return false; break; // other formats do not + } + break; } - return false; //only audio/video can require init data + return false; // only audio/video can require init data } /// True if current tag is init data for this media type. -bool FLV::Tag::isInitData() { - switch (data[0]) { - case 0x09: - switch (data[11] & 0xF0) { - case 0x50: - return true; - break; +bool FLV::Tag::isInitData(){ + switch (data[0]){ + case 0x09: + switch (data[11] & 0xF0){ + case 0x50: return true; break; + } + if ((data[11] & 0x0F) == 7){ + switch (data[12]){ + case 0: return true; break; } - if ((data[11] & 0x0F) == 7) { - switch (data[12]) { - case 0: - return true; - break; - } - } - break; - case 0x08: - if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)) { - return true; - } - break; + } + break; + case 0x08: + if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){return true;} + break; } return false; } -const char * FLV::Tag::getVideoCodec() { - switch (data[11] & 0x0F) { - case 1: - return "JPEG"; - case 2: - return "H263"; - case 3: - return "ScreenVideo1"; - case 4: - return "VP6"; - case 5: - return "VP6Alpha"; - case 6: - return "ScreenVideo2"; - case 7: - return "H264"; - default: - return "unknown"; +const char *FLV::Tag::getVideoCodec(){ + switch (data[11] & 0x0F){ + case 1: return "JPEG"; + case 2: return "H263"; + case 3: return "ScreenVideo1"; + case 4: return "VP6"; + case 5: return "VP6Alpha"; + case 6: return "ScreenVideo2"; + case 7: return "H264"; + default: return "unknown"; } } -const char * FLV::Tag::getAudioCodec() { - switch (data[11] & 0xF0) { - case 0x00: - if (data[11] & 0x02){ - return "PCMPE";//unknown endianness - }else{ - return "PCM";//8 bit is always regular PCM - } - case 0x10: - return "ADPCM"; - case 0x20: - return "MP3"; - case 0x30: - return "PCM"; - case 0x40: - case 0x50: - case 0x60: - return "Nellymoser"; - case 0x70: - return "ALAW"; - case 0x80: - return "ULAW"; - case 0x90: - return "reserved"; - case 0xA0: - return "AAC"; - case 0xB0: - return "Speex"; - case 0xE0: - return "MP3"; - case 0xF0: - return "DeviceSpecific"; - default: - return "unknown"; +const char *FLV::Tag::getAudioCodec(){ + switch (data[11] & 0xF0){ + case 0x00: + if (data[11] & 0x02){ + return "PCMPE"; // unknown endianness + }else{ + return "PCM"; // 8 bit is always regular PCM + } + case 0x10: return "ADPCM"; + case 0x20: return "MP3"; + case 0x30: return "PCM"; + case 0x40: + case 0x50: + case 0x60: return "Nellymoser"; + case 0x70: return "ALAW"; + case 0x80: return "ULAW"; + case 0x90: return "reserved"; + case 0xA0: return "AAC"; + case 0xB0: return "Speex"; + case 0xE0: return "MP3"; + case 0xF0: return "DeviceSpecific"; + default: return "unknown"; } } @@ -221,158 +174,117 @@ const char * FLV::Tag::getAudioCodec() { /// The string includes information about whether the tag is /// audio, video or metadata, what encoding is used, and the details /// of the encoding itself. -std::string FLV::Tag::tagType() { +std::string FLV::Tag::tagType(){ std::stringstream R; R << len << " bytes of "; - switch (data[0]) { - case 0x09: - R << getVideoCodec() << " video "; - switch (data[11] & 0xF0) { - case 0x10: - R << "keyframe"; - break; - case 0x20: - R << "iframe"; - break; - case 0x30: - R << "disposableiframe"; - break; - case 0x40: - R << "generatedkeyframe"; - break; - case 0x50: - R << "videoinfo"; - break; + switch (data[0]){ + case 0x09: + R << getVideoCodec() << " video "; + switch (data[11] & 0xF0){ + case 0x10: R << "keyframe"; break; + case 0x20: R << "iframe"; break; + case 0x30: R << "disposableiframe"; break; + case 0x40: R << "generatedkeyframe"; break; + case 0x50: R << "videoinfo"; break; + } + if ((data[11] & 0x0F) == 7){ + switch (data[12]){ + case 0: R << " header"; break; + case 1: R << " NALU"; break; + case 2: R << " endofsequence"; break; } - if ((data[11] & 0x0F) == 7) { - switch (data[12]) { - case 0: - R << " header"; - break; - case 1: - R << " NALU"; - break; - case 2: - R << " endofsequence"; - break; - } - } - break; - case 0x08: - R << getAudioCodec(); - switch (data[11] & 0x0C) { - case 0x0: - R << " 5.5kHz"; - break; - case 0x4: - R << " 11kHz"; - break; - case 0x8: - R << " 22kHz"; - break; - case 0xC: - R << " 44kHz"; - break; - } - switch (data[11] & 0x02) { - case 0: - R << " 8bit"; - break; - case 2: - R << " 16bit"; - break; - } - switch (data[11] & 0x01) { - case 0: - R << " mono"; - break; - case 1: - R << " stereo"; - break; - } - R << " audio"; - if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)) { - R << " initdata"; - } - break; - case 0x12: { - R << "(meta)data: "; - AMF::Object metadata = AMF::parse((unsigned char *)data + 11, len - 15); - R << metadata.Print(); - break; - } - default: - R << "unknown"; - break; + } + break; + case 0x08: + R << getAudioCodec(); + switch (data[11] & 0x0C){ + case 0x0: R << " 5.5kHz"; break; + case 0x4: R << " 11kHz"; break; + case 0x8: R << " 22kHz"; break; + case 0xC: R << " 44kHz"; break; + } + switch (data[11] & 0x02){ + case 0: R << " 8bit"; break; + case 2: R << " 16bit"; break; + } + switch (data[11] & 0x01){ + case 0: R << " mono"; break; + case 1: R << " stereo"; break; + } + R << " audio"; + if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){R << " initdata";} + break; + case 0x12:{ + R << "(meta)data: "; + AMF::Object metadata = AMF::parse((unsigned char *)data + 11, len - 15); + R << metadata.Print(); + break; + } + default: R << "unknown"; break; } return R.str(); -} //FLV::Tag::tagtype +}// FLV::Tag::tagtype /// Returns the 24-bit offset of this tag. /// Returns 0 if the tag isn't H264 -int FLV::Tag::offset() { - if ((data[11] & 0x0F) == 7) { - return (((data[13] << 16) + (data[14] << 8) + data[15]) << 8) >> 8; - } else { - return 0; - } -} //offset getter +int64_t FLV::Tag::offset(){ + if ((data[11] & 0x0F) != 7){return 0;} + return (((data[13] << 16) + (data[14] << 8) + data[15]) << 8) >> 8; +}// offset getter /// Sets the 24-bit offset of this tag. /// Ignored if the tag isn't H264 -void FLV::Tag::offset(int o) { +void FLV::Tag::offset(int64_t o){ data[13] = (o >> 16) & 0xFF; data[14] = (o >> 8) & 0XFF; data[15] = o & 0xFF; -} //offset setter +}// offset setter /// Returns the 32-bit timestamp of this tag. -unsigned int FLV::Tag::tagTime() { - return (data[4] << 16) + (data[5] << 8) + data[6] + (data[7] << 24); -} //tagTime getter +uint64_t FLV::Tag::tagTime(){ + return ((uint64_t)data[4] << 16) + ((uint64_t)data[5] << 8) + data[6] + ((uint64_t)data[7] << 24); +}// tagTime getter /// Sets the 32-bit timestamp of this tag. -void FLV::Tag::tagTime(unsigned int T) { +void FLV::Tag::tagTime(uint64_t T){ data[4] = ((T >> 16) & 0xFF); data[5] = ((T >> 8) & 0xFF); data[6] = (T & 0xFF); data[7] = ((T >> 24) & 0xFF); -} //tagTime setter +}// tagTime setter /// Constructor for a new, empty, tag. /// The buffer length is initialized to 0, and later automatically /// increased if neccesary. -FLV::Tag::Tag() { +FLV::Tag::Tag(){ len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; -} //empty constructor +}// empty constructor /// Copy constructor, copies the contents of an existing tag. /// The buffer length is initialized to the actual size of the tag /// that is being copied, and later automaticallt increased if /// neccesary. -FLV::Tag::Tag(const Tag & O) { +FLV::Tag::Tag(const Tag &O){ done = true; sofar = 0; len = O.len; data = 0; - if (len > 0) { - if (checkBufferSize()) { - memcpy(data, O.data, len); - } + if (len > 0){ + if (checkBufferSize()){memcpy(data, O.data, len);} } isKeyframe = O.isKeyframe; -} //copy constructor +}// copy constructor /// Copy constructor from a RTMP chunk. /// Copies the contents of a RTMP chunk into a valid FLV tag. /// Exactly the same as making a chunk by through the default (empty) constructor /// and then calling FLV::Tag::ChunkLoader with the chunk as argument. -FLV::Tag::Tag(const RTMPStream::Chunk & O) { +FLV::Tag::Tag(const RTMPStream::Chunk &O){ len = 0; buf = 0; data = 0; @@ -383,8 +295,8 @@ FLV::Tag::Tag(const RTMPStream::Chunk & O) { } /// Generic destructor that frees the allocated memory in the internal data variable, if any. -FLV::Tag::~Tag() { - if (data) { +FLV::Tag::~Tag(){ + if (data){ free(data); data = 0; buf = 0; @@ -394,155 +306,107 @@ FLV::Tag::~Tag() { /// Assignment operator - works exactly like the copy constructor. /// This operator checks for self-assignment. -FLV::Tag & FLV::Tag::operator=(const FLV::Tag & O) { - if (this != &O) { //no self-assignment +FLV::Tag &FLV::Tag::operator=(const FLV::Tag & O){ + if (this != &O){ //no self-assignment done = true; sofar = 0; len = O.len; - if (len > 0) { - if (checkBufferSize()) { + if (len > 0){ + if (checkBufferSize()){ memcpy(data, O.data, len); - } else { + }else{ len = buf; } } isKeyframe = O.isKeyframe; } return *this; -} //assignment operator +}// assignment operator -bool FLV::Tag::DTSCLoader(DTSC::Packet & packData, DTSC::Track & track) { +bool FLV::Tag::DTSCLoader(DTSC::Packet &packData, DTSC::Track &track){ std::string meta_str; len = 0; - if (track.type == "video") { - char * tmpData = 0; - unsigned int tmpLen = 0; + if (track.type == "video"){ + char *tmpData = 0; + size_t tmpLen = 0; packData.getString("data", tmpData, tmpLen); len = tmpLen + 16; - if (track.codec == "H264") { - len += 4; - } - if (!checkBufferSize()) { - return false; - } - if (track.codec == "H264") { + if (track.codec == "H264"){len += 4;} + if (!checkBufferSize()){return false;} + if (track.codec == "H264"){ memcpy(data + 16, tmpData, len - 20); data[12] = 1; offset(packData.getInt("offset")); - } else { + }else{ memcpy(data + 12, tmpData, len - 16); } data[11] = 0; - if (track.codec == "H264") { - data[11] |= 7; - } - if (track.codec == "ScreenVideo2") { - data[11] |= 6; - } - if (track.codec == "VP6Alpha") { - data[11] |= 5; - } - if (track.codec == "VP6") { - data[11] |= 4; - } - if (track.codec == "ScreenVideo1") { - data[11] |= 3; - } - if (track.codec == "H263") { - data[11] |= 2; - } - if (track.codec == "JPEG") { - data[11] |= 1; - } - if (packData.getFlag("keyframe")) { + if (track.codec == "H264"){data[11] |= 7;} + if (track.codec == "ScreenVideo2"){data[11] |= 6;} + if (track.codec == "VP6Alpha"){data[11] |= 5;} + if (track.codec == "VP6"){data[11] |= 4;} + if (track.codec == "ScreenVideo1"){data[11] |= 3;} + if (track.codec == "H263"){data[11] |= 2;} + if (track.codec == "JPEG"){data[11] |= 1;} + if (packData.getFlag("keyframe")){ data[11] |= 0x10; - } else { + }else{ data[11] |= 0x20; } - if (packData.getFlag("disposableframe")) { - data[11] |= 0x30; - } + if (packData.getFlag("disposableframe")){data[11] |= 0x30;} } - if (track.type == "audio") { - char * tmpData = 0; - unsigned int tmpLen = 0; + if (track.type == "audio"){ + char *tmpData = 0; + size_t tmpLen = 0; packData.getString("data", tmpData, tmpLen); len = tmpLen + 16; - if (track.codec == "AAC") { - len ++; - } - if (!checkBufferSize()) { - return false; - } - if (track.codec == "AAC") { + if (track.codec == "AAC"){len++;} + if (!checkBufferSize()){return false;} + if (track.codec == "AAC"){ memcpy(data + 13, tmpData, len - 17); - data[12] = 1; //raw AAC data, not sequence header - } else { + data[12] = 1; // raw AAC data, not sequence header + }else{ memcpy(data + 12, tmpData, len - 16); } unsigned int datarate = track.rate; data[11] = 0; - if (track.codec == "AAC") { - data[11] |= 0xA0; - } - if (track.codec == "MP3") { + if (track.codec == "AAC"){data[11] |= 0xA0;} + if (track.codec == "MP3"){ if (datarate == 8000){ data[11] |= 0xE0; }else{ data[11] |= 0x20; } } - if (track.codec == "ADPCM") { - data[11] |= 0x10; - } - if (track.codec == "PCM") { - data[11] |= 0x30; - } - if (track.codec == "Nellymoser") { + if (track.codec == "ADPCM"){data[11] |= 0x10;} + if (track.codec == "PCM"){data[11] |= 0x30;} + if (track.codec == "Nellymoser"){ if (datarate == 8000){ data[11] |= 0x50; - }else if(datarate == 16000){ + }else if (datarate == 16000){ data[11] |= 0x40; }else{ data[11] |= 0x60; } } - if (track.codec == "ALAW") { - data[11] |= 0x70; - } - if (track.codec == "ULAW") { - data[11] |= 0x80; - } - if (track.codec == "Speex") { - data[11] |= 0xB0; - } - if (datarate >= 44100) { + if (track.codec == "ALAW"){data[11] |= 0x70;} + if (track.codec == "ULAW"){data[11] |= 0x80;} + if (track.codec == "Speex"){data[11] |= 0xB0;} + if (datarate >= 44100){ data[11] |= 0x0C; - } else if (datarate >= 22050) { + }else if (datarate >= 22050){ data[11] |= 0x08; - } else if (datarate >= 11025) { + }else if (datarate >= 11025){ data[11] |= 0x04; } - if (track.size != 8) { - data[11] |= 0x02; - } - if (track.channels > 1) { - data[11] |= 0x01; - } - } - if (!len) { - return false; + if (track.size != 8){data[11] |= 0x02;} + if (track.channels > 1){data[11] |= 0x01;} } + if (!len){return false;} setLen(); - if (track.type == "video") { - data[0] = 0x09; - } - if (track.type == "audio") { - data[0] = 0x08; - } - if (track.type == "meta") { - data[0] = 0x12; - } + if (track.type == "video"){data[0] = 0x09;} + if (track.type == "audio"){data[0] = 0x08;} + if (track.type == "meta"){data[0] = 0x12;} data[1] = ((len - 15) >> 16) & 0xFF; data[2] = ((len - 15) >> 8) & 0xFF; data[3] = (len - 15) & 0xFF; @@ -554,37 +418,31 @@ bool FLV::Tag::DTSCLoader(DTSC::Packet & packData, DTSC::Track & track) { } /// Helper function that properly sets the tag length from the internal len variable. -void FLV::Tag::setLen() { +void FLV::Tag::setLen(){ int len4 = len - 4; int i = len; - data[ --i] = (len4) & 0xFF; + data[--i] = (len4)&0xFF; len4 >>= 8; - data[ --i] = (len4) & 0xFF; + data[--i] = (len4)&0xFF; len4 >>= 8; - data[ --i] = (len4) & 0xFF; + data[--i] = (len4)&0xFF; len4 >>= 8; - data[ --i] = (len4) & 0xFF; + data[--i] = (len4)&0xFF; } /// FLV Video init data loader function from metadata. -bool FLV::Tag::DTSCVideoInit(DTSC::Track & video) { - //Unknown? Assume H264. +bool FLV::Tag::DTSCVideoInit(DTSC::Track &video){ + // Unknown? Assume H264. len = 0; - if (video.codec == "?") { - video.codec = "H264"; - } - if (video.codec == "H264") { - len = video.init.size() + 20; - } - if (len <= 0 || !checkBufferSize()) { - return false; - } + if (video.codec == "?"){video.codec = "H264";} + if (video.codec == "H264"){len = video.init.size() + 20;} + if (len <= 0 || !checkBufferSize()){return false;} memcpy(data + 16, video.init.c_str(), len - 20); - data[12] = 0; //H264 sequence header + data[12] = 0; // H264 sequence header data[13] = 0; data[14] = 0; data[15] = 0; - data[11] = 0x17; //H264 keyframe (0x07 & 0x10) + data[11] = 0x17; // H264 keyframe (0x07 & 0x10) setLen(); data[0] = 0x09; data[1] = ((len - 15) >> 16) & 0xFF; @@ -598,41 +456,27 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Track & video) { } /// FLV Audio init data loader function from metadata. -bool FLV::Tag::DTSCAudioInit(DTSC::Track & audio) { +bool FLV::Tag::DTSCAudioInit(DTSC::Track &audio){ len = 0; - //Unknown? Assume AAC. - if (audio.codec == "?") { - audio.codec = "AAC"; - } - if (audio.codec == "AAC") { - len = audio.init.size() + 17; - } - if (len <= 0 || !checkBufferSize()) { - return false; - } + // Unknown? Assume AAC. + if (audio.codec == "?"){audio.codec = "AAC";} + if (audio.codec == "AAC"){len = audio.init.size() + 17;} + if (len <= 0 || !checkBufferSize()){return false;} memcpy(data + 13, audio.init.c_str(), len - 17); - data[12] = 0; //AAC sequence header + data[12] = 0; // AAC sequence header data[11] = 0; - if (audio.codec == "AAC") { - data[11] += 0xA0; - } - if (audio.codec == "MP3") { - data[11] += 0x20; - } + if (audio.codec == "AAC"){data[11] += 0xA0;} + if (audio.codec == "MP3"){data[11] += 0x20;} unsigned int datarate = audio.rate; - if (datarate >= 44100) { + if (datarate >= 44100){ data[11] += 0x0C; - } else if (datarate >= 22050) { + }else if (datarate >= 22050){ data[11] += 0x08; - } else if (datarate >= 11025) { + }else if (datarate >= 11025){ data[11] += 0x04; } - if (audio.size != 8) { - data[11] += 0x02; - } - if (audio.channels > 1) { - data[11] += 0x01; - } + if (audio.size != 8){data[11] += 0x02;} + if (audio.channels > 1){data[11] += 0x01;} setLen(); data[0] = 0x08; data[1] = ((len - 15) >> 16) & 0xFF; @@ -645,89 +489,110 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Track & audio) { return true; } -bool FLV::Tag::DTSCMetaInit(DTSC::Meta & M, std::set & selTracks) { +bool FLV::Tag::DTSCMetaInit(DTSC::Meta &M, std::set &selTracks){ AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); amfdata.addContent(AMF::Object("", "onMetaData")); amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); int i = 0; unsigned long long mediaLen = 0; - for (std::set::iterator it = selTracks.begin(); it != selTracks.end(); it++) { - if (M.tracks[*it].lastms - M.tracks[*it].firstms > mediaLen) { + for (std::set::iterator it = selTracks.begin(); it != selTracks.end(); it++){ + if (M.tracks[*it].lastms - M.tracks[*it].firstms > mediaLen){ mediaLen = M.tracks[*it].lastms - M.tracks[*it].firstms; } - if (M.tracks[*it].type == "video") { + if (M.tracks[*it].type == "video"){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)M.tracks[*it].lastms / 1000) * ((double)M.tracks[*it].fpks / 1000.0), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)M.tracks[*it].fpks / 1000.0), AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent(AMF::Object( + "length", ((double)M.tracks[*it].lastms / 1000) * ((double)M.tracks[*it].fpks / 1000.0), + AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent( + AMF::Object("timescale", ((double)M.tracks[*it].fpks / 1000.0), AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (M.tracks[*it].codec == "H264") { + if (M.tracks[*it].codec == "H264"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"avc1")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "avc1")); } - if (M.tracks[*it].codec == "ScreenVideo2") { + if (M.tracks[*it].codec == "ScreenVideo2"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 6, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"sv2")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "sv2")); } - if (M.tracks[*it].codec == "VP6Alpha") { + if (M.tracks[*it].codec == "VP6Alpha"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 5, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6a")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "vp6a")); } - if (M.tracks[*it].codec == "VP6") { + if (M.tracks[*it].codec == "VP6"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "vp6")); } - if (M.tracks[*it].codec == "ScreenVideo1") { + if (M.tracks[*it].codec == "ScreenVideo1"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 3, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"sv1")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "sv1")); } - if (M.tracks[*it].codec == "H263") { + if (M.tracks[*it].codec == "H263"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"h263")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "h263")); } - if (M.tracks[*it].codec == "JPEG") { + if (M.tracks[*it].codec == "JPEG"){ amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 1, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"jpeg")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "jpeg")); } - amfdata.getContentP(1)->addContent(AMF::Object("width", M.tracks[*it].width, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("height", M.tracks[*it].height, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)M.tracks[*it].fpks / 1000.0, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)M.tracks[*it].bps / 128.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("width", M.tracks[*it].width, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("height", M.tracks[*it].height, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("videoframerate", (double)M.tracks[*it].fpks / 1000.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("videodatarate", (double)M.tracks[*it].bps / 128.0, AMF::AMF0_NUMBER)); ++i; } - if (M.tracks[*it].type == "audio") { + if (M.tracks[*it].type == "audio"){ trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)M.tracks[*it].lastms) * ((double)M.tracks[*it].rate), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", M.tracks[*it].rate, AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent( + AMF::Object("length", ((double)M.tracks[*it].lastms) * ((double)M.tracks[*it].rate), + AMF::AMF0_NUMBER)); + trinfo.getContentP(i)->addContent( + AMF::Object("timescale", M.tracks[*it].rate, AMF::AMF0_NUMBER)); trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); - amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (M.tracks[*it].codec == "AAC") { - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp4a")); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp4a")); + amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0.0, AMF::AMF0_NUMBER)); + if (M.tracks[*it].codec == "AAC"){ + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string) "mp4a")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "mp4a")); } - if (M.tracks[*it].codec == "MP3") { - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp3")); - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp3")); + if (M.tracks[*it].codec == "MP3"){ + amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string) "mp3")); + trinfo.getContentP(i)->getContentP(2)->addContent( + AMF::Object("sampletype", (std::string) "mp3")); } - amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", M.tracks[*it].channels, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", M.tracks[*it].rate, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", M.tracks[*it].size, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)M.tracks[*it].bps / 128.0, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("audiochannels", M.tracks[*it].channels, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("audiosamplerate", M.tracks[*it].rate, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("audiosamplesize", M.tracks[*it].size, AMF::AMF0_NUMBER)); + amfdata.getContentP(1)->addContent( + AMF::Object("audiodatarate", (double)M.tracks[*it].bps / 128.0, AMF::AMF0_NUMBER)); ++i; } } - if (M.vod) { + if (M.vod){ amfdata.getContentP(1)->addContent(AMF::Object("duration", mediaLen / 1000, AMF::AMF0_NUMBER)); } amfdata.getContentP(1)->addContent(trinfo); std::string tmp = amfdata.Pack(); len = tmp.length() + 15; - if (len <= 0 || !checkBufferSize()) { - return false; - } + if (len <= 0 || !checkBufferSize()){return false;} memcpy(data + 11, tmp.data(), len - 15); setLen(); data[0] = 0x12; @@ -743,12 +608,10 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Meta & M, std::set & selTra /// FLV loader function from chunk. /// Copies the contents and wraps it in a FLV header. -bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk & O) { +bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk &O){ len = O.len + 15; - if (len > 0) { - if (!checkBufferSize()) { - return false; - } + if (len > 0){ + if (!checkBufferSize()){return false;} memcpy(data + 11, &(O.data[0]), O.len); } setLen(); @@ -772,63 +635,53 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk & O) { /// \param S The size of the data buffer. /// \param P The current position in the data buffer. Will be updated to reflect new position. /// \return True if count bytes are read succesfully, false otherwise. -bool FLV::Tag::MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P) { - if (sofar >= count) { - return true; - } +bool FLV::Tag::MemReadUntil(char *buffer, unsigned int count, unsigned int &sofar, char *D, + unsigned int S, unsigned int &P){ + if (sofar >= count){return true;} int r = 0; - if (P + (count - sofar) > S) { + if (P + (count - sofar) > S){ r = S - P; - } else { + }else{ r = count - sofar; } memcpy(buffer + sofar, D + P, r); P += r; sofar += r; - if (sofar >= count) { - return true; - } + if (sofar >= count){return true;} return false; -} //Tag::MemReadUntil +}// Tag::MemReadUntil /// Try to load a tag from a data buffer in memory. -/// This is a stateful function - if fed incorrect data, it will most likely never return true again! -/// While this function returns false, the Tag might not contain valid data. -/// \param D The location of the data buffer. -/// \param S The size of the data buffer. -/// \param P The current position in the data buffer. Will be updated to reflect new position. -/// \return True if a whole tag is succesfully read, false otherwise. -bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P) { - if (len < 15) { - len = 15; - } - if (!checkBufferSize()) { - return false; - } - if (done) { - //read a header - if (MemReadUntil(data, 11, sofar, D, S, P)) { - //if its a correct FLV header, throw away and read tag header - if (FLV::is_header(data)) { - if (MemReadUntil(data, 13, sofar, D, S, P)) { - if (FLV::check_header(data)) { +/// This is a stateful function - if fed incorrect data, it will most likely never return true +/// again! While this function returns false, the Tag might not contain valid data. \param D The +/// location of the data buffer. \param S The size of the data buffer. \param P The current position +/// in the data buffer. Will be updated to reflect new position. \return True if a whole tag is +/// succesfully read, false otherwise. +bool FLV::Tag::MemLoader(char *D, unsigned int S, unsigned int &P){ + if (len < 15){len = 15;} + if (!checkBufferSize()){return false;} + if (done){ + // read a header + if (MemReadUntil(data, 11, sofar, D, S, P)){ + // if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (MemReadUntil(data, 13, sofar, D, S, P)){ + if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - } else { + }else{ FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false; } } - } else { - //if a tag header, calculate length and read tag body + }else{ + // if a tag header, calculate length and read tag body len = data[3] + 15; len += (data[2] << 8); len += (data[1] << 16); - if (!checkBufferSize()) { - return false; - } - if (data[0] > 0x12) { + if (!checkBufferSize()){return false;} + if (data[0] > 0x12){ data[0] += 32; FLV::Parse_Error = true; Error_Str = "Invalid Tag received ("; @@ -839,10 +692,10 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P) { done = false; } } - } else { - //read tag body - if (MemReadUntil(data, len, sofar, D, S, P)) { - //calculate keyframeness, next time read header again, return true + }else{ + // read tag body + if (MemReadUntil(data, len, sofar, D, S, P)){ + // calculate keyframeness, next time read header again, return true isKeyframe = ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)); done = true; sofar = 0; @@ -850,7 +703,7 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P) { } } return false; -} //Tag::MemLoader +}// Tag::MemLoader /// Helper function for FLV::FileLoader. /// This function will try to read count bytes from file f into buffer. @@ -860,67 +713,56 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P) { /// \param sofar Current amount read. /// \param f File to read from. /// \return True if count bytes are read succesfully, false otherwise. -bool FLV::Tag::FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f) { - if (sofar >= count) { - return true; - } +bool FLV::Tag::FileReadUntil(char *buffer, unsigned int count, unsigned int &sofar, FILE *f){ + if (sofar >= count){return true;} int r = 0; r = fread(buffer + sofar, 1, count - sofar, f); - if (r < 0) { + if (r < 0){ FLV::Parse_Error = true; Error_Str = "File reading error."; return false; } sofar += r; - if (sofar >= count) { - return true; - } + if (sofar >= count){return true;} return false; } /// Try to load a tag from a file. -/// This is a stateful function - if fed incorrect data, it will most likely never return true again! -/// While this function returns false, the Tag might not contain valid data. -/// \param f The file to read from. -/// \return True if a whole tag is succesfully read, false otherwise. -bool FLV::Tag::FileLoader(FILE * f) { +/// This is a stateful function - if fed incorrect data, it will most likely never return true +/// again! While this function returns false, the Tag might not contain valid data. \param f The +/// file to read from. \return True if a whole tag is succesfully read, false otherwise. +bool FLV::Tag::FileLoader(FILE *f){ int preflags = fcntl(fileno(f), F_GETFL, 0); int postflags = preflags | O_NONBLOCK; fcntl(fileno(f), F_SETFL, postflags); - if (len < 15) { - len = 15; - } - if (!checkBufferSize()) { - return false; - } + if (len < 15){len = 15;} + if (!checkBufferSize()){return false;} - if (done) { - //read a header - if (FileReadUntil(data, 11, sofar, f)) { - //if its a correct FLV header, throw away and read tag header - if (FLV::is_header(data)) { - if (FileReadUntil(data, 13, sofar, f)) { - if (FLV::check_header(data)) { + if (done){ + // read a header + if (FileReadUntil(data, 11, sofar, f)){ + // if its a correct FLV header, throw away and read tag header + if (FLV::is_header(data)){ + if (FileReadUntil(data, 13, sofar, f)){ + if (FLV::check_header(data)){ sofar = 0; memcpy(FLV::Header, data, 13); - } else { + }else{ FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false; } - } else { - Util::sleep(100);//sleep 100ms + }else{ + Util::sleep(100); // sleep 100ms } - } else { - //if a tag header, calculate length and read tag body + }else{ + // if a tag header, calculate length and read tag body len = data[3] + 15; len += (data[2] << 8); len += (data[1] << 16); - if (!checkBufferSize()) { - return false; - } - if (data[0] > 0x12) { + if (!checkBufferSize()){return false;} + if (data[0] > 0x12){ data[0] += 32; FLV::Parse_Error = true; Error_Str = "Invalid Tag received ("; @@ -930,54 +772,50 @@ bool FLV::Tag::FileLoader(FILE * f) { } done = false; } - } else { - Util::sleep(100);//sleep 100ms + }else{ + Util::sleep(100); // sleep 100ms } - } else { - //read tag body - if (FileReadUntil(data, len, sofar, f)) { - //calculate keyframeness, next time read header again, return true + }else{ + // read tag body + if (FileReadUntil(data, len, sofar, f)){ + // calculate keyframeness, next time read header again, return true isKeyframe = ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)); done = true; sofar = 0; fcntl(fileno(f), F_SETFL, preflags); return true; - } else { - Util::sleep(100);//sleep 100ms + }else{ + Util::sleep(100); // sleep 100ms } } fcntl(fileno(f), F_SETFL, preflags); return false; -} //FLV_GetPacket +}// FLV_GetPacket /// Returns 1 for video, 2 for audio, 3 for meta, 0 otherwise. unsigned int FLV::Tag::getTrackID(){ switch (data[0]){ - case 0x08: return 2;//audio track - case 0x09: return 1;//video track - case 0x12: return 3;//meta track - default: return 0; + case 0x08: return 2; // audio track + case 0x09: return 1; // video track + case 0x12: return 3; // meta track + default: return 0; } } /// Returns a pointer to the raw media data for this packet. -char * FLV::Tag::getData(){ - if (data[0] == 0x08 && (data[11] & 0xF0) == 0xA0) { - return data+13; - } - if (data[0] == 0x09 && (data[11] & 0x0F) == 7) { - return data+16; - } - return data+12; +char *FLV::Tag::getData(){ + if (data[0] == 0x08 && (data[11] & 0xF0) == 0xA0){return data + 13;} + if (data[0] == 0x09 && (data[11] & 0x0F) == 7){return data + 16;} + return data + 12; } /// Returns the length of the raw media data for this packet. unsigned int FLV::Tag::getDataLen(){ - if (data[0] == 0x08 && (data[11] & 0xF0) == 0xA0) { + if (data[0] == 0x08 && (data[11] & 0xF0) == 0xA0){ if (len < 17){return 0;} return len - 17; } - if (data[0] == 0x09 && (data[11] & 0x0F) == 7) { + if (data[0] == 0x09 && (data[11] & 0x0F) == 7){ if (len < 20){return 0;} return len - 20; } @@ -985,120 +823,110 @@ unsigned int FLV::Tag::getDataLen(){ return len - 16; } -void FLV::Tag::toMeta(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned int reTrack){ +void FLV::Tag::toMeta(DTSC::Meta &metadata, AMF::Object &amf_storage, unsigned int reTrack){ if (!reTrack){ switch (data[0]){ - case 0x09: reTrack = 1; break;//video - case 0x08: reTrack = 2; break;//audio - case 0x12: reTrack = 3; break;//meta + case 0x09: reTrack = 1; break; // video + case 0x08: reTrack = 2; break; // audio + case 0x12: reTrack = 3; break; // meta } } - if (data[0] == 0x12) { + if (data[0] == 0x12){ AMF::Object meta_in = AMF::parse((unsigned char *)data + 11, len - 15); - AMF::Object * tmp = 0; - if (meta_in.getContentP(1) && meta_in.getContentP(0) && (meta_in.getContentP(0)->StrValue() == "onMetaData")) { + AMF::Object *tmp = 0; + if (meta_in.getContentP(1) && meta_in.getContentP(0) && + (meta_in.getContentP(0)->StrValue() == "onMetaData")){ tmp = meta_in.getContentP(1); - } else { - if (meta_in.getContentP(2) && meta_in.getContentP(1) && (meta_in.getContentP(1)->StrValue() == "onMetaData")) { + }else{ + if (meta_in.getContentP(2) && meta_in.getContentP(1) && + (meta_in.getContentP(1)->StrValue() == "onMetaData")){ tmp = meta_in.getContentP(2); } } - if (tmp) { - amf_storage = *tmp; - } + if (tmp){amf_storage = *tmp;} return; } - if (data[0] == 0x08 && (metadata.tracks[reTrack].codec == "" || metadata.tracks[reTrack].codec != getAudioCodec() || (needsInitData() && isInitData()))) { + if (data[0] == 0x08 && + (metadata.tracks[reTrack].codec == "" || metadata.tracks[reTrack].codec != getAudioCodec() || + (needsInitData() && isInitData()))){ char audiodata = data[11]; metadata.tracks[reTrack].trackID = reTrack; metadata.tracks[reTrack].type = "audio"; metadata.tracks[reTrack].codec = getAudioCodec(); - switch (audiodata & 0x0C) { - case 0x0: - metadata.tracks[reTrack].rate = 5512; - break; - case 0x4: - metadata.tracks[reTrack].rate = 11025; - break; - case 0x8: - metadata.tracks[reTrack].rate = 22050; - break; - case 0xC: - metadata.tracks[reTrack].rate = 44100; - break; + switch (audiodata & 0x0C){ + case 0x0: metadata.tracks[reTrack].rate = 5512; break; + case 0x4: metadata.tracks[reTrack].rate = 11025; break; + case 0x8: metadata.tracks[reTrack].rate = 22050; break; + case 0xC: metadata.tracks[reTrack].rate = 44100; break; } - if (amf_storage.getContentP("audiosamplerate")) { - metadata.tracks[reTrack].rate = (long long int)amf_storage.getContentP("audiosamplerate")->NumValue(); + if (amf_storage.getContentP("audiosamplerate")){ + metadata.tracks[reTrack].rate = + (long long int)amf_storage.getContentP("audiosamplerate")->NumValue(); } - switch (audiodata & 0x02) { - case 0x0: - metadata.tracks[reTrack].size = 8; - break; - case 0x2: - metadata.tracks[reTrack].size = 16; - break; + switch (audiodata & 0x02){ + case 0x0: metadata.tracks[reTrack].size = 8; break; + case 0x2: metadata.tracks[reTrack].size = 16; break; } - if (amf_storage.getContentP("audiosamplesize")) { - metadata.tracks[reTrack].size = (long long int)amf_storage.getContentP("audiosamplesize")->NumValue(); + if (amf_storage.getContentP("audiosamplesize")){ + metadata.tracks[reTrack].size = + (long long int)amf_storage.getContentP("audiosamplesize")->NumValue(); } - switch (audiodata & 0x01) { - case 0x0: - metadata.tracks[reTrack].channels = 1; - break; - case 0x1: + switch (audiodata & 0x01){ + case 0x0: metadata.tracks[reTrack].channels = 1; break; + case 0x1: metadata.tracks[reTrack].channels = 2; break; + } + if (amf_storage.getContentP("stereo")){ + if (amf_storage.getContentP("stereo")->NumValue() == 1){ metadata.tracks[reTrack].channels = 2; - break; - } - if (amf_storage.getContentP("stereo")) { - if (amf_storage.getContentP("stereo")->NumValue() == 1) { - metadata.tracks[reTrack].channels = 2; - } else { + }else{ metadata.tracks[reTrack].channels = 1; } } - if (needsInitData() && isInitData()) { - if ((audiodata & 0xF0) == 0xA0) { + if (needsInitData() && isInitData()){ + if ((audiodata & 0xF0) == 0xA0){ metadata.tracks[reTrack].init = std::string((char *)data + 13, (size_t)len - 17); - } else { + }else{ metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16); } } } - if (data[0] == 0x09 && ((needsInitData() && isInitData()) || !metadata.tracks[reTrack].codec.size())){ + if (data[0] == 0x09 && + ((needsInitData() && isInitData()) || !metadata.tracks[reTrack].codec.size())){ char videodata = data[11]; metadata.tracks[reTrack].codec = getVideoCodec(); metadata.tracks[reTrack].type = "video"; metadata.tracks[reTrack].trackID = reTrack; - if (amf_storage.getContentP("width")) { + if (amf_storage.getContentP("width")){ metadata.tracks[reTrack].width = (long long int)amf_storage.getContentP("width")->NumValue(); } - if (amf_storage.getContentP("height")) { - metadata.tracks[reTrack].height = (long long int)amf_storage.getContentP("height")->NumValue(); + if (amf_storage.getContentP("height")){ + metadata.tracks[reTrack].height = + (long long int)amf_storage.getContentP("height")->NumValue(); } - if (!metadata.tracks[reTrack].fpks && amf_storage.getContentP("videoframerate")) { + if (!metadata.tracks[reTrack].fpks && amf_storage.getContentP("videoframerate")){ if (amf_storage.getContentP("videoframerate")->NumValue()){ - metadata.tracks[reTrack].fpks = (long long int)(amf_storage.getContentP("videoframerate")->NumValue() * 1000.0); + metadata.tracks[reTrack].fpks = + (long long int)(amf_storage.getContentP("videoframerate")->NumValue() * 1000.0); }else{ - metadata.tracks[reTrack].fpks = atoi(amf_storage.getContentP("videoframerate")->StrValue().c_str()) * 1000.0; + metadata.tracks[reTrack].fpks = + atoi(amf_storage.getContentP("videoframerate")->StrValue().c_str()) * 1000.0; } } - if (needsInitData() && isInitData()) { - if ((videodata & 0x0F) == 7) { - if (len < 21) { - return; - } + if (needsInitData() && isInitData()){ + if ((videodata & 0x0F) == 7){ + if (len < 21){return;} metadata.tracks[reTrack].init = std::string((char *)data + 16, (size_t)len - 20); - } else { - if (len < 17) { - return; - } + }else{ + if (len < 17){return;} metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16); } - ///this is a hacky way around invalid FLV data (since it gets ignored nearly everywhere, but we do need correct data... - if (!metadata.tracks[reTrack].width || !metadata.tracks[reTrack].height || !metadata.tracks[reTrack].fpks){ + /// this is a hacky way around invalid FLV data (since it gets ignored nearly everywhere, but + /// we do need correct data... + if (!metadata.tracks[reTrack].width || !metadata.tracks[reTrack].height || + !metadata.tracks[reTrack].fpks){ h264::sequenceParameterSet sps; sps.fromDTSCInit(metadata.tracks[reTrack].init); h264::SPSMeta spsChar = sps.getCharacteristics(); @@ -1113,17 +941,18 @@ void FLV::Tag::toMeta(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned /// Checks if buf is large enough to contain len. /// Attempts to resize data buffer if not/ /// \returns True if buffer is large enough, false otherwise. -bool FLV::Tag::checkBufferSize() { - if (buf < len || !data) { - char * newdata = (char *)realloc(data, len); +bool FLV::Tag::checkBufferSize(){ + if (buf < len || !data){ + char *newdata = (char *)realloc(data, len); // on realloc fail, retain the old data - if (newdata != 0) { + if (newdata != 0){ data = newdata; buf = len; - } else { + }else{ len = buf; return false; } } return true; } + diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 0dbc6fd3..6f15c5ac 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -2,73 +2,78 @@ /// Holds all headers for the FLV namespace. #pragma once -#include "socket.h" -#include "dtsc.h" #include "amf.h" +#include "dtsc.h" +#include "socket.h" #include - -//forward declaration of RTMPStream::Chunk to avoid circular dependencies. -namespace RTMPStream { +// forward declaration of RTMPStream::Chunk to avoid circular dependencies. +namespace RTMPStream{ class Chunk; } /// This namespace holds all FLV-parsing related functionality. -namespace FLV { - //variables - extern char Header[13]; ///< Holds the last FLV header parsed. - extern bool Parse_Error; ///< This variable is set to true if a problem is encountered while parsing the FLV. - extern std::string Error_Str; ///< This variable is set if a problem is encountered while parsing the FLV. +namespace FLV{ + // variables + extern char Header[13]; ///< Holds the last FLV header parsed. + extern bool Parse_Error; ///< This variable is set to true if a problem is encountered while + ///< parsing the FLV. + extern std::string + Error_Str; ///< This variable is set if a problem is encountered while parsing the FLV. - //functions - bool check_header(char * header); ///< Checks a FLV Header for validness. - bool is_header(char * header); ///< Checks the first 3 bytes for the string "FLV". + // functions + bool check_header(char *header); ///< Checks a FLV Header for validness. + bool is_header(char *header); ///< Checks the first 3 bytes for the string "FLV". /// Helper function that can quickly skip through a file looking for a particular tag type - bool seekToTagType(FILE * f, uint8_t type); + bool seekToTagType(FILE *f, uint8_t type); /// This class is used to hold, work with and get information about a single FLV tag. - class Tag { - public: - int len; ///< Actual length of tag. - bool isKeyframe; ///< True if current tag is a video keyframe. - char * data; ///< Pointer to tag buffer. - bool needsInitData(); ///< True if this media type requires init data. - bool isInitData(); ///< True if current tag is init data for this media type. - const char * getAudioCodec(); ///< Returns a c-string with the audio codec name. - const char * getVideoCodec(); ///< Returns a c-string with the video codec name. - std::string tagType(); ///< Returns a std::string describing the tag in detail. - unsigned int tagTime(); - void tagTime(unsigned int T); - int offset(); - void offset(int o); - Tag(); ///< Constructor for a new, empty, tag. - Tag(const Tag & O); ///< Copy constructor, copies the contents of an existing tag. - Tag & operator=(const Tag & O); ///< Assignment operator - works exactly like the copy constructor. - Tag(const RTMPStream::Chunk & O); /// & selTracks); - void toMeta(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned int reTrack = 0); - bool MemLoader(char * D, unsigned int S, unsigned int & P); - bool FileLoader(FILE * f); - unsigned int getTrackID(); - char * getData(); - unsigned int getDataLen(); - protected: - int buf; ///< Maximum length of buffer space. - bool done; ///< Body reading done? - unsigned int sofar; ///< How many bytes are read sofar? - void setLen(); - bool checkBufferSize(); - //loader helper functions - bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P); - bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f); - }; -//Tag + class Tag{ + public: + int len; ///< Actual length of tag. + bool isKeyframe; ///< True if current tag is a video keyframe. + char *data; ///< Pointer to tag buffer. + bool needsInitData(); ///< True if this media type requires init data. + bool isInitData(); ///< True if current tag is init data for this media type. + const char *getAudioCodec(); ///< Returns a c-string with the audio codec name. + const char *getVideoCodec(); ///< Returns a c-string with the video codec name. + std::string tagType(); ///< Returns a std::string describing the tag in detail. + uint64_t tagTime(); + void tagTime(uint64_t T); + int64_t offset(); + void offset(int64_t o); + Tag(); ///< Constructor for a new, empty, tag. + Tag(const Tag &O); ///< Copy constructor, copies the contents of an existing tag. + Tag & + operator=(const Tag &O); ///< Assignment operator - works exactly like the copy constructor. + Tag(const RTMPStream::Chunk &O); ///< Copy constructor from a RTMP chunk. + ~Tag(); ///< Generic destructor. + // loader functions + bool ChunkLoader(const RTMPStream::Chunk &O); + bool DTSCLoader(DTSC::Packet &packData, DTSC::Track &track); + bool DTSCVideoInit(DTSC::Track &video); + bool DTSCAudioInit(DTSC::Track &audio); + bool DTSCMetaInit(DTSC::Meta &M, std::set &selTracks); + void toMeta(DTSC::Meta &metadata, AMF::Object &amf_storage, unsigned int reTrack = 0); + bool MemLoader(char *D, unsigned int S, unsigned int &P); + bool FileLoader(FILE *f); + unsigned int getTrackID(); + char *getData(); + unsigned int getDataLen(); + + protected: + int buf; ///< Maximum length of buffer space. + bool done; ///< Body reading done? + unsigned int sofar; ///< How many bytes are read sofar? + void setLen(); + bool checkBufferSize(); + // loader helper functions + bool MemReadUntil(char *buffer, unsigned int count, unsigned int &sofar, char *D, + unsigned int S, unsigned int &P); + bool FileReadUntil(char *buffer, unsigned int count, unsigned int &sofar, FILE *f); + }; + // Tag + +}// namespace FLV -}//FLV namespace diff --git a/lib/h264.cpp b/lib/h264.cpp index 931afb0b..dafcbc90 100644 --- a/lib/h264.cpp +++ b/lib/h264.cpp @@ -2,49 +2,48 @@ #define _GNU_SOURCE #endif #include "h264.h" -#include -#include #include "bitfields.h" #include "bitstream.h" #include "defines.h" +#include +#include +#include -namespace h264 { - - ///Helper function to determine if a H264 NAL unit is a keyframe or not - bool isKeyframe(const char * data, uint32_t len){ +namespace h264{ + + /// Helper function to determine if a H264 NAL unit is a keyframe or not + bool isKeyframe(const char *data, uint32_t len){ uint8_t nalType = (data[0] & 0x1F); if (nalType == 0x05){return true;} if (nalType != 0x01){return false;} Utils::bitstream bs; - for (size_t i = 1; i < 10 && i < len; ++i) { - if (i + 2 < len && (memcmp(data + i, "\000\000\003", 3) == 0)) { //Emulation prevention bytes + for (size_t i = 1; i < 10 && i < len; ++i){ + if (i + 2 < len && (memcmp(data + i, "\000\000\003", 3) == 0)){// Emulation prevention bytes bs.append(data + i, 2); i += 2; - } else { + }else{ bs.append(data + i, 1); } } - bs.getExpGolomb();//Discard first_mb_in_slice + bs.getExpGolomb(); // Discard first_mb_in_slice uint64_t sliceType = bs.getUExpGolomb(); - //Slice types: + // Slice types: // 0: P - Predictive slice (at most 1 reference) // 1: B - Bi-predictive slice (at most 2 references) // 2: I - Intra slice (no external references) // 3: SP - Switching predictive slice (at most 1 reference) // 4: SI - Switching intra slice (no external references) // 5-9: 0-4, but all in picture of same type - if (sliceType == 2 || sliceType == 4 || sliceType == 7 || sliceType == 9){ - return true; - } + if (sliceType == 2 || sliceType == 4 || sliceType == 7 || sliceType == 9){return true;} return false; } - std::deque analysePackets(const char * data, unsigned long len){ + std::deque analysePackets(const char *data, unsigned long len){ std::deque res; int offset = 0; - //Make sure entire packet is within len - while (offset+5 < len && Bit::btohl(data + offset)+offset+4 <= len){ + // Make sure entire packet is within len + while (offset + 5 < len && Bit::btohl(data + offset) + offset + 4 <= len){ nalu::nalData entry; entry.nalSize = Bit::btohl(data + offset); entry.nalType = (data + offset)[4] & 0x1F; @@ -53,75 +52,18 @@ namespace h264 { } return res; } - - unsigned long toAnnexB(const char * data, unsigned long dataSize, char *& result){ - //toAnnexB keeps the same size. - if (!result){ - result = (char *)malloc(dataSize); - } - int offset = 0; - while (offset < dataSize){ - //Read unit size - unsigned long unitSize = Bit::btohl(data + offset); - //Write annex b header - memset(result + offset, 0x00, 3); - result[offset + 3] = 0x01; - //Copy the nal unit - memcpy(result + offset + 4, data + offset + 4, unitSize); - //Update the offset - offset += 4 + unitSize; - } - return dataSize; - } - unsigned long fromAnnexB(const char * data, unsigned long dataSize, char *& result){ - const char * lastCheck = data + dataSize - 3; - if (!result){ - FAIL_MSG("No output buffer given to FromAnnexB"); - return 0; - } - int offset = 0; - int newOffset = 0; - while (offset < dataSize){ - const char * begin = data + offset; - while ( begin < lastCheck && !(!begin[0] && !begin[1] && begin[2] == 0x01)){ - begin++; - if (begin < lastCheck && begin[0]){ - begin++; - } - } - begin += 3;//Initialize begin after the first 0x000001 pattern. - if (begin > data + dataSize){ - offset = dataSize; - continue; - } - const char * end = (const char*)memmem(begin, dataSize - (begin - data), "\000\000\001", 3); - if (!end) { - end = data + dataSize; - } - //Check for 4-byte lead in's. Yes, we access -1 here - if (end > begin && end[-1] == 0x00){ - end--; - } - unsigned int nalSize = end - begin; - Bit::htobl(result + newOffset, nalSize); - memcpy(result + newOffset + 4, begin, nalSize); + sequenceParameterSet::sequenceParameterSet(const char *_data, size_t _dataLen) + : data(_data), dataLen(_dataLen){} - newOffset += 4 + nalSize; - offset = end - data; - } - return newOffset; - } - - sequenceParameterSet::sequenceParameterSet(const char * _data, unsigned long _dataLen) : data(_data), dataLen(_dataLen) {} - - //DTSC Initdata is the payload for an avcc box. init[8+] is data, init[6-7] is a network-encoded length - void sequenceParameterSet::fromDTSCInit(const std::string & dtscInit){ + // DTSC Initdata is the payload for an avcc box. init[8+] is data, init[6-7] is a network-encoded + // length + void sequenceParameterSet::fromDTSCInit(const std::string &dtscInit){ data = dtscInit.data() + 8; dataLen = Bit::btohs(dtscInit.data() + 6); } - void skipScalingList(Utils::bitstream & bs, size_t listSize){ + void skipScalingList(Utils::bitstream &bs, size_t listSize){ size_t lastScale = 8; size_t nextScale = 8; for (size_t i = 0; i < listSize; i++){ @@ -133,47 +75,48 @@ namespace h264 { } } - SPSMeta sequenceParameterSet::getCharacteristics() const { + SPSMeta sequenceParameterSet::getCharacteristics() const{ SPSMeta result; result.sep_col_plane = false; - //For calculating width - unsigned int widthInMbs = 0; - unsigned int cropHorizontal = 0; + // For calculating width + uint32_t widthInMbs = 0; + uint32_t cropHorizontal = 0; - //For calculating height - unsigned int heightInMapUnits = 0; - unsigned int cropVertical = 0; + // For calculating height + uint32_t heightInMapUnits = 0; + uint32_t cropVertical = 0; - //Fill the bitstream + // Fill the bitstream Utils::bitstream bs; - for (unsigned int i = 1; i < dataLen; i++) { - if (i + 2 < dataLen && (memcmp(data + i, "\000\000\003", 3) == 0)){//Emulation prevention bytes - //Yes, we increase i here + for (size_t i = 1; i < dataLen; i++){ + if (i + 2 < dataLen && + (memcmp(data + i, "\000\000\003", 3) == 0)){// Emulation prevention bytes + // Yes, we increase i here bs.append(data + i, 2); i += 2; - } else { - //No we don't increase i here + }else{ + // No we don't increase i here bs.append(data + i, 1); } } char profileIdc = bs.get(8); result.profile = profileIdc; - //Start skipping unused data + // Start skipping unused data bs.skip(8); result.level = bs.get(8); bs.getUExpGolomb(); - if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128) { - //chroma format idc + if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || + profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || + profileIdc == 128){ + // chroma format idc char chromaFormatIdc = bs.getUExpGolomb(); - if (chromaFormatIdc == 3) { - result.sep_col_plane = (bs.get(1) == 1); - } - bs.getUExpGolomb();//luma - bs.getUExpGolomb();//chroma - bs.skip(1);//transform bypass - if (bs.get(1)) {//Scaling matrix is present + if (chromaFormatIdc == 3){result.sep_col_plane = (bs.get(1) == 1);} + bs.getUExpGolomb(); // luma + bs.getUExpGolomb(); // chroma + bs.skip(1); // transform bypass + if (bs.get(1)){// Scaling matrix is present char listSize = (chromaFormatIdc == 3 ? 12 : 8); for (size_t i = 0; i < listSize; i++){ bool thisListPresent = bs.get(1); @@ -189,59 +132,51 @@ namespace h264 { } result.log2_max_frame_num = bs.getUExpGolomb() + 4; result.cnt_type = bs.getUExpGolomb(); - if (!result.cnt_type) { + if (!result.cnt_type){ result.log2_max_order_cnt = bs.getUExpGolomb() + 4; - } else if (result.cnt_type == 1) { - DEBUG_MSG(DLVL_DEVEL, "This part of the implementation is incomplete(2), to be continued. If this message is shown, contact developers immediately."); + }else if (result.cnt_type == 1){ + ERROR_MSG("This part of the implementation is incomplete(2), to be continued. If this " + "message is shown, contact developers immediately."); } - result.max_ref_frames = bs.getUExpGolomb();//max_num_ref_frames - result.gaps = (bs.get(1) == 1);//gaps in frame num allowed - //Stop skipping data and start doing useful stuff - + result.max_ref_frames = bs.getUExpGolomb(); // max_num_ref_frames + result.gaps = (bs.get(1) == 1); // gaps in frame num allowed + // Stop skipping data and start doing useful stuff widthInMbs = bs.getUExpGolomb() + 1; heightInMapUnits = bs.getUExpGolomb() + 1; - result.mbs_only = (bs.get(1) == 1);//Gets used in height calculation - if (!result.mbs_only) { - bs.skip(1); - } + result.mbs_only = (bs.get(1) == 1); // Gets used in height calculation + if (!result.mbs_only){bs.skip(1);} bs.skip(1); - //cropping flag - if (bs.get(1)) { - cropHorizontal = bs.getUExpGolomb();//leftOffset - cropHorizontal += bs.getUExpGolomb();//rightOffset - cropVertical = bs.getUExpGolomb();//topOffset - cropVertical += bs.getUExpGolomb();//bottomOffset + // cropping flag + if (bs.get(1)){ + cropHorizontal = bs.getUExpGolomb(); // leftOffset + cropHorizontal += bs.getUExpGolomb(); // rightOffset + cropVertical = bs.getUExpGolomb(); // topOffset + cropVertical += bs.getUExpGolomb(); // bottomOffset } - //vuiParameters + // vuiParameters result.fps = 0; - if (bs.get(1)) { - //Skipping all the paramters we dont use - if (bs.get(1)) { - if (bs.get(8) == 255) { - bs.skip(32); - } + if (bs.get(1)){ + // Skipping all the paramters we dont use + if (bs.get(1)){ + if (bs.get(8) == 255){bs.skip(32);} } - if (bs.get(1)) { - bs.skip(1); - } - if (bs.get(1)) { + if (bs.get(1)){bs.skip(1);} + if (bs.get(1)){ bs.skip(4); - if (bs.get(1)) { - bs.skip(24); - } + if (bs.get(1)){bs.skip(24);} } - if (bs.get(1)) { + if (bs.get(1)){ bs.getUExpGolomb(); bs.getUExpGolomb(); } - //Decode timing info - if (bs.get(1)) { - unsigned int unitsInTick = bs.get(32); - unsigned int timeScale = bs.get(32); + // Decode timing info + if (bs.get(1)){ + uint32_t unitsInTick = bs.get(32); + uint32_t timeScale = bs.get(32); result.fps = (double)timeScale / (2 * unitsInTick); bs.skip(1); } diff --git a/lib/h264.h b/lib/h264.h index 7578308f..826e1395 100644 --- a/lib/h264.h +++ b/lib/h264.h @@ -1,71 +1,80 @@ +#pragma once +#include +#include #include #include +#include "bitfields.h" +#include "bitstream.h" #include "nal.h" -namespace h264 { +namespace h264{ - std::deque analysePackets(const char * data, unsigned long len); + std::deque analysePackets(const char *data, size_t len); - ///Struct containing pre-calculated metadata of an SPS nal unit. Width and height in pixels, fps in Hz - struct SPSMeta { - unsigned int width; - unsigned int height; + /// Struct containing pre-calculated metadata of an SPS nal unit. Width and height in pixels, fps + /// in Hz + struct SPSMeta{ + uint32_t width; + uint32_t height; double fps; uint8_t profile; uint8_t level; bool sep_col_plane; uint8_t cnt_type; - bool gaps;///< Gaps in frame num allowed flag - bool mbs_only;/// -#include -#include -#include //for uint64_t -#include //for memcpy #include //for htonl +#include +#include +#include //for uint64_t +#include +#include //for memcpy /// Construct from a root Value to iterate over. -JSON::Iter::Iter(Value & root){ +JSON::Iter::Iter(Value &root){ myType = root.myType; i = 0; r = &root; if (!root.size()){myType = JSON::EMPTY;} - if (myType == JSON::ARRAY){ - aIt = root.arrVal.begin(); - } - if (myType == JSON::OBJECT){ - oIt = root.objVal.begin(); - } + if (myType == JSON::ARRAY){aIt = root.arrVal.begin();} + if (myType == JSON::OBJECT){oIt = root.objVal.begin();} } /// Dereferences into a Value reference. /// If invalid iterator, returns an empty reference and prints a warning message. -JSON::Value & JSON::Iter::operator*() const{ - if (myType == JSON::ARRAY && aIt != r->arrVal.end()){ - return **aIt; - } - if (myType == JSON::OBJECT && oIt != r->objVal.end()){ - return *(oIt->second); - } +JSON::Value &JSON::Iter::operator*() const{ + if (myType == JSON::ARRAY && aIt != r->arrVal.end()){return **aIt;} + if (myType == JSON::OBJECT && oIt != r->objVal.end()){return *(oIt->second);} static JSON::Value error; WARN_MSG("Dereferenced invalid JSON iterator"); return error; @@ -39,41 +32,36 @@ JSON::Value & JSON::Iter::operator*() const{ /// Dereferences into a Value reference. /// If invalid iterator, returns an empty reference and prints a warning message. -JSON::Value * JSON::Iter::operator->() const{ +JSON::Value *JSON::Iter::operator->() const{ return &(operator*()); } /// True if not done iterating. JSON::Iter::operator bool() const{ - return ((myType == JSON::ARRAY && aIt != r->arrVal.end()) || (myType == JSON::OBJECT && oIt != r->objVal.end())); + return ((myType == JSON::ARRAY && aIt != r->arrVal.end()) || + (myType == JSON::OBJECT && oIt != r->objVal.end())); } /// Go to next iteration. -JSON::Iter & JSON::Iter::operator++(){ +JSON::Iter &JSON::Iter::operator++(){ if (*this){ ++i; - if (myType == JSON::ARRAY){ - ++aIt; - } - if (myType == JSON::OBJECT){ - ++oIt; - } + if (myType == JSON::ARRAY){++aIt;} + if (myType == JSON::OBJECT){++oIt;} } return *this; } /// Return the name of the current indice. -const std::string & JSON::Iter::key() const{ - if (myType == JSON::OBJECT && *this){ - return oIt->first; - } +const std::string &JSON::Iter::key() const{ + if (myType == JSON::OBJECT && *this){return oIt->first;} static const std::string empty; WARN_MSG("Got key from invalid JSON iterator"); return empty; } /// Return the number of the current indice. -unsigned int JSON::Iter::num() const{ +uint32_t JSON::Iter::num() const{ return i; } @@ -92,28 +80,20 @@ void JSON::Iter::remove(){ } /// Construct from a root Value to iterate over. -JSON::ConstIter::ConstIter(const Value & root){ +JSON::ConstIter::ConstIter(const Value &root){ myType = root.myType; i = 0; r = &root; if (!root.size()){myType = JSON::EMPTY;} - if (myType == JSON::ARRAY){ - aIt = root.arrVal.begin(); - } - if (myType == JSON::OBJECT){ - oIt = root.objVal.begin(); - } + if (myType == JSON::ARRAY){aIt = root.arrVal.begin();} + if (myType == JSON::OBJECT){oIt = root.objVal.begin();} } /// Dereferences into a Value reference. /// If invalid iterator, returns an empty reference and prints a warning message. -const JSON::Value & JSON::ConstIter::operator*() const{ - if (myType == JSON::ARRAY && aIt != r->arrVal.end()){ - return **aIt; - } - if (myType == JSON::OBJECT && oIt != r->objVal.end()){ - return *(oIt->second); - } +const JSON::Value &JSON::ConstIter::operator*() const{ + if (myType == JSON::ARRAY && aIt != r->arrVal.end()){return **aIt;} + if (myType == JSON::OBJECT && oIt != r->objVal.end()){return *(oIt->second);} static JSON::Value error; WARN_MSG("Dereferenced invalid JSON iterator"); return error; @@ -121,58 +101,49 @@ const JSON::Value & JSON::ConstIter::operator*() const{ /// Dereferences into a Value reference. /// If invalid iterator, returns an empty reference and prints a warning message. -const JSON::Value * JSON::ConstIter::operator->() const{ +const JSON::Value *JSON::ConstIter::operator->() const{ return &(operator*()); } /// True if not done iterating. JSON::ConstIter::operator bool() const{ - return ((myType == JSON::ARRAY && aIt != r->arrVal.end()) || (myType == JSON::OBJECT && oIt != r->objVal.end())); + return ((myType == JSON::ARRAY && aIt != r->arrVal.end()) || + (myType == JSON::OBJECT && oIt != r->objVal.end())); } /// Go to next iteration. -JSON::ConstIter & JSON::ConstIter::operator++(){ +JSON::ConstIter &JSON::ConstIter::operator++(){ if (*this){ ++i; - if (myType == JSON::ARRAY){ - ++aIt; - } - if (myType == JSON::OBJECT){ - ++oIt; - } + if (myType == JSON::ARRAY){++aIt;} + if (myType == JSON::OBJECT){++oIt;} } return *this; } /// Return the name of the current indice. -const std::string & JSON::ConstIter::key() const{ - if (myType == JSON::OBJECT && *this){ - return oIt->first; - } +const std::string &JSON::ConstIter::key() const{ + if (myType == JSON::OBJECT && *this){return oIt->first;} static const std::string empty; WARN_MSG("Got key from invalid JSON iterator"); return empty; } /// Return the number of the current indice. -unsigned int JSON::ConstIter::num() const{ +uint32_t JSON::ConstIter::num() const{ return i; } -static inline char c2hex(char c) { +static inline char c2hex(char c){ if (c >= '0' && c <= '9') return c - '0'; if (c >= 'a' && c <= 'f') return c - 'a' + 10; if (c >= 'A' && c <= 'F') return c - 'A' + 10; return 0; } -static inline char hex2c(char c) { - if (c < 10) { - return '0' + c; - } - if (c < 16) { - return 'A' + (c - 10); - } +static inline char hex2c(char c){ + if (c < 10){return '0' + c;} + if (c < 16){return 'A' + (c - 10);} return '0'; } @@ -200,81 +171,66 @@ static std::string UTF8(uint32_t c){ return r; } -static std::string read_string(int separator, std::istream & fromstream) { +static std::string read_string(char separator, std::istream &fromstream){ std::string out; bool escaped = false; uint32_t fullChar = 0; - while (fromstream.good()) { + while (fromstream.good()){ char c; fromstream.get(c); - if (c == '\\') { + if (c == '\\'){ escaped = true; continue; } - if (escaped) { + if (escaped){ if (fullChar && c != 'u'){ out += UTF8(fullChar >> 16); fullChar = 0; } - switch (c) { - case 'b': - out += '\b'; - break; - case 'f': - out += '\f'; - break; - case 'n': - out += '\n'; - break; - case 'r': - out += '\r'; - break; - case 't': - out += '\t'; - break; - case 'x': - char d1, d2; - fromstream.get(d1); - fromstream.get(d2); - out.append(1, (c2hex(d2) + (c2hex(d1) << 4))); - break; - case 'u': { - char d1, d2, d3, d4; - fromstream.get(d1); - fromstream.get(d2); - fromstream.get(d3); - fromstream.get(d4); - uint32_t tmpChar = (c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16)); - if (fullChar && (tmpChar < 0xDC00 || tmpChar > 0xDFFF)){ - //not a low surrogate - handle high surrogate separately! - out += UTF8(fullChar >> 16); - fullChar = 0; - } - fullChar |= tmpChar; - if (fullChar >= 0xD800 && fullChar <= 0xDBFF){ - //possibly high surrogate! Read next characters before handling... - fullChar <<= 16;//save as high surrogate - }else{ - out += UTF8(fullChar); - fullChar = 0; - } - break; - } - default: - out.append(1, c); - break; + switch (c){ + case 'b': out += '\b'; break; + case 'f': out += '\f'; break; + case 'n': out += '\n'; break; + case 'r': out += '\r'; break; + case 't': out += '\t'; break; + case 'x': + char d1, d2; + fromstream.get(d1); + fromstream.get(d2); + out.append(1, (c2hex(d2) + (c2hex(d1) << 4))); + break; + case 'u':{ + char d1, d2, d3, d4; + fromstream.get(d1); + fromstream.get(d2); + fromstream.get(d3); + fromstream.get(d4); + uint32_t tmpChar = (c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16)); + if (fullChar && (tmpChar < 0xDC00 || tmpChar > 0xDFFF)){ + // not a low surrogate - handle high surrogate separately! + out += UTF8(fullChar >> 16); + fullChar = 0; + } + fullChar |= tmpChar; + if (fullChar >= 0xD800 && fullChar <= 0xDBFF){ + // possibly high surrogate! Read next characters before handling... + fullChar <<= 16; // save as high surrogate + }else{ + out += UTF8(fullChar); + fullChar = 0; + } + break; + } + default: out.append(1, c); break; } escaped = false; - } else { + }else{ if (fullChar){ out += UTF8(fullChar >> 16); fullChar = 0; } - if (c == separator) { - return out; - } else { - out.append(1, c); - } + if (c == separator){return out;} + out.append(1, c); } } if (fullChar){ @@ -288,79 +244,70 @@ static std::string UTF16(uint32_t c){ if (c > 0xFFFF){ c -= 0x010000; return UTF16(0xD800 + ((c >> 10) & 0x3FF)) + UTF16(0xDC00 + (c & 0x3FF)); - }else{ - std::string ret = "\\u"; - ret += hex2c((c >> 12) & 0xf); - ret += hex2c((c >> 8) & 0xf); - ret += hex2c((c >> 4) & 0xf); - ret += hex2c(c & 0xf); - return ret; } + std::string ret = "\\u"; + ret += hex2c((c >> 12) & 0xf); + ret += hex2c((c >> 8) & 0xf); + ret += hex2c((c >> 4) & 0xf); + ret += hex2c(c & 0xf); + return ret; } -std::string JSON::string_escape(const std::string & val) { +std::string JSON::string_escape(const std::string &val){ std::string out = "\""; - for (unsigned int i = 0; i < val.size(); ++i) { - const char & c = val.data()[i]; - switch (c) { - 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 (c < 32 || c > 126) { - //we assume our data is UTF-8 encoded internally. - //JavaScript expects UTF-16, so if we recognize a valid UTF-8 sequence, we turn it into UTF-16 for JavaScript. - //Anything else is escaped as a single character UTF-16 escape. - if ((c & 0xC0) == 0xC0){ - //possible UTF-8 sequence - //check for 2-byte sequence - if (((c & 0xE0) == 0XC0) && (i+1 < val.size()) && ((val.data()[i+1] & 0xC0) == 0x80)){ - //valid 2-byte sequence - out += UTF16(((c & 0x1F) << 6) | (val.data()[i+1] & 0x3F)); - i += 1; - break; - } - //check for 3-byte sequence - if (((c & 0xF0) == 0XE0) && (i+2 < val.size()) && ((val.data()[i+1] & 0xC0) == 0x80) && ((val.data()[i+2] & 0xC0) == 0x80)){ - //valid 3-byte sequence - out += UTF16(((c & 0x1F) << 12) | ((val.data()[i+1] & 0x3F) << 6) | (val.data()[i+2] & 0x3F)); - i += 2; - break; - } - //check for 4-byte sequence - if (((c & 0xF8) == 0XF0) && (i+3 < val.size()) && ((val.data()[i+1] & 0xC0) == 0x80) && ((val.data()[i+2] & 0xC0) == 0x80) && ((val.data()[i+3] & 0xC0) == 0x80)){ - //valid 4-byte sequence - out += UTF16(((c & 0x1F) << 18) | ((val.data()[i+1] & 0x3F) << 12) | ((val.data()[i+2] & 0x3F) << 6) | (val.data()[i+3] & 0x3F)); - i += 3; - break; - } + for (size_t i = 0; i < val.size(); ++i){ + const char &c = val.data()[i]; + switch (c){ + 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 (c < 32 || c > 126){ + // we assume our data is UTF-8 encoded internally. + // JavaScript expects UTF-16, so if we recognize a valid UTF-8 sequence, we turn it into + // UTF-16 for JavaScript. Anything else is escaped as a single character UTF-16 escape. + if ((c & 0xC0) == 0xC0){ + // possible UTF-8 sequence + // check for 2-byte sequence + if (((c & 0xE0) == 0XC0) && (i + 1 < val.size()) && + ((val.data()[i + 1] & 0xC0) == 0x80)){ + // valid 2-byte sequence + out += UTF16(((c & 0x1F) << 6) | (val.data()[i + 1] & 0x3F)); + i += 1; + break; + } + // check for 3-byte sequence + if (((c & 0xF0) == 0XE0) && (i + 2 < val.size()) && + ((val.data()[i + 1] & 0xC0) == 0x80) && ((val.data()[i + 2] & 0xC0) == 0x80)){ + // valid 3-byte sequence + out += UTF16(((c & 0x1F) << 12) | ((val.data()[i + 1] & 0x3F) << 6) | + (val.data()[i + 2] & 0x3F)); + i += 2; + break; + } + // check for 4-byte sequence + if (((c & 0xF8) == 0XF0) && (i + 3 < val.size()) && + ((val.data()[i + 1] & 0xC0) == 0x80) && ((val.data()[i + 2] & 0xC0) == 0x80) && + ((val.data()[i + 3] & 0xC0) == 0x80)){ + // valid 4-byte sequence + out += UTF16(((c & 0x1F) << 18) | ((val.data()[i + 1] & 0x3F) << 12) | + ((val.data()[i + 2] & 0x3F) << 6) | (val.data()[i + 3] & 0x3F)); + i += 3; + break; } - //Anything else, we encode as a single UTF-16 character. - out += "\\u00"; - out += hex2c((val.data()[i] >> 4) & 0xf); - out += hex2c(val.data()[i] & 0xf); - } else { - out += val.data()[i]; } - break; + // Anything else, we encode as a single UTF-16 character. + out += "\\u00"; + out += hex2c((val.data()[i] >> 4) & 0xf); + out += hex2c(val.data()[i] & 0xf); + }else{ + out += val.data()[i]; + } + break; } } out += "\""; @@ -368,233 +315,216 @@ std::string JSON::string_escape(const std::string & val) { } /// Skips an std::istream forward until any of the following characters is seen: ,]} -static void skipToEnd(std::istream & fromstream) { - while (fromstream.good()) { +static void skipToEnd(std::istream &fromstream){ + while (fromstream.good()){ char peek = fromstream.peek(); - if (peek == ',') { - return; - } - if (peek == ']') { - return; - } - if (peek == '}') { - return; - } + if (peek == ','){return;} + if (peek == ']'){return;} + if (peek == '}'){return;} peek = fromstream.get(); } } /// Sets this JSON::Value to null; -JSON::Value::Value() { +JSON::Value::Value(){ null(); } /// Sets this JSON::Value to null -JSON::Value::~Value() { +JSON::Value::~Value(){ null(); } -JSON::Value::Value(const Value & rhs) { +JSON::Value::Value(const Value &rhs){ null(); *this = rhs; } /// Sets this JSON::Value to read from this position in the std::istream -JSON::Value::Value(std::istream & fromstream) { +JSON::Value::Value(std::istream &fromstream){ null(); bool reading_object = false; bool reading_array = false; bool negative = false; bool stop = false; - while (!stop && fromstream.good()) { + while (!stop && fromstream.good()){ int c = fromstream.peek(); - switch (c) { - case '{': - reading_object = true; - c = fromstream.get(); - myType = OBJECT; - break; - case '[': { - reading_array = true; - c = fromstream.get(); - myType = ARRAY; - Value tmp = JSON::Value(fromstream); - if (tmp.myType != EMPTY) { - append(tmp); - } - break; - } - case '\'': - case '"': - c = fromstream.get(); - if (!reading_object) { - myType = STRING; - strVal = read_string(c, fromstream); - stop = true; - } else { - std::string tmpstr = read_string(c, fromstream); - (*this)[tmpstr] = JSON::Value(fromstream); - } - break; - case '-': - c = fromstream.get(); - negative = true; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - c = fromstream.get(); - if (myType != INTEGER && myType != DOUBLE){ - myType = INTEGER; - } - if (myType == INTEGER){ - intVal *= 10; - intVal += c - '0'; - }else{ - dblDivider *= 10; - dblVal += ((double)((c - '0')) / dblDivider); - } - break; - case '.': - c = fromstream.get(); - myType = DOUBLE; - if (negative){ - dblVal = -intVal; - dblDivider = -1; - }else{ - dblVal = intVal; - dblDivider = 1; - } - break; - case ',': - if (!reading_object && !reading_array) { - stop = true; - break; - } - c = fromstream.get(); - if (reading_array) { - append(JSON::Value(fromstream)); - } - break; - case '}': - if (reading_object) { - c = fromstream.get(); - } + switch (c){ + case '{': + reading_object = true; + c = fromstream.get(); + myType = OBJECT; + break; + case '[':{ + reading_array = true; + c = fromstream.get(); + myType = ARRAY; + Value tmp = JSON::Value(fromstream); + if (tmp.myType != EMPTY){append(tmp);} + break; + } + case '\'': + case '"': + c = fromstream.get(); + if (!reading_object){ + myType = STRING; + strVal = read_string(c, fromstream); + stop = true; + }else{ + std::string tmpstr = read_string(c, fromstream); + (*this)[tmpstr] = JSON::Value(fromstream); + } + break; + case '-': + c = fromstream.get(); + negative = true; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + c = fromstream.get(); + if (myType != INTEGER && myType != DOUBLE){myType = INTEGER;} + if (myType == INTEGER){ + intVal *= 10; + intVal += c - '0'; + }else{ + dblDivider *= 10; + dblVal += ((double)((c - '0')) / dblDivider); + } + break; + case '.': + c = fromstream.get(); + myType = DOUBLE; + if (negative){ + dblVal = -intVal; + dblDivider = -1; + }else{ + dblVal = intVal; + dblDivider = 1; + } + break; + case ',': + if (!reading_object && !reading_array){ stop = true; break; - case ']': - if (reading_array) { - c = fromstream.get(); - } - stop = true; - break; - case 't': - case 'T': - skipToEnd(fromstream); - myType = BOOL; - intVal = 1; - stop = true; - break; - case 'f': - case 'F': - skipToEnd(fromstream); - myType = BOOL; - intVal = 0; - stop = true; - break; - case 'n': - case 'N': - skipToEnd(fromstream); - myType = EMPTY; - stop = true; - break; - default: - c = fromstream.get(); //ignore this character - continue; - break; + } + c = fromstream.get(); + if (reading_array){append(JSON::Value(fromstream));} + break; + case '}': + if (reading_object){c = fromstream.get();} + stop = true; + break; + case ']': + if (reading_array){c = fromstream.get();} + stop = true; + break; + case 't': + case 'T': + skipToEnd(fromstream); + myType = BOOL; + intVal = 1; + stop = true; + break; + case 'f': + case 'F': + skipToEnd(fromstream); + myType = BOOL; + intVal = 0; + stop = true; + break; + case 'n': + case 'N': + skipToEnd(fromstream); + myType = EMPTY; + stop = true; + break; + default: + c = fromstream.get(); // ignore this character + continue; + break; } } - if (negative) { - intVal *= -1; - } + if (negative){intVal *= -1;} } /// Sets this JSON::Value to the given string. -JSON::Value::Value(const std::string & val) { +JSON::Value::Value(const std::string &val){ myType = STRING; strVal = val; intVal = 0; } /// Sets this JSON::Value to the given string. -JSON::Value::Value(const char * val) { +JSON::Value::Value(const char *val){ myType = STRING; strVal = val; intVal = 0; } /// Sets this JSON::Value to the given integer. -JSON::Value::Value(long long int val) { +JSON::Value::Value(long long int val){ + myType = INTEGER; + intVal = val; +} + +/// Sets this JSON::Value to the given integer. +JSON::Value::Value(uint32_t val){ + myType = INTEGER; + intVal = val; +} +/// Sets this JSON::Value to the given integer. +JSON::Value::Value(uint64_t val){ + myType = INTEGER; + intVal = val; +} +/// Sets this JSON::Value to the given integer. +JSON::Value::Value(int32_t val){ + myType = INTEGER; + intVal = val; +} +/// Sets this JSON::Value to the given integer. +JSON::Value::Value(int64_t val){ myType = INTEGER; intVal = val; } /// Sets this JSON::Value to the given double. -JSON::Value::Value(double val) { +JSON::Value::Value(double val){ myType = DOUBLE; dblVal = val; } /// Sets this JSON::Value to the given integer. -JSON::Value::Value(bool val) { +JSON::Value::Value(bool val){ myType = BOOL; - if (val){ - intVal = 1; - }else{ - intVal = 0; - } + intVal = (val ? 1 : 0); } /// Compares a JSON::Value to another for equality. -bool JSON::Value::operator==(const JSON::Value & rhs) const { - if (myType != rhs.myType){ - return false; - } - if (myType == INTEGER || myType == BOOL) { - return intVal == rhs.intVal; - } - if (myType == DOUBLE) { - return dblVal == rhs.dblVal; - } - if (myType == STRING) { - return strVal == rhs.strVal; - } - if (myType == EMPTY) { - return true; - } - if (size() != rhs.size()){ - return false; - } - if (myType == OBJECT) { +bool JSON::Value::operator==(const JSON::Value &rhs) const{ + if (myType != rhs.myType){return false;} + if (myType == INTEGER || myType == BOOL){return intVal == rhs.intVal;} + if (myType == DOUBLE){return dblVal == rhs.dblVal;} + if (myType == STRING){return strVal == rhs.strVal;} + if (myType == EMPTY){return true;} + if (size() != rhs.size()){return false;} + if (myType == OBJECT){ jsonForEachConst(*this, it){ - if (!rhs.isMember(it.key()) || *it != rhs[it.key()]) { - return false; - } + if (!rhs.isMember(it.key()) || *it != rhs[it.key()]){return false;} } return true; } - if (myType == ARRAY) { + if (myType == ARRAY){ jsonForEachConst(*this, it){ - if (*it != rhs[it.num()]) { - return false; - } + if (*it != rhs[it.num()]){return false;} } return true; } @@ -602,17 +532,15 @@ bool JSON::Value::operator==(const JSON::Value & rhs) const { } /// Compares a JSON::Value to another for equality. -bool JSON::Value::operator!=(const JSON::Value & rhs) const { +bool JSON::Value::operator!=(const JSON::Value &rhs) const{ return !((*this) == rhs); } -bool JSON::Value::compareExcept(const Value & rhs, const std::set & skip) const { - if (myType == OBJECT) { +bool JSON::Value::compareExcept(const Value &rhs, const std::set &skip) const{ + if (myType == OBJECT){ jsonForEachConst(*this, it){ if (skip.count(it.key())){continue;} - if (!rhs.isMember(it.key()) || !(*it).compareExcept(rhs[it.key()], skip)) { - return false; - } + if (!rhs.isMember(it.key()) || !(*it).compareExcept(rhs[it.key()], skip)){return false;} } jsonForEachConst(rhs, it){ if (skip.count(it.key())){continue;} @@ -620,7 +548,7 @@ bool JSON::Value::compareExcept(const Value & rhs, const std::set & } return true; } - if (myType == ARRAY) { + if (myType == ARRAY){ if (size() != rhs.size()){return false;} jsonForEachConst(*this, it){ if (!(*it).compareExcept(rhs[it.num()], skip)){return false;} @@ -630,13 +558,11 @@ bool JSON::Value::compareExcept(const Value & rhs, const std::set & return ((*this) == rhs); } -bool JSON::Value::compareOnly(const Value & rhs, const std::set & check) const { - if (myType == OBJECT) { +bool JSON::Value::compareOnly(const Value &rhs, const std::set &check) const{ + if (myType == OBJECT){ jsonForEachConst(*this, it){ if (!check.count(it.key())){continue;} - if (!rhs.isMember(it.key()) || !(*it).compareOnly(rhs[it.key()], check)) { - return false; - } + if (!rhs.isMember(it.key()) || !(*it).compareOnly(rhs[it.key()], check)){return false;} } jsonForEachConst(rhs, it){ if (!check.count(it.key())){continue;} @@ -644,7 +570,7 @@ bool JSON::Value::compareOnly(const Value & rhs, const std::set & c } return true; } - if (myType == ARRAY) { + if (myType == ARRAY){ if (size() != rhs.size()){return false;} jsonForEachConst(*this, it){ if (!(*it).compareOnly(rhs[it.num()], check)){return false;} @@ -656,7 +582,7 @@ bool JSON::Value::compareOnly(const Value & rhs, const std::set & c /// Completely clears the contents of this value, /// changing its type to NULL in the process. -void JSON::Value::null() { +void JSON::Value::null(){ shrink(0); strVal.clear(); intVal = 0; @@ -666,18 +592,12 @@ void JSON::Value::null() { } /// Assigns this JSON::Value to the given JSON::Value, skipping given member recursively. -JSON::Value & JSON::Value::assignFrom(const Value & rhs, const std::set & skip){ +JSON::Value &JSON::Value::assignFrom(const Value &rhs, const std::set &skip){ null(); myType = rhs.myType; - if (myType == STRING){ - strVal = rhs.strVal; - } - if (myType == BOOL || myType == INTEGER){ - intVal = rhs.intVal; - } - if (myType == DOUBLE){ - dblVal = rhs.dblVal; - } + if (myType == STRING){strVal = rhs.strVal;} + if (myType == BOOL || myType == INTEGER){intVal = rhs.intVal;} + if (myType == DOUBLE){dblVal = rhs.dblVal;} if (myType == OBJECT){ jsonForEachConst(rhs, i){ if (!skip.count(i.key())){ @@ -698,41 +618,31 @@ JSON::Value & JSON::Value::assignFrom(const Value & rhs, const std::set 0; - } - if (myType == ARRAY) { - return size() > 0; - } - if (myType == EMPTY) { - return false; - } - return false; //unimplemented? should never happen... +JSON::Value::operator bool() const{ + if (myType == STRING){return strVal != "";} + if (myType == DOUBLE){return dblVal != 0;} + if (myType == INTEGER || myType == BOOL){return intVal != 0;} + if (myType == OBJECT || myType == ARRAY){return size() > 0;} + return false; // Empty or 'unimplemented? should never happen...' } /// Explicit conversion to std::string. -const std::string JSON::Value::asString() const { +std::string JSON::Value::asString() const{ return (std::string) * this; } /// Explicit conversion to long long int. -const long long int JSON::Value::asInt() const { - return (long long int) * this; +int64_t JSON::Value::asInt() const{ + return (int64_t) * this; } /// Explicit conversion to double. -const double JSON::Value::asDouble() const { - return (double) * this; +const double JSON::Value::asDouble() const{ + return (double)*this; } /// Explicit conversion to bool. -const bool JSON::Value::asBool() const { - return (bool) * this; +bool JSON::Value::asBool() const{ + return (bool)*this; } /// Explicit conversion to std::string reference. /// Returns a direct reference for string type JSON::Value objects, /// but a reference to a static empty string otherwise. /// \warning Only save to use when the JSON::Value is a string type! -const std::string & JSON::Value::asStringRef() const { +const std::string &JSON::Value::asStringRef() const{ static std::string ugly_buffer; - if (myType == STRING) { - return strVal; - } + if (myType == STRING){return strVal;} return ugly_buffer; } @@ -872,21 +758,19 @@ const std::string & JSON::Value::asStringRef() const { /// Returns a direct reference for string type JSON::Value objects, /// a reference to an empty string otherwise. /// \warning Only save to use when the JSON::Value is a string type! -const char * JSON::Value::c_str() const { - if (myType == STRING) { - return strVal.c_str(); - } +const char *JSON::Value::c_str() const{ + if (myType == STRING){return strVal.c_str();} return ""; } /// Retrieves or sets the JSON::Value at this position in the object. /// Converts destructively to object if not already an object. -JSON::Value & JSON::Value::operator[](const std::string i) { - if (myType != OBJECT) { +JSON::Value &JSON::Value::operator[](const std::string &i){ + if (myType != OBJECT){ null(); myType = OBJECT; } - Value * pntr = objVal[i]; + Value *pntr = objVal[i]; if (!pntr){ objVal[i] = new JSON::Value(); pntr = objVal[i]; @@ -896,12 +780,12 @@ JSON::Value & JSON::Value::operator[](const std::string i) { /// Retrieves or sets the JSON::Value at this position in the object. /// Converts destructively to object if not already an object. -JSON::Value & JSON::Value::operator[](const char * i) { - if (myType != OBJECT) { +JSON::Value &JSON::Value::operator[](const char *i){ + if (myType != OBJECT){ null(); myType = OBJECT; } - Value * pntr = objVal[i]; + Value *pntr = objVal[i]; if (!pntr){ objVal[i] = new JSON::Value(); pntr = objVal[i]; @@ -911,54 +795,52 @@ JSON::Value & JSON::Value::operator[](const char * i) { /// Retrieves or sets the JSON::Value at this position in the array. /// Converts destructively to array if not already an array. -JSON::Value & JSON::Value::operator[](unsigned int i) { +JSON::Value &JSON::Value::operator[](uint32_t i){ static JSON::Value empty; - if (myType != ARRAY) { + if (myType != ARRAY){ null(); myType = ARRAY; } - while (i >= arrVal.size()) { - append(empty); - } + while (i >= arrVal.size()){append(empty);} return *arrVal[i]; } /// Retrieves the JSON::Value at this position in the object. /// Fails horribly if that values does not exist. -const JSON::Value & JSON::Value::operator[](const std::string i) const { +const JSON::Value &JSON::Value::operator[](const std::string &i) const{ return *objVal.find(i)->second; } /// Retrieves the JSON::Value at this position in the object. /// Fails horribly if that values does not exist. -const JSON::Value & JSON::Value::operator[](const char * i) const { +const JSON::Value &JSON::Value::operator[](const char *i) const{ return *objVal.find(i)->second; } /// Retrieves the JSON::Value at this position in the array. /// Fails horribly if that values does not exist. -const JSON::Value & JSON::Value::operator[](unsigned int i) const { +const JSON::Value &JSON::Value::operator[](uint32_t i) const{ return *arrVal[i]; } /// Packs to a std::string for transfer over the network. -/// If the object is a container type, this function will call itself recursively and contain all contents. -/// As a side effect, this function clear the internal buffer of any object-types. -std::string JSON::Value::toPacked() const { +/// If the object is a container type, this function will call itself recursively and contain all +/// contents. As a side effect, this function clear the internal buffer of any object-types. +std::string JSON::Value::toPacked() const{ std::string r; - if (isInt() || isNull() || isBool()) { + if (isInt() || isNull() || isBool()){ r += 0x01; uint64_t numval = intVal; - r += *(((char *) &numval) + 7); - r += *(((char *) &numval) + 6); - r += *(((char *) &numval) + 5); - r += *(((char *) &numval) + 4); - r += *(((char *) &numval) + 3); - r += *(((char *) &numval) + 2); - r += *(((char *) &numval) + 1); - r += *(((char *) &numval)); + r += *(((char *)&numval) + 7); + r += *(((char *)&numval) + 6); + r += *(((char *)&numval) + 5); + r += *(((char *)&numval) + 4); + r += *(((char *)&numval) + 3); + r += *(((char *)&numval) + 2); + r += *(((char *)&numval) + 1); + r += *(((char *)&numval)); } - if (isString()) { + if (isString()){ r += 0x02; r += strVal.size() / (256 * 256 * 256); r += strVal.size() / (256 * 256); @@ -966,11 +848,11 @@ std::string JSON::Value::toPacked() const { r += strVal.size() % 256; r += strVal; } - if (isObject()) { + if (isObject()){ r += 0xE0; - if (objVal.size() > 0) { + if (objVal.size() > 0){ jsonForEachConst(*this, i){ - if (i.key().size() > 0) { + if (i.key().size() > 0){ r += i.key().size() / 256; r += i.key().size() % 256; r += i.key(); @@ -982,11 +864,9 @@ std::string JSON::Value::toPacked() const { r += (char)0x0; r += (char)0xEE; } - if (isArray()) { + if (isArray()){ r += 0x0A; - jsonForEachConst(*this, i){ - r += i->toPacked(); - } + jsonForEachConst(*this, i){r += i->toPacked();} r += (char)0x0; r += (char)0x0; r += (char)0xEE; @@ -1007,12 +887,12 @@ std::string JSON::Value::toPacked() const { } return r; } -//toPacked +// toPacked /// Packs and transfers over the network. /// If the object is a container type, this function will call itself recursively for all contents. -void JSON::Value::sendTo(Socket::Connection & socket) const { - if (isInt() || isNull() || isBool()) { +void JSON::Value::sendTo(Socket::Connection &socket) const{ + if (isInt() || isNull() || isBool()){ socket.SendNow("\001", 1); int tmpHalf = htonl((int)(intVal >> 32)); socket.SendNow((char *)&tmpHalf, 4); @@ -1020,21 +900,22 @@ void JSON::Value::sendTo(Socket::Connection & socket) const { socket.SendNow((char *)&tmpHalf, 4); return; } - if (isString()) { + if (isString()){ socket.SendNow("\002", 1); int tmpVal = htonl((int)strVal.size()); socket.SendNow((char *)&tmpVal, 4); socket.SendNow(strVal); return; } - if (isObject()) { - if (isMember("trackid") && isMember("time")) { + if (isObject()){ + if (isMember("trackid") && isMember("time")){ unsigned int trackid = objVal.find("trackid")->second->asInt(); long long time = objVal.find("time")->second->asInt(); unsigned int size = 16; - if (objVal.size() > 0) { + if (objVal.size() > 0){ jsonForEachConst(*this, i){ - if (i.key().size() > 0 && i.key() != "trackid" && i.key() != "time" && i.key() != "datatype") { + if (i.key().size() > 0 && i.key() != "trackid" && i.key() != "time" && + i.key() != "datatype"){ size += 2 + i.key().size() + i->packedSize(); } } @@ -1049,10 +930,11 @@ void JSON::Value::sendTo(Socket::Connection & socket) const { tmpHalf = htonl((int)(time & 0xFFFFFFFF)); socket.SendNow((char *)&tmpHalf, 4); socket.SendNow("\340", 1); - if (objVal.size() > 0) { + if (objVal.size() > 0){ jsonForEachConst(*this, i){ - if (i.key().size() > 0 && i.key() != "trackid" && i.key() != "time" && i.key() != "datatype") { - char sizebuffer[2] = {0, 0}; + if (i.key().size() > 0 && i.key() != "trackid" && i.key() != "time" && + i.key() != "datatype"){ + char sizebuffer[2] ={0, 0}; sizebuffer[0] = (i.key().size() >> 8) & 0xFF; sizebuffer[1] = i.key().size() & 0xFF; socket.SendNow(sizebuffer, 2); @@ -1064,16 +946,16 @@ void JSON::Value::sendTo(Socket::Connection & socket) const { socket.SendNow("\000\000\356", 3); return; } - if (isMember("tracks")) { + if (isMember("tracks")){ socket.SendNow("DTSC", 4); unsigned int size = htonl(packedSize()); socket.SendNow((char *)&size, 4); } socket.SendNow("\340", 1); - if (objVal.size() > 0) { + if (objVal.size() > 0){ jsonForEachConst(*this, i){ - if (i.key().size() > 0) { - char sizebuffer[2] = {0, 0}; + if (i.key().size() > 0){ + char sizebuffer[2] ={0, 0}; sizebuffer[0] = (i.key().size() >> 8) & 0xFF; sizebuffer[1] = i.key().size() & 0xFF; socket.SendNow(sizebuffer, 2); @@ -1085,280 +967,245 @@ void JSON::Value::sendTo(Socket::Connection & socket) const { socket.SendNow("\000\000\356", 3); return; } - if (isArray()) { + if (isArray()){ socket.SendNow("\012", 1); - jsonForEachConst(*this, i){ - i->sendTo(socket); - } + jsonForEachConst(*this, i){i->sendTo(socket);} socket.SendNow("\000\000\356", 3); return; } -}//sendTo +}// sendTo /// Returns the packed size of this Value. -unsigned int JSON::Value::packedSize() const { - if (isInt() || isNull() || isBool()) { - return 9; - } - if (isString()) { - return 5 + strVal.size(); - } - if (isObject()) { - unsigned int ret = 4; - if (objVal.size() > 0) { +uint64_t JSON::Value::packedSize() const{ + if (isInt() || isNull() || isBool()){return 9;} + if (isString()){return 5 + strVal.size();} + if (isObject()){ + uint64_t ret = 4; + if (objVal.size() > 0){ jsonForEachConst(*this, i){ - if (i.key().size() > 0) { - ret += 2 + i.key().size() + i->packedSize(); - } + if (i.key().size() > 0){ret += 2 + i.key().size() + i->packedSize();} } } return ret; } - if (isArray()) { - unsigned int ret = 4; - jsonForEachConst(*this, i){ - ret += i->packedSize(); - } + if (isArray()){ + uint64_t ret = 4; + jsonForEachConst(*this, i){ret += i->packedSize();} return ret; } return 0; -}//packedSize +}// packedSize -/// Pre-packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. -/// Non-object-types will print an error. -/// The internal buffer is guaranteed to be up-to-date after this function is called. -void JSON::Value::netPrepare() { - if (myType != OBJECT) { - DEBUG_MSG(DLVL_ERROR, "Only objects may be netpacked!"); +/// Pre-packs any object-type JSON::Value to a std::string for transfer over the network, including +/// proper DTMI header. Non-object-types will print an error. The internal buffer is guaranteed to +/// be up-to-date after this function is called. +void JSON::Value::netPrepare(){ + if (myType != OBJECT){ + ERROR_MSG("Only objects may be netpacked!"); return; } std::string packed = toPacked(); - //insert proper header for this type of data - int packID = -1; - long long unsigned int time = (*this)["time"].asInt(); + // insert proper header for this type of data + int32_t packID = -1; + int64_t time = (*this)["time"].asInt(); std::string dataType; - if (isMember("datatype") || isMember("trackid")) { + if (isMember("datatype") || isMember("trackid")){ dataType = (*this)["datatype"].asString(); - if (isMember("trackid")) { + if (isMember("trackid")){ packID = (*this)["trackid"].asInt(); - } else { - if ((*this)["datatype"].asString() == "video") { - packID = 1; - } - if ((*this)["datatype"].asString() == "audio") { - packID = 2; - } - if ((*this)["datatype"].asString() == "meta") { - packID = 3; - } - //endmark and the likes... - if (packID == -1) { - packID = 0; - } + }else{ + if ((*this)["datatype"].asString() == "video"){packID = 1;} + if ((*this)["datatype"].asString() == "audio"){packID = 2;} + if ((*this)["datatype"].asString() == "meta"){packID = 3;} + // endmark and the likes... + if (packID == -1){packID = 0;} } removeMember("time"); - if (packID != 0) { - removeMember("datatype"); - } + if (packID != 0){removeMember("datatype");} removeMember("trackid"); packed = toPacked(); - (*this)["time"] = (long long int)time; + (*this)["time"] = time; (*this)["datatype"] = dataType; (*this)["trackid"] = packID; strVal.resize(packed.size() + 20); memcpy((void *)strVal.c_str(), "DTP2", 4); - } else { + }else{ packID = -1; strVal.resize(packed.size() + 8); memcpy((void *)strVal.c_str(), "DTSC", 4); } - //insert the packet length at bytes 4-7 - unsigned int size = packed.size(); - if (packID != -1) { - size += 12; - } + // insert the packet length at bytes 4-7 + size_t size = packed.size(); + if (packID != -1){size += 12;} size = htonl(size); - memcpy((void *)(strVal.c_str() + 4), (void *) &size, 4); - //copy the rest of the string - if (packID == -1) { + memcpy((void *)(strVal.c_str() + 4), (void *)&size, 4); + // copy the rest of the string + if (packID == -1){ memcpy((void *)(strVal.c_str() + 8), packed.c_str(), packed.size()); return; } packID = htonl(packID); - memcpy((void *)(strVal.c_str() + 8), (void *) &packID, 4); + memcpy((void *)(strVal.c_str() + 8), (void *)&packID, 4); int tmpHalf = htonl((int)(time >> 32)); - memcpy((void *)(strVal.c_str() + 12), (void *) &tmpHalf, 4); + memcpy((void *)(strVal.c_str() + 12), (void *)&tmpHalf, 4); tmpHalf = htonl((int)(time & 0xFFFFFFFF)); - memcpy((void *)(strVal.c_str() + 16), (void *) &tmpHalf, 4); + memcpy((void *)(strVal.c_str() + 16), (void *)&tmpHalf, 4); memcpy((void *)(strVal.c_str() + 20), packed.c_str(), packed.size()); } -/// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. -/// Non-object-types will print an error and return an empty string. -/// This function returns a reference to an internal buffer where the prepared data is kept. -/// The internal buffer is *not* made stale if any changes occur inside the object - subsequent calls to toPacked() will clear the buffer, -/// calls to netPrepare will guarantee it is up-to-date. -std::string & JSON::Value::toNetPacked() { +/// Packs any object-type JSON::Value to a std::string for transfer over the network, including +/// proper DTMI header. Non-object-types will print an error and return an empty string. This +/// function returns a reference to an internal buffer where the prepared data is kept. The internal +/// buffer is *not* made stale if any changes occur inside the object - subsequent calls to +/// toPacked() will clear the buffer, calls to netPrepare will guarantee it is up-to-date. +std::string &JSON::Value::toNetPacked(){ static std::string emptystring; - //check if this is legal - if (myType != OBJECT) { + // check if this is legal + if (myType != OBJECT){ INFO_MSG("Ignored attempt to netpack a non-object."); return emptystring; } - //if sneaky storage doesn't contain correct data, re-calculate it - if (strVal.size() == 0 || strVal[0] != 'D' || strVal[1] != 'T') { - netPrepare(); - } + // if sneaky storage doesn't contain correct data, re-calculate it + if (strVal.size() == 0 || strVal[0] != 'D' || strVal[1] != 'T'){netPrepare();} return strVal; } /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes absolutely no attempts to pretty-print anything. :-) -std::string JSON::Value::toString() const { - switch (myType) { - case INTEGER: { - std::stringstream st; - st << intVal; - return st.str(); - break; - } - case DOUBLE: { - std::stringstream st; - st.precision(10); - st << std::fixed << dblVal; - return st.str(); - break; - } - case BOOL: { - if (intVal != 0){ - return "true"; - }else{ - return "false"; - } - break; - } - case STRING: { - return JSON::string_escape(strVal); - break; - } - case ARRAY: { - std::string tmp = "["; - if (arrVal.size() > 0) { - jsonForEachConst(*this, i){ - tmp += i->toString(); - if (i.num()+1 != arrVal.size()) { - tmp += ","; - } - } - } - tmp += "]"; - return tmp; - break; - } - case OBJECT: { - std::string tmp2 = "{"; - if (objVal.size() > 0) { - jsonForEachConst(*this, i){ - tmp2 += JSON::string_escape(i.key()) + ":"; - tmp2 += i->toString(); - if (i.num()+1 != objVal.size()) { - tmp2 += ","; - } - } - } - tmp2 += "}"; - return tmp2; - break; - } - case EMPTY: - default: - return "null"; +std::string JSON::Value::toString() const{ + switch (myType){ + case INTEGER:{ + std::stringstream st; + st << intVal; + return st.str(); + break; } - return "null"; //should never get here... + case DOUBLE:{ + std::stringstream st; + st.precision(10); + st << std::fixed << dblVal; + return st.str(); + break; + } + case BOOL:{ + if (intVal != 0){ + return "true"; + }else{ + return "false"; + } + break; + } + case STRING:{ + return JSON::string_escape(strVal); + break; + } + case ARRAY:{ + std::string tmp = "["; + if (arrVal.size() > 0){ + jsonForEachConst(*this, i){ + tmp += i->toString(); + if (i.num() + 1 != arrVal.size()){tmp += ",";} + } + } + tmp += "]"; + return tmp; + break; + } + case OBJECT:{ + std::string tmp2 = "{"; + if (objVal.size() > 0){ + jsonForEachConst(*this, i){ + tmp2 += JSON::string_escape(i.key()) + ":"; + tmp2 += i->toString(); + if (i.num() + 1 != objVal.size()){tmp2 += ",";} + } + } + tmp2 += "}"; + return tmp2; + break; + } + case EMPTY: + default: return "null"; + } + return "null"; // should never get here... } /// Converts this JSON::Value to valid JSON notation and returns it. /// Makes an attempt at pretty-printing. -std::string JSON::Value::toPrettyString(int indentation) const { - switch (myType) { - case INTEGER: { - std::stringstream st; - st << intVal; - return st.str(); - break; - } - case DOUBLE: { - std::stringstream st; - st.precision(10); - st << std::fixed << dblVal; - return st.str(); - break; - } - case BOOL: { - if (intVal != 0){ - return "true"; - }else{ - return "false"; - } - break; - } - case STRING: { - for (unsigned int i = 0; i < 201 && i < strVal.size(); ++i) { - if (strVal[i] < 32 || strVal[i] > 126 || strVal.size() > 200) { - return "\"" + JSON::Value((long long int)strVal.size()).asString() + " bytes of data\""; - } - } - return JSON::string_escape(strVal); - break; - } - case ARRAY: { - if (arrVal.size() > 0) { - std::string tmp = "[\n" + std::string(indentation + 2, ' '); - jsonForEachConst(*this, i){ - tmp += i->toPrettyString(indentation + 2); - if (i.num() + 1 != arrVal.size()) { - tmp += ", "; - } - } - tmp += "\n" + std::string(indentation, ' ') + "]"; - return tmp; - } else { - return "[]"; - } - break; - } - case OBJECT: { - if (objVal.size() > 0) { - bool shortMode = false; - if (size() <= 3 && isMember("len")) { - shortMode = true; - } - std::string tmp2 = "{" + std::string((shortMode ? "" : "\n")); - jsonForEachConst(*this, i){ - tmp2 += (shortMode ? std::string("") : std::string(indentation + 2, ' ')) + JSON::string_escape(i.key()) + ":"; - tmp2 += i->toPrettyString(indentation + 2); - if (i.num() + 1 != objVal.size()) { - tmp2 += "," + std::string((shortMode ? " " : "\n")); - } - } - tmp2 += (shortMode ? std::string("") : "\n" + std::string(indentation, ' ')) + "}"; - return tmp2; - } else { - return "{}"; - } - break; - } - case EMPTY: - default: - return "null"; +std::string JSON::Value::toPrettyString(size_t indentation) const{ + switch (myType){ + case INTEGER:{ + std::stringstream st; + st << intVal; + return st.str(); + break; } - return "null"; //should never get here... + case DOUBLE:{ + std::stringstream st; + st.precision(10); + st << std::fixed << dblVal; + return st.str(); + break; + } + case BOOL:{ + if (intVal != 0){ + return "true"; + }else{ + return "false"; + } + break; + } + case STRING:{ + for (uint8_t i = 0; i < 201 && i < strVal.size(); ++i){ + if (strVal[i] < 32 || strVal[i] > 126 || strVal.size() > 200){ + return "\"" + JSON::Value((int64_t)strVal.size()).asString() + " bytes of data\""; + } + } + return JSON::string_escape(strVal); + break; + } + case ARRAY:{ + if (arrVal.size() > 0){ + std::string tmp = "[\n" + std::string(indentation + 2, ' '); + jsonForEachConst(*this, i){ + tmp += i->toPrettyString(indentation + 2); + if (i.num() + 1 != arrVal.size()){tmp += ", ";} + } + tmp += "\n" + std::string(indentation, ' ') + "]"; + return tmp; + }else{ + return "[]"; + } + break; + } + case OBJECT:{ + if (objVal.size() > 0){ + bool shortMode = false; + if (size() <= 3 && isMember("len")){shortMode = true;} + std::string tmp2 = "{" + std::string((shortMode ? "" : "\n")); + jsonForEachConst(*this, i){ + tmp2 += (shortMode ? std::string("") : std::string(indentation + 2, ' ')) + + JSON::string_escape(i.key()) + ":"; + tmp2 += i->toPrettyString(indentation + 2); + if (i.num() + 1 != objVal.size()){tmp2 += "," + std::string((shortMode ? " " : "\n"));} + } + tmp2 += (shortMode ? std::string("") : "\n" + std::string(indentation, ' ')) + "}"; + return tmp2; + }else{ + return "{}"; + } + break; + } + case EMPTY: + default: return "null"; + } + return "null"; // should never get here... } /// Appends the given value to the end of this JSON::Value array. /// Turns this value into an array if it is not already one. -void JSON::Value::append(const JSON::Value & rhs) { - if (myType != ARRAY) { +void JSON::Value::append(const JSON::Value &rhs){ + if (myType != ARRAY){ null(); myType = ARRAY; } @@ -1367,8 +1214,8 @@ void JSON::Value::append(const JSON::Value & rhs) { /// Prepends the given value to the beginning of this JSON::Value array. /// Turns this value into an array if it is not already one. -void JSON::Value::prepend(const JSON::Value & rhs) { - if (myType != ARRAY) { +void JSON::Value::prepend(const JSON::Value &rhs){ + if (myType != ARRAY){ null(); myType = ARRAY; } @@ -1381,12 +1228,12 @@ void JSON::Value::prepend(const JSON::Value & rhs) { /// Does nothing for other JSON::Value types, nor does it /// do anything if the size is already lower or equal to the /// given size. -void JSON::Value::shrink(unsigned int size) { - while (arrVal.size() > size) { +void JSON::Value::shrink(unsigned int size){ + while (arrVal.size() > size){ delete arrVal.front(); arrVal.pop_front(); } - while (objVal.size() > size) { + while (objVal.size() > size){ delete objVal.begin()->second; objVal.erase(objVal.begin()); } @@ -1394,19 +1241,19 @@ void JSON::Value::shrink(unsigned int size) { /// For object JSON::Value objects, removes the member with /// the given name, if it exists. Has no effect otherwise. -void JSON::Value::removeMember(const std::string & name) { +void JSON::Value::removeMember(const std::string &name){ if (objVal.count(name)){ delete objVal[name]; objVal.erase(name); } } -void JSON::Value::removeMember(const std::deque::iterator & it){ +void JSON::Value::removeMember(const std::deque::iterator &it){ delete (*it); arrVal.erase(it); } -void JSON::Value::removeMember(const std::map::iterator & it){ +void JSON::Value::removeMember(const std::map::iterator &it){ delete it->second; objVal.erase(it); } @@ -1427,64 +1274,64 @@ void JSON::Value::removeNullMembers(){ /// For object JSON::Value objects, returns true if the /// given name is a member. Returns false otherwise. -bool JSON::Value::isMember(const std::string & name) const { +bool JSON::Value::isMember(const std::string &name) const{ return objVal.count(name) > 0; } /// Returns true if this object is an integer. -bool JSON::Value::isInt() const { +bool JSON::Value::isInt() const{ return (myType == INTEGER); } /// Returns true if this object is a double. -bool JSON::Value::isDouble() const { +bool JSON::Value::isDouble() const{ return (myType == DOUBLE); } /// Returns true if this object is a string. -bool JSON::Value::isString() const { +bool JSON::Value::isString() const{ return (myType == STRING); } /// Returns true if this object is a bool. -bool JSON::Value::isBool() const { +bool JSON::Value::isBool() const{ return (myType == BOOL); } /// Returns true if this object is an object. -bool JSON::Value::isObject() const { +bool JSON::Value::isObject() const{ return (myType == OBJECT); } /// Returns true if this object is an array. -bool JSON::Value::isArray() const { +bool JSON::Value::isArray() const{ return (myType == ARRAY); } /// Returns true if this object is null. -bool JSON::Value::isNull() const { +bool JSON::Value::isNull() const{ return (myType == EMPTY); } /// Returns the total of the objects and array size combined. -unsigned int JSON::Value::size() const { +unsigned int JSON::Value::size() const{ return objVal.size() + arrVal.size(); } /// Converts a std::string to a JSON::Value. -JSON::Value JSON::fromString(const char * data, const uint32_t data_len) { - const std::string str(data, data_len); +JSON::Value JSON::fromString(const char *data, uint32_t data_len){ + std::string str(data, data_len); return JSON::fromString(str); } /// Converts a std::string to a JSON::Value. -JSON::Value JSON::fromString(const std::string & json) { +JSON::Value JSON::fromString(const std::string &json){ std::istringstream is(json); return JSON::Value(is); } /// Converts a file to a JSON::Value. -JSON::Value JSON::fromFile(std::string filename) { +JSON::Value JSON::fromFile(const std::string &filename){ std::ifstream File; File.open(filename.c_str()); JSON::Value ret(File); @@ -1498,7 +1345,7 @@ JSON::Value JSON::fromFile(std::string filename) { /// \param len The size of the raw data. /// \param i Current parsing position in the raw data (defaults to 0). /// \returns A single JSON::Value, parsed from the raw data. -JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i) { +JSON::Value JSON::fromDTMI(const char *data, uint64_t len, uint32_t &i){ JSON::Value ret; fromDTMI(data, len, i, ret); return ret; @@ -1510,123 +1357,113 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne /// \param len The size of the raw data. /// \param i Current parsing position in the raw data (defaults to 0). /// \param ret Will be set to JSON::Value, parsed from the raw data. -void JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i, JSON::Value & ret) { +void JSON::fromDTMI(const char *data, uint64_t len, uint32_t &i, JSON::Value &ret){ ret.null(); - if (i >= len) { + if (i >= len){return;} + switch (data[i]){ + case 0x01:{// integer + if (i + 8 >= len){return;} + unsigned char tmpdbl[8]; + tmpdbl[7] = data[i + 1]; + tmpdbl[6] = data[i + 2]; + tmpdbl[5] = data[i + 3]; + tmpdbl[4] = data[i + 4]; + tmpdbl[3] = data[i + 5]; + tmpdbl[2] = data[i + 6]; + tmpdbl[1] = data[i + 7]; + tmpdbl[0] = data[i + 8]; + i += 9; // skip 8(an uint64_t)+1 forwards + ret = *(int64_t *)tmpdbl; return; + break; } - switch (data[i]) { - case 0x01: { //integer - if (i + 8 >= len) { - return; - } - unsigned char tmpdbl[8]; - tmpdbl[7] = data[i + 1]; - tmpdbl[6] = data[i + 2]; - tmpdbl[5] = data[i + 3]; - tmpdbl[4] = data[i + 4]; - tmpdbl[3] = data[i + 5]; - tmpdbl[2] = data[i + 6]; - tmpdbl[1] = data[i + 7]; - tmpdbl[0] = data[i + 8]; - i += 9; //skip 8(an uint64_t)+1 forwards - uint64_t * d = (uint64_t *)tmpdbl; - ret = (long long int) * d; - return; - break; - } - case 0x02: { //string - if (i + 4 >= len) { - return; - } - unsigned int tmpi = data[i + 1] * 256 * 256 * 256 + data[i + 2] * 256 * 256 + data[i + 3] * 256 + data[i + 4]; //set tmpi to UTF-8-long length - std::string tmpstr = std::string((const char *)data + i + 5, (size_t)tmpi); //set the string data - if (i + 4 + tmpi >= len) { - return; - } - i += tmpi + 5; //skip length+size+1 forwards - ret = tmpstr; - return; - break; - } - case 0xFF: //also object - case 0xE0: { //object - ++i; - while (data[i] + data[i + 1] != 0 && i < len) { //while not encountering 0x0000 (we assume 0x0000EE) - if (i + 2 >= len) { - return; - } - unsigned int tmpi = data[i] * 256 + data[i + 1]; //set tmpi to the UTF-8 length - std::string tmpstr = std::string((const char *)data + i + 2, (size_t)tmpi); //set the string data - i += tmpi + 2; //skip length+size forwards - ret[tmpstr].null(); - fromDTMI(data, len, i, ret[tmpstr]); //add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3; //skip 0x0000EE - return; - break; - } - case 0x0A: { //array - ++i; - while (data[i] + data[i + 1] != 0 && i < len) { //while not encountering 0x0000 (we assume 0x0000EE) - JSON::Value tval; - fromDTMI(data, len, i, tval); //add content, recursively parsed, updating i - ret.append(tval); - } - i += 3; //skip 0x0000EE - return; - break; - } + case 0x02:{// string + if (i + 4 >= len){return;} + uint32_t tmpi = Bit::btohl(data + i + 1); // set tmpi to UTF-8-long length + std::string tmpstr = std::string(data + i + 5, tmpi); // set the string data + if (i + 4 + tmpi >= len){return;} + i += tmpi + 5; // skip length+size+1 forwards + ret = tmpstr; + return; + break; } - DEBUG_MSG(DLVL_FAIL, "Unimplemented DTMI type %hhx, @ %i / %i - returning.", data[i], i, len); + case 0xFF: // also object + case 0xE0:{// object + ++i; + while (data[i] + data[i + 1] != 0 && + i < len){// while not encountering 0x0000 (we assume 0x0000EE) + if (i + 2 >= len){return;} + uint16_t tmpi = Bit::btohs(data + i); // set tmpi to the UTF-8 length + std::string tmpstr = std::string(data + i + 2, tmpi); // set the string data + i += tmpi + 2; // skip length+size forwards + ret[tmpstr].null(); + fromDTMI( + data, len, i, + ret[tmpstr]); // add content, recursively parsed, updating i, setting indice to tmpstr + } + i += 3; // skip 0x0000EE + return; + break; + } + case 0x0A:{// array + ++i; + while (data[i] + data[i + 1] != 0 && + i < len){// while not encountering 0x0000 (we assume 0x0000EE) + JSON::Value tval; + fromDTMI(data, len, i, tval); // add content, recursively parsed, updating i + ret.append(tval); + } + i += 3; // skip 0x0000EE + return; + break; + } + } + FAIL_MSG("Unimplemented DTMI type %hhx, @ %i / %" PRIu64 " - returning.", data[i], i, len); i += 1; return; -} //fromOneDTMI +}// fromOneDTMI /// Parses a std::string to a valid JSON::Value. /// This function will find one DTMI object in the string and return it. -void JSON::fromDTMI(std::string & data, JSON::Value & ret) { - unsigned int i = 0; - return fromDTMI((const unsigned char *)data.c_str(), data.size(), i, ret); -} //fromDTMI +void JSON::fromDTMI(const std::string &data, JSON::Value &ret){ + uint32_t i = 0; + return fromDTMI(data.c_str(), data.size(), i, ret); +}// fromDTMI /// Parses a std::string to a valid JSON::Value. /// This function will find one DTMI object in the string and return it. -JSON::Value JSON::fromDTMI(std::string & data) { - unsigned int i = 0; - return fromDTMI((const unsigned char *)data.c_str(), data.size(), i); -} //fromDTMI +JSON::Value JSON::fromDTMI(const std::string &data){ + uint32_t i = 0; + return fromDTMI(data.c_str(), data.size(), i); +}// fromDTMI -void JSON::fromDTMI2(std::string & data, JSON::Value & ret) { - unsigned int i = 0; - fromDTMI2((const unsigned char *)data.c_str(), data.size(), i, ret); +void JSON::fromDTMI2(const std::string &data, JSON::Value &ret){ + uint32_t i = 0; + fromDTMI2(data.c_str(), data.size(), i, ret); return; } -JSON::Value JSON::fromDTMI2(std::string & data) { +JSON::Value JSON::fromDTMI2(const std::string &data){ JSON::Value ret; - unsigned int i = 0; - fromDTMI2((const unsigned char *)data.c_str(), data.size(), i, ret); + uint32_t i = 0; + fromDTMI2(data.c_str(), data.size(), i, ret); return ret; } -void JSON::fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i, JSON::Value & ret) { - if (len < 13) { - return; - } - long long int tmpTrackID = ntohl(((int *)(data + i))[0]); - long long int tmpTime = ntohl(((int *)(data + i))[1]); - tmpTime <<= 32; - tmpTime += ntohl(((int *)(data + i))[2]); +void JSON::fromDTMI2(const char *data, uint64_t len, uint32_t &i, JSON::Value &ret){ + if (len < 13){return;} + uint32_t tid = Bit::btohl(data + i); + uint64_t time = Bit::btohll(data + 4); i += 12; fromDTMI(data, len, i, ret); - ret["time"] = tmpTime; - ret["trackid"] = tmpTrackID; + ret["time"] = time; + ret["trackid"] = tid; return; } -JSON::Value JSON::fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i) { +JSON::Value JSON::fromDTMI2(const char *data, uint64_t len, uint32_t &i){ JSON::Value ret; fromDTMI2(data, len, i, ret); return ret; } + diff --git a/lib/json.h b/lib/json.h index e3168d46..c1d984ba 100644 --- a/lib/json.h +++ b/lib/json.h @@ -1,217 +1,161 @@ /// \file json.h Holds all JSON-related headers. #pragma once -#include -#include -#include -#include -#include -#include #include "socket.h" +#include +#include +#include +#include +#include +#include +#include /// JSON-related classes and functions -namespace JSON { +namespace JSON{ /// Lists all types of JSON::Value. - enum ValueType { - EMPTY, BOOL, INTEGER, DOUBLE, STRING, ARRAY, OBJECT - }; + enum ValueType{EMPTY, BOOL, INTEGER, DOUBLE, STRING, ARRAY, OBJECT}; /// JSON-string-escapes a value - std::string string_escape(const std::string & val); + std::string string_escape(const std::string &val); /// A JSON::Value is either a string or an integer, but may also be an object, array or null. - class Value { + class Value{ friend class Iter; friend class ConstIter; - private: - ValueType myType; - long long int intVal; - std::string strVal; - double dblVal; - double dblDivider; - std::deque arrVal; - std::map objVal; - public: - //constructors/destructors - Value(); - ~Value(); - Value(const Value & rhs); - Value(std::istream & fromstream); - Value(const std::string & val); - Value(const char * val); - Value(long long int val); - Value(double val); - Value(bool val); - //comparison operators - bool operator==(const Value & rhs) const; - bool operator!=(const Value & rhs) const; - bool compareExcept(const Value & rhs, const std::set & skip) const; - bool compareOnly(const Value & rhs, const std::set & check) const; - //assignment operators - Value & assignFrom(const Value & rhs, const std::set & skip); - Value & operator=(const Value & rhs); - Value & operator=(const std::string & rhs); - Value & operator=(const char * rhs); - Value & operator=(const long long int & rhs); - Value & operator=(const int & rhs); - Value & operator=(const double & rhs); - Value & operator=(const unsigned int & rhs); - Value & operator=(const bool & rhs); - //converts to basic types - operator long long int() const; - operator std::string() const; - operator bool() const; - operator double() const; - const std::string asString() const; - const long long int asInt() const; - const double asDouble() const; - const bool asBool() const; - const std::string & asStringRef() const; - const char * c_str() const; - //array operator for maps and arrays - Value & operator[](const std::string i); - Value & operator[](const char * i); - Value & operator[](unsigned int i); - const Value & operator[](const std::string i) const; - const Value & operator[](const char * i) const; - const Value & operator[](unsigned int i) const; - //handy functions and others - std::string toPacked() const; - void sendTo(Socket::Connection & socket) const; - unsigned int packedSize() const; - void netPrepare(); - std::string & toNetPacked(); - std::string toString() const; - std::string toPrettyString(int indentation = 0) const; - void append(const Value & rhs); - void prepend(const Value & rhs); - void shrink(unsigned int size); - void removeMember(const std::string & name); - void removeMember(const std::deque::iterator & it); - void removeMember(const std::map::iterator & it); - void removeNullMembers(); - bool isMember(const std::string & name) const; - bool isInt() const; - bool isDouble() const; - bool isString() const; - bool isBool() const; - bool isObject() const; - bool isArray() const; - bool isNull() const; - unsigned int size() const; - void null(); + + private: + ValueType myType; + long long int intVal; + std::string strVal; + double dblVal; + double dblDivider; + std::deque arrVal; + std::map objVal; + + public: + // constructors/destructors + Value(); + ~Value(); + Value(const Value &rhs); + Value(std::istream &fromstream); + Value(const std::string &val); + Value(const char *val); + Value(long long int val); + Value(int32_t val); + Value(int64_t val); + Value(uint32_t val); + Value(uint64_t val); + Value(double val); + Value(bool val); + // comparison operators + bool operator==(const Value &rhs) const; + bool operator!=(const Value &rhs) const; + bool compareExcept(const Value &rhs, const std::set &skip) const; + bool compareOnly(const Value &rhs, const std::set &check) const; + // assignment operators + Value &assignFrom(const Value &rhs, const std::set &skip); + Value &operator=(const Value &rhs); + Value &operator=(const std::string &rhs); + Value &operator=(const char *rhs); + Value &operator=(const long long int &rhs); + Value &operator=(const int64_t &rhs); + Value &operator=(const int32_t &rhs); + Value &operator=(const uint64_t &rhs); + Value &operator=(const uint32_t &rhs); + Value &operator=(const double &rhs); + Value &operator=(const bool &rhs); + // converts to basic types + operator int64_t() const; + operator std::string() const; + operator bool() const; + operator double() const; + std::string asString() const; + int64_t asInt() const; + bool asBool() const; + const double asDouble() const; + const std::string &asStringRef() const; + const char *c_str() const; + // array operator for maps and arrays + Value &operator[](const std::string &i); + Value &operator[](const char *i); + Value &operator[](uint32_t i); + const Value &operator[](const std::string &i) const; + const Value &operator[](const char *i) const; + const Value &operator[](uint32_t i) const; + // handy functions and others + std::string toPacked() const; + void sendTo(Socket::Connection &socket) const; + uint64_t packedSize() const; + void netPrepare(); + std::string &toNetPacked(); + std::string toString() const; + std::string toPrettyString(size_t indent = 0) const; + void append(const Value &rhs); + void prepend(const Value &rhs); + void shrink(uint32_t size); + void removeMember(const std::string &name); + void removeMember(const std::deque::iterator &it); + void removeMember(const std::map::iterator &it); + void removeNullMembers(); + bool isMember(const std::string &name) const; + bool isInt() const; + bool isDouble() const; + bool isString() const; + bool isBool() const; + bool isObject() const; + bool isArray() const; + bool isNull() const; + uint32_t size() const; + void null(); }; - Value fromDTMI2(std::string & data); - Value fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i); - Value fromDTMI(std::string & data); - Value fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i); - Value fromString(const std::string & json); - Value fromString(const char * data, const uint32_t data_len); - Value fromFile(std::string filename); - void fromDTMI2(std::string & data, Value & ret); - void fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i, Value & ret); - void fromDTMI(std::string & data, Value & ret); - void fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i, Value & ret); + Value fromDTMI2(const std::string &data); + Value fromDTMI2(const char *data, uint64_t len, uint32_t &i); + Value fromDTMI(const std::string &data); + Value fromDTMI(const char *data, uint64_t len, uint32_t &i); + Value fromString(const std::string &json); + Value fromString(const char *data, uint32_t data_len); + Value fromFile(const std::string &filename); + void fromDTMI2(const std::string &data, Value &ret); + void fromDTMI2(const char *data, uint64_t len, uint32_t &i, Value &ret); + void fromDTMI(const std::string &data, Value &ret); + void fromDTMI(const char *data, uint64_t len, uint32_t &i, Value &ret); - class Iter { - public: - Iter(Value & root);///() const;///< Dereferences into a Value reference. - operator bool() const;///< True if not done iterating. - Iter & operator++();///::iterator aIt; - std::map::iterator oIt; + class Iter{ + public: + Iter(Value &root); ///< Construct from a root Value to iterate over. + Value &operator*() const; ///< Dereferences into a Value reference. + Value *operator->() const; ///< Dereferences into a Value reference. + operator bool() const; ///< True if not done iterating. + Iter &operator++(); ///< Go to next iteration. + const std::string &key() const; ///< Return the name of the current indice. + uint32_t num() const; ///< Return the number of the current indice. + void remove(); ///< Delete the current indice from the parent JSON::Value. + private: + ValueType myType; + Value *r; + uint32_t i; + std::deque::iterator aIt; + std::map::iterator oIt; }; - class ConstIter { - public: - ConstIter(const Value & root);///() const;///< Dereferences into a Value reference. - operator bool() const;///< True if not done iterating. - ConstIter & operator++();///::const_iterator aIt; - std::map::const_iterator oIt; + class ConstIter{ + public: + ConstIter(const Value &root); ///< Construct from a root Value to iterate over. + const Value &operator*() const; ///< Dereferences into a Value reference. + const Value *operator->() const; ///< Dereferences into a Value reference. + operator bool() const; ///< True if not done iterating. + ConstIter &operator++(); ///< Go to next iteration. + const std::string &key() const; ///< Return the name of the current indice. + uint32_t num() const; ///< Return the number of the current indice. + private: + ValueType myType; + const Value *r; + uint32_t i; + std::deque::const_iterator aIt; + std::map::const_iterator oIt; }; - #define jsonForEach(val, i) for(JSON::Iter i(val); i; ++i) - #define jsonForEachConst(val, i) for(JSON::ConstIter i(val); i; ++i) +#define jsonForEach(val, i) for (JSON::Iter i(val); i; ++i) +#define jsonForEachConst(val, i) for (JSON::ConstIter i(val); i; ++i) +}// namespace JSON - template - std::string encodeVector(T begin, T end) { - std::string result; - for (T it = begin; it != end; it++) { - long long int tmp = (*it); - while (tmp >= 0xFFFF) { - result += (char)0xFF; - result += (char)0xFF; - tmp -= 0xFFFF; - } - result += (char)(tmp / 256); - result += (char)(tmp % 256); - } - return result; - } - - template - void decodeVector(std::string input, T & result) { - result.clear(); - unsigned int tmp = 0; - for (int i = 0; i < input.size(); i += 2) { - unsigned int curLen = (input[i] << 8) + input[i + 1]; - tmp += curLen; - if (curLen != 0xFFFF) { - result.push_back(tmp); - tmp = 0; - } - } - } - - template - std::string encodeVector4(T begin, T end) { - std::string result; - for (T it = begin; it != end; it++) { - long long int tmp = (*it); - while (tmp >= 0xFFFFFFFF) { - result += (char)0xFF; - result += (char)0xFF; - result += (char)0xFF; - result += (char)0xFF; - tmp -= 0xFFFFFFFF; - } - result += (char)((tmp & 0xFF000000) >> 24); - result += (char)((tmp & 0x00FF0000) >> 16); - result += (char)((tmp & 0x0000FF00) >> 8); - result += (char)((tmp & 0x000000FF)); - } - return result; - } - - template - void decodeVector4(std::string input, T & result) { - result.clear(); - unsigned int tmp = 0; - for (int i = 0; i < input.size(); i += 4) { - unsigned int curLen = (input[i] << 24) + (input[i + 1] << 16) + (input[i + 2] << 8) + (input[i + 3]); - tmp += curLen; - if (curLen != 0xFFFFFFFF) { - result.push_back(tmp); - tmp = 0; - } - } - } -} diff --git a/lib/mp4.cpp b/lib/mp4.cpp index 4a7afced..cd2cbdb1 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -8,6 +8,7 @@ #include "json.h" #include "defines.h" +#include "bitfields.h" /// Contains all MP4 format related code. namespace MP4 { @@ -78,7 +79,7 @@ namespace MP4 { payloadOffset = rs.payloadOffset; } /// Returns the values at byte positions 4 through 7. - std::string Box::getType() { + std::string Box::getType() const { return std::string(data + 4, 4); } @@ -188,7 +189,7 @@ namespace MP4 { } /// Returns the total boxed size of this box, including the header. - uint64_t Box::boxedSize() { + uint64_t Box::boxedSize() const { if (payloadOffset == 16) { return ((uint64_t)ntohl(((int *)data)[2]) << 32) | ntohl(((int *)data)[3]); } @@ -197,7 +198,7 @@ namespace MP4 { /// Retruns the size of the payload of thix box, excluding the header. /// This value is defined as boxedSize() - 8. - uint64_t Box::payloadSize() { + uint64_t Box::payloadSize() const { return boxedSize() - payloadOffset; } @@ -230,7 +231,7 @@ namespace MP4 { /// Attempts to typecast this Box to a more specific type and call the toPrettyString() function of that type. /// If this failed, it will print out a message saying pretty-printing is not implemented for boxtype. - std::string Box::toPrettyString(uint32_t indent) { + std::string Box::toPrettyString(uint32_t indent) const { switch (ntohl(*((int *)(data + 4)))) { //type is at this address case 0x6D666864: return ((MFHD *)this)->toPrettyString(indent); @@ -366,10 +367,10 @@ namespace MP4 { return ((AAC *)this)->toPrettyString(indent); break; case 0x61766331: + case 0x656E6376://encv return ((AVC1 *)this)->toPrettyString(indent); break; case 0x68323634://h264 - case 0x656E6376://encv return ((H264 *)this)->toPrettyString(indent); break; case 0x65647473: @@ -397,6 +398,7 @@ namespace MP4 { return ((PASP*)this)->toPrettyString(indent); break; default: + INFO_MSG("no code found: 0x%.8x",Bit::btohl(data + 4)); break; } std::stringstream retval; @@ -432,6 +434,16 @@ namespace MP4 { return data[index]; } + /// Gets the 8 bits integer at the given index. + /// Returns zero if out of bounds. + char Box::getInt8(size_t index) const { + index += payloadOffset; + if (index >= boxedSize()) { + return 0; + } + return data[index]; + } + /// Sets the 16 bits integer at the given index. /// Attempts to resize the data pointer if the index is out of range. /// Fails silently if resizing failed. @@ -442,8 +454,7 @@ namespace MP4 { return; } } - newData = htons(newData); - memcpy(data + index, (char *) &newData, 2); + Bit::htobs(data + index, newData); } /// Gets the 16 bits integer at the given index. @@ -457,9 +468,18 @@ namespace MP4 { } setInt16(0, index - payloadOffset); } - short result; - memcpy((char *) &result, data + index, 2); - return ntohs(result); + return Bit::btohs(data + index); + } + + /// Gets the 16 bits integer at the given index. + /// Attempts to resize the data pointer if the index is out of range. + /// Returns zero if resizing failed. + short Box::getInt16(size_t index) const { + index += payloadOffset; + if (index + 1 >= boxedSize()) { + return 0; + } + return Bit::btohs(data + index); } /// Sets the 24 bits integer at the given index. @@ -472,9 +492,7 @@ namespace MP4 { return; } } - data[index] = (newData & 0x00FF0000) >> 16; - data[index + 1] = (newData & 0x0000FF00) >> 8; - data[index + 2] = (newData & 0x000000FF); + Bit::htob24(data + index, newData); } /// Gets the 24 bits integer at the given index. @@ -488,12 +506,18 @@ namespace MP4 { } setInt24(0, index - payloadOffset); } - uint32_t result = data[index]; - result <<= 8; - result += data[index + 1]; - result <<= 8; - result += data[index + 2]; - return result; + return Bit::btoh24(data + index); + } + + /// Gets the 24 bits integer at the given index. + /// Attempts to resize the data pointer if the index is out of range. + /// Returns zero if resizing failed. + uint32_t Box::getInt24(size_t index) const { + index += payloadOffset; + if (index + 2 >= boxedSize()) { + return 0; + } + return Bit::btoh24(data + index); } /// Sets the 32 bits integer at the given index. @@ -506,7 +530,7 @@ namespace MP4 { return; } } - ((int *)(data + index))[0] = htonl(newData); + Bit::htobl(data + index, newData); } /// Gets the 32 bits integer at the given index. @@ -520,7 +544,18 @@ namespace MP4 { } setInt32(0, index - payloadOffset); } - return ntohl(((int *)(data + index))[0]); + return Bit::btohl(data + index); + } + + /// Gets the 32 bits integer at the given index. + /// Attempts to resize the data pointer if the index is out of range. + /// Returns zero if resizing failed. + uint32_t Box::getInt32(size_t index) const { + index += payloadOffset; + if (index + 3 >= boxedSize()) { + return 0; + } + return Bit::btohl(data + index); } /// Sets the 64 bits integer at the given index. @@ -533,8 +568,7 @@ namespace MP4 { return; } } - ((int *)(data + index))[0] = htonl((int)(newData >> 32)); - ((int *)(data + index))[1] = htonl((int)(newData & 0xFFFFFFFF)); + Bit::htobll(data + index, newData); } /// Gets the 64 bits integer at the given index. @@ -548,10 +582,18 @@ namespace MP4 { } setInt64(0, index - payloadOffset); } - uint64_t result = ntohl(((int *)(data + index))[0]); - result <<= 32; - result += ntohl(((int *)(data + index))[1]); - return result; + return Bit::btohll(data + index); + } + + /// Gets the 64 bits integer at the given index. + /// Attempts to resize the data pointer if the index is out of range. + /// Returns zero if resizing failed. + uint64_t Box::getInt64(size_t index) const { + index += payloadOffset; + if (index + 7 >= boxedSize()) { + return 0; + } + return Bit::btohll(data + index); } /// Sets the NULL-terminated string at the given index. @@ -596,7 +638,7 @@ namespace MP4 { /// Returns the length of the NULL-terminated string at the given index. /// Returns 0 if out of range. - size_t Box::getStringLen(size_t index) { + size_t Box::getStringLen(size_t index) const { index += payloadOffset; if (index >= boxedSize()) { return 0; @@ -625,8 +667,8 @@ namespace MP4 { /// Returns the size of the box at the given position. /// Returns undefined values if there is no box at the given position. /// Returns 0 if out of range. - size_t Box::getBoxLen(size_t index) { - if ((index + payloadOffset + 8) > boxedSize()) { + size_t Box::getBoxLen(size_t index) const { + if ((index + payloadOffset + 8) >= boxedSize()) { return 0; } return calcBoxSize(data + index + payloadOffset); @@ -689,7 +731,7 @@ namespace MP4 { setInt8(newVersion, 0); } - char fullBox::getVersion() { + char fullBox::getVersion() const { return getInt8(0); } @@ -697,11 +739,11 @@ namespace MP4 { setInt24(newFlags, 1); } - uint32_t fullBox::getFlags() { + uint32_t fullBox::getFlags() const { return getInt24(1); } - std::string fullBox::toPrettyString(uint32_t indent) { + std::string fullBox::toPrettyString(uint32_t indent) const { std::stringstream r; r << std::string(indent + 1, ' ') << "Version: " << (int)getVersion() << std::endl; r << std::string(indent + 1, ' ') << "Flags: " << getFlags() << std::endl; diff --git a/lib/mp4.h b/lib/mp4.h index f817a171..245e49fb 100644 --- a/lib/mp4.h +++ b/lib/mp4.h @@ -31,40 +31,47 @@ namespace MP4 { } void copyFrom(const Box & rs); - std::string getType(); + std::string getType() const; bool isType(const char * boxType) const; bool read(FILE * newData); bool read(std::string & newData); - uint64_t boxedSize(); - uint64_t payloadSize(); + uint64_t boxedSize() const; + uint64_t payloadSize() const; char * asBox(); char * payload(); void clear(); - std::string toPrettyString(uint32_t indent = 0); + std::string toPrettyString(uint32_t indent = 0) const; protected: //integer functions void setInt8(char newData, size_t index); char getInt8(size_t index); + char getInt8(size_t index) const; void setInt16(short newData, size_t index); short getInt16(size_t index); + short getInt16(size_t index) const; void setInt24(uint32_t newData, size_t index); uint32_t getInt24(size_t index); + uint32_t getInt24(size_t index) const; void setInt32(uint32_t newData, size_t index); uint32_t getInt32(size_t index); + uint32_t getInt32(size_t index) const; void setInt64(uint64_t newData, size_t index); uint64_t getInt64(size_t index); + uint64_t getInt64(size_t index) const; //string functions void setString(std::string newData, size_t index); void setString(char * newData, size_t size, size_t index); char * getString(size_t index); - size_t getStringLen(size_t index); + size_t getStringLen(size_t index) const; //box functions Box & getBox(size_t index); - size_t getBoxLen(size_t index); + size_t getBoxLen(size_t index) const; void setBox(Box & newEntry, size_t index); //data functions bool reserve(size_t position, size_t current, size_t wanted); + bool reserve(size_t position, size_t current, size_t wanted) const {return false;} + //internal variables char * data; ///< Holds the data of this box unsigned int data_size; ///< Currently reserved size @@ -77,10 +84,10 @@ namespace MP4 { public: fullBox(); void setVersion(char newVersion); - char getVersion(); + char getVersion() const; void setFlags(uint32_t newFlags); - uint32_t getFlags(); - std::string toPrettyString(uint32_t indent = 0); + uint32_t getFlags() const; + std::string toPrettyString(uint32_t indent = 0) const ; }; class containerBox: public Box { diff --git a/lib/mp4_generic.cpp b/lib/mp4_generic.cpp index 7daef6d7..448bf167 100644 --- a/lib/mp4_generic.cpp +++ b/lib/mp4_generic.cpp @@ -472,6 +472,10 @@ namespace MP4 { return getInt32(offset); } + bool TFHD::getDefaultBaseIsMoof() { + return getFlags() & tfhdBaseIsMoof; + } + std::string TFHD::toPrettyString(uint32_t indent) { std::stringstream r; r << std::string(indent, ' ') << "[tfhd] Track Fragment Header (" << boxedSize() << ")" << std::endl; @@ -497,6 +501,9 @@ namespace MP4 { if (flags & tfhdNoDuration) { r << " NoDuration"; } + if (flags & tfhdBaseIsMoof) { + r << " BaseIsMoof"; + } r << std::endl; r << std::string(indent + 1, ' ') << "TrackID " << getTrackID() << std::endl; @@ -511,7 +518,7 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "Default Sample Duration " << getDefaultSampleDuration() << std::endl; } if (flags & tfhdSampleSize) { - r << std::string(indent + 1, ' ') << "Default Same Size " << getDefaultSampleSize() << std::endl; + r << std::string(indent + 1, ' ') << "Default Sample Size " << getDefaultSampleSize() << std::endl; } if (flags & tfhdSampleFlag) { r << std::string(indent + 1, ' ') << "Default Sample Flags " << prettySampleFlags(getDefaultSampleFlags()) << std::endl; @@ -567,6 +574,10 @@ namespace MP4 { } void AVCC::setSPS(std::string newSPS, size_t index) { + setSPS(newSPS.data(), newSPS.size(), index); + } + + void AVCC::setSPS(const char * data, size_t len, size_t index) { if (index >= getSPSCount()){ WARN_MSG("Cannot set entry at position %zu/%u: Out of bounds", index, getSPSCount()); } @@ -574,9 +585,9 @@ namespace MP4 { for (size_t i = 0; i < index; i++){ offset += getInt16(offset) + 2; } - setInt16(newSPS.size(), offset); - for (unsigned int i = 0; i < newSPS.size(); i++) { - setInt8(newSPS[i], offset + 2 + i); + setInt16(len, offset); + for (unsigned int i = 0; i < len; i++) { + setInt8(data[i], offset + 2 + i); } //not null-terminated } @@ -646,6 +657,9 @@ namespace MP4 { } void AVCC::setPPS(std::string newPPS, size_t index) { + setPPS(newPPS.data(), newPPS.size(), index); + } + void AVCC::setPPS(const char * data, size_t len, size_t index) { if (index >= getPPSCount()){ WARN_MSG("Cannot set entry at position %zu/%u: Out of bounds", index, getPPSCount()); } @@ -653,9 +667,9 @@ namespace MP4 { for (size_t i = 0; i < index; i++){ offset += getInt16(offset) + 2; } - setInt16(newPPS.size(), offset); - for (unsigned int i = 0; i < newPPS.size(); i++) { - setInt8(newPPS[i], offset + 2 + i); + setInt16(len, offset); + for (unsigned int i = 0; i < len; i++) { + setInt8(data[i], offset + 2 + i); } //not null-terminated } @@ -718,11 +732,90 @@ namespace MP4 { } void AVCC::setPayload(std::string newPayload) { - if (!reserve(0, payloadSize(), newPayload.size())) { + setPayload(newPayload.data(), newPayload.size()); + } + void AVCC::setPayload(const char * data, size_t len){ + if (!reserve(0, payloadSize(), len)){ ERROR_MSG("Cannot allocate enough memory for payload"); return; } - memcpy((char *)payload(), (char *)newPayload.c_str(), newPayload.size()); + memcpy((char *)payload(), (char *)data, len); + } + + bool AVCC::sanitize(){ + bool needSanitization = false; + size_t count = getSPSCount(); + for (size_t i = 0; i < count; i++){ + char * sps = getSPS(i); + if (memcmp("\000\000\000\001", sps, 4) == 0 || memcmp("\000\000\001", sps, 3)){ + needSanitization = true; + break; + } + } + if (!needSanitization){ + count = getPPSCount(); + for (size_t i = 0; i < count; i++){ + char * pps = getPPS(i); + if (memcmp("\000\000\000\001", pps, 4) == 0 || memcmp("\000\000\001", pps, 3)){ + needSanitization = true; + break; + } + } + } + if (!needSanitization){return false;} + AVCC sanitized; + sanitized.setVersion(getVersion()); + sanitized.setProfile(getProfile()); + sanitized.setCompatibleProfiles(getCompatibleProfiles()); + sanitized.setLevel(getLevel()); + + count = getSPSCount(); + sanitized.setSPSCount(count); + for (size_t i = 0; i < count; i++){ + char * sps = getSPS(i); + size_t len = getSPSLen(i); + bool modded = true; + while (modded){ + modded = false; + if (memcmp("\000\000\001", sps, 3) == 0){ + modded = true; + len -= 3; + sps += 3; + } + if (memcmp("\000\000\000\001", sps, 4) == 0){ + modded = true; + len -= 4; + sps += 4; + } + } + sanitized.setSPS(sps, len, i); + } + count = getPPSCount(); + sanitized.setPPSCount(count); + for (size_t i = 0; i < count; i++){ + char * pps = getPPS(i); + size_t len = getPPSLen(i); + bool modded = true; + while (modded){ + modded = false; + if (memcmp("\000\000\001", pps, 3) == 0){ + modded = true; + len -= 3; + pps += 3; + } + if (memcmp("\000\000\000\001", pps, 4) == 0){ + modded = true; + len -= 4; + pps += 4; + } + } + sanitized.setPPS(pps, len, i); + } + clear(); + memcpy(data + 4, "avcC", 4); + setInt8(0xFF, 4); //reserved + 4-bytes NAL length + setPayload(sanitized.payload(), sanitized.payloadSize()); + return true; } Descriptor::Descriptor(){ @@ -2418,13 +2511,17 @@ namespace MP4 { r << std::string(indent, ' ') << "[stts] Sample Table Box (" << boxedSize() << ")" << std::endl; r << fullBox::toPrettyString(indent); r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + r << std::string(indent + 2, ' ') << "["; for (unsigned int i = 0; i < getEntryCount(); i++) { static STTSEntry temp; temp = getSTTSEntry(i); - r << std::string(indent + 1, ' ') << "Entry[" << i << "]: " << temp.sampleCount << " sample(s) of " << temp.sampleDelta << "ms each" << std::endl; + r << "(" << temp.sampleCount << " x " << temp.sampleDelta << "ms)"; + if (i < getEntryCount() - 1){ + r << ", "; } + } + r << "]" << std::endl; return r.str(); - } CTTS::CTTS() { @@ -2468,13 +2565,16 @@ namespace MP4 { r << std::string(indent, ' ') << "[ctts] Composition Time To Sample Box (" << boxedSize() << ")" << std::endl; r << fullBox::toPrettyString(indent); r << std::string(indent + 1, ' ') << "EntryCount: " << getEntryCount() << std::endl; + r << std::string(indent + 2, ' ') << "["; for (unsigned int i = 0; i < getEntryCount(); i++) { static CTTSEntry temp; temp = getCTTSEntry(i); - r << std::string(indent + 1, ' ') << "Entry[" << i << "]:" << std::endl; - r << std::string(indent + 2, ' ') << "SampleCount: " << temp.sampleCount << std::endl; - r << std::string(indent + 2, ' ') << "SampleOffset: " << temp.sampleOffset << std::endl; + r << "(" << temp.sampleCount << " x " << temp.sampleOffset << ")"; + if (i < getEntryCount() - 1){ + r << ", "; + } } + r << "]" << std::endl; return r.str(); } @@ -2807,10 +2907,10 @@ namespace MP4 { return r.str(); } - PASP::PASP() { + PASP::PASP(uint32_t hSpacing, uint32_t vSpacing) { memcpy(data + 4, "pasp", 4); - setHSpacing(1); - setVSpacing(1); + setHSpacing(hSpacing); + setVSpacing(vSpacing); } void PASP::setHSpacing(uint32_t newVal) { @@ -2852,6 +2952,8 @@ namespace MP4 { avccBox.setPayload(track.init); setCLAP(avccBox); } + MP4::PASP paspBox; + setPASP(paspBox); } void VisualSampleEntry::initialize(){ @@ -2956,12 +3058,64 @@ namespace MP4 { } if (payloadSize() < 78 + getBoxLen(78) + 8) { return ret; - } else { - return getBox(78 + getBoxLen(78)); } - + return getBox(78 + getBoxLen(78)); } + size_t VisualSampleEntry::getBoxEntryCount() { + if (payloadSize() < 84) { //if the EntryBox is not big enough to hold any box + return 0; + } + size_t count = 0; + size_t offset = 78; + while (offset < payloadSize()){ + offset +=getBoxLen(offset); + count++; + } + return count; + } + + Box & VisualSampleEntry::getBoxEntry(size_t index){ + static Box ret = Box((char *)"\000\000\000\010erro", false); + if (index >= getBoxEntryCount()){ + return ret; + } + size_t count = 0; + size_t offset = 78; + while (offset < payloadSize()){ + if (count == index){ + return getBox(offset); + } + offset +=getBoxLen(offset); + count++; + } + return ret; + } + + void VisualSampleEntry::setBoxEntry(size_t index, Box & box){ + if (index > getBoxEntryCount()){ + index = getBoxEntryCount(); + WARN_MSG("This function can not leave empty spaces, appending at index %zu nstead!", index); + } + size_t count = 0; + size_t offset = 78; + while (offset < payloadSize()){ + if (count == index){ + setBox(box, offset); + return; + } + offset +=getBoxLen(offset); + count++; + } + if (count == index){ + setBox(box, offset); + }else{ + INFO_MSG("Should not be here! Index is %zu, count is %zu, offset is %zu, payloadSize is %zu", index, count, offset, payloadSize()); + } + } + + + std::string VisualSampleEntry::toPrettyVisualString(uint32_t indent, std::string name) { std::stringstream r; r << std::string(indent, ' ') << name << " (" << boxedSize() << ")" << std::endl; @@ -2973,12 +3127,20 @@ namespace MP4 { r << std::string(indent + 1, ' ') << "FrameCount: " << getFrameCount() << std::endl; r << std::string(indent + 1, ' ') << "CompressorName: " << getCompressorName() << std::endl; r << std::string(indent + 1, ' ') << "Depth: " << getDepth() << std::endl; + + r << std::string(indent + 1, ' ') << "Box Count: " << getBoxEntryCount() << std::endl; if (!getCLAP().isType("erro")) { r << getCLAP().toPrettyString(indent + 1); } if (!getPASP().isType("erro")) { r << getPASP().toPrettyString(indent + 1); } + if (getBoxEntryCount() > 2){ + for (size_t index = 2; index < getBoxEntryCount(); ++index){ + MP4::Box tmpBox = getBoxEntry(index); + r << tmpBox.toPrettyString(indent + 1); + } + } return r.str(); } diff --git a/lib/mp4_generic.h b/lib/mp4_generic.h index f51f5a82..d69769a5 100644 --- a/lib/mp4_generic.h +++ b/lib/mp4_generic.h @@ -74,6 +74,7 @@ namespace MP4 { tfhdSampleSize = 0x000010, tfhdSampleFlag = 0x000020, tfhdNoDuration = 0x010000, + tfhdBaseIsMoof = 0x020000, }; class TFHD: public Box { public: @@ -92,6 +93,7 @@ namespace MP4 { uint32_t getDefaultSampleSize(); void setDefaultSampleFlags(uint32_t newFlags); uint32_t getDefaultSampleFlags(); + bool getDefaultBaseIsMoof(); std::string toPrettyString(uint32_t indent = 0); }; @@ -111,6 +113,7 @@ namespace MP4 { void setSPSCount(uint32_t _count); uint32_t getSPSCount(); void setSPS(std::string newSPS, size_t index = 0); + void setSPS(const char * data, size_t len, size_t index = 0); uint32_t getSPSLen(size_t index = 0); char * getSPS(size_t index = 0); std::string hexSPS(size_t index = 0); @@ -119,11 +122,16 @@ namespace MP4 { void setPPSCount(uint32_t _count); uint32_t getPPSCount(); void setPPS(std::string newPPS, size_t index = 0); + void setPPS(const char * data, size_t len, size_t index = 0); uint32_t getPPSLen(size_t index = 0); char * getPPS(size_t index = 0); std::string hexPPS(size_t index = 0); std::string asAnnexB(); void setPayload(std::string newPayload); + void setPayload(const char * data, size_t len); + + bool sanitize(); + std::string toPrettyString(uint32_t indent = 0); }; @@ -587,7 +595,7 @@ namespace MP4 { class PASP: public Box { //PixelAspectRatioBox public: - PASP(); + PASP(uint32_t hSpacing = 1, uint32_t vSpacing = 1); void setHSpacing(uint32_t newVal); uint32_t getHSpacing(); void setVSpacing(uint32_t newVal); @@ -620,6 +628,11 @@ namespace MP4 { void setCLAP(Box & clap); Box & getPASP(); void setPASP(Box & pasp); + + size_t getBoxEntryCount(); + Box & getBoxEntry(size_t index); + void setBoxEntry(size_t index, Box & box); + std::string toPrettyVisualString(uint32_t index = 0, std::string = ""); }; diff --git a/lib/nal.cpp b/lib/nal.cpp index d8864b84..26182596 100644 --- a/lib/nal.cpp +++ b/lib/nal.cpp @@ -3,18 +3,18 @@ #endif #include #include -#include //for log +#include //for log -#include "nal.h" -#include "bitstream.h" #include "bitfields.h" +#include "bitstream.h" #include "defines.h" +#include "nal.h" -namespace nalu { - std::deque parseNalSizes(DTSC::Packet & pack){ +namespace nalu{ + std::deque parseNalSizes(DTSC::Packet &pack){ std::deque result; - char * data; - unsigned int dataLen; + char *data; + size_t dataLen; pack.getString("data", data, dataLen); int offset = 0; while (offset < dataLen){ @@ -25,84 +25,79 @@ namespace nalu { return result; } - std::string removeEmulationPrevention(const std::string & data) { + std::string removeEmulationPrevention(const std::string &data){ std::string result; result.resize(data.size()); result[0] = data[0]; result[1] = data[1]; - unsigned int dataPtr = 2; - unsigned int dataLen = data.size(); - unsigned int resPtr = 2; - while (dataPtr + 2 < dataLen) { - if (!data[dataPtr] && !data[dataPtr + 1] && data[dataPtr + 2] == 3){ //We have found an emulation prevention + size_t dataPtr = 2; + size_t dataLen = data.size(); + size_t resPtr = 2; + while (dataPtr + 2 < dataLen){ + if (!data[dataPtr] && !data[dataPtr + 1] && + data[dataPtr + 2] == 3){// We have found an emulation prevention result[resPtr++] = data[dataPtr++]; result[resPtr++] = data[dataPtr++]; - dataPtr++; //Skip the emulation prevention byte - } else { + dataPtr++; // Skip the emulation prevention byte + }else{ result[resPtr++] = data[dataPtr++]; } } - while (dataPtr < dataLen){ - result[resPtr++] = data[dataPtr++]; - } + while (dataPtr < dataLen){result[resPtr++] = data[dataPtr++];} return result.substr(0, resPtr); } - unsigned long toAnnexB(const char * data, unsigned long dataSize, char *& result){ - //toAnnexB keeps the same size. - if (!result){ - result = (char *)malloc(dataSize); - } + unsigned long toAnnexB(const char *data, unsigned long dataSize, char *&result){ + // toAnnexB keeps the same size. + if (!result){result = (char *)malloc(dataSize);} int offset = 0; while (offset < dataSize){ - //Read unit size + // Read unit size unsigned long unitSize = Bit::btohl(data + offset); - //Write annex b header + // Write annex b header memset(result + offset, 0x00, 3); result[offset + 3] = 0x01; - //Copy the nal unit + // Copy the nal unit memcpy(result + offset + 4, data + offset + 4, unitSize); - //Update the offset + // Update the offset offset += 4 + unitSize; } return dataSize; } - ///Scans data for the last non-zero byte, returning a pointer to it. - const char* nalEndPosition(const char * data, uint32_t dataSize){ - while(dataSize > 0 && memcmp(data+dataSize-1, "\000",1) == 0 ){ - dataSize--; - } - return data+dataSize; + /// Scans data for the last non-zero byte, returning a pointer to it. + const char *nalEndPosition(const char *data, uint32_t dataSize){ + while (dataSize && !data[dataSize - 1]){--dataSize;} + return data + dataSize; } - ///Scan data for Annex B start code. Returns pointer to it when found, null otherwise. - const char * scanAnnexB(const char * data, uint32_t dataSize){ - char * offset = (char*)data; - const char * maxData = data + dataSize - 2; - while(offset < maxData){ + /// Scan data for Annex B start code. Returns pointer to it when found, null otherwise. + const char *scanAnnexB(const char *data, uint32_t dataSize){ + char *offset = (char *)data; + const char *maxData = data + dataSize - 2; + while (offset < maxData){ if (offset[2] > 1){ - //We have no zero in the third byte, so we need to skip at least 3 bytes forward + // We have no zero in the third byte, so we need to skip at least 3 bytes forward offset += 3; continue; } if (!offset[2]){ - //We skip forward 1 or 2 bytes depending on contents of the second byte - offset += (offset[1]?2:1); + // We COULD skip forward 1 or 2 bytes depending on contents of the second byte + // offset += (offset[1]?2:1); + //... but skipping a single byte (removing the 'if') is actually faster (benchmarked). + ++offset; continue; } - if (!offset[0] && !offset[1]){ - return offset; - } - //We have no zero in the third byte, so we need to skip at least 3 bytes forward + if (!offset[0] && !offset[1]){return offset;} + // We have no zero in the third byte, so we need to skip at least 3 bytes forward offset += 3; } return 0; } - unsigned long fromAnnexB(const char * data, unsigned long dataSize, char *& result){ - const char * lastCheck = data + dataSize - 3; + unsigned long fromAnnexB(const char *data, unsigned long dataSize, char *&result){ + const char *lastCheck = data + dataSize - 3; if (!result){ FAIL_MSG("No output buffer given to FromAnnexB"); return 0; @@ -110,26 +105,20 @@ namespace nalu { int offset = 0; int newOffset = 0; while (offset < dataSize){ - const char * begin = data + offset; - while ( begin < lastCheck && !(!begin[0] && !begin[1] && begin[2] == 0x01)){ + const char *begin = data + offset; + while (begin < lastCheck && !(!begin[0] && !begin[1] && begin[2] == 0x01)){ begin++; - if (begin < lastCheck && begin[0]){ - begin++; - } + if (begin < lastCheck && begin[0]){begin++;} } - begin += 3;//Initialize begin after the first 0x000001 pattern. + begin += 3; // Initialize begin after the first 0x000001 pattern. if (begin > data + dataSize){ offset = dataSize; continue; } - const char * end = (const char*)memmem(begin, dataSize - (begin - data), "\000\000\001", 3); - if (!end) { - end = data + dataSize; - } - //Check for 4-byte lead in's. Yes, we access -1 here - if (end > begin && (end - data) != dataSize && end[-1] == 0x00){ - end--; - } + const char *end = (const char *)memmem(begin, dataSize - (begin - data), "\000\000\001", 3); + if (!end){end = data + dataSize;} + // Check for 4-byte lead in's. Yes, we access -1 here + if (end > begin && (end - data) != dataSize && end[-1] == 0x00){end--;} unsigned int nalSize = end - begin; Bit::htobl(result + newOffset, nalSize); memcpy(result + newOffset + 4, begin, nalSize); @@ -139,4 +128,5 @@ namespace nalu { } return newOffset; } -} +}// namespace nalu + diff --git a/lib/nal.h b/lib/nal.h index 524b670c..37d80ad9 100644 --- a/lib/nal.h +++ b/lib/nal.h @@ -1,21 +1,21 @@ #pragma once -#include -#include +#include "dtsc.h" #include #include -#include "dtsc.h" +#include -namespace nalu { - struct nalData { - unsigned char nalType; - unsigned long nalSize; +namespace nalu{ + struct nalData{ + uint8_t nalType; + size_t nalSize; }; - std::deque parseNalSizes(DTSC::Packet & pack); - std::string removeEmulationPrevention(const std::string & data); + std::deque parseNalSizes(DTSC::Packet &pack); + std::string removeEmulationPrevention(const std::string &data); + + unsigned long toAnnexB(const char *data, unsigned long dataSize, char *&result); + unsigned long fromAnnexB(const char *data, unsigned long dataSize, char *&result); + const char *scanAnnexB(const char *data, uint32_t dataSize); + const char *nalEndPosition(const char *data, uint32_t dataSize); +}// namespace nalu - unsigned long toAnnexB(const char * data, unsigned long dataSize, char *& result); - unsigned long fromAnnexB(const char * data, unsigned long dataSize, char *& result); - const char* scanAnnexB(const char * data, uint32_t dataSize); - const char* nalEndPosition(const char * data, uint32_t dataSize); -} diff --git a/lib/ogg.cpp b/lib/ogg.cpp index 925e4a51..6876dd0c 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -112,7 +112,7 @@ namespace OGG { return false; } if (newData.substr(0, 4) != "OggS"){ - DEBUG_MSG(DLVL_FAIL, "Invalid Ogg page encountered (magic number wrong: %s) - cannot continue", newData.c_str()); + FAIL_MSG("Invalid Ogg page encountered (magic number wrong: %s) - cannot continue", newData.c_str()); return false; } memcpy(data, newData.c_str(), 27);//copying the header, always 27 bytes @@ -142,7 +142,7 @@ namespace OGG { return false; } if (std::string(data, 4) != "OggS"){ - DEBUG_MSG(DLVL_FAIL, "Invalid Ogg page encountered (magic number wrong: %s) - cannot continue bytePos %d", data, oriPos); + FAIL_MSG("Invalid Ogg page encountered (magic number wrong: %s) - cannot continue bytePos %d", data, oriPos); return false; } if (!fread(data + 27, getPageSegments(), 1, inFile)){ @@ -155,10 +155,10 @@ namespace OGG { if (*it){ char * thisSeg = (char *)malloc(*it * sizeof(char)); if (!thisSeg){ - DEBUG_MSG(DLVL_WARN, "malloc failed"); + WARN_MSG("malloc failed"); } if (!fread(thisSeg, *it, 1, inFile)){ - DEBUG_MSG(DLVL_WARN, "Unable to read a segment @ pos %d segment size: %d getPageSegments: %d", oriPos, *it, getPageSegments()); + WARN_MSG("Unable to read a segment @ pos %d segment size: %d getPageSegments: %d", oriPos, *it, getPageSegments()); fseek(inFile, oriPos, SEEK_SET); return false; } @@ -524,7 +524,7 @@ namespace OGG { ///\todo Rewrite this void Page::sendTo(Socket::Connection & destination, int calcGranule){ //combines all data and sends it to socket if (!oggSegments.size()){ - DEBUG_MSG(DLVL_HIGH, "!segments.size()"); + HIGH_MSG("!segments.size()"); return; } if (codec == OGG::VORBIS){ @@ -587,7 +587,7 @@ namespace OGG { checksum = crc32(checksum, &tableSize, 1); //calculating the checksum over the segment Table Size checksum = crc32(checksum, table, tableSize);//calculating the checksum over the segment Table - DEBUG_MSG(DLVL_DONTEVEN, "numSegments: %d", numSegments); + DONTEVEN_MSG("numSegments: %d", numSegments); for (unsigned int i = 0; i < numSegments; i++){ //INFO_MSG("checksum, i: %d", i); diff --git a/lib/opus.h b/lib/opus.h index 0a09fc28..3206ac39 100644 --- a/lib/opus.h +++ b/lib/opus.h @@ -1,4 +1,5 @@ #include +#include namespace Opus{ uint16_t getPreSkip(const char * initData); diff --git a/lib/procs.cpp b/lib/procs.cpp index eeece6c7..dd6ffa95 100644 --- a/lib/procs.cpp +++ b/lib/procs.cpp @@ -131,7 +131,7 @@ void Util::Procs::exit_handler() { //send sigkill to all remaining if (!listcopy.empty()) { for (it = listcopy.begin(); it != listcopy.end(); it++) { - DEBUG_MSG(DLVL_DEVEL, "SIGKILL %d", *it); + INFO_MSG("SIGKILL %d", *it); kill(*it, SIGKILL); } } @@ -273,14 +273,13 @@ pid_t Util::Procs::StartPiped(std::deque & argDeq, int * fdin, int pid_t Util::Procs::StartPiped(const char * const * argv, int * fdin, int * fdout, int * fderr) { pid_t pid; int pipein[2], pipeout[2], pipeerr[2]; - //DEBUG_MSG(DLVL_DEVEL, "setHandler"); setHandler(); if (fdin && *fdin == -1 && pipe(pipein) < 0) { - DEBUG_MSG(DLVL_ERROR, "stdin pipe creation failed for process %s, reason: %s", argv[0], strerror(errno)); + ERROR_MSG("stdin pipe creation failed for process %s, reason: %s", argv[0], strerror(errno)); return 0; } if (fdout && *fdout == -1 && pipe(pipeout) < 0) { - DEBUG_MSG(DLVL_ERROR, "stdout pipe creation failed for process %s, reason: %s", argv[0], strerror(errno)); + ERROR_MSG("stdout pipe creation failed for process %s, reason: %s", argv[0], strerror(errno)); if (*fdin == -1) { close(pipein[0]); close(pipein[1]); @@ -288,7 +287,7 @@ pid_t Util::Procs::StartPiped(const char * const * argv, int * fdin, int * fdout return 0; } if (fderr && *fderr == -1 && pipe(pipeerr) < 0) { - DEBUG_MSG(DLVL_ERROR, "stderr pipe creation failed for process %s, reason: %s", argv[0], strerror(errno)); + ERROR_MSG("stderr pipe creation failed for process %s, reason: %s", argv[0], strerror(errno)); if (*fdin == -1) { close(pipein[0]); close(pipein[1]); @@ -303,7 +302,7 @@ pid_t Util::Procs::StartPiped(const char * const * argv, int * fdin, int * fdout if (!fdin || !fdout || !fderr) { devnull = open("/dev/null", O_RDWR); if (devnull == -1) { - DEBUG_MSG(DLVL_ERROR, "Could not open /dev/null for process %s, reason: %s", argv[0], strerror(errno)); + ERROR_MSG("Could not open /dev/null for process %s, reason: %s", argv[0], strerror(errno)); if (*fdin == -1) { close(pipein[0]); close(pipein[1]); @@ -366,10 +365,10 @@ pid_t Util::Procs::StartPiped(const char * const * argv, int * fdin, int * fdout } //Because execvp requires a char* const* and we have a const char* const* execvp(argv[0], (char* const*)argv); - DEBUG_MSG(DLVL_ERROR, "execvp failed for process %s, reason: %s", argv[0], strerror(errno)); + ERROR_MSG("execvp failed for process %s, reason: %s", argv[0], strerror(errno)); exit(42); } else if (pid == -1) { - DEBUG_MSG(DLVL_ERROR, "fork failed for process %s, reason: %s", argv[0], strerror(errno)); + ERROR_MSG("fork failed for process %s, reason: %s", argv[0], strerror(errno)); if (fdin && *fdin == -1) { close(pipein[0]); close(pipein[1]); @@ -391,7 +390,7 @@ pid_t Util::Procs::StartPiped(const char * const * argv, int * fdin, int * fdout tthread::lock_guard guard(plistMutex); plist.insert(pid); } - DEBUG_MSG(DLVL_HIGH, "Piped process %s started, PID %d", argv[0], pid); + HIGH_MSG("Piped process %s started, PID %d", argv[0], pid); if (devnull != -1) { close(devnull); } diff --git a/lib/riff.cpp b/lib/riff.cpp index 1315e9bd..cba231f5 100644 --- a/lib/riff.cpp +++ b/lib/riff.cpp @@ -5,7 +5,7 @@ namespace RIFF{ Chunk::Chunk(const void *_p, uint32_t len){ p = (const char *)_p; if (len && len < getPayloadSize() + 8){ - FAIL_MSG("Chunk %s (%lub) does not fit in %lu bytes length!", getType().c_str(), + FAIL_MSG("Chunk %s (%" PRIu32 "b) does not fit in %" PRIu32 " bytes length!", getType().c_str(), getPayloadSize() + 8, len); p = 0; } diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 8a08caee..e7f4cf7f 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -2,22 +2,22 @@ /// Holds all code for the RTMPStream namespace. #include "rtmpchunks.h" +#include "auth.h" #include "defines.h" #include "flv_tag.h" #include "timing.h" -#include "auth.h" -std::string RTMPStream::handshake_in; ///< Input for the handshake. +std::string RTMPStream::handshake_in; ///< Input for the handshake. std::string RTMPStream::handshake_out; ///< Output for the handshake. -unsigned int RTMPStream::chunk_rec_max = 128; -unsigned int RTMPStream::chunk_snd_max = 128; -unsigned int RTMPStream::rec_window_size = 2500000; -unsigned int RTMPStream::snd_window_size = 2500000; -unsigned int RTMPStream::rec_window_at = 0; -unsigned int RTMPStream::snd_window_at = 0; -unsigned int RTMPStream::rec_cnt = 0; -unsigned int RTMPStream::snd_cnt = 0; +size_t RTMPStream::chunk_rec_max = 128; +size_t RTMPStream::chunk_snd_max = 128; +size_t RTMPStream::rec_window_size = 2500000; +size_t RTMPStream::snd_window_size = 2500000; +size_t RTMPStream::rec_window_at = 0; +size_t RTMPStream::snd_window_at = 0; +size_t RTMPStream::rec_cnt = 0; +size_t RTMPStream::snd_cnt = 0; timeval RTMPStream::lastrec; @@ -26,94 +26,98 @@ std::map RTMPStream::lastsend; /// Holds the last received chunk for every msg_id. std::map RTMPStream::lastrecv; -#define P1024 \ - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ - "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ +#define P1024 \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404" \ + "DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7" \ + "ED" \ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" -char genuineFMSKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, - 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, - 0x76, // Genuine Adobe Flash Media Server 001 - 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, - 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae - }; // 68 +char genuineFMSKey[] ={ + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, + 0x6c, 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72, + 0x76, // Genuine Adobe Flash Media Server 001 + 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, + 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, + 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 68 -char genuineFPKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, - 0x50, 0x6c, 0x61, - 0x79, // Genuine Adobe Flash Player 001 - 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, - 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae - }; // 62 +char genuineFPKey[] ={ + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, + 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20, 0x50, 0x6c, 0x61, + 0x79, // Genuine Adobe Flash Player 001 + 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, + 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec, 0x5d, 0x2d, + 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae}; // 62 -inline uint32_t GetDigestOffset(uint8_t * pBuffer, uint8_t scheme) { - if (scheme == 0) { +inline uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){ + if (scheme == 0){ return ((pBuffer[8] + pBuffer[9] + pBuffer[10] + pBuffer[11]) % 728) + 12; - } else { + }else{ return ((pBuffer[772] + pBuffer[773] + pBuffer[774] + pBuffer[775]) % 728) + 776; } } -bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { +bool ValidateClientScheme(uint8_t *pBuffer, uint8_t scheme){ uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); - uint8_t pTempBuffer[1536-32]; + uint8_t pTempBuffer[1536 - 32]; memcpy(pTempBuffer, pBuffer, clientDigestOffset); - memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); + memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, + 1536 - clientDigestOffset - 32); char pTempHash[32]; - Secure::hmac_sha256bin((char*)pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); + Secure::hmac_sha256bin((char *)pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); bool result = (memcmp(pBuffer + clientDigestOffset, pTempHash, 32) == 0); - DEBUG_MSG(DLVL_MEDIUM, "Client scheme validation %hhi %s", scheme, result ? "success" : "failed"); + MEDIUM_MSG("Client scheme validation %hhi %s", scheme, result ? "success" : "failed"); return result; } /// Packs up the chunk for sending over the network. /// \warning Do not call if you are not actually sending the resulting data! /// \returns A std::string ready to be sent. -std::string & RTMPStream::Chunk::Pack() { +std::string &RTMPStream::Chunk::Pack(){ static std::string output; output.clear(); bool allow_short = lastsend.count(cs_id); RTMPStream::Chunk prev = lastsend[cs_id]; unsigned int tmpi; unsigned char chtype = 0x00; - if (allow_short && (prev.cs_id == cs_id)) { - if (msg_stream_id == prev.msg_stream_id) { - chtype = 0x40; //do not send msg_stream_id - if (len == prev.len) { - if (msg_type_id == prev.msg_type_id) { - chtype = 0x80; //do not send len and msg_type_id - if (timestamp - prev.timestamp == prev.ts_delta) { - chtype = 0xC0; //do not send timestamp + if (allow_short && (prev.cs_id == cs_id)){ + if (msg_stream_id == prev.msg_stream_id){ + chtype = 0x40; // do not send msg_stream_id + if (len == prev.len){ + if (msg_type_id == prev.msg_type_id){ + chtype = 0x80; // do not send len and msg_type_id + if (timestamp - prev.timestamp == prev.ts_delta){ + chtype = 0xC0; // do not send timestamp } } } } - //override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel - if (timestamp < prev.timestamp) { - chtype = 0x00; - } + // override - we always sent type 0x00 if the timestamp has decreased since last chunk in this + // channel + if (timestamp < prev.timestamp){chtype = 0x00;} } - if (cs_id <= 63) { + if (cs_id <= 63){ output += (unsigned char)(chtype | cs_id); - } else { - if (cs_id <= 255 + 64) { + }else{ + if (cs_id <= 255 + 64){ output += (unsigned char)(chtype | 0); output += (unsigned char)(cs_id - 64); - } else { + }else{ output += (unsigned char)(chtype | 1); output += (unsigned char)((cs_id - 64) % 256); output += (unsigned char)((cs_id - 64) / 256); } } unsigned int ntime = 0; - if (chtype != 0xC0) { - //timestamp or timestamp diff - if (chtype == 0x00) { + if (chtype != 0xC0){ + // timestamp or timestamp diff + if (chtype == 0x00){ tmpi = timestamp; - } else { + }else{ tmpi = timestamp - prev.timestamp; } ts_delta = tmpi; - if (tmpi >= 0x00ffffff) { + if (tmpi >= 0x00ffffff){ ntime = tmpi; tmpi = 0x00ffffff; } @@ -121,16 +125,16 @@ std::string & RTMPStream::Chunk::Pack() { output += (unsigned char)((tmpi >> 16) & 0xff); output += (unsigned char)((tmpi >> 8) & 0xff); output += (unsigned char)(tmpi & 0xff); - if (chtype != 0x80) { - //len + if (chtype != 0x80){ + // len tmpi = len; output += (unsigned char)((tmpi >> 16) & 0xff); output += (unsigned char)((tmpi >> 8) & 0xff); output += (unsigned char)(tmpi & 0xff); - //msg type id + // msg type id output += (unsigned char)msg_type_id; - if (chtype != 0x40) { - //msg stream id + if (chtype != 0x40){ + // msg stream id output += (unsigned char)(msg_stream_id % 256); output += (unsigned char)(msg_stream_id / 256); output += (unsigned char)(msg_stream_id / (256 * 256)); @@ -139,39 +143,35 @@ std::string & RTMPStream::Chunk::Pack() { } }else{ ts_header = prev.ts_header; - if (ts_header == 0xffffff){ - ntime = timestamp; - } + if (ts_header == 0xffffff){ntime = timestamp;} } - //support for 0x00ffffff timestamps - if (ntime) { + // support for 0x00ffffff timestamps + if (ntime){ output += (unsigned char)((ntime >> 24) & 0xff); output += (unsigned char)((ntime >> 16) & 0xff); output += (unsigned char)((ntime >> 8) & 0xff); output += (unsigned char)(ntime & 0xff); } len_left = 0; - while (len_left < len) { + while (len_left < len){ tmpi = len - len_left; - if (tmpi > RTMPStream::chunk_snd_max) { - tmpi = RTMPStream::chunk_snd_max; - } + if (tmpi > RTMPStream::chunk_snd_max){tmpi = RTMPStream::chunk_snd_max;} output.append(data, len_left, tmpi); len_left += tmpi; - if (len_left < len) { - if (cs_id <= 63) { + if (len_left < len){ + if (cs_id <= 63){ output += (unsigned char)(0xC0 + cs_id); - } else { - if (cs_id <= 255 + 64) { + }else{ + if (cs_id <= 255 + 64){ output += (unsigned char)(0xC0); output += (unsigned char)(cs_id - 64); - } else { + }else{ output += (unsigned char)(0xC1); output += (unsigned char)((cs_id - 64) % 256); output += (unsigned char)((cs_id - 64) / 256); } } - if (ntime) { + if (ntime){ output += (unsigned char)((ntime >> 24) & 0xff); output += (unsigned char)((ntime >> 16) & 0xff); output += (unsigned char)((ntime >> 8) & 0xff); @@ -182,10 +182,10 @@ std::string & RTMPStream::Chunk::Pack() { lastsend[cs_id] = *this; RTMPStream::snd_cnt += output.size(); return output; -} //SendChunk +}// SendChunk /// Default constructor, creates an empty chunk with all values initialized to zero. -RTMPStream::Chunk::Chunk() { +RTMPStream::Chunk::Chunk(){ headertype = 0; cs_id = 0; timestamp = 0; @@ -195,10 +195,11 @@ RTMPStream::Chunk::Chunk() { msg_type_id = 0; msg_stream_id = 0; data = ""; -} //constructor +}// constructor /// Packs up a chunk with the given arguments as properties. -std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data) { +std::string &RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, + unsigned int msg_stream_id, std::string data){ static RTMPStream::Chunk ch; ch.cs_id = cs_id; ch.timestamp = 0; @@ -209,14 +210,15 @@ std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_i ch.msg_stream_id = msg_stream_id; ch.data = data; return ch.Pack(); -} //constructor +}// constructor /// Packs up a chunk with media contents. /// \param msg_type_id Type number of the media, as per FLV standard. /// \param data Contents of the media data. /// \param len Length of the media data, in bytes. /// \param ts Timestamp of the media data, relative to current system time. -std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts) { +std::string &RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char *data, int len, + unsigned int ts){ static RTMPStream::Chunk ch; ch.cs_id = msg_type_id + 42; ch.timestamp = ts; @@ -227,16 +229,16 @@ std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * d ch.msg_stream_id = 1; ch.data = std::string((char *)data, (size_t)len); return ch.Pack(); -} //SendMedia +}// SendMedia /// Packs up a chunk with media contents. /// \param tag FLV::Tag with media to send. -std::string & RTMPStream::SendMedia(FLV::Tag & tag) { +std::string &RTMPStream::SendMedia(FLV::Tag &tag){ static RTMPStream::Chunk ch; - //Commented bit is more efficient and correct according to RTMP spec. - //Simply passing "4" is the only thing that actually plays correctly, though. - //Adobe, if you're ever reading this... wtf? Seriously. - ch.cs_id = 4;//((unsigned char)tag.data[0]); + // Commented bit is more efficient and correct according to RTMP spec. + // Simply passing "4" is the only thing that actually plays correctly, though. + // Adobe, if you're ever reading this... wtf? Seriously. + ch.cs_id = 4; //((unsigned char)tag.data[0]); ch.timestamp = tag.tagTime(); ch.len_left = 0; ch.msg_type_id = (unsigned char)tag.data[0]; @@ -245,10 +247,10 @@ std::string & RTMPStream::SendMedia(FLV::Tag & tag) { ch.len = ch.data.size(); ch.real_len = ch.len; return ch.Pack(); -} //SendMedia +}// SendMedia /// Packs up a chunk for a control message with 1 argument. -std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data) { +std::string &RTMPStream::SendCTL(unsigned char type, unsigned int data){ static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = Util::getMS(); @@ -259,7 +261,7 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data) { ch.len = 5; ch.real_len = 5; ch.data.resize(5); - ((char*)ch.data.data())[4] = 0; + ((char *)ch.data.data())[4] = 0; }else{ ch.len = 4; ch.real_len = 4; @@ -267,10 +269,10 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data) { } *(int *)((char *)ch.data.data()) = htonl(data); return ch.Pack(); -} //SendCTL +}// SendCTL /// Packs up a chunk for a control message with 2 arguments. -std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2) { +std::string &RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2){ static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = Util::getMS(); @@ -283,10 +285,10 @@ std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigne *(unsigned int *)((char *)ch.data.c_str()) = htonl(data); ch.data[4] = data2; return ch.Pack(); -} //SendCTL +}// SendCTL /// Packs up a chunk for a user control message with 1 argument. -std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data) { +std::string &RTMPStream::SendUSR(unsigned char type, unsigned int data){ static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = Util::getMS(); @@ -300,10 +302,10 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data) { ch.data[0] = 0; ch.data[1] = type; return ch.Pack(); -} //SendUSR +}// SendUSR /// Packs up a chunk for a user control message with 2 arguments. -std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2) { +std::string &RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2){ static RTMPStream::Chunk ch; ch.cs_id = 2; ch.timestamp = Util::getMS(); @@ -318,7 +320,7 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne ch.data[0] = 0; ch.data[1] = type; return ch.Pack(); -} //SendUSR +}// SendUSR /// Parses the argument Socket::Buffer into the current chunk. /// Tries to read a whole chunk, removing data from the Buffer as it reads. @@ -328,225 +330,204 @@ std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigne /// \param buffer The input to parse and update. /// \warning This function will destroy the current data in this chunk! /// \returns True if a whole chunk could be read, false otherwise. -bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer) { +bool RTMPStream::Chunk::Parse(Socket::Buffer &buffer){ gettimeofday(&RTMPStream::lastrec, 0); unsigned int i = 0; - if (!buffer.available(3)) { - return false; - } //we want at least 3 bytes + if (!buffer.available(3)){return false;}// we want at least 3 bytes std::string indata = buffer.copy(3); - unsigned char chunktype = indata[i++ ]; - //read the chunkstream ID properly - switch (chunktype & 0x3F) { - case 0: - cs_id = indata[i++ ] + 64; - break; - case 1: - cs_id = indata[i++ ] + 64; - cs_id += indata[i++ ] * 256; - break; - default: - cs_id = chunktype & 0x3F; - break; + unsigned char chunktype = indata[i++]; + // read the chunkstream ID properly + switch (chunktype & 0x3F){ + case 0: cs_id = indata[i++] + 64; break; + case 1: + cs_id = indata[i++] + 64; + cs_id += indata[i++] * 256; + break; + default: cs_id = chunktype & 0x3F; break; } bool allow_short = lastrecv.count(cs_id); RTMPStream::Chunk prev = lastrecv[cs_id]; - //process the rest of the header, for each chunk type + // process the rest of the header, for each chunk type headertype = chunktype & 0xC0; - - DEBUG_MSG(DLVL_DONTEVEN, "Parsing RTMP chunk header (%#.2hhX) at offset %#X", chunktype, RTMPStream::rec_cnt); - - switch (headertype) { - case 0x00: - if (!buffer.available(i + 11)) { - return false; - } //can't read whole header - indata = buffer.copy(i + 11); - timestamp = indata[i++ ] * 256 * 256; - timestamp += indata[i++ ] * 256; - timestamp += indata[i++ ]; + + DONTEVEN_MSG("Parsing RTMP chunk header (%#.2hhX) at offset %#zx", chunktype, + RTMPStream::rec_cnt); + + switch (headertype){ + case 0x00: + if (!buffer.available(i + 11)){return false;}// can't read whole header + indata = buffer.copy(i + 11); + timestamp = indata[i++] * 256 * 256; + timestamp += indata[i++] * 256; + timestamp += indata[i++]; + ts_delta = timestamp; + ts_header = timestamp; + len = indata[i++] * 256 * 256; + len += indata[i++] * 256; + len += indata[i++]; + len_left = 0; + msg_type_id = indata[i++]; + msg_stream_id = indata[i++]; + msg_stream_id += indata[i++] * 256; + msg_stream_id += indata[i++] * 256 * 256; + msg_stream_id += indata[i++] * 256 * 256 * 256; + break; + case 0x40: + if (!buffer.available(i + 7)){return false;}// can't read whole header + indata = buffer.copy(i + 7); + if (!allow_short){WARN_MSG("Warning: Header type 0x40 with no valid previous chunk!");} + timestamp = indata[i++] * 256 * 256; + timestamp += indata[i++] * 256; + timestamp += indata[i++]; + ts_header = timestamp; + if (timestamp != 0x00ffffff){ ts_delta = timestamp; - ts_header = timestamp; - len = indata[i++ ] * 256 * 256; - len += indata[i++ ] * 256; - len += indata[i++ ]; - len_left = 0; - msg_type_id = indata[i++ ]; - msg_stream_id = indata[i++ ]; - msg_stream_id += indata[i++ ] * 256; - msg_stream_id += indata[i++ ] * 256 * 256; - msg_stream_id += indata[i++ ] * 256 * 256 * 256; - break; - case 0x40: - if (!buffer.available(i + 7)) { - return false; - } //can't read whole header - indata = buffer.copy(i + 7); - if (!allow_short) { - DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x40 with no valid previous chunk!"); - } - timestamp = indata[i++ ] * 256 * 256; - timestamp += indata[i++ ] * 256; - timestamp += indata[i++ ]; - ts_header = timestamp; - if (timestamp != 0x00ffffff) { - ts_delta = timestamp; - timestamp = prev.timestamp + ts_delta; - } - len = indata[i++ ] * 256 * 256; - len += indata[i++ ] * 256; - len += indata[i++ ]; - len_left = 0; - msg_type_id = indata[i++ ]; - msg_stream_id = prev.msg_stream_id; - break; - case 0x80: - if (!buffer.available(i + 3)) { - return false; - } //can't read whole header - indata = buffer.copy(i + 3); - if (!allow_short) { - DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x80 with no valid previous chunk!"); - } - timestamp = indata[i++ ] * 256 * 256; - timestamp += indata[i++ ] * 256; - timestamp += indata[i++ ]; - ts_header = timestamp; - if (timestamp != 0x00ffffff) { - ts_delta = timestamp; - timestamp = prev.timestamp + ts_delta; - } - len = prev.len; - len_left = prev.len_left; - msg_type_id = prev.msg_type_id; - msg_stream_id = prev.msg_stream_id; - break; - case 0xC0: - if (!allow_short) { - DEBUG_MSG(DLVL_WARN, "Warning: Header type 0xC0 with no valid previous chunk!"); - } - timestamp = prev.timestamp + prev.ts_delta; - ts_header = prev.ts_header; - ts_delta = prev.ts_delta; - len = prev.len; - len_left = prev.len_left; - if (len_left > 0){ - timestamp = prev.timestamp; - } - msg_type_id = prev.msg_type_id; - msg_stream_id = prev.msg_stream_id; - break; + timestamp = prev.timestamp + ts_delta; + } + len = indata[i++] * 256 * 256; + len += indata[i++] * 256; + len += indata[i++]; + len_left = 0; + msg_type_id = indata[i++]; + msg_stream_id = prev.msg_stream_id; + break; + case 0x80: + if (!buffer.available(i + 3)){return false;}// can't read whole header + indata = buffer.copy(i + 3); + if (!allow_short){WARN_MSG("Warning: Header type 0x80 with no valid previous chunk!");} + timestamp = indata[i++] * 256 * 256; + timestamp += indata[i++] * 256; + timestamp += indata[i++]; + ts_header = timestamp; + if (timestamp != 0x00ffffff){ + ts_delta = timestamp; + timestamp = prev.timestamp + ts_delta; + } + len = prev.len; + len_left = prev.len_left; + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; + case 0xC0: + if (!allow_short){WARN_MSG("Warning: Header type 0xC0 with no valid previous chunk!");} + timestamp = prev.timestamp + prev.ts_delta; + ts_header = prev.ts_header; + ts_delta = prev.ts_delta; + len = prev.len; + len_left = prev.len_left; + if (len_left > 0){timestamp = prev.timestamp;} + msg_type_id = prev.msg_type_id; + msg_stream_id = prev.msg_stream_id; + break; } - //calculate chunk length, real length, and length left till complete - if (len_left > 0) { + // calculate chunk length, real length, and length left till complete + if (len_left > 0){ real_len = len_left; len_left -= real_len; - } else { + }else{ real_len = len; } - if (real_len > RTMPStream::chunk_rec_max) { + if (real_len > RTMPStream::chunk_rec_max){ len_left += real_len - RTMPStream::chunk_rec_max; real_len = RTMPStream::chunk_rec_max; } - - DEBUG_MSG(DLVL_DONTEVEN, "Parsing RTMP chunk result: len_left=%d, real_len=%d", len_left, real_len); - - //read extended timestamp, if necessary - if (ts_header == 0x00ffffff) { - if (!buffer.available(i + 4)) { - return false; - } //can't read timestamp + + DONTEVEN_MSG("Parsing RTMP chunk result: len_left=%d, real_len=%d", len_left, real_len); + + // read extended timestamp, if necessary + if (ts_header == 0x00ffffff){ + if (!buffer.available(i + 4)){return false;}// can't read timestamp indata = buffer.copy(i + 4); - timestamp += indata[i++ ] * 256 * 256 * 256; - timestamp += indata[i++ ] * 256 * 256; - timestamp += indata[i++ ] * 256; - timestamp = indata[i++ ]; + timestamp += indata[i++] * 256 * 256 * 256; + timestamp += indata[i++] * 256 * 256; + timestamp += indata[i++] * 256; + timestamp = indata[i++]; ts_delta = timestamp; - DEBUG_MSG(DLVL_DONTEVEN, "Extended timestamp: %u", timestamp); + DONTEVEN_MSG("Extended timestamp: %u", timestamp); } - //read data if length > 0, and allocate it - if (real_len > 0) { - if (!buffer.available(i + real_len)) { - return false; - } //can't read all data (yet) - buffer.remove(i); //remove the header - if (prev.len_left > 0) { - data = prev.data + buffer.remove(real_len); //append the data and remove from buffer - } else { - data = buffer.remove(real_len); //append the data and remove from buffer + // read data if length > 0, and allocate it + if (real_len > 0){ + if (!buffer.available(i + real_len)){return false;}// can't read all data (yet) + buffer.remove(i); // remove the header + if (prev.len_left > 0){ + data = prev.data + buffer.remove(real_len); // append the data and remove from buffer + }else{ + data = buffer.remove(real_len); // append the data and remove from buffer } lastrecv[cs_id] = *this; RTMPStream::rec_cnt += i + real_len; - if (len_left == 0) { + if (len_left == 0){ return true; - } else { + }else{ return Parse(buffer); } - } else { - buffer.remove(i); //remove the header + }else{ + buffer.remove(i); // remove the header data = ""; indata = indata.substr(i + real_len); lastrecv[cs_id] = *this; RTMPStream::rec_cnt += i + real_len; return true; } -} //Parse +}// Parse /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. /// After calling this function, don't forget to read and ignore 1536 extra bytes, /// these are the handshake response and not interesting for us because we don't do client /// verification. -bool RTMPStream::doHandshake() { +bool RTMPStream::doHandshake(){ char Version; - //Read C0 - if (handshake_in.size() < 1537) { - DEBUG_MSG(DLVL_FAIL, "Handshake wasn't filled properly (%lu/1537) - aborting!", handshake_in.size()); + // Read C0 + if (handshake_in.size() < 1537){ + FAIL_MSG("Handshake wasn't filled properly (%lu/1537) - aborting!", handshake_in.size()); return false; } Version = RTMPStream::handshake_in[0]; - uint8_t * Client = (uint8_t *)RTMPStream::handshake_in.data() + 1; + uint8_t *Client = (uint8_t *)RTMPStream::handshake_in.data() + 1; RTMPStream::handshake_out.resize(3073); - uint8_t * Server = (uint8_t *)RTMPStream::handshake_out.data() + 1; + uint8_t *Server = (uint8_t *)RTMPStream::handshake_out.data() + 1; RTMPStream::rec_cnt += 1537; - //Build S1 Packet - *((uint32_t *)Server) = 0; //time zero - *(((uint32_t *)(Server + 4))) = htonl(0x01020304); //version 1 2 3 4 - for (int i = 8; i < 3072; ++i) { + // Build S1 Packet + *((uint32_t *)Server) = 0; // time zero + *(((uint32_t *)(Server + 4))) = htonl(0x01020304); // version 1 2 3 4 + for (int i = 8; i < 3072; ++i){ Server[i] = FILLER_DATA[i % sizeof(FILLER_DATA)]; - } //"random" data + }//"random" data bool encrypted = (Version == 6); - DEBUG_MSG(DLVL_HIGH, "Handshake version is %hhi", Version); + HIGH_MSG("Handshake version is %hhi", Version); uint8_t _validationScheme = 5; if (ValidateClientScheme(Client, 0)) _validationScheme = 0; if (ValidateClientScheme(Client, 1)) _validationScheme = 1; - DEBUG_MSG(DLVL_HIGH, "Handshake type is %hhi, encryption is %s", _validationScheme, encrypted ? "on" : "off"); + HIGH_MSG("Handshake type is %hhi, encryption is %s", _validationScheme, encrypted ? "on" : "off"); uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme); uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme); - //FIRST 1536 bytes for server response + // FIRST 1536 bytes for server response char pTempBuffer[1504]; memcpy(pTempBuffer, Server, serverDigestOffset); - memcpy(pTempBuffer + serverDigestOffset, Server + serverDigestOffset + 32, 1504 - serverDigestOffset); - Secure::hmac_sha256bin(pTempBuffer, 1504, genuineFMSKey, 36, (char*)Server + serverDigestOffset); + memcpy(pTempBuffer + serverDigestOffset, Server + serverDigestOffset + 32, + 1504 - serverDigestOffset); + Secure::hmac_sha256bin(pTempBuffer, 1504, genuineFMSKey, 36, (char *)Server + serverDigestOffset); - //SECOND 1536 bytes for server response + // SECOND 1536 bytes for server response if (_validationScheme == 5 && Version == 3){ - //copy exactly from client - memcpy(Server+1536, Client, 1536); + // copy exactly from client + memcpy(Server + 1536, Client, 1536); }else{ char pTempHash[32]; - Secure::hmac_sha256bin((char*)Client + keyChallengeIndex, 32, genuineFMSKey, 68, pTempHash); - Secure::hmac_sha256bin((char*)Server + 1536, 1536 - 32, pTempHash, 32, (char*)Server + 1536 * 2 - 32); + Secure::hmac_sha256bin((char *)Client + keyChallengeIndex, 32, genuineFMSKey, 68, pTempHash); + Secure::hmac_sha256bin((char *)Server + 1536, 1536 - 32, pTempHash, 32, + (char *)Server + 1536 * 2 - 32); } - Server[ -1] = Version; + Server[-1] = Version; RTMPStream::snd_cnt += 3073; return true; } diff --git a/lib/rtmpchunks.h b/lib/rtmpchunks.h index b5de6d3b..26aa47cd 100644 --- a/lib/rtmpchunks.h +++ b/lib/rtmpchunks.h @@ -2,68 +2,82 @@ /// Holds all headers for the RTMPStream namespace. #pragma once -#include -#include -#include -#include -#include -#include #include "socket.h" +#include +#include +#include +#include +#include +#include #ifndef FILLER_DATA -#define FILLER_DATA "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros tortor commodo neque, vitae hendrerit nunc sem ut odio." +#define FILLER_DATA \ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu " \ + "commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet " \ + "suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae " \ + "iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam " \ + "dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, " \ + "in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. " \ + "Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. " \ + "Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo " \ + "elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum " \ + "nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue " \ + "elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros " \ + "tortor commodo neque, vitae hendrerit nunc sem ut odio." #endif -//forward declaration of FLV::Tag to avoid circular dependencies. -namespace FLV { +// forward declaration of FLV::Tag to avoid circular dependencies. +namespace FLV{ class Tag; } /// Contains all functions and classes needed for RTMP connections. -namespace RTMPStream { +namespace RTMPStream{ - extern unsigned int chunk_rec_max; ///< Maximum size for a received chunk. - extern unsigned int chunk_snd_max; ///< Maximum size for a sent chunk. - extern unsigned int rec_window_size; ///< Window size for receiving. - extern unsigned int snd_window_size; ///< Window size for sending. - extern unsigned int rec_window_at; ///< Current position of the receiving window. - extern unsigned int snd_window_at; ///< Current position of the sending window. - extern unsigned int rec_cnt; ///< Counter for total data received, in bytes. - extern unsigned int snd_cnt; ///< Counter for total data sent, in bytes. + extern size_t chunk_rec_max; ///< Maximum size for a received chunk. + extern size_t chunk_snd_max; ///< Maximum size for a sent chunk. + extern size_t rec_window_size; ///< Window size for receiving. + extern size_t snd_window_size; ///< Window size for sending. + extern size_t rec_window_at; ///< Current position of the receiving window. + extern size_t snd_window_at; ///< Current position of the sending window. + extern size_t rec_cnt; ///< Counter for total data received, in bytes. + extern size_t snd_cnt; ///< Counter for total data sent, in bytes. extern timeval lastrec; ///< Timestamp of last time data was received. /// Holds a single RTMP chunk, either send or receive direction. - class Chunk { - public: - unsigned char headertype; ///< For input chunks, the type of header. This is calculated automatically for output chunks. - unsigned int cs_id; ///< ContentStream ID - unsigned int timestamp; ///< Timestamp of this chunk. - unsigned int ts_delta; ///< Last timestamp delta. - unsigned int ts_header; ///< Last header timestamp without extensions or deltas. - unsigned int len; ///< Length of the complete chunk. - unsigned int real_len; ///< Length of this particular part of it. - unsigned int len_left; ///< Length not yet received, out of complete chunk. - unsigned char msg_type_id; ///< Message Type ID - unsigned int msg_stream_id; ///< Message Stream ID - std::string data; ///< Payload of chunk. + class Chunk{ + public: + unsigned char headertype; ///< For input chunks, the type of header. This is calculated + ///< automatically for output chunks. + unsigned int cs_id; ///< ContentStream ID + unsigned int timestamp; ///< Timestamp of this chunk. + unsigned int ts_delta; ///< Last timestamp delta. + unsigned int ts_header; ///< Last header timestamp without extensions or deltas. + unsigned int len; ///< Length of the complete chunk. + unsigned int real_len; ///< Length of this particular part of it. + unsigned int len_left; ///< Length not yet received, out of complete chunk. + unsigned char msg_type_id; ///< Message Type ID + unsigned int msg_stream_id; ///< Message Stream ID + std::string data; ///< Payload of chunk. - Chunk(); - bool Parse(Socket::Buffer & data); - std::string & Pack(); + Chunk(); + bool Parse(Socket::Buffer &data); + std::string &Pack(); }; - //RTMPStream::Chunk + // RTMPStream::Chunk extern std::map lastsend; extern std::map lastrecv; - std::string & SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data); - std::string & SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts); - std::string & SendMedia(FLV::Tag & tag); - std::string & SendCTL(unsigned char type, unsigned int data); - std::string & SendCTL(unsigned char type, unsigned int data, unsigned char data2); - std::string & SendUSR(unsigned char type, unsigned int data); - std::string & SendUSR(unsigned char type, unsigned int data, unsigned int data2); + std::string &SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, + std::string data); + std::string &SendMedia(unsigned char msg_type_id, unsigned char *data, int len, unsigned int ts); + std::string &SendMedia(FLV::Tag &tag); + std::string &SendCTL(unsigned char type, unsigned int data); + std::string &SendCTL(unsigned char type, unsigned int data, unsigned char data2); + std::string &SendUSR(unsigned char type, unsigned int data); + std::string &SendUSR(unsigned char type, unsigned int data, unsigned int data2); /// This value should be set to the first 1537 bytes received. extern std::string handshake_in; @@ -71,4 +85,5 @@ namespace RTMPStream { extern std::string handshake_out; /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. bool doHandshake(); -} //RTMPStream namespace +}// namespace RTMPStream + diff --git a/lib/shared_memory.cpp b/lib/shared_memory.cpp index dd7f169f..6da6525b 100644 --- a/lib/shared_memory.cpp +++ b/lib/shared_memory.cpp @@ -39,53 +39,6 @@ namespace IPC { } #endif - /// 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; - } - - /// 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; - } - - - /// Stores a long long value of val in network order to the pointer p. - static void htobll(char * p, long long val) { - p[0] = (val >> 56) & 0xFF; - p[1] = (val >> 48) & 0xFF; - p[2] = (val >> 40) & 0xFF; - p[3] = (val >> 32) & 0xFF; - p[4] = (val >> 24) & 0xFF; - p[5] = (val >> 16) & 0xFF; - p[6] = (val >> 8) & 0xFF; - p[7] = val & 0xFF; - } - - /// Reads a long value of p in host order to val. - static void btohl(char * p, long & val) { - val = ((long)p[0] << 24) | ((long)p[1] << 16) | ((long)p[2] << 8) | p[3]; - } - - /// Reads a short value of p in host order to val. - static void btohs(char * p, unsigned short & val) { - val = ((short)p[0] << 8) | p[1]; - } - - /// Reads a long value of p in host order to val. - static void btohl(char * p, unsigned int & val) { - val = ((long)p[0] << 24) | ((long)p[1] << 16) | ((long)p[2] << 8) | p[3]; - } - - /// Reads a long long value of p in host order to val. - static void btohll(char * p, long long & val) { - val = ((long long)p[0] << 56) | ((long long)p[1] << 48) | ((long long)p[2] << 40) | ((long long)p[3] << 32) | ((long long)p[4] << 24) | ((long long)p[5] << 16) | ((long long)p[6] << 8) | p[7]; - } - ///\brief Empty semaphore constructor, clears all values semaphore::semaphore() { #if defined(__CYGWIN__) || defined(_WIN32) @@ -261,8 +214,8 @@ namespace IPC { } #elif defined(__APPLE__) /// \todo (roxlu) test tryWaitOneSecond, shared_memory.cpp - long long unsigned int now = Util::getMicros(); - long long unsigned int timeout = now + 1e6; + uint64_t now = Util::getMicros(); + uint64_t timeout = now + 1e6; while (now < timeout) { if (0 == sem_trywait(mySem)) { isLocked = true; @@ -362,7 +315,7 @@ namespace IPC { ///\param len_ The size to make the page ///\param master_ Whether to create or merely open the page ///\param autoBackoff When only opening the page, wait for it to appear or fail - sharedPage::sharedPage(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { + sharedPage::sharedPage(const std::string & name_, uint64_t len_, bool master_, bool autoBackoff) { handle = 0; len = 0; master = false; @@ -452,7 +405,7 @@ namespace IPC { ///\param len_ The size to make the page ///\param master_ Whether to create or merely open the page ///\param autoBackoff When only opening the page, wait for it to appear or fail - void sharedPage::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { + void sharedPage::init(const std::string & name_, uint64_t len_, bool master_, bool autoBackoff) { close(); name = name_; len = len_; @@ -485,9 +438,9 @@ namespace IPC { } //Under cygwin, the extra 4 bytes contain the real size of the page. if (master) { - ((unsigned int *)mapped)[0] = len_; + Bit::htobl(mapped, len); } else { - len = ((unsigned int *)mapped)[0]; + len = Bit::btohl(mapped); } //Now shift by those 4 bytes. mapped += 4; @@ -495,7 +448,7 @@ namespace IPC { handle = shm_open(name.c_str(), (master ? O_CREAT | O_EXCL : 0) | O_RDWR, ACCESSPERMS); if (handle == -1) { if (master) { - DEBUG_MSG(DLVL_HIGH, "Overwriting old page for %s", name.c_str()); + ERROR_MSG("Overwriting old page for %s", name.c_str()); handle = shm_open(name.c_str(), O_CREAT | O_RDWR, ACCESSPERMS); } else { int i = 0; @@ -514,7 +467,7 @@ namespace IPC { } if (master) { if (ftruncate(handle, len) < 0) { - FAIL_MSG("truncate to %lld for page %s failed: %s", len, name.c_str(), strerror(errno)); + FAIL_MSG("truncate to %" PRIu64 " for page %s failed: %s", len, name.c_str(), strerror(errno)); return; } } else { @@ -546,7 +499,7 @@ namespace IPC { ///\param len_ The size to make the file ///\param master_ Whether to create or merely open the file ///\param autoBackoff When only opening the file, wait for it to appear or fail - sharedFile::sharedFile(std::string name_, unsigned int len_, bool master_, bool autoBackoff) : handle(0), name(name_), len(len_), master(master_), mapped(NULL) { + sharedFile::sharedFile(const std::string & name_, uint64_t len_, bool master_, bool autoBackoff) : handle(0), name(name_), len(len_), master(master_), mapped(NULL) { handle = 0; name = name_; len = len_; @@ -611,7 +564,7 @@ namespace IPC { ///\param len_ The size to make the page ///\param master_ Whether to create or merely open the page ///\param autoBackoff When only opening the page, wait for it to appear or fail - void sharedFile::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { + void sharedFile::init(const std::string & name_, uint64_t len_, bool master_, bool autoBackoff) { close(); name = name_; len = len_; @@ -622,7 +575,7 @@ namespace IPC { handle = open(std::string(Util::getTmpFolder() + name).c_str(), (master ? O_CREAT | O_TRUNC | O_EXCL : 0) | O_RDWR, (mode_t)0600); if (handle == -1) { if (master) { - DEBUG_MSG(DLVL_HIGH, "Overwriting old file for %s", name.c_str()); + HIGH_MSG("Overwriting old file for %s", name.c_str()); handle = open(std::string(Util::getTmpFolder() + name).c_str(), O_CREAT | O_TRUNC | O_RDWR, (mode_t)0600); } else { int i = 0; @@ -669,62 +622,53 @@ namespace IPC { ///\brief Sets timestamp of the current stats void statExchange::now(long long int time) { - htobll(data, time); + Bit::htobll(data, time); } ///\brief Gets timestamp of the current stats long long int statExchange::now() { long long int result; - btohll(data, result); - return result; + return Bit::btohll(data); } ///\brief Sets time currently connected void statExchange::time(long time) { - htobl(data + 8, time); + Bit::htobl(data + 8, time); } ///\brief Gets time currently connected long statExchange::time() { - long result; - btohl(data + 8, result); - return result; + return Bit::btohl(data + 8); } ///\brief Sets the last viewing second of this user void statExchange::lastSecond(long time) { - htobl(data + 12, time); + Bit::htobl(data + 12, time); } ///\brief Gets the last viewing second of this user long statExchange::lastSecond() { - long result; - btohl(data + 12, result); - return result; + return Bit::btohl(data + 12); } ///\brief Sets the amount of bytes received void statExchange::down(long long int bytes) { - htobll(data + 16, bytes); + Bit::htobll(data + 16, bytes); } ///\brief Gets the amount of bytes received long long int statExchange::down() { - long long int result; - btohll(data + 16, result); - return result; + return Bit::btohll(data + 16); } ///\brief Sets the amount of bytes sent void statExchange::up(long long int bytes) { - htobll(data + 24, bytes); + Bit::htobll(data + 24, bytes); } ///\brief Gets the amount of bytes sent long long int statExchange::up() { - long long int result; - btohll(data + 24, result); - return result; + return Bit::btohll(data + 24); } ///\brief Sets the host of this connection @@ -766,14 +710,12 @@ namespace IPC { ///\brief Sets checksum field void statExchange::crc(unsigned int sum) { - htobl(data + 168, sum); + Bit::htobl(data + 168, sum); } ///\brief Gets checksum field unsigned int statExchange::crc() { - unsigned int result; - btohl(data + 168, result); - return result; + return Bit::btohl(data + 168); } ///\brief Sets checksum field diff --git a/lib/shared_memory.h b/lib/shared_memory.h index fe146910..64b60796 100644 --- a/lib/shared_memory.h +++ b/lib/shared_memory.h @@ -102,11 +102,11 @@ namespace IPC { ///\brief A class for managing shared files. class sharedFile { public: - sharedFile(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); + sharedFile(const std::string & name_ = "", uint64_t len_ = 0, bool master_ = false, bool autoBackoff = true); sharedFile(const sharedFile & rhs); ~sharedFile(); operator bool() const; - void init(std::string name_, unsigned int len_, bool master_ = false, bool autoBackoff = true); + void init(const std::string & name_, uint64_t len_, bool master_ = false, bool autoBackoff = true); void operator =(sharedFile & rhs); bool operator < (const sharedFile & rhs) const { return name < rhs.name; @@ -119,7 +119,7 @@ namespace IPC { ///\brief The name of the opened shared file std::string name; ///\brief The size in bytes of the opened shared file - long long int len; + uint64_t len; ///\brief Whether this class should unlink the shared file upon deletion or not bool master; ///\brief A pointer to the payload of the file file @@ -135,11 +135,11 @@ namespace IPC { ///\brief A class for managing shared memory pages. class sharedPage { public: - sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); + sharedPage(const std::string & name_ = "", uint64_t len_ = 0, bool master_ = false, bool autoBackoff = true); sharedPage(const sharedPage & rhs); ~sharedPage(); operator bool() const; - void init(std::string name_, unsigned int len_, bool master_ = false, bool autoBackoff = true); + void init(const std::string & name_, uint64_t len_, bool master_ = false, bool autoBackoff = true); void operator =(sharedPage & rhs); bool operator < (const sharedPage & rhs) const { return name < rhs.name; @@ -168,7 +168,7 @@ namespace IPC { ///Uses shared files at its backbone, defined for portability class sharedPage: public sharedFile { public: - sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); + sharedPage(const std::string & name_ = "", uint64_t len_ = 0, bool master_ = false, bool autoBackoff = true); sharedPage(const sharedPage & rhs); ~sharedPage(); }; diff --git a/lib/socket.cpp b/lib/socket.cpp index 84239e0f..b591fa58 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -6,13 +6,13 @@ #include "defines.h" #include "timing.h" #include +#include #include #include #include #include #include #include -#include #define BUFFER_BLOCKSIZE 4096 // set buffer blocksize to 4KiB @@ -23,33 +23,30 @@ #endif /// Local-scope only helper function that prints address families -static const char* addrFam(int f){ - switch(f){ - case AF_UNSPEC: return "Unspecified"; - case AF_INET: return "IPv4"; - case AF_INET6: return "IPv6"; - case PF_UNIX: return "Unix"; - default: return "???"; +static const char *addrFam(int f){ + switch (f){ + case AF_UNSPEC: return "Unspecified"; + case AF_INET: return "IPv4"; + case AF_INET6: return "IPv6"; + case PF_UNIX: return "Unix"; + default: return "???"; } } -static std::string getIPv6BinAddr(const struct sockaddr_in6 & remoteaddr){ +static std::string getIPv6BinAddr(const struct sockaddr_in6 &remoteaddr){ char tmpBuffer[17] = "\000\000\000\000\000\000\000\000\000\000\377\377\000\000\000\000"; switch (remoteaddr.sin6_family){ - case AF_INET: - memcpy(tmpBuffer + 12, &(reinterpret_cast(&remoteaddr)->sin_addr.s_addr), 4); - break; - case AF_INET6: - memcpy(tmpBuffer, &(remoteaddr.sin6_addr.s6_addr), 16); - break; - default: - return ""; - break; + case AF_INET: + memcpy(tmpBuffer + 12, &(reinterpret_cast(&remoteaddr)->sin_addr.s_addr), + 4); + break; + case AF_INET6: memcpy(tmpBuffer, &(remoteaddr.sin6_addr.s6_addr), 16); break; + default: return ""; break; } return std::string(tmpBuffer, 16); } -bool Socket::isLocalhost(const std::string & remotehost){ +bool Socket::isLocalhost(const std::string &remotehost){ std::string tmpInput = remotehost; std::string bf = Socket::getBinForms(tmpInput); std::string tmpAddr; @@ -61,33 +58,31 @@ bool Socket::isLocalhost(const std::string & remotehost){ return false; } -bool Socket::isLocal(const std::string & remotehost){ - struct ifaddrs * ifAddrStruct=NULL; - struct ifaddrs * ifa=NULL; - void * tmpAddrPtr=NULL; +bool Socket::isLocal(const std::string &remotehost){ + struct ifaddrs *ifAddrStruct = NULL; + struct ifaddrs *ifa = NULL; + void *tmpAddrPtr = NULL; char addressBuffer[INET6_ADDRSTRLEN]; getifaddrs(&ifAddrStruct); for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next){ - if (!ifa->ifa_addr){ - continue; - } + if (!ifa->ifa_addr){continue;} if (ifa->ifa_addr->sa_family == AF_INET){// check it is IP4 - tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); INSANE_MSG("Comparing '%s' to '%s'", remotehost.c_str(), addressBuffer); if (remotehost == addressBuffer){return true;} INSANE_MSG("Comparing '%s' to '::ffff:%s'", remotehost.c_str(), addressBuffer); if (remotehost == std::string("::ffff:") + addressBuffer){return true;} }else if (ifa->ifa_addr->sa_family == AF_INET6){// check it is IP6 - tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; + tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr; inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); INSANE_MSG("Comparing '%s' to '%s'", remotehost.c_str(), addressBuffer); if (remotehost == addressBuffer){return true;} } } - if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct); + if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); return false; } @@ -111,13 +106,13 @@ bool Socket::matchIPv6Addr(const std::string &A, const std::string &B, uint8_t p /// Attempts to match the given address with optional subnet to the given binary-form IPv6 address. /// Returns true if match could be made, false otherwise. bool Socket::isBinAddress(const std::string &binAddr, std::string addr){ - //Check if we need to do prefix matching + // Check if we need to do prefix matching uint8_t prefixLen = 0; if (addr.find('/') != std::string::npos){ prefixLen = atoi(addr.c_str() + addr.find('/') + 1); addr.erase(addr.find('/'), std::string::npos); } - //Loops over all IPs for the given address and matches them in IPv6 form. + // Loops over all IPs for the given address and matches them in IPv6 form. struct addrinfo *result, *rp, hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -145,13 +140,13 @@ bool Socket::isBinAddress(const std::string &binAddr, std::string addr){ /// Converts the given address with optional subnet to binary IPv6 form. /// Returns 16 bytes of address, followed by 1 byte of subnet bits, zero or more times. std::string Socket::getBinForms(std::string addr){ - //Check if we need to do prefix matching + // Check if we need to do prefix matching uint8_t prefixLen = 128; if (addr.find('/') != std::string::npos){ prefixLen = atoi(addr.c_str() + addr.find('/') + 1); addr.erase(addr.find('/'), std::string::npos); } - //Loops over all IPs for the given address and converts to IPv6 binary form. + // Loops over all IPs for the given address and converts to IPv6 binary form. struct addrinfo *result, *rp, hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; @@ -167,7 +162,7 @@ std::string Socket::getBinForms(std::string addr){ for (rp = result; rp != NULL; rp = rp->ai_next){ ret += getIPv6BinAddr(*((sockaddr_in6 *)rp->ai_addr)); if (rp->ai_family == AF_INET){ - ret += (char)(prefixLen<=32 ? prefixLen + 96 : prefixLen); + ret += (char)(prefixLen <= 32 ? prefixLen + 96 : prefixLen); }else{ ret += (char)prefixLen; } @@ -176,8 +171,8 @@ std::string Socket::getBinForms(std::string addr){ return ret; } -/// Checks bytes (length len) containing a binary-encoded IPv4 or IPv6 IP address, and writes it in human-readable notation to target. -/// Writes "unknown" if it cannot decode to a sensible value. +/// Checks bytes (length len) containing a binary-encoded IPv4 or IPv6 IP address, and writes it in +/// human-readable notation to target. Writes "unknown" if it cannot decode to a sensible value. void Socket::hostBytesToStr(const char *bytes, size_t len, std::string &target){ switch (len){ case 4: @@ -192,7 +187,7 @@ void Socket::hostBytesToStr(const char *bytes, size_t len, std::string &target){ target = tmpstr; }else{ char tmpstr[40]; - snprintf(tmpstr, 40, "%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x:%0.2x%0.2x", bytes[0], bytes[1], + snprintf(tmpstr, 40, "%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); target = tmpstr; @@ -235,7 +230,8 @@ unsigned int Socket::Buffer::bytesToSplit(){ unsigned int i = 0; for (std::deque::reverse_iterator it = data.rbegin(); it != data.rend(); ++it){ i += (*it).size(); - if ((*it).size() >= splitter.size() && (*it).substr((*it).size()-splitter.size()) == splitter){ + if ((*it).size() >= splitter.size() && + (*it).substr((*it).size() - splitter.size()) == splitter){ return i; } } @@ -243,12 +239,13 @@ unsigned int Socket::Buffer::bytesToSplit(){ } /// Appends this string to the internal std::deque of std::string objects. -/// It is automatically split every BUFFER_BLOCKSIZE bytes and when the splitter string is encountered. +/// It is automatically split every BUFFER_BLOCKSIZE bytes and when the splitter string is +/// encountered. void Socket::Buffer::append(const std::string &newdata){ append(newdata.data(), newdata.size()); } -///Helper function that does a short-circuiting string compare +/// Helper function that does a short-circuiting string compare inline bool string_compare(const char *a, const char *b, const size_t len){ for (size_t i = 0; i < len; ++i){ if (a[i] != b[i]){return false;} @@ -257,7 +254,8 @@ inline bool string_compare(const char *a, const char *b, const size_t len){ } /// Appends this data block to the internal std::deque of std::string objects. -/// It is automatically split every BUFFER_BLOCKSIZE bytes and when the splitter string is encountered. +/// It is automatically split every BUFFER_BLOCKSIZE bytes and when the splitter string is +/// encountered. void Socket::Buffer::append(const char *newdata, const unsigned int newdatasize){ uint32_t i = 0; while (i < newdatasize){ @@ -269,10 +267,12 @@ void Socket::Buffer::append(const char *newdata, const unsigned int newdatasize) j = newdatasize - i; } }else{ - while (j+i < newdatasize && j < BUFFER_BLOCKSIZE){ + while (j + i < newdatasize && j < BUFFER_BLOCKSIZE){ j++; if (j >= splitter.size()){ - if (string_compare(newdata+i+j-splitter.size(), splitter.data(), splitter.size())){break;} + if (string_compare(newdata + i + j - splitter.size(), splitter.data(), splitter.size())){ + break; + } } } } @@ -286,7 +286,7 @@ void Socket::Buffer::append(const char *newdata, const unsigned int newdatasize) } } if (data.size() > 5000){ - DEBUG_MSG(DLVL_WARN, "Warning: After %d new bytes, buffer has %d parts containing over %u bytes!", newdatasize, (int)data.size(), + WARN_MSG("Warning: After %d new bytes, buffer has %d parts containing over %u bytes!", newdatasize, (int)data.size(), bytes(9000)); } } @@ -372,8 +372,8 @@ void Socket::Buffer::clear(){ data.clear(); } -/// Create a new base socket. This is a basic constructor for converting any valid socket to a Socket::Connection. -/// \param sockNo Integer representing the socket to convert. +/// Create a new base socket. This is a basic constructor for converting any valid socket to a +/// Socket::Connection. \param sockNo Integer representing the socket to convert. Socket::Connection::Connection(int sockNo){ sSend = sockNo; sRecv = -1; @@ -509,7 +509,8 @@ int Socket::Connection::getPureSocket(){ } /// Returns a string describing the last error that occured. -/// Only reports errors if an error actually occured - returns the host address or empty string otherwise. +/// Only reports errors if an error actually occured - returns the host address or empty string +/// otherwise. std::string Socket::Connection::getError(){ return remotehost; } @@ -572,7 +573,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ hints.ai_flags = AI_ADDRCONFIG; int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result); if (s != 0){ - DEBUG_MSG(DLVL_FAIL, "Could not connect to %s:%i! Error: %s", host.c_str(), port, gai_strerror(s)); + FAIL_MSG("Could not connect to %s:%i! Error: %s", host.c_str(), port, gai_strerror(s)); close(); return; } @@ -588,7 +589,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ freeaddrinfo(result); if (rp == 0){ - DEBUG_MSG(DLVL_FAIL, "Could not connect to %s! Error: %s", host.c_str(), remotehost.c_str()); + FAIL_MSG("Could not connect to %s! Error: %s", host.c_str(), remotehost.c_str()); close(); }else{ if (nonblock){ @@ -629,7 +630,8 @@ uint64_t Socket::Connection::dataDown(){ /// Returns a std::string of stats, ended by a newline. /// Requires the current connector name as an argument. std::string Socket::Connection::getStats(std::string C){ - return "S " + getHost() + " " + C + " " + uint2string(Util::epoch() - conntime) + " " + uint2string(up) + " " + uint2string(down) + "\n"; + return "S " + getHost() + " " + C + " " + uint2string(Util::epoch() - conntime) + " " + + uint2string(up) + " " + uint2string(down) + "\n"; } /// Updates the downbuffer internal variable. @@ -660,7 +662,9 @@ void Socket::Connection::SendNow(const char *data, size_t len){ bool bing = isBlocking(); if (!bing){setBlocking(true);} unsigned int i = iwrite(data, std::min((long unsigned int)len, SOCKETSIZE)); - while (i < len && connected()){i += iwrite(data + i, std::min((long unsigned int)(len - i), SOCKETSIZE));} + while (i < len && connected()){ + i += iwrite(data + i, std::min((long unsigned int)(len - i), SOCKETSIZE)); + } if (!bing){setBlocking(false);} } @@ -678,7 +682,7 @@ void Socket::Connection::SendNow(const std::string &data){ } void Socket::Connection::skipBytes(uint32_t byteCount){ - INFO_MSG("Skipping first %lu bytes going to socket", byteCount); + INFO_MSG("Skipping first %" PRIu32 " bytes going to socket", byteCount); skipCount = byteCount; } @@ -690,15 +694,15 @@ void Socket::Connection::skipBytes(uint32_t byteCount){ unsigned int Socket::Connection::iwrite(const void *buffer, int len){ if (!connected() || len < 1){return 0;} if (skipCount){ - //We have bytes to skip writing. - //Pretend we write them, but don't really. + // We have bytes to skip writing. + // Pretend we write them, but don't really. if (len <= skipCount){ skipCount -= len; return len; }else{ unsigned int toSkip = skipCount; skipCount = 0; - return iwrite((((char*)buffer)+toSkip), len-toSkip) + toSkip; + return iwrite((((char *)buffer) + toSkip), len - toSkip) + toSkip; } } int r; @@ -802,7 +806,7 @@ std::string Socket::Connection::getBinHost(){ /// Overwrites the detected host, thus possibily making it incorrect. void Socket::Connection::setHost(std::string host){ remotehost = host; - struct addrinfo *result, *rp, hints; + struct addrinfo *result, hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; @@ -813,9 +817,7 @@ void Socket::Connection::setHost(std::string host){ hints.ai_next = NULL; int s = getaddrinfo(host.c_str(), 0, &hints, &result); if (s != 0){return;} - if (result){ - remoteaddr = *((sockaddr_in6 *)result->ai_addr); - } + if (result){remoteaddr = *((sockaddr_in6 *)result->ai_addr);} freeaddrinfo(result); } @@ -840,7 +842,7 @@ Socket::Connection::operator bool() const{ /// Returns true if the given address can be matched with the remote host. /// Can no longer return true after any socket error have occurred. bool Socket::Connection::isAddress(const std::string &addr){ - //Retrieve current socket binary address + // Retrieve current socket binary address std::string myBinAddr = getBinHost(); return isBinAddress(myBinAddr, addr); } @@ -865,7 +867,8 @@ static void my_debug(void *ctx, int level, const char *file, int line, const cha fflush((FILE *)ctx); } -Socket::SSLConnection::SSLConnection(std::string hostname, int port, bool nonblock) : Socket::Connection(){ +Socket::SSLConnection::SSLConnection(std::string hostname, int port, bool nonblock) + : Socket::Connection(){ mbedtls_debug_set_threshold(0); isConnected = true; server_fd = new mbedtls_net_context; @@ -879,14 +882,15 @@ Socket::SSLConnection::SSLConnection(std::string hostname, int port, bool nonblo mbedtls_ctr_drbg_init(ctr_drbg); mbedtls_entropy_init(entropy); DONTEVEN_MSG("SSL init"); - if (mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, (const unsigned char*)"meow", 4) != 0){ + if (mbedtls_ctr_drbg_seed(ctr_drbg, mbedtls_entropy_func, entropy, (const unsigned char *)"meow", + 4) != 0){ FAIL_MSG("SSL socket init failed"); close(); return; } DONTEVEN_MSG("SSL connect"); int ret = 0; - if ((ret = mbedtls_net_connect(server_fd, hostname.c_str(), JSON::Value((long long)port).asString().c_str(), MBEDTLS_NET_PROTO_TCP)) != 0){ + if ((ret = mbedtls_net_connect(server_fd, hostname.c_str(), JSON::Value(port).asString().c_str(), MBEDTLS_NET_PROTO_TCP)) != 0){ FAIL_MSG(" failed\n ! mbedtls_net_connect returned %d\n\n", ret); close(); return; @@ -899,13 +903,13 @@ Socket::SSLConnection::SSLConnection(std::string hostname, int port, bool nonblo } mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, ctr_drbg); - mbedtls_ssl_conf_dbg(conf, my_debug, stderr ); + mbedtls_ssl_conf_dbg(conf, my_debug, stderr); if ((ret = mbedtls_ssl_setup(ssl, conf)) != 0){ - char estr[200]; - mbedtls_strerror(ret, estr, 200); - FAIL_MSG("SSL setup error %d: %s", ret, estr); - close(); - return; + char estr[200]; + mbedtls_strerror(ret, estr, 200); + FAIL_MSG("SSL setup error %d: %s", ret, estr); + close(); + return; } if ((ret = mbedtls_ssl_set_hostname(ssl, hostname.c_str())) != 0){ FAIL_MSG(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); @@ -923,9 +927,7 @@ Socket::SSLConnection::SSLConnection(std::string hostname, int port, bool nonblo } } Blocking = true; - if (nonblock){ - setBlocking(false); - } + if (nonblock){setBlocking(false);} } void Socket::SSLConnection::close(){ @@ -969,7 +971,7 @@ int Socket::SSLConnection::iread(void *buffer, int len, int flags){ if (!connected() || len < 1){return 0;} int r; /// \TODO Flags ignored... Bad. - r = mbedtls_ssl_read(ssl, (unsigned char*)buffer, len); + r = mbedtls_ssl_read(ssl, (unsigned char *)buffer, len); if (r < 0){ char estr[200]; mbedtls_strerror(r, estr, 200); @@ -1006,7 +1008,7 @@ unsigned int Socket::SSLConnection::iwrite(const void *buffer, int len){ DONTEVEN_MSG("SSL iwrite"); if (!connected() || len < 1){return 0;} int r; - r = mbedtls_ssl_write(ssl, (const unsigned char*)buffer, len); + r = mbedtls_ssl_write(ssl, (const unsigned char *)buffer, len); if (r < 0){ char estr[200]; mbedtls_strerror(r, estr, 200); @@ -1050,7 +1052,8 @@ void Socket::SSLConnection::setBlocking(bool blocking){ #endif -/// Create a new base Server. The socket is never connected, and a placeholder for later connections. +/// Create a new base Server. The socket is never connected, and a placeholder for later +/// connections. Socket::Server::Server(){ sock = -1; }// Socket::Server base Constructor @@ -1060,10 +1063,11 @@ Socket::Server::Server(){ /// Any further connections coming in will be dropped. /// \param port The TCP port to listen on /// \param hostname (optional) The interface to bind to. The default is 0.0.0.0 (all interfaces). -/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false +/// (blocking). Socket::Server::Server(int port, std::string hostname, bool nonblock){ if (!IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ - DEBUG_MSG(DLVL_FAIL, "Could not create socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); + FAIL_MSG("Could not create socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); sock = -1; } }// Socket::Server TCP Constructor @@ -1077,7 +1081,7 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "Could not create IPv6 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); + ERROR_MSG("Could not create IPv6 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); return false; } int on = 1; @@ -1095,7 +1099,7 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(port); // set port - //set interface, 0.0.0.0 (default) is all + // set interface, 0.0.0.0 (default) is all if (hostname == "0.0.0.0" || hostname.length() == 0){ addr.sin6_addr = in6addr_any; }else{ @@ -1110,17 +1114,17 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ if (ret == 0){ ret = listen(sock, 100); // start listening, backlog of 100 allowed if (ret == 0){ - DEBUG_MSG(DLVL_DEVEL, "IPv6 socket success @ %s:%i", hostname.c_str(), port); + DEVEL_MSG("IPv6 socket success @ %s:%i", hostname.c_str(), port); return true; }else{ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "IPv6 listen failed! Error: %s", errors.c_str()); + ERROR_MSG("IPv6 listen failed! Error: %s", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "IPv6 Binding %s:%i failed (%s)", hostname.c_str(), port, errors.c_str()); + ERROR_MSG("IPv6 Binding %s:%i failed (%s)", hostname.c_str(), port, errors.c_str()); close(); return false; } @@ -1135,7 +1139,7 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "Could not create IPv4 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); + ERROR_MSG("Could not create IPv4 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); return false; } int on = 1; @@ -1149,7 +1153,7 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ memset(&addr4, 0, sizeof(addr4)); addr4.sin_family = AF_INET; addr4.sin_port = htons(port); // set port - //set interface, 0.0.0.0 (default) is all + // set interface, 0.0.0.0 (default) is all if (hostname == "0.0.0.0" || hostname.length() == 0){ addr4.sin_addr.s_addr = INADDR_ANY; }else{ @@ -1164,17 +1168,17 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ if (ret == 0){ ret = listen(sock, 100); // start listening, backlog of 100 allowed if (ret == 0){ - DEBUG_MSG(DLVL_DEVEL, "IPv4 socket success @ %s:%i", hostname.c_str(), port); + DEVEL_MSG("IPv4 socket success @ %s:%i", hostname.c_str(), port); return true; }else{ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "IPv4 listen failed! Error: %s", errors.c_str()); + ERROR_MSG("IPv4 listen failed! Error: %s", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "IPv4 Binding %s:%i failed (%s)", hostname.c_str(), port, errors.c_str()); + ERROR_MSG("IPv4 Binding %s:%i failed (%s)", hostname.c_str(), port, errors.c_str()); close(); return false; } @@ -1183,16 +1187,16 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ /// Create a new Unix Server. The socket is immediately bound and set to listen. /// A maximum of 100 connections will be accepted between accept() calls. /// Any further connections coming in will be dropped. -/// The address used will first be unlinked - so it succeeds if the Unix socket already existed. Watch out for this behaviour - it will delete -/// any file located at address! -/// \param address The location of the Unix socket to bind to. -/// \param nonblock (optional) Whether accept() calls will be nonblocking. Default is false (blocking). +/// The address used will first be unlinked - so it succeeds if the Unix socket already existed. +/// Watch out for this behaviour - it will delete any file located at address! \param address The +/// location of the Unix socket to bind to. \param nonblock (optional) Whether accept() calls will +/// be nonblocking. Default is false (blocking). Socket::Server::Server(std::string address, bool nonblock){ unlink(address.c_str()); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "Could not create unix socket %s! Error: %s", address.c_str(), errors.c_str()); + ERROR_MSG("Could not create unix socket %s! Error: %s", address.c_str(), errors.c_str()); return; } if (nonblock){ @@ -1210,22 +1214,23 @@ Socket::Server::Server(std::string address, bool nonblock){ return; }else{ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "Unix listen failed! Error: %s", errors.c_str()); + ERROR_MSG("Unix listen failed! Error: %s", errors.c_str()); close(); return; } }else{ errors = strerror(errno); - DEBUG_MSG(DLVL_ERROR, "Unix Binding %s failed (%s)", address.c_str(), errors.c_str()); + ERROR_MSG("Unix Binding %s failed (%s)", address.c_str(), errors.c_str()); close(); return; } }// Socket::Server Unix Constructor -/// Accept any waiting connections. If the Socket::Server is blocking, this function will block until there is an incoming connection. -/// If the Socket::Server is nonblocking, it might return a Socket::Connection that is not connected, so check for this. -/// \param nonblock (optional) Whether the newly connected socket should be nonblocking. Default is false (blocking). -/// \returns A Socket::Connection, which may or may not be connected, depending on settings and circumstances. +/// Accept any waiting connections. If the Socket::Server is blocking, this function will block +/// until there is an incoming connection. If the Socket::Server is nonblocking, it might return a +/// Socket::Connection that is not connected, so check for this. \param nonblock (optional) Whether +/// the newly connected socket should be nonblocking. Default is false (blocking). \returns A +/// Socket::Connection, which may or may not be connected, depending on settings and circumstances. Socket::Connection Socket::Server::accept(bool nonblock){ if (sock < 0){return Socket::Connection(-1);} struct sockaddr_in6 tmpaddr; @@ -1254,14 +1259,15 @@ Socket::Connection Socket::Server::accept(bool nonblock){ tmp.remoteaddr = tmpaddr; if (tmpaddr.sin6_family == AF_INET6){ tmp.remotehost = inet_ntop(AF_INET6, &(tmpaddr.sin6_addr), addrconv, INET6_ADDRSTRLEN); - DEBUG_MSG(DLVL_HIGH, "IPv6 addr [%s]", tmp.remotehost.c_str()); + HIGH_MSG("IPv6 addr [%s]", tmp.remotehost.c_str()); } if (tmpaddr.sin6_family == AF_INET){ - tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in *)&tmpaddr)->sin_addr), addrconv, INET6_ADDRSTRLEN); - DEBUG_MSG(DLVL_HIGH, "IPv4 addr [%s]", tmp.remotehost.c_str()); + tmp.remotehost = + inet_ntop(AF_INET, &(((sockaddr_in *)&tmpaddr)->sin_addr), addrconv, INET6_ADDRSTRLEN); + HIGH_MSG("IPv4 addr [%s]", tmp.remotehost.c_str()); } if (tmpaddr.sin6_family == AF_UNIX){ - DEBUG_MSG(DLVL_HIGH, "Unix connection"); + HIGH_MSG("Unix connection"); tmp.remotehost = "UNIX_SOCKET"; } return tmp; @@ -1294,7 +1300,7 @@ void Socket::Server::close(){ void Socket::Server::drop(){ if (connected()){ if (sock != -1){ - DEBUG_MSG(DLVL_HIGH, "ServerSocket %d closed", sock); + HIGH_MSG("ServerSocket %d closed", sock); errno = EINTR; while (::close(sock) != 0 && errno == EINTR){} sock = -1; @@ -1327,7 +1333,7 @@ Socket::UDPConnection::UDPConnection(bool nonblock){ sock = socket(AF_INET, SOCK_DGRAM, 0); family = AF_INET; } - if (sock == -1){DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));} + if (sock == -1){FAIL_MSG("Could not create UDP socket: %s", strerror(errno));} up = 0; down = 0; destAddr = 0; @@ -1347,7 +1353,7 @@ Socket::UDPConnection::UDPConnection(const UDPConnection &o){ sock = socket(AF_INET, SOCK_DGRAM, 0); family = AF_INET; } - if (sock == -1){DEBUG_MSG(DLVL_FAIL, "Could not create UDP socket: %s", strerror(errno));} + if (sock == -1){FAIL_MSG("Could not create UDP socket: %s", strerror(errno));} up = 0; down = 0; if (o.destAddr && o.destAddr_size){ @@ -1392,11 +1398,9 @@ Socket::UDPConnection::~UDPConnection(){ /// Stores the properties of the receiving end of this UDP socket. /// This will be the receiving end for all SendNow calls. void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ - //UDP sockets can switch between IPv4 and IPv6 on demand. - //We change IPv4-mapped IPv6 addresses into IPv4 addresses for Windows-sillyness reasons. - if (destIp.substr(0, 7) == "::ffff:"){ - destIp = destIp.substr(7); - } + // UDP sockets can switch between IPv4 and IPv6 on demand. + // We change IPv4-mapped IPv6 addresses into IPv4 addresses for Windows-sillyness reasons. + if (destIp.substr(0, 7) == "::ffff:"){destIp = destIp.substr(7);} struct addrinfo *result, *rp, hints; std::stringstream ss; ss << port; @@ -1411,7 +1415,7 @@ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ hints.ai_next = NULL; int s = getaddrinfo(destIp.c_str(), ss.str().c_str(), &hints, &result); if (s != 0){ - DEBUG_MSG(DLVL_FAIL, "Could not connect UDP socket to %s:%i! Error: %s", destIp.c_str(), port, gai_strerror(s)); + FAIL_MSG("Could not connect UDP socket to %s:%i! Error: %s", destIp.c_str(), port, gai_strerror(s)); return; } @@ -1426,7 +1430,8 @@ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ if (!destAddr){return;} memcpy(destAddr, rp->ai_addr, rp->ai_addrlen); if (family != rp->ai_family){ - INFO_MSG("Socket is wrong type (%s), re-opening as %s", addrFam(family), addrFam(rp->ai_family)); + INFO_MSG("Socket is wrong type (%s), re-opening as %s", addrFam(family), + addrFam(rp->ai_family)); close(); family = rp->ai_family; sock = socket(family, SOCK_DGRAM, 0); @@ -1439,7 +1444,7 @@ void Socket::UDPConnection::SetDestination(std::string destIp, uint32_t port){ freeaddrinfo(result); free(destAddr); destAddr = 0; - DEBUG_MSG(DLVL_FAIL, "Could not set destination for UDP socket: %s:%d", destIp.c_str(), port); + FAIL_MSG("Could not set destination for UDP socket: %s:%d", destIp.c_str(), port); }// Socket::UDPConnection SetDestination /// Gets the properties of the receiving end of this UDP socket. @@ -1453,14 +1458,16 @@ void Socket::UDPConnection::GetDestination(std::string &destIp, uint32_t &port){ char addr_str[INET6_ADDRSTRLEN + 1]; addr_str[INET6_ADDRSTRLEN] = 0; // set last byte to zero, to prevent walking out of the array if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6){ - if (inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)destAddr)->sin6_addr), addr_str, INET6_ADDRSTRLEN) != 0){ + if (inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)destAddr)->sin6_addr), addr_str, + INET6_ADDRSTRLEN) != 0){ destIp = addr_str; port = ntohs(((struct sockaddr_in6 *)destAddr)->sin6_port); return; } } if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET){ - if (inet_ntop(AF_INET, &(((struct sockaddr_in *)destAddr)->sin_addr), addr_str, INET6_ADDRSTRLEN) != 0){ + if (inet_ntop(AF_INET, &(((struct sockaddr_in *)destAddr)->sin_addr), addr_str, + INET6_ADDRSTRLEN) != 0){ destIp = addr_str; port = ntohs(((struct sockaddr_in *)destAddr)->sin_port); return; @@ -1468,15 +1475,19 @@ void Socket::UDPConnection::GetDestination(std::string &destIp, uint32_t &port){ } destIp = ""; port = 0; - DEBUG_MSG(DLVL_FAIL, "Could not get destination for UDP socket"); + FAIL_MSG("Could not get destination for UDP socket"); }// Socket::UDPConnection GetDestination /// Returns the port number of the receiving end of this socket. /// Returns 0 on error. uint32_t Socket::UDPConnection::getDestPort() const{ if (!destAddr || !destAddr_size){return 0;} - if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6){return ntohs(((struct sockaddr_in6 *)destAddr)->sin6_port);} - if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET){return ntohs(((struct sockaddr_in *)destAddr)->sin_port);} + if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET6){ + return ntohs(((struct sockaddr_in6 *)destAddr)->sin6_port); + } + if (((struct sockaddr_in *)destAddr)->sin_family == AF_INET){ + return ntohs(((struct sockaddr_in *)destAddr)->sin_port); + } return 0; } @@ -1509,7 +1520,7 @@ void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ if (r > 0){ up += r; }else{ - DEBUG_MSG(DLVL_FAIL, "Could not send UDP data through %d: %s", sock, strerror(errno)); + FAIL_MSG("Could not send UDP data through %d: %s", sock, strerror(errno)); } } @@ -1517,12 +1528,12 @@ void Socket::UDPConnection::SendNow(const char *sdata, size_t len){ /// If that fails, returns zero. /// \arg port Port to bind to, required. /// \arg iface Interface address to listen for packets on (may be multicast address) -/// \arg multicastInterfaces Comma-separated list of interfaces to listen on for multicast packets. Optional, left out means automatically chosen -/// by kernel. -/// \return Actually bound port number, or zero on error. -uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::string &multicastInterfaces){ +/// \arg multicastInterfaces Comma-separated list of interfaces to listen on for multicast packets. +/// Optional, left out means automatically chosen by kernel. \return Actually bound port number, or +/// zero on error. +uint16_t Socket::UDPConnection::bind(int port, std::string iface, + const std::string &multicastInterfaces){ close(); // we open a new socket for each attempt - int result = 0; int addr_ret; bool multicast = false; struct addrinfo hints, *addr_result, *rp; @@ -1533,7 +1544,7 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str }else{ hints.ai_family = AF_UNSPEC; } - + hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; @@ -1559,13 +1570,13 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str if (sock == -1){continue;} char human_addr[INET6_ADDRSTRLEN]; char human_port[16]; - getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, human_port, 16, NI_NUMERICHOST | NI_NUMERICSERV); + getnameinfo(rp->ai_addr, rp->ai_addrlen, human_addr, INET6_ADDRSTRLEN, human_port, 16, + NI_NUMERICHOST | NI_NUMERICSERV); MEDIUM_MSG("Attempting bind to %s:%s (%s)", human_addr, human_port, addrFam(rp->ai_family)); family = rp->ai_family; hints.ai_family = family; if (family == AF_INET6){ sockaddr_in6 *addr6 = (sockaddr_in6 *)(rp->ai_addr); - result = ntohs(addr6->sin6_port); if (memcmp((char *)&(addr6->sin6_addr), "\000\000\000\000\000\000\000\000\000\000\377\377", 12) == 0){ // IPv6-mapped IPv4 address - 13th byte ([12]) holds the first IPv4 byte multicast = (((char *)&(addr6->sin6_addr))[12] & 0xF0) == 0xE0; @@ -1575,7 +1586,6 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str } }else{ sockaddr_in *addr4 = (sockaddr_in *)(rp->ai_addr); - result = ntohs(addr4->sin_port); // multicast has a "1110" bit prefix multicast = (((char *)&(addr4->sin_addr))[0] & 0xF0) == 0xE0; } @@ -1586,14 +1596,14 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str } } if (::bind(sock, rp->ai_addr, rp->ai_addrlen) == 0){ - //get port number + // get port number struct sockaddr_storage fin_addr; socklen_t alen = sizeof(fin_addr); - if (getsockname(sock, (struct sockaddr*)&fin_addr, &alen) == 0){ + if (getsockname(sock, (struct sockaddr *)&fin_addr, &alen) == 0){ if (family == AF_INET6){ - portNo = ntohs(((struct sockaddr_in6*)&fin_addr)->sin6_port); + portNo = ntohs(((struct sockaddr_in6 *)&fin_addr)->sin6_port); }else{ - portNo = ntohs(((struct sockaddr_in*)&fin_addr)->sin_port); + portNo = ntohs(((struct sockaddr_in *)&fin_addr)->sin_port); } } INFO_MSG("UDP bind success on %s:%u (%s)", human_addr, portNo, addrFam(rp->ai_family)); @@ -1626,11 +1636,11 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str if (!multicastInterfaces.length()){ if (family == AF_INET6){ - memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6 *)resmulti->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr)); + memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6 *)resmulti->ai_addr)->sin6_addr, + sizeof(mreq6.ipv6mr_multiaddr)); if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)) != 0){ FAIL_MSG("Unable to register for IPv6 multicast on all interfaces: %s", strerror(errno)); close(); - result = -1; } }else{ mreq4.imr_multiaddr = ((sockaddr_in *)resmulti->ai_addr)->sin_addr; @@ -1638,15 +1648,16 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq4, sizeof(mreq4)) != 0){ FAIL_MSG("Unable to register for IPv4 multicast on all interfaces: %s", strerror(errno)); close(); - result = -1; } } }else{ size_t nxtPos = std::string::npos; bool atLeastOne = false; - for (size_t loc = 0; loc != std::string::npos; loc = (nxtPos == std::string::npos ? nxtPos : nxtPos + 1)){ + for (size_t loc = 0; loc != std::string::npos; + loc = (nxtPos == std::string::npos ? nxtPos : nxtPos + 1)){ nxtPos = multicastInterfaces.find(',', loc); - std::string curIface = multicastInterfaces.substr(loc, (nxtPos == std::string::npos ? nxtPos : nxtPos - loc)); + std::string curIface = + multicastInterfaces.substr(loc, (nxtPos == std::string::npos ? nxtPos : nxtPos - loc)); // do a bit of filtering for IPv6, removing the []-braces, if any if (curIface[0] == '['){ if (curIface[curIface.size() - 1] == ']'){ @@ -1658,34 +1669,38 @@ uint16_t Socket::UDPConnection::bind(int port, std::string iface, const std::str if (family == AF_INET6){ INFO_MSG("Registering for IPv6 multicast on interface %s", curIface.c_str()); if ((addr_ret = getaddrinfo(curIface.c_str(), 0, &hints, &reslocal)) != 0){ - WARN_MSG("Unable to resolve IPv6 interface address %s: %s", curIface.c_str(), gai_strerror(addr_ret)); + WARN_MSG("Unable to resolve IPv6 interface address %s: %s", curIface.c_str(), + gai_strerror(addr_ret)); continue; } - memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6 *)resmulti->ai_addr)->sin6_addr, sizeof(mreq6.ipv6mr_multiaddr)); + memcpy(&mreq6.ipv6mr_multiaddr, &((sockaddr_in6 *)resmulti->ai_addr)->sin6_addr, + sizeof(mreq6.ipv6mr_multiaddr)); mreq6.ipv6mr_interface = ((sockaddr_in6 *)reslocal->ai_addr)->sin6_scope_id; if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&mreq6, sizeof(mreq6)) != 0){ - FAIL_MSG("Unable to register for IPv6 multicast on interface %s (%u): %s", curIface.c_str(), - ((sockaddr_in6 *)reslocal->ai_addr)->sin6_scope_id, strerror(errno)); + FAIL_MSG("Unable to register for IPv6 multicast on interface %s (%u): %s", + curIface.c_str(), ((sockaddr_in6 *)reslocal->ai_addr)->sin6_scope_id, + strerror(errno)); }else{ atLeastOne = true; } }else{ INFO_MSG("Registering for IPv4 multicast on interface %s", curIface.c_str()); if ((addr_ret = getaddrinfo(curIface.c_str(), 0, &hints, &reslocal)) != 0){ - WARN_MSG("Unable to resolve IPv4 interface address %s: %s", curIface.c_str(), gai_strerror(addr_ret)); + WARN_MSG("Unable to resolve IPv4 interface address %s: %s", curIface.c_str(), + gai_strerror(addr_ret)); continue; } mreq4.imr_multiaddr = ((sockaddr_in *)resmulti->ai_addr)->sin_addr; mreq4.imr_interface = ((sockaddr_in *)reslocal->ai_addr)->sin_addr; if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq4, sizeof(mreq4)) != 0){ - FAIL_MSG("Unable to register for IPv4 multicast on interface %s: %s", curIface.c_str(), strerror(errno)); + FAIL_MSG("Unable to register for IPv4 multicast on interface %s: %s", curIface.c_str(), + strerror(errno)); }else{ atLeastOne = true; } } if (!atLeastOne){ close(); - result = -1; } freeaddrinfo(reslocal); // free resolved interface addr }// loop over all interfaces @@ -1728,10 +1743,9 @@ bool Socket::UDPConnection::Receive(){ down += r; data_len = r; return true; - }else{ - data_len = 0; - return false; } + data_len = 0; + return false; } int Socket::UDPConnection::getSock(){ diff --git a/lib/stream.cpp b/lib/stream.cpp index c898db8c..8c7ce702 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -1,44 +1,39 @@ /// \file stream.cpp /// Utilities for handling streams. +#include "stream.h" +#include "config.h" +#include "defines.h" +#include "dtsc.h" +#include "json.h" +#include "procs.h" +#include "shared_memory.h" +#include "socket.h" +#include +#include #include #include #include -#include -#include -#include "json.h" -#include "stream.h" -#include "procs.h" -#include "config.h" -#include "socket.h" -#include "defines.h" -#include "shared_memory.h" -#include "dtsc.h" -std::string Util::getTmpFolder() { +std::string Util::getTmpFolder(){ std::string dir; - char * tmp_char = 0; - if (!tmp_char) { - tmp_char = getenv("TMP"); - } - if (!tmp_char) { - tmp_char = getenv("TEMP"); - } - if (!tmp_char) { - tmp_char = getenv("TMPDIR"); - } - if (tmp_char) { + char *tmp_char = 0; + if (!tmp_char){tmp_char = getenv("TMP");} + if (!tmp_char){tmp_char = getenv("TEMP");} + if (!tmp_char){tmp_char = getenv("TMPDIR");} + if (tmp_char){ dir = tmp_char; dir += "/mist"; - } else { + }else{ #if defined(_WIN32) || defined(_CYGWIN_) dir = "C:/tmp/mist"; #else dir = "/tmp/mist"; #endif } - if (access(dir.c_str(), 0) != 0) { - mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); //attempt to create mist folder - ignore failures + if (access(dir.c_str(), 0) != 0){ + mkdir(dir.c_str(), + S_IRWXU | S_IRWXG | S_IRWXO); // attempt to create mist folder - ignore failures } return dir + "/"; } @@ -47,36 +42,37 @@ std::string Util::getTmpFolder() { /// letters to lowercase. If a '?' character is found, everything following /// that character is deleted. The original string is modified. If a '+' or space /// exists, then only the part before that is sanitized. -void Util::sanitizeName(std::string & streamname) { - //strip anything that isn't numbers, digits or underscores +void Util::sanitizeName(std::string &streamname){ + // strip anything that isn't numbers, digits or underscores size_t index = streamname.find_first_of("+ "); - if(index != std::string::npos){ - std::string preplus = streamname.substr(0,index); + if (index != std::string::npos){ + std::string preplus = streamname.substr(0, index); sanitizeName(preplus); - std::string postplus = streamname.substr(index+1); + std::string postplus = streamname.substr(index + 1); if (postplus.find('?') != std::string::npos){ postplus = postplus.substr(0, (postplus.find('?'))); } - streamname = preplus+"+"+postplus; + streamname = preplus + "+" + postplus; return; } - for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i) { - if (*i == '?') { + for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i){ + if (*i == '?'){ streamname.erase(i, streamname.end()); break; } - if ( !isalpha( *i) && !isdigit( *i) && *i != '_' && *i != '.'){ + if (!isalpha(*i) && !isdigit(*i) && *i != '_' && *i != '.'){ streamname.erase(i); - } else { + }else{ *i = tolower(*i); } } } -JSON::Value Util::getStreamConfig(const std::string & streamname){ +JSON::Value Util::getStreamConfig(const std::string &streamname){ JSON::Value result; if (streamname.size() > 100){ - FAIL_MSG("Stream opening denied: %s is longer than 100 characters (%lu).", streamname.c_str(), streamname.size()); + FAIL_MSG("Stream opening denied: %s is longer than 100 characters (%lu).", streamname.c_str(), + streamname.size()); return result; } std::string smp = streamname.substr(0, streamname.find_first_of("+ ")); @@ -92,7 +88,7 @@ JSON::Value Util::getStreamConfig(const std::string & streamname){ return stream_cfg.asJSON(); } -DTSC::Meta Util::getStreamMeta(const std::string & streamname){ +DTSC::Meta Util::getStreamMeta(const std::string &streamname){ DTSC::Meta ret; char pageId[NAME_BUFFER_SIZE]; snprintf(pageId, NAME_BUFFER_SIZE, SHM_STREAM_INDEX, streamname.c_str()); @@ -108,12 +104,12 @@ DTSC::Meta Util::getStreamMeta(const std::string & streamname){ /// Checks if the given streamname has an active input serving it. Returns true if this is the case. /// Assumes the streamname has already been through sanitizeName()! -bool Util::streamAlive(std::string & streamname){ +bool Util::streamAlive(std::string &streamname){ char semName[NAME_BUFFER_SIZE]; snprintf(semName, NAME_BUFFER_SIZE, SEM_INPUT, streamname.c_str()); IPC::semaphore playerLock(semName, O_RDWR, ACCESSPERMS, 1, true); if (!playerLock){return false;} - if (!playerLock.tryWait()) { + if (!playerLock.tryWait()){ playerLock.close(); return true; }else{ @@ -126,120 +122,118 @@ bool Util::streamAlive(std::string & streamname){ /// Assures the input for the given stream name is active. /// Does stream name sanitation first, followed by a stream name length check (<= 100 chars). /// Then, checks if an input is already active by running streamAlive(). If yes, return true. -/// If no, loads up the server configuration and attempts to start the given stream according to current configuration. -/// At this point, fails and aborts if MistController isn't running. -bool Util::startInput(std::string streamname, std::string filename, bool forkFirst, bool isProvider, const std::map & overrides, pid_t * spawn_pid ) { +/// If no, loads up the server configuration and attempts to start the given stream according to +/// current configuration. At this point, fails and aborts if MistController isn't running. +bool Util::startInput(std::string streamname, std::string filename, bool forkFirst, bool isProvider, + const std::map &overrides, pid_t *spawn_pid){ sanitizeName(streamname); if (streamname.size() > 100){ - FAIL_MSG("Stream opening denied: %s is longer than 100 characters (%lu).", streamname.c_str(), streamname.size()); + FAIL_MSG("Stream opening denied: %s is longer than 100 characters (%lu).", streamname.c_str(), + streamname.size()); return false; } - //Check if the stream is already active. - //If yes, don't activate again to prevent duplicate inputs. - //It's still possible a duplicate starts anyway, this is caught in the inputs initializer. - //Note: this uses the _whole_ stream name, including + (if any). - //This means "test+a" and "test+b" have separate locks and do not interact with each other. + // Check if the stream is already active. + // If yes, don't activate again to prevent duplicate inputs. + // It's still possible a duplicate starts anyway, this is caught in the inputs initializer. + // Note: this uses the _whole_ stream name, including + (if any). + // This means "test+a" and "test+b" have separate locks and do not interact with each other. uint8_t streamStat = getStreamStatus(streamname); - //Wait for a maximum of 240 x 250ms sleeps = 60 seconds + // Wait for a maximum of 240 x 250ms sleeps = 60 seconds size_t sleeps = 0; - while (++sleeps < 240 && streamStat != STRMSTAT_OFF && streamStat != STRMSTAT_READY && (!isProvider || streamStat != STRMSTAT_WAIT)){ - if (streamStat == STRMSTAT_BOOT && overrides.count("throughboot")){ - break; - } + while (++sleeps < 240 && streamStat != STRMSTAT_OFF && streamStat != STRMSTAT_READY && + (!isProvider || streamStat != STRMSTAT_WAIT)){ + if (streamStat == STRMSTAT_BOOT && overrides.count("throughboot")){break;} Util::sleep(250); streamStat = getStreamStatus(streamname); } if (streamAlive(streamname) && !overrides.count("alwaysStart")){ - DEBUG_MSG(DLVL_MEDIUM, "Stream %s already active; continuing", streamname.c_str()); + MEDIUM_MSG("Stream %s already active; continuing", streamname.c_str()); return true; } - //Find stream base name + // Find stream base name std::string smp = streamname.substr(0, streamname.find_first_of("+ ")); - //check if base name (everything before + or space) exists + // check if base name (everything before + or space) exists const JSON::Value stream_cfg = getStreamConfig(streamname); if (!stream_cfg){ - DEBUG_MSG(DLVL_HIGH, "Stream %s not configured - attempting to ignore", streamname.c_str()); + HIGH_MSG("Stream %s not configured - attempting to ignore", streamname.c_str()); } - - //Only use configured source if not manually overridden. Abort if no config is available. + + // Only use configured source if not manually overridden. Abort if no config is available. if (!filename.size()){ if (!stream_cfg){ - DEBUG_MSG(DLVL_MEDIUM, "Stream %s not configured, no source manually given, cannot start", streamname.c_str()); + MEDIUM_MSG("Stream %s not configured, no source manually given, cannot start", streamname.c_str()); return false; } filename = stream_cfg["source"].asStringRef(); } - + const JSON::Value input = getInputBySource(filename, isProvider); if (!input){return false;} - //copy the necessary arguments to separate storage so we can unlock the config semaphore safely + // copy the necessary arguments to separate storage so we can unlock the config semaphore safely std::map str_args; - //check required parameters + // check required parameters if (input.isMember("required")){ jsonForEachConst(input["required"], prm){ const std::string opt = (*prm)["option"].asStringRef(); - //check for overrides + // check for overrides if (overrides.count(opt)){ str_args[opt] = overrides.at(opt); }else{ if (!stream_cfg.isMember(prm.key())){ - FAIL_MSG("Required parameter %s for stream %s missing", prm.key().c_str(), streamname.c_str()); + FAIL_MSG("Required parameter %s for stream %s missing", prm.key().c_str(), + streamname.c_str()); return false; } str_args[opt] = stream_cfg[opt].asStringRef(); } } } - //check optional parameters + // check optional parameters if (input.isMember("optional")){ jsonForEachConst(input["optional"], prm){ const std::string opt = (*prm)["option"].asStringRef(); - //check for overrides + // check for overrides if (overrides.count(opt)){ str_args[opt] = overrides.at(opt); }else{ - if (stream_cfg.isMember(prm.key())){ - str_args[opt] = stream_cfg[prm.key()].asStringRef(); - } - } - if (!prm->isMember("type") && str_args.count(opt)){ - str_args[opt] = ""; + if (stream_cfg.isMember(prm.key())){str_args[opt] = stream_cfg[prm.key()].asStringRef();} } + if (!prm->isMember("type") && str_args.count(opt)){str_args[opt] = "";} } } - + if (isProvider){ - //Set environment variable so we can know if we have a provider when re-exec'ing. + // Set environment variable so we can know if we have a provider when re-exec'ing. setenv("MISTPROVIDER", "1", 1); } std::string player_bin = Util::getMyPath() + "MistIn" + input["name"].asStringRef(); INFO_MSG("Starting %s -s %s %s", player_bin.c_str(), streamname.c_str(), filename.c_str()); - char * argv[30] = {(char *)player_bin.c_str(), (char *)"-s", (char *)streamname.c_str(), (char *)filename.c_str()}; + char *argv[30] ={(char *)player_bin.c_str(), (char *)"-s", (char *)streamname.c_str(), + (char *)filename.c_str()}; int argNum = 3; std::string debugLvl; if (Util::Config::printDebugLevel != DEBUG && !str_args.count("--debug")){ - debugLvl = JSON::Value((long long)Util::Config::printDebugLevel).asString(); + debugLvl = JSON::Value(Util::Config::printDebugLevel).asString(); argv[++argNum] = (char *)"--debug"; argv[++argNum] = (char *)debugLvl.c_str(); } - for (std::map::iterator it = str_args.begin(); it != str_args.end(); ++it){ + for (std::map::iterator it = str_args.begin(); it != str_args.end(); + ++it){ argv[++argNum] = (char *)it->first.c_str(); - if (it->second.size()){ - argv[++argNum] = (char *)it->second.c_str(); - } + if (it->second.size()){argv[++argNum] = (char *)it->second.c_str();} } argv[++argNum] = (char *)0; Util::Procs::setHandler(); - + int pid = 0; if (forkFirst){ - DEBUG_MSG(DLVL_DONTEVEN, "Forking"); + DONTEVEN_MSG("Forking"); pid = fork(); - if (pid == -1) { + if (pid == -1){ FAIL_MSG("Forking process for stream %s failed: %s", streamname.c_str(), strerror(errno)); return false; } @@ -248,15 +242,16 @@ bool Util::startInput(std::string streamname, std::string filename, bool forkFir Util::Procs::remember(pid); } }else{ - DEBUG_MSG(DLVL_DONTEVEN, "Not forking"); + DONTEVEN_MSG("Not forking"); } - + if (pid == 0){ Socket::Connection io(0, 1); io.drop(); - DEBUG_MSG(DLVL_DONTEVEN, "execvp"); + DONTEVEN_MSG("execvp"); execvp(argv[0], argv); - FAIL_MSG("Starting process %s for stream %s failed: %s", argv[0], streamname.c_str(), strerror(errno)); + FAIL_MSG("Starting process %s for stream %s failed: %s", argv[0], streamname.c_str(), + strerror(errno)); _exit(42); }else if (spawn_pid != NULL){ *spawn_pid = pid; @@ -277,16 +272,16 @@ bool Util::startInput(std::string streamname, std::string filename, bool forkFir JSON::Value Util::getInputBySource(const std::string &filename, bool isProvider){ JSON::Value ret; - //Attempt to load up configuration and find this stream + // Attempt to load up configuration and find this stream Util::DTSCShmReader rCapa(SHM_CAPA); DTSC::Scan inputs = rCapa.getMember("inputs"); - //Abort if not available + // Abort if not available if (!inputs){ FAIL_MSG("Capabilities not available, aborting! Is MistController running?"); return false; } - //check in curConf for -priority/source_match + // check in curConf for -priority/source_match bool selected = false; long long int curPrio = -1; DTSC::Scan input; @@ -294,17 +289,19 @@ JSON::Value Util::getInputBySource(const std::string &filename, bool isProvider) bool noProviderNoPick = false; for (unsigned int i = 0; i < input_size; ++i){ DTSC::Scan tmp_input = inputs.getIndice(i); - - //if match voor current stream && priority is hoger dan wat we al hebben + + // if match voor current stream && priority is hoger dan wat we al hebben if (tmp_input.getMember("source_match") && curPrio < tmp_input.getMember("priority").asInt()){ if (tmp_input.getMember("source_match").getSize()){ - for(unsigned int j = 0; j < tmp_input.getMember("source_match").getSize(); ++j){ + for (unsigned int j = 0; j < tmp_input.getMember("source_match").getSize(); ++j){ std::string source = tmp_input.getMember("source_match").getIndice(j).asString(); - std::string front = source.substr(0,source.find('*')); - std::string back = source.substr(source.find('*')+1); - MEDIUM_MSG("Checking input %s: %s (%s)", inputs.getIndiceName(i).c_str(), tmp_input.getMember("name").asString().c_str(), source.c_str()); - - if (filename.substr(0,front.size()) == front && filename.substr(filename.size()-back.size()) == back){ + std::string front = source.substr(0, source.find('*')); + std::string back = source.substr(source.find('*') + 1); + MEDIUM_MSG("Checking input %s: %s (%s)", inputs.getIndiceName(i).c_str(), + tmp_input.getMember("name").asString().c_str(), source.c_str()); + + if (filename.substr(0, front.size()) == front && + filename.substr(filename.size() - back.size()) == back){ if (tmp_input.getMember("non-provider") && !isProvider){ noProviderNoPick = true; continue; @@ -316,11 +313,13 @@ JSON::Value Util::getInputBySource(const std::string &filename, bool isProvider) } }else{ std::string source = tmp_input.getMember("source_match").asString(); - std::string front = source.substr(0,source.find('*')); - std::string back = source.substr(source.find('*')+1); - MEDIUM_MSG("Checking input %s: %s (%s)", inputs.getIndiceName(i).c_str(), tmp_input.getMember("name").asString().c_str(), source.c_str()); - - if (filename.substr(0,front.size()) == front && filename.substr(filename.size()-back.size()) == back){ + std::string front = source.substr(0, source.find('*')); + std::string back = source.substr(source.find('*') + 1); + MEDIUM_MSG("Checking input %s: %s (%s)", inputs.getIndiceName(i).c_str(), + tmp_input.getMember("name").asString().c_str(), source.c_str()); + + if (filename.substr(0, front.size()) == front && + filename.substr(filename.size() - back.size()) == back){ if (tmp_input.getMember("non-provider") && !isProvider){ noProviderNoPick = true; continue; @@ -330,7 +329,6 @@ JSON::Value Util::getInputBySource(const std::string &filename, bool isProvider) input = tmp_input; } } - } } if (!selected){ @@ -345,7 +343,7 @@ JSON::Value Util::getInputBySource(const std::string &filename, bool isProvider) return ret; } -uint8_t Util::getStreamStatus(const std::string & streamname){ +uint8_t Util::getStreamStatus(const std::string &streamname){ char pageName[NAME_BUFFER_SIZE]; snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_STATE, streamname.c_str()); IPC::sharedPage streamStatus(pageName, 1, false, false); diff --git a/lib/theora.cpp b/lib/theora.cpp index f807cb32..5a41679f 100644 --- a/lib/theora.cpp +++ b/lib/theora.cpp @@ -61,8 +61,8 @@ namespace theora { if (length < 7){ return false; } - if (! newData[0] & 0x80){ - DEBUG_MSG(DLVL_FAIL, "newdata != 0x80: %.2X", newData[0]); + if (!(newData[0] & 0x80)){ + FAIL_MSG("newdata != 0x80: %.2X", newData[0]); return false; } if (memcmp(newData + 1, "theora", 6) != 0){ diff --git a/lib/timing.cpp b/lib/timing.cpp index 78ff8824..c2ebd4a3 100644 --- a/lib/timing.cpp +++ b/lib/timing.cpp @@ -29,15 +29,15 @@ void clock_gettime(int ign, struct timespec * ts) { /// Will not sleep for longer than 10 minutes (600000ms). /// If interrupted by signal, resumes sleep until at least ms milliseconds have passed. /// Can be slightly off (in positive direction only) depending on OS accuracy. -void Util::wait(int ms){ +void Util::wait(int64_t ms){ if (ms < 0) { return; } if (ms > 600000) { ms = 600000; } - long long int start = getMS(); - long long int now = start; + uint64_t start = getMS(); + uint64_t now = start; while (now < start+ms){ sleep(start+ms-now); now = getMS(); @@ -49,7 +49,7 @@ void Util::wait(int ms){ /// Will not sleep for longer than 100 seconds (100000ms). /// Can be interrupted early by a signal, no guarantee of minimum sleep time. /// Can be slightly off depending on OS accuracy. -void Util::sleep(int ms) { +void Util::sleep(int64_t ms) { if (ms < 0) { return; } @@ -62,20 +62,20 @@ void Util::sleep(int ms) { nanosleep(&T, 0); } -long long Util::getNTP() { +uint64_t Util::getNTP() { struct timespec t; clock_gettime(CLOCK_REALTIME, &t); - return ((((long long int)t.tv_sec) + 2208988800ll) << 32) + (t.tv_nsec * 4.2949); + return ((uint64_t)(t.tv_sec + 2208988800ull) << 32) + (t.tv_nsec * 4.2949); } /// Gets the current time in milliseconds. -long long int Util::getMS() { +uint64_t Util::getMS() { struct timespec t; clock_gettime(CLOCK_REALTIME, &t); - return ((long long int)t.tv_sec) * 1000 + t.tv_nsec / 1000000; + return (uint64_t)t.tv_sec * 1000 + t.tv_nsec / 1000000; } -long long int Util::bootSecs() { +uint64_t Util::bootSecs() { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); return t.tv_sec; @@ -88,30 +88,30 @@ uint64_t Util::bootMS() { } /// Gets the current time in microseconds. -long long unsigned int Util::getMicros() { +uint64_t Util::getMicros() { struct timespec t; clock_gettime(CLOCK_REALTIME, &t); - return ((long long unsigned int)t.tv_sec) * 1000000 + t.tv_nsec / 1000; + return (uint64_t)t.tv_sec * 1000000 + t.tv_nsec / 1000; } /// Gets the time difference in microseconds. -long long unsigned int Util::getMicros(long long unsigned int previous) { +uint64_t Util::getMicros(uint64_t previous) { struct timespec t; clock_gettime(CLOCK_REALTIME, &t); - return ((long long unsigned int)t.tv_sec) * 1000000 + t.tv_nsec / 1000 - previous; + return (uint64_t)t.tv_sec * 1000000 + t.tv_nsec / 1000 - previous; } /// Gets the amount of seconds since 01/01/1970. -long long int Util::epoch() { +uint64_t Util::epoch() { return time(0); } -std::string Util::getUTCString(long long int epoch){ +std::string Util::getUTCString(uint64_t epoch){ if (!epoch){epoch = time(0);} time_t rawtime = epoch; struct tm * ptm; ptm = gmtime(&rawtime); char result[20]; - snprintf(result, 20, "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec); + snprintf(result, 20, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec); return std::string(result); } diff --git a/lib/timing.h b/lib/timing.h index a3f9de22..6fac1a13 100644 --- a/lib/timing.h +++ b/lib/timing.h @@ -2,18 +2,19 @@ /// Utilities for handling time and timestamps. #pragma once +#include #include #include namespace Util { - void wait(int ms); ///< Sleeps for the indicated amount of milliseconds or longer. - void sleep(int ms); ///< Sleeps for roughly the indicated amount of milliseconds. - long long int getMS(); ///< Gets the current time in milliseconds. - long long int bootSecs(); ///< Gets the current system uptime in seconds. + void wait(int64_t ms); ///< Sleeps for the indicated amount of milliseconds or longer. + void sleep(int64_t ms); ///< Sleeps for roughly the indicated amount of milliseconds. + uint64_t getMS(); ///< Gets the current time in milliseconds. + uint64_t bootSecs(); ///< Gets the current system uptime in seconds. uint64_t bootMS(); ///< Gets the current system uptime in milliseconds. - long long unsigned int getMicros();///getInt(field, recordNo); } std::string FieldAccX::string(size_t recordNo) const { - return std::string(src->getPointer(field, recordNo), field.size) ; + std::string res(src->getPointer(field, recordNo)); + if (res.size() > field.size){ + res.resize(field.size); + } + return res; } void FieldAccX::set(uint64_t val, size_t recordNo){ @@ -448,9 +452,15 @@ namespace Util{ /// Gets the size in bytes of a single record in the structure. uint32_t RelAccX::getRSize() const{return RAXHDR_RECORDSIZE;} + /// Gets the position in the records where the entries start + uint32_t RelAccX::getStartPos() const{return RAXHDR_STARTPOS;} + /// Gets the number of deleted records uint64_t RelAccX::getDeleted() const{return RAXHDR_DELETED;} + ///Gets the number of records present + size_t RelAccX::getPresent() const{return RAXHDR_PRESENT;} + /// Gets the number of the last valid index uint64_t RelAccX::getEndPos() const{return RAXHDR_ENDPOS;} @@ -497,20 +507,22 @@ namespace Util{ /// For other types, returns the maximum size possible. /// Returns 0 if the field does not exist. uint32_t RelAccX::getSize(const std::string &name, uint64_t recordNo) const{ - const RelAccXFieldData &fd = fields.at(name); + if (!isRecordAvailable(recordNo)){ return 0;} + std::map::const_iterator it = fields.find(name); + if (it == fields.end()){return 0;} + const RelAccXFieldData &fd = it->second; if ((fd.type & 0xF0) == RAX_STRING){ - if (!fields.count(name) || !isRecordAvailable(recordNo)){return 0;} return strnlen(RECORD_POINTER, fd.size); - }else{ - return fd.size; } + return fd.size; } /// Returns a pointer to the given field in the given record number. /// Returns a null pointer if the field does not exist. char *RelAccX::getPointer(const std::string &name, uint64_t recordNo) const{ - if (!fields.count(name)){return 0;} - return getPointer(fields.at(name), recordNo); + std::map::const_iterator it = fields.find(name); + if (it == fields.end()){return 0;} + return getPointer(it->second, recordNo); } char * RelAccX::getPointer(const RelAccXFieldData & fd, uint64_t recordNo) const{ @@ -520,8 +532,9 @@ namespace Util{ /// Returns the value of the given integer-type field in the given record, as an uint64_t type. /// Returns 0 if the field does not exist or is not an integer type. uint64_t RelAccX::getInt(const std::string &name, uint64_t recordNo) const{ - if (!fields.count(name)){return 0;} - return getInt(fields.at(name), recordNo); + std::map::const_iterator it = fields.find(name); + if (it == fields.end()){return 0;} + return getInt(it->second, recordNo); } uint64_t RelAccX::getInt(const RelAccXFieldData & fd, uint64_t recordNo) const{ @@ -703,9 +716,16 @@ namespace Util{ /// Sets the record counter to the given value. void RelAccX::setRCount(uint32_t count){RAXHDR_RECORDCNT = count;} + /// Sets the position in the records where the entries start + void RelAccX::setStartPos(uint32_t n){RAXHDR_STARTPOS = n;} + /// Sets the number of deleted records void RelAccX::setDeleted(uint64_t n){RAXHDR_DELETED = n;} + /// Sets the number of records present + /// Defaults to the record count if set to zero. + void RelAccX::setPresent(uint32_t n){RAXHDR_PRESENT = n;} + /// Sets the number of the last valid index void RelAccX::setEndPos(uint64_t n){RAXHDR_ENDPOS = n;} @@ -732,13 +752,17 @@ namespace Util{ /// Fails if ready is not set. /// Ensures the last byte is always a zero. void RelAccX::setString(const std::string &name, const std::string &val, uint64_t recordNo){ - if (!fields.count(name)){ + std::map::const_iterator it = fields.find(name); + if (it == fields.end()){ WARN_MSG("Setting non-existent string %s", name.c_str()); return; } - const RelAccXFieldData &fd = fields.at(name); + setString(it->second, val, recordNo); + } + + void RelAccX::setString(const RelAccXFieldData & fd, const std::string &val, uint64_t recordNo){ if ((fd.type & 0xF0) != RAX_STRING){ - WARN_MSG("Setting non-string %s", name.c_str()); + WARN_MSG("Setting non-string"); return; } char *ptr = RECORD_POINTER; @@ -749,11 +773,12 @@ namespace Util{ /// Writes the given int to the given field in the given record. /// Fails if ready is not set or the field is not an integer type. void RelAccX::setInt(const std::string &name, uint64_t val, uint64_t recordNo){ - if (!fields.count(name)){ + std::map::const_iterator it = fields.find(name); + if (it == fields.end()){ WARN_MSG("Setting non-existent integer %s", name.c_str()); return; } - return setInt(fields.at(name), val, recordNo); + setInt(it->second, val, recordNo); } void RelAccX::setInt(const RelAccXFieldData & fd, uint64_t val, uint64_t recordNo){ @@ -781,12 +806,15 @@ namespace Util{ WARN_MSG("Setting non-integer field (%u) to integer value!", fd.type); } + ///Writes the given int to the given field in the given record. + ///Fails if ready is not set or the field is not an integer type. void RelAccX::setInts(const std::string & name, uint64_t * values, size_t len){ - if (!fields.count(name)){ + std::map::const_iterator it = fields.find(name); + if (it == fields.end()){ WARN_MSG("Setting non-existent integer %s", name.c_str()); return; } - const RelAccXFieldData & fd = fields.at(name); + const RelAccXFieldData & fd = it->second; for (uint64_t recordNo = 0; recordNo < len; recordNo++){ setInt(fd, values[recordNo], recordNo); } @@ -795,17 +823,124 @@ namespace Util{ /// Updates the deleted record counter, the start position and the present record counter, /// shifting the ring buffer start position forward without moving the ring buffer end position. void RelAccX::deleteRecords(uint32_t amount){ - RAXHDR_DELETED += amount; // update deleted record counter + uint32_t &startPos = RAXHDR_STARTPOS; + uint64_t &deletedRecs = RAXHDR_DELETED; + uint32_t &recsPresent = RAXHDR_PRESENT; + startPos += amount; // update start position + deletedRecs += amount; // update deleted record counter + if (recsPresent >= amount){ + recsPresent -= amount; // decrease records present + }else{ + WARN_MSG("Depleting recordCount!"); + recsPresent = 0; + } } /// Updates the present record counter, shifting the ring buffer end position forward without /// moving the ring buffer start position. void RelAccX::addRecords(uint32_t amount){ - RAXHDR_ENDPOS += amount; + uint32_t & recsPresent = RAXHDR_PRESENT; + uint32_t & recordsCount = RAXHDR_RECORDCNT; + uint64_t & recordEndPos = RAXHDR_ENDPOS; + if (recsPresent+amount > recordsCount){ + WARN_MSG("Exceeding recordCount (%d [%d + %d] > %d)", recsPresent + amount, recsPresent, amount, recordsCount); + recsPresent = 0; + }else{ + recsPresent += amount; + } + recordEndPos += amount; + } + + void RelAccX::minimalFrom(const RelAccX & src){ + copyFieldsFrom(src, true); + + uint64_t rCount = src.getPresent(); + setRCount(rCount); + setReady(); + addRecords(rCount); + + flowFrom(src); + } + + void RelAccX::copyFieldsFrom(const RelAccX & src, bool minimal){ + fields.clear(); + if (!minimal){ + for (std::map::const_iterator it = src.fields.begin(); it != src.fields.end(); it++){ + addField(it->first, it->second.type, it->second.size); + } + return; + } + for (std::map::const_iterator it = src.fields.begin(); it != src.fields.end(); it++){ + switch(it->second.type & 0xF0){ + case 0x00: //nested RelAccX + { + uint64_t maxSize = 0; + for (int i = 0; i < src.getPresent(); i++){ + Util::RelAccX child(src.getPointer(it->first, i), false); + char * tmpBuf = (char*)malloc(src.getOffset() + (src.getRCount() * src.getRSize())); + Util::RelAccX minChild(tmpBuf, false); + minChild.minimalFrom(child); + uint64_t thisSize = minChild.getOffset() + (minChild.getRSize() * minChild.getPresent()); + maxSize = std::max(thisSize, maxSize); + free(tmpBuf); + } + addField(it->first, it->second.type, maxSize); + } + break; + default: + addField(it->first, it->second.type, it->second.size); + break; + } + } + } + + void RelAccX::flowFrom(const RelAccX & src){ + uint64_t rCount = src.getPresent(); + if (getRCount() == 0){ + setRCount(rCount); + } + if (rCount > getRCount()){ + FAIL_MSG("Abandoning reflow, target does not have enough records available (%" PRIu64 " records, %d available)", rCount, getRCount()); + return; + } + addRecords(rCount - getPresent()); + for (int i = 0; i < rCount; i++){ + for (std::map::const_iterator it = src.fields.begin(); it != src.fields.end(); it++){ + if (!fields.count(it->first)){ + INFO_MSG("Field %s in source but not in target", it->first.c_str()); + continue; + } + switch(it->second.type & 0xF0){ + case RAX_RAW: + memcpy(getPointer(it->first, i), src.getPointer(it->first, i), std::min(it->second.size, fields.at(it->first).size)); + break; + case RAX_INT: + case RAX_UINT: + setInt(it->first, src.getInt(it->first, i), i); + break; + case RAX_STRING: + setString(it->first, src.getPointer(it->first, i), i); + break; + case 0x00: //nested RelAccX + { + Util::RelAccX srcChild(src.getPointer(it->first, i), false); + Util::RelAccX child(getPointer(it->first, i), false); + child.flowFrom(srcChild); + } + break; + default: + break; + } + } + } } FieldAccX RelAccX::getFieldAccX(const std::string & fName){ - return FieldAccX(this, fields.at(fName), p + getOffset()); + return FieldAccX(this, fields.at(fName)); + } + + RelAccXFieldData RelAccX::getFieldData(const std::string & fName) const { + return fields.at(fName); } } diff --git a/lib/util.h b/lib/util.h index 9a4f1854..425896b1 100644 --- a/lib/util.h +++ b/lib/util.h @@ -26,7 +26,7 @@ namespace Util{ public: ResizeablePointer(); ~ResizeablePointer(); - inline uint32_t& size(){return currSize;} + inline size_t& size(){return currSize;} bool assign(const void * p, uint32_t l); bool append(const void * p, uint32_t l); bool allocate(uint32_t l); @@ -34,8 +34,8 @@ namespace Util{ inline operator void*(){return ptr;} private: void * ptr; - uint32_t currSize; - uint32_t maxSize; + size_t currSize; + size_t maxSize; }; @@ -117,8 +117,10 @@ namespace Util{ uint32_t getRCount() const; uint32_t getRSize() const; uint16_t getOffset() const; + uint32_t getStartPos() const; uint64_t getDeleted() const; uint64_t getEndPos() const; + size_t getPresent() const; uint32_t getFieldCount() const; bool isReady() const; bool isExit() const; @@ -138,12 +140,15 @@ namespace Util{ //Read-write functions: void addField(const std::string & name, uint8_t fType, uint32_t fLen=0); void setRCount(uint32_t count); + void setStartPos(uint32_t n); void setDeleted(uint64_t n); void setEndPos(uint64_t n); + void setPresent(uint32_t n); void setReady(); void setExit(); void setReload(); void setString(const std::string & name, const std::string & val, uint64_t recordNo=0); + void setString(const RelAccXFieldData & fd, const std::string & val, uint64_t recordNo=0); void setInt(const std::string & name, uint64_t val, uint64_t recordNo=0); void setInt(const RelAccXFieldData & fd, uint64_t val, uint64_t recordNo=0); void setInts(const std::string & name, uint64_t * values, size_t len); @@ -155,6 +160,7 @@ namespace Util{ void flowFrom(const RelAccX & src); FieldAccX getFieldAccX(const std::string & fName); + RelAccXFieldData getFieldData(const std::string & fName) const; protected: static uint32_t getDefaultSize(uint8_t fType); std::map fields; @@ -164,7 +170,7 @@ namespace Util{ class FieldAccX { public: - FieldAccX(RelAccX * _src = NULL, RelAccXFieldData _field = RelAccXFieldData(), char * _data = NULL); + FieldAccX(RelAccX * _src = NULL, RelAccXFieldData _field = RelAccXFieldData()); uint64_t uint(size_t recordNo) const; std::string string(size_t recordNo) const; void set(uint64_t val, size_t recordNo = 0); @@ -172,6 +178,5 @@ namespace Util{ private: RelAccX * src; RelAccXFieldData field; - char * data; }; } diff --git a/lib/vorbis.cpp b/lib/vorbis.cpp index 0e088c70..db45cf1f 100644 --- a/lib/vorbis.cpp +++ b/lib/vorbis.cpp @@ -198,7 +198,7 @@ namespace vorbis{ for (int i = 0; i < codebook_count; i++){ long long unsigned int CMN = stream.get(24); if (CMN != 0x564342){ - DEBUG_MSG(DLVL_WARN,"Is dit het? VCB != %c%c%c", (char)(CMN >> 16), (char)(CMN >> 8), (char)CMN); + WARN_MSG("Is dit het? VCB != %c%c%c", (char)(CMN >> 16), (char)(CMN >> 8), (char)CMN); exit(1); } unsigned short codebook_dimensions = stream.get(16); @@ -256,7 +256,7 @@ namespace vorbis{ long long unsigned int floorType = stream.get(16); switch(floorType){ case 0:{ - DEBUG_MSG(DLVL_WARN, "FloorType 0 in vorbis setup header not tested!"); + WARN_MSG("FloorType 0 in vorbis setup header not tested!"); stream.skip(8);//order stream.skip(16);//rate stream.skip(16);//bark_map_size @@ -359,7 +359,7 @@ namespace vorbis{ } char meh = stream.get(2); if (meh != 0){ - DEBUG_MSG(DLVL_ERROR, "Sanity check ==0 : %i", (int)meh); + ERROR_MSG("Sanity check ==0 : %i", (int)meh); exit(0); } if (mappingSubmaps > 1){ diff --git a/src/analysers/analyser_dtsc.cpp b/src/analysers/analyser_dtsc.cpp index 2367e536..75a33bb8 100644 --- a/src/analysers/analyser_dtsc.cpp +++ b/src/analysers/analyser_dtsc.cpp @@ -53,7 +53,7 @@ bool AnalyserDTSC::parsePacket(){ } if (detail >= 8){ char * payDat; - unsigned int payLen; + size_t payLen; P.getString("data", payDat, payLen); for (uint64_t i = 0; i < payLen; ++i){ if ((i % 32) == 0){std::cout << std::endl;} diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index aa289415..c79c5fea 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -321,7 +321,7 @@ int main_loop(int argc, char **argv){ // Check if we have a usable server, if not, print messages with helpful hints { - std::string web_port = JSON::Value((long long)Controller::conf.getInteger("port")).asString(); + std::string web_port = JSON::Value(Controller::conf.getInteger("port")).asString(); // check for username if (!Controller::Storage.isMember("account") || Controller::Storage["account"].size() < 1){ Controller::Log("CONF", @@ -406,7 +406,6 @@ int main(int argc, char **argv){ Util::Procs::setHandler(); // set child handler { struct sigaction new_action; - struct sigaction cur_action; new_action.sa_sigaction = handleUSR1; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; @@ -423,7 +422,6 @@ int main(int argc, char **argv){ Util::Procs::reaper_thread = 0; { struct sigaction new_action; - struct sigaction cur_action; new_action.sa_sigaction = handleUSR1; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; @@ -460,7 +458,7 @@ int main(int argc, char **argv){ execvp(myFile.c_str(), argv); FAIL_MSG("Error restarting: %s", strerror(errno)); } - INFO_MSG("Controller uncleanly shut down! Restarting in %llu...", reTimer); + INFO_MSG("Controller uncleanly shut down! Restarting in %" PRIu64 "...", reTimer); Util::wait(reTimer); reTimer += 1000; } diff --git a/src/controller/controller_api.cpp b/src/controller/controller_api.cpp index 0df51a57..19fe8ecf 100644 --- a/src/controller/controller_api.cpp +++ b/src/controller/controller_api.cpp @@ -182,7 +182,7 @@ void Controller::handleWebSocket(HTTP::Parser & H, Socket::Connection & C){ sent = true; JSON::Value tmp; tmp[0u] = "log"; - tmp[1u].append((long long)rlxLog.getInt("time", logPos)); + tmp[1u].append(rlxLog.getInt("time", logPos)); tmp[1u].append(rlxLog.getPointer("kind", logPos)); tmp[1u].append(rlxLog.getPointer("msg", logPos)); W.sendFrame(tmp.toString()); @@ -192,14 +192,14 @@ void Controller::handleWebSocket(HTTP::Parser & H, Socket::Connection & C){ sent = true; JSON::Value tmp; tmp[0u] = "access"; - tmp[1u].append((long long)rlxAccs.getInt("time", accsPos)); + tmp[1u].append(rlxAccs.getInt("time", accsPos)); tmp[1u].append(rlxAccs.getPointer("session", accsPos)); tmp[1u].append(rlxAccs.getPointer("stream", accsPos)); tmp[1u].append(rlxAccs.getPointer("connector", accsPos)); tmp[1u].append(rlxAccs.getPointer("host", accsPos)); - tmp[1u].append((long long)rlxAccs.getInt("duration", accsPos)); - tmp[1u].append((long long)rlxAccs.getInt("up", accsPos)); - tmp[1u].append((long long)rlxAccs.getInt("down", accsPos)); + tmp[1u].append(rlxAccs.getInt("duration", accsPos)); + tmp[1u].append(rlxAccs.getInt("up", accsPos)); + tmp[1u].append(rlxAccs.getInt("down", accsPos)); tmp[1u].append(rlxAccs.getPointer("tags", accsPos)); W.sendFrame(tmp.toString()); accsPos++; @@ -220,10 +220,10 @@ void Controller::handleWebSocket(HTTP::Parser & H, Socket::Connection & C){ JSON::Value tmp; tmp[0u] = "stream"; tmp[1u].append(strm); - tmp[1u].append((long long)tmpStat.status); - tmp[1u].append((long long)tmpStat.viewers); - tmp[1u].append((long long)tmpStat.inputs); - tmp[1u].append((long long)tmpStat.outputs); + tmp[1u].append(tmpStat.status); + tmp[1u].append(tmpStat.viewers); + tmp[1u].append(tmpStat.inputs); + tmp[1u].append(tmpStat.outputs); W.sendFrame(tmp.toString()); } } @@ -233,10 +233,10 @@ void Controller::handleWebSocket(HTTP::Parser & H, Socket::Connection & C){ JSON::Value tmp; tmp[0u] = "stream"; tmp[1u].append(strm); - tmp[1u].append((long long)0); - tmp[1u].append((long long)0); - tmp[1u].append((long long)0); - tmp[1u].append((long long)0); + tmp[1u].append(0u); + tmp[1u].append(0u); + tmp[1u].append(0u); + tmp[1u].append(0u); W.sendFrame(tmp.toString()); strmRemove.erase(strm); lastStrmStat.erase(strm); @@ -534,7 +534,7 @@ void Controller::handleAPICommands(JSON::Value & Request, JSON::Value & Response } if (!Request.isMember("minimal") || Request.isMember("streams") || Request.isMember("addstream") || Request.isMember("deletestream")){ if (!Request.isMember("streams") && (Request.isMember("addstream") || Request.isMember("deletestream"))){ - Response["streams"]["incomplete list"] = 1ll; + Response["streams"]["incomplete list"] = 1u; if (Request.isMember("addstream")){ jsonForEach(Request["addstream"], jit){ if (Controller::Storage["streams"].isMember(jit.key())){ diff --git a/src/controller/controller_capabilities.cpp b/src/controller/controller_capabilities.cpp index bf3d3caf..215db106 100644 --- a/src/controller/controller_capabilities.cpp +++ b/src/controller/controller_capabilities.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "controller_capabilities.h" @@ -233,23 +234,23 @@ namespace Controller { } continue; } - long long int i; - if (sscanf(line, "MemTotal : %lli kB", &i) == 1){ + uint64_t i; + if (sscanf(line, "MemTotal : %" PRIu64 " kB", &i) == 1){ capa["mem"]["total"] = i / 1024; } - if (sscanf(line, "MemFree : %lli kB", &i) == 1){ + if (sscanf(line, "MemFree : %" PRIu64 " kB", &i) == 1){ capa["mem"]["free"] = i / 1024; } - if (sscanf(line, "SwapTotal : %lli kB", &i) == 1){ + if (sscanf(line, "SwapTotal : %" PRIu64 " kB", &i) == 1){ capa["mem"]["swaptotal"] = i / 1024; } - if (sscanf(line, "SwapFree : %lli kB", &i) == 1){ + if (sscanf(line, "SwapFree : %" PRIu64 " kB", &i) == 1){ capa["mem"]["swapfree"] = i / 1024; } - if (sscanf(line, "Buffers : %lli kB", &i) == 1){ + if (sscanf(line, "Buffers : %" PRIu64 " kB", &i) == 1){ bufcache += i / 1024; } - if (sscanf(line, "Cached : %lli kB", &i) == 1){ + if (sscanf(line, "Cached : %" PRIu64 " kB", &i) == 1){ bufcache += i / 1024; } } @@ -265,23 +266,23 @@ namespace Controller { //parse lines here float onemin, fivemin, fifteenmin; if (sscanf(line, "%f %f %f", &onemin, &fivemin, &fifteenmin) == 3){ - capa["load"]["one"] = (long long int)(onemin * 100); - capa["load"]["five"] = (long long int)(fivemin * 100); - capa["load"]["fifteen"] = (long long int)(fifteenmin * 100); + capa["load"]["one"] = uint64_t(onemin * 100); + capa["load"]["five"] = uint64_t(fivemin * 100); + capa["load"]["fifteen"] = uint64_t(fifteenmin * 100); } } std::ifstream cpustat("/proc/stat"); if (cpustat){ char line[300]; while (cpustat.getline(line, 300)){ - static unsigned long long cl_total = 0, cl_idle = 0; - unsigned long long c_user, c_nice, c_syst, c_idle, c_total; - if (sscanf(line, "cpu %Lu %Lu %Lu %Lu", &c_user, &c_nice, &c_syst, &c_idle) == 4){ + static uint64_t cl_total = 0, cl_idle = 0; + uint64_t c_user, c_nice, c_syst, c_idle, c_total; + if (sscanf(line, "cpu %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, &c_user, &c_nice, &c_syst, &c_idle) == 4){ c_total = c_user + c_nice + c_syst + c_idle; if (c_total - cl_total > 0){ - capa["cpu_use"] = (long long int)(1000 - ((c_idle - cl_idle) * 1000) / (c_total - cl_total)); + capa["cpu_use"] = (1000 - ((c_idle - cl_idle) * 1000) / (c_total - cl_total)); }else{ - capa["cpu_use"] = 0ll; + capa["cpu_use"] = 0u; } cl_total = c_total; cl_idle = c_idle; diff --git a/src/controller/controller_connectors.cpp b/src/controller/controller_connectors.cpp index 5065c678..cdd7edab 100644 --- a/src/controller/controller_connectors.cpp +++ b/src/controller/controller_connectors.cpp @@ -63,7 +63,10 @@ namespace Controller { /// Deletes the shared memory page with connector information /// in preparation of shutdown. void prepareActiveConnectorsForShutdown(){ - IPC::sharedPage f("MstCnns", 4096, true, false); + IPC::sharedPage f("MstCnns", 4096, false, false); + if (f){ + f.master = true; + } } /// Forgets all active connectors, preventing them from being killed, @@ -103,7 +106,7 @@ namespace Controller { }else{ if (it.key() == "debug"){ static std::string debugLvlStr; - debugLvlStr = JSON::Value((long long)Util::Config::printDebugLevel).asString(); + debugLvlStr = JSON::Value(Util::Config::printDebugLevel).asString(); argarr[argnum++] = (char*)((*it)["option"].asStringRef().c_str()); argarr[argnum++] = (char*)debugLvlStr.c_str(); } diff --git a/src/controller/controller_statistics.cpp b/src/controller/controller_statistics.cpp index f79ff417..2f59a932 100644 --- a/src/controller/controller_statistics.cpp +++ b/src/controller/controller_statistics.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "controller_statistics.h" #include "controller_storage.h" @@ -36,14 +37,14 @@ tthread::mutex Controller::statsMutex; //For server-wide totals. Local to this file only. struct streamTotals { - unsigned long long upBytes; - unsigned long long downBytes; - unsigned long long inputs; - unsigned long long outputs; - unsigned long long viewers; - unsigned long long currIns; - unsigned long long currOuts; - unsigned long long currViews; + uint64_t upBytes; + uint64_t downBytes; + uint64_t inputs; + uint64_t outputs; + uint64_t viewers; + uint64_t currIns; + uint64_t currOuts; + uint64_t currViews; uint8_t status; }; static std::map streamStats; @@ -107,7 +108,7 @@ IPC::sharedServer * statPointer = 0; /// statistics from all connected clients, as well as wipes /// old statistics that have disconnected over 10 minutes ago. void Controller::SharedMemStats(void * config){ - DEBUG_MSG(DLVL_HIGH, "Starting stats thread"); + HIGH_MSG("Starting stats thread"); IPC::sharedServer statServer(SHM_STATISTICS, STAT_EX_SIZE, true); statPointer = &statServer; std::set inactiveStreams; @@ -288,7 +289,7 @@ Controller::sessType Controller::statSession::getSessType(){ } /// Archives the given connection. -void Controller::statSession::wipeOld(unsigned long long cutOff){ +void Controller::statSession::wipeOld(uint64_t cutOff){ if (firstSec > cutOff){ return; } @@ -326,7 +327,7 @@ void Controller::statSession::wipeOld(unsigned long long cutOff){ } } -void Controller::statSession::ping(const Controller::sessIndex & index, unsigned long long disconnectPoint){ +void Controller::statSession::ping(const Controller::sessIndex & index, uint64_t disconnectPoint){ if (!tracked){return;} if (lastSec < disconnectPoint){ switch (sessionType){ @@ -339,6 +340,8 @@ void Controller::statSession::ping(const Controller::sessIndex & index, unsigned case SESS_VIEWER: if (streamStats[index.streamName].currViews){streamStats[index.streamName].currViews--;} break; + default: + break; } uint64_t duration = lastSec - firstActive; if (duration < 1){duration = 1;} @@ -418,17 +421,17 @@ void Controller::statSession::switchOverTo(statSession & newSess, unsigned long } /// Returns the first measured timestamp in this session. -unsigned long long Controller::statSession::getStart(){ +uint64_t Controller::statSession::getStart(){ return firstSec; } /// Returns the last measured timestamp in this session. -unsigned long long Controller::statSession::getEnd(){ +uint64_t Controller::statSession::getEnd(){ return lastSec; } /// Returns true if there is data for this session at timestamp t. -bool Controller::statSession::hasDataFor(unsigned long long t){ +bool Controller::statSession::hasDataFor(uint64_t t){ if (lastSec < t){return false;} if (firstSec > t){return false;} if (oldConns.size()){ @@ -437,7 +440,7 @@ bool Controller::statSession::hasDataFor(unsigned long long t){ } } if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.hasDataFor(t)){return true;} } } @@ -453,7 +456,7 @@ bool Controller::statSession::hasData(){ } } if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.log.size()){return true;} } } @@ -461,7 +464,7 @@ bool Controller::statSession::hasData(){ } /// Returns true if this session should count as a viewer on the given timestamp. -bool Controller::statSession::isViewerOn(unsigned long long t){ +bool Controller::statSession::isViewerOn(uint64_t t){ return getUp(t) + getDown(t) > COUNTABLE_BYTES; } @@ -488,8 +491,8 @@ bool Controller::statSession::isViewer(){ } /// Returns the cumulative connected time for this session at timestamp t. -long long Controller::statSession::getConnTime(unsigned long long t){ - long long retVal = 0; +uint64_t Controller::statSession::getConnTime(uint64_t t){ + uint64_t retVal = 0; if (oldConns.size()){ for (std::deque::iterator it = oldConns.begin(); it != oldConns.end(); ++it){ if (it->hasDataFor(t)){ @@ -498,7 +501,7 @@ long long Controller::statSession::getConnTime(unsigned long long t){ } } if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.hasDataFor(t)){ retVal += it->second.getDataFor(t).time; } @@ -508,9 +511,9 @@ long long Controller::statSession::getConnTime(unsigned long long t){ } /// Returns the last requested media timestamp for this session at timestamp t. -long long Controller::statSession::getLastSecond(unsigned long long t){ +uint64_t Controller::statSession::getLastSecond(uint64_t t){ if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.hasDataFor(t)){ return it->second.getDataFor(t).lastSecond; } @@ -527,8 +530,8 @@ long long Controller::statSession::getLastSecond(unsigned long long t){ } /// Returns the cumulative downloaded bytes for this session at timestamp t. -long long Controller::statSession::getDown(unsigned long long t){ - long long retVal = wipedDown; +uint64_t Controller::statSession::getDown(uint64_t t){ + uint64_t retVal = wipedDown; if (oldConns.size()){ for (std::deque::iterator it = oldConns.begin(); it != oldConns.end(); ++it){ if (it->hasDataFor(t)){ @@ -537,7 +540,7 @@ long long Controller::statSession::getDown(unsigned long long t){ } } if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.hasDataFor(t)){ retVal += it->second.getDataFor(t).down; } @@ -547,8 +550,8 @@ long long Controller::statSession::getDown(unsigned long long t){ } /// Returns the cumulative uploaded bytes for this session at timestamp t. -long long Controller::statSession::getUp(unsigned long long t){ - long long retVal = wipedUp; +uint64_t Controller::statSession::getUp(uint64_t t){ + uint64_t retVal = wipedUp; if (oldConns.size()){ for (std::deque::iterator it = oldConns.begin(); it != oldConns.end(); ++it){ if (it->hasDataFor(t)){ @@ -557,7 +560,7 @@ long long Controller::statSession::getUp(unsigned long long t){ } } if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.hasDataFor(t)){ retVal += it->second.getDataFor(t).up; } @@ -567,8 +570,8 @@ long long Controller::statSession::getUp(unsigned long long t){ } /// Returns the cumulative downloaded bytes for this session at timestamp t. -long long Controller::statSession::getDown(){ - long long retVal = wipedDown; +uint64_t Controller::statSession::getDown(){ + uint64_t retVal = wipedDown; if (oldConns.size()){ for (std::deque::iterator it = oldConns.begin(); it != oldConns.end(); ++it){ if (it->log.size()){ @@ -577,7 +580,7 @@ long long Controller::statSession::getDown(){ } } if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.log.size()){ retVal += it->second.log.rbegin()->second.down; } @@ -587,8 +590,8 @@ long long Controller::statSession::getDown(){ } /// Returns the cumulative uploaded bytes for this session at timestamp t. -long long Controller::statSession::getUp(){ - long long retVal = wipedUp; +uint64_t Controller::statSession::getUp(){ + uint64_t retVal = wipedUp; if (oldConns.size()){ for (std::deque::iterator it = oldConns.begin(); it != oldConns.end(); ++it){ if (it->log.size()){ @@ -597,7 +600,7 @@ long long Controller::statSession::getUp(){ } } if (curConns.size()){ - for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ + for (std::map::iterator it = curConns.begin(); it != curConns.end(); ++it){ if (it->second.log.size()){ retVal += it->second.log.rbegin()->second.up; } @@ -607,35 +610,31 @@ long long Controller::statSession::getUp(){ } /// Returns the cumulative downloaded bytes per second for this session at timestamp t. -long long Controller::statSession::getBpsDown(unsigned long long t){ - unsigned long long aTime = t - 5; +uint64_t Controller::statSession::getBpsDown(uint64_t t){ + uint64_t aTime = t - 5; if (aTime < firstSec){ aTime = firstSec; } - long long valA = getDown(aTime); - long long valB = getDown(t); - if (t > aTime){ - //INFO_MSG("Saying the speed from time %lli to %lli (being %lli - %lli) is %lli.", aTime, t, valB, valA, (valB - valA) / (t - aTime)); - return (valB - valA) / (t - aTime); - }else{ - //INFO_MSG("Saying the speed from time %lli to %lli (being %lli - %lli) is %lli.", aTime, t, valB, valA, 0); + if (t <= aTime){ return 0; } + uint64_t valA = getDown(aTime); + uint64_t valB = getDown(t); + return (valB - valA) / (t - aTime); } /// Returns the cumulative uploaded bytes per second for this session at timestamp t. -long long Controller::statSession::getBpsUp(unsigned long long t){ - unsigned long long aTime = t - 5; +uint64_t Controller::statSession::getBpsUp(uint64_t t){ + uint64_t aTime = t - 5; if (aTime < firstSec){ aTime = firstSec; } - long long valA = getUp(aTime); - long long valB = getUp(t); - if (t > aTime){ - return (valB - valA) / (t - aTime); - }else{ + if (t <= aTime){ return 0; } + uint64_t valA = getUp(aTime); + uint64_t valB = getUp(t); + return (valB - valA) / (t - aTime); } /// Returns true if there is data available for timestamp t. @@ -679,20 +678,25 @@ void Controller::statStorage::update(IPC::statExchange & data) { /// This function is called by the shared memory page that holds statistics. /// It updates the internally saved statistics data, moving across sessions or archiving when necessary. -void Controller::parseStatistics(char * data, size_t len, unsigned int id){ +void Controller::parseStatistics(char * data, size_t len, uint32_t id){ //retrieve stats data IPC::statExchange tmpEx(data); //calculate the current session index, store as idx. sessIndex idx(tmpEx); //if the connection was already indexed and it has changed, move it if (connToSession.count(id) && connToSession[id] != idx){ + if (sessions[connToSession[id]].getSessType() != SESS_UNSET){ + INFO_MSG("Switching connection %" PRIu32 " from active session %s over to %s", id, connToSession[id].toStr().c_str(), idx.toStr().c_str()); + }else{ + INFO_MSG("Switching connection %" PRIu32 " from inactive session %s over to %s", id, connToSession[id].toStr().c_str(), idx.toStr().c_str()); + } sessions[connToSession[id]].switchOverTo(sessions[idx], id); if (!sessions[connToSession[id]].hasData()){ sessions.erase(connToSession[id]); } } if (!connToSession.count(id)){ - INSANE_MSG("New connection: %lu as %s", id, idx.toStr().c_str()); + INSANE_MSG("New connection: %" PRIu32 " as %s", id, idx.toStr().c_str()); } //store the index for later comparison connToSession[id] = idx; @@ -702,7 +706,7 @@ void Controller::parseStatistics(char * data, size_t len, unsigned int id){ char counter = (*(data - 1)) & 0x7F; if (counter == 126 || counter == 127){ //the data is no longer valid - connection has gone away, store for later - INSANE_MSG("Ended connection: %lu as %s", id, idx.toStr().c_str()); + INSANE_MSG("Ended connection: %" PRIu32 " as %s", id, idx.toStr().c_str()); sessions[idx].finish(id); connToSession.erase(id); }else{ @@ -763,7 +767,7 @@ bool Controller::hasViewers(std::string streamName){ void Controller::fillClients(JSON::Value & req, JSON::Value & rep){ tthread::lock_guard guard(statsMutex); //first, figure out the timestamp wanted - long long int reqTime = 0; + uint64_t reqTime = 0; if (req.isMember("time")){ reqTime = req["time"].asInt(); } @@ -839,7 +843,7 @@ void Controller::fillClients(JSON::Value & req, JSON::Value & rep){ if (fields & STAT_CLI_UP){d.append(it->second.getUp(time));} if (fields & STAT_CLI_BPS_DOWN){d.append(it->second.getBpsDown(time));} if (fields & STAT_CLI_BPS_UP){d.append(it->second.getBpsUp(time));} - if (fields & STAT_CLI_CRC){d.append((long long)it->first.crc);} + if (fields & STAT_CLI_CRC){d.append(it->first.crc);} rep["data"].append(d); } } @@ -917,7 +921,7 @@ void Controller::fillActive(JSON::Value & req, JSON::Value & rep, bool onlyNow){ rep[*it].null(); jsonForEach(req, j){ if (j->asStringRef() == "clients"){ - rep[*it].append((long long)clients[*it]); + rep[*it].append(clients[*it]); } if (j->asStringRef() == "lastms"){ char pageId[NAME_BUFFER_SIZE]; @@ -941,7 +945,7 @@ void Controller::fillActive(JSON::Value & req, JSON::Value & rep, bool onlyNow){ rep[*it].append(lms); metaLocker.post(); }else{ - rep[*it].append(-1ll); + rep[*it].append(-1); } } } @@ -961,20 +965,21 @@ class totalsData { downbps = 0; upbps = 0; } - void add(unsigned int down, unsigned int up, Controller::sessType sT){ + void add(uint64_t down, uint64_t up, Controller::sessType sT){ switch (sT){ case Controller::SESS_VIEWER: clients++; break; case Controller::SESS_INPUT: inputs++; break; case Controller::SESS_OUTPUT: outputs++; break; + default: break; } downbps += down; upbps += up; } - long long clients; - long long inputs; - long long outputs; - long long downbps; - long long upbps; + uint64_t clients; + uint64_t inputs; + uint64_t outputs; + uint64_t downbps; + uint64_t upbps; }; /// This takes a "totals" request, and fills in the response data. @@ -1036,7 +1041,7 @@ void Controller::fillTotals(JSON::Value & req, JSON::Value & rep){ if (fields & STAT_TOT_BPS_DOWN){rep["fields"].append("downbps");} if (fields & STAT_TOT_BPS_UP){rep["fields"].append("upbps");} //start data collection - std::map totalsCount; + std::map totalsCount; //loop over all sessions /// \todo Make the interval configurable instead of 1 second if (sessions.size()){ @@ -1061,13 +1066,13 @@ void Controller::fillTotals(JSON::Value & req, JSON::Value & rep){ return; } //yay! We have data! - rep["start"] = (long long)totalsCount.begin()->first; - rep["end"] = (long long)totalsCount.rbegin()->first; + rep["start"] = totalsCount.begin()->first; + rep["end"] = totalsCount.rbegin()->first; rep["data"].null(); rep["interval"].null(); - long long prevT = 0; + uint64_t prevT = 0; JSON::Value i; - for (std::map::iterator it = totalsCount.begin(); it != totalsCount.end(); it++){ + for (std::map::iterator it = totalsCount.begin(); it != totalsCount.end(); it++){ JSON::Value d; if (fields & STAT_TOT_CLIENTS){d.append(it->second.clients);} if (fields & STAT_TOT_INPUTS){d.append(it->second.inputs);} @@ -1077,13 +1082,13 @@ void Controller::fillTotals(JSON::Value & req, JSON::Value & rep){ rep["data"].append(d); if (prevT){ if (i.size() < 2){ - i.append(1ll); - i.append((long long)(it->first - prevT)); + i.append(1u); + i.append(it->first - prevT); }else{ - if (i[1u].asInt() != (long long)(it->first - prevT)){ + if (i[1u].asInt() != it->first - prevT){ rep["interval"].append(i); - i[0u] = 1ll; - i[1u] = (long long)(it->first - prevT); + i[0u] = 1u; + i[1u] = it->first - prevT; }else{ i[0u] = i[0u].asInt() + 1; } diff --git a/src/controller/controller_statistics.h b/src/controller/controller_statistics.h index 31c6e4f0..46294f02 100644 --- a/src/controller/controller_statistics.h +++ b/src/controller/controller_statistics.h @@ -13,10 +13,10 @@ namespace Controller { struct statLog { - long time; - long lastSecond; - long long down; - long long up; + uint64_t time; + uint64_t lastSecond; + uint64_t down; + uint64_t up; }; enum sessType { @@ -61,38 +61,38 @@ namespace Controller { class statSession { private: uint64_t firstActive; - unsigned long long firstSec; - unsigned long long lastSec; - unsigned long long wipedUp; - unsigned long long wipedDown; + uint64_t firstSec; + uint64_t lastSec; + uint64_t wipedUp; + uint64_t wipedDown; std::deque oldConns; sessType sessionType; bool tracked; public: statSession(); - std::map curConns; + std::map curConns; sessType getSessType(); - void wipeOld(unsigned long long); - void finish(unsigned long index); - void switchOverTo(statSession & newSess, unsigned long index); - void update(unsigned long index, IPC::statExchange & data); - void ping(const sessIndex & index, unsigned long long disconnectPoint); - unsigned long long getStart(); - unsigned long long getEnd(); - bool isViewerOn(unsigned long long time); + void wipeOld(uint64_t); + void finish(uint64_t index); + void switchOverTo(statSession & newSess, uint64_t index); + void update(uint64_t index, IPC::statExchange & data); + void ping(const sessIndex & index, uint64_t disconnectPoint); + uint64_t getStart(); + uint64_t getEnd(); + bool isViewerOn(uint64_t time); bool isViewer(); - bool hasDataFor(unsigned long long time); + bool hasDataFor(uint64_t time); bool hasData(); - long long getConnTime(unsigned long long time); - long long getLastSecond(unsigned long long time); - long long getDown(unsigned long long time); - long long getUp(); - long long getDown(); - long long getUp(unsigned long long time); - long long getBpsDown(unsigned long long time); - long long getBpsUp(unsigned long long time); - long long getBpsDown(unsigned long long start, unsigned long long end); - long long getBpsUp(unsigned long long start, unsigned long long end); + uint64_t getConnTime(uint64_t time); + uint64_t getLastSecond(uint64_t time); + uint64_t getDown(uint64_t time); + uint64_t getUp(); + uint64_t getDown(); + uint64_t getUp(uint64_t time); + uint64_t getBpsDown(uint64_t time); + uint64_t getBpsUp(uint64_t time); + uint64_t getBpsDown(uint64_t start, uint64_t end); + uint64_t getBpsUp(uint64_t start, uint64_t end); }; diff --git a/src/controller/controller_storage.cpp b/src/controller/controller_storage.cpp index 5cef6a10..da08c33f 100644 --- a/src/controller/controller_storage.cpp +++ b/src/controller/controller_storage.cpp @@ -18,7 +18,7 @@ namespace Controller { JSON::Value Storage; ///< Global storage of data. tthread::mutex configMutex; tthread::mutex logMutex; - unsigned long long logCounter = 0; + uint64_t logCounter = 0; bool configChanged = false; bool restarting = false; bool isTerminal = false; @@ -53,7 +53,7 @@ namespace Controller { tthread::lock_guard guard(logMutex); JSON::Value m; uint64_t logTime = Util::epoch(); - m.append((long long)logTime); + m.append(logTime); m.append(kind); m.append(message); Storage["log"].append(m); diff --git a/src/controller/controller_storage.h b/src/controller/controller_storage.h index 6ef2a150..9f50d7c3 100644 --- a/src/controller/controller_storage.h +++ b/src/controller/controller_storage.h @@ -14,7 +14,7 @@ namespace Controller { extern bool restarting;///< Signals if the controller is shutting down (false) or restarting (true). extern bool isTerminal;///< True if connected to a terminal and not a log file. extern bool isColorized;///< True if we colorize the output - extern unsigned long long logCounter; ///