Abstraction of semaphore to a class
This commit is contained in:
		
							parent
							
								
									1e3b38f777
								
							
						
					
					
						commit
						4f1e1fa1d7
					
				
					 8 changed files with 809 additions and 216 deletions
				
			
		
							
								
								
									
										69
									
								
								Doxyfile
									
										
									
									
									
								
							
							
						
						
									
										69
									
								
								Doxyfile
									
										
									
									
									
								
							|  | @ -1,4 +1,4 @@ | |||
| # Doxyfile 1.8.6 | ||||
| # Doxyfile 1.8.7 | ||||
| 
 | ||||
| # This file describes the settings to be used by the documentation system | ||||
| # doxygen (www.doxygen.org) for a project. | ||||
|  | @ -70,6 +70,14 @@ OUTPUT_DIRECTORY       = ./docs | |||
| 
 | ||||
| CREATE_SUBDIRS         = NO | ||||
| 
 | ||||
| # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII | ||||
| # characters to appear in the names of generated files. If set to NO, non-ASCII | ||||
| # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode | ||||
| # U+3044. | ||||
| # The default value is: NO. | ||||
| 
 | ||||
| ALLOW_UNICODE_NAMES    = NO | ||||
| 
 | ||||
| # The OUTPUT_LANGUAGE tag is used to specify the language in which all | ||||
| # documentation generated by doxygen is written. Doxygen will use this | ||||
| # information to generate all constant output in the proper language. | ||||
|  | @ -261,9 +269,12 @@ OPTIMIZE_OUTPUT_VHDL   = NO | |||
| # extension. Doxygen has a built-in mapping, but you can override or extend it | ||||
| # using this tag. The format is ext=language, where ext is a file extension, and | ||||
| # language is one of the parsers supported by doxygen: IDL, Java, Javascript, | ||||
| # C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make | ||||
| # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C | ||||
| # (default is Fortran), use: inc=Fortran f=C. | ||||
| # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: | ||||
| # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: | ||||
| # Fortran. In the later case the parser tries to guess whether the code is fixed | ||||
| # or free formatted code, this is the default for Fortran type files), VHDL. For | ||||
| # instance to make doxygen treat .inc files as Fortran files (default is PHP), | ||||
| # and .f files as C (default is Fortran), use: inc=Fortran f=C. | ||||
| # | ||||
| # Note For files without extension you can use no_extension as a placeholder. | ||||
| # | ||||
|  | @ -1230,7 +1241,8 @@ GENERATE_CHI           = NO | |||
| CHM_INDEX_ENCODING     = | ||||
| 
 | ||||
| # The BINARY_TOC flag controls whether a binary table of contents is generated ( | ||||
| # YES) or a normal table of contents ( NO) in the .chm file. | ||||
| # YES) or a normal table of contents ( NO) in the .chm file. Furthermore it | ||||
| # enables the Previous and Next buttons. | ||||
| # The default value is: NO. | ||||
| # This tag requires that the tag GENERATE_HTMLHELP is set to YES. | ||||
| 
 | ||||
|  | @ -1470,11 +1482,11 @@ SEARCHENGINE           = YES | |||
| 
 | ||||
| # When the SERVER_BASED_SEARCH tag is enabled the search engine will be | ||||
| # implemented using a web server instead of a web client using Javascript. There | ||||
| # are two flavours of web server based searching depending on the | ||||
| # EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for | ||||
| # searching and an index file used by the script. When EXTERNAL_SEARCH is | ||||
| # enabled the indexing and searching needs to be provided by external tools. See | ||||
| # the section "External Indexing and Searching" for details. | ||||
| # are two flavors of web server based searching depending on the EXTERNAL_SEARCH | ||||
| # setting. When disabled, doxygen will generate a PHP script for searching and | ||||
| # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing | ||||
| # and searching needs to be provided by external tools. See the section | ||||
| # "External Indexing and Searching" for details. | ||||
| # The default value is: NO. | ||||
| # This tag requires that the tag SEARCHENGINE is set to YES. | ||||
| 
 | ||||
|  | @ -1762,6 +1774,13 @@ MAN_OUTPUT             = man | |||
| 
 | ||||
| MAN_EXTENSION          = .3 | ||||
| 
 | ||||
| # The MAN_SUBDIR tag determines the name of the directory created within | ||||
| # MAN_OUTPUT in which the man pages are placed. If defaults to man followed by | ||||
| # MAN_EXTENSION with the initial . removed. | ||||
| # This tag requires that the tag GENERATE_MAN is set to YES. | ||||
| 
 | ||||
| MAN_SUBDIR             = | ||||
| 
 | ||||
| # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it | ||||
| # will generate one additional man file for each entity documented in the real | ||||
| # man page(s). These additional files only source the real man page, but without | ||||
|  | @ -1789,18 +1808,6 @@ GENERATE_XML           = NO | |||
| 
 | ||||
| XML_OUTPUT             = xml | ||||
| 
 | ||||
| # The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a | ||||
| # validating XML parser to check the syntax of the XML files. | ||||
| # This tag requires that the tag GENERATE_XML is set to YES. | ||||
| 
 | ||||
| XML_SCHEMA             = | ||||
| 
 | ||||
| # The XML_DTD tag can be used to specify a XML DTD, which can be used by a | ||||
| # validating XML parser to check the syntax of the XML files. | ||||
| # This tag requires that the tag GENERATE_XML is set to YES. | ||||
| 
 | ||||
| XML_DTD                = | ||||
| 
 | ||||
| # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program | ||||
| # listings (including syntax highlighting and cross-referencing information) to | ||||
| # the XML output. Note that enabling this will significantly increase the size | ||||
|  | @ -1947,9 +1954,9 @@ PREDEFINED             = | |||
| EXPAND_AS_DEFINED      = | ||||
| 
 | ||||
| # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will | ||||
| # remove all refrences to function-like macros that are alone on a line, have an | ||||
| # all uppercase name, and do not end with a semicolon. Such function macros are | ||||
| # typically used for boiler-plate code, and will confuse the parser if not | ||||
| # remove all references to function-like macros that are alone on a line, have | ||||
| # an all uppercase name, and do not end with a semicolon. Such function macros | ||||
| # are typically used for boiler-plate code, and will confuse the parser if not | ||||
| # removed. | ||||
| # The default value is: YES. | ||||
| # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. | ||||
|  | @ -1969,7 +1976,7 @@ SKIP_FUNCTION_MACROS   = YES | |||
| # where loc1 and loc2 can be relative or absolute paths or URLs. See the | ||||
| # section "Linking to external documentation" for more information about the use | ||||
| # of tag files. | ||||
| # Note: Each tag file must have an unique name (where the name does NOT include | ||||
| # Note: Each tag file must have a unique name (where the name does NOT include | ||||
| # the path). If a tag file is not located in the directory in which doxygen is | ||||
| # run, you must also specify the path to the tagfile here. | ||||
| 
 | ||||
|  | @ -2061,6 +2068,16 @@ HAVE_DOT               = NO | |||
| 
 | ||||
| DOT_NUM_THREADS        = 0 | ||||
| 
 | ||||
| # When you want a differently looking font n the dot files that doxygen | ||||
| # generates you can specify the font name using DOT_FONTNAME. You need to make | ||||
| # sure dot is able to find the font, which can be done by putting it in a | ||||
| # standard location or by setting the DOTFONTPATH environment variable or by | ||||
| # setting DOT_FONTPATH to the directory containing the font. | ||||
| # The default value is: Helvetica. | ||||
| # This tag requires that the tag HAVE_DOT is set to YES. | ||||
| 
 | ||||
| DOT_FONTNAME           = Helvetica | ||||
| 
 | ||||
| # The DOT_FONTSIZE tag can be used to set the size (in points) of the font of | ||||
| # dot graphs. | ||||
| # Minimum value: 4, maximum value: 24, default value: 10. | ||||
|  |  | |||
|  | @ -12,10 +12,17 @@ | |||
| 
 | ||||
| namespace Converter { | ||||
|    | ||||
|   ///\brief The base constructor
 | ||||
|   Converter::Converter(){ | ||||
|     fillFFMpegEncoders(); | ||||
|   } | ||||
|    | ||||
|   ///\brief A function that fill the internal variables with values provided by examing ffmpeg output
 | ||||
|   ///
 | ||||
|   ///Checks for the following encoders:
 | ||||
|   /// - AAC
 | ||||
|   /// - H264
 | ||||
|   /// - MP3
 | ||||
|   void Converter::fillFFMpegEncoders(){ | ||||
|     std::vector<char*> cmd; | ||||
|     cmd.reserve(3); | ||||
|  | @ -45,10 +52,14 @@ namespace Converter { | |||
|     fclose( outFile ); | ||||
|   } | ||||
|    | ||||
|   ///\brief A function to obtain all available codecs that have been obtained from the encoders.
 | ||||
|   ///\return A reference to the allCodecs member.
 | ||||
|   converterInfo & Converter::getCodecs(){ | ||||
|     return allCodecs; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief A function to obtain the available encoders in JSON format.
 | ||||
|   ///\return A JSON::Value containing all encoder:codec pairs.
 | ||||
|   JSON::Value Converter::getEncoders(){ | ||||
|     JSON::Value result; | ||||
|     for (converterInfo::iterator convIt = allCodecs.begin(); convIt != allCodecs.end(); convIt++){ | ||||
|  | @ -64,6 +75,9 @@ namespace Converter { | |||
|     return result; | ||||
|   } | ||||
|    | ||||
|   ///\brief Looks in a given path for all files that could be converted
 | ||||
|   ///\param myPath The location to look at, this should be a folder.
 | ||||
|   ///\return A JSON::Value containing all media files in the location, with their corresponding metadata values.
 | ||||
|   JSON::Value Converter::queryPath(std::string myPath){ | ||||
|     char const * cmd[3] = {0, 0, 0}; | ||||
|     std::string mistPath = Util::getMyPath() + "MistInfo"; | ||||
|  | @ -89,6 +103,20 @@ namespace Converter { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Start a conversion with the given parameters
 | ||||
|   ///\param name The name to use for logging the conversion.
 | ||||
|   ///\param parameters The parameters, accepted are the following:
 | ||||
|   /// - input The input url
 | ||||
|   /// - output The output url
 | ||||
|   /// - encoder The encoder to use
 | ||||
|   /// - video An object containing video parameters, if not existant no video will be output. Values are:
 | ||||
|   ///   - width The width of the resulting video
 | ||||
|   ///   - height The height of the resulting video
 | ||||
|   ///   - codec The codec to encode video in, or copy to use the current codec
 | ||||
|   ///   - fpks The framerate in fps * 1000
 | ||||
|   /// - audio An object containing audio parameters, if not existant no audio will be output. Values are:
 | ||||
|   ///   - codec The codec to encode audio in, or copy to use the current codec
 | ||||
|   ///   - samplerate The target samplerate for the audio, in hz
 | ||||
|   void Converter::startConversion(std::string name, JSON::Value parameters) { | ||||
|     if ( !parameters.isMember("input")){ | ||||
|       statusHistory[name] = "No input file supplied"; | ||||
|  | @ -185,6 +213,9 @@ namespace Converter { | |||
|     allConversions[name]["status"]["time"] = 0; | ||||
|   } | ||||
|    | ||||
|   ///\brief Updates the internal status of the converter class.
 | ||||
|   ///
 | ||||
|   ///Will check for each running conversion whether it is still running, and update its status accordingly
 | ||||
|   void Converter::updateStatus(){ | ||||
|     if (allConversions.size()){ | ||||
|       std::map<std::string,JSON::Value>::iterator cIt; | ||||
|  | @ -249,6 +280,11 @@ namespace Converter { | |||
|     } | ||||
|   } | ||||
|    | ||||
|   ///\brief Parses a single ffmpeg status line into a JSON format
 | ||||
|   ///\param statusLine The current status of ffmpeg
 | ||||
|   ///\return A JSON::Value with the following values set:
 | ||||
|   /// - frame The current last encoded frame
 | ||||
|   /// - time The current last encoded timestamp
 | ||||
|   JSON::Value Converter::parseFFMpegStatus(std::string statusLine){ | ||||
|     JSON::Value result; | ||||
|     int curOffset = statusLine.find("frame=") + 6; | ||||
|  | @ -263,6 +299,8 @@ namespace Converter { | |||
|     return result; | ||||
|   } | ||||
|    | ||||
|   ///\brief Obtain the current internal status of the conversion class
 | ||||
|   ///\return A JSON::Value with the status of each conversion
 | ||||
|   JSON::Value Converter::getStatus(){ | ||||
|     updateStatus(); | ||||
|     JSON::Value result; | ||||
|  | @ -281,6 +319,7 @@ namespace Converter { | |||
|     return result; | ||||
|   } | ||||
|    | ||||
|   ///\brief Clears the status history of all conversions
 | ||||
|   void Converter::clearStatus(){ | ||||
|     statusHistory.clear(); | ||||
|   } | ||||
|  |  | |||
|  | @ -3,10 +3,15 @@ | |||
| 
 | ||||
| #include "json.h" | ||||
| 
 | ||||
| ///\brief A typedef to simplify accessing all codecs
 | ||||
| typedef std::map<std::string,std::string> codecInfo; | ||||
| ///\brief A typedef to simplify accessing all encoders
 | ||||
| typedef std::map<std::string,codecInfo> converterInfo; | ||||
| 
 | ||||
| ///\brief A namespace containing all functions for handling the conversion API
 | ||||
| namespace Converter { | ||||
| 
 | ||||
|   ///\brief A class containing the basic conversion API functionality
 | ||||
|   class Converter { | ||||
|     public: | ||||
|       Converter(); | ||||
|  | @ -20,8 +25,11 @@ namespace Converter { | |||
|       JSON::Value parseFFMpegStatus(std::string statusLine); | ||||
|     private: | ||||
|       void fillFFMpegEncoders(); | ||||
|       ///\brief Holds a list of all current known codecs
 | ||||
|       converterInfo allCodecs; | ||||
|       ///\brief Holds a list of all the current conversions
 | ||||
|       std::map<std::string,JSON::Value> allConversions; | ||||
|       ///\brief Stores the status of all conversions, and the history
 | ||||
|       std::map<std::string,std::string> statusHistory; | ||||
|   }; | ||||
| } | ||||
|  |  | |||
|  | @ -210,6 +210,7 @@ namespace DTSC { | |||
|       char data[11]; | ||||
|   }; | ||||
| 
 | ||||
|   ///\brief Basic class for storage of a read-only track
 | ||||
|   class readOnlyTrack { | ||||
|     public: | ||||
|       readOnlyTrack(); | ||||
|  | @ -248,6 +249,7 @@ namespace DTSC { | |||
|       void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0); | ||||
|   }; | ||||
| 
 | ||||
|   ///\brief Class for storage of track data
 | ||||
|   class Track : public readOnlyTrack { | ||||
|     public: | ||||
|       Track(); | ||||
|  | @ -270,6 +272,7 @@ namespace DTSC { | |||
|       void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0); | ||||
|   }; | ||||
| 
 | ||||
|   ///\brief Class for storage of read-only meta data
 | ||||
|   class readOnlyMeta { | ||||
|     public: | ||||
|       readOnlyMeta(); | ||||
|  | @ -291,6 +294,7 @@ namespace DTSC { | |||
|       void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0); | ||||
|   }; | ||||
| 
 | ||||
|   ///\brief Class for storage of meta data
 | ||||
|   class Meta : public readOnlyMeta { | ||||
|     public: | ||||
|       Meta(); | ||||
|  |  | |||
							
								
								
									
										142
									
								
								lib/dtscmeta.cpp
									
										
									
									
									
								
							
							
						
						
									
										142
									
								
								lib/dtscmeta.cpp
									
										
									
									
									
								
							|  | @ -115,6 +115,7 @@ namespace DTSC { | |||
| 
 | ||||
|   /// Internally used resize function for when operating in copy mode and the internal buffer is too small.
 | ||||
|   /// It will only resize up, never down.
 | ||||
|   ///\param len The length th scale the buffer up to if necessary
 | ||||
|   void Packet::resize(unsigned int len) { | ||||
|     if (master && len > bufferLen) { | ||||
|       char * tmp = (char *)realloc(data, len); | ||||
|  | @ -127,6 +128,10 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Initializes a packet with new data
 | ||||
|   ///\param data_ The new data for the packet
 | ||||
|   ///\param len The length of the data pointed to by data_
 | ||||
|   ///\param noCopy Determines whether to make a copy or not
 | ||||
|   void Packet::reInit(const char * data_, unsigned int len, bool noCopy) { | ||||
|     if (!data_){ | ||||
|       DEBUG_MSG(DLVL_DEVEL, "ReInit received a null pointer with len %d, ignoring", len); | ||||
|  | @ -240,6 +245,9 @@ namespace DTSC { | |||
|     return (char*)1;//out of packet! 1 == error
 | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Locates an identifier within the payload
 | ||||
|   ///\param identifier The identifier to find
 | ||||
|   ///\return A pointer to the location of the identifier
 | ||||
|   char * Packet::findIdentifier(const char * identifier){ | ||||
|     char * p = data; | ||||
|     if (version == DTSC_V2){ | ||||
|  | @ -251,6 +259,10 @@ namespace DTSC { | |||
|     return ret; | ||||
|   } | ||||
|    | ||||
|   ///\brief Retrieves a single parameter as a string
 | ||||
|   ///\param identifier The name of the parameter
 | ||||
|   ///\param result A location on which the string will be returned
 | ||||
|   ///\param len An integer in which the length of the string will be returned
 | ||||
|   void Packet::getString(const char * identifier, char *& result, int & len) { | ||||
|     char * pos = findIdentifier(identifier); | ||||
|     if (pos < (char*)2) { | ||||
|  | @ -267,6 +279,9 @@ namespace DTSC { | |||
|     len = ntohl(((int *)(pos + 1))[0]); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Retrieves a single parameter as a string
 | ||||
|   ///\param identifier The name of the parameter
 | ||||
|   ///\param result The string in which to store the result
 | ||||
|   void Packet::getString(const char * identifier, std::string & result) { | ||||
|     char * data = NULL; | ||||
|     int len = 0; | ||||
|  | @ -274,6 +289,9 @@ namespace DTSC { | |||
|     result = std::string(data, len); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Retrieves a single parameter as an integer
 | ||||
|   ///\param identifier The name of the parameter
 | ||||
|   ///\param result The result is stored in this integer
 | ||||
|   void Packet::getInt(const char * identifier, int & result) { | ||||
|     char * pos = findIdentifier(identifier); | ||||
|     if (pos < (char*)2) { | ||||
|  | @ -287,28 +305,42 @@ namespace DTSC { | |||
|     result = ((long long int)pos[1] << 56) | ((long long int)pos[2] << 48) | ((long long int)pos[3] << 40) | ((long long int)pos[4] << 32) | ((long long int)pos[5] << 24) | ((long long int)pos[6] << 16) | ((long long int)pos[7] << 8) | pos[8]; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Retrieves a single parameter as an integer
 | ||||
|   ///\param identifier The name of the parameter
 | ||||
|   ///\result The requested parameter as an integer
 | ||||
|   int Packet::getInt(const char * identifier) { | ||||
|     int result; | ||||
|     getInt(identifier, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Retrieves a single parameter as a boolean
 | ||||
|   ///\param identifier The name of the parameter
 | ||||
|   ///\param result The result is stored in this boolean
 | ||||
|   void Packet::getFlag(const char * identifier, bool & result) { | ||||
|     int result_; | ||||
|     getInt(identifier, result_); | ||||
|     result = (bool)result_; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Retrieves a single parameter as a boolean
 | ||||
|   ///\param identifier The name of the parameter
 | ||||
|   ///\result The requested parameter as a boolean
 | ||||
|   bool Packet::getFlag(const char * identifier) { | ||||
|     bool result; | ||||
|     getFlag(identifier, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Checks whether a parameter exists
 | ||||
|   ///\param identifier The name of the parameter
 | ||||
|   ///\result Whether the parameter exists or not
 | ||||
|   bool Packet::hasMember(const char * identifier) { | ||||
|     return findIdentifier(identifier) > (char*)2; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the timestamp of the packet.
 | ||||
|   ///\return The timestamp of this packet.
 | ||||
|   long long unsigned int Packet::getTime() { | ||||
|     if (version != DTSC_V2){ | ||||
|       if (!data){return 0;} | ||||
|  | @ -317,6 +349,8 @@ namespace DTSC { | |||
|     return ((long long int)ntohl(((int *)(data + 12))[0]) << 32) | ntohl(((int *)(data + 12))[1]); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the track id of the packet.
 | ||||
|   ///\return The track id of this packet.
 | ||||
|   long int Packet::getTrackId() { | ||||
|     if (version != DTSC_V2){ | ||||
|       return getInt("trackid"); | ||||
|  | @ -324,15 +358,21 @@ namespace DTSC { | |||
|     return ntohl(((int *)data)[2]); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns a pointer to the payload of this packet.
 | ||||
|   ///\return A pointer to the payload of this packet.
 | ||||
|   char * Packet::getData() { | ||||
|     return data; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the size of the payload of this packet.
 | ||||
|   ///\return The size of the payload of this packet.
 | ||||
|   int Packet::getDataLen() { | ||||
|     return dataLen; | ||||
|    | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts the packet into a JSON value
 | ||||
|   ///\return A JSON::Value representation of this packet.
 | ||||
|   JSON::Value Packet::toJSON(){ | ||||
|     JSON::Value result; | ||||
|     unsigned int i = 8; | ||||
|  | @ -346,44 +386,56 @@ namespace DTSC { | |||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   ///\brief Returns the payloadsize of a part
 | ||||
|   long Part::getSize() { | ||||
|     return ((long)data[0] << 16) | ((long)data[1] << 8) | data[2]; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the payloadsize of a part
 | ||||
|   void Part::setSize(long newSize) { | ||||
|     data[0] = (newSize & 0xFF0000) >> 16; | ||||
|     data[1] = (newSize & 0x00FF00) >> 8; | ||||
|     data[2] = (newSize & 0x0000FF); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Retruns the duration of a part
 | ||||
|   short Part::getDuration() { | ||||
|     return btohs(data + 3); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the duration of a part
 | ||||
|   void Part::setDuration(short newDuration) { | ||||
|     htobs(data + 3, newDuration); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief returns the offset of a part
 | ||||
|   long Part::getOffset() { | ||||
|     return btohl(data + 5); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the offset of a part
 | ||||
|   void Part::setOffset(long newOffset) { | ||||
|     htobl(data + 5, newOffset); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the data of a part
 | ||||
|   char * Part::getData() { | ||||
|     return data; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a part to a human readable string
 | ||||
|   ///\param str The stringstream to append to
 | ||||
|   ///\param indent the amount of indentation needed
 | ||||
|   void Part::toPrettyString(std::ostream & str, int indent){ | ||||
|     str << std::string(indent, ' ') << "Part: Size(" << getSize() << "), Dur(" << getDuration() << "), Offset(" << getOffset() << ")" << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the byteposition of a keyframe
 | ||||
|   long long unsigned int Key::getBpos() { | ||||
|     return (((long long unsigned int)data[0] << 32) | (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4]); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the byteposition of a keyframe
 | ||||
|   void Key::setBpos(long long unsigned int newBpos) { | ||||
|     data[4] = newBpos & 0xFF; | ||||
|     data[3] = (newBpos >> 8) & 0xFF; | ||||
|  | @ -392,88 +444,113 @@ namespace DTSC { | |||
|     data[0] = (newBpos >> 32) & 0xFF; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the byteposition of a keyframe
 | ||||
|   long Key::getLength() { | ||||
|     return ((data[5] << 16) | (data[6] << 8) | data[7]); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the byteposition of a keyframe
 | ||||
|   void Key::setLength(long newLength) { | ||||
|     data[7] = newLength & 0xFF; | ||||
|     data[6] = (newLength >> 8) & 0xFF; | ||||
|     data[5] = (newLength >> 16) & 0xFF; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the number of a keyframe
 | ||||
|   unsigned short Key::getNumber() { | ||||
|     return btohs(data + 8); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the number of a keyframe
 | ||||
|   void Key::setNumber(unsigned short newNumber) { | ||||
|     htobs(data + 8, newNumber); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the number of parts of a keyframe
 | ||||
|   short Key::getParts() { | ||||
|     return btohs(data + 10); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the number of parts of a keyframe
 | ||||
|   void Key::setParts(short newParts) { | ||||
|     htobs(data + 10, newParts); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the timestamp of a keyframe
 | ||||
|   long Key::getTime() { | ||||
|     return btohl(data + 12); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the timestamp of a keyframe
 | ||||
|   void Key::setTime(long newTime) { | ||||
|     htobl(data + 12, newTime); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the data of this keyframe struct
 | ||||
|   char * Key::getData() { | ||||
|     return data; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a keyframe to a human readable string
 | ||||
|   ///\param str The stringstream to append to
 | ||||
|   ///\param indent the amount of indentation needed
 | ||||
|   void Key::toPrettyString(std::ostream & str, int indent){ | ||||
|     str << std::string(indent, ' ') << "Key " << getNumber() << ": Pos(" << getBpos() << "), Dur(" << getLength() << "), Parts(" << getParts() <<  "), Time(" << getTime() << ")" << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the duration of this fragment
 | ||||
|   long Fragment::getDuration() { | ||||
|     return btohl(data); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the duration of this fragment
 | ||||
|   void Fragment::setDuration(long newDuration) { | ||||
|     htobl(data, newDuration); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the length of this fragment
 | ||||
|   char Fragment::getLength() { | ||||
|     return data[4]; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the length of this fragment
 | ||||
|   void Fragment::setLength(char newLength) { | ||||
|     data[4] = newLength; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the number of the first keyframe in this fragment
 | ||||
|   short Fragment::getNumber() { | ||||
|     return btohs(data + 5); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the number of the first keyframe in this fragment
 | ||||
|   void Fragment::setNumber(short newNumber) { | ||||
|     htobs(data + 5, newNumber); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the size of a fragment
 | ||||
|   long Fragment::getSize() { | ||||
|     return btohl(data + 7); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the size of a fragement
 | ||||
|   void Fragment::setSize(long newSize) { | ||||
|     htobl(data + 7, newSize); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns thte data of this fragment structure
 | ||||
|   char * Fragment::getData() { | ||||
|     return data; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a fragment to a human readable string
 | ||||
|   ///\param str The stringstream to append to
 | ||||
|   ///\param indent the amount of indentation needed
 | ||||
|   void Fragment::toPrettyString(std::ostream & str, int indent){ | ||||
|     str << std::string(indent, ' ') << "Fragment " << getNumber() << ": Dur(" << getDuration() << "), Len(" << (int)getLength() << "), Size(" << getSize() << ")" << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Constructs an empty readOnlyTrack
 | ||||
|   readOnlyTrack::readOnlyTrack() { | ||||
|     fragments = NULL; | ||||
|     fragLen = 0; | ||||
|  | @ -493,6 +570,7 @@ namespace DTSC { | |||
|     fpks = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Constructs a readOnlyTrack from a JSON::Value
 | ||||
|   readOnlyTrack::readOnlyTrack(JSON::Value & trackRef) { | ||||
|     if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) { | ||||
|       fragments = (Fragment *)trackRef["fragments"].asStringRef().data(); | ||||
|  | @ -539,6 +617,7 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Constructs an empty track
 | ||||
|   Track::Track() { | ||||
|     trackID = 0; | ||||
|     firstms = 0; | ||||
|  | @ -553,6 +632,7 @@ namespace DTSC { | |||
|     fpks = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Constructs a track from a readOnlyTrack
 | ||||
|   Track::Track(const readOnlyTrack & rhs) { | ||||
|     trackID = rhs.trackID; | ||||
|     firstms = rhs.firstms; | ||||
|  | @ -581,6 +661,7 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Constructs a track from a JSON::Value
 | ||||
|   Track::Track(JSON::Value & trackRef) { | ||||
|     if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) { | ||||
|       Fragment * tmp = (Fragment *)trackRef["fragments"].asStringRef().data(); | ||||
|  | @ -618,6 +699,9 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Updates a track and its metadata given a DTSC::Packet.
 | ||||
|   ///
 | ||||
|   ///Will also insert keyframes on non-video tracks, and creates fragments
 | ||||
|   void Track::update(DTSC::Packet & pack) { | ||||
|     if (pack.getTime() < lastms) { | ||||
|       DEBUG_MSG(DLVL_WARN, "Received packets for track %d in wrong order (%d < %d) - ignoring!", (int)trackID, (int)pack.getTime(), (int)lastms); | ||||
|  | @ -679,6 +763,9 @@ namespace DTSC { | |||
|     fragments.rbegin()->setSize(fragments.rbegin()->getSize() + dataLen); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Updates a track and its metadata given a JSON::Value
 | ||||
|   ///
 | ||||
|   ///Will also insert keyframes on non-video tracks, and creates fragments
 | ||||
|   void Track::update(JSON::Value & pack) { | ||||
|     if (pack["time"].asInt() < lastms) { | ||||
|       DEBUG_MSG(DLVL_WARN, "Received packets for track %d in wrong order (%d < %d) - ignoring!", (int)trackID, (int)pack["time"].asInt(), (int)lastms); | ||||
|  | @ -737,6 +824,7 @@ namespace DTSC { | |||
|     fragments.rbegin()->setSize(fragments.rbegin()->getSize() + pack["data"].asStringRef().size()); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns a key given its number, or an empty key if the number is out of bounds
 | ||||
|   Key & Track::getKey(unsigned int keyNum) { | ||||
|     static Key empty; | ||||
|     if (keyNum < keys[0].getNumber()) { | ||||
|  | @ -748,6 +836,7 @@ namespace DTSC { | |||
|     return keys[keyNum - keys[0].getNumber()]; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns a unique identifier for a track
 | ||||
|   std::string readOnlyTrack::getIdentifier() { | ||||
|     std::stringstream result; | ||||
|     if (type == "") { | ||||
|  | @ -766,12 +855,14 @@ namespace DTSC { | |||
|     return result.str(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns a writable identifier for a track, to prevent overwrites on readout
 | ||||
|   std::string readOnlyTrack::getWritableIdentifier() { | ||||
|     std::stringstream result; | ||||
|     result << getIdentifier() << "_" << trackID; | ||||
|     return result.str(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Resets a track, clears all meta values
 | ||||
|   void Track::reset() { | ||||
|     fragments.clear(); | ||||
|     parts.clear(); | ||||
|  | @ -781,6 +872,7 @@ namespace DTSC { | |||
|     lastms = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates an empty read-only meta object
 | ||||
|   readOnlyMeta::readOnlyMeta() { | ||||
|     vod = false; | ||||
|     live = false; | ||||
|  | @ -790,6 +882,7 @@ namespace DTSC { | |||
|     bufferWindow = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates a read-only meta object from a given JSON::Value
 | ||||
|   readOnlyMeta::readOnlyMeta(JSON::Value & meta) { | ||||
|     vod = meta.isMember("vod") && meta["vod"]; | ||||
|     live = meta.isMember("live") && meta["live"]; | ||||
|  | @ -810,6 +903,10 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a read-only track to a human readable string
 | ||||
|   ///\param str The stringstream to append to
 | ||||
|   ///\param indent the amount of indentation needed
 | ||||
|   ///\param verbosity How verbose the output needs to be
 | ||||
|   void readOnlyTrack::toPrettyString(std::ostream & str, int indent, int verbosity){ | ||||
|     str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl; | ||||
|     str << std::string(indent + 2, ' ') << "ID: " << trackID << std::endl; | ||||
|  | @ -859,6 +956,7 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates an empty meta object
 | ||||
|   Meta::Meta() { | ||||
|     vod = false; | ||||
|     live = false; | ||||
|  | @ -867,6 +965,7 @@ namespace DTSC { | |||
|     bufferWindow = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates a meta object from a read-only meta object
 | ||||
|   Meta::Meta(const readOnlyMeta & rhs) { | ||||
|     vod = rhs.vod; | ||||
|     live = rhs.live; | ||||
|  | @ -878,6 +977,7 @@ namespace DTSC { | |||
|     moreheader = rhs.moreheader; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates a meta object from a JSON::Value
 | ||||
|   Meta::Meta(JSON::Value & meta) { | ||||
|     vod = meta.isMember("vod") && meta["vod"]; | ||||
|     live = meta.isMember("live") && meta["live"]; | ||||
|  | @ -898,6 +998,7 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Updates a meta object given a JSON::Value
 | ||||
|   void Meta::update(JSON::Value & pack) { | ||||
|     vod = pack.isMember("bpos"); | ||||
|     live = !vod; | ||||
|  | @ -906,6 +1007,7 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Updates a meta object given a DTSC::Packet
 | ||||
|   void Meta::update(DTSC::Packet & pack) { | ||||
|     vod = pack.hasMember("bpos"); | ||||
|     live = !vod; | ||||
|  | @ -914,6 +1016,10 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a track to a human readable string
 | ||||
|   ///\param str The stringstream to append to
 | ||||
|   ///\param indent the amount of indentation needed
 | ||||
|   ///\param verbosity How verbose the output needs to be
 | ||||
|   void Track::toPrettyString(std::ostream & str, int indent, int verbosity){ | ||||
|     str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl; | ||||
|     str << std::string(indent + 2, ' ') << "ID: " << trackID << std::endl; | ||||
|  | @ -963,6 +1069,7 @@ namespace DTSC { | |||
|     } | ||||
|   } | ||||
|    | ||||
|   ///\brief Converts a short to a char*
 | ||||
|   char * convertShort(short input) { | ||||
|     static char result[2]; | ||||
|     result[0] = (input >> 8) & 0xFF; | ||||
|  | @ -970,6 +1077,7 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts an integer to a char*
 | ||||
|   char * convertInt(int input) { | ||||
|     static char result[4]; | ||||
|     result[0] = (input >> 24) & 0xFF; | ||||
|  | @ -979,6 +1087,7 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a long long to a char*
 | ||||
|   char * convertLongLong(long long int input) { | ||||
|     static char result[8]; | ||||
|     result[0] = (input >> 56) & 0xFF; | ||||
|  | @ -992,6 +1101,7 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Determines the "packed" size of a read-only track
 | ||||
|   int readOnlyTrack::getSendLen() { | ||||
|     int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size(); | ||||
|     result += fragLen * 11; | ||||
|  | @ -1012,6 +1122,7 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Determines the "packed" size of a track
 | ||||
|   int Track::getSendLen() { | ||||
|     int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size(); | ||||
|     result += fragments.size() * 11; | ||||
|  | @ -1032,15 +1143,22 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
|    | ||||
|   ///\brief Writes a pointer to the specified destination
 | ||||
|   ///
 | ||||
|   ///Does a memcpy and increases the destination pointer accordingly
 | ||||
|   static void writePointer(char *& p, const char * src, unsigned int len){ | ||||
|     memcpy(p, src, len); | ||||
|     p += len; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Writes a pointer to the specified destination
 | ||||
|   ///
 | ||||
|   ///Does a memcpy and increases the destination pointer accordingly
 | ||||
|   static void writePointer(char *& p, const std::string & src){ | ||||
|     writePointer(p, src.data(), src.size()); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Writes a read-only track to a pointer
 | ||||
|   void readOnlyTrack::writeTo(char *& p){ | ||||
|     std::string iden = getWritableIdentifier(); | ||||
|     writePointer(p, convertShort(iden.size()), 2); | ||||
|  | @ -1102,6 +1220,7 @@ namespace DTSC { | |||
|     writePointer(p, "\000\000\356", 3);//End this track Object
 | ||||
|   } | ||||
|    | ||||
|   ///\brief Writes a read-only track to a socket
 | ||||
|   void readOnlyTrack::send(Socket::Connection & conn) { | ||||
|     conn.SendNow(convertShort(getWritableIdentifier().size()), 2); | ||||
|     conn.SendNow(getWritableIdentifier()); | ||||
|  | @ -1162,6 +1281,7 @@ namespace DTSC { | |||
|     conn.SendNow("\000\000\356", 3);//End this track Object
 | ||||
|   } | ||||
|    | ||||
|   ///\brief Writes a track to a pointer
 | ||||
|   void Track::writeTo(char *& p){ | ||||
|     writePointer(p, convertShort(getWritableIdentifier().size()), 2); | ||||
|     writePointer(p, getWritableIdentifier()); | ||||
|  | @ -1228,6 +1348,7 @@ namespace DTSC { | |||
|     writePointer(p, "\000\000\356", 3);//End this track Object
 | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Writes a track to a socket
 | ||||
|   void Track::send(Socket::Connection & conn) { | ||||
|     conn.SendNow(convertShort(getWritableIdentifier().size()), 2); | ||||
|     conn.SendNow(getWritableIdentifier()); | ||||
|  | @ -1294,6 +1415,7 @@ namespace DTSC { | |||
|     conn.SendNow("\000\000\356", 3);//End this track Object
 | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Determines the "packed" size of a read-only meta object 
 | ||||
|   unsigned int readOnlyMeta::getSendLen(){ | ||||
|     unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; | ||||
|     for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|  | @ -1302,6 +1424,7 @@ namespace DTSC { | |||
|     return dataLen; | ||||
|   } | ||||
|    | ||||
|   ///\brief Writes a read-only meta object to a pointer
 | ||||
|   void readOnlyMeta::writeTo(char * p){ | ||||
|     int dataLen = getSendLen(); | ||||
|     writePointer(p, DTSC::Magic_Header, 4); | ||||
|  | @ -1332,6 +1455,7 @@ namespace DTSC { | |||
|     writePointer(p, "\000\000\356", 3);//End global object
 | ||||
|   } | ||||
|    | ||||
|   ///\brief Writes a read-only meta object to a socket
 | ||||
|   void readOnlyMeta::send(Socket::Connection & conn) { | ||||
|     int dataLen = getSendLen(); | ||||
|     conn.SendNow(DTSC::Magic_Header, 4); | ||||
|  | @ -1362,6 +1486,7 @@ namespace DTSC { | |||
|     conn.SendNow("\000\000\356", 3);//End global object
 | ||||
|   } | ||||
|    | ||||
|   ///\brief Determines the "packed" size of a meta object 
 | ||||
|   unsigned int Meta::getSendLen(){ | ||||
|     unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; | ||||
|     for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|  | @ -1370,6 +1495,7 @@ namespace DTSC { | |||
|     return dataLen; | ||||
|   } | ||||
|    | ||||
|   ///\brief Writes a meta object to a pointer
 | ||||
|   void Meta::writeTo(char * p){ | ||||
|     int dataLen = getSendLen(); | ||||
|     writePointer(p, DTSC::Magic_Header, 4); | ||||
|  | @ -1400,6 +1526,7 @@ namespace DTSC { | |||
|     writePointer(p, "\000\000\356", 3);//End global object
 | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Writes a meta object to a socket
 | ||||
|   void Meta::send(Socket::Connection & conn) { | ||||
|     int dataLen = getSendLen(); | ||||
|     conn.SendNow(DTSC::Magic_Header, 4); | ||||
|  | @ -1430,6 +1557,7 @@ namespace DTSC { | |||
|     conn.SendNow("\000\000\356", 3);//End global object
 | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a read-only track to a JSON::Value
 | ||||
|   JSON::Value readOnlyTrack::toJSON() { | ||||
|     JSON::Value result; | ||||
|     if (fragments) { | ||||
|  | @ -1467,6 +1595,7 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a track to a JSON::Value
 | ||||
|   JSON::Value Track::toJSON() { | ||||
|     JSON::Value result; | ||||
|     std::string tmp; | ||||
|  | @ -1513,6 +1642,7 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a meta object to a JSON::Value
 | ||||
|   JSON::Value Meta::toJSON() { | ||||
|     JSON::Value result; | ||||
|     for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|  | @ -1534,6 +1664,10 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a read-only meta object to a human readable string
 | ||||
|   ///\param str The stringstream to append to
 | ||||
|   ///\param indent the amount of indentation needed
 | ||||
|   ///\param verbosity How verbose the output needs to be
 | ||||
|   void readOnlyMeta::toPrettyString(std::ostream & str, int indent, int verbosity){ | ||||
|     for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|       it->second.toPrettyString(str, indent, verbosity); | ||||
|  | @ -1553,6 +1687,10 @@ namespace DTSC { | |||
|     str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Converts a meta object to a human readable string
 | ||||
|   ///\param str The stringstream to append to
 | ||||
|   ///\param indent the amount of indentation needed
 | ||||
|   ///\param verbosity How verbose the output needs to be
 | ||||
|   void Meta::toPrettyString(std::ostream & str, int indent, int verbosity){ | ||||
|     for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|       it->second.toPrettyString(str, indent, verbosity); | ||||
|  | @ -1572,6 +1710,7 @@ namespace DTSC { | |||
|     str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl; | ||||
|   } | ||||
|    | ||||
|   ///\brief Converts a read-only meta object to a JSON::Value
 | ||||
|   JSON::Value readOnlyMeta::toJSON() { | ||||
|     JSON::Value result; | ||||
|     for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|  | @ -1593,12 +1732,14 @@ namespace DTSC { | |||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Resets a meta object, removes all unimportant meta values
 | ||||
|   void Meta::reset() { | ||||
|     for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|       it->second.reset(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns whether a read-only meta object is fixed or not
 | ||||
|   bool readOnlyMeta::isFixed() { | ||||
|     for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|       if (!it->second.keyLen || !(it->second.keys[it->second.keyLen - 1].getBpos())) { | ||||
|  | @ -1608,6 +1749,7 @@ namespace DTSC { | |||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns whether a meta object is fixed or not
 | ||||
|   bool Meta::isFixed() { | ||||
|     for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||
|       if (it->second.type == "meta" || it->second.type == "") { | ||||
|  |  | |||
|  | @ -7,8 +7,10 @@ | |||
| #include <cstdio> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include "defines.h" | ||||
| #include "shared_memory.h" | ||||
| #include "stream.h" | ||||
| 
 | ||||
| namespace IPC { | ||||
|   /// Stores a long value of val in network order to the pointer p.
 | ||||
|  | @ -19,6 +21,7 @@ namespace IPC { | |||
|     p[3] = val & 0xFF; | ||||
|   } | ||||
| 
 | ||||
|   /// Stores a long long value of val in network order to the pointer p.
 | ||||
|   static void htobll(char * p, long long val) { | ||||
|     p[0] = (val >> 56) & 0xFF; | ||||
|     p[1] = (val >> 48) & 0xFF; | ||||
|  | @ -30,15 +33,177 @@ namespace IPC { | |||
|     p[7] = val & 0xFF; | ||||
|   } | ||||
| 
 | ||||
|   /// Reads a long value of p in host order to val.
 | ||||
|   static void btohl(char * p, long & val) { | ||||
|     val = ((long)p[0] << 24) | ((long)p[1] << 16) | ((long)p[2] << 8) | p[3]; | ||||
|   } | ||||
| 
 | ||||
|   /// Reads a long long value of p in host order to val.
 | ||||
|   static void btohll(char * p, long long & val) { | ||||
|     val = ((long long)p[0] << 56) | ((long long)p[1] << 48) | ((long long)p[2] << 40) | ((long long)p[3] << 32) | ((long long)p[4] << 24) | ((long long)p[5] << 16) | ((long long)p[6] << 8) | p[7]; | ||||
|   } | ||||
| 
 | ||||
| #if !defined __APPLE__ && !defined __CYGWIN__ | ||||
|   ///\brief Empty semaphore constructor, clears all values
 | ||||
|   semaphore::semaphore() { | ||||
| #ifdef __CYGWIN__ | ||||
|     mySem = 0; | ||||
| #else | ||||
|     mySem = SEM_FAILED; | ||||
| #endif | ||||
|     myName = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Constructs a named semaphore
 | ||||
|   ///\param name The name of the semaphore
 | ||||
|   ///\param oflag The flags with which to open the semaphore
 | ||||
|   ///\param mode The mode in which to create the semaphore, if O_CREAT is given in oflag, ignored otherwise
 | ||||
|   ///\param value The initial value of the semaphore if O_CREAT is given in oflag, ignored otherwise
 | ||||
|   semaphore::semaphore(const char * name, int oflag, mode_t mode, unsigned int value) { | ||||
| #ifdef __CYGWIN__ | ||||
|     mySem = 0; | ||||
| #else | ||||
|     mySem = SEM_FAILED; | ||||
| #endif | ||||
|     open(name, oflag, mode, value); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief The deconstructor
 | ||||
|   semaphore::~semaphore() {} | ||||
| 
 | ||||
|   ///\brief Returns whether we have a valid semaphore
 | ||||
|   semaphore::operator bool() const { | ||||
| #ifdef __CYGWIN__ | ||||
|     return mySem != 0; | ||||
| #else | ||||
|     return mySem != SEM_FAILED; | ||||
| #endif | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Opens a semaphore
 | ||||
|   ///
 | ||||
|   ///Closes currently opened semaphore if needed
 | ||||
|   ///\param name The name of the semaphore
 | ||||
|   ///\param oflag The flags with which to open the semaphore
 | ||||
|   ///\param mode The mode in which to create the semaphore, if O_CREAT is given in oflag, ignored otherwise
 | ||||
|   ///\param value The initial value of the semaphore if O_CREAT is given in oflag, ignored otherwise
 | ||||
|   void semaphore::open(const char * name, int oflag, mode_t mode, unsigned int value) { | ||||
|     close(); | ||||
| #ifdef __CYGWIN__ | ||||
|     mySem = CreateSemaphore(0, value, 1 , std::string("Global\\" + std::string(name)).c_str()); | ||||
| #else | ||||
|     if (oflag & O_CREAT) { | ||||
|       mySem = sem_open(name, oflag, mode, value); | ||||
|     } else { | ||||
|       mySem = sem_open(name, oflag); | ||||
|     } | ||||
| #endif | ||||
|     myName = (char *)name; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns the current value of the semaphore
 | ||||
|   int semaphore::getVal() const { | ||||
|     int res; | ||||
| #ifdef __CYGWIN__ | ||||
|     ReleaseSemaphore(mySem, 0, &res);//not really release.... just checking to see if I can get the value this way
 | ||||
| #else | ||||
|     sem_getvalue(mySem, &res); | ||||
| #endif | ||||
|     return res; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Posts to the semaphore, increases its value by one
 | ||||
|   void semaphore::post() { | ||||
|     if (*this) { | ||||
| #ifdef __CYGWIN__ | ||||
|       ReleaseSemaphore(mySem, 1, 0); | ||||
| #else | ||||
|       sem_post(mySem); | ||||
| #endif | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Waits for the semaphore, decreases its value by one
 | ||||
|   void semaphore::wait() { | ||||
|     if (*this) { | ||||
| #ifdef __CYGWIN__ | ||||
|       WaitForSingleObject(mySem, INFINITE); | ||||
| #else | ||||
|       int tmp; | ||||
|       do { | ||||
|         tmp = sem_wait(mySem); | ||||
|       } while (tmp == -1 && errno == EINTR); | ||||
| #endif | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Tries to wait for the semaphore, returns true if successfull, false otherwise
 | ||||
|   bool semaphore::tryWait() { | ||||
|     bool result; | ||||
| #ifdef __CYGWIN__ | ||||
|     result = WaitForSingleObject(mySem, 0);//wait at most 1ms
 | ||||
| #else | ||||
|     result = sem_trywait(mySem); | ||||
| #endif | ||||
|     return (result == 0); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Closes the currently opened semaphore
 | ||||
|   void semaphore::close() { | ||||
|     if (*this) { | ||||
| #ifdef __CYGWIN__ | ||||
|       CloseHandle(mySem); | ||||
|       mySem = 0; | ||||
| #else | ||||
|       sem_close(mySem); | ||||
|       mySem = SEM_FAILED; | ||||
| #endif | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Unlinks the previously opened semaphore
 | ||||
|   void semaphore::unlink() { | ||||
|     close(); | ||||
| #ifndef __CYGWIN__ | ||||
|     if (myName) { | ||||
|       sem_unlink(myName); | ||||
|     } | ||||
| #endif | ||||
|     myName = 0; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   ///\brief Unmaps a shared page if allowed
 | ||||
|   void sharedPage::unmap() { | ||||
|     if (mapped && len) { | ||||
| #ifdef __CYGWIN__ | ||||
|       UnmapViewOfFile(mapped); | ||||
| #else | ||||
|       munmap(mapped, len); | ||||
| #endif | ||||
|     } | ||||
|     mapped = 0; | ||||
|     len = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Closes a shared page if allowed
 | ||||
|   void sharedPage::close() { | ||||
|     if (handle > 0) { | ||||
| #ifdef __CYGWIN__ | ||||
|       CloseHandle(handle); | ||||
| #else | ||||
|       ::close(handle); | ||||
| #endif | ||||
|     } | ||||
|     handle = 0; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| #if !defined __APPLE__ | ||||
|   ///brief Creates a shared page
 | ||||
|   ///\param name_ The name of the page to be created
 | ||||
|   ///\param len_ The size to make the page
 | ||||
|   ///\param master_ Whether to create or merely open the page
 | ||||
|   ///\param autoBackoff When only opening the page, wait for it to appear or fail
 | ||||
|   sharedPage::sharedPage(std::string name_, unsigned int len_, bool master_, bool autoBackoff) : handle(0), name(name_), len(len_), master(master_), mapped(NULL) { | ||||
|     handle = 0; | ||||
|     name = name_; | ||||
|  | @ -47,6 +212,9 @@ namespace IPC { | |||
|     mapped = 0; | ||||
|     init(name_, len_, master_, autoBackoff); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates a copy of a shared page
 | ||||
|   ///\param rhs The page to copy
 | ||||
|   sharedPage::sharedPage(const sharedPage & rhs) { | ||||
|     handle = 0; | ||||
|     name = ""; | ||||
|  | @ -55,24 +223,60 @@ namespace IPC { | |||
|     mapped = 0; | ||||
|     init(rhs.name, rhs.len, rhs.master); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns whether the shared page is valid or not
 | ||||
|   sharedPage::operator bool() const { | ||||
|     return mapped != 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Assignment operator
 | ||||
|   void sharedPage::operator =(sharedPage & rhs) { | ||||
|     init(rhs.name, rhs.len, rhs.master); | ||||
|     rhs.master = false;//Make sure the memory does not get unlinked
 | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   #ifdef __CYGWIN__ | ||||
|     ///\brief Initialize a page, de-initialize before if needed
 | ||||
|     ///\param name_ The name of the page to be created
 | ||||
|     ///\param len_ The size to make the page
 | ||||
|     ///\param master_ Whether to create or merely open the page
 | ||||
|     ///\param autoBackoff When only opening the page, wait for it to appear or fail
 | ||||
|     void sharedPage::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { | ||||
|     if (mapped && len){ | ||||
|       munmap(mapped,len); | ||||
|       unmap(); | ||||
|       close(); | ||||
|       name = name_; | ||||
|       len = len_; | ||||
|       master = master_; | ||||
|       mapped = 0; | ||||
|       if (name.size()) { | ||||
|         if (master){ | ||||
|           handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, len, name.c_str()); | ||||
|         }else{ | ||||
|           handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name.c_str());   | ||||
|         } | ||||
|         if (!handle) { | ||||
|           DEBUG_MSG(DLVL_FAIL, "%s for page %s failed: %s", (master ? "CreateFileMapping" : "OpenFileMapping"), name.c_str(), strerror(errno)); | ||||
|           return; | ||||
|         } | ||||
|         mapped = (char *)MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, 0); | ||||
|         if (!mapped) { | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   #else | ||||
|     ///\brief Initialize a page, de-initialize before if needed
 | ||||
|     ///\param name_ The name of the page to be created
 | ||||
|     ///\param len_ The size to make the page
 | ||||
|     ///\param master_ Whether to create or merely open the page
 | ||||
|     ///\param autoBackoff When only opening the page, wait for it to appear or fail
 | ||||
|     void sharedPage::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { | ||||
|       unmap(); | ||||
|       if (master) { | ||||
|         shm_unlink(name.c_str()); | ||||
|       } | ||||
|     if (handle > 0){ | ||||
|       close(handle); | ||||
|     } | ||||
|     handle = 0; | ||||
|       close(); | ||||
|       name = name_; | ||||
|       len = len_; | ||||
|       master = master_; | ||||
|  | @ -120,19 +324,27 @@ namespace IPC { | |||
|         } | ||||
|       } | ||||
|     } | ||||
|   #endif | ||||
| 
 | ||||
|   ///\brief Default destructor
 | ||||
|   sharedPage::~sharedPage() { | ||||
|     if (mapped && len){ | ||||
|       munmap(mapped,len); | ||||
|     } | ||||
|     unmap(); | ||||
|     if (master) { | ||||
| #ifndef __CYGWIN__ | ||||
|       shm_unlink(name.c_str()); | ||||
| #endif | ||||
|     } | ||||
|     if (handle > 0){ | ||||
|       close(handle); | ||||
|     } | ||||
|     close(); | ||||
|   } | ||||
| 
 | ||||
| #else | ||||
|   sharedPage::sharedPage(std::string name_, unsigned int len_, bool master_, bool autoBackoff) /*: handle(0), name(name_), len(len_), master(master_), mapped(NULL) */{ | ||||
| 
 | ||||
|   ///brief Creates a shared page
 | ||||
|   ///\param name_ The name of the page to be created
 | ||||
|   ///\param len_ The size to make the page
 | ||||
|   ///\param master_ Whether to create or merely open the page
 | ||||
|   ///\param autoBackoff When only opening the page, wait for it to appear or fail
 | ||||
|   sharedPage::sharedPage(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { | ||||
|     handle = 0; | ||||
|     name = name_; | ||||
|     len = len_; | ||||
|  | @ -140,6 +352,9 @@ namespace IPC { | |||
|     mapped = 0; | ||||
|     init(name_, len_, master_, autoBackoff); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates a copy of a shared page
 | ||||
|   ///\param rhs The page to copy
 | ||||
|   sharedPage::sharedPage(const sharedPage & rhs) { | ||||
|     handle = 0; | ||||
|     name = ""; | ||||
|  | @ -148,18 +363,23 @@ namespace IPC { | |||
|     mapped = 0; | ||||
|     init(rhs.name, rhs.len, rhs.master); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Default destructor
 | ||||
|   sharedPage::~sharedPage() { | ||||
|     if (mapped && len){ | ||||
|       munmap(mapped,len); | ||||
|     } | ||||
|     unmap(); | ||||
|     if (master) { | ||||
|       unlink(name.c_str()); | ||||
|     } | ||||
|     if (handle > 0){ | ||||
|     close(handle); | ||||
|   } | ||||
|   } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|   ///brief Creates a shared file
 | ||||
|   ///\param name_ The name of the file to be created
 | ||||
|   ///\param len_ The size to make the file
 | ||||
|   ///\param master_ Whether to create or merely open the file
 | ||||
|   ///\param autoBackoff When only opening the file, wait for it to appear or fail
 | ||||
|   sharedFile::sharedFile(std::string name_, unsigned int len_, bool master_, bool autoBackoff) : handle(0), name(name_), len(len_), master(master_), mapped(NULL) { | ||||
|     handle = 0; | ||||
|     name = name_; | ||||
|  | @ -168,7 +388,11 @@ namespace IPC { | |||
|     mapped = 0; | ||||
|     init(name_, len_, master_, autoBackoff); | ||||
|   } | ||||
|   sharedFile::sharedFile(const sharedPage & rhs){ | ||||
| 
 | ||||
| 
 | ||||
|   ///\brief Creates a copy of a shared page
 | ||||
|   ///\param rhs The page to copy
 | ||||
|   sharedFile::sharedFile(const sharedFile & rhs) { | ||||
|     handle = 0; | ||||
|     name = ""; | ||||
|     len = 0; | ||||
|  | @ -176,17 +400,34 @@ namespace IPC { | |||
|     mapped = 0; | ||||
|     init(rhs.name, rhs.len, rhs.master); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Returns whether the shared file is valid or not
 | ||||
|   sharedFile::operator bool() const { | ||||
|     return mapped != 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Assignment operator
 | ||||
|   void sharedFile::operator =(sharedFile & rhs) { | ||||
|     init(rhs.name, rhs.len, rhs.master); | ||||
|     rhs.master = false;//Make sure the memory does not get unlinked
 | ||||
|   } | ||||
|   void sharedFile::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { | ||||
| 
 | ||||
|   ///\brief Unmaps a shared file if allowed
 | ||||
|   void sharedFile::unmap() { | ||||
|     if (mapped && len) { | ||||
|       munmap(mapped, len); | ||||
|       mapped = 0; | ||||
|       len = 0; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Initialize a page, de-initialize before if needed
 | ||||
|   ///\param name_ The name of the page to be created
 | ||||
|   ///\param len_ The size to make the page
 | ||||
|   ///\param master_ Whether to create or merely open the page
 | ||||
|   ///\param autoBackoff When only opening the page, wait for it to appear or fail
 | ||||
|   void sharedFile::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) { | ||||
|     unmap(); | ||||
|     if (master) { | ||||
|       unlink(name.c_str()); | ||||
|     } | ||||
|  | @ -200,17 +441,17 @@ namespace IPC { | |||
|     mapped = 0; | ||||
|     if (name.size()) { | ||||
|       /// \todo Use ACCESSPERMS instead of 0600?
 | ||||
|       handle = open(name.c_str(), ( master ? O_CREAT | O_TRUNC | O_EXCL : 0 )| O_RDWR, (mode_t)0600); | ||||
|       handle = open(std::string(Util::getTmpFolder() + name).c_str(), (master ? O_CREAT | O_TRUNC | O_EXCL : 0) | O_RDWR, (mode_t)0600); | ||||
|       if (handle == -1) { | ||||
|         if (master) { | ||||
|           DEBUG_MSG(DLVL_HIGH, "Overwriting old file for %s", name.c_str()); | ||||
|           handle = open(name.c_str(), O_CREAT | O_TRUNC | O_RDWR, (mode_t)0600); | ||||
|           handle = open(std::string(Util::getTmpFolder() + name).c_str(), O_CREAT | O_TRUNC | O_RDWR, (mode_t)0600); | ||||
|         } else { | ||||
|           int i = 0; | ||||
|           while (i < 10 && handle == -1 && autoBackoff) { | ||||
|             i++; | ||||
|             Util::sleep(1000); | ||||
|             handle = open(name.c_str(), O_RDWR, (mode_t)0600); | ||||
|             handle = open(std::string(Util::getTmpFolder() + name).c_str(), O_RDWR, (mode_t)0600); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | @ -238,10 +479,10 @@ namespace IPC { | |||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Default destructor
 | ||||
|   sharedFile::~sharedFile() { | ||||
|     if (mapped && len){ | ||||
|       munmap(mapped,len); | ||||
|     } | ||||
|     unmap(); | ||||
|     if (master) { | ||||
|       unlink(name.c_str()); | ||||
|     } | ||||
|  | @ -250,121 +491,147 @@ namespace IPC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   ///\brief StatExchange constructor, sets the datapointer to the given value
 | ||||
|   statExchange::statExchange(char * _data) : data(_data) {} | ||||
| 
 | ||||
|   ///\brief Sets timestamp of the current stats
 | ||||
|   void statExchange::now(long long int time) { | ||||
|     htobll(data, time); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets timestamp of the current stats
 | ||||
|   long long int statExchange::now() { | ||||
|     long long int result; | ||||
|     btohll(data, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets time currently connected
 | ||||
|   void statExchange::time(long time) { | ||||
|     htobl(data + 8, time); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets time currently connected
 | ||||
|   long statExchange::time() { | ||||
|     long result; | ||||
|     btohl(data + 8, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the last viewing second of this user
 | ||||
|   void statExchange::lastSecond(long time) { | ||||
|     htobl(data + 12, time); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets the last viewing second of this user
 | ||||
|   long statExchange::lastSecond() { | ||||
|     long result; | ||||
|     btohl(data + 12, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the amount of bytes received
 | ||||
|   void statExchange::down(long long int bytes) { | ||||
|     htobll(data + 16, bytes); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets the amount of bytes received
 | ||||
|   long long int statExchange::down() { | ||||
|     long long int result; | ||||
|     btohll(data + 16, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the amount of bytes sent
 | ||||
|   void statExchange::up(long long int bytes) { | ||||
|     htobll(data + 24, bytes); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets the amount of bytes sent
 | ||||
|   long long int statExchange::up() { | ||||
|     long long int result; | ||||
|     btohll(data + 24, result); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the host of this connection
 | ||||
|   void statExchange::host(std::string name) { | ||||
|     memcpy(data + 32, name.c_str(), std::min((int)name.size(), 16)); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets the host of this connection
 | ||||
|   std::string statExchange::host() { | ||||
|     return std::string(data + 32, std::min((int)strlen(data + 32), 16)); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the name of the stream this user is viewing
 | ||||
|   void statExchange::streamName(std::string name) { | ||||
|     memcpy(data + 48, name.c_str(), std::min((int)name.size(), 20)); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets the name of the stream this user is viewing
 | ||||
|   std::string statExchange::streamName() { | ||||
|     return std::string(data + 48, std::min((int)strlen(data + 48), 20)); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Sets the name of the connector through which this user is viewing
 | ||||
|   void statExchange::connector(std::string name) { | ||||
|     memcpy(data + 68, name.c_str(), std::min((int)name.size(), 20)); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Gets the name of the connector through which this user is viewing
 | ||||
|   std::string statExchange::connector() { | ||||
|     return std::string(data + 68, std::min((int)strlen(data + 68), 20)); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   semGuard::semGuard(sem_t * semaphore) : mySemaphore(semaphore) { | ||||
|     sem_wait(mySemaphore); | ||||
|   ///\brief Creates a semaphore guard, locks the semaphore on call
 | ||||
|   semGuard::semGuard(semaphore thisSemaphore) : mySemaphore(thisSemaphore) { | ||||
|     mySemaphore.wait(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Destructs a semaphore guard, unlocks the semaphore on call
 | ||||
|   semGuard::~semGuard() { | ||||
|     sem_post(mySemaphore); | ||||
|     mySemaphore.post(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Default constructor, erases all the values
 | ||||
|   sharedServer::sharedServer() { | ||||
|     mySemaphore = 0; | ||||
|     payLen = 0; | ||||
|     hasCounter = false; | ||||
|     amount = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Desired constructor, initializes after cleaning.
 | ||||
|   ///\param name The basename of this server
 | ||||
|   ///\param len The lenght of the payload
 | ||||
|   ///\param withCounter Whether the content should have a counter
 | ||||
|   sharedServer::sharedServer(std::string name, int len, bool withCounter) { | ||||
|     sharedServer(); | ||||
|     init(name, len, withCounter); | ||||
|   } | ||||
| 
 | ||||
|      | ||||
|   ///\brief Initialize the server
 | ||||
|   ///\param name The basename of this server
 | ||||
|   ///\param len The lenght of the payload
 | ||||
|   ///\param withCounter Whether the content should have a counter
 | ||||
|   void sharedServer::init(std::string name, int len, bool withCounter) { | ||||
|     amount = 0; | ||||
|     if (mySemaphore != SEM_FAILED) { | ||||
|       sem_close(mySemaphore); | ||||
|     if (mySemaphore) { | ||||
|       mySemaphore.close(); | ||||
|     } | ||||
|     if (baseName != "") { | ||||
|       sem_unlink(std::string("/" + baseName).c_str()); | ||||
|       mySemaphore.unlink(); | ||||
|     } | ||||
|     myPages.clear(); | ||||
|     baseName = name; | ||||
|     payLen = len; | ||||
|     hasCounter = withCounter; | ||||
|     mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_CREAT | O_EXCL | O_RDWR, ACCESSPERMS, 1); | ||||
|     if (mySemaphore == SEM_FAILED) { | ||||
|       mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1); | ||||
|     mySemaphore.open(std::string("/" + baseName).c_str(), O_CREAT | O_EXCL | O_RDWR, ACCESSPERMS, 1); | ||||
|     if (!mySemaphore) { | ||||
|       mySemaphore.open(std::string("/" + baseName).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1); | ||||
|     } | ||||
|     if (mySemaphore == SEM_FAILED) { | ||||
|     if (!mySemaphore) { | ||||
|       DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno)); | ||||
|       return; | ||||
|     } | ||||
|  | @ -375,17 +642,18 @@ namespace IPC { | |||
|     newPage(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief The deconstructor
 | ||||
|   sharedServer::~sharedServer() { | ||||
|     if (mySemaphore != SEM_FAILED) { | ||||
|       sem_close(mySemaphore); | ||||
|     } | ||||
|     sem_unlink(std::string("/" + baseName).c_str()); | ||||
|     mySemaphore.close(); | ||||
|     mySemaphore.unlink(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Determines whether a sharedServer is valid
 | ||||
|   sharedServer::operator bool() const { | ||||
|     return myPages.size(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates the next page with the correct size
 | ||||
|   void sharedServer::newPage() { | ||||
|     semGuard tmpGuard(mySemaphore); | ||||
|     sharedPage tmp(std::string(baseName + (char)(myPages.size() + (int)'A')), (4096 << myPages.size()), true); | ||||
|  | @ -394,6 +662,7 @@ namespace IPC { | |||
|     DEBUG_MSG(DLVL_MEDIUM, "Added a new page: %s", tmp.name.c_str()); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Deletes the highest allocated page
 | ||||
|   void sharedServer::deletePage() { | ||||
|     if (myPages.size() == 1) { | ||||
|       DEBUG_MSG(DLVL_WARN, "Can't remove last page for %s", baseName.c_str()); | ||||
|  | @ -403,6 +672,7 @@ namespace IPC { | |||
|     myPages.erase((*myPages.rbegin())); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Determines whether an id is currently in use or not
 | ||||
|   bool sharedServer::isInUse(unsigned int id) { | ||||
|     unsigned int i = 0; | ||||
|     for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) { | ||||
|  | @ -432,6 +702,7 @@ namespace IPC { | |||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Parse each of the possible payload pieces, and runs a callback on it if in use.
 | ||||
|   void sharedServer::parseEach(void (*callback)(char * data, size_t len, unsigned int id)) { | ||||
|     char * empty = 0; | ||||
|     if (!hasCounter) { | ||||
|  | @ -522,6 +793,7 @@ namespace IPC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Creates an empty shared client
 | ||||
|   sharedClient::sharedClient() { | ||||
|     hasCounter = 0; | ||||
|     payLen = 0; | ||||
|  | @ -529,17 +801,19 @@ namespace IPC { | |||
|     mySemaphore = 0; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Copy constructor for sharedClients
 | ||||
|   ///\param rhs The client ro copy
 | ||||
|   sharedClient::sharedClient(const sharedClient & rhs) { | ||||
|     baseName = rhs.baseName; | ||||
|     payLen = rhs.payLen; | ||||
|     hasCounter = rhs.hasCounter; | ||||
| #ifdef __APPLE__ | ||||
|     //note: O_CREAT is only needed for mac, probably
 | ||||
|     mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR | O_CREAT, 0); | ||||
|     mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR | O_CREAT, 0); | ||||
| #else | ||||
|     mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR); | ||||
|     mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR); | ||||
| #endif | ||||
|     if (mySemaphore == SEM_FAILED) { | ||||
|     if (!mySemaphore) { | ||||
|       DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno)); | ||||
|       return; | ||||
|     } | ||||
|  | @ -548,17 +822,18 @@ namespace IPC { | |||
|     offsetOnPage = rhs.offsetOnPage; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Assignment operator
 | ||||
|   void sharedClient::operator =(const sharedClient & rhs) { | ||||
|     baseName = rhs.baseName; | ||||
|     payLen = rhs.payLen; | ||||
|     hasCounter = rhs.hasCounter; | ||||
| #ifdef __APPLE__ | ||||
|     //note: O_CREAT is only needed for mac, probably
 | ||||
|     mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR | O_CREAT, 0); | ||||
|     mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR | O_CREAT, 0); | ||||
| #else | ||||
|     mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR); | ||||
|     mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR); | ||||
| #endif | ||||
|     if (mySemaphore == SEM_FAILED) { | ||||
|     if (!mySemaphore) { | ||||
|       DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno)); | ||||
|       return; | ||||
|     } | ||||
|  | @ -567,14 +842,18 @@ namespace IPC { | |||
|     offsetOnPage = rhs.offsetOnPage; | ||||
|   } | ||||
| 
 | ||||
|   ///\brief SharedClient Constructor, allocates space on the correct page.
 | ||||
|   ///\param name The basename of the server to connect to
 | ||||
|   ///\param len The size of the payload to allocate
 | ||||
|   ///\param withCounter Whether or not this payload has a counter
 | ||||
|   sharedClient::sharedClient(std::string name, int len, bool withCounter) : baseName(name), payLen(len), offsetOnPage(-1), hasCounter(withCounter) { | ||||
| #ifdef __APPLE__ | ||||
|     //note: O_CREAT is only needed for mac, probably
 | ||||
|     mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR | O_CREAT, 0); | ||||
|     mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR | O_CREAT, 0); | ||||
| #else | ||||
|     mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR); | ||||
|     mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR); | ||||
| #endif | ||||
|     if (mySemaphore == SEM_FAILED) { | ||||
|     if (!mySemaphore) { | ||||
|       DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno)); | ||||
|       return; | ||||
|     } | ||||
|  | @ -608,15 +887,15 @@ namespace IPC { | |||
|     free(empty); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief The deconstructor
 | ||||
|   sharedClient::~sharedClient() { | ||||
|     if (hasCounter) { | ||||
|       finish(); | ||||
|     } | ||||
|     if (mySemaphore != SEM_FAILED) { | ||||
|       sem_close(mySemaphore); | ||||
|     } | ||||
|     mySemaphore.close(); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Writes data to the shared data
 | ||||
|   void sharedClient::write(char * data, int len) { | ||||
|     if (hasCounter) { | ||||
|       keepAlive(); | ||||
|  | @ -624,6 +903,7 @@ namespace IPC { | |||
|     memcpy(myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0), data, std::min(len, payLen)); | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Indicate that the process is done using this piece of memory, set the counter to finished
 | ||||
|   void sharedClient::finish() { | ||||
|     if (!hasCounter) { | ||||
|       DEBUG_MSG(DLVL_WARN, "Trying to time-out an element without counters"); | ||||
|  | @ -634,6 +914,7 @@ namespace IPC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Re-initialize the counter
 | ||||
|   void sharedClient::keepAlive() { | ||||
|     if (!hasCounter) { | ||||
|       DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element without counters"); | ||||
|  | @ -646,8 +927,12 @@ namespace IPC { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ///\brief Get a pointer to the data of this client
 | ||||
|   char * sharedClient::getData() { | ||||
|     if (!myPage.mapped){return 0;} | ||||
|     if (!myPage.mapped) { | ||||
|       return 0; | ||||
|     } | ||||
|     return (myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0)); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,12 +1,18 @@ | |||
| #pragma once | ||||
| #include <string> | ||||
| #include <set> | ||||
| #include <semaphore.h> | ||||
| 
 | ||||
| #include "timing.h" | ||||
| 
 | ||||
| #ifdef __CYGWIN__ | ||||
| #include <windows.h> | ||||
| #else | ||||
| #include <semaphore.h> | ||||
| #endif | ||||
| 
 | ||||
| namespace IPC { | ||||
|    | ||||
|   ///\brief A class used for the exchange of statistics over shared memory.
 | ||||
|   class statExchange { | ||||
|     public: | ||||
|       statExchange(char * _data); | ||||
|  | @ -27,18 +33,53 @@ namespace IPC { | |||
|       void connector(std::string name); | ||||
|       std::string connector(); | ||||
|     private: | ||||
|       ///\brief The payload for the stat exchange
 | ||||
|       /// - 8 byte - now (timestamp of last statistics)
 | ||||
|       /// - 4 byte - time (duration of the current connection) 
 | ||||
|       /// - 4 byte - lastSecond (last second of content viewed)
 | ||||
|       /// - 8 byte - down (Number of bytes received from peer)
 | ||||
|       /// - 8 byte - up (Number of bytes sent to peer)
 | ||||
|       /// - 16 byte - host (ip address of the peer)
 | ||||
|       /// - 20 byte - streamName (name of the stream peer is viewing)
 | ||||
|       /// - 20 byte - connector (name of the connector the peer is using) 
 | ||||
|       char * data; | ||||
|   }; | ||||
| 
 | ||||
|   class semGuard { | ||||
|   ///\brief A class used for the abstraction of semaphores
 | ||||
|   class semaphore { | ||||
|     public: | ||||
|       semGuard(sem_t * semaphore); | ||||
|       ~semGuard(); | ||||
|       semaphore(); | ||||
|       semaphore(const char * name, int oflag, mode_t mode, unsigned int value); | ||||
|       ~semaphore(); | ||||
|       operator bool() const; | ||||
|       void open(const char * name, int oflag, mode_t mode = 0, unsigned int value = 0); | ||||
|       int getVal() const; | ||||
|       void post(); | ||||
|       void wait(); | ||||
|       bool tryWait(); | ||||
|       void close(); | ||||
|       void unlink(); | ||||
|     private: | ||||
|       sem_t * mySemaphore; | ||||
| #ifdef __CYGWIN__ | ||||
|       HANDLE mySem; | ||||
| #else | ||||
|       sem_t * mySem; | ||||
| #endif | ||||
|       char * myName; | ||||
|   }; | ||||
| 
 | ||||
| #if !defined __APPLE__ && !defined __CYGWIN__ | ||||
|   ///\brief A class used as a semaphore guard
 | ||||
|   class semGuard { | ||||
|     public: | ||||
|       semGuard(semaphore thisSemaphore); | ||||
|       ~semGuard(); | ||||
|     private: | ||||
|       ///\brief The semaphore to guard.
 | ||||
|       semaphore mySemaphore; | ||||
|   }; | ||||
| 
 | ||||
| #if !defined __APPLE__ | ||||
|   ///\brief A class for managing shared memory pages.
 | ||||
|   class sharedPage{ | ||||
|     public: | ||||
|       sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); | ||||
|  | @ -50,20 +91,37 @@ namespace IPC { | |||
|       bool operator < (const sharedPage & rhs) const { | ||||
|         return name < rhs.name; | ||||
|       } | ||||
|       void unmap(); | ||||
|       void close(); | ||||
|   #ifdef __CYGWIN__ | ||||
|       ///\brief The handle of the opened shared memory page
 | ||||
|       HANDLE handle; | ||||
|   #else | ||||
|       ///\brief The fd handle of the opened shared memory page
 | ||||
|       int handle; | ||||
|   #endif | ||||
|       ///\brief The name of the opened shared memory page
 | ||||
|       std::string name; | ||||
|       ///\brief The size in bytes of the opened shared memory page
 | ||||
|       long long int len; | ||||
|       ///\brief Whether this class should unlink the shared memory upon deletion or not
 | ||||
|       bool master; | ||||
|       ///\brief A pointer to the payload of the page
 | ||||
|       char * mapped; | ||||
|   }; | ||||
| #else | ||||
|   class sharedPage; | ||||
| #endif | ||||
| 
 | ||||
| #if !defined __APPLE__ | ||||
|   ///\brief A class for managing shared files in the same form as shared memory pages
 | ||||
| #else | ||||
|   ///\brief A class for managing shared files.
 | ||||
| #endif | ||||
|   class sharedFile{ | ||||
|     public: | ||||
|       sharedFile(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); | ||||
|       sharedFile(const sharedPage & rhs); | ||||
|       sharedFile(const sharedFile & rhs); | ||||
|       ~sharedFile(); | ||||
|       operator bool() const; | ||||
|       void init(std::string name_, unsigned int len_, bool master_ =  false, bool autoBackoff = true); | ||||
|  | @ -71,14 +129,23 @@ namespace IPC { | |||
|       bool operator < (const sharedFile & rhs) const { | ||||
|         return name < rhs.name; | ||||
|       } | ||||
|       void unmap(); | ||||
|       ///\brief The fd handle of the opened shared file
 | ||||
|       int handle; | ||||
|       ///\brief The name of the opened shared file
 | ||||
|       std::string name; | ||||
|       ///\brief The size in bytes of the opened shared file
 | ||||
|       long long int len; | ||||
|       ///\brief Whether this class should unlink the shared file upon deletion or not
 | ||||
|       bool master; | ||||
|       ///\brief A pointer to the payload of the file file
 | ||||
|       char * mapped; | ||||
|   }; | ||||
| 
 | ||||
| #if defined __APPLE__ || defined __CYGWIN__ | ||||
| #ifdef __APPLE__ | ||||
|   ///\brief A class for handling shared memory pages.
 | ||||
|   ///
 | ||||
|   ///Uses shared files at its backbone, defined for portability
 | ||||
|   class sharedPage: public sharedFile{ | ||||
|     public: | ||||
|       sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); | ||||
|  | @ -87,6 +154,15 @@ namespace IPC { | |||
|   }; | ||||
| #endif | ||||
| 
 | ||||
|   ///\brief The server part of a server/client model for shared memory.
 | ||||
|   ///
 | ||||
|   ///The server manages the shared memory pages, and allocates new pages when needed.
 | ||||
|   ///
 | ||||
|   ///Pages are created with a basename + index, where index is in the range of 'A' - 'Z'
 | ||||
|   ///Each time a page is nearly full, the next page is created with a size double to the previous one.
 | ||||
|   ///
 | ||||
|   ///Clients should allocate payLen bytes at a time, possibly with the addition of a counter.
 | ||||
|   ///If no such length can be allocated, the next page should be tried, and so on.
 | ||||
|   class sharedServer{ | ||||
|     public: | ||||
|       sharedServer(); | ||||
|  | @ -95,18 +171,33 @@ namespace IPC { | |||
|       ~sharedServer(); | ||||
|       void parseEach(void (*callback)(char * data, size_t len, unsigned int id)); | ||||
|       operator bool() const; | ||||
|       ///\brief The amount of connected clients
 | ||||
|       unsigned int amount; | ||||
|     private: | ||||
|       bool isInUse(unsigned int id); | ||||
|       void newPage(); | ||||
|       void deletePage(); | ||||
|       ///\brief The basename of the shared pages.
 | ||||
|       std::string baseName; | ||||
|       ///\brief The length of each consecutive piece of payload
 | ||||
|       unsigned int payLen; | ||||
|       ///\brief The set of sharedPage structures to manage the actual memory
 | ||||
|       std::set<sharedPage> myPages; | ||||
|       sem_t * mySemaphore; | ||||
|       ///\brief A semaphore that is locked upon creation and deletion of the page, to ensure no new data is allocated during this step.
 | ||||
|       semaphore mySemaphore; | ||||
|       ///\brief Whether the payload has a counter, if so, it is added in front of the payload
 | ||||
|       bool hasCounter; | ||||
|   }; | ||||
| 
 | ||||
|   ///\brief The client part of a server/client model for shared memory.
 | ||||
|   ///
 | ||||
|   ///The server manages the shared memory pages, and allocates new pages when needed.
 | ||||
|   ///
 | ||||
|   ///Pages are created with a basename + index, where index is in the range of 'A' - 'Z'
 | ||||
|   ///Each time a page is nearly full, the next page is created with a size double to the previous one.
 | ||||
|   ///
 | ||||
|   ///Clients should allocate payLen bytes at a time, possibly with the addition of a counter.
 | ||||
|   ///If no such length can be allocated, the next page should be tried, and so on.
 | ||||
|   class sharedClient{ | ||||
|     public: | ||||
|       sharedClient(); | ||||
|  | @ -119,11 +210,17 @@ namespace IPC { | |||
|       void keepAlive(); | ||||
|       char * getData(); | ||||
|     private: | ||||
|       ///\brief The basename of the shared pages.
 | ||||
|       std::string baseName; | ||||
|       ///\brief The shared page this client has reserved a space on.
 | ||||
|       sharedPage myPage; | ||||
|       sem_t * mySemaphore; | ||||
|       ///\brief A semaphore that is locked upon trying to allocate space on a page
 | ||||
|       semaphore mySemaphore; | ||||
|       ///\brief The size in bytes of the opened page
 | ||||
|       int payLen; | ||||
|       ///\brief The offset of the payload reserved for this client within the opened page
 | ||||
|       int offsetOnPage; | ||||
|       ///\brief Whether the payload has a counter, if so, it is added in front of the payload
 | ||||
|       bool hasCounter; | ||||
|   }; | ||||
| } | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ | |||
| #include "config.h" | ||||
| #include "socket.h" | ||||
| #include "defines.h" | ||||
| #include "shared_memory.h" | ||||
| 
 | ||||
| std::string Util::getTmpFolder(){ | ||||
|   std::string dir; | ||||
|  | @ -119,14 +120,14 @@ bool Util::Stream::getStream(std::string streamname){ | |||
|   JSON::Value ServConf = JSON::fromFile(getTmpFolder() + "streamlist"); | ||||
|   if (ServConf["streams"].isMember(streamname)){ | ||||
|     //check if the stream is already active, if yes, don't re-activate
 | ||||
|     sem_t * playerLock = sem_open(std::string("/lock_" + streamname).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1); | ||||
|     if (sem_trywait(playerLock) == -1){ | ||||
|       sem_close(playerLock); | ||||
|     IPC::semaphore playerLock(std::string("/lock_" + streamname).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1); | ||||
|     if (!playerLock.tryWait()){ | ||||
|       playerLock.close(); | ||||
|       DEBUG_MSG(DLVL_MEDIUM, "Playerlock for %s already active - not re-activating stream", streamname.c_str()); | ||||
|       return true; | ||||
|     } | ||||
|     sem_post(playerLock); | ||||
|     sem_close(playerLock); | ||||
|     playerLock.post(); | ||||
|     playerLock.close(); | ||||
|     if (ServConf["streams"][streamname]["source"].asString()[0] == '/'){ | ||||
|       DEBUG_MSG(DLVL_MEDIUM, "Activating VoD stream %s", streamname.c_str()); | ||||
|       return getVod(ServConf["streams"][streamname]["source"].asString(), streamname); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Erik Zandvliet
						Erik Zandvliet