From af12c6a94ead9e305341bf689b5e55622bad7833 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Tue, 10 Jul 2012 00:39:31 +0200 Subject: [PATCH] Restructuring so our libraries can go into a separate libmist project. --- Makefile.am | 2 +- configure.ac | 3 +- lib/Makefile.am | 20 - lib/amf.cpp | 969 -------------------------------- lib/amf.h | 129 ----- lib/auth.cpp | 45 -- lib/auth.h | 11 - lib/base64.cpp | 64 --- lib/base64.h | 11 - lib/config.cpp | 106 ---- lib/config.h | 29 - lib/crypto.cpp | 509 ----------------- lib/crypto.h | 56 -- lib/dtsc.cpp | 477 ---------------- lib/dtsc.h | 144 ----- lib/flv_tag.cpp | 954 ------------------------------- lib/flv_tag.h | 66 --- lib/http_parser.cpp | 278 --------- lib/http_parser.h | 47 -- lib/json.cpp | 459 --------------- lib/json.h | 75 --- lib/mp4.cpp | 413 -------------- lib/mp4.h | 131 ----- lib/procs.cpp | 361 ------------ lib/procs.h | 32 -- lib/rtmpchunks.cpp | 467 --------------- lib/rtmpchunks.h | 70 --- lib/socket.cpp | 747 ------------------------ lib/socket.h | 93 --- src/Makefile.am | 9 +- src/analysers/Makefile.am | 6 +- src/analysers/amf_analyser.cpp | 2 +- src/analysers/dtsc_analyser.cpp | 2 +- src/analysers/flv_analyser.cpp | 2 +- src/analysers/rtmp_analyser.cpp | 6 +- src/buffer_stream.h | 4 +- src/buffer_user.h | 6 +- src/conn_http.cpp | 16 +- src/conn_http_dynamic.cpp | 235 ++++++++ src/conn_http_progressive.cpp | 145 +++++ src/conn_raw.cpp | 2 +- src/conn_rtmp.cpp | 8 +- src/controller.cpp | 12 +- src/converters/Makefile.am | 4 +- src/converters/dtsc2flv.cpp | 6 +- src/converters/flv2dtsc.cpp | 6 +- src/server_setup.h | 6 +- src/server_setup_http.h | 112 ++++ {lib => src}/tinythread.cpp | 0 {lib => src}/tinythread.h | 0 50 files changed, 541 insertions(+), 6816 deletions(-) delete mode 100644 lib/Makefile.am delete mode 100644 lib/amf.cpp delete mode 100644 lib/amf.h delete mode 100644 lib/auth.cpp delete mode 100644 lib/auth.h delete mode 100644 lib/base64.cpp delete mode 100644 lib/base64.h delete mode 100644 lib/config.cpp delete mode 100644 lib/config.h delete mode 100644 lib/crypto.cpp delete mode 100644 lib/crypto.h delete mode 100644 lib/dtsc.cpp delete mode 100644 lib/dtsc.h delete mode 100644 lib/flv_tag.cpp delete mode 100644 lib/flv_tag.h delete mode 100644 lib/http_parser.cpp delete mode 100644 lib/http_parser.h delete mode 100644 lib/json.cpp delete mode 100644 lib/json.h delete mode 100644 lib/mp4.cpp delete mode 100644 lib/mp4.h delete mode 100644 lib/procs.cpp delete mode 100644 lib/procs.h delete mode 100644 lib/rtmpchunks.cpp delete mode 100644 lib/rtmpchunks.h delete mode 100644 lib/socket.cpp delete mode 100644 lib/socket.h create mode 100644 src/conn_http_dynamic.cpp create mode 100644 src/conn_http_progressive.cpp create mode 100644 src/server_setup_http.h rename {lib => src}/tinythread.cpp (100%) rename {lib => src}/tinythread.h (100%) diff --git a/Makefile.am b/Makefile.am index 644e3eba..fbcc107c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS=lib src +SUBDIRS=src EXTRA_DIST=server.html VERSION docs: doxygen ./Doxyfile > /dev/null diff --git a/configure.ac b/configure.ac index 289a67fe..1241dffa 100644 --- a/configure.ac +++ b/configure.ac @@ -16,10 +16,12 @@ AC_PROG_CC # Checks for libraries. AC_CHECK_LIB(ssl, RC4) +PKG_CHECK_MODULES([MIST], [mist-1.0 >= 0.0.0]) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h]) + # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_INLINE @@ -43,7 +45,6 @@ CXXFLAGS="$CXXFLAGS -funsigned-char" AC_ARG_ENABLE([verbose], AC_HELP_STRING([--enable-verbose], [Compile with verbose messages]), CXXFLAGS="-DDEBUG=4 $CXXFLAGS") AC_CONFIG_FILES([Makefile - lib/Makefile src/converters/Makefile src/analysers/Makefile src/Makefile]) diff --git a/lib/Makefile.am b/lib/Makefile.am deleted file mode 100644 index ee478a4a..00000000 --- a/lib/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -noinst_LTLIBRARIES=libamf.la libauth.la libbase64.la libconfig.la libkeycrypto.la libdtsc.la libflv_tag.la libhttp_parser.la libjson.la libprocs.la librtmpchunks.la libsocket.la libtinythread.la libmp4.la -libamf_la_SOURCES=amf.h amf.cpp -libauth_la_SOURCES=auth.h auth.cpp -libauth_la_LIBADD=-lssl -lcrypto -libbase64_la_SOURCES=base64.h base64.cpp -libconfig_la_SOURCES=config.h config.cpp -libkeycrypto_la_SOURCES=crypto.h crypto.cpp -libkeycrypto_la_LIBADD=-lssl -lcrypto -libdtsc_la_SOURCES=dtsc.h dtsc.cpp -libflv_tag_la_SOURCES=flv_tag.h flv_tag.cpp -libflv_tag_la_LIBADD=./libamf.la ./libsocket.la -libhttp_parser_la_SOURCES=http_parser.h http_parser.cpp -libjson_la_SOURCES=json.h json.cpp -libprocs_la_SOURCES=procs.h procs.cpp -librtmpchunks_la_SOURCES=rtmpchunks.h rtmpchunks.cpp -librtmpchunks_la_LIBADD=./libflv_tag.la -lssl -lcrypto ./libkeycrypto.la -libsocket_la_SOURCES=socket.h socket.cpp -libtinythread_la_SOURCES=tinythread.h tinythread.cpp -libtinythread_la_LIBADD=-lpthread -libmp4_la_SOURCES=mp4.h mp4.cpp diff --git a/lib/amf.cpp b/lib/amf.cpp deleted file mode 100644 index a11788fb..00000000 --- a/lib/amf.cpp +++ /dev/null @@ -1,969 +0,0 @@ -/// \file amf.cpp -/// Holds all code for the AMF namespace. - -#include "amf.h" -#include -#include //needed for stderr only - -/// Returns the std::string Indice for the current object, if available. -/// Returns an empty string if no indice exists. -std::string AMF::Object::Indice(){return myIndice;}; - -/// Returns the AMF::obj0type AMF0 object type for this object. -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(){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(){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(){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(){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){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(int i){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(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);} - } - return 0; -}; - -/// 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;} - } - return AMF::Object("error", AMF0_DDV_CONTAINER); -}; - -/// Default constructor. -/// Simply fills the data with AMF::Object("error", AMF0_DDV_CONTAINER) -AMF::Object::Object(){ - *this = AMF::Object("error", AMF0_DDV_CONTAINER); -};//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 - myIndice = indice; - myType = setType; - strval = ""; - numval = val; -}; - -/// 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 - myIndice = indice; - myType = setType; - strval = val; - numval = 0; -}; - -/// 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 - myIndice = indice; - myType = setType; - strval = ""; - numval = 0; -}; - -/// Prints the contents of this object to std::cerr. -/// If this object contains other objects, it will call itself recursively -/// and print all nested content in a nice human-readable format. -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; - } - // 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... - } - 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++){st << it->Print(indent+" ");} - } - return st.str(); -};//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(){ - 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: - r += Indice().size() / 256; - r += Indice().size() % 256; - r += Indice(); - //is an object, with the classname first - 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)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(); - } - }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; - } - return r; -};//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. -/// \param data The raw data to parse. -/// \param len The size of the raw data. -/// \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){ - std::string tmpstr; - unsigned int tmpi = 0; - unsigned char tmpdbl[8]; - #if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); - #endif - 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 - return AMF::Object(name, *(double*)tmpdbl, 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 - return AMF::Object(name, *(double*)tmpdbl, 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; - } - #if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); - #endif - return AMF::Object("error", AMF::AMF0_DDV_CONTAINER); -}//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 - unsigned int i = 0, j = 0; - while (i < len){ - ret.addContent(AMF::parseOne(data, len, i, "")); - if (i > j){j = i;}else{return ret;} - } - return ret; -}//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){ - return AMF::parse((const unsigned char*)data.c_str(), data.size()); -}//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(){return myIndice;}; - -/// Returns the AMF::obj0type AMF0 object type for this object. -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(){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(){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(){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(){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(){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){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){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){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);} - } - return 0; -}; - -/// 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;} - } - return AMF::Object3("error", AMF3_DDV_CONTAINER); -}; - -/// Default constructor. -/// Simply fills the data with AMF::Object3("error", AMF3_DDV_CONTAINER) -AMF::Object3::Object3(){ - *this = AMF::Object3("error", AMF3_DDV_CONTAINER); -};//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 - myIndice = indice; - myType = setType; - strval = ""; - dblval = val; - intval = 0; -}; - -/// 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 -myIndice = indice; -myType = setType; -strval = ""; -dblval = val; -intval = 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::Object3::Object3(std::string indice, std::string val, AMF::obj3type setType){//str type initializer - myIndice = indice; - myType = setType; - strval = val; - dblval = 0; - intval = 0; -}; - -/// 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 - myIndice = indice; - myType = setType; - strval = ""; - dblval = 0; - intval = 0; -}; - -/// Prints the contents of this object to std::cerr. -/// If this object contains other objects, it will call itself recursively -/// and print all nested content in a nice human-readable format. -void AMF::Object3::Print(std::string indent){ - std::cerr << indent; - // print my type - switch (myType){ - case AMF::AMF3_UNDEFINED: std::cerr << "Undefined"; break; - case AMF::AMF3_NULL: std::cerr << "Null"; break; - case AMF::AMF3_FALSE: std::cerr << "False"; break; - case AMF::AMF3_TRUE: std::cerr << "True"; break; - case AMF::AMF3_INTEGER: std::cerr << "Integer"; break; - case AMF::AMF3_DOUBLE: std::cerr << "Double"; break; - case AMF::AMF3_STRING: std::cerr << "String"; break; - case AMF::AMF3_XMLDOC: std::cerr << "XML Doc"; break; - case AMF::AMF3_DATE: std::cerr << "Date"; break; - case AMF::AMF3_ARRAY: std::cerr << "Array"; break; - case AMF::AMF3_OBJECT: std::cerr << "Object"; break; - case AMF::AMF3_XML: std::cerr << "XML"; break; - case AMF::AMF3_BYTES: std::cerr << "ByteArray"; break; - case AMF::AMF3_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; - } - // print my string indice, if available - std::cerr << " " << myIndice << " "; - // print my numeric or string contents - switch (myType){ - case AMF::AMF3_INTEGER: std::cerr << intval; break; - case AMF::AMF3_DOUBLE: std::cerr << dblval; break; - case AMF::AMF3_STRING: case AMF::AMF3_XMLDOC: case AMF::AMF3_XML: case AMF::AMF3_BYTES: - if (intval > 0){ - std::cerr << "REF" << intval; - }else{ - std::cerr << strval; - } - break; - case AMF::AMF3_DATE: - if (intval > 0){ - std::cerr << "REF" << intval; - }else{ - std::cerr << dblval; - } - break; - case AMF::AMF3_ARRAY: case AMF::AMF3_OBJECT: - if (intval > 0){ - std::cerr << "REF" << intval; - } - break; - default: break;//we don't care about the rest, and don't want a compiler warning... - } - std::cerr << 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++){it->Print(indent+" ");} - } -};//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(){ - std::string r = ""; - return r; -};//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. -/// \param data The raw data to parse. -/// \param len The size of the raw data. -/// \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){ - std::string tmpstr; - unsigned int tmpi = 0; - unsigned int arrsize = 0; - unsigned char tmpdbl[8]; - #if DEBUG >= 10 - fprintf(stderr, "Note: AMF3 type %hhx found. %i bytes left\n", data[i], len-i); - #endif - 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 - return AMF::Object3(name, *(double*)tmpdbl, 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]; - i += 8;//skip a double forwards - return AMF::Object3(name, *(double*)tmpdbl, 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){ - 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; - } - #if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented AMF3 type %hhx - returning.\n", data[i]); - #endif - return AMF::Object3("error", AMF::AMF3_DDV_CONTAINER); -}//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 - unsigned int i = 0, j = 0; - while (i < len){ - ret.addContent(AMF::parseOne3(data, len, i, "")); - if (i > j){j = i;}else{return ret;} - } - return ret; -}//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){ - return AMF::parse3((const unsigned char*)data.c_str(), data.size()); -}//parse diff --git a/lib/amf.h b/lib/amf.h deleted file mode 100644 index 836cfdb2..00000000 --- a/lib/amf.h +++ /dev/null @@ -1,129 +0,0 @@ -/// \file amf.h -/// 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{ - - /// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use. - enum obj0type { - AMF0_NUMBER = 0x00, - AMF0_BOOL = 0x01, - AMF0_STRING = 0x02, - AMF0_OBJECT = 0x03, - AMF0_MOVIECLIP = 0x04, - AMF0_NULL = 0x05, - AMF0_UNDEFINED = 0x06, - AMF0_REFERENCE = 0x07, - AMF0_ECMA_ARRAY = 0x08, - AMF0_OBJ_END = 0x09, - AMF0_STRICT_ARRAY = 0x0A, - AMF0_DATE = 0x0B, - AMF0_LONGSTRING = 0x0C, - AMF0_UNSUPPORTED = 0x0D, - AMF0_RECORDSET = 0x0E, - AMF0_XMLDOC = 0x0F, - AMF0_TYPED_OBJ = 0x10, - AMF0_UPGRADE = 0x11, - AMF0_DDV_CONTAINER = 0xFF - }; - - /// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use. - enum obj3type { - AMF3_UNDEFINED = 0x00, - AMF3_NULL = 0x01, - AMF3_FALSE = 0x02, - AMF3_TRUE = 0x03, - AMF3_INTEGER = 0x04, - AMF3_DOUBLE = 0x05, - AMF3_STRING = 0x06, - AMF3_XMLDOC = 0x07, - AMF3_DATE = 0x08, - AMF3_ARRAY = 0x09, - AMF3_OBJECT = 0x0A, - AMF3_XML = 0x0B, - AMF3_BYTES = 0x0C, - AMF3_DDV_CONTAINER = 0xFF - }; - - /// 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(int i); - Object getContent(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). - };//AMFType - - /// Parses a C-string to a valid AMF::Object. - 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); - - /// 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); - void 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 - - /// Parses a C-string to a valid AMF::Object3. - 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); - -};//AMF namespace diff --git a/lib/auth.cpp b/lib/auth.cpp deleted file mode 100644 index 14be28f2..00000000 --- a/lib/auth.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "auth.h" -#include "base64.h" - -static unsigned char __gbv2keypub_der[] = { - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, - 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe5, 0xd7, 0x9c, - 0x7d, 0x73, 0xc6, 0xe6, 0xfb, 0x35, 0x7e, 0xd7, 0x57, 0x99, 0x07, 0xdb, - 0x99, 0x70, 0xc9, 0xd0, 0x3e, 0x53, 0x57, 0x3c, 0x1e, 0x55, 0xda, 0x0f, - 0x69, 0xbf, 0x26, 0x79, 0xc7, 0xb6, 0xdd, 0x8e, 0x83, 0x32, 0x65, 0x74, - 0x0d, 0x74, 0x48, 0x42, 0x49, 0x22, 0x52, 0x58, 0x56, 0xc3, 0xe4, 0x49, - 0x5d, 0xac, 0x6a, 0x94, 0xb1, 0x64, 0x14, 0xbf, 0x4d, 0xd5, 0xd7, 0x3a, - 0xca, 0x5c, 0x1e, 0x6f, 0x42, 0x30, 0xac, 0x29, 0xaa, 0xa0, 0x85, 0xd2, - 0x16, 0xa2, 0x8e, 0x89, 0x12, 0xc4, 0x92, 0x06, 0xea, 0xed, 0x48, 0xf6, - 0xdb, 0xed, 0x4f, 0x62, 0x6c, 0xfa, 0xcf, 0xc2, 0xb9, 0x8d, 0x04, 0xb2, - 0xba, 0x63, 0xc9, 0xcc, 0xee, 0x23, 0x64, 0x46, 0x14, 0x12, 0xc8, 0x38, - 0x67, 0x69, 0x6b, 0xaf, 0xd1, 0x7c, 0xb1, 0xb5, 0x79, 0xe4, 0x4e, 0x3a, - 0xa7, 0xe8, 0x28, 0x89, 0x25, 0xc0, 0xd0, 0xd8, 0xc7, 0xd2, 0x26, 0xaa, - 0xf5, 0xbf, 0x36, 0x55, 0x01, 0x89, 0x58, 0x1f, 0x1e, 0xf5, 0xa5, 0x42, - 0x8f, 0x60, 0x2e, 0xc2, 0xd8, 0x21, 0x0b, 0x6c, 0x8d, 0xbb, 0x72, 0xf2, - 0x19, 0x30, 0xe3, 0x4c, 0x3e, 0x80, 0xe7, 0xf2, 0xe3, 0x89, 0x4f, 0xd4, - 0xee, 0x96, 0x3e, 0x4a, 0x9b, 0xe5, 0x16, 0x01, 0xf1, 0x98, 0xc9, 0x0b, - 0xd6, 0xdf, 0x8a, 0x64, 0x47, 0xc4, 0x44, 0xcc, 0x92, 0x69, 0x28, 0xee, - 0x7d, 0xac, 0xdc, 0x30, 0x56, 0x3a, 0xe7, 0xbc, 0xba, 0x45, 0x16, 0x2c, - 0x4c, 0x46, 0x6b, 0x2b, 0x20, 0xfb, 0x3d, 0x20, 0x35, 0xbb, 0x48, 0x49, - 0x13, 0x65, 0xc9, 0x9a, 0x38, 0x10, 0x84, 0x1a, 0x8c, 0xc9, 0xd7, 0xde, - 0x07, 0x10, 0x5a, 0xfb, 0xb4, 0x95, 0xae, 0x18, 0xf2, 0xe3, 0x15, 0xe8, - 0xad, 0x7e, 0xe5, 0x3c, 0xa8, 0x47, 0x85, 0xd6, 0x1f, 0x54, 0xb5, 0xa3, - 0x79, 0x02, 0x03, 0x01, 0x00, 0x01 -}; ///< The GBv2 public key file. -static unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data - -/// Attempts to load the GBv2 public key. -Auth::Auth(){ - const unsigned char * key = __gbv2keypub_der; - pubkey = d2i_RSAPublicKey(0, &key, __gbv2keypub_der_len); -} - -/// Attempts to verify RSA signature using the public key. -/// Assumes basesign argument is base64 encoded RSA signature for data. -/// Returns true if the data could be verified, false otherwise. -bool Auth::PubKey_Check(std::string & data, std::string basesign){ - std::string sign = Base64::decode(basesign); - return (RSA_verify(NID_md5, (unsigned char*)data.c_str(), data.size(), (unsigned char*)sign.c_str(), sign.size(), pubkey) == 1); -} diff --git a/lib/auth.h b/lib/auth.h deleted file mode 100644 index 5d8da7b6..00000000 --- a/lib/auth.h +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include -#include - -class Auth{ - private: - RSA * pubkey; ///< Holds the public key. - public: - Auth(); ///< Attempts to load the GBv2 public key. - bool PubKey_Check(std::string & data, std::string basesign); ///< Attempts to verify RSA signature using the public key. -}; diff --git a/lib/base64.cpp b/lib/base64.cpp deleted file mode 100644 index a2c9dec3..00000000 --- a/lib/base64.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "base64.h" - -/// Needed for base64_encode function -const std::string Base64::chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - /// Helper for base64_decode function -inline bool Base64::is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); -} - -/// Used to base64 encode data. Input is the plaintext as std::string, output is the encoded data as std::string. -/// \param input Plaintext data to encode. -/// \returns Base64 encoded data. -std::string Base64::encode(std::string const input) { - std::string ret; - unsigned int in_len = input.size(); - char quad[4], triple[3]; - unsigned int i, x, n = 3; - for (x = 0; x < in_len; x = x + 3){ - if ((in_len - x) / 3 == 0){n = (in_len - x) % 3;} - for (i=0; i < 3; i++){triple[i] = '0';} - for (i=0; i < n; i++){triple[i] = input[x + i];} - quad[0] = chars[(triple[0] & 0xFC) >> 2]; // FC = 11111100 - quad[1] = chars[((triple[0] & 0x03) << 4) | ((triple[1] & 0xF0) >> 4)]; // 03 = 11 - quad[2] = chars[((triple[1] & 0x0F) << 2) | ((triple[2] & 0xC0) >> 6)]; // 0F = 1111, C0=11110 - quad[3] = chars[triple[2] & 0x3F]; // 3F = 111111 - if (n < 3){quad[3] = '=';} - if (n < 2){quad[2] = '=';} - for(i=0; i < 4; i++){ret += quad[i];} - } - return ret; -}//base64_encode - -/// Used to base64 decode data. Input is the encoded data as std::string, output is the plaintext data as std::string. -/// \param encoded_string Base64 encoded data to decode. -/// \returns Plaintext decoded data. -std::string Base64::decode(std::string const& encoded_string) { - int in_len = encoded_string.size(); - int i = 0; - int j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; - while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; in_++; - if (i ==4) { - for (i = 0; i <4; i++){char_array_4[i] = chars.find(char_array_4[i]);} - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - for (i = 0; (i < 3); i++){ret += char_array_3[i];} - i = 0; - } - } - if (i) { - for (j = i; j <4; j++){char_array_4[j] = 0;} - for (j = 0; j <4; j++){char_array_4[j] = chars.find(char_array_4[j]);} - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } - return ret; -} diff --git a/lib/base64.h b/lib/base64.h deleted file mode 100644 index 2358ae98..00000000 --- a/lib/base64.h +++ /dev/null @@ -1,11 +0,0 @@ -#include - -/// 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); -}; diff --git a/lib/config.cpp b/lib/config.cpp deleted file mode 100644 index 17cb5d4f..00000000 --- a/lib/config.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/// \file config.cpp -/// Contains generic functions for managing configuration. - -#include "config.h" -#include -#include - -#ifdef __FreeBSD__ -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/// Creates a new configuration manager. -Util::Config::Config(){ - listen_port = 4242; - daemon_mode = true; - interface = "0.0.0.0"; - username = "root"; -} - -/// Parses commandline arguments. -/// Calls exit if an unknown option is encountered, printing a help message. -/// confsection must be either already set or never be set at all when this function is called. -/// In other words: do not change confsection after calling this function. -void Util::Config::parseArgs(int argc, char ** argv){ - int opt = 0; - static const char *optString = "ndvp:i:u:c:h?"; - static const struct option longOpts[] = { - {"help",0,0,'h'}, - {"port",1,0,'p'}, - {"interface",1,0,'i'}, - {"username",1,0,'u'}, - {"nodaemon",0,0,'n'}, - {"daemon",0,0,'d'}, - {"version",0,0,'v'}, - 0 - }; - while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ - switch (opt){ - case 'p': listen_port = atoi(optarg); break; - case 'i': interface = optarg; break; - case 'n': daemon_mode = false; break; - case 'd': daemon_mode = true; break; - case 'u': username = optarg; break; - case 'v': - printf("%s\n", PACKAGE_VERSION); - exit(1); - break; - case 'h': - case '?': - std::string doingdaemon = "true"; - if (!daemon_mode){doingdaemon = "false";} - printf("Options: -h[elp], -?, -v[ersion], -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -u[sername] VAL\n"); - printf("Defaults:\n interface: %s\n port: %i\n daemon mode: %s\n username: %s\n", interface.c_str(), listen_port, doingdaemon.c_str(), username.c_str()); - printf("Username root means no change to UID, no matter what the UID is.\n"); - printf("This is %s version %s\n", argv[0], PACKAGE_VERSION); - exit(1); - break; - } - }//commandline options parser -} - -/// 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){ - #if DEBUG >= 1 - fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str()); - #endif - return; - }else{ - if (setuid(user_info->pw_uid) != 0){ - #if DEBUG >= 1 - fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str()); - #endif - }else{ - #if DEBUG >= 3 - fprintf(stderr, "Changed user to %s\n", username.c_str()); - #endif - } - } - } -} - -/// Will turn the current process into a daemon. -/// Works by calling daemon(1,0): -/// Does not change directory to root. -/// Does redirect output to /dev/null -void Util::Daemonize(){ - #if DEBUG >= 3 - fprintf(stderr, "Going into background mode...\n"); - #endif - daemon(1, 0); -} diff --git a/lib/config.h b/lib/config.h deleted file mode 100644 index acf3fb17..00000000 --- a/lib/config.h +++ /dev/null @@ -1,29 +0,0 @@ -/// \file config.h -/// Contains generic function headers for managing configuration. - -#include - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - -/// Contains utility code, not directly related to streaming media -namespace Util{ - - /// Deals with parsing configuration from commandline options. - class Config{ - public: - bool daemon_mode; - std::string interface; - int listen_port; - std::string username; - Config(); - void parseArgs(int argc, char ** argv); - }; - - /// Will set the active user to the named username. - void setUser(std::string user); - - /// Will turn the current process into a daemon. - void Daemonize(); - -}; diff --git a/lib/crypto.cpp b/lib/crypto.cpp deleted file mode 100644 index c523c680..00000000 --- a/lib/crypto.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/// \file crypto.cpp -/// Holds all code needed for RTMP cryptography. - -#define STR(x) (((std::string)(x)).c_str()) - -#include "crypto.h" - -#define P768 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" - -#define P1024 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ -"FFFFFFFFFFFFFFFF" - -#define Q1024 \ -"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \ -"948127044533E63A0105DF531D89CD9128A5043CC71A026E" \ -"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \ -"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \ -"F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \ -"FFFFFFFFFFFFFFFF" - -#define P1536 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" - -#define P2048 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AACAA68FFFFFFFFFFFFFFFF" - -#define P3072 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF" - -#define P4096 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ -"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ -"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ -"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ -"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ -"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \ -"FFFFFFFFFFFFFFFF" - -#define P6144 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ -"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ -"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ -"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ -"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ -"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ -"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ -"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ -"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ -"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ -"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ -"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ -"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ -"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ -"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ -"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ -"12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF" - -#define P8192 \ -"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ -"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ -"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ -"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ -"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ -"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ -"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ -"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \ -"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \ -"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \ -"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \ -"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \ -"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \ -"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \ -"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \ -"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \ -"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \ -"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \ -"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \ -"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \ -"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \ -"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \ -"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \ -"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \ -"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \ -"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \ -"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \ -"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \ -"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \ -"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \ -"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \ -"12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \ -"38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \ -"741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \ -"3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \ -"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \ -"4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \ -"062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \ -"4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \ -"B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \ -"4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \ -"9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \ -"60C980DD98EDD3DFFFFFFFFFFFFFFFFF" - - -uint8_t 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, 0x65, 0x72, - 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 - 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 - -uint8_t 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, - 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 - 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 - - -void replace(std::string &target, std::string search, std::string replacement) { - if (search == replacement) - return; - if (search == "") - return; - std::string::size_type i = std::string::npos; - while ((i = target.find(search)) != std::string::npos) { - target.replace(i, search.length(), replacement); - } -} - - -DHWrapper::DHWrapper(int32_t bitsCount) { - _bitsCount = bitsCount; - _pDH = NULL; - _pSharedKey = NULL; - _sharedKeyLength = 0; - _peerPublickey = NULL; -} - -DHWrapper::~DHWrapper() { - Cleanup(); -} - -bool DHWrapper::Initialize() { - Cleanup(); - - //1. Create the DH - _pDH = DH_new(); - if (_pDH == NULL) { - Cleanup(); - return false; - } - - //2. Create his internal p and g - _pDH->p = BN_new(); - if (_pDH->p == NULL) { - Cleanup(); - return false; - } - _pDH->g = BN_new(); - if (_pDH->g == NULL) { - Cleanup(); - return false; - } - - //3. initialize p, g and key length - if (BN_hex2bn(&_pDH->p, P1024) == 0) { - Cleanup(); - return false; - } - if (BN_set_word(_pDH->g, 2) != 1) { - Cleanup(); - return false; - } - - //4. Set the key length - _pDH->length = _bitsCount; - - //5. Generate private and public key - if (DH_generate_key(_pDH) != 1) { - Cleanup(); - return false; - } - - return true; -} - -bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) { - if (_pDH == NULL) { - return false; - } - - return CopyKey(_pDH->pub_key, pDst, dstLength); -} - -bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) { - if (_pDH == NULL) { - return false; - } - - return CopyKey(_pDH->priv_key, pDst, dstLength); -} - -bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) { - if (_pDH == NULL) { - return false; - } - - if (_sharedKeyLength != 0 || _pSharedKey != NULL) { - return false; - } - - _sharedKeyLength = DH_size(_pDH); - if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024) { - return false; - } - _pSharedKey = new uint8_t[_sharedKeyLength]; - - _peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0); - if (_peerPublickey == NULL) { - return false; - } - - if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength) { - return false; - } - - return true; -} - -bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) { - if (_pDH == NULL) { - return false; - } - - if (dstLength != _sharedKeyLength) { - return false; - } - - memcpy(pDst, _pSharedKey, _sharedKeyLength); - - return true; -} - -void DHWrapper::Cleanup() { - if (_pDH != NULL) { - if (_pDH->p != NULL) { - BN_free(_pDH->p); - _pDH->p = NULL; - } - if (_pDH->g != NULL) { - BN_free(_pDH->g); - _pDH->g = NULL; - } - DH_free(_pDH); - _pDH = NULL; - } - - if (_pSharedKey != NULL) { - delete[] _pSharedKey; - _pSharedKey = NULL; - } - _sharedKeyLength = 0; - - if (_peerPublickey != NULL) { - BN_free(_peerPublickey); - _peerPublickey = NULL; - } -} - -bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) { - int32_t keySize = BN_num_bytes(pNum); - if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)) { - return false; - } - - if (BN_bn2bin(pNum, pDst) != keySize) { - return false; - } - - return true; -} - -void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) { - uint8_t digest[SHA256_DIGEST_LENGTH]; - unsigned int digestLen = 0; - - HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); - HMAC_Update(&ctx, pubKeyIn, 128); - HMAC_Final(&ctx, digest, &digestLen); - HMAC_CTX_cleanup(&ctx); - - RC4_set_key(rc4keyOut, 16, digest); - - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0); - HMAC_Update(&ctx, pubKeyOut, 128); - HMAC_Final(&ctx, digest, &digestLen); - HMAC_CTX_cleanup(&ctx); - - RC4_set_key(rc4keyIn, 16, digest); -} - -std::string md5(std::string source, bool textResult) { - EVP_MD_CTX mdctx; - unsigned char md_value[EVP_MAX_MD_SIZE]; - unsigned int md_len; - - EVP_DigestInit(&mdctx, EVP_md5()); - EVP_DigestUpdate(&mdctx, STR(source), source.length()); - EVP_DigestFinal_ex(&mdctx, md_value, &md_len); - EVP_MD_CTX_cleanup(&mdctx); - - if (textResult) { - std::string result = ""; - char tmp[3]; - for (uint32_t i = 0; i < md_len; i++) { - sprintf(tmp, "%02x", md_value[i]); - result += tmp; - } - return result; - } else { - return std::string((char *) md_value, md_len); - } -} - -std::string b64(std::string source) { - return b64((uint8_t *) STR(source), source.size()); -} - -std::string b64(uint8_t *pBuffer, uint32_t length) { - BIO *bmem; - BIO *b64; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - bmem = BIO_new(BIO_s_mem()); - - b64 = BIO_push(b64, bmem); - BIO_write(b64, pBuffer, length); - std::string result = ""; - if (BIO_flush(b64) == 1) { - BIO_get_mem_ptr(b64, &bptr); - result = std::string(bptr->data, bptr->length); - } - - BIO_free_all(b64); - - - replace(result, "\n", ""); - replace(result, "\r", ""); - - return result; -} - -std::string unb64(std::string source) { - return unb64((uint8_t *)STR(source),source.length()); -} - -std::string unb64(uint8_t *pBuffer, uint32_t length){ - // create a memory buffer containing base64 encoded data - //BIO* bmem = BIO_new_mem_buf((void*) STR(source), source.length()); - BIO* bmem = BIO_new_mem_buf((void *)pBuffer, length); - - // push a Base64 filter so that reading from buffer decodes it - BIO *bioCmd = BIO_new(BIO_f_base64()); - // we don't want newlines - BIO_set_flags(bioCmd, BIO_FLAGS_BASE64_NO_NL); - bmem = BIO_push(bioCmd, bmem); - - char *pOut = new char[length]; - - int finalLen = BIO_read(bmem, (void*) pOut, length); - BIO_free_all(bmem); - std::string result(pOut, finalLen); - delete[] pOut; - return result; -} - -void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) { - unsigned int digestLen; - HMAC_CTX ctx; - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), NULL); - HMAC_Update(&ctx, (unsigned char *) pData, dataLength); - HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen); - HMAC_CTX_cleanup(&ctx); -} - -uint32_t GetDigestOffset0(uint8_t *pBuffer) { - uint32_t offset = pBuffer[8] + pBuffer[9] + pBuffer[10] + pBuffer[11]; - return (offset % 728) + 12; -} -uint32_t GetDigestOffset1(uint8_t *pBuffer) { - uint32_t offset = pBuffer[772] + pBuffer[773] + pBuffer[774] + pBuffer[775]; - return (offset % 728) + 776; -} -uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){ - if (scheme == 0){return GetDigestOffset0(pBuffer);}else{return GetDigestOffset1(pBuffer);} -} -uint32_t GetDHOffset0(uint8_t *pBuffer) { - uint32_t offset = pBuffer[1532] + pBuffer[1533] + pBuffer[1534] + pBuffer[1535]; - return (offset % 632) + 772; -} -uint32_t GetDHOffset1(uint8_t *pBuffer) { - uint32_t offset = pBuffer[768] + pBuffer[769] + pBuffer[770] + pBuffer[771]; - return (offset % 632) + 8; -} -uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){ - if (scheme == 0){return GetDHOffset0(pBuffer);}else{return GetDHOffset1(pBuffer);} -} - - -bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) { - uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme); - uint8_t *pTempBuffer = new uint8_t[1536 - 32]; - memcpy(pTempBuffer, pBuffer, clientDigestOffset); - memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32); - uint8_t *pTempHash = new uint8_t[512]; - HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); - bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0); - #if DEBUG >= 4 - fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); - #endif - delete[] pTempBuffer; - delete[] pTempHash; - return result; -} diff --git a/lib/crypto.h b/lib/crypto.h deleted file mode 100644 index f4daa4bb..00000000 --- a/lib/crypto.h +++ /dev/null @@ -1,56 +0,0 @@ -/// \file crypto.h -/// Holds all headers needed for RTMP cryptography functions. - -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class DHWrapper { -private: - int32_t _bitsCount; - DH *_pDH; - uint8_t *_pSharedKey; - int32_t _sharedKeyLength; - BIGNUM *_peerPublickey; -public: - DHWrapper(int32_t bitsCount); - virtual ~DHWrapper(); - - bool Initialize(); - bool CopyPublicKey(uint8_t *pDst, int32_t dstLength); - bool CopyPrivateKey(uint8_t *pDst, int32_t dstLength); - bool CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length); - bool CopySharedKey(uint8_t *pDst, int32_t dstLength); -private: - void Cleanup(); - bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength); -}; - - -void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut); -std::string md5(std::string source, bool textResult); -std::string b64(std::string source); -std::string b64(uint8_t *pBuffer, uint32_t length); -std::string unb64(std::string source); -std::string unb64(uint8_t *pBuffer, uint32_t length); - -void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult); - -uint32_t GetDigestOffset0(uint8_t *pBuffer); -uint32_t GetDigestOffset1(uint8_t *pBuffer); -uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme); -uint32_t GetDHOffset0(uint8_t *pBuffer); -uint32_t GetDHOffset1(uint8_t *pBuffer); -uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme); - -extern uint8_t genuineFMSKey[]; - -bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme); diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp deleted file mode 100644 index f73f73c3..00000000 --- a/lib/dtsc.cpp +++ /dev/null @@ -1,477 +0,0 @@ -/// \file dtsc.cpp -/// Holds all code for DDVTECH Stream Container parsing/generation. - -#include "dtsc.h" -#include //for memcmp -#include //for htonl/ntohl -#include //for fprint, stderr - -char DTSC::Magic_Header[] = "DTSC"; -char DTSC::Magic_Packet[] = "DTPD"; - -/// Initializes a DTSC::Stream with only one packet buffer. -DTSC::Stream::Stream(){ - datapointer = 0; - buffercount = 1; -} - -/// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers. -/// The actual buffer count may not at all times be the requested amount. -DTSC::Stream::Stream(unsigned int rbuffers){ - datapointer = 0; - if (rbuffers < 1){rbuffers = 1;} - buffercount = rbuffers; -} - -/// Returns the time in milliseconds of the last received packet. -/// This is _not_ the time this packet was received, only the stored time. -unsigned int DTSC::Stream::getTime(){ - return buffers.front().getContentP("time")->NumValue(); -} - -/// Attempts to parse a packet from the given std::string buffer. -/// Returns true if successful, removing the parsed part from the buffer string. -/// Returns false if invalid or not enough data is in the buffer. -/// \arg buffer The std::string buffer to attempt to parse. -bool DTSC::Stream::parsePacket(std::string & buffer){ - uint32_t len; - static bool syncing = false; - if (buffer.length() > 8){ - if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0){ - len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len+8){return false;} - metadata = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); - buffer.erase(0, len+8); - return false; - } - if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0){ - len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len+8){return false;} - buffers.push_front(DTSC::DTMI("empty", DTMI_ROOT)); - buffers.front() = DTSC::parseDTMI((unsigned char*)buffer.c_str() + 8, len); - datapointertype = INVALID; - if (buffers.front().getContentP("data")){ - datapointer = &(buffers.front().getContentP("data")->StrValue()); - if (buffers.front().getContentP("datatype")){ - std::string tmp = buffers.front().getContentP("datatype")->StrValue(); - if (tmp == "video"){datapointertype = VIDEO;} - if (tmp == "audio"){datapointertype = AUDIO;} - if (tmp == "meta"){datapointertype = META;} - } - }else{ - datapointer = 0; - } - buffer.erase(0, len+8); - while (buffers.size() > buffercount){buffers.pop_back();} - advanceRings(); - syncing = false; - return true; - } - #if DEBUG >= 2 - if (!syncing){ - std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl; - syncing = true; - } - #endif - size_t magic_search = buffer.find(Magic_Packet); - if (magic_search == std::string::npos){ - buffer.clear(); - }else{ - buffer.erase(0, magic_search); - } - } - return false; -} - -/// Returns a direct pointer to the data attribute of the last received packet, if available. -/// Returns NULL if no valid pointer or packet is available. -std::string & DTSC::Stream::lastData(){ - return *datapointer; -} - -/// Returns the packed in this buffer number. -/// \arg num Buffer number. -DTSC::DTMI & DTSC::Stream::getPacket(unsigned int num){ - return buffers[num]; -} - -/// Returns the type of the last received packet. -DTSC::datatype DTSC::Stream::lastType(){ - return datapointertype; -} - -/// Returns true if the current stream contains at least one video track. -bool DTSC::Stream::hasVideo(){ - return (metadata.getContentP("video") != 0); -} - -/// Returns true if the current stream contains at least one audio track. -bool DTSC::Stream::hasAudio(){ - return (metadata.getContentP("audio") != 0); -} - -/// Returns a packed DTSC packet, ready to sent over the network. -std::string & DTSC::Stream::outPacket(unsigned int num){ - static std::string emptystring; - if (num >= buffers.size()) return emptystring; - buffers[num].Pack(true); - return buffers[num].packed; -} - -/// Returns a packed DTSC header, ready to sent over the network. -std::string & DTSC::Stream::outHeader(){ - if ((metadata.packed.length() < 4) || !metadata.netpacked){ - metadata.Pack(true); - metadata.packed.replace(0, 4, Magic_Header); - } - return metadata.packed; -} - -/// advances all given out and internal Ring classes to point to the new buffer, after one has been added. -/// Also updates the internal keyframes ring, as well as marking rings as starved if they are. -/// Unsets waiting rings, updating them with their new buffer number. -void DTSC::Stream::advanceRings(){ - std::deque::iterator dit; - std::set::iterator sit; - for (sit = rings.begin(); sit != rings.end(); sit++){ - (*sit)->b++; - if ((*sit)->waiting){(*sit)->waiting = false; (*sit)->b = 0;} - if ((*sit)->starved || ((*sit)->b >= buffers.size())){(*sit)->starved = true; (*sit)->b = 0;} - } - for (dit = keyframes.begin(); dit != keyframes.end(); dit++){ - dit->b++; - if (dit->b >= buffers.size()){keyframes.erase(dit); break;} - } - if ((lastType() == VIDEO) && (buffers.front().getContentP("keyframe"))){ - keyframes.push_front(DTSC::Ring(0)); - } - //increase buffer size if no keyframes available - if ((buffercount > 1) && (keyframes.size() < 1)){buffercount++;} -} - -/// Constructs a new Ring, at the given buffer position. -/// \arg v Position for buffer. -DTSC::Ring::Ring(unsigned int v){ - b = v; - waiting = false; - starved = false; -} - -/// Requests a new Ring, which will be created and added to the internal Ring list. -/// This Ring will be kept updated so it always points to valid data or has the starved boolean set. -/// Don't forget to call dropRing() for all requested Ring classes that are no longer neccessary! -DTSC::Ring * DTSC::Stream::getRing(){ - DTSC::Ring * tmp; - if (keyframes.size() == 0){ - tmp = new DTSC::Ring(0); - }else{ - tmp = new DTSC::Ring(keyframes[0].b); - } - rings.insert(tmp); - return tmp; -} - -/// Deletes a given out Ring class from memory and internal Ring list. -/// Checks for NULL pointers and invalid pointers, silently discarding them. -void DTSC::Stream::dropRing(DTSC::Ring * ptr){ - if (rings.find(ptr) != rings.end()){ - rings.erase(ptr); - delete ptr; - } -} - -/// Properly cleans up the object for erasing. -/// Drops all Ring classes that have been given out. -DTSC::Stream::~Stream(){ - std::set::iterator sit; - for (sit = rings.begin(); sit != rings.end(); sit++){delete (*sit);} -} - -/// Returns the std::string Indice for the current object, if available. -/// Returns an empty string if no indice exists. -std::string DTSC::DTMI::Indice(){return myIndice;}; - -/// Returns the DTSC::DTMItype AMF0 object type for this object. -DTSC::DTMItype DTSC::DTMI::GetType(){return myType;}; - -/// Returns the numeric value of this object, if available. -/// If this object holds no numeric value, 0 is returned. -uint64_t & DTSC::DTMI::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 & DTSC::DTMI::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 * DTSC::DTMI::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 DTSC::DTMI::hasContent(){return contents.size();}; - -/// Returns true if this DTSC::DTMI value is non-default. -/// Non-default means it is either not a root element or has content. -bool DTSC::DTMI::isEmpty(){ - if (myType != DTMI_ROOT){return false;} - return (hasContent() == 0); -}; - -/// Adds an DTSC::DTMI to this object. Works for all types, but only makes sense for container types. -/// This function resets DTMI::packed to an empty string, forcing a repack on the next call to DTMI::Pack. -/// If the indice name already exists, replaces the indice. -void DTSC::DTMI::addContent(DTSC::DTMI c){ - std::vector::iterator it; - for (it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == c.Indice()){ - contents.erase(it); - break; - } - } - contents.push_back(c); packed = ""; -}; - -/// Returns a pointer to the object held at indice i. -/// Returns null pointer if no object is held at this indice. -/// \param i The indice of the object in this container. -DTSC::DTMI* DTSC::DTMI::getContentP(int i){ - if (contents.size() <= (unsigned int)i){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. -DTSC::DTMI DTSC::DTMI::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. -DTSC::DTMI* DTSC::DTMI::getContentP(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return &(*it);} - } - return 0; -}; - -/// 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. -DTSC::DTMI DTSC::DTMI::getContent(std::string s){ - for (std::vector::iterator it = contents.begin(); it != contents.end(); it++){ - if (it->Indice() == s){return *it;} - } - return DTSC::DTMI("error", DTMI_ROOT); -}; - -/// Default constructor. -/// Simply fills the data with DTSC::DTMI("error", AMF0_DDV_CONTAINER) -DTSC::DTMI::DTMI(){ - *this = DTSC::DTMI("error", DTMI_ROOT); -};//default constructor - -/// Constructor for numeric objects. -/// The object type is by default DTMItype::DTMI_INT, 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 objects only support uint64_t values. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, uint64_t val, DTSC::DTMItype setType){//num type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = val; -}; - -/// Constructor for string objects. -/// \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. -DTSC::DTMI::DTMI(std::string indice, std::string val, DTSC::DTMItype setType){//str type initializer - myIndice = indice; - myType = setType; - strval = val; - numval = 0; -}; - -/// Constructor for container objects. -/// \param indice The string indice of this object in its container, or empty string if none. -/// \param setType The object type to force this object to. -DTSC::DTMI::DTMI(std::string indice, DTSC::DTMItype setType){//object type initializer - myIndice = indice; - myType = setType; - strval = ""; - numval = 0; -}; - -/// Prints the contents of this object to std::cerr. -/// If this object contains other objects, it will call itself recursively -/// and print all nested content in a nice human-readable format. -void DTSC::DTMI::Print(std::string indent){ - std::cerr << indent; - // print my type - switch (myType){ - case DTMI_INT: std::cerr << "Integer"; break; - case DTMI_STRING: std::cerr << "String"; break; - case DTMI_OBJECT: std::cerr << "Object"; break; - case DTMI_OBJ_END: std::cerr << "Object end"; break; - case DTMI_ROOT: std::cerr << "Root Node"; break; - } - // print my string indice, if available - std::cerr << " " << myIndice << " "; - // print my numeric or string contents - switch (myType){ - case DTMI_INT: std::cerr << numval; break; - case DTMI_STRING: - if (strval.length() > 200 || ((strval.length() > 1) && ( (strval[0] < 'A') || (strval[0] > 'z') ) )){ - std::cerr << strval.length() << " bytes of data"; - }else{ - std::cerr << strval; - } - break; - default: break;//we don't care about the rest, and don't want a compiler warning... - } - std::cerr << 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++){it->Print(indent+" ");} - } -};//print - -/// Packs the DTMI to a std::string for transfer over the network. -/// If a packed version already exists, does not regenerate it. -/// If the object is a container type, this function will call itself recursively and contain all contents. -/// \arg netpack If true, will pack as a full DTMI packet, if false only as the contents without header. -std::string DTSC::DTMI::Pack(bool netpack){ - if (packed != ""){ - if (netpacked == netpack){return packed;} - if (netpacked){ - packed.erase(0, 8); - }else{ - unsigned int size = htonl(packed.length()); - packed.insert(0, (char*)&size, 4); - packed.insert(0, Magic_Packet); - } - netpacked = !netpacked; - return packed; - } - std::string r = ""; - r += myType; - //output the properly formatted data stream for this object's contents. - switch (myType){ - case DTMI_INT: - 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 DTMI_STRING: - r += strval.size() / (256*256*256); - r += strval.size() / (256*256); - r += strval.size() / 256; - r += strval.size() % 256; - r += strval; - break; - case DTMI_OBJECT: - case DTMI_ROOT: - 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)0x0; r += (char)0x0; r += (char)0xEE; - break; - case DTMI_OBJ_END: - break; - } - packed = r; - netpacked = netpack; - if (netpacked){ - unsigned int size = htonl(packed.length()); - packed.insert(0, (char*)&size, 4); - packed.insert(0, Magic_Packet); - } - return packed; -};//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. -/// \param data The raw data to parse. -/// \param len The size of the raw data. -/// \param i Current parsing position in the raw data. -/// \param name Indice name for any new object created. -/// \returns A single DTSC::DTMI, parsed from the raw data. -DTSC::DTMI DTSC::parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ - unsigned int tmpi = 0; - unsigned char tmpdbl[8]; - #if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); - #endif - switch (data[i]){ - case DTMI_INT: - 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 - return DTSC::DTMI(name, *(uint64_t*)tmpdbl, DTMI_INT); - break; - case DTMI_STRING:{ - 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 - i += tmpi + 5;//skip length+size+1 forwards - return DTSC::DTMI(name, tmpstr, DTMI_STRING); - } break; - case DTMI_ROOT:{ - ++i; - DTSC::DTMI ret(name, DTMI_ROOT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) - 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.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3;//skip 0x0000EE - return ret; - } break; - case DTMI_OBJECT:{ - ++i; - DTSC::DTMI ret(name, DTMI_OBJECT); - while (data[i] + data[i+1] != 0){//while not encountering 0x0000 (we assume 0x0000EE) - 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.addContent(parseOneDTMI(data, len, i, tmpstr));//add content, recursively parsed, updating i, setting indice to tmpstr - } - i += 3;//skip 0x0000EE - return ret; - } break; - } - #if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented DTMI type %hhx - returning.\n", data[i]); - #endif - return DTSC::DTMI("error", DTMI_ROOT); -}//parseOne - -/// Parses a C-string to a valid DTSC::DTMI. -/// This function will find one DTMI object in the string and return it. -DTSC::DTMI DTSC::parseDTMI(const unsigned char * data, unsigned int len){ - DTSC::DTMI ret;//container type - unsigned int i = 0; - ret = parseOneDTMI(data, len, i, ""); - ret.packed = std::string((char*)data, (size_t)len); - ret.netpacked = false; - return ret; -}//parse - -/// Parses a std::string to a valid DTSC::DTMI. -/// This function will find one DTMI object in the string and return it. -DTSC::DTMI DTSC::parseDTMI(std::string data){ - return parseDTMI((const unsigned char*)data.c_str(), data.size()); -}//parse diff --git a/lib/dtsc.h b/lib/dtsc.h deleted file mode 100644 index 428a445e..00000000 --- a/lib/dtsc.h +++ /dev/null @@ -1,144 +0,0 @@ -/// \file dtsc.h -/// Holds all headers for DDVTECH Stream Container parsing/generation. - -#pragma once -#include -#include -#include //for uint64_t -#include -#include -#include - - - -/// Holds all DDVTECH Stream Container classes and parsers. -///Video: -/// - codec (string: H264, H263, VP6) -/// - width (int, pixels) -/// - height (int, pixels) -/// - fpks (int, frames per kilosecond (FPS * 1000)) -/// - bps (int, bytes per second) -/// - init (string, init data) -/// -///Audio: -/// - codec (string: AAC, MP3) -/// - rate (int, Hz) -/// - size (int, bitsize) -/// - bps (int, bytes per second) -/// - channels (int, channelcount) -/// - init (string, init data) -/// -///All packets: -/// - datatype (string: audio, video, meta (unused)) -/// - data (string: data) -/// - time (int: ms into video) -/// -///Video packets: -/// - keyframe (int, if set, is a seekable keyframe) -/// - interframe (int, if set, is a non-seekable interframe) -/// - disposableframe (int, if set, is a disposable interframe) -/// -///H264 video packets: -/// - nalu (int, if set, is a nalu) -/// - nalu_end (int, if set, is a end-of-sequence) -/// - offset (int, unsigned version of signed int! Holds the ms offset between timestamp and proper display time for B-frames) -namespace DTSC{ - - /// Enumerates all possible DTMI types. - enum DTMItype { - DTMI_INT = 0x01, ///< Unsigned 64-bit integer. - DTMI_STRING = 0x02, ///< String, equivalent to the AMF longstring type. - DTMI_OBJECT = 0xE0, ///< Object, equivalent to the AMF object type. - DTMI_OBJ_END = 0xEE, ///< End of object marker. - DTMI_ROOT = 0xFF ///< Root node for all DTMI data. - }; - - /// Recursive class that holds DDVTECH MediaInfo. - class DTMI { - public: - std::string Indice(); - DTMItype GetType(); - uint64_t & NumValue(); - std::string & StrValue(); - const char * Str(); - int hasContent(); - bool isEmpty(); - void addContent(DTMI c); - DTMI* getContentP(int i); - DTMI getContent(int i); - DTMI* getContentP(std::string s); - DTMI getContent(std::string s); - DTMI(); - DTMI(std::string indice, uint64_t val, DTMItype setType = DTMI_INT); - DTMI(std::string indice, std::string val, DTMItype setType = DTMI_STRING); - DTMI(std::string indice, DTMItype setType = DTMI_OBJECT); - void Print(std::string indent = ""); - std::string Pack(bool netpack = false); - bool netpacked; - std::string packed; - protected: - std::string myIndice; ///< Holds this objects indice, if any. - DTMItype myType; ///< Holds this objects AMF0 type. - std::string strval; ///< Holds this objects string value, if any. - uint64_t numval; ///< Holds this objects numeric value, if any. - std::vector contents; ///< Holds this objects contents, if any (for container types). - };//AMFType - - /// Parses a C-string to a valid DTSC::DTMI. - DTMI parseDTMI(const unsigned char * data, unsigned int len); - /// Parses a std::string to a valid DTSC::DTMI. - DTMI parseDTMI(std::string data); - /// Parses a single DTMI type - used recursively by the DTSC::parseDTMI() functions. - DTMI parseOneDTMI(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name); - - /// This enum holds all possible datatypes for DTSC packets. - enum datatype { - AUDIO, ///< Stream Audio data - VIDEO, ///< Stream Video data - META, ///< Stream Metadata - INVALID ///< Anything else or no data available. - }; - - extern char Magic_Header[]; ///< The magic bytes for a DTSC header - extern char Magic_Packet[]; ///< The magic bytes for a DTSC packet - - /// A part from the DTSC::Stream ringbuffer. - /// Holds information about a buffer that will stay consistent - class Ring { - public: - Ring(unsigned int v); - volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! - volatile bool waiting; ///< If true, this Ring is currently waiting for a buffer fill. - volatile bool starved; ///< If true, this Ring can no longer receive valid data. - }; - - /// Holds temporary data for a DTSC stream and provides functions to utilize it. - /// Optionally also acts as a ring buffer of a certain requested size. - /// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe. - class Stream { - public: - Stream(); - ~Stream(); - Stream(unsigned int buffers); - DTSC::DTMI metadata; - DTSC::DTMI & getPacket(unsigned int num = 0); - datatype lastType(); - std::string & lastData(); - bool hasVideo(); - bool hasAudio(); - bool parsePacket(std::string & buffer); - std::string & outPacket(unsigned int num); - std::string & outHeader(); - Ring * getRing(); - unsigned int getTime(); - void dropRing(Ring * ptr); - private: - std::deque buffers; - std::set rings; - std::deque keyframes; - void advanceRings(); - std::string * datapointer; - datatype datapointertype; - unsigned int buffercount; - }; -}; diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp deleted file mode 100644 index 6a70cc06..00000000 --- a/lib/flv_tag.cpp +++ /dev/null @@ -1,954 +0,0 @@ -/// \file flv_tag.cpp -/// Holds all code for the FLV namespace. - -#include "flv_tag.h" -#include "amf.h" -#include "rtmpchunks.h" -#include //for Tag::FileLoader -#include //for Tag::FileLoader -#include //for Tag::FileLoader -#include //malloc -#include //memcpy -#include - -/// 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}; - -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 -/// if the header is not. Not valid can mean: -/// - Not starting with the string "FLV". -/// - The DataOffset is not 9 bytes. -/// - 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){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - if (header[5] != 0) return false; - if (header[6] != 0) return false; - if (header[7] != 0) return false; - if (header[8] != 0x09) return false; - if (header[9] != 0) return false; - if (header[10] != 0) return false; - if (header[11] != 0) return false; - if (header[12] != 0) return false; - return true; -}//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){ - if (header[0] != 'F') return false; - if (header[1] != 'L') return false; - if (header[2] != 'V') return false; - return true; -}//FLV::is_header - -/// True if this media type requires init data. -/// 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; - } - 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; - } - 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; - } - return false; -} - -/// Returns a std::string describing the tag in detail. -/// 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::stringstream R; - R << len << " bytes of "; - switch (data[0]){ - case 0x09: - switch (data[11] & 0x0F){ - case 1: R << "JPEG"; break; - case 2: R << "H263"; break; - case 3: R << "ScreenVideo1"; break; - case 4: R << "VP6"; break; - case 5: R << "VP6Alpha"; break; - case 6: R << "ScreenVideo2"; break; - case 7: R << "H264"; break; - default: R << "unknown"; break; - } - R << " 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; - } - } - break; - case 0x08: - switch (data[11] & 0xF0){ - case 0x00: R << "linear PCM PE"; break; - case 0x10: R << "ADPCM"; break; - case 0x20: R << "MP3"; break; - case 0x30: R << "linear PCM LE"; break; - case 0x40: R << "Nelly16kHz"; break; - case 0x50: R << "Nelly8kHz"; break; - case 0x60: R << "Nelly"; break; - case 0x70: R << "G711A-law"; break; - case 0x80: R << "G711mu-law"; break; - case 0x90: R << "reserved"; break; - case 0xA0: R << "AAC"; break; - case 0xB0: R << "Speex"; break; - case 0xE0: R << "MP38kHz"; break; - case 0xF0: R << "DeviceSpecific"; break; - default: R << "unknown"; break; - } - 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 - -/// 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 - -/// Sets the 32-bit timestamp of this tag. -void FLV::Tag::tagTime(unsigned int T){ - data[4] = ((T >> 16) & 0xFF); - data[5] = ((T >> 8) & 0xFF); - data[6] = (T & 0xFF); - data[7] = ((T >> 24) & 0xFF); -}//tagTime setter - -/// Constructor for a new, empty, tag. -/// The buffer length is initialized to 0, and later automatically -/// increased if neccesary. -FLV::Tag::Tag(){ - len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; -}//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){ - done = true; - sofar = 0; - buf = O.len; - len = buf; - if (len > 0){ - data = (char*)malloc(len); - memcpy(data, O.data, len); - }else{ - data = 0; - } - isKeyframe = O.isKeyframe; -}//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){ - len = 0; buf = 0; data = 0; isKeyframe = false; done = true; sofar = 0; - ChunkLoader(O); -} - -/// 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 - len = O.len; - if (len > 0){ - if (!data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } - } - memcpy(data, O.data, len); - } - isKeyframe = O.isKeyframe; - } - return *this; -}//assignment operator - -/// FLV loader function from DTSC. -/// Takes the DTSC data and makes it into FLV. -bool FLV::Tag::DTSCLoader(DTSC::Stream & S){ - switch (S.lastType()){ - case DTSC::VIDEO: - len = S.lastData().length() + 16; - if (S.metadata.getContentP("video") && S.metadata.getContentP("video")->getContentP("codec")){ - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){len += 4;} - } - break; - case DTSC::AUDIO: - len = S.lastData().length() + 16; - if (S.metadata.getContentP("audio") && S.metadata.getContentP("audio")->getContentP("codec")){ - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){len += 1;} - } - break; - case DTSC::META: - len = S.lastData().length() + 15; - break; - default://ignore all other types (there are currently no other types...) - break; - } - if (len > 0){ - if (!data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } - } - switch (S.lastType()){ - case DTSC::VIDEO: - if ((unsigned int)len == S.lastData().length() + 16){ - memcpy(data+12, S.lastData().c_str(), S.lastData().length()); - }else{ - memcpy(data+16, S.lastData().c_str(), S.lastData().length()); - if (S.getPacket().getContentP("nalu")){data[12] = 1;}else{data[12] = 2;} - int offset = S.getPacket().getContentP("offset")->NumValue(); - data[13] = (offset >> 16) & 0xFF; - data[14] = (offset >> 8) & 0XFF; - data[15] = offset & 0xFF; - } - data[11] = 0; - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){data[11] += 7;} - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){data[11] += 2;} - if (S.getPacket().getContentP("keyframe")){data[11] += 0x10;} - if (S.getPacket().getContentP("interframe")){data[11] += 0x20;} - if (S.getPacket().getContentP("disposableframe")){data[11] += 0x30;} - break; - case DTSC::AUDIO:{ - if ((unsigned int)len == S.lastData().length() + 16){ - memcpy(data+12, S.lastData().c_str(), S.lastData().length()); - }else{ - memcpy(data+13, S.lastData().c_str(), S.lastData().length()); - data[12] = 1;//raw AAC data, not sequence header - } - data[11] = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); - if (datarate >= 44100){ - data[11] += 0x0C; - }else if(datarate >= 22050){ - data[11] += 0x08; - }else if(datarate >= 11025){ - data[11] += 0x04; - } - if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} - break; - } - case DTSC::META: - memcpy(data+11, S.lastData().c_str(), S.lastData().length()); - break; - default: break; - } - } - setLen(); - switch (S.lastType()){ - case DTSC::VIDEO: data[0] = 0x09; break; - case DTSC::AUDIO: data[0] = 0x08; break; - case DTSC::META: data[0] = 0x12; break; - default: break; - } - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; - data[8] = 0; - data[9] = 0; - data[10] = 0; - tagTime(S.getPacket().getContentP("time")->NumValue()); - return true; -} - -/// Helper function that properly sets the tag length from the internal len variable. -void FLV::Tag::setLen(){ - int len4 = len - 4; - int i = len; - data[--i] = (len4) & 0xFF; - len4 >>= 8; - data[--i] = (len4) & 0xFF; - len4 >>= 8; - data[--i] = (len4) & 0xFF; - len4 >>= 8; - data[--i] = (len4) & 0xFF; -} - -/// FLV Video init data loader function from DTSC. -/// Takes the DTSC Video init data and makes it into FLV. -/// Assumes init data is available - so check before calling! -bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){ - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ - len = S.metadata.getContentP("video")->getContentP("init")->StrValue().length() + 20; - } - if (len > 0){ - if (!data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } - } - memcpy(data+16, S.metadata.getContentP("video")->getContentP("init")->StrValue().c_str(), len-20); - data[12] = 0;//H264 sequence header - data[13] = 0; - data[14] = 0; - data[15] = 0; - data[11] = 0x17;//H264 keyframe (0x07 & 0x10) - } - setLen(); - data[0] = 0x09; - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; - data[8] = 0; - data[9] = 0; - data[10] = 0; - tagTime(0); - return true; -} - -/// FLV Audio init data loader function from DTSC. -/// Takes the DTSC Audio init data and makes it into FLV. -/// Assumes init data is available - so check before calling! -bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){ - len = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ - len = S.metadata.getContentP("audio")->getContentP("init")->StrValue().length() + 17; - } - if (len > 0){ - if (!data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } - } - memcpy(data+13, S.metadata.getContentP("audio")->getContentP("init")->StrValue().c_str(), len-17); - data[12] = 0;//AAC sequence header - data[11] = 0; - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} - unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue(); - if (datarate >= 44100){ - data[11] += 0x0C; - }else if(datarate >= 22050){ - data[11] += 0x08; - }else if(datarate >= 11025){ - data[11] += 0x04; - } - if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} - } - setLen(); - data[0] = 0x08; - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; - data[8] = 0; - data[9] = 0; - data[10] = 0; - tagTime(0); - return true; -} - -/// FLV metadata loader function from DTSC. -/// Takes the DTSC metadata and makes it into FLV. -/// Assumes metadata is available - so check before calling! -bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){ - AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); - - amfdata.addContent(AMF::Object("", "onMetaData")); - amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.getContentP("video")){ - amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H264"){ - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 7, AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "VP6"){ - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("video")->getContentP("codec")->StrValue() == "H263"){ - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("video")->getContentP("width")){ - amfdata.getContentP(1)->addContent(AMF::Object("width", S.metadata.getContentP("video")->getContentP("width")->NumValue(), AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("video")->getContentP("height")){ - amfdata.getContentP(1)->addContent(AMF::Object("height", S.metadata.getContentP("video")->getContentP("height")->NumValue(), AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("video")->getContentP("fpks")){ - amfdata.getContentP(1)->addContent(AMF::Object("framerate", (double)S.metadata.getContentP("video")->getContentP("fpks")->NumValue() / 1000.0, AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("video")->getContentP("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", ((double)S.metadata.getContentP("video")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); - } - } - if (S.metadata.getContentP("audio")){ - amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); - amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){ - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 10, AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){ - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", 2, AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("audio")->getContentP("channels")){ - if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){ - amfdata.getContentP(1)->addContent(AMF::Object("stereo", 1, AMF::AMF0_BOOL)); - }else{ - amfdata.getContentP(1)->addContent(AMF::Object("stereo", 0, AMF::AMF0_BOOL)); - } - } - if (S.metadata.getContentP("audio")->getContentP("rate")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", S.metadata.getContentP("audio")->getContentP("rate")->NumValue(), AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("audio")->getContentP("size")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", S.metadata.getContentP("audio")->getContentP("size")->NumValue(), AMF::AMF0_NUMBER)); - } - if (S.metadata.getContentP("audio")->getContentP("bps")){ - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", ((double)S.metadata.getContentP("audio")->getContentP("bps")->NumValue() * 8.0) / 1024.0, AMF::AMF0_NUMBER)); - } - } - - std::string tmp = amfdata.Pack(); - len = tmp.length() + 15; - if (len > 0){ - if (!data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } - } - memcpy(data+11, tmp.c_str(), len-15); - } - setLen(); - data[0] = 0x12; - data[1] = ((len-15) >> 16) & 0xFF; - data[2] = ((len-15) >> 8) & 0xFF; - data[3] = (len-15) & 0xFF; - data[8] = 0; - data[9] = 0; - data[10] = 0; - tagTime(0); - return true; -} - -/// FLV loader function from chunk. -/// Copies the contents and wraps it in a FLV header. -bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk& O){ - len = O.len + 15; - if (len > 0){ - if (!data){ - data = (char*)malloc(len); - buf = len; - }else{ - if (buf < len){ - data = (char*)realloc(data, len); - buf = len; - } - } - memcpy(data+11, &(O.data[0]), O.len); - } - setLen(); - data[0] = O.msg_type_id; - data[3] = O.len & 0xFF; - data[2] = (O.len >> 8) & 0xFF; - data[1] = (O.len >> 16) & 0xFF; - tagTime(O.timestamp); - return true; -} - - -/// Helper function for FLV::MemLoader. -/// This function will try to read count bytes from data buffer D into buffer. -/// This function should be called repeatedly until true. -/// P and sofar are not the same value, because D may not start with the current tag. -/// \param buffer The target buffer. -/// \param count Amount of bytes to read. -/// \param sofar Current amount read. -/// \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 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;} - int r = 0; - if (P+(count-sofar) > S){r = S-P;}else{r = count-sofar;} - memcpy(buffer+sofar, D+P, r); - P += r; - sofar += r; - if (sofar >= count){return true;} - return false; -}//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 (buf < 15){data = (char*)realloc(data, 15); buf = 15;} - 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{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} - } - }else{ - //if a tag header, calculate length and read tag body - len = data[3] + 15; - len += (data[2] << 8); - len += (data[1] << 16); - if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){ - data[0] += 32; - FLV::Parse_Error = true; - Error_Str = "Invalid Tag received ("; - Error_Str += data[0]; - Error_Str += ")."; - return false; - } - done = false; - } - } - }else{ - //read tag body - if (MemReadUntil(data, len, sofar, D, S, P)){ - //calculate keyframeness, next time read header again, return true - if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} - done = true; - sofar = 0; - return true; - } - } - return false; -}//Tag::MemLoader - - -/// Helper function for FLV::SockLoader. -/// This function will try to read count bytes from socket sock into buffer. -/// This function should be called repeatedly until true. -/// \param buffer The target buffer. -/// \param count Amount of bytes to read. -/// \param sofar Current amount read. -/// \param sock Socket to read from. -/// \return True if count bytes are read succesfully, false otherwise. -bool FLV::Tag::SockReadUntil(char * buffer, unsigned int count, unsigned int & sofar, Socket::Connection & sock){ - if (sofar >= count){return true;} - int r = 0; - r = sock.iread(buffer + sofar,count-sofar); - sofar += r; - if (sofar >= count){return true;} - return false; -}//Tag::SockReadUntil - -/// Try to load a tag from a socket. -/// 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 sock The socket to read from. -/// \return True if a whole tag is succesfully read, false otherwise. -bool FLV::Tag::SockLoader(Socket::Connection sock){ - if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} - if (done){ - if (SockReadUntil(data, 11, sofar, sock)){ - //if its a correct FLV header, throw away and read tag header - if (FLV::is_header(data)){ - if (SockReadUntil(data, 13, sofar, sock)){ - if (FLV::check_header(data)){ - sofar = 0; - memcpy(FLV::Header, data, 13); - }else{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} - } - }else{ - //if a tag header, calculate length and read tag body - len = data[3] + 15; - len += (data[2] << 8); - len += (data[1] << 16); - if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){ - data[0] += 32; - FLV::Parse_Error = true; - Error_Str = "Invalid Tag received ("; - Error_Str += data[0]; - Error_Str += ")."; - return false; - } - done = false; - } - } - }else{ - //read tag body - if (SockReadUntil(data, len, sofar, sock)){ - //calculate keyframeness, next time read header again, return true - if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} - done = true; - sofar = 0; - return true; - } - } - return false; -}//Tag::SockLoader - -/// Try to load a tag from a socket. -/// 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 sock The socket to read from. -/// \return True if a whole tag is succesfully read, false otherwise. -bool FLV::Tag::SockLoader(int sock){ - return SockLoader(Socket::Connection(sock)); -}//Tag::SockLoader - -/// Helper function for FLV::FileLoader. -/// This function will try to read count bytes from file f into buffer. -/// This function should be called repeatedly until true. -/// \param buffer The target buffer. -/// \param count Amount of bytes to read. -/// \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;} - int r = 0; - r = fread(buffer + sofar,1,count-sofar,f); - if (r < 0){FLV::Parse_Error = true; Error_Str = "File reading error."; return false;} - sofar += r; - 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){ - int preflags = fcntl(fileno(f), F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(fileno(f), F_SETFL, postflags); - if (buf < 15){data = (char*)realloc(data, 15); buf = 15;} - - 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{FLV::Parse_Error = true; Error_Str = "Invalid header received."; return false;} - } - }else{ - //if a tag header, calculate length and read tag body - len = data[3] + 15; - len += (data[2] << 8); - len += (data[1] << 16); - if (buf < len){data = (char*)realloc(data, len); buf = len;} - if (data[0] > 0x12){ - data[0] += 32; - FLV::Parse_Error = true; - Error_Str = "Invalid Tag received ("; - Error_Str += data[0]; - Error_Str += ")."; - return false; - } - done = false; - } - } - }else{ - //read tag body - if (FileReadUntil(data, len, sofar, f)){ - //calculate keyframeness, next time read header again, return true - if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)){isKeyframe = true;}else{isKeyframe = false;} - done = true; - sofar = 0; - fcntl(fileno(f), F_SETFL, preflags); - return true; - } - } - fcntl(fileno(f), F_SETFL, preflags); - return false; -}//FLV_GetPacket - -DTSC::DTMI FLV::Tag::toDTSC(DTSC::DTMI & metadata){ - DTSC::DTMI pack_out; // Storage for outgoing DTMI data. - - if (data[0] == 0x12){ - AMF::Object meta_in = AMF::parse((unsigned char*)data+11, len-15); - if (meta_in.getContentP(0) && (meta_in.getContentP(0)->StrValue() == "onMetaData") && meta_in.getContentP(1)){ - AMF::Object * tmp = meta_in.getContentP(1); - if (tmp->getContentP("videocodecid")){ - switch ((unsigned int)tmp->getContentP("videocodecid")->NumValue()){ - case 2: Meta_Put(metadata, "video", "codec", "H263"); break; - case 4: Meta_Put(metadata, "video", "codec", "VP6"); break; - case 7: Meta_Put(metadata, "video", "codec", "H264"); break; - default: Meta_Put(metadata, "video", "codec", "?"); break; - } - } - if (tmp->getContentP("audiocodecid")){ - switch ((unsigned int)tmp->getContentP("audiocodecid")->NumValue()){ - case 2: Meta_Put(metadata, "audio", "codec", "MP3"); break; - case 10: Meta_Put(metadata, "audio", "codec", "AAC"); break; - default: Meta_Put(metadata, "audio", "codec", "?"); break; - } - } - if (tmp->getContentP("width")){ - Meta_Put(metadata, "video", "width", (unsigned long long int)tmp->getContentP("width")->NumValue()); - } - if (tmp->getContentP("height")){ - Meta_Put(metadata, "video", "height", (unsigned long long int)tmp->getContentP("height")->NumValue()); - } - if (tmp->getContentP("framerate")){ - Meta_Put(metadata, "video", "fpks", (unsigned long long int)tmp->getContentP("framerate")->NumValue()*1000); - } - if (tmp->getContentP("videodatarate")){ - Meta_Put(metadata, "video", "bps", (unsigned long long int)(tmp->getContentP("videodatarate")->NumValue()*1024)/8); - } - if (tmp->getContentP("audiodatarate")){ - Meta_Put(metadata, "audio", "bps", (unsigned long long int)(tmp->getContentP("audiodatarate")->NumValue()*1024)/8); - } - if (tmp->getContentP("audiosamplerate")){ - Meta_Put(metadata, "audio", "rate", (unsigned long long int)tmp->getContentP("audiosamplerate")->NumValue()); - } - if (tmp->getContentP("audiosamplesize")){ - Meta_Put(metadata, "audio", "size", (unsigned long long int)tmp->getContentP("audiosamplesize")->NumValue()); - } - if (tmp->getContentP("stereo")){ - if (tmp->getContentP("stereo")->NumValue() == 1){ - Meta_Put(metadata, "audio", "channels", 2); - }else{ - Meta_Put(metadata, "audio", "channels", 1); - } - } - } - return pack_out;//empty - } - if (data[0] == 0x08){ - char audiodata = data[11]; - if (needsInitData() && isInitData()){ - if ((audiodata & 0xF0) == 0xA0){ - Meta_Put(metadata, "audio", "init", std::string((char*)data+13, (size_t)len-17)); - }else{ - Meta_Put(metadata, "audio", "init", std::string((char*)data+12, (size_t)len-16)); - } - return pack_out;//skip rest of parsing, get next tag. - } - pack_out = DTSC::DTMI("audio", DTSC::DTMI_ROOT); - pack_out.addContent(DTSC::DTMI("datatype", "audio")); - pack_out.addContent(DTSC::DTMI("time", tagTime())); - if (!Meta_Has(metadata, "audio", "codec")){ - switch (audiodata & 0xF0){ - case 0x20: Meta_Put(metadata, "audio", "codec", "MP3"); break; - case 0xA0: Meta_Put(metadata, "audio", "codec", "AAC"); break; - default: Meta_Put(metadata, "audio", "codec", "?"); break; - } - } - if (!Meta_Has(metadata, "audio", "rate")){ - switch (audiodata & 0x0C){ - case 0x0: Meta_Put(metadata, "audio", "rate", 5512); break; - case 0x4: Meta_Put(metadata, "audio", "rate", 11025); break; - case 0x8: Meta_Put(metadata, "audio", "rate", 22050); break; - case 0xC: Meta_Put(metadata, "audio", "rate", 44100); break; - } - } - if (!Meta_Has(metadata, "audio", "size")){ - switch (audiodata & 0x02){ - case 0x0: Meta_Put(metadata, "audio", "size", 8); break; - case 0x2: Meta_Put(metadata, "audio", "size", 16); break; - } - } - if (!Meta_Has(metadata, "audio", "channels")){ - switch (audiodata & 0x01){ - case 0x0: Meta_Put(metadata, "audio", "channels", 1); break; - case 0x1: Meta_Put(metadata, "audio", "channels", 2); break; - } - } - if ((audiodata & 0xF0) == 0xA0){ - if (len < 18){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+13, (size_t)len-17))); - }else{ - if (len < 17){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); - } - return pack_out; - } - if (data[0] == 0x09){ - char videodata = data[11]; - if (needsInitData() && isInitData()){ - if ((videodata & 0x0F) == 7){ - if (len < 21){return DTSC::DTMI();} - Meta_Put(metadata, "video", "init", std::string((char*)data+16, (size_t)len-20)); - }else{ - if (len < 17){return DTSC::DTMI();} - Meta_Put(metadata, "video", "init", std::string((char*)data+12, (size_t)len-16)); - } - return pack_out;//skip rest of parsing, get next tag. - } - if (!Meta_Has(metadata, "video", "codec")){ - switch (videodata & 0x0F){ - case 2: Meta_Put(metadata, "video", "codec", "H263"); break; - case 4: Meta_Put(metadata, "video", "codec", "VP6"); break; - case 7: Meta_Put(metadata, "video", "codec", "H264"); break; - default: Meta_Put(metadata, "video", "codec", "?"); break; - } - } - pack_out = DTSC::DTMI("video", DTSC::DTMI_ROOT); - pack_out.addContent(DTSC::DTMI("datatype", "video")); - switch (videodata & 0xF0){ - case 0x10: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; - case 0x20: pack_out.addContent(DTSC::DTMI("interframe", 1)); break; - case 0x30: pack_out.addContent(DTSC::DTMI("disposableframe", 1)); break; - case 0x40: pack_out.addContent(DTSC::DTMI("keyframe", 1)); break; - case 0x50: return DTSC::DTMI(); break;//the video info byte we just throw away - useless to us... - } - pack_out.addContent(DTSC::DTMI("time", tagTime())); - if ((videodata & 0x0F) == 7){ - switch (data[12]){ - case 1: pack_out.addContent(DTSC::DTMI("nalu", 1)); break; - case 2: pack_out.addContent(DTSC::DTMI("nalu_end", 1)); break; - } - int offset = (data[13] << 16) + (data[14] << 8) + data[15]; - offset = (offset << 8) >> 8; - pack_out.addContent(DTSC::DTMI("offset", offset)); - if (len < 21){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+16, (size_t)len-20))); - }else{ - if (len < 17){return DTSC::DTMI();} - pack_out.addContent(DTSC::DTMI("data", std::string((char*)data+12, (size_t)len-16))); - } - return pack_out; - } - return pack_out;//should never get here -}//FLV::Tag::toDTSC - -/// Inserts std::string type metadata into the passed DTMI object. -/// \arg meta The DTMI object to put the metadata into. -/// \arg cat Metadata category to insert into. -/// \arg elem Element name to put into the category. -/// \arg val Value to put into the element name. -void FLV::Tag::Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, std::string val){ - if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} - meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); -} - -/// Inserts uint64_t type metadata into the passed DTMI object. -/// \arg meta The DTMI object to put the metadata into. -/// \arg cat Metadata category to insert into. -/// \arg elem Element name to put into the category. -/// \arg val Value to put into the element name. -void FLV::Tag::Meta_Put(DTSC::DTMI & meta, std::string cat, std::string elem, uint64_t val){ - if (meta.getContentP(cat) == 0){meta.addContent(DTSC::DTMI(cat));} - meta.getContentP(cat)->addContent(DTSC::DTMI(elem, val)); -} - -/// Returns true if the named category and elementname are available in the metadata. -/// \arg meta The DTMI object to check. -/// \arg cat Metadata category to check. -/// \arg elem Element name to check. -bool FLV::Tag::Meta_Has(DTSC::DTMI & meta, std::string cat, std::string elem){ - if (meta.getContentP(cat) == 0){return false;} - if (meta.getContentP(cat)->getContentP(elem) == 0){return false;} - return true; -} diff --git a/lib/flv_tag.h b/lib/flv_tag.h deleted file mode 100644 index 6848995c..00000000 --- a/lib/flv_tag.h +++ /dev/null @@ -1,66 +0,0 @@ -/// \file flv_tag.h -/// Holds all headers for the FLV namespace. - -#pragma once -#include "socket.h" -#include "dtsc.h" -#include - -//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. - - //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". - - /// 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. - std::string tagType(); ///< Returns a std::string describing the tag in detail. - unsigned int tagTime(); ///< Returns the 32-bit timestamp of this tag. - void tagTime(unsigned int T); ///< Sets the 32-bit timestamp of this tag. - 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); ///::iterator it; - std::string tmp = method+" "+url+" "+protocol+"\n"; - for (it=headers.begin(); it != headers.end(); it++){ - tmp += (*it).first + ": " + (*it).second + "\n"; - } - tmp += "\n" + body + "\n"; - return tmp; -} - -/// Returns a string containing a valid HTTP 1.0 or 1.1 response, ready for sending. -/// The response is partly build from internal variables set before this call is made. -/// To be precise, protocol, headers and body are used. -/// \param code The HTTP response code. Usually you want 200. -/// \param message The HTTP response message. Usually you want "OK". -/// \return A string containing a valid HTTP 1.0 or 1.1 response, ready for sending. -std::string HTTP::Parser::BuildResponse(std::string code, std::string message){ - /// \todo Include GET/POST variable parsing? - std::map::iterator it; - std::string tmp = protocol+" "+code+" "+message+"\n"; - for (it=headers.begin(); it != headers.end(); it++){ - tmp += (*it).first + ": " + (*it).second + "\n"; - } - tmp += "\n"; - tmp += body; - return tmp; -} - -/// Trims any whitespace at the front or back of the string. -/// Used when getting/setting headers. -/// \param s The string to trim. The string itself will be changed, not returned. -void HTTP::Parser::Trim(std::string & s){ - size_t startpos = s.find_first_not_of(" \t"); - size_t endpos = s.find_last_not_of(" \t"); - if ((std::string::npos == startpos) || (std::string::npos == endpos)){s = "";}else{s = s.substr(startpos, endpos-startpos+1);} -} - -/// Function that sets the body of a response or request, along with the correct Content-Length header. -/// \param s The string to set the body to. -void HTTP::Parser::SetBody(std::string s){ - body = s; - SetHeader("Content-Length", s.length()); -} - -/// Function that sets the body of a response or request, along with the correct Content-Length header. -/// \param buffer The buffer data to set the body to. -/// \param len Length of the buffer data. -void HTTP::Parser::SetBody(char * buffer, int len){ - body = ""; - body.append(buffer, len); - SetHeader("Content-Length", len); -} - -/// Returns header i, if set. -std::string HTTP::Parser::GetHeader(std::string i){return headers[i];} -/// Returns POST variable i, if set. -std::string HTTP::Parser::GetVar(std::string i){return vars[i];} - -/// Sets header i to string value v. -void HTTP::Parser::SetHeader(std::string i, std::string v){ - Trim(i); - Trim(v); - headers[i] = v; -} - -/// Sets header i to integer value v. -void HTTP::Parser::SetHeader(std::string i, int v){ - Trim(i); - char val[128]; - sprintf(val, "%i", v); - headers[i] = val; -} - -/// Sets POST variable i to string value v. -void HTTP::Parser::SetVar(std::string i, std::string v){ - Trim(i); - Trim(v); - //only set if there is actually a key - if(!i.empty()){ - vars[i] = v; - } -} - -/// Attempt to read a whole HTTP request or response from a std::string buffer. -/// If a whole request could be read, it is removed from the front of the given buffer. -/// \param strbuf The buffer to read from. -/// \return True if a whole request or response was read, false otherwise. -bool HTTP::Parser::Read(std::string & strbuf){ - return parse(strbuf); -}//HTTPReader::Read - -/// Attempt to read a whole HTTP response or request from a data buffer. -/// If succesful, fills its own fields with the proper data and removes the response/request -/// from the data buffer. -/// \param HTTPbuffer The data buffer to read from. -/// \return True on success, false otherwise. -bool HTTP::Parser::parse(std::string & HTTPbuffer){ - size_t f; - std::string tmpA, tmpB, tmpC; - while (HTTPbuffer != ""){ - if (!seenHeaders){ - f = HTTPbuffer.find('\n'); - if (f == std::string::npos) return false; - tmpA = HTTPbuffer.substr(0, f); - HTTPbuffer.erase(0, f+1); - while (tmpA.find('\r') != std::string::npos){tmpA.erase(tmpA.find('\r'));} - if (!seenReq){ - seenReq = true; - f = tmpA.find(' '); - if (f != std::string::npos){method = tmpA.substr(0, f); tmpA.erase(0, f+1);} - f = tmpA.find(' '); - if (f != std::string::npos){url = tmpA.substr(0, f); tmpA.erase(0, f+1);} - f = tmpA.find(' '); - if (f != std::string::npos){protocol = tmpA.substr(0, f); tmpA.erase(0, f+1);} - if (url.find('?') != std::string::npos){ - parseVars(url.substr(url.find('?')+1)); //parse GET variables - } - }else{ - if (tmpA.size() == 0){ - seenHeaders = true; - if (GetHeader("Content-Length") != ""){length = atoi(GetHeader("Content-Length").c_str());} - }else{ - f = tmpA.find(':'); - if (f == std::string::npos) continue; - tmpB = tmpA.substr(0, f); - tmpC = tmpA.substr(f+1); - SetHeader(tmpB, tmpC); - } - } - } - if (seenHeaders){ - if (length > 0){ - if (HTTPbuffer.length() >= length){ - body = HTTPbuffer.substr(0, length); - parseVars(body); //parse POST variables - HTTPbuffer.erase(0, length); - return true; - }else{ - return false; - } - }else{ - return true; - } - } - } - return false; //we should never get here... -}//HTTPReader::parse - -#include -/// Parses GET or POST-style variable data. -/// Saves to internal variable structure using HTTP::Parser::SetVar. -void HTTP::Parser::parseVars(std::string data){ - std::string varname; - std::string varval; - // position where a part start (e.g. after &) - size_t pos = 0; - while (pos < data.length()){ - size_t nextpos = data.find('&', pos); - if (nextpos == std::string::npos){ - nextpos = data.length(); - } - size_t eq_pos = data.find('=', pos); - if (eq_pos < nextpos){ - // there is a key and value - varname = data.substr(pos, eq_pos - pos); - varval = data.substr(eq_pos + 1, nextpos - eq_pos - 1); - }else{ - // no value, only a key - varname = data.substr(pos, nextpos - pos); - varval.clear(); - } - SetVar(urlunescape(varname), urlunescape(varval)); - if (nextpos == std::string::npos){ - // in case the string is gigantic - break; - } - // erase & - pos = nextpos + 1; - } -} - -/// Converts a string to chunked format if protocol is HTTP/1.1 - does nothing otherwise. -/// \param bodypart The data to convert - will be converted in-place. -void HTTP::Parser::Chunkify(std::string & bodypart){ - if (protocol == "HTTP/1.1"){ - static char len[10]; - int sizelen = snprintf(len, 10, "%x\r\n", (unsigned int)bodypart.size()); - //prepend the chunk size and \r\n - bodypart.insert(0, len, sizelen); - //append \r\n - bodypart.append("\r\n", 2); - } -} - -/// Unescapes URLencoded std::string data. -std::string HTTP::Parser::urlunescape(const std::string & in){ - std::string out; - for (unsigned int i = 0; i < in.length(); ++i){ - if (in[i] == '%'){ - char tmp = 0; - ++i; - if (i < in.length()){ - tmp = unhex(in[i]) << 4; - } - ++i; - if (i < in.length()){ - tmp += unhex(in[i]); - } - out += tmp; - } else { - if (in[i] == '+'){out += ' ';}else{out += in[i];} - } - } - return out; -} - -/// Helper function for urlunescape. -/// Takes a single char input and outputs its integer hex value. -int HTTP::Parser::unhex(char c){ - return( c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10 ); -} - -/// URLencodes std::string data. -std::string HTTP::Parser::urlencode(const std::string &c){ - std::string escaped=""; - int max = c.length(); - for(int i=0; i>4; - char dig2 = (dec&0x0F); - if (dig1<= 9) dig1+=48; - if (10<= dig1 && dig1<=15) dig1+=97-10; - if (dig2<= 9) dig2+=48; - if (10<= dig2 && dig2<=15) dig2+=97-10; - std::string r; - r.append(&dig1, 1); - r.append(&dig2, 1); - return r; -} diff --git a/lib/http_parser.h b/lib/http_parser.h deleted file mode 100644 index 609e7d28..00000000 --- a/lib/http_parser.h +++ /dev/null @@ -1,47 +0,0 @@ -/// \file http_parser.h -/// Holds all headers for the HTTP namespace. - -#pragma once -#include -#include -#include -#include - -/// Holds all HTTP processing related code. -namespace HTTP{ - /// Simple class for reading and writing HTTP 1.0 and 1.1. - class Parser{ - public: - Parser(); - bool Read(std::string & strbuf); - std::string GetHeader(std::string i); - std::string GetVar(std::string i); - void SetHeader(std::string i, std::string v); - void SetHeader(std::string i, int v); - void SetVar(std::string i, std::string v); - void SetBody(std::string s); - void SetBody(char * buffer, int len); - std::string BuildRequest(); - std::string BuildResponse(std::string code, std::string message); - void Chunkify(std::string & bodypart); - void Clean(); - static std::string urlunescape(const std::string & in); - static std::string urlencode(const std::string & in); - std::string body; - std::string method; - std::string url; - std::string protocol; - unsigned int length; - private: - bool seenHeaders; - bool seenReq; - bool parse(std::string & HTTPbuffer); - void parseVars(std::string data); - std::string read_buffer; - std::map headers; - std::map vars; - void Trim(std::string & s); - static int unhex(char c); - static std::string hex(char dec); - };//HTTP::Parser class -};//HTTP namespace diff --git a/lib/json.cpp b/lib/json.cpp deleted file mode 100644 index 6fe60c84..00000000 --- a/lib/json.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/// \file json.cpp Holds all JSON-related code. - -#include "json.h" -#include -#include -#include - -int JSON::Value::c2hex(int 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; -} - - -std::string JSON::Value::read_string(int separator, std::istream & fromstream){ - std::string out; - bool escaped = false; - while (fromstream.good()){ - int c = fromstream.get(); - if (c == '\\'){ - escaped = true; - continue; - } - if (escaped){ - 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 'u':{ - int d1 = fromstream.get(); - int d2 = fromstream.get(); - int d3 = fromstream.get(); - int d4 = fromstream.get(); - c = c2hex(d4) + (c2hex(d3) << 4) + (c2hex(d2) << 8) + (c2hex(d1) << 16); - } - default: - out += (char)c; - break; - } - }else{ - if (c == separator){ - return out; - }else{ - out += (char)c; - } - } - } - return out; -} - -std::string JSON::Value::string_escape(std::string val){ - std::string out = "\""; - for (unsigned int i = 0; i < val.size(); ++i){ - switch (val[i]){ - case '"': out += "\\\""; break; - case '\\': out += "\\\\"; break; - case '\n': out += "\\n"; break; - case '\b': out += "\\b"; break; - case '\f': out += "\\f"; break; - case '\r': out += "\\r"; break; - case '\t': out += "\\t"; break; - default: out += val[i]; - } - } - out += "\""; - return out; -} - -/// Skips an std::istream forward until any of the following characters is seen: ,]} -void JSON::Value::skipToEnd(std::istream & fromstream){ - while (fromstream.good()){ - char peek = fromstream.peek(); - if (peek == ','){return;} - if (peek == ']'){return;} - if (peek == '}'){return;} - peek = fromstream.get(); - } -} - -/// Sets this JSON::Value to null; -JSON::Value::Value(){ - null(); -} - -/// Sets this JSON::Value to read from this position in the std::istream -JSON::Value::Value(std::istream & fromstream){ - null(); - bool reading_object = false; - bool reading_array = false; - while (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); - return; - }else{ - std::string tmpstr = read_string(c, fromstream); - (*this)[tmpstr] = JSON::Value(fromstream); - } - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - c = fromstream.get(); - myType = INTEGER; - intVal *= 10; - intVal += c - '0'; - break; - case ',': - if (!reading_object && !reading_array) return; - c = fromstream.get(); - if (reading_array){ - append(JSON::Value(fromstream)); - } - break; - case '}': - if (reading_object){c = fromstream.get();} - return; - break; - case ']': - if (reading_array){c = fromstream.get();} - return; - break; - case 't': - case 'T': - skipToEnd(fromstream); - myType = BOOL; - intVal = 1; - return; - break; - case 'f': - case 'F': - skipToEnd(fromstream); - myType = BOOL; - intVal = 0; - return; - break; - case 'n': - case 'N': - skipToEnd(fromstream); - myType = EMPTY; - return; - break; - default: - c = fromstream.get();//ignore this character - continue; - break; - } - } -} - -/// Sets this JSON::Value to the given string. -JSON::Value::Value(const std::string & val){ - myType = STRING; - strVal = val; -} - -/// Sets this JSON::Value to the given string. -JSON::Value::Value(const char * val){ - myType = STRING; - strVal = val; -} - -/// Sets this JSON::Value to the given integer. -JSON::Value::Value(long long int val){ - myType = INTEGER; - intVal = val; -} - -/// 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 == STRING){return strVal == rhs.strVal;} - if (myType == EMPTY){return true;} - if (myType == OBJECT){ - if (objVal.size() != rhs.objVal.size()) return false; - for (std::map::const_iterator it = objVal.begin(); it != objVal.end(); ++it){ - if (!rhs.isMember(it->first)){return false;} - if (it->second != rhs.objVal.find(it->first)->second){return false;} - } - return true; - } - return true; -} - -/// Compares a JSON::Value to another for equality. -bool JSON::Value::operator!=(const JSON::Value & rhs) const{ - return !((*this) == rhs); -} - -/// Sets this JSON::Value to the given boolean. -JSON::Value & JSON::Value::operator=(const bool &rhs){ - null(); - myType = BOOL; - if (rhs) intVal = 1; - return *this; -} - -/// Sets this JSON::Value to the given string. -JSON::Value & JSON::Value::operator=(const std::string &rhs){ - null(); - myType = STRING; - strVal = rhs; - return *this; -} - -/// Sets this JSON::Value to the given string. -JSON::Value & JSON::Value::operator=(const char * rhs){ - return ((*this) = (std::string)rhs); -} - -/// Sets this JSON::Value to the given integer. -JSON::Value & JSON::Value::operator=(const long long int &rhs){ - null(); - myType = INTEGER; - intVal = rhs; - return *this; -} - -/// Sets this JSON::Value to the given integer. -JSON::Value & JSON::Value::operator=(const int &rhs){ - return ((*this) = (long long int)rhs); -} - -/// Sets this JSON::Value to the given integer. -JSON::Value & JSON::Value::operator=(const unsigned int &rhs){ - return ((*this) = (long long int)rhs); -} - -/// Automatic conversion to long long int - returns 0 if not convertable. -JSON::Value::operator long long int(){ - if (myType == INTEGER){ - return intVal; - } - if (myType == STRING){ - return atoll(strVal.c_str()); - } - return 0; -} - - -/// Automatic conversion to std::string. -/// Returns the raw string value if available, otherwise calls toString(). -JSON::Value::operator std::string(){ - if (myType == STRING){ - return strVal; - }else{ - if (myType == EMPTY){ - return ""; - }else{ - return toString(); - } - } -} - -/// 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){ - null(); - myType = OBJECT; - } - return objVal[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){ - null(); - myType = OBJECT; - } - return objVal[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){ - if (myType != ARRAY){ - null(); - myType = ARRAY; - } - while (i >= arrVal.size()){ - append(JSON::Value()); - } - return arrVal[i]; -} - -/// Converts this JSON::Value to valid JSON notation and returns it. -/// Makes absolutely no attempts to pretty-print anything. :-) -std::string JSON::Value::toString(){ - switch (myType){ - case INTEGER: { - std::stringstream st; - st << intVal; - return st.str(); - break; - } - case STRING: { - return string_escape(strVal); - break; - } - case ARRAY: { - std::string tmp = "["; - if (arrVal.size() > 0){ - for (ArrIter it = ArrBegin(); it != ArrEnd(); it++){ - tmp += it->toString(); - if (it + 1 != ArrEnd()){tmp += ",";} - } - } - tmp += "]"; - return tmp; - break; - } - case OBJECT: { - std::string tmp2 = "{"; - if (objVal.size() > 0){ - ObjIter it3 = ObjEnd(); - --it3; - for (ObjIter it2 = ObjBegin(); it2 != ObjEnd(); it2++){ - tmp2 += "\"" + it2->first + "\":"; - tmp2 += it2->second.toString(); - if (it2 != it3){tmp2 += ",";} - } - } - tmp2 += "}"; - return tmp2; - 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){ - null(); - myType = ARRAY; - } - arrVal.push_back(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){ - null(); - myType = ARRAY; - } - arrVal.push_front(rhs); -} - -/// For array and object JSON::Value objects, reduces them -/// so they contain at most size elements, throwing away -/// the first elements and keeping the last ones. -/// 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){ - if (myType == ARRAY){ - while (arrVal.size() > size){arrVal.pop_front();} - return; - } - if (myType == OBJECT){ - while (objVal.size() > size){objVal.erase(objVal.begin());} - return; - } -} - -/// 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){ - objVal.erase(name); -} - -/// 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{ - return objVal.count(name) > 0; -} - -/// Returns an iterator to the begin of the object map, if any. -JSON::ObjIter JSON::Value::ObjBegin(){ - return objVal.begin(); -} - -/// Returns an iterator to the end of the object map, if any. -JSON::ObjIter JSON::Value::ObjEnd(){ - return objVal.end(); -} - -/// Returns an iterator to the begin of the array, if any. -JSON::ArrIter JSON::Value::ArrBegin(){ - return arrVal.begin(); -} - -/// Returns an iterator to the end of the array, if any. -JSON::ArrIter JSON::Value::ArrEnd(){ - return arrVal.end(); -} - -/// Completely clears the contents of this value, -/// changing its type to NULL in the process. -void JSON::Value::null(){ - objVal.clear(); - arrVal.clear(); - strVal.clear(); - intVal = 0; - myType = EMPTY; -} - -/// Converts a std::string to a JSON::Value. -JSON::Value JSON::fromString(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){ - std::ifstream File; - File.open(filename.c_str()); - JSON::Value ret(File); - File.close(); - return ret; -} diff --git a/lib/json.h b/lib/json.h deleted file mode 100644 index f8d47aec..00000000 --- a/lib/json.h +++ /dev/null @@ -1,75 +0,0 @@ -/// \file json.h Holds all JSON-related headers. - -#include -#include -#include -#include - -/// JSON-related classes and functions -namespace JSON{ - - /// Lists all types of JSON::Value. - enum ValueType{ EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT }; - - class Value;//forward declaration for below typedef - - typedef std::map::iterator ObjIter; - typedef std::deque::iterator ArrIter; - - /// A JSON::Value is either a string or an integer, but may also be an object, array or null. - class Value{ - private: - ValueType myType; - long long int intVal; - std::string strVal; - std::deque arrVal; - std::map objVal; - std::string read_string(int separator, std::istream & fromstream); - std::string string_escape(std::string val); - int c2hex(int c); - static void skipToEnd(std::istream & fromstream); - public: - //constructors - Value(); - Value(std::istream & fromstream); - Value(const std::string & val); - Value(const char * val); - Value(long long int val); - Value(bool val); - //comparison operators - bool operator==(const Value &rhs) const; - bool operator!=(const Value &rhs) const; - //assignment operators - 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 unsigned int &rhs); - Value & operator=(const bool &rhs); - //converts to basic types - operator long long int(); - operator std::string(); - operator bool(); - //array operator for maps and arrays - Value & operator[](const std::string i); - Value & operator[](const char * i); - Value & operator[](unsigned int i); - //handy functions and others - std::string toString(); - void append(const Value & rhs); - void prepend(const Value & rhs); - void shrink(unsigned int size); - void removeMember(const std::string & name); - bool isMember(const std::string & name) const; - ObjIter ObjBegin(); - ObjIter ObjEnd(); - ArrIter ArrBegin(); - ArrIter ArrEnd(); - unsigned int size(); - void null(); - }; - - Value fromString(std::string json); - Value fromFile(std::string filename); - -}; diff --git a/lib/mp4.cpp b/lib/mp4.cpp deleted file mode 100644 index 85ded767..00000000 --- a/lib/mp4.cpp +++ /dev/null @@ -1,413 +0,0 @@ -#include "mp4.h" -#include //for malloc and free -#include //for memcpy -#include //for htonl and friends - -/// Contains all MP4 format related code. -namespace MP4{ - -Box::Box() { - Payload = (uint8_t *)malloc(8); - PayloadSize = 0; -} - -Box::Box(uint32_t BoxType) { - Payload = (uint8_t *)malloc(8); - SetBoxType(BoxType); - PayloadSize = 0; -} - -Box::Box(uint8_t * Content, uint32_t length) { - PayloadSize = length-8; - Payload = (uint8_t *)malloc(length); - memcpy(Payload, Content, length); -} - -Box::~Box() { - if (Payload) free(Payload); -} - -void Box::SetBoxType(uint32_t BoxType) { - ((unsigned int*)Payload)[1] = htonl(BoxType); -} - -uint32_t Box::GetBoxType() { - return ntohl(((unsigned int*)Payload)[1]); -} - -void Box::SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index) { - if ( Index + Size > PayloadSize ) { - PayloadSize = Index + Size; - ((unsigned int*)Payload)[0] = htonl(PayloadSize+8); - Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); - } - memcpy(Payload + 8 + Index, Data, Size); -} - -uint32_t Box::GetPayloadSize() { - return PayloadSize; -} - -uint8_t * Box::GetPayload() { - return Payload+8; -} - -uint8_t * Box::GetPayload(uint32_t Index, uint32_t & Size) { - if(Index > PayloadSize) {Size = 0;} - if(Index + Size > PayloadSize) { Size = PayloadSize - Index; } - return Payload + 8 + Index; -} - -uint32_t Box::GetBoxedDataSize() { - return ntohl(((unsigned int*)Payload)[0]); -} - -uint8_t * Box::GetBoxedData( ) { - return Payload; -} - - -uint8_t * Box::uint32_to_uint8( uint32_t data ) { - uint8_t * temp = new uint8_t[4]; - temp[0] = (data >> 24) & 0x000000FF; - temp[1] = (data >> 16 ) & 0x000000FF; - temp[2] = (data >> 8 ) & 0x000000FF; - temp[3] = (data ) & 0x000000FF; - return temp; -} - -uint8_t * Box::uint16_to_uint8( uint16_t data ) { - uint8_t * temp = new uint8_t[2]; - temp[0] = (data >> 8) & 0x00FF; - temp[1] = (data ) & 0x00FF; - return temp; -} - -uint8_t * Box::uint8_to_uint8( uint8_t data ) { - uint8_t * temp = new uint8_t[1]; - temp[0] = data; - return temp; -} - -void Box::ResetPayload( ) { - PayloadSize = 0; - Payload = (uint8_t *)realloc(Payload, PayloadSize + 8); - ((unsigned int*)Payload)[0] = htonl(0); -} - -void ABST::SetBootstrapVersion( uint32_t Version ) { - curBootstrapInfoVersion = Version; -} - -void ABST::SetProfile( uint8_t Profile ) { - curProfile = Profile; -} - -void ABST::SetLive( bool Live ) { - isLive = Live; -} - -void ABST::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void ABST::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; -} - -void ABST::SetMediaTime( uint32_t Time ) { - curMediatime = Time; -} - -void ABST::SetSMPTE( uint32_t Smpte ) { - curSMPTE = Smpte; -} - -void ABST::SetMovieIdentifier( std::string Identifier ) { - curMovieIdentifier = Identifier; -} - -void ABST::SetDRM( std::string Drm ) { - curDRM = Drm; -} - -void ABST::SetMetaData( std::string MetaData ) { - curMetaData = MetaData; -} - -void ABST::AddServerEntry( std::string Url, uint32_t Offset ) { - if(Offset >= Servers.size()) { - Servers.resize(Offset+1); - } - Servers[Offset].ServerBaseUrl = Url; -} - -void ABST::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= Qualities.size()) { - Qualities.resize(Offset+1); - } - Qualities[Offset].QualityModifier = Quality; -} - -void ABST::AddSegmentRunTable( Box * newSegment, uint32_t Offset ) { - if( Offset >= SegmentRunTables.size() ) { - SegmentRunTables.resize(Offset+1); - } - if( SegmentRunTables[Offset] ) { - delete SegmentRunTables[Offset]; - } - SegmentRunTables[Offset] = newSegment; -} - -void ABST::AddFragmentRunTable( Box * newFragment, uint32_t Offset ) { - if( Offset >= FragmentRunTables.size() ) { - FragmentRunTables.resize(Offset+1); - } - if( FragmentRunTables[Offset] ) { - delete FragmentRunTables[Offset]; - } - FragmentRunTables[Offset] = newFragment; -} - -void ABST::SetDefaults( ) { - SetProfile( ); - SetLive( ); - SetUpdate( ); - SetTimeScale( ); - SetMediaTime( ); - SetSMPTE( ); - SetMovieIdentifier( ); - SetDRM( ); - SetMetaData( ); - SetVersion( ); -} - -void ABST::SetVersion( bool NewVersion) { - Version = NewVersion; -} - -void ABST::SetReserved( ) { - SetPayload((uint32_t)4,Box::uint32_to_uint8(0)); -} - -void ABST::WriteContent( ) { - Box * current; - std::string serializedServers = ""; - std::string serializedQualities = ""; - std::string serializedSegments = ""; - std::string serializedFragments = ""; - int SegmentAmount = 0; - int FragmentAmount = 0; - uint8_t * temp = new uint8_t[1]; - - ResetPayload( ); - SetReserved( ); - - for( uint32_t i = 0; i < Servers.size(); i++ ) { - serializedServers.append(Servers[i].ServerBaseUrl.c_str()); - serializedServers += '\0'; - } - for( uint32_t i = 0; i < Qualities.size(); i++ ) { - serializedQualities.append(Qualities[i].QualityModifier.c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < SegmentRunTables.size(); i++ ) { - current=SegmentRunTables[i]; - if( current ) { - SegmentAmount ++; - serializedSegments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - for( uint32_t i = 0; i < FragmentRunTables.size(); i++ ) { - current=FragmentRunTables[i]; - if( current ) { - FragmentAmount ++; - serializedFragments.append((char*)current->GetBoxedData(),current->GetBoxedDataSize()); - } - } - uint32_t OffsetServerEntryCount = 29 + curMovieIdentifier.size() + 1; - uint32_t OffsetQualityEntryCount = OffsetServerEntryCount + 1 + serializedServers.size(); - uint32_t OffsetDrmData = OffsetQualityEntryCount + 1 + serializedQualities.size(); - uint32_t OffsetMetaData = OffsetDrmData + curDRM.size() + 1; - uint32_t OffsetSegmentRuntableCount = OffsetMetaData + curMetaData.size() + 1; - uint32_t OffsetFragmentRuntableCount = OffsetSegmentRuntableCount + 1 + serializedSegments.size(); - - temp[0] = 0 + ( curProfile << 6 ) + ( (uint8_t)isLive << 7 ) + ( (uint8_t)isUpdate << 7 ); - - SetPayload((uint32_t)serializedFragments.size(),(uint8_t*)serializedFragments.c_str(),OffsetFragmentRuntableCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(FragmentAmount),OffsetFragmentRuntableCount); - SetPayload((uint32_t)serializedSegments.size(),(uint8_t*)serializedSegments.c_str(),OffsetSegmentRuntableCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(SegmentAmount),OffsetSegmentRuntableCount); - SetPayload((uint32_t)curMetaData.size()+1,(uint8_t*)curMetaData.c_str(),OffsetMetaData); - SetPayload((uint32_t)curDRM.size()+1,(uint8_t*)curDRM.c_str(),OffsetDrmData); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),OffsetQualityEntryCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(Qualities.size()),OffsetQualityEntryCount); - SetPayload((uint32_t)serializedServers.size(),(uint8_t*)serializedServers.c_str(),OffsetServerEntryCount+1); - SetPayload((uint32_t)1,Box::uint8_to_uint8(Servers.size()),OffsetServerEntryCount); - SetPayload((uint32_t)curMovieIdentifier.size()+1,(uint8_t*)curMovieIdentifier.c_str(),29);//+1 for \0-terminated string... - SetPayload((uint32_t)4,Box::uint32_to_uint8(curSMPTE),25); - SetPayload((uint32_t)4,Box::uint32_to_uint8(0),21); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curMediatime),17); - SetPayload((uint32_t)4,Box::uint32_to_uint8(0),13); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),9); - SetPayload((uint32_t)1,temp,8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curBootstrapInfoVersion),4); -} - -void AFRT::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void AFRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); - } - QualitySegmentUrlModifiers[Offset] = Quality; -} - -void AFRT::AddFragmentRunEntry( uint32_t FirstFragment, uint32_t FirstFragmentTimestamp, uint32_t FragmentsDuration, uint8_t Discontinuity, uint32_t Offset ) { - if( Offset >= FragmentRunEntryTable.size() ) { - FragmentRunEntryTable.resize(Offset+1); - } - FragmentRunEntryTable[Offset].FirstFragment = FirstFragment; - FragmentRunEntryTable[Offset].FirstFragmentTimestamp = FirstFragmentTimestamp; - FragmentRunEntryTable[Offset].FragmentDuration = FragmentsDuration; - if( FragmentsDuration == 0) { - FragmentRunEntryTable[Offset].DiscontinuityIndicator = Discontinuity; - } -} - -void AFRT::SetDefaults( ) { - SetUpdate( ); - SetTimeScale( ); -} - -void AFRT::SetTimeScale( uint32_t Scale ) { - curTimeScale = Scale; -} - -void AFRT::WriteContent( ) { - std::string serializedQualities = ""; - std::string serializedFragmentEntries = ""; - ResetPayload( ); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; - } - for( uint32_t i = 0; i < FragmentRunEntryTable.size(); i ++ ) { - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragment),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(0),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FirstFragmentTimestamp),4); - serializedFragmentEntries.append((char*)Box::uint32_to_uint8(FragmentRunEntryTable[i].FragmentDuration),4); - if(FragmentRunEntryTable[i].FragmentDuration == 0) { - serializedFragmentEntries.append((char*)Box::uint8_to_uint8(FragmentRunEntryTable[i].DiscontinuityIndicator),1); - } - } - - uint32_t OffsetFragmentRunEntryCount = 9 + serializedQualities.size(); - - SetPayload((uint32_t)serializedFragmentEntries.size(),(uint8_t*)serializedFragmentEntries.c_str(),OffsetFragmentRunEntryCount+4); - SetPayload((uint32_t)4,Box::uint32_to_uint8(FragmentRunEntryTable.size()),OffsetFragmentRunEntryCount); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),9); - SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(curTimeScale),4); - SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); -} - -void ASRT::SetUpdate( bool Update ) { - isUpdate = Update; -} - -void ASRT::AddQualityEntry( std::string Quality, uint32_t Offset ) { - if(Offset >= QualitySegmentUrlModifiers.size()) { - QualitySegmentUrlModifiers.resize(Offset+1); - } - QualitySegmentUrlModifiers[Offset] = Quality; -} - -void ASRT::AddSegmentRunEntry( uint32_t FirstSegment, uint32_t FragmentsPerSegment, uint32_t Offset ) { - if( Offset >= SegmentRunEntryTable.size() ) { - SegmentRunEntryTable.resize(Offset+1); - } - SegmentRunEntryTable[Offset].FirstSegment = FirstSegment; - SegmentRunEntryTable[Offset].FragmentsPerSegment = FragmentsPerSegment; -} - -void ASRT::SetVersion( bool NewVersion ) { - Version = NewVersion; -} - -void ASRT::SetDefaults( ) { - SetUpdate( ); -} - -void ASRT::WriteContent( ) { - std::string serializedQualities = ""; - ResetPayload( ); - - for( uint32_t i = 0; i < QualitySegmentUrlModifiers.size(); i++ ) { - serializedQualities.append(QualitySegmentUrlModifiers[i].c_str()); - serializedQualities += '\0'; - } - - uint32_t OffsetSegmentRunEntryCount = 5 + serializedQualities.size(); - - for( uint32_t i = 0; i < SegmentRunEntryTable.size(); i ++ ) { - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FragmentsPerSegment),(8*i)+OffsetSegmentRunEntryCount+8); - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable[i].FirstSegment),(8*i)+OffsetSegmentRunEntryCount+4); - } - SetPayload((uint32_t)4,Box::uint32_to_uint8(SegmentRunEntryTable.size()),OffsetSegmentRunEntryCount); - SetPayload((uint32_t)serializedQualities.size(),(uint8_t*)serializedQualities.c_str(),5); - SetPayload((uint32_t)1,Box::uint8_to_uint8(QualitySegmentUrlModifiers.size()),4); - SetPayload((uint32_t)4,Box::uint32_to_uint8((isUpdate ? 1 : 0))); -} - -std::string GenerateLiveBootstrap( uint32_t CurMediaTime ) { - AFRT afrt; - afrt.SetUpdate(false); - afrt.SetTimeScale(1000); - afrt.AddQualityEntry(""); - afrt.AddFragmentRunEntry(1, 0 , 4000); //FirstFragment, FirstFragmentTimestamp,Fragment Duration in milliseconds - afrt.WriteContent(); - - ASRT asrt; - asrt.SetUpdate(false); - asrt.AddQualityEntry(""); - asrt.AddSegmentRunEntry(1, 199);//1 Segment, 199 Fragments - asrt.WriteContent(); - - ABST abst; - abst.AddFragmentRunTable(&afrt); - abst.AddSegmentRunTable(&asrt); - abst.SetBootstrapVersion(1); - abst.SetProfile(0); - abst.SetLive(true); - abst.SetUpdate(false); - abst.SetTimeScale(1000); - abst.SetMediaTime(0xFFFFFFFF); - abst.SetSMPTE(0); - abst.SetMovieIdentifier("fifa"); - abst.SetDRM(""); - abst.SetMetaData(""); - abst.AddServerEntry(""); - abst.AddQualityEntry(""); - abst.WriteContent(); - - std::string Result; - Result.append((char*)abst.GetBoxedData(), (int)abst.GetBoxedDataSize()); - return Result; -} - -std::string mdatFold(std::string data){ - std::string Result; - unsigned int t_int; - t_int = htonl(data.size()+8); - Result.append((char*)&t_int, 4); - t_int = htonl(0x6D646174); - Result.append((char*)&t_int, 4); - Result.append(data); - return Result; -} - -}; diff --git a/lib/mp4.h b/lib/mp4.h deleted file mode 100644 index 0ef24659..00000000 --- a/lib/mp4.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once -#include -#include -#include - -/// Contains all MP4 format related code. -namespace MP4{ - - class Box { - public: - Box(); - Box(uint32_t BoxType); - Box(uint8_t * Content, uint32_t length); - ~Box(); - void SetBoxType(uint32_t BoxType); - uint32_t GetBoxType(); - void SetPayload(uint32_t Size, uint8_t * Data, uint32_t Index = 0); - uint32_t GetPayloadSize(); - uint8_t * GetPayload(); - uint8_t * GetPayload(uint32_t Index, uint32_t & Size); - uint32_t GetBoxedDataSize(); - uint8_t * GetBoxedData( ); - static uint8_t * uint32_to_uint8( uint32_t data ); - static uint8_t * uint16_to_uint8( uint16_t data ); - static uint8_t * uint8_to_uint8( uint8_t data ); - void ResetPayload( ); - private: - uint8_t * Payload; - uint32_t PayloadSize; - };//Box Class - - struct abst_serverentry { - std::string ServerBaseUrl; - };//abst_serverentry - - struct abst_qualityentry { - std::string QualityModifier; - };//abst_qualityentry - - /// ABST Box class - class ABST: public Box { - public: - ABST() : Box(0x61627374){}; - void SetBootstrapVersion( uint32_t Version = 1 ); - void SetProfile( uint8_t Profile = 0 ); - void SetLive( bool Live = true ); - void SetUpdate( bool Update = false ); - void SetTimeScale( uint32_t Scale = 1000 ); - void SetMediaTime( uint32_t Time = 0 ); - void SetSMPTE( uint32_t Smpte = 0 ); - void SetMovieIdentifier( std::string Identifier = "" ); - void SetDRM( std::string Drm = "" ); - void SetMetaData( std::string MetaData = "" ); - void AddServerEntry( std::string Url = "", uint32_t Offset = 0 ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddSegmentRunTable( Box * newSegment, uint32_t Offset = 0 ); - void AddFragmentRunTable( Box * newFragment, uint32_t Offset = 0 ); - void SetVersion( bool NewVersion = 0 ); - void WriteContent( ); - private: - void SetDefaults( ); - void SetReserved( ); - uint32_t curBootstrapInfoVersion; - uint8_t curProfile; - bool isLive; - bool isUpdate; - bool Version; - uint32_t curTimeScale; - uint32_t curMediatime;//write as uint64_t - uint32_t curSMPTE;//write as uint64_t - std::string curMovieIdentifier; - std::string curDRM; - std::string curMetaData; - std::vector Servers; - std::vector Qualities; - std::vector SegmentRunTables; - std::vector FragmentRunTables; - };//ABST Box - - struct afrt_fragmentrunentry { - uint32_t FirstFragment; - uint32_t FirstFragmentTimestamp; //write as uint64_t - uint32_t FragmentDuration; - uint8_t DiscontinuityIndicator;//if FragmentDuration == 0 - };//afrt_fragmentrunentry - - - /// AFRT Box class - class AFRT : public Box { - public: - AFRT() : Box(0x61667274){}; - void SetUpdate( bool Update = false ); - void SetTimeScale( uint32_t Scale = 1000 ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddFragmentRunEntry( uint32_t FirstFragment = 0, uint32_t FirstFragmentTimestamp = 0, uint32_t FragmentsDuration = 1, uint8_t Discontinuity = 0, uint32_t Offset = 0 ); - void WriteContent( ); - private: - void SetDefaults( ); - bool isUpdate; - uint32_t curTimeScale; - std::vector QualitySegmentUrlModifiers; - std::vector FragmentRunEntryTable; - };//AFRT Box - - struct asrt_segmentrunentry { - uint32_t FirstSegment; - uint32_t FragmentsPerSegment; - };//abst_qualityentry - - /// ASRT Box class - class ASRT : public Box { - public: - ASRT() : Box(0x61737274){}; - void SetUpdate( bool Update = false ); - void AddQualityEntry( std::string Quality = "", uint32_t Offset = 0 ); - void AddSegmentRunEntry( uint32_t FirstSegment = 0, uint32_t FragmentsPerSegment = 100, uint32_t Offset = 0 ); - void WriteContent( ); - void SetVersion( bool NewVersion = 0 ); - private: - void SetDefaults( ); - bool isUpdate; - bool Version; - std::vector QualitySegmentUrlModifiers; - std::vector SegmentRunEntryTable; - Box * Container; - };//ASRT Box - - std::string GenerateLiveBootstrap( uint32_t CurMediaTime ); - std::string mdatFold(std::string data); - -}; diff --git a/lib/procs.cpp b/lib/procs.cpp deleted file mode 100644 index 1512849d..00000000 --- a/lib/procs.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/// \file procs.cpp -/// Contains generic functions for managing processes. - -#include "procs.h" -#include -#include -#include - -#ifdef __FreeBSD__ -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -std::map Util::Procs::plist; -bool Util::Procs::handler_set = false; - -/// Used internally to capture child signals and update plist. -void Util::Procs::childsig_handler(int signum){ - if (signum != SIGCHLD){return;} - pid_t ret = wait(0); - #if DEBUG >= 1 - std::string pname = plist[ret]; - #endif - plist.erase(ret); - #if DEBUG >= 1 - if (isActive(pname)){ - std::cerr << "Process " << pname << " part-terminated." << std::endl; - Stop(pname); - }else{ - std::cerr << "Process " << pname << " fully terminated." << std::endl; - } - #endif -} - -/// Attempts to run the command cmd. -/// Replaces the current process - use after forking first! -/// This function will never return - it will either run the given -/// command or kill itself with return code 42. -void Util::Procs::runCmd(std::string & cmd){ - //split cmd into arguments - //supports a maximum of 20 arguments - char * tmp = (char*)cmd.c_str(); - char * tmp2 = 0; - char * args[21]; - int i = 0; - tmp2 = strtok(tmp, " "); - args[0] = tmp2; - while (tmp2 != 0 && (i < 20)){ - tmp2 = strtok(0, " "); - ++i; - args[i] = tmp2; - } - if (i == 20){args[20] = 0;} - //execute the command - execvp(args[0], args); - #if DEBUG >= 1 - std::cerr << "Error running \"" << cmd << "\": " << strerror(errno) << std::endl; - #endif - _exit(42); -} - -/// Starts a new process if the name is not already active. -/// \return 0 if process was not started, process PID otherwise. -/// \arg name Name for this process - only used internally. -/// \arg cmd Commandline for this process. -pid_t Util::Procs::Start(std::string name, std::string cmd){ - if (isActive(name)){return getPid(name);} - if (!handler_set){ - struct sigaction new_action; - new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset(&new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGCHLD, &new_action, NULL); - handler_set = true; - } - pid_t ret = fork(); - if (ret == 0){ - runCmd(cmd); - }else{ - if (ret > 0){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl; - #endif - plist.insert(std::pair(ret, name)); - }else{ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif - return 0; - } - } - return ret; -} - -/// Starts two piped processes if the name is not already active. -/// \return 0 if process was not started, sub (sending) process PID otherwise. -/// \arg name Name for this process - only used internally. -/// \arg cmd Commandline for sub (sending) process. -/// \arg cmd2 Commandline for main (receiving) process. -pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ - if (isActive(name)){return getPid(name);} - if (!handler_set){ - struct sigaction new_action; - new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset(&new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGCHLD, &new_action, NULL); - handler_set = true; - } - int pfildes[2]; - if (pipe(pfildes) == -1){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; - #endif - return 0; - } - - int devnull = open("/dev/null", O_RDWR); - pid_t ret = fork(); - if (ret == 0){ - close(pfildes[0]); - dup2(pfildes[1],STDOUT_FILENO); - close(pfildes[1]); - dup2(devnull, STDIN_FILENO); - dup2(devnull, STDERR_FILENO); - runCmd(cmd); - }else{ - if (ret > 0){ - plist.insert(std::pair(ret, name)); - }else{ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif - close(pfildes[1]); - close(pfildes[0]); - return 0; - } - } - - pid_t ret2 = fork(); - if (ret2 == 0){ - close(pfildes[1]); - dup2(pfildes[0],STDIN_FILENO); - close(pfildes[0]); - dup2(devnull, STDOUT_FILENO); - dup2(devnull, STDERR_FILENO); - runCmd(cmd2); - }else{ - if (ret2 > 0){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; - #endif - plist.insert(std::pair(ret2, name)); - }else{ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif - Stop(name); - close(pfildes[1]); - close(pfildes[0]); - return 0; - } - } - close(pfildes[1]); - close(pfildes[0]); - return ret; -} - -/// Starts three piped processes if the name is not already active. -/// \return 0 if process was not started, sub (sending) process PID otherwise. -/// \arg name Name for this process - only used internally. -/// \arg cmd Commandline for sub (sending) process. -/// \arg cmd2 Commandline for sub (middle) process. -/// \arg cmd3 Commandline for main (receiving) process. -pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3){ - if (isActive(name)){return getPid(name);} - if (!handler_set){ - struct sigaction new_action; - new_action.sa_handler = Util::Procs::childsig_handler; - sigemptyset(&new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGCHLD, &new_action, NULL); - handler_set = true; - } - - int pfildes[2]; - int pfildes2[2]; - if (pipe(pfildes) == -1){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; - #endif - return 0; - } - if (pipe(pfildes2) == -1){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; - #endif - return 0; - } - - int devnull = open("/dev/null", O_RDWR); - pid_t ret = fork(); - if (ret == 0){ - close(pfildes[0]); - dup2(pfildes[1],STDOUT_FILENO); - close(pfildes[1]); - dup2(devnull, STDIN_FILENO); - dup2(devnull, STDERR_FILENO); - close(pfildes2[1]); - close(pfildes2[0]); - runCmd(cmd); - }else{ - if (ret > 0){ - plist.insert(std::pair(ret, name)); - }else{ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif - close(pfildes[1]); - close(pfildes[0]); - close(pfildes2[1]); - close(pfildes2[0]); - return 0; - } - } - - pid_t ret2 = fork(); - if (ret2 == 0){ - close(pfildes[1]); - close(pfildes2[0]); - dup2(pfildes[0],STDIN_FILENO); - close(pfildes[0]); - dup2(pfildes2[1],STDOUT_FILENO); - close(pfildes2[1]); - dup2(devnull, STDERR_FILENO); - runCmd(cmd2); - }else{ - if (ret2 > 0){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; - #endif - plist.insert(std::pair(ret2, name)); - }else{ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif - Stop(name); - close(pfildes[1]); - close(pfildes[0]); - close(pfildes2[1]); - close(pfildes2[0]); - return 0; - } - } - close(pfildes[1]); - close(pfildes[0]); - - pid_t ret3 = fork(); - if (ret3 == 0){ - close(pfildes[1]); - close(pfildes[0]); - close(pfildes2[1]); - dup2(pfildes2[0],STDIN_FILENO); - close(pfildes2[0]); - dup2(devnull, STDOUT_FILENO); - dup2(devnull, STDERR_FILENO); - runCmd(cmd3); - }else{ - if (ret3 > 0){ - #if DEBUG >= 1 - std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << ", " << ret3 << "): " << cmd << " | " << cmd2 << " | " << cmd3 << std::endl; - #endif - plist.insert(std::pair(ret3, name)); - }else{ - #if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; - #endif - Stop(name); - close(pfildes[1]); - close(pfildes[0]); - close(pfildes2[1]); - close(pfildes2[0]); - return 0; - } - } - - return ret3; -} - -/// Stops the named process, if running. -/// \arg name (Internal) name of process to stop -void Util::Procs::Stop(std::string name){ - int max = 5; - while (isActive(name)){ - Stop(getPid(name)); - max--; - if (max <= 0){return;} - } -} - -/// Stops the process with this pid, if running. -/// \arg name The PID of the process to stop. -void Util::Procs::Stop(pid_t name){ - if (isActive(name)){ - kill(name, SIGTERM); - } -} - -/// (Attempts to) stop all running child processes. -void Util::Procs::StopAll(){ - std::map::iterator it; - for (it = plist.begin(); it != plist.end(); it++){ - Stop((*it).first); - } -} - -/// Returns the number of active child processes. -int Util::Procs::Count(){ - return plist.size(); -} - -/// Returns true if a process by this name is currently active. -bool Util::Procs::isActive(std::string name){ - std::map::iterator it; - for (it = plist.begin(); it != plist.end(); it++){ - if ((*it).second == name){return true;} - } - return false; -} - -/// Returns true if a process with this PID is currently active. -bool Util::Procs::isActive(pid_t name){ - return (plist.count(name) == 1); -} - -/// Gets PID for this named process, if active. -/// \return NULL if not active, process PID otherwise. -pid_t Util::Procs::getPid(std::string name){ - std::map::iterator it; - for (it = plist.begin(); it != plist.end(); it++){ - if ((*it).second == name){return (*it).first;} - } - return 0; -} - -/// Gets name for this process PID, if active. -/// \return Empty string if not active, name otherwise. -std::string Util::Procs::getName(pid_t name){ - if (plist.count(name) == 1){ - return plist[name]; - } - return ""; -} diff --git a/lib/procs.h b/lib/procs.h deleted file mode 100644 index c10620b8..00000000 --- a/lib/procs.h +++ /dev/null @@ -1,32 +0,0 @@ -/// \file procs.h -/// Contains generic function headers for managing processes. - -#include -#include -#include - -/// Contains utility code, not directly related to streaming media -namespace Util{ - - /// Deals with spawning, monitoring and stopping child processes - class Procs{ - private: - static std::map plist; ///< Holds active processes - static bool handler_set; ///< If true, the sigchld handler has been setup. - static void childsig_handler(int signum); - static void runCmd(std::string & cmd); - public: - static pid_t Start(std::string name, std::string cmd); - static pid_t Start(std::string name, std::string cmd, std::string cmd2); - static pid_t Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3); - static void Stop(std::string name); - static void Stop(pid_t name); - static void StopAll(); - static int Count(); - static bool isActive(std::string name); - static bool isActive(pid_t name); - static pid_t getPid(std::string name); - static std::string getName(pid_t name); - }; - -}; diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp deleted file mode 100644 index 51a2e9dd..00000000 --- a/lib/rtmpchunks.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/// \file rtmpchunks.cpp -/// Holds all code for the RTMPStream namespace. - -#include "rtmpchunks.h" -#include "flv_tag.h" -#include "crypto.h" - -char versionstring[] = "WWW.DDVTECH.COM "; ///< String that is repeated in the RTMP handshake -std::string RTMPStream::handshake_in; ///< Input for the handshake. -std::string RTMPStream::handshake_out;///< Output for the handshake. - -/// Gets the current system time in milliseconds. -unsigned int RTMPStream::getNowMS(){ - timeval t; - gettimeofday(&t, 0); - return t.tv_sec * 1000 + t.tv_usec/1000; -}//RTMPStream::getNowMS - - -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; - -timeval RTMPStream::lastrec; - -/// Holds the last sent chunk for every msg_id. -std::map RTMPStream::Chunk::lastsend; -/// Holds the last received chunk for every msg_id. -std::map RTMPStream::Chunk::lastrecv; - -/// 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 output = ""; - RTMPStream::Chunk prev = lastsend[cs_id]; - unsigned int tmpi; - unsigned char chtype = 0x00; - if ((prev.msg_type_id > 0) && (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){ - 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;} - } - if (cs_id <= 63){ - output += (unsigned char)(chtype | cs_id); - }else{ - if (cs_id <= 255+64){ - output += (unsigned char)(chtype | 0); - output += (unsigned char)(cs_id - 64); - }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){ - tmpi = timestamp; - }else{ - tmpi = timestamp - prev.timestamp; - } - if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} - output += (unsigned char)((tmpi >> 16) & 0xff); - output += (unsigned char)((tmpi >> 8) & 0xff); - output += (unsigned char)(tmpi & 0xff); - 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 - output += (unsigned char)msg_type_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)); - output += (unsigned char)(msg_stream_id / (256*256*256)); - } - } - } - //support for 0x00ffffff timestamps - if (ntime){ - output += (unsigned char)(ntime & 0xff); - output += (unsigned char)((ntime >> 8) & 0xff); - output += (unsigned char)((ntime >> 16) & 0xff); - output += (unsigned char)((ntime >> 24) & 0xff); - } - len_left = 0; - while (len_left < len){ - tmpi = len - len_left; - 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){ - output += (unsigned char)(0xC0 + cs_id); - }else{ - if (cs_id <= 255+64){ - output += (unsigned char)(0xC0); - output += (unsigned char)(cs_id - 64); - }else{ - output += (unsigned char)(0xC1); - output += (unsigned char)((cs_id - 64) % 256); - output += (unsigned char)((cs_id - 64) / 256); - } - } - } - } - lastsend[cs_id] = *this; - RTMPStream::snd_cnt += output.size(); - return output; -}//SendChunk - -/// Default contructor, creates an empty chunk with all values initialized to zero. -RTMPStream::Chunk::Chunk(){ - cs_id = 0; - timestamp = 0; - len = 0; - real_len = 0; - len_left = 0; - msg_type_id = 0; - msg_stream_id = 0; - data = ""; -}//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){ - RTMPStream::Chunk ch; - ch.cs_id = cs_id; - ch.timestamp = RTMPStream::getNowMS(); - ch.len = data.size(); - ch.real_len = data.size(); - ch.len_left = 0; - ch.msg_type_id = msg_type_id; - ch.msg_stream_id = msg_stream_id; - ch.data = data; - return ch.Pack(); -}//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){ - RTMPStream::Chunk ch; - ch.cs_id = msg_type_id+42; - ch.timestamp = ts; - ch.len = len; - ch.real_len = len; - ch.len_left = 0; - ch.msg_type_id = msg_type_id; - ch.msg_stream_id = 1; - ch.data.append((char*)data, (size_t)len); - return ch.Pack(); -}//SendMedia - -/// Packs up a chunk with media contents. -/// \param tag FLV::Tag with media to send. -std::string RTMPStream::SendMedia(FLV::Tag & tag){ - RTMPStream::Chunk ch; - ch.cs_id = ((unsigned char)tag.data[0]); - ch.timestamp = tag.tagTime(); - ch.len = tag.len-15; - ch.real_len = tag.len-15; - ch.len_left = 0; - ch.msg_type_id = (unsigned char)tag.data[0]; - ch.msg_stream_id = 1; - ch.data.append(tag.data+11, (size_t)(tag.len-15)); - return ch.Pack(); -}//SendMedia - -/// Packs up a chunk for a control message with 1 argument. -std::string RTMPStream::SendCTL(unsigned char type, unsigned int data){ - RTMPStream::Chunk ch; - ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); - ch.len = 4; - ch.real_len = 4; - ch.len_left = 0; - ch.msg_type_id = type; - ch.msg_stream_id = 0; - ch.data.resize(4); - *(int*)((char*)ch.data.c_str()) = htonl(data); - return ch.Pack(); -}//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){ - RTMPStream::Chunk ch; - ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); - ch.len = 5; - ch.real_len = 5; - ch.len_left = 0; - ch.msg_type_id = type; - ch.msg_stream_id = 0; - ch.data.resize(5); - *(unsigned int*)((char*)ch.data.c_str()) = htonl(data); - ch.data[4] = data2; - return ch.Pack(); -}//SendCTL - -/// Packs up a chunk for a user control message with 1 argument. -std::string RTMPStream::SendUSR(unsigned char type, unsigned int data){ - RTMPStream::Chunk ch; - ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); - ch.len = 6; - ch.real_len = 6; - ch.len_left = 0; - ch.msg_type_id = 4; - ch.msg_stream_id = 0; - ch.data.resize(6); - *(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); - ch.data[0] = 0; - ch.data[1] = type; - return ch.Pack(); -}//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){ - RTMPStream::Chunk ch; - ch.cs_id = 2; - ch.timestamp = RTMPStream::getNowMS(); - ch.len = 10; - ch.real_len = 10; - ch.len_left = 0; - ch.msg_type_id = 4; - ch.msg_stream_id = 0; - ch.data.resize(10); - *(unsigned int*)(((char*)ch.data.c_str())+2) = htonl(data); - *(unsigned int*)(((char*)ch.data.c_str())+6) = htonl(data2); - ch.data[0] = 0; - ch.data[1] = type; - return ch.Pack(); -}//SendUSR - - -/// Parses the argument string into the current chunk. -/// Tries to read a whole chunk, if successful it will remove -/// the corresponding data from the input string. -/// If only part of a chunk is read, it will remove the part and call itself again. -/// This has the effect of only causing a "true" reponse in the case a *whole* chunk -/// is read, not just part of a chunk. -/// \param indata The input string 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(std::string & indata){ - gettimeofday(&RTMPStream::lastrec, 0); - unsigned int i = 0; - if (indata.size() < 1) return false;//need at least a byte - - unsigned char chunktype = indata[i++]; - //read the chunkstream ID properly - switch (chunktype & 0x3F){ - case 0: - if (indata.size() < 2) return false;//need at least 2 bytes to continue - cs_id = indata[i++] + 64; - break; - case 1: - if (indata.size() < 3) return false;//need at least 3 bytes to continue - cs_id = indata[i++] + 64; - cs_id += indata[i++] * 256; - break; - default: - cs_id = chunktype & 0x3F; - break; - } - - RTMPStream::Chunk prev = lastrecv[cs_id]; - - //process the rest of the header, for each chunk type - headertype = chunktype & 0xC0; - switch (headertype){ - case 0x00: - if (indata.size() < i+11) return false; //can't read whole header - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - 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 (indata.size() < i+7) return false; //can't read whole header - if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n");} - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} - 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 (indata.size() < i+3) return false; //can't read whole header - if (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n");} - timestamp = indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - if (timestamp != 0x00ffffff){timestamp += prev.timestamp;} - 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 (prev.msg_type_id == 0){fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n");} - timestamp = prev.timestamp; - len = prev.len; - len_left = prev.len_left; - 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){ - real_len = len_left; - len_left -= real_len; - }else{ - real_len = len; - } - if (real_len > RTMPStream::chunk_rec_max){ - len_left += real_len - RTMPStream::chunk_rec_max; - real_len = RTMPStream::chunk_rec_max; - } - //read extended timestamp, if neccesary - if (timestamp == 0x00ffffff){ - if (indata.size() < i+4) return false; //can't read whole header - timestamp = indata[i++]*256*256*256; - timestamp += indata[i++]*256*256; - timestamp += indata[i++]*256; - timestamp += indata[i++]; - } - - //read data if length > 0, and allocate it - if (real_len > 0){ - if (prev.len_left > 0){ - data = prev.data; - }else{ - data = ""; - } - if (indata.size() < i+real_len) return false;//can't read all data (yet) - data.append(indata, i, real_len); - indata = indata.substr(i+real_len); - lastrecv[cs_id] = *this; - RTMPStream::rec_cnt += i+real_len; - if (len_left == 0){ - return true; - }else{ - return Parse(indata); - } - }else{ - data = ""; - indata = indata.substr(i+real_len); - lastrecv[cs_id] = *this; - RTMPStream::rec_cnt += i+real_len; - return true; - } -}//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(){ - char Version; - //Read C0 - Version = RTMPStream::handshake_in[0]; - uint8_t * Client = (uint8_t *)RTMPStream::handshake_in.c_str() + 1; - RTMPStream::handshake_out.resize(3073); - uint8_t * Server = (uint8_t *)RTMPStream::handshake_out.c_str() + 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){Server[i] = versionstring[i%16];}//"random" data - - bool encrypted = (Version == 6); - #if DEBUG >= 4 - fprintf(stderr, "Handshake version is %hhi\n", Version); - #endif - uint8_t _validationScheme = 5; - if (ValidateClientScheme(Client, 0)) _validationScheme = 0; - if (ValidateClientScheme(Client, 1)) _validationScheme = 1; - - #if DEBUG >= 4 - fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); - #endif - - //FIRST 1536 bytes from server response - //compute DH key position - uint32_t serverDHOffset = GetDHOffset(Server, _validationScheme); - uint32_t clientDHOffset = GetDHOffset(Client, _validationScheme); - - //generate DH key - DHWrapper dhWrapper(1024); - if (!dhWrapper.Initialize()) return false; - if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false; - if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false; - - if (encrypted) { - uint8_t secretKey[128]; - if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false; - RC4_KEY _pKeyIn; - RC4_KEY _pKeyOut; - InitRC4Encryption(secretKey, (uint8_t*) & Client[clientDHOffset], (uint8_t*) & Server[serverDHOffset], &_pKeyIn, &_pKeyOut); - uint8_t data[1536]; - RC4(&_pKeyIn, 1536, data, data); - RC4(&_pKeyOut, 1536, data, data); - } - //generate the digest - uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme); - uint8_t *pTempBuffer = new uint8_t[1536 - 32]; - memcpy(pTempBuffer, Server, serverDigestOffset); - memcpy(pTempBuffer + serverDigestOffset, Server + serverDigestOffset + 32, 1536 - serverDigestOffset - 32); - uint8_t *pTempHash = new uint8_t[512]; - HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash); - memcpy(Server + serverDigestOffset, pTempHash, 32); - delete[] pTempBuffer; - delete[] pTempHash; - - //SECOND 1536 bytes from server response - uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme); - pTempHash = new uint8_t[512]; - HMACsha256(Client + keyChallengeIndex, 32, genuineFMSKey, 68, pTempHash); - uint8_t *pLastHash = new uint8_t[512]; - HMACsha256(Server + 1536, 1536 - 32, pTempHash, 32, pLastHash); - memcpy(Server + 1536 * 2 - 32, pLastHash, 32); - delete[] pTempHash; - delete[] pLastHash; - //DONE BUILDING THE RESPONSE ***// - Server[-1] = Version; - RTMPStream::snd_cnt += 3073; - return true; -} diff --git a/lib/rtmpchunks.h b/lib/rtmpchunks.h deleted file mode 100644 index 009cfd29..00000000 --- a/lib/rtmpchunks.h +++ /dev/null @@ -1,70 +0,0 @@ -/// \file rtmpchunks.h -/// Holds all headers for the RTMPStream namespace. - -#pragma once -#include -#include -#include -#include -#include -#include - -//forward declaration of FLV::Tag to avoid circular dependencies. -namespace FLV{ - class Tag; -}; - -/// Contains all functions and classes needed for RTMP connections. -namespace RTMPStream{ - - /// Gets the current system time in milliseconds. - unsigned int getNowMS(); - - 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 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 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(std::string & data); - std::string Pack(); - - private: - static std::map lastsend; - static std::map lastrecv; - };//RTMPStream::Chunk - - 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; - /// This value is the handshake response that is to be sent out. - extern std::string handshake_out; - /// Does the handshake. Expects handshake_in to be filled, and fills handshake_out. - bool doHandshake(); -};//RTMPStream namespace diff --git a/lib/socket.cpp b/lib/socket.cpp deleted file mode 100644 index 62cbc081..00000000 --- a/lib/socket.cpp +++ /dev/null @@ -1,747 +0,0 @@ -/// \file socket.cpp -/// A handy Socket wrapper library. -/// Written by Jaron Vietor in 2010 for DDVTech - -#include "socket.h" -#include -#include -#include -#include - -#ifdef __FreeBSD__ -#include -#endif - -std::string uint2string(unsigned int i){ - std::stringstream st; - st << i; - return st.str(); -} - -/// 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){ - sock = sockNo; - up = 0; - down = 0; - conntime = time(0); - Error = false; - Blocking = false; -}//Socket::Connection basic constructor - -/// Create a new disconnected base socket. This is a basic constructor for placeholder purposes. -/// A socket created like this is always disconnected and should/could be overwritten at some point. -Socket::Connection::Connection(){ - sock = -1; - up = 0; - down = 0; - conntime = time(0); - Error = false; - Blocking = false; -}//Socket::Connection basic constructor - - -/// Set this socket to be blocking (true) or nonblocking (false). -void Socket::Connection::setBlocking(bool blocking){ - int flags = fcntl(sock, F_GETFL, 0); - if (!blocking){ - flags |= O_NONBLOCK; - }else{ - flags &= !O_NONBLOCK; - } - fcntl(sock, F_SETFL, flags); -} - -/// Close connection. The internal socket is closed and then set to -1. -/// If the connection is already closed, nothing happens. -void Socket::Connection::close(){ - if (connected()){ - #if DEBUG >= 6 - fprintf(stderr, "Socket closed.\n"); - #endif - shutdown(sock, SHUT_RDWR); - ::close(sock); - sock = -1; - } -}//Socket::Connection::close - -/// Returns internal socket number. -int Socket::Connection::getSocket(){return sock;} - -/// Returns a string describing the last error that occured. -/// Simply calls strerror(errno) - not very reliable! -/// \todo Improve getError at some point to be more reliable and only report socket errors. -std::string Socket::Connection::getError(){return strerror(errno);} - -/// Create a new Unix Socket. This socket will (try to) connect to the given address right away. -/// \param address String containing the location of the Unix socket to connect to. -/// \param nonblock Whether the socket should be nonblocking. False by default. -Socket::Connection::Connection(std::string address, bool nonblock){ - sock = socket(PF_UNIX, SOCK_STREAM, 0); - if (sock < 0){ - #if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); - #endif - return; - } - Error = false; - Blocking = false; - up = 0; - down = 0; - conntime = time(0); - sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, address.c_str(), address.size()+1); - int r = connect(sock, (sockaddr*)&addr, sizeof(addr)); - if (r == 0){ - if (nonblock){ - int flags = fcntl(sock, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(sock, F_SETFL, flags); - } - }else{ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), strerror(errno)); - #endif - close(); - } -}//Socket::Connection Unix Contructor - -/// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away. -/// \param host String containing the hostname to connect to. -/// \param port String containing the port to connect to. -/// \param nonblock Whether the socket should be nonblocking. -Socket::Connection::Connection(std::string host, int port, bool nonblock){ - struct addrinfo *result, *rp, hints; - Error = false; - Blocking = false; - up = 0; - down = 0; - conntime = time(0); - std::stringstream ss; - ss << port; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_ADDRCONFIG; - hints.ai_protocol = 0; - hints.ai_canonname = NULL; - hints.ai_addr = NULL; - hints.ai_next = NULL; - int s = getaddrinfo(host.c_str(), ss.str().c_str(), &hints, &result); - if (s != 0){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s:%i! Error: %s\n", host.c_str(), port, gai_strerror(s)); - #endif - close(); - return; - } - - for (rp = result; rp != NULL; rp = rp->ai_next) { - sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sock < 0){continue;} - if (connect(sock, rp->ai_addr, rp->ai_addrlen) == 0){break;} - ::close(sock); - } - freeaddrinfo(result); - - if (rp == 0){ - #if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), strerror(errno)); - #endif - close(); - }else{ - if (nonblock){ - int flags = fcntl(sock, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(sock, F_SETFL, flags); - } - } -}//Socket::Connection TCP Contructor - -/// Calls poll() on the socket, checking if data is available. -/// This function may return true even if there is no data, but never returns false when there is. -bool Socket::Connection::canRead(){ - struct pollfd PFD; - PFD.fd = sock; - PFD.events = POLLIN; - PFD.revents = 0; - poll(&PFD, 1, 5); - return (PFD.revents & POLLIN) == POLLIN; -} -/// Calls poll() on the socket, checking if data can be written. -bool Socket::Connection::canWrite(){ - struct pollfd PFD; - PFD.fd = sock; - PFD.events = POLLOUT; - PFD.revents = 0; - poll(&PFD, 1, 5); - return (PFD.revents & POLLOUT) == POLLOUT; -} - - -/// Returns the ready-state for this socket. -/// \returns 1 if data is waiting to be read, -1 if not connected, 0 otherwise. -signed int Socket::Connection::ready(){ - if (sock < 0) return -1; - char tmp; - int preflags = fcntl(sock, F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(sock, F_SETFL, postflags); - int r = recv(sock, &tmp, 1, MSG_PEEK); - fcntl(sock, F_SETFL, preflags); - if (r < 0){ - if (errno == EAGAIN || errno == EWOULDBLOCK){ - return 0; - }else{ - #if DEBUG >= 2 - fprintf(stderr, "Socket ready error! Error: %s\n", strerror(errno)); - #endif - close(); - return -1; - } - } - if (r == 0){ - #if DEBUG >= 4 - fprintf(stderr, "Socket ready error - socket is closed.\n"); - #endif - close(); - return -1; - } - return r; -} - -/// Returns the connected-state for this socket. -/// Note that this function might be slightly behind the real situation. -/// The connection status is updated after every read/write attempt, when errors occur -/// and when the socket is closed manually. -/// \returns True if socket is connected, false otherwise. -bool Socket::Connection::connected() const{ - return (sock >= 0); -} - -/// Returns total amount of bytes sent. -unsigned int Socket::Connection::dataUp(){ - return up; -} - -/// Returns total amount of bytes received. -unsigned int Socket::Connection::dataDown(){ - return down; -} - -/// 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 getHost() + " " + C + " " + uint2string(time(0) - conntime) + " " + uint2string(up) + " " + uint2string(down) + "\n"; -} - -/// Updates the downbuffer and upbuffer internal variables. -/// Returns true if new data was received, false otherwise. -bool Socket::Connection::spool(){ - iwrite(upbuffer); - return iread(downbuffer); -} - -/// Returns a reference to the download buffer. -std::string & Socket::Connection::Received(){ - return downbuffer; -} - -/// Appends data to the upbuffer. -void Socket::Connection::Send(std::string data){ - upbuffer.append(data); -} - -/// Writes data to socket. This function blocks if the socket is blocking and all data cannot be written right away. -/// If the socket is nonblocking and not all data can be written, this function sets internal variable Blocking to true -/// and returns false. -/// \param buffer Location of the buffer to write from. -/// \param len Amount of bytes to write. -/// \returns True if the whole write was succesfull, false otherwise. -bool Socket::Connection::write(const void * buffer, int len){ - int sofar = 0; - if (sock < 0){return false;} - while (sofar != len){ - int r = send(sock, (char*)buffer + sofar, len-sofar, 0); - if (r <= 0){ - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not write data! Error: %s\n", strerror(errno)); - #endif - close(); - up += sofar; - return false; - }else{ - sofar += r; - } - } - up += sofar; - return true; -}//DDv::Socket::write - -/// Reads data from socket. This function blocks if the socket is blocking and all data cannot be read right away. -/// If the socket is nonblocking and not all data can be read, this function sets internal variable Blocking to true -/// and returns false. -/// \param buffer Location of the buffer to read to. -/// \param len Amount of bytes to read. -/// \returns True if the whole read was succesfull, false otherwise. -bool Socket::Connection::read(const void * buffer, int len){ - int sofar = 0; - if (sock < 0){return false;} - while (sofar != len){ - int r = recv(sock, (char*)buffer + sofar, len-sofar, 0); - if (r < 0){ - switch (errno){ - case EWOULDBLOCK: - down += sofar; - return 0; - break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not read data! Error %i: %s\n", r, strerror(errno)); - #endif - close(); - down += sofar; - break; - } - return false; - }else{ - if (r == 0){ - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not read data! Socket is closed.\n"); - #endif - close(); - down += sofar; - return false; - } - sofar += r; - } - } - down += sofar; - return true; -}//Socket::Connection::read - -/// Read call that is compatible with file access syntax. This function simply calls the other read function. -bool Socket::Connection::read(const void * buffer, int width, int count){return read(buffer, width*count);} -/// Write call that is compatible with file access syntax. This function simply calls the other write function. -bool Socket::Connection::write(const void * buffer, int width, int count){return write(buffer, width*count);} -/// Write call that is compatible with std::string. This function simply calls the other write function. -bool Socket::Connection::write(const std::string data){return write(data.c_str(), data.size());} - -/// Incremental write call. This function tries to write len bytes to the socket from the buffer, -/// returning the amount of bytes it actually wrote. -/// \param buffer Location of the buffer to write from. -/// \param len Amount of bytes to write. -/// \returns The amount of bytes actually written. -int Socket::Connection::iwrite(const void * buffer, int len){ - if (sock < 0){return 0;} - int r = send(sock, buffer, len, 0); - if (r < 0){ - switch (errno){ - case EWOULDBLOCK: return 0; break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not iwrite data! Error: %s\n", strerror(errno)); - #endif - close(); - return 0; - break; - } - } - if (r == 0){ - #if DEBUG >= 4 - fprintf(stderr, "Could not iwrite data! Socket is closed.\n"); - #endif - close(); - } - up += r; - return r; -}//Socket::Connection::iwrite - -/// Incremental read call. This function tries to read len bytes to the buffer from the socket, -/// returning the amount of bytes it actually read. -/// \param buffer Location of the buffer to read to. -/// \param len Amount of bytes to read. -/// \returns The amount of bytes actually read. -int Socket::Connection::iread(void * buffer, int len){ - if (sock < 0){return 0;} - int r = recv(sock, buffer, len, 0); - if (r < 0){ - switch (errno){ - case EWOULDBLOCK: return 0; break; - default: - Error = true; - #if DEBUG >= 2 - fprintf(stderr, "Could not iread data! Error: %s\n", strerror(errno)); - #endif - close(); - return 0; - break; - } - } - if (r == 0){ - #if DEBUG >= 4 - fprintf(stderr, "Could not iread data! Socket is closed.\n"); - #endif - close(); - } - down += r; - return r; -}//Socket::Connection::iread - -/// Read call that is compatible with std::string. -/// Data is read using iread (which is nonblocking if the Socket::Connection itself is), -/// then appended to end of buffer. This functions reads at least one byte before returning. -/// \param buffer std::string to append data to. -/// \return True if new data arrived, false otherwise. -bool Socket::Connection::read(std::string & buffer){ - char cbuffer[5000]; - if (!read(cbuffer, 1)){return false;} - int num = iread(cbuffer+1, 4999); - if (num > 0){ - buffer.append(cbuffer, num+1); - }else{ - buffer.append(cbuffer, 1); - } - return true; -}//read - -/// Read call that is compatible with std::string. -/// Data is read using iread (which is nonblocking if the Socket::Connection itself is), -/// then appended to end of buffer. -/// \param buffer std::string to append data to. -/// \return True if new data arrived, false otherwise. -bool Socket::Connection::iread(std::string & buffer){ - char cbuffer[5000]; - int num = iread(cbuffer, 5000); - if (num < 1){return false;} - buffer.append(cbuffer, num); - return true; -}//iread - -/// Incremental write call that is compatible with std::string. -/// Data is written using iwrite (which is nonblocking if the Socket::Connection itself is), -/// then removed from front of buffer. -/// \param buffer std::string to remove data from. -/// \return True if more data was sent, false otherwise. -bool Socket::Connection::iwrite(std::string & buffer){ - if (buffer.size() < 1){return false;} - int tmp = iwrite((void*)buffer.c_str(), buffer.size()); - if (tmp < 1){return false;} - buffer = buffer.substr(tmp); - return true; -}//iwrite - -/// Write call that is compatible with std::string. -/// Data is written using write (which is always blocking), -/// then removed from front of buffer. -/// \param buffer std::string to remove data from. -/// \return True if more data was sent, false otherwise. -bool Socket::Connection::swrite(std::string & buffer){ - if (buffer.size() < 1){return false;} - bool tmp = write((void*)buffer.c_str(), buffer.size()); - if (tmp){buffer = "";} - return tmp; -}//write - -/// Gets hostname for connection, if available. -std::string Socket::Connection::getHost(){ - return remotehost; -} - -/// Returns true if these sockets are the same socket. -/// Does not check the internal stats - only the socket itself. -bool Socket::Connection::operator== (const Connection &B) const{ - return sock == B.sock; -} - -/// Returns true if these sockets are not the same socket. -/// Does not check the internal stats - only the socket itself. -bool Socket::Connection::operator!= (const Connection &B) const{ - return sock != B.sock; -} - -/// Returns true if the socket is valid. -/// Aliases for Socket::Connection::connected() -Socket::Connection::operator bool() const{ - return connected(); -} - -/// 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 - -/// Create a new TCP 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. -/// \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). -Socket::Server::Server(int port, std::string hostname, bool nonblock){ - if (!IPv6bind(port, hostname, nonblock) && !IPv4bind(port, hostname, nonblock)){ - fprintf(stderr, "Could not create socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); - sock = -1; - } -}//Socket::Server TCP Constructor - -/// Attempt to bind an IPv6 socket. -/// \param port The TCP port to listen on -/// \param hostname The interface to bind to. The default is 0.0.0.0 (all interfaces). -/// \param nonblock Whether accept() calls will be nonblocking. Default is false (blocking). -/// \return True if successful, false otherwise. -bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ - sock = socket(AF_INET6, SOCK_STREAM, 0); - if (sock < 0){ - #if DEBUG >= 1 - fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); - #endif - return false; - } - int on = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (nonblock){ - int flags = fcntl(sock, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(sock, F_SETFL, flags); - } - struct sockaddr_in6 addr; - addr.sin6_family = AF_INET6; - addr.sin6_port = htons(port);//set port - if (hostname == "0.0.0.0" || hostname.length() == 0){ - addr.sin6_addr = in6addr_any; - }else{ - inet_pton(AF_INET6, hostname.c_str(), &addr.sin6_addr);//set interface, 0.0.0.0 (default) is all - } - int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));//do the actual bind - if (ret == 0){ - ret = listen(sock, 100);//start listening, backlog of 100 allowed - if (ret == 0){ - return true; - }else{ - #if DEBUG >= 1 - fprintf(stderr, "IPv6 Listen failed! Error: %s\n", strerror(errno)); - #endif - close(); - return false; - } - }else{ - #if DEBUG >= 1 - fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); - #endif - close(); - return false; - } -} - -/// Attempt to bind an IPv4 socket. -/// \param port The TCP port to listen on -/// \param hostname The interface to bind to. The default is 0.0.0.0 (all interfaces). -/// \param nonblock Whether accept() calls will be nonblocking. Default is false (blocking). -/// \return True if successful, false otherwise. -bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0){ - #if DEBUG >= 1 - fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, strerror(errno)); - #endif - return false; - } - int on = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (nonblock){ - int flags = fcntl(sock, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(sock, F_SETFL, flags); - } - struct sockaddr_in addr4; - addr4.sin_family = AF_INET; - addr4.sin_port = htons(port);//set port - if (hostname == "0.0.0.0" || hostname.length() == 0){ - addr4.sin_addr.s_addr = INADDR_ANY; - }else{ - inet_pton(AF_INET, hostname.c_str(), &addr4.sin_addr);//set interface, 0.0.0.0 (default) is all - } - int ret = bind(sock, (sockaddr*)&addr4, sizeof(addr4));//do the actual bind - if (ret == 0){ - ret = listen(sock, 100);//start listening, backlog of 100 allowed - if (ret == 0){ - return true; - }else{ - #if DEBUG >= 1 - fprintf(stderr, "IPv4 Listen failed! Error: %s\n", strerror(errno)); - #endif - close(); - return false; - } - }else{ - #if DEBUG >= 1 - fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, strerror(errno)); - #endif - close(); - return false; - } -} - -/// 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). -Socket::Server::Server(std::string address, bool nonblock){ - unlink(address.c_str()); - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0){ - #if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", strerror(errno)); - #endif - return; - } - if (nonblock){ - int flags = fcntl(sock, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(sock, F_SETFL, flags); - } - sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, address.c_str(), address.size()+1); - int ret = bind(sock, (sockaddr*)&addr, sizeof(addr)); - if (ret == 0){ - ret = listen(sock, 100);//start listening, backlog of 100 allowed - if (ret == 0){ - return; - }else{ - #if DEBUG >= 1 - fprintf(stderr, "Listen failed! Error: %s\n", strerror(errno)); - #endif - close(); - return; - } - }else{ - #if DEBUG >= 1 - fprintf(stderr, "Binding failed! Error: %s\n", strerror(errno)); - #endif - 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. -Socket::Connection Socket::Server::accept(bool nonblock){ - if (sock < 0){return Socket::Connection(-1);} - struct sockaddr_in6 addrinfo; - socklen_t len = sizeof(addrinfo); - static char addrconv[INET6_ADDRSTRLEN]; - int r = ::accept(sock, (sockaddr*)&addrinfo, &len); - //set the socket to be nonblocking, if requested. - //we could do this through accept4 with a flag, but that call is non-standard... - if ((r >= 0) && nonblock){ - int flags = fcntl(r, F_GETFL, 0); - flags |= O_NONBLOCK; - fcntl(r, F_SETFL, flags); - } - Socket::Connection tmp(r); - if (r < 0){ - if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)){ - #if DEBUG >= 1 - fprintf(stderr, "Error during accept - closing server socket.\n"); - #endif - close(); - } - }else{ - if (addrinfo.sin6_family == AF_INET6){ - tmp.remotehost = inet_ntop(AF_INET6, &(addrinfo.sin6_addr), addrconv, INET6_ADDRSTRLEN); - #if DEBUG >= 6 - fprintf(stderr,"IPv6 addr: %s\n", tmp.remotehost.c_str()); - #endif - } - if (addrinfo.sin6_family == AF_INET){ - tmp.remotehost = inet_ntop(AF_INET, &(((sockaddr_in*)&addrinfo)->sin_addr), addrconv, INET6_ADDRSTRLEN); - #if DEBUG >= 6 - fprintf(stderr,"IPv4 addr: %s\n", tmp.remotehost.c_str()); - #endif - } - if (addrinfo.sin6_family == AF_UNIX){ - #if DEBUG >= 6 - tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; - fprintf(stderr,"Unix socket, no address\n"); - #endif - tmp.remotehost = "UNIX_SOCKET"; - } - } - return tmp; -} - -/// Close connection. The internal socket is closed and then set to -1. -/// If the connection is already closed, nothing happens. -void Socket::Server::close(){ - if (connected()){ - #if DEBUG >= 6 - fprintf(stderr, "ServerSocket closed.\n"); - #endif - shutdown(sock, SHUT_RDWR); - ::close(sock); - sock = -1; - } -}//Socket::Server::close - -/// Returns the connected-state for this socket. -/// Note that this function might be slightly behind the real situation. -/// The connection status is updated after every accept attempt, when errors occur -/// and when the socket is closed manually. -/// \returns True if socket is connected, false otherwise. -bool Socket::Server::connected() const{ - return (sock >= 0); -}//Socket::Server::connected - -/// Returns internal socket number. -int Socket::Server::getSocket(){return sock;} - -/// Connect to a stream on the system. -/// Filters the streamname, removing invalid characters and -/// converting all letters to lowercase. -/// If a '?' character is found, everything following that character is deleted. -Socket::Connection Socket::getStream(std::string streamname){ - //strip anything that isn't numbers, digits or underscores - 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 != '_'){ - streamname.erase(i); - }else{ - *i=tolower(*i); - } - } - return Socket::Connection("/tmp/mist/stream_"+streamname); -} - -/// Create a stream on the system. -/// Filters the streamname, removing invalid characters and -/// converting all letters to lowercase. -/// If a '?' character is found, everything following that character is deleted. -/// If the /tmp/ddvtech directory doesn't exist yet, this will create it. -Socket::Server Socket::makeStream(std::string streamname){ - //strip anything that isn't numbers, digits or underscores - 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 != '_'){ - streamname.erase(i); - }else{ - *i=tolower(*i); - } - } - std::string loc = "/tmp/mist/stream_"+streamname; - //attempt to create the /tmp/mist directory if it doesn't exist already. - //ignore errors - we catch all problems in the Socket::Server creation already - mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); - //create and return the Socket::Server - return Socket::Server(loc); -} diff --git a/lib/socket.h b/lib/socket.h deleted file mode 100644 index 0d33573b..00000000 --- a/lib/socket.h +++ /dev/null @@ -1,93 +0,0 @@ -/// \file socket.h -/// A handy Socket wrapper library. -/// Written by Jaron Vietor in 2010 for DDVTech - -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -///Holds Socket tools. -namespace Socket{ - - /// This class is for easy communicating through sockets, either TCP or Unix. - class Connection{ - private: - int sock; ///< Internally saved socket number. - std::string remotehost; ///< Stores remote host address. - unsigned int up; - unsigned int down; - unsigned int conntime; - std::string downbuffer; ///< Stores temporary data coming in. - std::string upbuffer; ///< Stores temporary data going out. - public: - Connection(); ///< Create a new disconnected base socket. - Connection(int sockNo); ///< Create a new base socket. - Connection(std::string hostname, int port, bool nonblock); ///< Create a new TCP socket. - Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket. - void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). - bool canRead(); ///< Calls poll() on the socket, checking if data is available. - bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. - signed int ready(); ///< Returns the ready-state for this socket. - bool connected() const; ///< Returns the connected-state for this socket. - bool read(const void * buffer, int len); ///< Reads data from socket. - bool read(const void * buffer, int width, int count); ///< Read call that is compatible with file access syntax. - bool write(const void * buffer, int len); ///< Writes data to socket. - bool write(const void * buffer, int width, int count); ///< Write call that is compatible with file access syntax. - bool write(const std::string data); ///< Write call that is compatible with std::string. - int iwrite(const void * buffer, int len); ///< Incremental write call. - int iread(void * buffer, int len); ///< Incremental read call. - bool read(std::string & buffer); ///< Read call that is compatible with std::string. - bool swrite(std::string & buffer); ///< Write call that is compatible with std::string. - bool iread(std::string & buffer); ///< Incremental write call that is compatible with std::string. - bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string. - bool spool(); ///< Updates the downbuffer and upbuffer internal variables. - std::string & Received(); ///< Returns a reference to the download buffer. - void Send(std::string data); ///< Appends data to the upbuffer. - void close(); ///< Close connection. - std::string getHost(); ///< Gets hostname for connection, if available. - int getSocket(); ///< Returns internal socket number. - std::string getError(); ///< Returns a string describing the last error that occured. - unsigned int dataUp(); ///< Returns total amount of bytes sent. - unsigned int dataDown(); ///< Returns total amount of bytes received. - std::string getStats(std::string C); ///< Returns a std::string of stats, ended by a newline. - friend class Server; - bool Error; ///< Set to true if a socket error happened. - bool Blocking; ///< Set to true if a socket is currently or wants to be blocking. - //overloaded operators - bool operator== (const Connection &B) const; - bool operator!= (const Connection &B) const; - operator bool() const; - }; - - /// This class is for easily setting up listening socket, either TCP or Unix. - class Server{ - private: - int sock; ///< Internally saved socket number. - bool IPv6bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv6 socket - bool IPv4bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv4 socket - public: - Server(); ///< Create a new base Server. - Server(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP Server. - Server(std::string adres, bool nonblock = false); ///< Create a new Unix Server. - Connection accept(bool nonblock = false); ///< Accept any waiting connections. - bool connected() const; ///< Returns the connected-state for this socket. - void close(); ///< Close connection. - int getSocket(); ///< Returns internal socket number. - }; - - /// Connect to a stream on the system. - Connection getStream(std::string streamname); - - /// Create a stream on the system. - Server makeStream(std::string streamname); - -}; diff --git a/src/Makefile.am b/src/Makefile.am index e1c6b4ed..dac4d77b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,12 +1,9 @@ +AM_CPPFLAGS = $(MIST_CFLAGS) +LDADD = $(MIST_LIBS) SUBDIRS=converters analysers bin_PROGRAMS=MistBuffer MistController MistConnRAW MistConnRTMP MistConnHTTP -MistBuffer_SOURCES=buffer.cpp buffer_user.h buffer_user.cpp buffer_stream.h buffer_stream.cpp ../VERSION -MistBuffer_LDADD=../lib/libjson.la ../lib/libsocket.la ../lib/libdtsc.la ../lib/libtinythread.la +MistBuffer_SOURCES=buffer.cpp buffer_user.h buffer_user.cpp buffer_stream.h buffer_stream.cpp tinythread.cpp tinythread.h ../VERSION MistController_SOURCES=controller.cpp ../VERSION -MistController_LDADD=../lib/libjson.la ../lib/libsocket.la ../lib/libprocs.la ../lib/libconfig.la ../lib/libhttp_parser.la ../lib/libauth.la ../lib/libbase64.la MistConnRAW_SOURCES=conn_raw.cpp ../VERSION -MistConnRAW_LDADD=../lib/libsocket.la MistConnRTMP_SOURCES=conn_rtmp.cpp server_setup.h ../VERSION -MistConnRTMP_LDADD=../lib/libdtsc.la ../lib/librtmpchunks.la ../lib/libconfig.la MistConnHTTP_SOURCES=conn_http.cpp server_setup.h ../VERSION -MistConnHTTP_LDADD=../lib/libdtsc.la ../lib/libflv_tag.la ../lib/libjson.la ../lib/libhttp_parser.la ../lib/libmp4.la ../lib/libprocs.la ../lib/libbase64.la ../lib/libconfig.la diff --git a/src/analysers/Makefile.am b/src/analysers/Makefile.am index f7d8be2e..342282c7 100644 --- a/src/analysers/Makefile.am +++ b/src/analysers/Makefile.am @@ -1,9 +1,7 @@ +AM_CPPFLAGS = $(MIST_CFLAGS) +LDADD = $(MIST_LIBS) bin_PROGRAMS=MistAnalyserRTMP MistAnalyserFLV MistAnalyserDTSC MistAnalyserAMF MistAnalyserRTMP_SOURCES=rtmp_analyser.cpp -MistAnalyserRTMP_LDADD=../../lib/librtmpchunks.la ../../lib/libdtsc.la ../../lib/libkeycrypto.la MistAnalyserFLV_SOURCES=flv_analyser.cpp -MistAnalyserFLV_LDADD=../../lib/libflv_tag.la ../../lib/libdtsc.la MistAnalyserDTSC_SOURCES=dtsc_analyser.cpp -MistAnalyserDTSC_LDADD=../../lib/libdtsc.la MistAnalyserAMF_SOURCES=amf_analyser.cpp -MistAnalyserAMF_LDADD=../../lib/libamf.la diff --git a/src/analysers/amf_analyser.cpp b/src/analysers/amf_analyser.cpp index 1153f9b1..8e7075e7 100644 --- a/src/analysers/amf_analyser.cpp +++ b/src/analysers/amf_analyser.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "../../lib/amf.h" +#include /// Debugging tool for AMF data. /// Expects AMF data through stdin, outputs human-readable information to stderr. diff --git a/src/analysers/dtsc_analyser.cpp b/src/analysers/dtsc_analyser.cpp index 2f6fea92..5c8d911f 100644 --- a/src/analysers/dtsc_analyser.cpp +++ b/src/analysers/dtsc_analyser.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "../../lib/dtsc.h" //DTSC support +#include //DTSC support /// Reads DTSC from stdin and outputs human-readable information to stderr. int main() { diff --git a/src/analysers/flv_analyser.cpp b/src/analysers/flv_analyser.cpp index 04f53660..9892349f 100644 --- a/src/analysers/flv_analyser.cpp +++ b/src/analysers/flv_analyser.cpp @@ -10,7 +10,7 @@ #include #include #include -#include "../../lib/flv_tag.h" //FLV support +#include //FLV support /// Reads FLV from stdin and outputs human-readable information to stderr. int main() { diff --git a/src/analysers/rtmp_analyser.cpp b/src/analysers/rtmp_analyser.cpp index fe87d229..81b8474d 100644 --- a/src/analysers/rtmp_analyser.cpp +++ b/src/analysers/rtmp_analyser.cpp @@ -14,9 +14,9 @@ #include #include #include -#include "../../lib/flv_tag.h" -#include "../../lib/amf.h" -#include "../../lib/rtmpchunks.h" +#include +#include +#include int Detail = 0; #define DETAIL_RECONSTRUCT 1 diff --git a/src/buffer_stream.h b/src/buffer_stream.h index 3cc9b4d1..a4d340e2 100644 --- a/src/buffer_stream.h +++ b/src/buffer_stream.h @@ -3,8 +3,8 @@ #pragma once #include -#include "../lib/tinythread.h" -#include "../lib/json.h" +#include +#include "tinythread.h" #include "buffer_user.h" namespace Buffer{ diff --git a/src/buffer_user.h b/src/buffer_user.h index 9cfb950a..dd85ce39 100644 --- a/src/buffer_user.h +++ b/src/buffer_user.h @@ -3,9 +3,9 @@ #pragma once #include -#include "../lib/dtsc.h" -#include "../lib/socket.h" -#include "../lib/tinythread.h" +#include +#include +#include "tinythread.h" namespace Buffer{ /// Converts a stats line to up, down, host, connector and conntime values. diff --git a/src/conn_http.cpp b/src/conn_http.cpp index 4adf8785..789005ae 100644 --- a/src/conn_http.cpp +++ b/src/conn_http.cpp @@ -11,14 +11,14 @@ #include #include #include -#include "../lib/socket.h" -#include "../lib/http_parser.h" -#include "../lib/json.h" -#include "../lib/dtsc.h" -#include "../lib/flv_tag.h" -#include "../lib/base64.h" -#include "../lib/amf.h" -#include "../lib/mp4.h" +#include +#include +#include +#include +#include +#include +#include +#include /// Holds everything unique to HTTP Connector. namespace Connector_HTTP{ diff --git a/src/conn_http_dynamic.cpp b/src/conn_http_dynamic.cpp new file mode 100644 index 00000000..64bec69d --- /dev/null +++ b/src/conn_http_dynamic.cpp @@ -0,0 +1,235 @@ +/// \file conn_http_dynamic.cpp +/// Contains the main code for the HTTP Dynamic Connector + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// Holds everything unique to HTTP Dynamic Connector. +namespace Connector_HTTP_Dynamic{ + + /// Returns AMF-format metadata + std::string GetMetaData( ) { + /// \todo Make this actually do what it should - even though it seems to be ignored completely by all media players. + AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); + amfreply.addContent(AMF::Object("onMetaData",AMF::AMF0_STRING)); + amfreply.addContent(AMF::Object("",AMF::AMF0_ECMA_ARRAY)); + amfreply.getContentP(1)->addContent(AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY)); + amfreply.getContentP(1)->getContentP(0)->addContent(AMF::Object("arrVal")); + //amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("timescale",(double)1000)); + //amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("length",(double)59641700)); + amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("language","eng")); + amfreply.getContentP(1)->getContentP(0)->getContentP(0)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); + amfreply.getContentP(1)->getContentP(0)->getContentP(0)->getContentP(1)->addContent(AMF::Object("arrVal")); + amfreply.getContentP(1)->getContentP(0)->getContentP(0)->getContentP(1)->getContentP(0)->addContent(AMF::Object("sampletype","avc1")); + amfreply.getContentP(1)->getContentP(0)->addContent(AMF::Object("arrVal")); + //amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("timescale",(double)44100)); + //amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("length",(double)28630000)); + amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("language","eng")); + amfreply.getContentP(1)->getContentP(0)->getContentP(1)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); + amfreply.getContentP(1)->getContentP(0)->getContentP(1)->getContentP(1)->addContent(AMF::Object("arrVal")); + amfreply.getContentP(1)->getContentP(0)->getContentP(1)->getContentP(1)->getContentP(0)->addContent(AMF::Object("sampletype","mp4a")); + amfreply.getContentP(1)->addContent(AMF::Object("audiochannels",(double)2)); + amfreply.getContentP(1)->addContent(AMF::Object("audiosamplerate",(double)44100)); + amfreply.getContentP(1)->addContent(AMF::Object("videoframerate",(double)25)); + amfreply.getContentP(1)->addContent(AMF::Object("aacaot",(double)2)); + amfreply.getContentP(1)->addContent(AMF::Object("avclevel",(double)12)); + amfreply.getContentP(1)->addContent(AMF::Object("avcprofile",(double)77)); + amfreply.getContentP(1)->addContent(AMF::Object("audiocodecid","mp4a")); + amfreply.getContentP(1)->addContent(AMF::Object("videocodecid","avc1")); + amfreply.getContentP(1)->addContent(AMF::Object("width",(double)1280)); + amfreply.getContentP(1)->addContent(AMF::Object("height",(double)720)); + amfreply.getContentP(1)->addContent(AMF::Object("frameWidth",(double)1280)); + amfreply.getContentP(1)->addContent(AMF::Object("frameHeight",(double)720)); + amfreply.getContentP(1)->addContent(AMF::Object("displayWidth",(double)1280)); + amfreply.getContentP(1)->addContent(AMF::Object("displayHeight",(double)720)); + //amfreply.getContentP(1)->addContent(AMF::Object("moovposition",(double)35506700)); + //amfreply.getContentP(1)->addContent(AMF::Object("duration",(double)596.458)); + return amfreply.Pack( ); + }//getMetaData + + /// Returns a F4M-format manifest file + std::string BuildManifest(std::string MovieId) { + std::string Result="\n" + + "\n" + + "" + MovieId + "\n" + + "video/mp4\n" + + "live\n" + + "streaming\n" + + "" + Base64::encode(MP4::GenerateLiveBootstrap(1)) + "\n" + + "\n" + + "" + Base64::encode(GetMetaData()) + "\n" + + "\n" + + "\n"; + return Result; + }//BuildManifest + + /// Main function for Connector_HTTP_Dynamic + int Connector_HTTP_Dynamic(Socket::Connection conn){ + std::string FlashBuf; + FLV::Tag tmp;//temporary tag, for init data + + std::queue Flash_FragBuffer;//Fragment buffer + DTSC::Stream Strm;//Incoming stream buffer. + HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. + + bool ready4data = false;//Set to true when streaming is to begin. + bool inited = false; + Socket::Connection ss(-1); + std::string streamname; + FLV::Tag tag;///< Temporary tag buffer. + std::string recBuffer = ""; + + std::string Movie; + std::string Quality; + int Segment = -1; + int ReqFragment = -1; + int temp; + int Flash_RequestPending = 0; + unsigned int lastStats = 0; + conn.setBlocking(false);//do not block on conn.spool() when no data is available + + while (conn.connected()){ + if (conn.spool()){ + if (HTTP_R.Read(conn.Received())){ + #if DEBUG >= 4 + std::cout << "Received request: " << HTTP_R.url << std::endl; + #endif + if (HTTP_R.url.find("f4m") == std::string::npos){ + Movie = HTTP_R.url.substr(1); + Movie = Movie.substr(0,Movie.find("/")); + Quality = HTTP_R.url.substr( HTTP_R.url.find("/",1)+1 ); + Quality = Quality.substr(0, Quality.find("Seg")); + temp = HTTP_R.url.find("Seg") + 3; + Segment = atoi( HTTP_R.url.substr(temp,HTTP_R.url.find("-",temp)-temp).c_str()); + temp = HTTP_R.url.find("Frag") + 4; + ReqFragment = atoi( HTTP_R.url.substr(temp).c_str() ); + #if DEBUG >= 4 + printf( "Quality: %s, Seg %d Frag %d\n", Quality.c_str(), Segment, ReqFragment); + #endif + Flash_RequestPending++; + }else{ + Movie = HTTP_R.url.substr(1); + Movie = Movie.substr(0,Movie.find("/")); + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type","text/xml"); + HTTP_S.SetHeader("Cache-Control","no-cache"); + std::string manifest = BuildManifest(Movie); + HTTP_S.SetBody(manifest); + conn.Send(HTTP_S.BuildResponse("200", "OK")); + #if DEBUG >= 3 + printf("Sent manifest\n"); + #endif + } + ready4data = true; + HTTP_R.Clean(); //clean for any possible next requests + }else{ + #if DEBUG >= 3 + fprintf(stderr, "Could not parse the following:\n%s\n", conn.Received().c_str()); + #endif + } + } + if (ready4data){ + if (!inited){ + //we are ready, connect the socket! + ss = Socket::getStream(streamname); + if (!ss.connected()){ + #if DEBUG >= 1 + fprintf(stderr, "Could not connect to server!\n"); + #endif + ss.close(); + HTTP_S.Clean(); + HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); + conn.Send(HTTP_S.BuildResponse("404", "Not found")); + ready4data = false; + continue; + } + #if DEBUG >= 3 + fprintf(stderr, "Everything connected, starting to send video data...\n"); + #endif + inited = true; + } + if ((Flash_RequestPending > 0) && !Flash_FragBuffer.empty()){ + HTTP_S.Clean(); + HTTP_S.SetHeader("Content-Type","video/mp4"); + HTTP_S.SetBody(MP4::mdatFold(Flash_FragBuffer.front())); + Flash_FragBuffer.pop(); + conn.Send(HTTP_S.BuildResponse("200", "OK")); + Flash_RequestPending--; + #if DEBUG >= 3 + fprintf(stderr, "Sending a video fragment. %i left in buffer, %i requested\n", (int)Flash_FragBuffer.size(), Flash_RequestPending); + #endif + } + unsigned int now = time(0); + if (now != lastStats){ + lastStats = now; + ss.Send("S "+conn.getStats("HTTP_Dynamic")); + } + if (ss.spool() || ss.Received() != ""){ + if (Strm.parsePacket(ss.Received())){ + tag.DTSCLoader(Strm); + if (Strm.getPacket(0).getContentP("keyframe")){ + if (FlashBuf != ""){ + Flash_FragBuffer.push(FlashBuf); + while (Flash_FragBuffer.size() > 2){ + Flash_FragBuffer.pop(); + } + #if DEBUG >= 4 + fprintf(stderr, "Received a fragment. Now %i in buffer.\n", (int)Flash_FragBuffer.size()); + #endif + } + FlashBuf.clear(); + //fill buffer with init data, if needed. + if (Strm.metadata.getContentP("audio") && Strm.metadata.getContentP("audio")->getContentP("init")){ + tmp.DTSCAudioInit(Strm); + FlashBuf.append(tmp.data, tmp.len); + } + if (Strm.metadata.getContentP("video") && Strm.metadata.getContentP("video")->getContentP("init")){ + tmp.DTSCVideoInit(Strm); + FlashBuf.append(tmp.data, tmp.len); + } + } + FlashBuf.append(tag.data, tag.len); + } + } + } + } + conn.close(); + ss.close(); + #if DEBUG >= 1 + if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());} + fprintf(stderr, "User %i disconnected.\n", conn.getSocket()); + if (inited){ + fprintf(stderr, "Status was: inited\n"); + }else{ + if (ready4data){ + fprintf(stderr, "Status was: ready4data\n"); + }else{ + fprintf(stderr, "Status was: connected\n"); + } + } + #endif + return 0; + }//Connector_HTTP_Dynamic main function + +};//Connector_HTTP_Dynamic namespace + +// Load http setup file with the correct settings for this HTTP connector +#define MAINHANDLER Connector_HTTP_Dynamic::Connector_HTTP_Dynamic +#define CONNECTOR dynamic +#include "server_setup_http.h" diff --git a/src/conn_http_progressive.cpp b/src/conn_http_progressive.cpp new file mode 100644 index 00000000..6ac9268c --- /dev/null +++ b/src/conn_http_progressive.cpp @@ -0,0 +1,145 @@ +/// \file conn_http_progressive.cpp +/// Contains the main code for the HTTP Progressive Connector + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// Holds everything unique to HTTP Progressive Connector. +namespace Connector_HTTP_Progressive{ + + /// Main function for Connector_HTTP_Progressive + int Connector_HTTP_Progressive(Socket::Connection conn){ + bool progressive_has_sent_header = false; + bool ready4data = false;///< Set to true when streaming is to begin. + DTSC::Stream Strm;///< Incoming stream buffer. + HTTP::Parser HTTP_R, HTTP_S;///= 4 + std::cout << "Received request: " << HTTP_R.url << std::endl; + #endif + //we assume the URL is the stream name with a 3 letter extension + std::string extension = HTTP_R.url.substr(HTTP_R.url.size()-4); + Movie = HTTP_R.url.substr(0, HTTP_R.url.size()-4);//strip the extension + /// \todo VoD streams will need support for position reading from the URL parameters + ready4data = true; + HTTP_R.Clean(); //clean for any possible next requests + }else{ + #if DEBUG >= 3 + fprintf(stderr, "Could not parse the following:\n%s\n", conn.Received().c_str()); + #endif + } + } + if (ready4data){ + if (!inited){ + //we are ready, connect the socket! + ss = Socket::getStream(streamname); + if (!ss.connected()){ + #if DEBUG >= 1 + fprintf(stderr, "Could not connect to server!\n"); + #endif + ss.close(); + HTTP_S.Clean(); + HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); + conn.Send(HTTP_S.BuildResponse("404", "Not found")); + ready4data = false; + continue; + } + #if DEBUG >= 3 + fprintf(stderr, "Everything connected, starting to send video data...\n"); + #endif + inited = true; + } + unsigned int now = time(0); + if (now != lastStats){ + lastStats = now; + ss.Send("S "+conn.getStats("HTTP_Progressive")); + } + if (ss.spool() || ss.Received() != ""){ + if (Strm.parsePacket(ss.Received())){ + tag.DTSCLoader(Strm); + if (!progressive_has_sent_header){ + HTTP_S.Clean();//make sure no parts of old requests are left in any buffers + HTTP_S.SetHeader("Content-Type", "video/x-flv");//Send the correct content-type for FLV files + //HTTP_S.SetHeader("Transfer-Encoding", "chunked"); + HTTP_S.protocol = "HTTP/1.0"; + conn.Send(HTTP_S.BuildResponse("200", "OK"));//no SetBody = unknown length - this is intentional, we will stream the entire file + conn.Send(std::string(FLV::Header, 13));//write FLV header + static FLV::Tag tmp; + //write metadata + tmp.DTSCMetaInit(Strm); + conn.Send(std::string(tmp.data, tmp.len)); + //write video init data, if needed + if (Strm.metadata.getContentP("video") && Strm.metadata.getContentP("video")->getContentP("init")){ + tmp.DTSCVideoInit(Strm); + conn.Send(std::string(tmp.data, tmp.len)); + } + //write audio init data, if needed + if (Strm.metadata.getContentP("audio") && Strm.metadata.getContentP("audio")->getContentP("init")){ + tmp.DTSCAudioInit(Strm); + conn.Send(std::string(tmp.data, tmp.len)); + } + progressive_has_sent_header = true; + #if DEBUG >= 1 + fprintf(stderr, "Sent progressive FLV header\n"); + #endif + } + conn.Send(std::string(tag.data, tag.len));//write the tag contents + } + } + } + } + conn.close(); + ss.close(); + #if DEBUG >= 1 + if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());} + fprintf(stderr, "User %i disconnected.\n", conn.getSocket()); + if (inited){ + fprintf(stderr, "Status was: inited\n"); + }else{ + if (ready4data){ + fprintf(stderr, "Status was: ready4data\n"); + }else{ + fprintf(stderr, "Status was: connected\n"); + } + } + #endif + return 0; + }//Connector_HTTP main function + +};//Connector_HTTP namespace + +// Load http setup file with the correct settings for this HTTP connector +#define MAINHANDLER Connector_HTTP_Progressive::Connector_HTTP_Progressive +#define CONNECTOR progressive +#include "server_setup_http.h" diff --git a/src/conn_raw.cpp b/src/conn_raw.cpp index b131c6ce..0044606f 100644 --- a/src/conn_raw.cpp +++ b/src/conn_raw.cpp @@ -2,7 +2,7 @@ /// Contains the main code for the RAW connector. #include -#include "../lib/socket.h" +#include /// Contains the main code for the RAW connector. /// Expects a single commandline argument telling it which stream to connect to, diff --git a/src/conn_rtmp.cpp b/src/conn_rtmp.cpp index 1ca51817..5f574015 100644 --- a/src/conn_rtmp.cpp +++ b/src/conn_rtmp.cpp @@ -11,10 +11,10 @@ #include #include #include -#include "../lib/socket.h" -#include "../lib/flv_tag.h" -#include "../lib/amf.h" -#include "../lib/rtmpchunks.h" +#include +#include +#include +#include /// Holds all functions and data unique to the RTMP Connector namespace Connector_RTMP{ diff --git a/src/controller.cpp b/src/controller.cpp index 9a36b559..26e3b4b5 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -25,12 +25,12 @@ #include #include #include -#include "../lib/socket.h" -#include "../lib/http_parser.h" -#include "../lib/json.h" -#include "../lib/procs.h" -#include "../lib/config.h" -#include "../lib/auth.h" +#include +#include +#include +#include +#include +#include #define UPLINK_INTERVAL 30 diff --git a/src/converters/Makefile.am b/src/converters/Makefile.am index 6007657d..4bd17aee 100644 --- a/src/converters/Makefile.am +++ b/src/converters/Makefile.am @@ -1,5 +1,5 @@ +AM_CPPFLAGS = $(MIST_CFLAGS) +LDADD = $(MIST_LIBS) bin_PROGRAMS=MistDTSC2FLV MistFLV2DTSC MistDTSC2FLV_SOURCES=dtsc2flv.cpp -MistDTSC2FLV_LDADD=../../lib/libdtsc.la ../../lib/libflv_tag.la MistFLV2DTSC_SOURCES=flv2dtsc.cpp -MistFLV2DTSC_LDADD=../../lib/libdtsc.la ../../lib/libflv_tag.la diff --git a/src/converters/dtsc2flv.cpp b/src/converters/dtsc2flv.cpp index 78306c6f..b7cadd7a 100644 --- a/src/converters/dtsc2flv.cpp +++ b/src/converters/dtsc2flv.cpp @@ -10,9 +10,9 @@ #include #include #include -#include "../../lib/flv_tag.h" //FLV support -#include "../../lib/dtsc.h" //DTSC support -#include "../../lib/amf.h" //AMF support +#include //FLV support +#include //DTSC support +#include //AMF support /// Holds all code that converts filetypes to/from DTSC. namespace Converters{ diff --git a/src/converters/flv2dtsc.cpp b/src/converters/flv2dtsc.cpp index 4030ac09..1045b320 100644 --- a/src/converters/flv2dtsc.cpp +++ b/src/converters/flv2dtsc.cpp @@ -10,9 +10,9 @@ #include #include #include -#include "../../lib/flv_tag.h" //FLV support -#include "../../lib/dtsc.h" //DTSC support -#include "../../lib/amf.h" //AMF support +#include //FLV support +#include //DTSC support +#include //AMF support /// Holds all code that converts filetypes to/from to DTSC. namespace Converters{ diff --git a/src/server_setup.h b/src/server_setup.h index af7dec29..8eb16daa 100644 --- a/src/server_setup.h +++ b/src/server_setup.h @@ -1,5 +1,5 @@ /// \file server_setup.h -/// Contains generic functions for setting up a DDVTECH Connector. +/// Contains generic functions for setting up a Connector. #ifndef MAINHANDLER /// Handler that is called for accepted incoming connections. @@ -15,8 +15,8 @@ #endif -#include "../lib/socket.h" //Socket library -#include "../lib/config.h" //utilities for config management +#include //Socket library +#include //utilities for config management #include #include #include diff --git a/src/server_setup_http.h b/src/server_setup_http.h new file mode 100644 index 00000000..48c91be7 --- /dev/null +++ b/src/server_setup_http.h @@ -0,0 +1,112 @@ +/// \file server_setup_http.h +/// Contains generic functions for setting up a HTTP Connector. + +#ifndef MAINHANDLER + /// Handler that is called for accepted incoming connections. + #define MAINHANDLER NoHandler + #error "No handler was set!" +#endif + +#ifndef CONNECTOR + /// Connector name for the socket. + #define CONNECTOR NoConnector + #error "No connector was set!" +#endif + +#include //Socket library +#include //utilities for config management +#include +#include +#include +#include +Socket::Server server_socket; ///< Placeholder for the server socket + +/// Basic signal handler. Disconnects the server_socket if it receives +/// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE. +/// Disconnecting the server_socket will terminate the main listening loop +/// and cleanly shut down the process. +void signal_handler (int signum){ + switch (signum){ + case SIGINT: + #if DEBUG >= 1 + fprintf(stderr, "Received SIGINT - closing server socket.\n"); + #endif + break; + case SIGHUP: + #if DEBUG >= 1 + fprintf(stderr, "Received SIGHUP - closing server socket.\n"); + #endif + break; + case SIGTERM: + #if DEBUG >= 1 + fprintf(stderr, "Received SIGTERM - closing server socket.\n"); + #endif + break; + case SIGCHLD: + wait(0); + return; + break; + default: return; break; + } + if (!server_socket.connected()) return; + server_socket.close(); +}//signal_handler + +/// Generic main entry point and loop for DDV HTTP-based Connectors. +/// This sets up the proper termination handler, checks commandline options, +/// parses config files and opens a listening socket. +/// Any incoming connections will be accepted and start up the function #MAINHANDLER, +/// which should be defined before including server_setup_http.cpp. +/// The connector name is set by define #CONNECTOR. +int main(int argc, char ** argv){ + Socket::Connection S;//placeholder for incoming connections + + //setup signal handler + struct sigaction new_action; + new_action.sa_handler = signal_handler; + sigemptyset (&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGINT, &new_action, NULL); + sigaction(SIGHUP, &new_action, NULL); + sigaction(SIGTERM, &new_action, NULL); + sigaction(SIGPIPE, &new_action, NULL); + sigaction(SIGCHLD, &new_action, NULL); + + //set and parse configuration + Util::Config C; + C.parseArgs(argc, argv); + + //setup a new server socket, for the correct interface and port + server_socket = Socket::Server("/tmp/mist/http_" CONNECTOR); + if (!server_socket.connected()){ + #if DEBUG >= 1 + fprintf(stderr, "Error: could not make listening socket\n"); + #endif + return 1; + }else{ + #if DEBUG >= 3 + fprintf(stderr, "Made a listening socket on %s:%i...\n", C.interface.c_str(), C.listen_port); + #endif + } + + Util::setUser(C.username); + if (C.daemon_mode){Util::Daemonize();} + + while (server_socket.connected()){ + S = server_socket.accept(); + if (S.connected()){//check if the new connection is valid + pid_t myid = fork(); + if (myid == 0){//if new child, start MAINHANDLER + return MAINHANDLER(S); + }else{//otherwise, do nothing or output debugging text + #if DEBUG >= 3 + fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); + #endif + } + } + }//while connected + #if DEBUG >= 1 + fprintf(stderr, "Server socket closed, exiting.\n"); + #endif + return 0; +}//main diff --git a/lib/tinythread.cpp b/src/tinythread.cpp similarity index 100% rename from lib/tinythread.cpp rename to src/tinythread.cpp diff --git a/lib/tinythread.h b/src/tinythread.h similarity index 100% rename from lib/tinythread.h rename to src/tinythread.h