diff --git a/Makefile b/Makefile index 4aed5262..633f54ac 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,9 @@ endif CPPFLAGS = -Wall -g -O2 -fPIC override CPPFLAGS += -funsigned-char -DDEBUG="$(DEBUG)" -DPACKAGE_VERSION="\"$(PACKAGE_VERSION)\"" -LDLIBS = -lcrypto -lrt +LDLIBS = -lcrypto +THREADLIB = -lpthread -lrt +LDLIBS = -lcrypto $(THREADLIB) .DEFAULT_GOAL := all diff --git a/README b/README index ed78f84a..1af6710e 100644 --- a/README +++ b/README @@ -6,6 +6,9 @@ | See COPYING file for full license | |_________________________________________________| +NOTE: TinyThread++ is included also, but *not* copyright DDVTech BV. +License and author information for TinyThread++ can be found in the tinythread.h/cpp files. + The latest version of this code can always be found at: https://github.com/DDVTECH/mistlib diff --git a/lib/amf.cpp b/lib/amf.cpp index dcd163e5..65c67200 100644 --- a/lib/amf.cpp +++ b/lib/amf.cpp @@ -2,8 +2,8 @@ /// Holds all code for the AMF namespace. #include "amf.h" +#include "defines.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(){ @@ -124,9 +124,9 @@ AMF::Object::Object(std::string indice, AMF::obj0type setType){ //object type in numval = 0; } -/// Prints the contents of this object to std::cerr. +/// Return the contents as a human-readable string. /// If this object contains other objects, it will call itself recursively -/// and print all nested content in a nice human-readable format. +/// and print all nested content as well. std::string AMF::Object::Print(std::string indent){ std::stringstream st; st << indent; @@ -368,9 +368,6 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign unsigned int tmpi = 0; unsigned char tmpdbl[8]; double *d; // hack to work around strict aliasing -#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]; @@ -496,9 +493,7 @@ AMF::Object AMF::parseOne(const unsigned char *& data, unsigned int &len, unsign } break; } -#if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); -#endif + DEBUG_MSG(DLVL_ERROR, "Error: Unimplemented AMF type %hhx - returning.", data[i]); return AMF::Object("error", AMF::AMF0_DDV_CONTAINER); } //parseOne @@ -668,99 +663,101 @@ AMF::Object3::Object3(std::string indice, AMF::obj3type setType){ //object type intval = 0; } -/// Prints the contents of this object to std::cerr. +/// Return the contents as a human-readable string. /// 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; +/// and print all nested content as well. +std::string AMF::Object3::Print(std::string indent){ + std::stringstream st; + st << indent; // print my type switch (myType){ case AMF::AMF3_UNDEFINED: - std::cerr << "Undefined"; + st << "Undefined"; break; case AMF::AMF3_NULL: - std::cerr << "Null"; + st << "Null"; break; case AMF::AMF3_FALSE: - std::cerr << "False"; + st << "False"; break; case AMF::AMF3_TRUE: - std::cerr << "True"; + st << "True"; break; case AMF::AMF3_INTEGER: - std::cerr << "Integer"; + st << "Integer"; break; case AMF::AMF3_DOUBLE: - std::cerr << "Double"; + st << "Double"; break; case AMF::AMF3_STRING: - std::cerr << "String"; + st << "String"; break; case AMF::AMF3_XMLDOC: - std::cerr << "XML Doc"; + st << "XML Doc"; break; case AMF::AMF3_DATE: - std::cerr << "Date"; + st << "Date"; break; case AMF::AMF3_ARRAY: - std::cerr << "Array"; + st << "Array"; break; case AMF::AMF3_OBJECT: - std::cerr << "Object"; + st << "Object"; break; case AMF::AMF3_XML: - std::cerr << "XML"; + st << "XML"; break; case AMF::AMF3_BYTES: - std::cerr << "ByteArray"; + st << "ByteArray"; break; case AMF::AMF3_DDV_CONTAINER: - std::cerr << "DDVTech Container"; + st << "DDVTech Container"; break; } // print my string indice, if available - std::cerr << " " << myIndice << " "; + st << " " << myIndice << " "; // print my numeric or string contents switch (myType){ case AMF::AMF3_INTEGER: - std::cerr << intval; + st << intval; break; case AMF::AMF3_DOUBLE: - std::cerr << dblval; + st << 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; + st << "REF" << intval; }else{ - std::cerr << strval; + st << strval; } break; case AMF::AMF3_DATE: if (intval > 0){ - std::cerr << "REF" << intval; + st << "REF" << intval; }else{ - std::cerr << dblval; + st << dblval; } break; case AMF::AMF3_ARRAY: case AMF::AMF3_OBJECT: if (intval > 0){ - std::cerr << "REF" << intval; + st << "REF" << intval; } break; default: break; //we don't care about the rest, and don't want a compiler warning... } - std::cerr << std::endl; + 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++){ - it->Print(indent + " "); + st << it->Print(indent + " "); } } + return st.str(); } //print /// Packs the AMF object to a std::string for transfer over the network. @@ -784,9 +781,6 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi unsigned int arrsize = 0; unsigned char tmpdbl[8]; double *d; // hack to work around strict aliasing -#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: @@ -1121,9 +1115,7 @@ AMF::Object3 AMF::parseOne3(const unsigned char *& data, unsigned int &len, unsi } break; } -#if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented AMF3 type %hhx - returning.\n", data[i]); -#endif + DEBUG_MSG(DLVL_ERROR, "Error: Unimplemented AMF3 type %hhx - returning.", data[i]); return AMF::Object3("error", AMF::AMF3_DDV_CONTAINER); } //parseOne diff --git a/lib/amf.h b/lib/amf.h index f797a6ba..b805a496 100644 --- a/lib/amf.h +++ b/lib/amf.h @@ -108,7 +108,7 @@ namespace AMF { 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 Print(std::string indent = ""); std::string Pack(); protected: std::string myIndice; ///< Holds this objects indice, if any. diff --git a/lib/bitstream.cpp b/lib/bitstream.cpp index 7994f00a..cec44b34 100644 --- a/lib/bitstream.cpp +++ b/lib/bitstream.cpp @@ -1,7 +1,7 @@ -#include"bitstream.h" -#include -#include -#include +#include "bitstream.h" +#include "defines.h" +#include +#include namespace Utils{ bitstream::bitstream(){ @@ -31,7 +31,6 @@ namespace Utils{ memcpy(data+dataSize, input, bytes); dataSize += bytes; } - //std::cout << std::hex << std::string(data, dataSize) << std::dec << std::endl; } void bitstream::append(std::string input){ @@ -45,11 +44,11 @@ namespace Utils{ long long unsigned int bitstream::peek(size_t count){ if (count > 64){ - std::cerr << "Utils::bitstream: Warning; Can not read "<< count <<" bits into a long long unsigned int!" << std::endl; + DEBUG_MSG(DLVL_WARN, "Can not read %d bits into a long long unsigned int!", (int)count); //return 0; } if (count > size()){ - std::cerr << "Utils::bitstream: not enough bits left in stream. Left: " << size() << " requested: " << count << std::endl; + DEBUG_MSG(DLVL_ERROR, "Not enough bits left in stream. Left: %d requested: %d", (int)size(), (int)count); return 0; } long long unsigned int retval = 0; diff --git a/lib/config.cpp b/lib/config.cpp index 0c53ac11..575fbbcb 100644 --- a/lib/config.cpp +++ b/lib/config.cpp @@ -2,6 +2,9 @@ /// Contains generic functions for managing configuration. #include "config.h" +#include "defines.h" +#include "timing.h" +#include "tinythread.h" #include #include @@ -22,7 +25,6 @@ #include #include #include -#include #include #include //for getMyExec @@ -295,6 +297,46 @@ bool Util::Config::getBool(std::string optname){ return getOption(optname).asBool(); } +struct callbackData{ + Socket::Connection * sock; + void (*cb)(Socket::Connection &); +}; + +static void callThreadCallback(void * cDataArg){ + DEBUG_MSG(DLVL_INSANE, "Thread for %p started", cDataArg); + callbackData * cData = (callbackData*)cDataArg; + cData->cb(*(cData->sock)); + cData->sock->close(); + delete cData->sock; + delete cData; + DEBUG_MSG(DLVL_INSANE, "Thread for %p ended", cDataArg); +} + +int Util::Config::serveThreadedSocket(void (*callback)(Socket::Connection &)){ + Socket::Server server_socket = Socket::Server(getInteger("listen_port"), getString("listen_interface"), false); + if (!server_socket.connected()){return 1;} + DEBUG_MSG(DLVL_DEVEL, "Activating threaded server: %s", getString("cmd").c_str()); + activate(); + + while (is_active && server_socket.connected()){ + Socket::Connection S = server_socket.accept(); + if (S.connected()){ //check if the new connection is valid + callbackData * cData = new callbackData; + cData->sock = new Socket::Connection(S); + cData->cb = callback; + //spawn a new thread for this connection + tthread::thread T(callThreadCallback, (void*)cData); + //detach it, no need to keep track of it anymore + T.detach(); + }else{ + Util::sleep(10); //sleep 10ms + } + }//main loop + server_socket.close(); + DEBUG_MSG(DLVL_DEVEL, "Threaded server exiting: %s", getString("cmd").c_str()); + return 0; +} + /// Activated the stored config. This will: /// - Drop permissions to the stored "username", if any. /// - Daemonize the process if "daemonize" exists and is true. @@ -450,19 +492,13 @@ 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 + DEBUG_MSG(DLVL_ERROR, "Error: could not setuid %s: could not get PID", username.c_str()); 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 + DEBUG_MSG(DLVL_ERROR, "Error: could not setuid %s: not allowed", username.c_str()); }else{ -#if DEBUG >= 3 - fprintf(stderr, "Changed user to %s\n", username.c_str()); -#endif + DEBUG_MSG(DLVL_DEVEL, "Change user to %s", username.c_str()); } } } @@ -473,12 +509,8 @@ void Util::setUser(std::string username){ /// 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 + DEBUG_MSG(DLVL_DEVEL, "Going into background mode..."); if (daemon(1, 0) < 0){ -#if DEBUG >= 1 - fprintf(stderr, "Failed to daemonize: %s\n", strerror(errno)); -#endif + DEBUG_MSG(DLVL_ERROR, "Failed to daemonize: %s", strerror(errno)); } } diff --git a/lib/config.h b/lib/config.h index 4d35fa56..001fcf8b 100644 --- a/lib/config.h +++ b/lib/config.h @@ -33,6 +33,9 @@ namespace Util { long long int getInteger(std::string optname); bool getBool(std::string optname); void activate(); + int serveThreadedSocket(void (*callback)(Socket::Connection & S)); + int serveForkedSocket(void (*callback)(Socket::Connection & S)); + int servePlainSocket(void (*callback)(Socket::Connection & S)); void addBasicConnectorOptions(JSON::Value & capabilities); void addConnectorOptions(int port, JSON::Value & capabilities); }; diff --git a/lib/converter.cpp b/lib/converter.cpp index 6774354b..1dc7362b 100644 --- a/lib/converter.cpp +++ b/lib/converter.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -69,7 +68,6 @@ namespace Converter { char const * cmd[3] = {0, 0, 0}; std::string mistPath = Util::getMyPath() + "MistInfo"; cmd[0] = mistPath.c_str(); - fprintf( stderr, "Querying %s\n", myPath.c_str()); JSON::Value result; DIR * Dirp = opendir(myPath.c_str()); struct stat StatBuf; diff --git a/lib/defines.h b/lib/defines.h new file mode 100644 index 00000000..6e88b639 --- /dev/null +++ b/lib/defines.h @@ -0,0 +1,18 @@ +// Defines to print debug messages. +#define DLVL_NONE 0 // All debugging disabled. +#define DLVL_FAIL 1 // Only messages about failed operations. +#define DLVL_ERROR 2 // Only messages about errors and failed operations. +#define DLVL_WARN 3 // Warnings, errors, and fail messages. +#define DLVL_DEVEL 4 // All of the above, plus status messages handy during development. +#define DLVL_MEDIUM 5 // Slightly more than just development-level messages. +#define DLVL_HIGH 6 // Verbose debugging messages. +#define DLVL_VERYHIGH 7 // Very verbose debugging messages. +#define DLVL_EXTREME 8 // Everything is reported in extreme detail. +#define DLVL_INSANE 9 // Everything is reported in insane detail. +#define DLVL_DONTEVEN 10 // All messages enabled, even pointless ones. +#if DEBUG > 0 +#include +#define DEBUG_MSG(lvl, msg, ...) if (DEBUG >= lvl){fprintf(stderr, "[%s:%d] " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__);} +#else +#define DEBUG_MSG(lvl, msg, ...) // Debugging disabled. +#endif diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index 9fd21f4e..3cffd439 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -2,6 +2,7 @@ /// Holds all code for DDVTECH Stream Container parsing/generation. #include "dtsc.h" +#include "defines.h" #include #include //for memcmp #include //for htonl/ntohl @@ -87,9 +88,9 @@ bool DTSC::Stream::parsePacket(std::string & buffer){ syncing = false; return true; } -#if DEBUG >= 2 +#if DEBUG >= DLVL_WARN if (!syncing){ - std::cerr << "Error: Invalid DTMI data detected - re-syncing" << std::endl; + DEBUG_MSG(DLVL_WARN, "Invalid DTMI data detected - re-syncing"); syncing = true; } #endif @@ -155,9 +156,9 @@ bool DTSC::Stream::parsePacket(Socket::Buffer & buffer){ syncing = false; return true; } -#if DEBUG >= 2 +#if DEBUG >= DLVL_WARN if (!syncing){ - std::cerr << "Error: Invalid DTMI data detected - syncing" << std::endl; + DEBUG_MSG(DLVL_WARN, "Invalid DTMI data detected - syncing"); syncing = true; } #endif @@ -206,9 +207,7 @@ void DTSC::Stream::waitForMeta(Socket::Connection & sourceSocket){ if (Util::getMS() - start >= 5000){ sourceSocket.close(); //and optionally print a debug message that this happened - #if DEBUG >= 4 - fprintf(stderr, "Timed out while waiting for metadata\n"); - #endif + DEBUG_MSG(DLVL_DEVEL, "Timing out while waiting for metadata"); } } @@ -283,7 +282,7 @@ void DTSC::Stream::addPacket(JSON::Value & newPack){ } /// Deletes a the first part of the buffer, updating the keyframes list and metadata as required. -/// Will print a warning to std::cerr if a track has less than 2 keyframes left because of this. +/// Will print a warning if a track has less than 2 keyframes left because of this. void DTSC::Stream::cutOneBuffer(){ int trid = buffers.begin()->first.trackID; long long unsigned int delTime = buffers.begin()->first.seekTime; @@ -517,7 +516,7 @@ DTSC::File::File(std::string filename, bool create){ if (create){ F = fopen(filename.c_str(), "w+b"); if(!F){ - std::cerr << "Could not create file" << filename << ": " << strerror(errno) << std::endl; + DEBUG_MSG(DLVL_ERROR, "Could not create file %s: %s", filename.c_str(), strerror(errno)); return; } //write an empty header @@ -531,7 +530,7 @@ DTSC::File::File(std::string filename, bool create){ } created = create; if ( !F){ - fprintf(stderr, "Could not open file %s\n", filename.c_str()); + DEBUG_MSG(DLVL_ERROR, "Could not open file %s\n", filename.c_str()); return; } fseek(F, 0, SEEK_END); @@ -561,7 +560,7 @@ DTSC::readOnlyMeta & DTSC::File::getMeta(){ /// Forces a write if force is set to true. bool DTSC::File::writeHeader(std::string & header, bool force){ if (headerSize != header.size() && !force){ - fprintf(stderr, "Could not overwrite header - not equal size\n"); + DEBUG_MSG(DLVL_ERROR, "Could not overwrite header - not equal size"); return false; } headerSize = header.size(); @@ -601,30 +600,28 @@ long long int DTSC::File::addHeader(std::string & header){ } /// Reads the header at the given file position. -/// If the packet could not be read for any reason, the reason is printed to stderr. +/// If the packet could not be read for any reason, the reason is printed. /// Reading the header means the file position is moved to after the header. void DTSC::File::readHeader(int pos){ fseek(F, pos, SEEK_SET); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ -#if DEBUG >= 4 - fprintf(stderr, "End of file reached (H%i)\n", pos); -#endif + DEBUG_MSG(DLVL_DEVEL, "End of file reached while reading header @ %d", pos); }else{ - fprintf(stderr, "Could not read header (H%i)\n", pos); + DEBUG_MSG(DLVL_ERROR, "Could not read header @ %d", pos); } strbuffer = ""; metadata = readOnlyMeta(); return; } if (memcmp(buffer, DTSC::Magic_Header, 4) != 0){ - fprintf(stderr, "Invalid header - %.4s != %.4s (H%i)\n", buffer, DTSC::Magic_Header, pos); + DEBUG_MSG(DLVL_ERROR, "Invalid header - %.4s != %.4s @ %i", buffer, DTSC::Magic_Header, pos); strbuffer = ""; metadata = readOnlyMeta(); return; } if (fread(buffer, 4, 1, F) != 1){ - fprintf(stderr, "Could not read size (H%i)\n", pos); + DEBUG_MSG(DLVL_ERROR, "Could not read header size @ %i", pos); strbuffer = ""; metadata = readOnlyMeta(); return; @@ -634,7 +631,7 @@ void DTSC::File::readHeader(int pos){ strbuffer.resize(packSize); if (packSize){ if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ - fprintf(stderr, "Could not read packet (H%i)\n", pos); + DEBUG_MSG(DLVL_ERROR, "Could not read header packet @ %i", pos); strbuffer = ""; metadata = readOnlyMeta(); return; @@ -666,7 +663,7 @@ bool DTSC::File::reachedEOF(){ } /// Reads the packet available at the current file position. -/// If the packet could not be read for any reason, the reason is printed to stderr. +/// If the packet could not be read for any reason, the reason is printed. /// Reading the packet means the file position is increased to the next packet. void DTSC::File::seekNext(){ if ( !currentPositions.size()){ @@ -689,11 +686,9 @@ void DTSC::File::seekNext(){ lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ -#if DEBUG >= 4 - fprintf(stderr, "End of file reached.\n"); -#endif + DEBUG_MSG(DLVL_DEVEL, "End of file reached while seeking @ %i", (int)lastreadpos); }else{ - fprintf(stderr, "Could not read header\n"); + DEBUG_MSG(DLVL_ERROR, "Could not seek to next @ %i", (int)lastreadpos); } strbuffer = ""; jsonbuffer.null(); @@ -712,13 +707,13 @@ void DTSC::File::seekNext(){ version = 2; } if (version == 0){ - fprintf(stderr, "Invalid packet header @ %#x - %.4s != %.4s\n", (unsigned int)lastreadpos, buffer, DTSC::Magic_Packet2); + DEBUG_MSG(DLVL_ERROR, "Invalid packet header @ %#x - %.4s != %.4s @ %d", (unsigned int)lastreadpos, buffer, DTSC::Magic_Packet2, (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; } if (fread(buffer, 4, 1, F) != 1){ - fprintf(stderr, "Could not read size\n"); + DEBUG_MSG(DLVL_ERROR, "Could not read packet size @ %d", (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; @@ -727,7 +722,7 @@ void DTSC::File::seekNext(){ long packSize = ntohl(ubuffer[0]); strbuffer.resize(packSize); if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ - fprintf(stderr, "Could not read packet\n"); + DEBUG_MSG(DLVL_ERROR, "Could not read packet @ %d", (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; @@ -782,11 +777,9 @@ void DTSC::File::parseNext(){ lastreadpos = ftell(F); if (fread(buffer, 4, 1, F) != 1){ if (feof(F)){ -#if DEBUG >= 4 - fprintf(stderr, "End of file reached.\n"); -#endif + DEBUG_MSG(DLVL_DEVEL, "End of file reached @ %d", (int)lastreadpos); }else{ - fprintf(stderr, "Could not read header\n"); + DEBUG_MSG(DLVL_ERROR, "Could not read header @ %d", (int)lastreadpos); } strbuffer = ""; jsonbuffer.null(); @@ -798,7 +791,7 @@ void DTSC::File::parseNext(){ jsonbuffer = metadata.toJSON(); }else{ if (fread(buffer, 4, 1, F) != 1){ - fprintf(stderr, "Could not read size\n"); + DEBUG_MSG(DLVL_ERROR, "Could not read header size @ %d", (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; @@ -807,7 +800,7 @@ void DTSC::File::parseNext(){ long packSize = ntohl(ubuffer[0]); strbuffer.resize(packSize); if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ - fprintf(stderr, "Could not read packet\n"); + DEBUG_MSG(DLVL_ERROR, "Could not read header @ %d", (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; @@ -824,13 +817,13 @@ void DTSC::File::parseNext(){ version = 2; } if (version == 0){ - fprintf(stderr, "Invalid packet header @ %#x - %.4s != %.4s\n", (unsigned int)lastreadpos, buffer, DTSC::Magic_Packet2); + DEBUG_MSG(DLVL_ERROR, "Invalid packet header @ %#x - %.4s != %.4s @ %d", (unsigned int)lastreadpos, buffer, DTSC::Magic_Packet2, (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; } if (fread(buffer, 4, 1, F) != 1){ - fprintf(stderr, "Could not read size\n"); + DEBUG_MSG(DLVL_ERROR, "Could not read packet size @ %d", (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; @@ -839,7 +832,7 @@ void DTSC::File::parseNext(){ long packSize = ntohl(ubuffer[0]); strbuffer.resize(packSize); if (fread((void*)strbuffer.c_str(), packSize, 1, F) != 1){ - fprintf(stderr, "Could not read packet\n"); + DEBUG_MSG(DLVL_ERROR, "Could not read packet @ %d", (int)lastreadpos); strbuffer = ""; jsonbuffer.null(); return; diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index 72dea973..1b2a13da 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -1,4 +1,5 @@ #include "dtsc.h" +#include "defines.h" /// Retrieves a short in network order from the pointer p. static short btohs(char * p){ @@ -269,7 +270,7 @@ namespace DTSC { void Track::update(JSON::Value & pack){ if (pack["time"].asInt() < lastms){ - std::cerr << "Received packets for track " << trackID << " in wrong order (" << pack["time"].asInt() << " < " << lastms << ") - ignoring!" << std::endl; + DEBUG_MSG(DLVL_WARN, "Received packets for track %d in wrong order (%d < %d) - ignoring!", (int)trackID, (int)pack["time"].asInt(), (int)lastms); return; } Part newPart; diff --git a/lib/filesystem.cpp b/lib/filesystem.cpp index e318bfc9..f11fe654 100644 --- a/lib/filesystem.cpp +++ b/lib/filesystem.cpp @@ -1,4 +1,5 @@ #include "filesystem.h" +#include "defines.h" Filesystem::Directory::Directory(std::string PathName, std::string BasePath){ MyBase = BasePath; @@ -26,9 +27,7 @@ void Filesystem::Directory::FillEntries(){ dirent * entry; while ((entry = readdir(Dirp))){ if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1){ -#if DEBUG >= 4 - fprintf(stderr, "\tSkipping %s\n\t\tReason: %s\n", entry->d_name, strerror(errno)); -#endif + DEBUG_MSG(DLVL_DEVEL, "Skipping %s, reason %s", entry->d_name, strerror(errno)); continue; } ///Convert stat to string @@ -38,8 +37,9 @@ void Filesystem::Directory::FillEntries(){ } void Filesystem::Directory::Print(){ + /// \todo Remove? Libraries shouldn't print stuff. if ( !ValidDir){ - printf("%s is not a valid directory\n", (MyBase + MyPath).c_str()); + DEBUG_MSG(DLVL_ERROR, "%s is not a valid directory", (MyBase + MyPath).c_str()); return; } printf("%s:\n", (MyBase + MyPath).c_str()); @@ -74,9 +74,6 @@ std::string Filesystem::Directory::LIST(std::vector ActiveStreams){ for (std::map::iterator it = Entries.begin(); it != Entries.end(); it++){ bool Active = (std::find(ActiveStreams.begin(), ActiveStreams.end(), ( *it).first) != ActiveStreams.end()); - fprintf(stderr, "%s active?: %d\n", ( *it).first.c_str(), Active); - fprintf(stderr, "\tMyPath: %s\n\tVisible: %d\n", MyPath.c_str(), MyVisible[MyPath]); - fprintf(stderr, "\t\tBitmask S_ACTIVE: %d\n\t\tBitmask S_INACTIVE: %d\n", MyVisible[MyPath] & S_ACTIVE, MyVisible[MyPath] & S_INACTIVE); if ((Active && (MyVisible[MyPath] & S_ACTIVE)) || (( !Active) && (MyVisible[MyPath] & S_INACTIVE)) || ((( *it).second.st_mode / 010000) == 4)){ if ((( *it).second.st_mode / 010000) == 4){ Converter << 'd'; @@ -217,7 +214,6 @@ void Filesystem::Directory::STOR(std::string Path, std::string Data){ bool Filesystem::Directory::SimplifyPath(){ MyPath += "/"; - fprintf(stderr, "MyPath: %s\n", MyPath.c_str()); std::vector TempPath; std::string TempString; for (std::string::iterator it = MyPath.begin(); it != MyPath.end(); it++){ @@ -258,7 +254,7 @@ bool Filesystem::Directory::DELE(std::string Path){ FileName = MyBase + MyPath + "/" + Path; } if (std::remove(FileName.c_str())){ - fprintf(stderr, "Removing file %s unsuccesfull\n", FileName.c_str()); + DEBUG_MSG(DLVL_ERROR, "Removing file %s failed", FileName.c_str()); return false; } return true; @@ -275,7 +271,7 @@ bool Filesystem::Directory::MKD(std::string Path){ FileName = MyBase + MyPath + "/" + Path; } if (mkdir(FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)){ - fprintf(stderr, "Creating directory %s unsuccesfull\n", FileName.c_str()); + DEBUG_MSG(DLVL_ERROR, "Creating directory %s failed", FileName.c_str()); return false; } MyVisible[FileName] = S_ALL; @@ -298,7 +294,7 @@ bool Filesystem::Directory::Rename(std::string From, std::string To){ FileTo = MyBase + MyPath + "/" + To; } if (std::rename(FileFrom.c_str(), FileTo.c_str())){ - fprintf(stderr, "Renaming file %s to %s unsuccesfull\n", FileFrom.c_str(), FileTo.c_str()); + DEBUG_MSG(DLVL_ERROR, "Renaming %s to %s failed", FileFrom.c_str(), FileTo.c_str()); return false; } return true; diff --git a/lib/ftp.cpp b/lib/ftp.cpp index 4bc36936..b39e8d89 100644 --- a/lib/ftp.cpp +++ b/lib/ftp.cpp @@ -23,7 +23,6 @@ FTP::User::User(Socket::Connection NewConnection, std::map #include #include @@ -642,11 +643,11 @@ unsigned int JSON::Value::packedSize() const{ }//packedSize /// Pre-packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. -/// Non-object-types will print an error to stderr. +/// Non-object-types will print an error. /// The internal buffer is guaranteed to be up-to-date after this function is called. void JSON::Value::netPrepare(){ if (myType != OBJECT){ - fprintf(stderr, "Error: Only objects may be NetPacked!\n"); + DEBUG_MSG(DLVL_ERROR, "Only objects may be netpacked!"); return; } std::string packed = toPacked(); @@ -711,7 +712,7 @@ void JSON::Value::netPrepare(){ } /// Packs any object-type JSON::Value to a std::string for transfer over the network, including proper DTMI header. -/// Non-object-types will print an error to stderr and return an empty string. +/// Non-object-types will print an error and return an empty string. /// This function returns a reference to an internal buffer where the prepared data is kept. /// The internal buffer is *not* made stale if any changes occur inside the object - subsequent calls to toPacked() will clear the buffer, /// calls to netPrepare will guarantee it is up-to-date. @@ -719,7 +720,7 @@ std::string & JSON::Value::toNetPacked(){ static std::string emptystring; //check if this is legal if (myType != OBJECT){ - fprintf(stderr, "Error: Only objects may be NetPacked!\n"); + DEBUG_MSG(DLVL_ERROR, "Only objects may be netpacked!"); return emptystring; } //if sneaky storage doesn't contain correct data, re-calculate it @@ -1018,9 +1019,6 @@ JSON::Value JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigne /// \param i Current parsing position in the raw data (defaults to 0). /// \param ret Will be set to JSON::Value, parsed from the raw data. void JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int &i, JSON::Value & ret){ -#if DEBUG >= 10 - fprintf(stderr, "Note: AMF type %hhx found. %i bytes left\n", data[i], len-i); -#endif ret.null(); if (i >= len){ return; @@ -1087,9 +1085,7 @@ void JSON::fromDTMI(const unsigned char * data, unsigned int len, unsigned int & break; } } -#if DEBUG >= 2 - fprintf(stderr, "Error: Unimplemented DTMI type %hhx, @ %i / %i - returning.\n", data[i], i, len); -#endif + DEBUG_MSG(DLVL_FAIL, "Unimplemented DTMI type %hhx, @ %i / %i - returning.\n", data[i], i, len); i += 1; return; } //fromOneDTMI diff --git a/lib/mp4.cpp b/lib/mp4.cpp index b28e4d33..93bd5b96 100644 --- a/lib/mp4.cpp +++ b/lib/mp4.cpp @@ -3,6 +3,7 @@ #include //for htonl and friends #include "mp4.h" #include "json.h" +#include "defines.h" #define Int64 uint64_t @@ -2286,7 +2287,7 @@ namespace MP4 { void AVCC::setPayload(std::string newPayload){ if ( !reserve(0, payloadSize(), newPayload.size())){ - std::cerr << "Cannot allocate enough memory for payload" << std::endl; + DEBUG_MSG(DLVL_ERROR, "Cannot allocate enough memory for payload"); return; } memcpy((char*)payload(), (char*)newPayload.c_str(), newPayload.size()); diff --git a/lib/nal.cpp b/lib/nal.cpp index c56efefa..975dd8fa 100644 --- a/lib/nal.cpp +++ b/lib/nal.cpp @@ -18,7 +18,6 @@ bool NAL_Unit::ReadData(std::string & InputData){ ShortAnnexB += (char)0x00; ShortAnnexB += (char)0x00; ShortAnnexB += (char)0x01; -// fprintf( stderr, "NAL_Unit::ReadData --- DataSize: %d\n", InputData.size() ); if (InputData.size() < 3){ return false; } diff --git a/lib/ogg.cpp b/lib/ogg.cpp index bfffe720..0f973f2c 100644 --- a/lib/ogg.cpp +++ b/lib/ogg.cpp @@ -1,4 +1,5 @@ #include "ogg.h" +#include "defines.h" #include #include #include @@ -204,7 +205,7 @@ namespace OGG{ } static void STerrMSG(){ - std::cerr << "Segments too big, create a continue page" << std::endl; + DEBUG_MSG(DLVL_ERROR, "Segment too big, create a continue page"); } /// \todo MAKE FIX HERE @@ -486,10 +487,10 @@ namespace OGG{ setGranulePosition(DTSCVec[0]["granule"].asInt()); for (unsigned int i = 1; i < DTSCVec.size(); i++){ if (DTSCVec[0]["granule"].asInt() != DTSCVec[i]["granule"].asInt()){ - std::cerr << "Granule inconcistency!! " << DTSCVec[0]["granule"].asInt() << " != " << DTSCVec[i]["granule"].asInt() << std::endl; + DEBUG_MSG(DLVL_WARN, "Granule inconcistency!! %u != %u", (unsigned int)DTSCVec[0]["granule"].asInt(), (unsigned int)DTSCVec[i]["granule"].asInt()); } if (DTSCVec[0]["trackid"].asInt() != DTSCVec[i]["trackid"].asInt()){ - std::cerr << "Track ID inconcistency!! " << DTSCVec[0]["trackid"].asInt() << " != " < #include #include @@ -127,22 +128,19 @@ void Util::Procs::childsig_handler(int signum){ return; } -#if DEBUG >= 5 +#if DEBUG >= DLVL_DEVEL std::string pname = plist[ret]; #endif plist.erase(ret); -#if DEBUG >= 5 +#if DEBUG >= DLVL_DEVEL if (!isActive(pname)){ - std::cerr << "Process " << pname << " fully terminated." << std::endl; + DEBUG_MSG(DLVL_DEVEL, "Process %s fully terminated", pname.c_str()); } #endif if (exitHandlers.count(ret) > 0){ TerminationNotifier tn = exitHandlers[ret]; exitHandlers.erase(ret); -#if DEBUG >= 5 - std::cerr << "Calling termination handler for " << pname << std::endl; -#endif tn(ret, exitcode); } } @@ -205,9 +203,7 @@ void Util::Procs::runCmd(std::string & cmd){ } //execute the command execvp(args[0], args); -#if DEBUG >= 1 - std::cerr << "Error running \"" << cmd << "\": " << strerror(errno) << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Error running %s: %s", cmd.c_str(), strerror(errno)); _exit(42); } @@ -225,14 +221,10 @@ pid_t Util::Procs::Start(std::string name, std::string cmd){ runCmd(cmd); }else{ if (ret > 0){ -#if DEBUG >= 5 - std::cerr << "Process " << name << " started, PID " << ret << ": " << cmd << std::endl; -#endif + DEBUG_MSG(DLVL_DEVEL, "Process %s started, PID %d: %s", name.c_str(), ret, cmd.c_str()); plist.insert(std::pair(ret, name)); }else{ -#if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started: fork() failed", name.c_str()); return 0; } } @@ -251,9 +243,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ setHandler(); int pfildes[2]; if (pipe(pfildes) == -1){ -#if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str()); return 0; } @@ -270,9 +260,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ 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 + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str()); close(pfildes[1]); close(pfildes[0]); return 0; @@ -289,14 +277,10 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2){ runCmd(cmd2); }else{ if (ret2 > 0){ -#if DEBUG >= 5 - std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; -#endif + DEBUG_MSG(DLVL_DEVEL, "Process %s started, PIDs (%d, %d): %s | %s", name.c_str(), ret, ret2, cmd.c_str(), cmd2.c_str()); plist.insert(std::pair(ret2, name)); }else{ -#if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str()); Stop(name); close(pfildes[1]); close(pfildes[0]); @@ -322,15 +306,11 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st 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 + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str()); return 0; } if (pipe(pfildes2) == -1){ -#if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str()); return 0; } @@ -349,9 +329,7 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st 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 + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str()); close(pfildes[1]); close(pfildes[0]); close(pfildes2[1]); @@ -372,14 +350,10 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st runCmd(cmd2); }else{ if (ret2 > 0){ -#if DEBUG >= 5 - std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << "): " << cmd << " | " << cmd2 << std::endl; -#endif + DEBUG_MSG(DLVL_DEVEL, "Process %s started, PIDs (%d, %d): %s | %s", name.c_str(), ret, ret2, cmd.c_str(), cmd2.c_str()); plist.insert(std::pair(ret2, name)); }else{ -#if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str()); Stop(name); close(pfildes[1]); close(pfildes[0]); @@ -403,14 +377,10 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st runCmd(cmd3); }else{ if (ret3 > 0){ -#if DEBUG >= 5 - std::cerr << "Process " << name << " started, PIDs (" << ret << ", " << ret2 << ", " << ret3 << "): " << cmd << " | " << cmd2 << " | " << cmd3 << std::endl; -#endif + DEBUG_MSG(DLVL_DEVEL, "Process %s started, PIDs (%d, %d, %d): %s | %s | %s", name.c_str(), ret, ret2, ret3, cmd.c_str(), cmd2.c_str(), cmd3.c_str()); plist.insert(std::pair(ret3, name)); }else{ -#if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. fork() failed." << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str()); Stop(name); close(pfildes[1]); close(pfildes[0]); @@ -432,24 +402,18 @@ pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, st /// \arg fdout Same as fdin, but for stderr. pid_t Util::Procs::StartPiped(std::string name, char* const* argv, int * fdin, int * fdout, int * fderr){ if (isActive(name)){ - #if DEBUG >= 1 - std::cerr << name << " already active - skipping start" << std::endl; - #endif + DEBUG_MSG(DLVL_WARN, "Process %s already active - skipping start", name.c_str()); return getPid(name); } pid_t pid; int pipein[2], pipeout[2], pipeerr[2]; setHandler(); if (fdin && *fdin == -1 && pipe(pipein) < 0){ -#if DEBUG >= 1 - std::cerr << "Pipe (in) creation failed for " << name << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Pipe in creation failed for process %s", name.c_str()); return 0; } if (fdout && *fdout == -1 && pipe(pipeout) < 0){ -#if DEBUG >= 1 - std::cerr << "Pipe (out) creation failed for " << name << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Pipe out creation failed for process %s", name.c_str()); if ( *fdin == -1){ close(pipein[0]); close(pipein[1]); @@ -457,9 +421,7 @@ pid_t Util::Procs::StartPiped(std::string name, char* const* argv, int * fdin, i return 0; } if (fderr && *fderr == -1 && pipe(pipeerr) < 0){ -#if DEBUG >= 1 - std::cerr << "Pipe (err) creation failed for " << name << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Pipe err creation failed for process %s", name.c_str()); if ( *fdin == -1){ close(pipein[0]); close(pipein[1]); @@ -474,9 +436,7 @@ pid_t Util::Procs::StartPiped(std::string name, char* const* argv, int * fdin, i if ( !fdin || !fdout || !fderr){ devnull = open("/dev/null", O_RDWR); if (devnull == -1){ -#if DEBUG >= 1 - std::cerr << "Could not open /dev/null for " << name << ": " << strerror(errno) << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Could not open /dev/null for process %s: %s", name.c_str(), strerror(errno)); if ( *fdin == -1){ close(pipein[0]); close(pipein[1]); @@ -528,14 +488,10 @@ pid_t Util::Procs::StartPiped(std::string name, char* const* argv, int * fdin, i close(devnull); } execvp(argv[0], argv); -#if DEBUG >= 1 - perror("execvp failed"); -#endif + DEBUG_MSG(DLVL_ERROR, "execvp() failed for process %s", name.c_str()); exit(42); }else if (pid == -1){ -#if DEBUG >= 1 - std::cerr << "Failed to fork for pipe: " << name << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "fork() for pipe failed for process %s", name.c_str()); if (fdin && *fdin == -1){ close(pipein[0]); close(pipein[1]); @@ -553,14 +509,7 @@ pid_t Util::Procs::StartPiped(std::string name, char* const* argv, int * fdin, i } return 0; }else{ //parent -#if DEBUG >= 5 - std::cerr << "Piped process " << name << " started"; - if (fdin ) std::cerr << " in=" << (*fdin == -1 ? pipein [1] : *fdin ); - if (fdout) std::cerr << " out=" << (*fdout == -1 ? pipeout[0] : *fdout); - if (fderr) std::cerr << " err=" << (*fderr == -1 ? pipeerr[0] : *fderr); - if (devnull != -1) std::cerr << " null=" << devnull; - std::cerr << ", PID " << pid << ": " << argv[0] << std::endl; -#endif + DEBUG_MSG(DLVL_DEVEL, "Piped process %s started, PID %d: %s", name.c_str(), pid, argv[0]); if (devnull != -1){ close(devnull); } @@ -611,9 +560,7 @@ pid_t Util::Procs::StartPiped(std::string name, std::string cmd, int * fdin, int pid_t Util::Procs::StartPiped2(std::string name, std::string cmd1, std::string cmd2, int * fdin, int * fdout, int * fderr1, int * fderr2){ int pfildes[2]; if (pipe(pfildes) == -1){ -#if DEBUG >= 1 - std::cerr << "Process " << name << " could not be started. Pipe creation failed." << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Pipe creation failed for process %s", name.c_str()); return 0; } pid_t res1 = StartPiped(name, cmd1, fdin, &pfildes[1], fderr1); diff --git a/lib/rtmpchunks.cpp b/lib/rtmpchunks.cpp index 8ff6128a..4e001795 100644 --- a/lib/rtmpchunks.cpp +++ b/lib/rtmpchunks.cpp @@ -2,6 +2,7 @@ /// Holds all code for the RTMPStream namespace. #include "rtmpchunks.h" +#include "defines.h" #include "flv_tag.h" #include "timing.h" @@ -261,9 +262,7 @@ bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme){ uint8_t *pTempHash = new uint8_t[512]; HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash); bool result = (memcmp(pBuffer + clientDigestOffset, pTempHash, 32) == 0); -#if DEBUG >= 5 - fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); -#endif + DEBUG_MSG(DLVL_MEDIUM, "Client scheme validation %hhi %s", scheme, result?"success":"failed"); delete[] pTempBuffer; delete[] pTempHash; return result; @@ -556,7 +555,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ 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"); + DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x40 with no valid previous chunk!"); } timestamp = indata[i++ ] * 256 * 256; timestamp += indata[i++ ] * 256; @@ -574,7 +573,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ 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"); + DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x80 with no valid previous chunk!"); } timestamp = indata[i++ ] * 256 * 256; timestamp += indata[i++ ] * 256; @@ -589,7 +588,7 @@ bool RTMPStream::Chunk::Parse(std::string & indata){ break; case 0xC0: if (prev.msg_type_id == 0){ - fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n"); + DEBUG_MSG(DLVL_WARN, "Warning: Header type 0xC0 with no valid previous chunk!"); } timestamp = prev.timestamp; len = prev.len; @@ -704,7 +703,7 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){ } //can't read whole header indata = buffer.copy(i + 7); if (prev.msg_type_id == 0){ - fprintf(stderr, "Warning: Header type 0x40 with no valid previous chunk!\n"); + DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x40 with no valid previous chunk!"); } timestamp = indata[i++ ] * 256 * 256; timestamp += indata[i++ ] * 256; @@ -725,7 +724,7 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){ } //can't read whole header indata = buffer.copy(i + 3); if (prev.msg_type_id == 0){ - fprintf(stderr, "Warning: Header type 0x80 with no valid previous chunk!\n"); + DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x80 with no valid previous chunk!"); } timestamp = indata[i++ ] * 256 * 256; timestamp += indata[i++ ] * 256; @@ -740,7 +739,7 @@ bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer){ break; case 0xC0: if (prev.msg_type_id == 0){ - fprintf(stderr, "Warning: Header type 0xC0 with no valid previous chunk!\n"); + DEBUG_MSG(DLVL_WARN, "Warning: Header type 0xC0 with no valid previous chunk!"); } timestamp = prev.timestamp; len = prev.len; @@ -821,16 +820,12 @@ bool RTMPStream::doHandshake(){ } //"random" data bool encrypted = (Version == 6); -#if DEBUG >= 8 - fprintf(stderr, "Handshake version is %hhi\n", Version); -#endif + DEBUG_MSG(DLVL_HIGH, "Handshake version is %hhi", Version); uint8_t _validationScheme = 5; if (ValidateClientScheme(Client, 0)) _validationScheme = 0; if (ValidateClientScheme(Client, 1)) _validationScheme = 1; -#if DEBUG >= 8 - fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off"); -#endif + DEBUG_MSG(DLVL_HIGH, "Handshake type is %hhi, encryption is %s", _validationScheme, encrypted?"on":"off"); //FIRST 1536 bytes from server response //compute DH key position diff --git a/lib/socket.cpp b/lib/socket.cpp index 6f8b37d6..4120a9c1 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -4,6 +4,7 @@ #include "socket.h" #include "timing.h" +#include "defines.h" #include #include #include @@ -14,7 +15,6 @@ #endif #define BUFFER_BLOCKSIZE 4096 //set buffer blocksize to 4KiB -#include //temporary for debugging std::string uint2string(unsigned int i){ std::stringstream st; @@ -70,7 +70,7 @@ void Socket::Buffer::append(const char * newdata, const unsigned int newdatasize } } if (data.size() > 5000){ - std::cerr << "Warning: After " << newdatasize << " new bytes, buffer has " << data.size() << " parts!" << std::endl; + DEBUG_MSG(DLVL_WARN, "Warning: After %d new bytes, buffer has %d parts!", newdatasize, (int)data.size()); } } @@ -240,9 +240,7 @@ bool Socket::Connection::isBlocking(){ /// If the connection is already closed, nothing happens. void Socket::Connection::close(){ if (connected()){ -#if DEBUG >= 6 - fprintf(stderr, "Socket closed.\n"); -#endif + DEBUG_MSG(DLVL_HIGH, "Socket %d closed", sock); if (sock != -1){ shutdown(sock, SHUT_RDWR); errno = EINTR; @@ -285,9 +283,7 @@ Socket::Connection::Connection(std::string address, bool nonblock){ sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0){ remotehost = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", remotehost.c_str()); -#endif + DEBUG_MSG(DLVL_FAIL, "Could not create socket! Error: %s", remotehost.c_str()); return; } Error = false; @@ -307,9 +303,7 @@ Socket::Connection::Connection(std::string address, bool nonblock){ } }else{ remotehost = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s! Error: %s\n", address.c_str(), remotehost.c_str()); -#endif + DEBUG_MSG(DLVL_FAIL, "Could not connect to %s! Error: %s", address.c_str(), remotehost.c_str()); close(); } } //Socket::Connection Unix Contructor @@ -340,9 +334,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ 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 + DEBUG_MSG(DLVL_FAIL, "Could not connect to %s:%i! Error: %s", host.c_str(), port, gai_strerror(s)); close(); return; } @@ -362,9 +354,7 @@ Socket::Connection::Connection(std::string host, int port, bool nonblock){ freeaddrinfo(result); if (rp == 0){ -#if DEBUG >= 1 - fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), remotehost.c_str()); -#endif + DEBUG_MSG(DLVL_FAIL, "Could not connect to %s! Error: %s", host.c_str(), remotehost.c_str()); close(); }else{ if (nonblock){ @@ -531,9 +521,7 @@ unsigned int Socket::Connection::iwrite(const void * buffer, int len){ if (errno != EPIPE){ Error = true; remotehost = strerror(errno); -#if DEBUG >= 2 - fprintf(stderr, "Could not iwrite data! Error: %s\n", remotehost.c_str()); -#endif + DEBUG_MSG(DLVL_WARN, "Could not iwrite data! Error: %s\n", remotehost.c_str()); } close(); return 0; @@ -571,9 +559,7 @@ int Socket::Connection::iread(void * buffer, int len){ if (errno != EPIPE){ Error = true; remotehost = strerror(errno); -#if DEBUG >= 2 - fprintf(stderr, "Could not iread data! Error: %s\n", remotehost.c_str()); -#endif + DEBUG_MSG(DLVL_WARN, "Could not iread data! Error: %s\n", remotehost.c_str()); } close(); return 0; @@ -661,7 +647,7 @@ Socket::Server::Server(){ /// \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, errors.c_str()); + DEBUG_MSG(DLVL_FAIL, "Could not create socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); sock = -1; } } //Socket::Server TCP Constructor @@ -675,9 +661,7 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "Could not create IPv6 socket %s:%i! Error: %s\n", hostname.c_str(), port, errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "Could not create IPv6 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); return false; } int on = 1; @@ -699,23 +683,17 @@ bool Socket::Server::IPv6bind(int port, std::string hostname, bool nonblock){ if (ret == 0){ ret = listen(sock, 100); //start listening, backlog of 100 allowed if (ret == 0){ -#if DEBUG >= 1 - fprintf(stderr, "IPv6 socket success @ %s:%i\n", hostname.c_str(), port); -#endif + DEBUG_MSG(DLVL_DEVEL, "IPv6 socket success @ %s:%i\n", hostname.c_str(), port); return true; }else{ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "IPv6 Listen failed! Error: %s\n", errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "IPv6 listen failed! Error: %s\n", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "IPv6 Binding %s:%i failed (%s)\n", hostname.c_str(), port, errors.c_str()); close(); return false; } @@ -730,9 +708,7 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "Could not create IPv4 socket %s:%i! Error: %s\n", hostname.c_str(), port, errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "Could not create IPv4 socket %s:%i! Error: %s", hostname.c_str(), port, errors.c_str()); return false; } int on = 1; @@ -754,23 +730,17 @@ bool Socket::Server::IPv4bind(int port, std::string hostname, bool nonblock){ if (ret == 0){ ret = listen(sock, 100); //start listening, backlog of 100 allowed if (ret == 0){ -#if DEBUG >= 1 - fprintf(stderr, "IPv4 socket success @ %s:%i\n", hostname.c_str(), port); -#endif + DEBUG_MSG(DLVL_DEVEL, "IPv4 socket success @ %s:%i\n", hostname.c_str(), port); return true; }else{ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "IPv4 Listen failed! Error: %s\n", errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "IPv4 listen failed! Error: %s\n", errors.c_str()); close(); return false; } }else{ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "IPv4 binding %s:%i failed (%s)\n", hostname.c_str(), port, errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "IPv4 Binding %s:%i failed (%s)\n", hostname.c_str(), port, errors.c_str()); close(); return false; } @@ -787,9 +757,7 @@ Socket::Server::Server(std::string address, bool nonblock){ sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0){ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "Could not create socket! Error: %s\n", errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "Could not create unix socket %s! Error: %s", address.c_str(), errors.c_str()); return; } if (nonblock){ @@ -807,17 +775,13 @@ Socket::Server::Server(std::string address, bool nonblock){ return; }else{ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "Listen failed! Error: %s\n", errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "Unix listen failed! Error: %s\n", errors.c_str()); close(); return; } }else{ errors = strerror(errno); -#if DEBUG >= 1 - fprintf(stderr, "Binding failed! Error: %s\n", errors.c_str()); -#endif + DEBUG_MSG(DLVL_ERROR, "Unix Binding %s failed (%s)\n", address.c_str(), errors.c_str()); close(); return; } @@ -845,29 +809,20 @@ Socket::Connection Socket::Server::accept(bool nonblock){ 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 + DEBUG_MSG(DLVL_FAIL, "Error during accept - closing server socket %d.", sock); 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 + DEBUG_MSG(DLVL_DEVEL, "IPv6 addr [%s]", tmp.remotehost.c_str()); } 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 + DEBUG_MSG(DLVL_DEVEL, "IPv4 addr [%s]", tmp.remotehost.c_str()); } if (addrinfo.sin6_family == AF_UNIX){ -#if DEBUG >= 6 - tmp.remotehost = ((sockaddr_un*)&addrinfo)->sun_path; - fprintf(stderr,"Unix socket, no address\n"); -#endif + DEBUG_MSG(DLVL_DEVEL, "Unix addr [?]"); tmp.remotehost = "UNIX_SOCKET"; } } @@ -893,9 +848,7 @@ bool Socket::Server::isBlocking(){ /// If the connection is already closed, nothing happens. void Socket::Server::close(){ if (connected()){ -#if DEBUG >= 6 - fprintf(stderr, "ServerSocket closed.\n"); -#endif + DEBUG_MSG(DLVL_HIGH, "ServerSocket %d closed", sock); shutdown(sock, SHUT_RDWR); errno = EINTR; while (::close(sock) != 0 && errno == EINTR){ diff --git a/lib/stream.cpp b/lib/stream.cpp index 9135a15d..bbb281ed 100644 --- a/lib/stream.cpp +++ b/lib/stream.cpp @@ -1,10 +1,6 @@ /// \file stream.cpp /// Utilities for handling streams. -#if DEBUG >= 4 -#include -#endif - #include #include #include @@ -14,6 +10,7 @@ #include "procs.h" #include "config.h" #include "socket.h" +#include "defines.h" std::string Util::getTmpFolder(){ std::string dir; @@ -83,20 +80,12 @@ Socket::Connection Util::Stream::getStream(std::string streamname){ JSON::Value ServConf = JSON::fromFile(getTmpFolder() + "streamlist"); if (ServConf["streams"].isMember(streamname)){ if (ServConf["streams"][streamname]["source"].asString()[0] == '/'){ -#if DEBUG >= 5 - std::cerr << "Opening VoD stream from file " << ServConf["streams"][streamname]["source"].asString() << std::endl; -#endif return getVod(ServConf["streams"][streamname]["source"].asString(), streamname); }else{ -#if DEBUG >= 5 - std::cerr << "Opening live stream " << streamname << std::endl; -#endif return Socket::Connection(getTmpFolder() + "stream_" + streamname); } } -#if DEBUG >= 5 - std::cerr << "Could not open stream " << streamname << " - stream not found" << std::endl; -#endif + DEBUG_MSG(DLVL_ERROR, "Stream not found: %s", streamname.c_str()); return Socket::Connection(); } diff --git a/lib/tinythread.cpp b/lib/tinythread.cpp new file mode 100644 index 00000000..690eceea --- /dev/null +++ b/lib/tinythread.cpp @@ -0,0 +1,303 @@ +/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2010-2012 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#include +#include "tinythread.h" + +#if defined(_TTHREAD_POSIX_) + #include + #include +#elif defined(_TTHREAD_WIN32_) + #include +#endif + + +namespace tthread { + +//------------------------------------------------------------------------------ +// condition_variable +//------------------------------------------------------------------------------ +// NOTE 1: The Win32 implementation of the condition_variable class is based on +// the corresponding implementation in GLFW, which in turn is based on a +// description by Douglas C. Schmidt and Irfan Pyarali: +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html +// +// NOTE 2: Windows Vista actually has native support for condition variables +// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to +// be portable with pre-Vista Windows versions, so TinyThread++ does not use +// Vista condition variables. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_WIN32_) + #define _CONDITION_EVENT_ONE 0 + #define _CONDITION_EVENT_ALL 1 +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::condition_variable() : mWaitersCount(0) +{ + mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); + mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); + InitializeCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +condition_variable::~condition_variable() +{ + CloseHandle(mEvents[_CONDITION_EVENT_ONE]); + CloseHandle(mEvents[_CONDITION_EVENT_ALL]); + DeleteCriticalSection(&mWaitersCountLock); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::_wait() +{ + // Wait for either event to become signaled due to notify_one() or + // notify_all() being called + int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE); + + // Check if we are the last waiter + EnterCriticalSection(&mWaitersCountLock); + -- mWaitersCount; + bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && + (mWaitersCount == 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we are the last waiter to be notified to stop waiting, reset the event + if(lastWaiter) + ResetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_one() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ONE]); +} +#endif + +#if defined(_TTHREAD_WIN32_) +void condition_variable::notify_all() +{ + // Are there any waiters? + EnterCriticalSection(&mWaitersCountLock); + bool haveWaiters = (mWaitersCount > 0); + LeaveCriticalSection(&mWaitersCountLock); + + // If we have any waiting threads, send them a signal + if(haveWaiters) + SetEvent(mEvents[_CONDITION_EVENT_ALL]); +} +#endif + + +//------------------------------------------------------------------------------ +// POSIX pthread_t to unique thread::id mapping logic. +// Note: Here we use a global thread safe std::map to convert instances of +// pthread_t to small thread identifier numbers (unique within one process). +// This method should be portable across different POSIX implementations. +//------------------------------------------------------------------------------ + +#if defined(_TTHREAD_POSIX_) +static thread::id _pthread_t_to_ID(const pthread_t &aHandle) +{ + static mutex idMapLock; + static std::map idMap; + static unsigned long int idCount(1); + + lock_guard guard(idMapLock); + if(idMap.find(aHandle) == idMap.end()) + idMap[aHandle] = idCount ++; + return thread::id(idMap[aHandle]); +} +#endif // _TTHREAD_POSIX_ + + +//------------------------------------------------------------------------------ +// thread +//------------------------------------------------------------------------------ + +/// Information to pass to the new thread (what to run). +struct _thread_start_info { + void (*mFunction)(void *); ///< Pointer to the function to be executed. + void * mArg; ///< Function argument for the thread function. + thread * mThread; ///< Pointer to the thread object. +}; + +// Thread wrapper function. +#if defined(_TTHREAD_WIN32_) +unsigned WINAPI thread::wrapper_function(void * aArg) +#elif defined(_TTHREAD_POSIX_) +void * thread::wrapper_function(void * aArg) +#endif +{ + // Get thread startup information + _thread_start_info * ti = (_thread_start_info *) aArg; + + try + { + // Call the actual client thread function + ti->mFunction(ti->mArg); + } + catch(...) + { + // Uncaught exceptions will terminate the application (default behavior + // according to C++11) + std::terminate(); + } + + // The thread is no longer executing + lock_guard guard(ti->mThread->mDataMutex); + ti->mThread->mNotAThread = true; + + // The thread is responsible for freeing the startup information + delete ti; + + return 0; +} + +thread::thread(void (*aFunction)(void *), void * aArg) +{ + // Serialize access to this thread structure + lock_guard guard(mDataMutex); + + // Fill out the thread startup information (passed to the thread wrapper, + // which will eventually free it) + _thread_start_info * ti = new _thread_start_info; + ti->mFunction = aFunction; + ti->mArg = aArg; + ti->mThread = this; + + // The thread is now alive + mNotAThread = false; + + // Create the thread +#if defined(_TTHREAD_WIN32_) + mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + if(pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0) + mHandle = 0; +#endif + + // Did we fail to create the thread? + if(!mHandle) + { + mNotAThread = true; + delete ti; + } +} + +thread::~thread() +{ + if(joinable()) + std::terminate(); +} + +void thread::join() +{ + if(joinable()) + { +#if defined(_TTHREAD_WIN32_) + WaitForSingleObject(mHandle, INFINITE); + CloseHandle(mHandle); +#elif defined(_TTHREAD_POSIX_) + pthread_join(mHandle, NULL); +#endif + } +} + +bool thread::joinable() const +{ + mDataMutex.lock(); + bool result = !mNotAThread; + mDataMutex.unlock(); + return result; +} + +void thread::detach() +{ + mDataMutex.lock(); + if(!mNotAThread) + { +#if defined(_TTHREAD_WIN32_) + CloseHandle(mHandle); +#elif defined(_TTHREAD_POSIX_) + pthread_detach(mHandle); +#endif + mNotAThread = true; + } + mDataMutex.unlock(); +} + +thread::id thread::get_id() const +{ + if(!joinable()) + return id(); +#if defined(_TTHREAD_WIN32_) + return id((unsigned long int) mWin32ThreadID); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(mHandle); +#endif +} + +unsigned thread::hardware_concurrency() +{ +#if defined(_TTHREAD_WIN32_) + SYSTEM_INFO si; + GetSystemInfo(&si); + return (int) si.dwNumberOfProcessors; +#elif defined(_SC_NPROCESSORS_ONLN) + return (int) sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(_SC_NPROC_ONLN) + return (int) sysconf(_SC_NPROC_ONLN); +#else + // The standard requires this function to return zero if the number of + // hardware cores could not be determined. + return 0; +#endif +} + + +//------------------------------------------------------------------------------ +// this_thread +//------------------------------------------------------------------------------ + +thread::id this_thread::get_id() +{ +#if defined(_TTHREAD_WIN32_) + return thread::id((unsigned long int) GetCurrentThreadId()); +#elif defined(_TTHREAD_POSIX_) + return _pthread_t_to_ID(pthread_self()); +#endif +} + +} diff --git a/lib/tinythread.h b/lib/tinythread.h new file mode 100644 index 00000000..aed7b585 --- /dev/null +++ b/lib/tinythread.h @@ -0,0 +1,714 @@ +/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- +Copyright (c) 2010-2012 Marcus Geelnard + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef _TINYTHREAD_H_ +#define _TINYTHREAD_H_ + +/// @file +/// @mainpage TinyThread++ API Reference +/// +/// @section intro_sec Introduction +/// TinyThread++ is a minimal, portable implementation of basic threading +/// classes for C++. +/// +/// They closely mimic the functionality and naming of the C++11 standard, and +/// should be easily replaceable with the corresponding std:: variants. +/// +/// @section port_sec Portability +/// The Win32 variant uses the native Win32 API for implementing the thread +/// classes, while for other systems, the POSIX threads API (pthread) is used. +/// +/// @section class_sec Classes +/// In order to mimic the threading API of the C++11 standard, subsets of +/// several classes are provided. The fundamental classes are: +/// @li tthread::thread +/// @li tthread::mutex +/// @li tthread::recursive_mutex +/// @li tthread::condition_variable +/// @li tthread::lock_guard +/// @li tthread::fast_mutex +/// +/// @section misc_sec Miscellaneous +/// The following special keywords are available: #thread_local. +/// +/// For more detailed information (including additional classes), browse the +/// different sections of this documentation. A good place to start is: +/// tinythread.h. + +// Which platform are we on? +#if !defined(_TTHREAD_PLATFORM_DEFINED_) + #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define _TTHREAD_WIN32_ + #else + #define _TTHREAD_POSIX_ + #endif + #define _TTHREAD_PLATFORM_DEFINED_ +#endif + +// Platform specific includes +#if defined(_TTHREAD_WIN32_) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #define __UNDEF_LEAN_AND_MEAN + #endif + #include + #ifdef __UNDEF_LEAN_AND_MEAN + #undef WIN32_LEAN_AND_MEAN + #undef __UNDEF_LEAN_AND_MEAN + #endif +#else + #include + #include + #include + #include +#endif + +// Generic includes +#include + +/// TinyThread++ version (major number). +#define TINYTHREAD_VERSION_MAJOR 1 +/// TinyThread++ version (minor number). +#define TINYTHREAD_VERSION_MINOR 1 +/// TinyThread++ version (full version). +#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR) + +// Do we have a fully featured C++11 compiler? +#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L)) + #define _TTHREAD_CPP11_ +#endif + +// ...at least partial C++11? +#if defined(_TTHREAD_CPP11_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) + #define _TTHREAD_CPP11_PARTIAL_ +#endif + +// Macro for disabling assignments of objects. +#ifdef _TTHREAD_CPP11_PARTIAL_ + #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ + name(const name&) = delete; \ + name& operator=(const name&) = delete; +#else + #define _TTHREAD_DISABLE_ASSIGNMENT(name) \ + name(const name&); \ + name& operator=(const name&); +#endif + +/// @def thread_local +/// Thread local storage keyword. +/// A variable that is declared with the @c thread_local keyword makes the +/// value of the variable local to each thread (known as thread-local storage, +/// or TLS). Example usage: +/// @code +/// // This variable is local to each thread. +/// thread_local int variable; +/// @endcode +/// @note The @c thread_local keyword is a macro that maps to the corresponding +/// compiler directive (e.g. @c __declspec(thread)). While the C++11 standard +/// allows for non-trivial types (e.g. classes with constructors and +/// destructors) to be declared with the @c thread_local keyword, most pre-C++11 +/// compilers only allow for trivial types (e.g. @c int). So, to guarantee +/// portable code, only use trivial types for thread local storage. +/// @note This directive is currently not supported on Mac OS X (it will give +/// a compiler error), since compile-time TLS is not supported in the Mac OS X +/// executable format. Also, some older versions of MinGW (before GCC 4.x) do +/// not support this directive. +/// @hideinitializer + +#if !defined(_TTHREAD_CPP11_) && !defined(thread_local) + #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) + #define thread_local __thread + #else + #define thread_local __declspec(thread) + #endif +#endif + + +/// Main name space for TinyThread++. +/// This namespace is more or less equivalent to the @c std namespace for the +/// C++11 thread classes. For instance, the tthread::mutex class corresponds to +/// the std::mutex class. +namespace tthread { + +/// Mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is non-recursive (i.e. a +/// program may deadlock if the thread that owns a mutex object calls lock() +/// on that object). +/// @see recursive_mutex +class mutex { + public: + /// Constructor. + mutex() +#if defined(_TTHREAD_WIN32_) + : mAlreadyLocked(false) +#endif + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutex_init(&mHandle, NULL); +#endif + } + + /// Destructor. + ~mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until @c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); + while(mAlreadyLocked) Sleep(1000); // Simulate deadlock... + mAlreadyLocked = true; +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return @c true if the lock was acquired, or @c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + bool ret = (TryEnterCriticalSection(&mHandle) ? true : false); + if(ret && mAlreadyLocked) + { + LeaveCriticalSection(&mHandle); + ret = false; + } + return ret; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + mAlreadyLocked = false; + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; + bool mAlreadyLocked; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Recursive mutex class. +/// This is a mutual exclusion object for synchronizing access to shared +/// memory areas for several threads. The mutex is recursive (i.e. a thread +/// may lock the mutex several times, as long as it unlocks the mutex the same +/// number of times). +/// @see mutex +class recursive_mutex { + public: + /// Constructor. + recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + InitializeCriticalSection(&mHandle); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mHandle, &attr); +#endif + } + + /// Destructor. + ~recursive_mutex() + { +#if defined(_TTHREAD_WIN32_) + DeleteCriticalSection(&mHandle); +#else + pthread_mutex_destroy(&mHandle); +#endif + } + + /// Lock the mutex. + /// The method will block the calling thread until a lock on the mutex can + /// be obtained. The mutex remains locked until @c unlock() is called. + /// @see lock_guard + inline void lock() + { +#if defined(_TTHREAD_WIN32_) + EnterCriticalSection(&mHandle); +#else + pthread_mutex_lock(&mHandle); +#endif + } + + /// Try to lock the mutex. + /// The method will try to lock the mutex. If it fails, the function will + /// return immediately (non-blocking). + /// @return @c true if the lock was acquired, or @c false if the lock could + /// not be acquired. + inline bool try_lock() + { +#if defined(_TTHREAD_WIN32_) + return TryEnterCriticalSection(&mHandle) ? true : false; +#else + return (pthread_mutex_trylock(&mHandle) == 0) ? true : false; +#endif + } + + /// Unlock the mutex. + /// If any threads are waiting for the lock on this mutex, one of them will + /// be unblocked. + inline void unlock() + { +#if defined(_TTHREAD_WIN32_) + LeaveCriticalSection(&mHandle); +#else + pthread_mutex_unlock(&mHandle); +#endif + } + + _TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex) + + private: +#if defined(_TTHREAD_WIN32_) + CRITICAL_SECTION mHandle; +#else + pthread_mutex_t mHandle; +#endif + + friend class condition_variable; +}; + +/// Lock guard class. +/// The constructor locks the mutex, and the destructor unlocks the mutex, so +/// the mutex will automatically be unlocked when the lock guard goes out of +/// scope. Example usage: +/// @code +/// mutex m; +/// int counter; +/// +/// void increment() +/// { +/// lock_guard guard(m); +/// ++ counter; +/// } +/// @endcode + +template +class lock_guard { + public: + typedef T mutex_type; + + lock_guard() : mMutex(0) {} + + /// The constructor locks the mutex. + explicit lock_guard(mutex_type &aMutex) + { + mMutex = &aMutex; + mMutex->lock(); + } + + /// The destructor unlocks the mutex. + ~lock_guard() + { + if(mMutex) + mMutex->unlock(); + } + + private: + mutex_type * mMutex; +}; + +/// Condition variable class. +/// This is a signalling object for synchronizing the execution flow for +/// several threads. Example usage: +/// @code +/// // Shared data and associated mutex and condition variable objects +/// int count; +/// mutex m; +/// condition_variable cond; +/// +/// // Wait for the counter to reach a certain number +/// void wait_counter(int targetCount) +/// { +/// lock_guard guard(m); +/// while(count < targetCount) +/// cond.wait(m); +/// } +/// +/// // Increment the counter, and notify waiting threads +/// void increment() +/// { +/// lock_guard guard(m); +/// ++ count; +/// cond.notify_all(); +/// } +/// @endcode +class condition_variable { + public: + /// Constructor. +#if defined(_TTHREAD_WIN32_) + condition_variable(); +#else + condition_variable() + { + pthread_cond_init(&mHandle, NULL); + } +#endif + + /// Destructor. +#if defined(_TTHREAD_WIN32_) + ~condition_variable(); +#else + ~condition_variable() + { + pthread_cond_destroy(&mHandle); + } +#endif + + /// Wait for the condition. + /// The function will block the calling thread until the condition variable + /// is woken by @c notify_one(), @c notify_all() or a spurious wake up. + /// @param[in] aMutex A mutex that will be unlocked when the wait operation + /// starts, an locked again as soon as the wait operation is finished. + template + inline void wait(_mutexT &aMutex) + { +#if defined(_TTHREAD_WIN32_) + // Increment number of waiters + EnterCriticalSection(&mWaitersCountLock); + ++ mWaitersCount; + LeaveCriticalSection(&mWaitersCountLock); + + // Release the mutex while waiting for the condition (will decrease + // the number of waiters when done)... + aMutex.unlock(); + _wait(); + aMutex.lock(); +#else + pthread_cond_wait(&mHandle, &aMutex.mHandle); +#endif + } + + /// Notify one thread that is waiting for the condition. + /// If at least one thread is blocked waiting for this condition variable, + /// one will be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_one(); +#else + inline void notify_one() + { + pthread_cond_signal(&mHandle); + } +#endif + + /// Notify all threads that are waiting for the condition. + /// All threads that are blocked waiting for this condition variable will + /// be woken up. + /// @note Only threads that started waiting prior to this call will be + /// woken up. +#if defined(_TTHREAD_WIN32_) + void notify_all(); +#else + inline void notify_all() + { + pthread_cond_broadcast(&mHandle); + } +#endif + + _TTHREAD_DISABLE_ASSIGNMENT(condition_variable) + + private: +#if defined(_TTHREAD_WIN32_) + void _wait(); + HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs. + unsigned int mWaitersCount; ///< Count of the number of waiters. + CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount. +#else + pthread_cond_t mHandle; +#endif +}; + + +/// Thread class. +class thread { + public: +#if defined(_TTHREAD_WIN32_) + typedef HANDLE native_handle_type; +#else + typedef pthread_t native_handle_type; +#endif + + class id; + + /// Default constructor. + /// Construct a @c thread object without an associated thread of execution + /// (i.e. non-joinable). + thread() : mHandle(0), mNotAThread(true) +#if defined(_TTHREAD_WIN32_) + , mWin32ThreadID(0) +#endif + {} + + /// Thread starting constructor. + /// Construct a @c thread object with a new thread of execution. + /// @param[in] aFunction A function pointer to a function of type: + /// void fun(void * arg) + /// @param[in] aArg Argument to the thread function. + /// @note This constructor is not fully compatible with the standard C++ + /// thread class. It is more similar to the pthread_create() (POSIX) and + /// CreateThread() (Windows) functions. + thread(void (*aFunction)(void *), void * aArg); + + /// Destructor. + /// @note If the thread is joinable upon destruction, @c std::terminate() + /// will be called, which terminates the process. It is always wise to do + /// @c join() before deleting a thread object. + ~thread(); + + /// Wait for the thread to finish (join execution flows). + /// After calling @c join(), the thread object is no longer associated with + /// a thread of execution (i.e. it is not joinable, and you may not join + /// with it nor detach from it). + void join(); + + /// Check if the thread is joinable. + /// A thread object is joinable if it has an associated thread of execution. + bool joinable() const; + + /// Detach from the thread. + /// After calling @c detach(), the thread object is no longer assicated with + /// a thread of execution (i.e. it is not joinable). The thread continues + /// execution without the calling thread blocking, and when the thread + /// ends execution, any owned resources are released. + void detach(); + + /// Return the thread ID of a thread object. + id get_id() const; + + /// Get the native handle for this thread. + /// @note Under Windows, this is a @c HANDLE, and under POSIX systems, this + /// is a @c pthread_t. + inline native_handle_type native_handle() + { + return mHandle; + } + + /// Determine the number of threads which can possibly execute concurrently. + /// This function is useful for determining the optimal number of threads to + /// use for a task. + /// @return The number of hardware thread contexts in the system. + /// @note If this value is not defined, the function returns zero (0). + static unsigned hardware_concurrency(); + + _TTHREAD_DISABLE_ASSIGNMENT(thread) + + private: + native_handle_type mHandle; ///< Thread handle. + mutable mutex mDataMutex; ///< Serializer for access to the thread private data. + bool mNotAThread; ///< True if this object is not a thread of execution. +#if defined(_TTHREAD_WIN32_) + unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex). +#endif + + // This is the internal thread wrapper function. +#if defined(_TTHREAD_WIN32_) + static unsigned WINAPI wrapper_function(void * aArg); +#else + static void * wrapper_function(void * aArg); +#endif +}; + +/// Thread ID. +/// The thread ID is a unique identifier for each thread. +/// @see thread::get_id() +class thread::id { + public: + /// Default constructor. + /// The default constructed ID is that of thread without a thread of + /// execution. + id() : mId(0) {}; + + id(unsigned long int aId) : mId(aId) {}; + + id(const id& aId) : mId(aId.mId) {}; + + inline id & operator=(const id &aId) + { + mId = aId.mId; + return *this; + } + + inline friend bool operator==(const id &aId1, const id &aId2) + { + return (aId1.mId == aId2.mId); + } + + inline friend bool operator!=(const id &aId1, const id &aId2) + { + return (aId1.mId != aId2.mId); + } + + inline friend bool operator<=(const id &aId1, const id &aId2) + { + return (aId1.mId <= aId2.mId); + } + + inline friend bool operator<(const id &aId1, const id &aId2) + { + return (aId1.mId < aId2.mId); + } + + inline friend bool operator>=(const id &aId1, const id &aId2) + { + return (aId1.mId >= aId2.mId); + } + + inline friend bool operator>(const id &aId1, const id &aId2) + { + return (aId1.mId > aId2.mId); + } + + inline friend std::ostream& operator <<(std::ostream &os, const id &obj) + { + os << obj.mId; + return os; + } + + private: + unsigned long int mId; +}; + + +// Related to - minimal to be able to support chrono. +typedef long long __intmax_t; + +/// Minimal implementation of the @c ratio class. This class provides enough +/// functionality to implement some basic @c chrono classes. +template <__intmax_t N, __intmax_t D = 1> class ratio { + public: + static double _as_double() { return double(N) / double(D); } +}; + +/// Minimal implementation of the @c chrono namespace. +/// The @c chrono namespace provides types for specifying time intervals. +namespace chrono { + /// Duration template class. This class provides enough functionality to + /// implement @c this_thread::sleep_for(). + template > class duration { + private: + _Rep rep_; + public: + typedef _Rep rep; + typedef _Period period; + + /// Construct a duration object with the given duration. + template + explicit duration(const _Rep2& r) : rep_(r) {}; + + /// Return the value of the duration object. + rep count() const + { + return rep_; + } + }; + + // Standard duration types. + typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds. + typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds. + typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds. + typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds. + typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes. + typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours. +} + +/// The namespace @c this_thread provides methods for dealing with the +/// calling thread. +namespace this_thread { + /// Return the thread ID of the calling thread. + thread::id get_id(); + + /// Yield execution to another thread. + /// Offers the operating system the opportunity to schedule another thread + /// that is ready to run on the current processor. + inline void yield() + { +#if defined(_TTHREAD_WIN32_) + Sleep(0); +#else + sched_yield(); +#endif + } + + /// Blocks the calling thread for a period of time. + /// @param[in] aTime Minimum time to put the thread to sleep. + /// Example usage: + /// @code + /// // Sleep for 100 milliseconds + /// this_thread::sleep_for(chrono::milliseconds(100)); + /// @endcode + /// @note Supported duration types are: nanoseconds, microseconds, + /// milliseconds, seconds, minutes and hours. + template void sleep_for(const chrono::duration<_Rep, _Period>& aTime) + { +#if defined(_TTHREAD_WIN32_) + Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5)); +#else + usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5)); +#endif + } +} + +} + +// Define/macro cleanup +#undef _TTHREAD_DISABLE_ASSIGNMENT + +#endif // _TINYTHREAD_H_ diff --git a/lib/ts_packet.cpp b/lib/ts_packet.cpp index f3b5ef2b..092e3a47 100644 --- a/lib/ts_packet.cpp +++ b/lib/ts_packet.cpp @@ -3,6 +3,7 @@ #include #include "ts_packet.h" +#include "defines.h" #ifndef FILLER_DATA #define FILLER_DATA "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros tortor commodo neque, vitae hendrerit nunc sem ut odio." @@ -236,7 +237,7 @@ void TS::Packet::DefaultPMT(){ /// \return A string representation of the packet. const char* TS::Packet::ToString(){ if (strBuf.size() != 188){ - std::cerr << "Error: Size invalid (" << strBuf.size() << ") Invalid data from this point on." << std::endl; + DEBUG_MSG(DLVL_ERROR, "Size invalid (%i) - invalid data from this point on", (int)strBuf.size()); } return strBuf.c_str(); } diff --git a/lib/vorbis.cpp b/lib/vorbis.cpp index ff312704..35ea7d0f 100644 --- a/lib/vorbis.cpp +++ b/lib/vorbis.cpp @@ -1,4 +1,5 @@ #include "vorbis.h" +#include "defines.h" #include #include #include @@ -236,7 +237,7 @@ namespace vorbis{ long long unsigned int floorType = stream.get(16); switch(floorType){ case 0:{ - std::cerr << "WARNING: FloorType 0 in vorbis setup header not tested!" << std::endl; + DEBUG_MSG(DLVL_WARN, "FloorType 0 in vorbis setup header not tested!"); stream.skip(8);//order stream.skip(16);//rate stream.skip(16);//bark_map_size @@ -339,7 +340,7 @@ namespace vorbis{ } char meh = stream.get(2); if (meh != 0){ - std::cerr << " Sanity Check ==0 : " << (int)meh << std::endl; + DEBUG_MSG(DLVL_ERROR, "Sanity check ==0 : %i", (int)meh); exit(0); } if (mappingSubmaps > 1){