Abstraction of semaphore to a class

This commit is contained in:
Erik Zandvliet 2014-04-23 13:06:17 +02:00 committed by Thulinma
parent 1e3b38f777
commit 4f1e1fa1d7
8 changed files with 809 additions and 216 deletions

View file

@ -1,4 +1,4 @@
# Doxyfile 1.8.6 # Doxyfile 1.8.7
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@ -70,6 +70,14 @@ OUTPUT_DIRECTORY = ./docs
CREATE_SUBDIRS = NO 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 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this # documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language. # 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 # 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 # 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, # 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 # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# (default is Fortran), use: inc=Fortran f=C. # 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. # Note For files without extension you can use no_extension as a placeholder.
# #
@ -1230,7 +1241,8 @@ GENERATE_CHI = NO
CHM_INDEX_ENCODING = CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated ( # 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. # The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES. # 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 # 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 # implemented using a web server instead of a web client using Javascript. There
# are two flavours of web server based searching depending on the # are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # setting. When disabled, doxygen will generate a PHP script for searching and
# searching and an index file used by the script. When EXTERNAL_SEARCH is # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# enabled the indexing and searching needs to be provided by external tools. See # and searching needs to be provided by external tools. See the section
# the section "External Indexing and Searching" for details. # "External Indexing and Searching" for details.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES. # This tag requires that the tag SEARCHENGINE is set to YES.
@ -1762,6 +1774,13 @@ MAN_OUTPUT = man
MAN_EXTENSION = .3 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 # 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 # 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 # man page(s). These additional files only source the real man page, but without
@ -1789,18 +1808,6 @@ GENERATE_XML = NO
XML_OUTPUT = xml 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 # If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to # listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size # the XML output. Note that enabling this will significantly increase the size
@ -1947,9 +1954,9 @@ PREDEFINED =
EXPAND_AS_DEFINED = EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # 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 # remove all references to function-like macros that are alone on a line, have
# all uppercase name, and do not end with a semicolon. Such function macros are # an all uppercase name, and do not end with a semicolon. Such function macros
# typically used for boiler-plate code, and will confuse the parser if not # are typically used for boiler-plate code, and will confuse the parser if not
# removed. # removed.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to 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 # 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 # section "Linking to external documentation" for more information about the use
# of tag files. # 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 # 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. # run, you must also specify the path to the tagfile here.
@ -2061,6 +2068,16 @@ HAVE_DOT = NO
DOT_NUM_THREADS = 0 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 # The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs. # dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10. # Minimum value: 4, maximum value: 24, default value: 10.

View file

@ -12,10 +12,17 @@
namespace Converter { namespace Converter {
///\brief The base constructor
Converter::Converter(){ Converter::Converter(){
fillFFMpegEncoders(); 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(){ void Converter::fillFFMpegEncoders(){
std::vector<char*> cmd; std::vector<char*> cmd;
cmd.reserve(3); cmd.reserve(3);
@ -45,10 +52,14 @@ namespace Converter {
fclose( outFile ); 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(){ converterInfo & Converter::getCodecs(){
return allCodecs; 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 Converter::getEncoders(){
JSON::Value result; JSON::Value result;
for (converterInfo::iterator convIt = allCodecs.begin(); convIt != allCodecs.end(); convIt++){ for (converterInfo::iterator convIt = allCodecs.begin(); convIt != allCodecs.end(); convIt++){
@ -64,6 +75,9 @@ namespace Converter {
return result; 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){ JSON::Value Converter::queryPath(std::string myPath){
char const * cmd[3] = {0, 0, 0}; char const * cmd[3] = {0, 0, 0};
std::string mistPath = Util::getMyPath() + "MistInfo"; std::string mistPath = Util::getMyPath() + "MistInfo";
@ -89,6 +103,20 @@ namespace Converter {
return result; 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) { void Converter::startConversion(std::string name, JSON::Value parameters) {
if ( !parameters.isMember("input")){ if ( !parameters.isMember("input")){
statusHistory[name] = "No input file supplied"; statusHistory[name] = "No input file supplied";
@ -185,6 +213,9 @@ namespace Converter {
allConversions[name]["status"]["time"] = 0; 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(){ void Converter::updateStatus(){
if (allConversions.size()){ if (allConversions.size()){
std::map<std::string,JSON::Value>::iterator cIt; 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 Converter::parseFFMpegStatus(std::string statusLine){
JSON::Value result; JSON::Value result;
int curOffset = statusLine.find("frame=") + 6; int curOffset = statusLine.find("frame=") + 6;
@ -263,6 +299,8 @@ namespace Converter {
return result; 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(){ JSON::Value Converter::getStatus(){
updateStatus(); updateStatus();
JSON::Value result; JSON::Value result;
@ -281,6 +319,7 @@ namespace Converter {
return result; return result;
} }
///\brief Clears the status history of all conversions
void Converter::clearStatus(){ void Converter::clearStatus(){
statusHistory.clear(); statusHistory.clear();
} }

View file

@ -3,10 +3,15 @@
#include "json.h" #include "json.h"
///\brief A typedef to simplify accessing all codecs
typedef std::map<std::string,std::string> codecInfo; typedef std::map<std::string,std::string> codecInfo;
///\brief A typedef to simplify accessing all encoders
typedef std::map<std::string,codecInfo> converterInfo; typedef std::map<std::string,codecInfo> converterInfo;
///\brief A namespace containing all functions for handling the conversion API
namespace Converter { namespace Converter {
///\brief A class containing the basic conversion API functionality
class Converter { class Converter {
public: public:
Converter(); Converter();
@ -20,8 +25,11 @@ namespace Converter {
JSON::Value parseFFMpegStatus(std::string statusLine); JSON::Value parseFFMpegStatus(std::string statusLine);
private: private:
void fillFFMpegEncoders(); void fillFFMpegEncoders();
///\brief Holds a list of all current known codecs
converterInfo allCodecs; converterInfo allCodecs;
///\brief Holds a list of all the current conversions
std::map<std::string,JSON::Value> allConversions; std::map<std::string,JSON::Value> allConversions;
///\brief Stores the status of all conversions, and the history
std::map<std::string,std::string> statusHistory; std::map<std::string,std::string> statusHistory;
}; };
} }

View file

@ -210,6 +210,7 @@ namespace DTSC {
char data[11]; char data[11];
}; };
///\brief Basic class for storage of a read-only track
class readOnlyTrack { class readOnlyTrack {
public: public:
readOnlyTrack(); readOnlyTrack();
@ -248,6 +249,7 @@ namespace DTSC {
void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0); void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0);
}; };
///\brief Class for storage of track data
class Track : public readOnlyTrack { class Track : public readOnlyTrack {
public: public:
Track(); Track();
@ -270,6 +272,7 @@ namespace DTSC {
void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0); void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0);
}; };
///\brief Class for storage of read-only meta data
class readOnlyMeta { class readOnlyMeta {
public: public:
readOnlyMeta(); readOnlyMeta();
@ -291,6 +294,7 @@ namespace DTSC {
void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0); void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0);
}; };
///\brief Class for storage of meta data
class Meta : public readOnlyMeta { class Meta : public readOnlyMeta {
public: public:
Meta(); Meta();

View file

@ -115,6 +115,7 @@ namespace DTSC {
/// Internally used resize function for when operating in copy mode and the internal buffer is too small. /// Internally used resize function for when operating in copy mode and the internal buffer is too small.
/// It will only resize up, never down. /// It will only resize up, never down.
///\param len The length th scale the buffer up to if necessary
void Packet::resize(unsigned int len) { void Packet::resize(unsigned int len) {
if (master && len > bufferLen) { if (master && len > bufferLen) {
char * tmp = (char *)realloc(data, len); 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) { void Packet::reInit(const char * data_, unsigned int len, bool noCopy) {
if (!data_){ if (!data_){
DEBUG_MSG(DLVL_DEVEL, "ReInit received a null pointer with len %d, ignoring", len); 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 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 * Packet::findIdentifier(const char * identifier){
char * p = data; char * p = data;
if (version == DTSC_V2){ if (version == DTSC_V2){
@ -251,6 +259,10 @@ namespace DTSC {
return ret; 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) { void Packet::getString(const char * identifier, char *& result, int & len) {
char * pos = findIdentifier(identifier); char * pos = findIdentifier(identifier);
if (pos < (char*)2) { if (pos < (char*)2) {
@ -267,6 +279,9 @@ namespace DTSC {
len = ntohl(((int *)(pos + 1))[0]); 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) { void Packet::getString(const char * identifier, std::string & result) {
char * data = NULL; char * data = NULL;
int len = 0; int len = 0;
@ -274,6 +289,9 @@ namespace DTSC {
result = std::string(data, len); 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) { void Packet::getInt(const char * identifier, int & result) {
char * pos = findIdentifier(identifier); char * pos = findIdentifier(identifier);
if (pos < (char*)2) { 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]; 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 Packet::getInt(const char * identifier) {
int result; int result;
getInt(identifier, result); getInt(identifier, result);
return 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) { void Packet::getFlag(const char * identifier, bool & result) {
int result_; int result_;
getInt(identifier, result_); getInt(identifier, result_);
result = (bool)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 Packet::getFlag(const char * identifier) {
bool result; bool result;
getFlag(identifier, result); getFlag(identifier, result);
return 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) { bool Packet::hasMember(const char * identifier) {
return findIdentifier(identifier) > (char*)2; return findIdentifier(identifier) > (char*)2;
} }
///\brief Returns the timestamp of the packet.
///\return The timestamp of this packet.
long long unsigned int Packet::getTime() { long long unsigned int Packet::getTime() {
if (version != DTSC_V2){ if (version != DTSC_V2){
if (!data){return 0;} 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]); 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() { long int Packet::getTrackId() {
if (version != DTSC_V2){ if (version != DTSC_V2){
return getInt("trackid"); return getInt("trackid");
@ -324,15 +358,21 @@ namespace DTSC {
return ntohl(((int *)data)[2]); 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() { char * Packet::getData() {
return data; return data;
} }
///\brief Returns the size of the payload of this packet.
///\return The size of the payload of this packet.
int Packet::getDataLen() { int Packet::getDataLen() {
return dataLen; return dataLen;
} }
///\brief Converts the packet into a JSON value
///\return A JSON::Value representation of this packet.
JSON::Value Packet::toJSON(){ JSON::Value Packet::toJSON(){
JSON::Value result; JSON::Value result;
unsigned int i = 8; unsigned int i = 8;
@ -346,44 +386,56 @@ namespace DTSC {
} }
///\brief Returns the payloadsize of a part
long Part::getSize() { long Part::getSize() {
return ((long)data[0] << 16) | ((long)data[1] << 8) | data[2]; return ((long)data[0] << 16) | ((long)data[1] << 8) | data[2];
} }
///\brief Sets the payloadsize of a part
void Part::setSize(long newSize) { void Part::setSize(long newSize) {
data[0] = (newSize & 0xFF0000) >> 16; data[0] = (newSize & 0xFF0000) >> 16;
data[1] = (newSize & 0x00FF00) >> 8; data[1] = (newSize & 0x00FF00) >> 8;
data[2] = (newSize & 0x0000FF); data[2] = (newSize & 0x0000FF);
} }
///\brief Retruns the duration of a part
short Part::getDuration() { short Part::getDuration() {
return btohs(data + 3); return btohs(data + 3);
} }
///\brief Sets the duration of a part
void Part::setDuration(short newDuration) { void Part::setDuration(short newDuration) {
htobs(data + 3, newDuration); htobs(data + 3, newDuration);
} }
///\brief returns the offset of a part
long Part::getOffset() { long Part::getOffset() {
return btohl(data + 5); return btohl(data + 5);
} }
///\brief Sets the offset of a part
void Part::setOffset(long newOffset) { void Part::setOffset(long newOffset) {
htobl(data + 5, newOffset); htobl(data + 5, newOffset);
} }
///\brief Returns the data of a part
char * Part::getData() { char * Part::getData() {
return data; 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){ void Part::toPrettyString(std::ostream & str, int indent){
str << std::string(indent, ' ') << "Part: Size(" << getSize() << "), Dur(" << getDuration() << "), Offset(" << getOffset() << ")" << std::endl; 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() { 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]); 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) { void Key::setBpos(long long unsigned int newBpos) {
data[4] = newBpos & 0xFF; data[4] = newBpos & 0xFF;
data[3] = (newBpos >> 8) & 0xFF; data[3] = (newBpos >> 8) & 0xFF;
@ -392,88 +444,113 @@ namespace DTSC {
data[0] = (newBpos >> 32) & 0xFF; data[0] = (newBpos >> 32) & 0xFF;
} }
///\brief Returns the byteposition of a keyframe
long Key::getLength() { long Key::getLength() {
return ((data[5] << 16) | (data[6] << 8) | data[7]); return ((data[5] << 16) | (data[6] << 8) | data[7]);
} }
///\brief Sets the byteposition of a keyframe
void Key::setLength(long newLength) { void Key::setLength(long newLength) {
data[7] = newLength & 0xFF; data[7] = newLength & 0xFF;
data[6] = (newLength >> 8) & 0xFF; data[6] = (newLength >> 8) & 0xFF;
data[5] = (newLength >> 16) & 0xFF; data[5] = (newLength >> 16) & 0xFF;
} }
///\brief Returns the number of a keyframe
unsigned short Key::getNumber() { unsigned short Key::getNumber() {
return btohs(data + 8); return btohs(data + 8);
} }
///\brief Sets the number of a keyframe
void Key::setNumber(unsigned short newNumber) { void Key::setNumber(unsigned short newNumber) {
htobs(data + 8, newNumber); htobs(data + 8, newNumber);
} }
///\brief Returns the number of parts of a keyframe
short Key::getParts() { short Key::getParts() {
return btohs(data + 10); return btohs(data + 10);
} }
///\brief Sets the number of parts of a keyframe
void Key::setParts(short newParts) { void Key::setParts(short newParts) {
htobs(data + 10, newParts); htobs(data + 10, newParts);
} }
///\brief Returns the timestamp of a keyframe
long Key::getTime() { long Key::getTime() {
return btohl(data + 12); return btohl(data + 12);
} }
///\brief Sets the timestamp of a keyframe
void Key::setTime(long newTime) { void Key::setTime(long newTime) {
htobl(data + 12, newTime); htobl(data + 12, newTime);
} }
///\brief Returns the data of this keyframe struct
char * Key::getData() { char * Key::getData() {
return data; 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){ void Key::toPrettyString(std::ostream & str, int indent){
str << std::string(indent, ' ') << "Key " << getNumber() << ": Pos(" << getBpos() << "), Dur(" << getLength() << "), Parts(" << getParts() << "), Time(" << getTime() << ")" << std::endl; 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() { long Fragment::getDuration() {
return btohl(data); return btohl(data);
} }
///\brief Sets the duration of this fragment
void Fragment::setDuration(long newDuration) { void Fragment::setDuration(long newDuration) {
htobl(data, newDuration); htobl(data, newDuration);
} }
///\brief Returns the length of this fragment
char Fragment::getLength() { char Fragment::getLength() {
return data[4]; return data[4];
} }
///\brief Sets the length of this fragment
void Fragment::setLength(char newLength) { void Fragment::setLength(char newLength) {
data[4] = newLength; data[4] = newLength;
} }
///\brief Returns the number of the first keyframe in this fragment
short Fragment::getNumber() { short Fragment::getNumber() {
return btohs(data + 5); return btohs(data + 5);
} }
///\brief Sets the number of the first keyframe in this fragment
void Fragment::setNumber(short newNumber) { void Fragment::setNumber(short newNumber) {
htobs(data + 5, newNumber); htobs(data + 5, newNumber);
} }
///\brief Returns the size of a fragment
long Fragment::getSize() { long Fragment::getSize() {
return btohl(data + 7); return btohl(data + 7);
} }
///\brief Sets the size of a fragement
void Fragment::setSize(long newSize) { void Fragment::setSize(long newSize) {
htobl(data + 7, newSize); htobl(data + 7, newSize);
} }
///\brief Returns thte data of this fragment structure
char * Fragment::getData() { char * Fragment::getData() {
return data; 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){ void Fragment::toPrettyString(std::ostream & str, int indent){
str << std::string(indent, ' ') << "Fragment " << getNumber() << ": Dur(" << getDuration() << "), Len(" << (int)getLength() << "), Size(" << getSize() << ")" << std::endl; str << std::string(indent, ' ') << "Fragment " << getNumber() << ": Dur(" << getDuration() << "), Len(" << (int)getLength() << "), Size(" << getSize() << ")" << std::endl;
} }
///\brief Constructs an empty readOnlyTrack
readOnlyTrack::readOnlyTrack() { readOnlyTrack::readOnlyTrack() {
fragments = NULL; fragments = NULL;
fragLen = 0; fragLen = 0;
@ -493,6 +570,7 @@ namespace DTSC {
fpks = 0; fpks = 0;
} }
///\brief Constructs a readOnlyTrack from a JSON::Value
readOnlyTrack::readOnlyTrack(JSON::Value & trackRef) { readOnlyTrack::readOnlyTrack(JSON::Value & trackRef) {
if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) { if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) {
fragments = (Fragment *)trackRef["fragments"].asStringRef().data(); fragments = (Fragment *)trackRef["fragments"].asStringRef().data();
@ -539,6 +617,7 @@ namespace DTSC {
} }
} }
///\brief Constructs an empty track
Track::Track() { Track::Track() {
trackID = 0; trackID = 0;
firstms = 0; firstms = 0;
@ -553,6 +632,7 @@ namespace DTSC {
fpks = 0; fpks = 0;
} }
///\brief Constructs a track from a readOnlyTrack
Track::Track(const readOnlyTrack & rhs) { Track::Track(const readOnlyTrack & rhs) {
trackID = rhs.trackID; trackID = rhs.trackID;
firstms = rhs.firstms; firstms = rhs.firstms;
@ -581,6 +661,7 @@ namespace DTSC {
} }
} }
///\brief Constructs a track from a JSON::Value
Track::Track(JSON::Value & trackRef) { Track::Track(JSON::Value & trackRef) {
if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) { if (trackRef.isMember("fragments") && trackRef["fragments"].isString()) {
Fragment * tmp = (Fragment *)trackRef["fragments"].asStringRef().data(); 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) { void Track::update(DTSC::Packet & pack) {
if (pack.getTime() < lastms) { 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); 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); 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) { void Track::update(JSON::Value & pack) {
if (pack["time"].asInt() < lastms) { 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); 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()); 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) { Key & Track::getKey(unsigned int keyNum) {
static Key empty; static Key empty;
if (keyNum < keys[0].getNumber()) { if (keyNum < keys[0].getNumber()) {
@ -748,6 +836,7 @@ namespace DTSC {
return keys[keyNum - keys[0].getNumber()]; return keys[keyNum - keys[0].getNumber()];
} }
///\brief Returns a unique identifier for a track
std::string readOnlyTrack::getIdentifier() { std::string readOnlyTrack::getIdentifier() {
std::stringstream result; std::stringstream result;
if (type == "") { if (type == "") {
@ -766,12 +855,14 @@ namespace DTSC {
return result.str(); return result.str();
} }
///\brief Returns a writable identifier for a track, to prevent overwrites on readout
std::string readOnlyTrack::getWritableIdentifier() { std::string readOnlyTrack::getWritableIdentifier() {
std::stringstream result; std::stringstream result;
result << getIdentifier() << "_" << trackID; result << getIdentifier() << "_" << trackID;
return result.str(); return result.str();
} }
///\brief Resets a track, clears all meta values
void Track::reset() { void Track::reset() {
fragments.clear(); fragments.clear();
parts.clear(); parts.clear();
@ -781,6 +872,7 @@ namespace DTSC {
lastms = 0; lastms = 0;
} }
///\brief Creates an empty read-only meta object
readOnlyMeta::readOnlyMeta() { readOnlyMeta::readOnlyMeta() {
vod = false; vod = false;
live = false; live = false;
@ -790,6 +882,7 @@ namespace DTSC {
bufferWindow = 0; bufferWindow = 0;
} }
///\brief Creates a read-only meta object from a given JSON::Value
readOnlyMeta::readOnlyMeta(JSON::Value & meta) { readOnlyMeta::readOnlyMeta(JSON::Value & meta) {
vod = meta.isMember("vod") && meta["vod"]; vod = meta.isMember("vod") && meta["vod"];
live = meta.isMember("live") && meta["live"]; 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){ void readOnlyTrack::toPrettyString(std::ostream & str, int indent, int verbosity){
str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl; str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl;
str << std::string(indent + 2, ' ') << "ID: " << trackID << 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() { Meta::Meta() {
vod = false; vod = false;
live = false; live = false;
@ -867,6 +965,7 @@ namespace DTSC {
bufferWindow = 0; bufferWindow = 0;
} }
///\brief Creates a meta object from a read-only meta object
Meta::Meta(const readOnlyMeta & rhs) { Meta::Meta(const readOnlyMeta & rhs) {
vod = rhs.vod; vod = rhs.vod;
live = rhs.live; live = rhs.live;
@ -878,6 +977,7 @@ namespace DTSC {
moreheader = rhs.moreheader; moreheader = rhs.moreheader;
} }
///\brief Creates a meta object from a JSON::Value
Meta::Meta(JSON::Value & meta) { Meta::Meta(JSON::Value & meta) {
vod = meta.isMember("vod") && meta["vod"]; vod = meta.isMember("vod") && meta["vod"];
live = meta.isMember("live") && meta["live"]; 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) { void Meta::update(JSON::Value & pack) {
vod = pack.isMember("bpos"); vod = pack.isMember("bpos");
live = !vod; live = !vod;
@ -906,6 +1007,7 @@ namespace DTSC {
} }
} }
///\brief Updates a meta object given a DTSC::Packet
void Meta::update(DTSC::Packet & pack) { void Meta::update(DTSC::Packet & pack) {
vod = pack.hasMember("bpos"); vod = pack.hasMember("bpos");
live = !vod; 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){ void Track::toPrettyString(std::ostream & str, int indent, int verbosity){
str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl; str << std::string(indent, ' ') << "Track " << getWritableIdentifier() << std::endl;
str << std::string(indent + 2, ' ') << "ID: " << trackID << 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) { char * convertShort(short input) {
static char result[2]; static char result[2];
result[0] = (input >> 8) & 0xFF; result[0] = (input >> 8) & 0xFF;
@ -970,6 +1077,7 @@ namespace DTSC {
return result; return result;
} }
///\brief Converts an integer to a char*
char * convertInt(int input) { char * convertInt(int input) {
static char result[4]; static char result[4];
result[0] = (input >> 24) & 0xFF; result[0] = (input >> 24) & 0xFF;
@ -979,6 +1087,7 @@ namespace DTSC {
return result; return result;
} }
///\brief Converts a long long to a char*
char * convertLongLong(long long int input) { char * convertLongLong(long long int input) {
static char result[8]; static char result[8];
result[0] = (input >> 56) & 0xFF; result[0] = (input >> 56) & 0xFF;
@ -992,6 +1101,7 @@ namespace DTSC {
return result; return result;
} }
///\brief Determines the "packed" size of a read-only track
int readOnlyTrack::getSendLen() { int readOnlyTrack::getSendLen() {
int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size(); int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size();
result += fragLen * 11; result += fragLen * 11;
@ -1012,6 +1122,7 @@ namespace DTSC {
return result; return result;
} }
///\brief Determines the "packed" size of a track
int Track::getSendLen() { int Track::getSendLen() {
int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size(); int result = 146 + init.size() + codec.size() + type.size() + getWritableIdentifier().size();
result += fragments.size() * 11; result += fragments.size() * 11;
@ -1032,15 +1143,22 @@ namespace DTSC {
return result; 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){ static void writePointer(char *& p, const char * src, unsigned int len){
memcpy(p, src, len); memcpy(p, src, len);
p += 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){ static void writePointer(char *& p, const std::string & src){
writePointer(p, src.data(), src.size()); writePointer(p, src.data(), src.size());
} }
///\brief Writes a read-only track to a pointer
void readOnlyTrack::writeTo(char *& p){ void readOnlyTrack::writeTo(char *& p){
std::string iden = getWritableIdentifier(); std::string iden = getWritableIdentifier();
writePointer(p, convertShort(iden.size()), 2); writePointer(p, convertShort(iden.size()), 2);
@ -1102,6 +1220,7 @@ namespace DTSC {
writePointer(p, "\000\000\356", 3);//End this track Object 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) { void readOnlyTrack::send(Socket::Connection & conn) {
conn.SendNow(convertShort(getWritableIdentifier().size()), 2); conn.SendNow(convertShort(getWritableIdentifier().size()), 2);
conn.SendNow(getWritableIdentifier()); conn.SendNow(getWritableIdentifier());
@ -1162,6 +1281,7 @@ namespace DTSC {
conn.SendNow("\000\000\356", 3);//End this track Object conn.SendNow("\000\000\356", 3);//End this track Object
} }
///\brief Writes a track to a pointer
void Track::writeTo(char *& p){ void Track::writeTo(char *& p){
writePointer(p, convertShort(getWritableIdentifier().size()), 2); writePointer(p, convertShort(getWritableIdentifier().size()), 2);
writePointer(p, getWritableIdentifier()); writePointer(p, getWritableIdentifier());
@ -1228,6 +1348,7 @@ namespace DTSC {
writePointer(p, "\000\000\356", 3);//End this track Object writePointer(p, "\000\000\356", 3);//End this track Object
} }
///\brief Writes a track to a socket
void Track::send(Socket::Connection & conn) { void Track::send(Socket::Connection & conn) {
conn.SendNow(convertShort(getWritableIdentifier().size()), 2); conn.SendNow(convertShort(getWritableIdentifier().size()), 2);
conn.SendNow(getWritableIdentifier()); conn.SendNow(getWritableIdentifier());
@ -1294,6 +1415,7 @@ namespace DTSC {
conn.SendNow("\000\000\356", 3);//End this track Object 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 readOnlyMeta::getSendLen(){
unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; 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++) { for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) {
@ -1302,6 +1424,7 @@ namespace DTSC {
return dataLen; return dataLen;
} }
///\brief Writes a read-only meta object to a pointer
void readOnlyMeta::writeTo(char * p){ void readOnlyMeta::writeTo(char * p){
int dataLen = getSendLen(); int dataLen = getSendLen();
writePointer(p, DTSC::Magic_Header, 4); writePointer(p, DTSC::Magic_Header, 4);
@ -1332,6 +1455,7 @@ namespace DTSC {
writePointer(p, "\000\000\356", 3);//End global object 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) { void readOnlyMeta::send(Socket::Connection & conn) {
int dataLen = getSendLen(); int dataLen = getSendLen();
conn.SendNow(DTSC::Magic_Header, 4); conn.SendNow(DTSC::Magic_Header, 4);
@ -1362,6 +1486,7 @@ namespace DTSC {
conn.SendNow("\000\000\356", 3);//End global object conn.SendNow("\000\000\356", 3);//End global object
} }
///\brief Determines the "packed" size of a meta object
unsigned int Meta::getSendLen(){ unsigned int Meta::getSendLen(){
unsigned int dataLen = 16 + (vod ? 14 : 0) + (live ? 15 : 0) + (merged ? 17 : 0) + (bufferWindow ? 24 : 0) + 21; 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++) { for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
@ -1370,6 +1495,7 @@ namespace DTSC {
return dataLen; return dataLen;
} }
///\brief Writes a meta object to a pointer
void Meta::writeTo(char * p){ void Meta::writeTo(char * p){
int dataLen = getSendLen(); int dataLen = getSendLen();
writePointer(p, DTSC::Magic_Header, 4); writePointer(p, DTSC::Magic_Header, 4);
@ -1400,6 +1526,7 @@ namespace DTSC {
writePointer(p, "\000\000\356", 3);//End global object writePointer(p, "\000\000\356", 3);//End global object
} }
///\brief Writes a meta object to a socket
void Meta::send(Socket::Connection & conn) { void Meta::send(Socket::Connection & conn) {
int dataLen = getSendLen(); int dataLen = getSendLen();
conn.SendNow(DTSC::Magic_Header, 4); conn.SendNow(DTSC::Magic_Header, 4);
@ -1430,6 +1557,7 @@ namespace DTSC {
conn.SendNow("\000\000\356", 3);//End global object 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 readOnlyTrack::toJSON() {
JSON::Value result; JSON::Value result;
if (fragments) { if (fragments) {
@ -1467,6 +1595,7 @@ namespace DTSC {
return result; return result;
} }
///\brief Converts a track to a JSON::Value
JSON::Value Track::toJSON() { JSON::Value Track::toJSON() {
JSON::Value result; JSON::Value result;
std::string tmp; std::string tmp;
@ -1513,6 +1642,7 @@ namespace DTSC {
return result; return result;
} }
///\brief Converts a meta object to a JSON::Value
JSON::Value Meta::toJSON() { JSON::Value Meta::toJSON() {
JSON::Value result; JSON::Value result;
for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
@ -1534,6 +1664,10 @@ namespace DTSC {
return result; 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){ void readOnlyMeta::toPrettyString(std::ostream & str, int indent, int verbosity){
for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) { for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) {
it->second.toPrettyString(str, indent, verbosity); it->second.toPrettyString(str, indent, verbosity);
@ -1553,6 +1687,10 @@ namespace DTSC {
str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl; str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl;
} }
///\brief Converts a meta object to a human readable string
///\param str The stringstream to append to
///\param indent the amount of indentation needed
///\param verbosity How verbose the output needs to be
void Meta::toPrettyString(std::ostream & str, int indent, int verbosity){ void Meta::toPrettyString(std::ostream & str, int indent, int verbosity){
for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
it->second.toPrettyString(str, indent, verbosity); it->second.toPrettyString(str, indent, verbosity);
@ -1572,6 +1710,7 @@ namespace DTSC {
str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl; str << std::string(indent, ' ') << "More Header: " << moreheader << std::endl;
} }
///\brief Converts a read-only meta object to a JSON::Value
JSON::Value readOnlyMeta::toJSON() { JSON::Value readOnlyMeta::toJSON() {
JSON::Value result; JSON::Value result;
for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) { for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) {
@ -1593,12 +1732,14 @@ namespace DTSC {
return result; return result;
} }
///\brief Resets a meta object, removes all unimportant meta values
void Meta::reset() { void Meta::reset() {
for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
it->second.reset(); it->second.reset();
} }
} }
///\brief Returns whether a read-only meta object is fixed or not
bool readOnlyMeta::isFixed() { bool readOnlyMeta::isFixed() {
for (std::map<int, readOnlyTrack>::iterator it = tracks.begin(); it != tracks.end(); it++) { 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())) { if (!it->second.keyLen || !(it->second.keys[it->second.keyLen - 1].getBpos())) {
@ -1608,6 +1749,7 @@ namespace DTSC {
return true; return true;
} }
///\brief Returns whether a meta object is fixed or not
bool Meta::isFixed() { bool Meta::isFixed() {
for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) { for (std::map<int, Track>::iterator it = tracks.begin(); it != tracks.end(); it++) {
if (it->second.type == "meta" || it->second.type == "") { if (it->second.type == "meta" || it->second.type == "") {

View file

@ -7,8 +7,10 @@
#include <cstdio> #include <cstdio>
#include <unistd.h> #include <unistd.h>
#include <iostream>
#include "defines.h" #include "defines.h"
#include "shared_memory.h" #include "shared_memory.h"
#include "stream.h"
namespace IPC { namespace IPC {
/// Stores a long value of val in network order to the pointer p. /// Stores a long value of val in network order to the pointer p.
@ -19,6 +21,7 @@ namespace IPC {
p[3] = val & 0xFF; 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) { static void htobll(char * p, long long val) {
p[0] = (val >> 56) & 0xFF; p[0] = (val >> 56) & 0xFF;
p[1] = (val >> 48) & 0xFF; p[1] = (val >> 48) & 0xFF;
@ -30,24 +33,189 @@ namespace IPC {
p[7] = val & 0xFF; p[7] = val & 0xFF;
} }
/// Reads a long value of p in host order to val.
static void btohl(char * p, long & val) { static void btohl(char * p, long & val) {
val = ((long)p[0] << 24) | ((long)p[1] << 16) | ((long)p[2] << 8) | p[3]; 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) { 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]; 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) { sharedPage::sharedPage(std::string name_, unsigned int len_, bool master_, bool autoBackoff) : handle(0), name(name_), len(len_), master(master_), mapped(NULL) {
handle = 0; handle = 0;
name = name_; name = name_;
len = len_; len = len_;
master = master_; master = master_;
mapped = 0; mapped = 0;
init(name_,len_,master_, autoBackoff); init(name_, len_, master_, autoBackoff);
} }
sharedPage::sharedPage(const sharedPage & rhs){
///\brief Creates a copy of a shared page
///\param rhs The page to copy
sharedPage::sharedPage(const sharedPage & rhs) {
handle = 0; handle = 0;
name = ""; name = "";
len = 0; len = 0;
@ -55,37 +223,73 @@ namespace IPC {
mapped = 0; mapped = 0;
init(rhs.name, rhs.len, rhs.master); init(rhs.name, rhs.len, rhs.master);
} }
///\brief Returns whether the shared page is valid or not
sharedPage::operator bool() const { sharedPage::operator bool() const {
return mapped != 0; return mapped != 0;
} }
void sharedPage::operator =(sharedPage & rhs){
///\brief Assignment operator
void sharedPage::operator =(sharedPage & rhs) {
init(rhs.name, rhs.len, rhs.master); init(rhs.name, rhs.len, rhs.master);
rhs.master = false;//Make sure the memory does not get unlinked 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) { void sharedPage::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) {
if (mapped && len){ unmap();
munmap(mapped,len); close();
}
if(master){
shm_unlink(name.c_str());
}
if (handle > 0){
close(handle);
}
handle = 0;
name = name_; name = name_;
len = len_; len = len_;
master = master_; master = master_;
mapped = 0; mapped = 0;
if (name.size()){ if (name.size()) {
handle = shm_open(name.c_str(), ( master ? O_CREAT | O_EXCL : 0 )| O_RDWR, ACCESSPERMS);
if (handle == -1) {
if (master){ 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());
}
close();
name = name_;
len = len_;
master = master_;
mapped = 0;
if (name.size()) {
handle = shm_open(name.c_str(), (master ? O_CREAT | O_EXCL : 0) | O_RDWR, ACCESSPERMS);
if (handle == -1) {
if (master) {
DEBUG_MSG(DLVL_HIGH, "Overwriting old page for %s", name.c_str()); DEBUG_MSG(DLVL_HIGH, "Overwriting old page for %s", name.c_str());
handle = shm_open(name.c_str(), O_CREAT | O_RDWR, ACCESSPERMS); handle = shm_open(name.c_str(), O_CREAT | O_RDWR, ACCESSPERMS);
}else{ } else {
int i = 0; int i = 0;
while (i < 10 && handle == -1 && autoBackoff){ while (i < 10 && handle == -1 && autoBackoff) {
i++; i++;
Util::sleep(1000); Util::sleep(1000);
handle = shm_open(name.c_str(), O_RDWR, ACCESSPERMS); handle = shm_open(name.c_str(), O_RDWR, ACCESSPERMS);
@ -96,7 +300,7 @@ namespace IPC {
DEBUG_MSG(DLVL_FAIL, "shm_open for page %s failed: %s", name.c_str(), strerror(errno)); DEBUG_MSG(DLVL_FAIL, "shm_open for page %s failed: %s", name.c_str(), strerror(errno));
return; return;
} }
if (master){ if (master) {
if (ftruncate(handle, 0) < 0) { if (ftruncate(handle, 0) < 0) {
DEBUG_MSG(DLVL_FAIL, "truncate to zero for page %s failed: %s", name.c_str(), strerror(errno)); DEBUG_MSG(DLVL_FAIL, "truncate to zero for page %s failed: %s", name.c_str(), strerror(errno));
return; return;
@ -105,42 +309,53 @@ namespace IPC {
DEBUG_MSG(DLVL_FAIL, "truncate to %lld for page %s failed: %s", len, name.c_str(), strerror(errno)); DEBUG_MSG(DLVL_FAIL, "truncate to %lld for page %s failed: %s", len, name.c_str(), strerror(errno));
return; return;
} }
}else{ } else {
struct stat buffStats; struct stat buffStats;
int xRes = fstat(handle, &buffStats); int xRes = fstat(handle, &buffStats);
if (xRes < 0){ if (xRes < 0) {
return; return;
} }
len = buffStats.st_size; len = buffStats.st_size;
} }
mapped = (char*)mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0); mapped = (char *)mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0);
if (mapped == MAP_FAILED){ if (mapped == MAP_FAILED) {
mapped = 0; mapped = 0;
return; return;
} }
} }
} }
sharedPage::~sharedPage(){ #endif
if (mapped && len){
munmap(mapped,len); ///\brief Default destructor
} sharedPage::~sharedPage() {
if(master){ unmap();
if (master) {
#ifndef __CYGWIN__
shm_unlink(name.c_str()); shm_unlink(name.c_str());
#endif
} }
if (handle > 0){ close();
close(handle);
}
} }
#else #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; handle = 0;
name = name_; name = name_;
len = len_; len = len_;
master = master_; master = master_;
mapped = 0; mapped = 0;
init(name_,len_,master_, autoBackoff); init(name_, len_, master_, autoBackoff);
} }
sharedPage::sharedPage(const sharedPage & rhs){
///\brief Creates a copy of a shared page
///\param rhs The page to copy
sharedPage::sharedPage(const sharedPage & rhs) {
handle = 0; handle = 0;
name = ""; name = "";
len = 0; len = 0;
@ -148,27 +363,36 @@ namespace IPC {
mapped = 0; mapped = 0;
init(rhs.name, rhs.len, rhs.master); init(rhs.name, rhs.len, rhs.master);
} }
sharedPage::~sharedPage(){
if (mapped && len){ ///\brief Default destructor
munmap(mapped,len); sharedPage::~sharedPage() {
} unmap();
if(master){ if (master) {
unlink(name.c_str()); unlink(name.c_str());
} }
if (handle > 0){
close(handle); close(handle);
} }
}
#endif #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) { sharedFile::sharedFile(std::string name_, unsigned int len_, bool master_, bool autoBackoff) : handle(0), name(name_), len(len_), master(master_), mapped(NULL) {
handle = 0; handle = 0;
name = name_; name = name_;
len = len_; len = len_;
master = master_; master = master_;
mapped = 0; mapped = 0;
init(name_,len_,master_, autoBackoff); 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; handle = 0;
name = ""; name = "";
len = 0; len = 0;
@ -176,21 +400,38 @@ namespace IPC {
mapped = 0; mapped = 0;
init(rhs.name, rhs.len, rhs.master); init(rhs.name, rhs.len, rhs.master);
} }
///\brief Returns whether the shared file is valid or not
sharedFile::operator bool() const { sharedFile::operator bool() const {
return mapped != 0; return mapped != 0;
} }
void sharedFile::operator =(sharedFile & rhs){
///\brief Assignment operator
void sharedFile::operator =(sharedFile & rhs) {
init(rhs.name, rhs.len, rhs.master); init(rhs.name, rhs.len, rhs.master);
rhs.master = false;//Make sure the memory does not get unlinked rhs.master = false;//Make sure the memory does not get unlinked
} }
void sharedFile::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) {
if (mapped && len){ ///\brief Unmaps a shared file if allowed
munmap(mapped,len); void sharedFile::unmap() {
if (mapped && len) {
munmap(mapped, len);
mapped = 0;
len = 0;
} }
if(master){ }
///\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()); unlink(name.c_str());
} }
if (handle > 0){ if (handle > 0) {
close(handle); close(handle);
} }
handle = 0; handle = 0;
@ -198,19 +439,19 @@ namespace IPC {
len = len_; len = len_;
master = master_; master = master_;
mapped = 0; mapped = 0;
if (name.size()){ if (name.size()) {
/// \todo Use ACCESSPERMS instead of 0600? /// \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 (handle == -1) {
if (master){ if (master) {
DEBUG_MSG(DLVL_HIGH, "Overwriting old file for %s", name.c_str()); 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{ } else {
int i = 0; int i = 0;
while (i < 10 && handle == -1 && autoBackoff){ while (i < 10 && handle == -1 && autoBackoff) {
i++; i++;
Util::sleep(1000); 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);
} }
} }
} }
@ -218,154 +459,180 @@ namespace IPC {
perror(std::string("open for file " + name + " failed").c_str()); perror(std::string("open for file " + name + " failed").c_str());
return; return;
} }
if (master){ if (master) {
if (ftruncate(handle, len) < 0) { if (ftruncate(handle, len) < 0) {
perror(std::string("ftruncate to len for file " + name + " failed").c_str()); perror(std::string("ftruncate to len for file " + name + " failed").c_str());
return; return;
} }
}else{ } else {
struct stat buffStats; struct stat buffStats;
int xRes = fstat(handle, &buffStats); int xRes = fstat(handle, &buffStats);
if (xRes < 0){ if (xRes < 0) {
return; return;
} }
len = buffStats.st_size; len = buffStats.st_size;
} }
mapped = (char*)mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0); mapped = (char *)mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0);
if (mapped == MAP_FAILED){ if (mapped == MAP_FAILED) {
mapped = 0; mapped = 0;
return; return;
} }
} }
} }
sharedFile::~sharedFile(){
if (mapped && len){ ///\brief Default destructor
munmap(mapped,len); sharedFile::~sharedFile() {
} unmap();
if(master){ if (master) {
unlink(name.c_str()); unlink(name.c_str());
} }
if (handle > 0){ if (handle > 0) {
close(handle); close(handle);
} }
} }
///\brief StatExchange constructor, sets the datapointer to the given value
statExchange::statExchange(char * _data) : data(_data) {} statExchange::statExchange(char * _data) : data(_data) {}
///\brief Sets timestamp of the current stats
void statExchange::now(long long int time) { void statExchange::now(long long int time) {
htobll(data, time); htobll(data, time);
} }
///\brief Gets timestamp of the current stats
long long int statExchange::now() { long long int statExchange::now() {
long long int result; long long int result;
btohll(data, result); btohll(data, result);
return result; return result;
} }
///\brief Sets time currently connected
void statExchange::time(long time) { void statExchange::time(long time) {
htobl(data + 8, time); htobl(data + 8, time);
} }
///\brief Gets time currently connected
long statExchange::time() { long statExchange::time() {
long result; long result;
btohl(data + 8, result); btohl(data + 8, result);
return result; return result;
} }
///\brief Sets the last viewing second of this user
void statExchange::lastSecond(long time) { void statExchange::lastSecond(long time) {
htobl(data + 12, time); htobl(data + 12, time);
} }
///\brief Gets the last viewing second of this user
long statExchange::lastSecond() { long statExchange::lastSecond() {
long result; long result;
btohl(data + 12, result); btohl(data + 12, result);
return result; return result;
} }
///\brief Sets the amount of bytes received
void statExchange::down(long long int bytes) { void statExchange::down(long long int bytes) {
htobll(data + 16, bytes); htobll(data + 16, bytes);
} }
///\brief Gets the amount of bytes received
long long int statExchange::down() { long long int statExchange::down() {
long long int result; long long int result;
btohll(data + 16, result); btohll(data + 16, result);
return result; return result;
} }
///\brief Sets the amount of bytes sent
void statExchange::up(long long int bytes) { void statExchange::up(long long int bytes) {
htobll(data + 24, bytes); htobll(data + 24, bytes);
} }
///\brief Gets the amount of bytes sent
long long int statExchange::up() { long long int statExchange::up() {
long long int result; long long int result;
btohll(data + 24, result); btohll(data + 24, result);
return result; return result;
} }
///\brief Sets the host of this connection
void statExchange::host(std::string name) { void statExchange::host(std::string name) {
memcpy(data + 32, name.c_str(), std::min((int)name.size(), 16)); memcpy(data + 32, name.c_str(), std::min((int)name.size(), 16));
} }
///\brief Gets the host of this connection
std::string statExchange::host() { std::string statExchange::host() {
return std::string(data + 32, std::min((int)strlen(data + 32), 16)); 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) { void statExchange::streamName(std::string name) {
memcpy(data + 48, name.c_str(), std::min((int)name.size(), 20)); 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() { std::string statExchange::streamName() {
return std::string(data + 48, std::min((int)strlen(data + 48), 20)); 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) { void statExchange::connector(std::string name) {
memcpy(data + 68, name.c_str(), std::min((int)name.size(), 20)); 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() { std::string statExchange::connector() {
return std::string(data + 68, std::min((int)strlen(data + 68), 20)); return std::string(data + 68, std::min((int)strlen(data + 68), 20));
} }
///\brief Creates a semaphore guard, locks the semaphore on call
semGuard::semGuard(sem_t * semaphore) : mySemaphore(semaphore) { semGuard::semGuard(semaphore thisSemaphore) : mySemaphore(thisSemaphore) {
sem_wait(mySemaphore); mySemaphore.wait();
} }
///\brief Destructs a semaphore guard, unlocks the semaphore on call
semGuard::~semGuard() { semGuard::~semGuard() {
sem_post(mySemaphore); mySemaphore.post();
} }
sharedServer::sharedServer(){ ///\brief Default constructor, erases all the values
mySemaphore = 0; sharedServer::sharedServer() {
payLen = 0; payLen = 0;
hasCounter = false; hasCounter = false;
amount = 0; amount = 0;
} }
sharedServer::sharedServer(std::string name, int len, bool withCounter){ ///\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(); sharedServer();
init(name, len, withCounter); init(name, len, withCounter);
} }
///\brief Initialize the server
void sharedServer::init(std::string name, int len, bool withCounter){ ///\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; amount = 0;
if (mySemaphore != SEM_FAILED) { if (mySemaphore) {
sem_close(mySemaphore); mySemaphore.close();
} }
if (baseName != ""){ if (baseName != "") {
sem_unlink(std::string("/" + baseName).c_str()); mySemaphore.unlink();
} }
myPages.clear(); myPages.clear();
baseName = name; baseName = name;
payLen = len; payLen = len;
hasCounter = withCounter; hasCounter = withCounter;
mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_CREAT | O_EXCL | O_RDWR, ACCESSPERMS, 1); mySemaphore.open(std::string("/" + baseName).c_str(), O_CREAT | O_EXCL | O_RDWR, ACCESSPERMS, 1);
if (mySemaphore == SEM_FAILED) { if (!mySemaphore) {
mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1); 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)); DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return; return;
} }
newPage(); newPage();
@ -375,17 +642,18 @@ namespace IPC {
newPage(); newPage();
} }
///\brief The deconstructor
sharedServer::~sharedServer() { sharedServer::~sharedServer() {
if (mySemaphore != SEM_FAILED) { mySemaphore.close();
sem_close(mySemaphore); mySemaphore.unlink();
}
sem_unlink(std::string("/" + baseName).c_str());
} }
///\brief Determines whether a sharedServer is valid
sharedServer::operator bool() const { sharedServer::operator bool() const {
return myPages.size(); return myPages.size();
} }
///\brief Creates the next page with the correct size
void sharedServer::newPage() { void sharedServer::newPage() {
semGuard tmpGuard(mySemaphore); semGuard tmpGuard(mySemaphore);
sharedPage tmp(std::string(baseName + (char)(myPages.size() + (int)'A')), (4096 << myPages.size()), true); 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()); DEBUG_MSG(DLVL_MEDIUM, "Added a new page: %s", tmp.name.c_str());
} }
///\brief Deletes the highest allocated page
void sharedServer::deletePage() { void sharedServer::deletePage() {
if (myPages.size() == 1) { if (myPages.size() == 1) {
DEBUG_MSG(DLVL_WARN, "Can't remove last page for %s", baseName.c_str()); DEBUG_MSG(DLVL_WARN, "Can't remove last page for %s", baseName.c_str());
@ -403,25 +672,26 @@ namespace IPC {
myPages.erase((*myPages.rbegin())); myPages.erase((*myPages.rbegin()));
} }
bool sharedServer::isInUse(unsigned int id){ ///\brief Determines whether an id is currently in use or not
bool sharedServer::isInUse(unsigned int id) {
unsigned int i = 0; unsigned int i = 0;
for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) { for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) {
//return if we reached the end //return if we reached the end
if (!it->mapped || !it->len){ if (!it->mapped || !it->len) {
return false; return false;
} }
//not on this page? skip to next. //not on this page? skip to next.
if (it->len < (id - i)*payLen){ if (it->len < (id - i)*payLen) {
i += it->len / payLen; i += it->len / payLen;
continue; continue;
} }
if (hasCounter){ if (hasCounter) {
//counter? return true if it is non-zero. //counter? return true if it is non-zero.
return (it->mapped[(id - i)*payLen] != 0); return (it->mapped[(id - i) * payLen] != 0);
}else{ } else {
//no counter - check the entire size for being all zeroes. //no counter - check the entire size for being all zeroes.
for (unsigned int j = 0; j < payLen; ++j){ for (unsigned int j = 0; j < payLen; ++j) {
if (it->mapped[(id-i)*payLen+j]){ if (it->mapped[(id - i)*payLen + j]) {
return true; return true;
} }
} }
@ -432,6 +702,7 @@ namespace IPC {
return false; 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)) { void sharedServer::parseEach(void (*callback)(char * data, size_t len, unsigned int id)) {
char * empty = 0; char * empty = 0;
if (!hasCounter) { if (!hasCounter) {
@ -440,18 +711,18 @@ namespace IPC {
} }
unsigned int id = 0; unsigned int id = 0;
for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) { for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) {
if (!it->mapped || !it->len){ if (!it->mapped || !it->len) {
DEBUG_MSG(DLVL_FAIL, "Something went terribly wrong?"); DEBUG_MSG(DLVL_FAIL, "Something went terribly wrong?");
break; break;
} }
unsigned int offset = 0; unsigned int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= it->len) { while (offset + payLen + (hasCounter ? 1 : 0) <= it->len) {
if (hasCounter){ if (hasCounter) {
if (it->mapped[offset] != 0) { if (it->mapped[offset] != 0) {
int counter = it->mapped[offset]; int counter = it->mapped[offset];
//increase the count if needed //increase the count if needed
if (id >= amount){ if (id >= amount) {
amount = id+1; amount = id + 1;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
callback(it->mapped + offset + 1, payLen, id); callback(it->mapped + offset + 1, payLen, id);
@ -477,11 +748,11 @@ namespace IPC {
} else { } else {
it->mapped[offset] ++; it->mapped[offset] ++;
} }
}else{ } else {
//stop if we're past the amount counted and we're empty //stop if we're past the amount counted and we're empty
if (id >= amount - 1){ if (id >= amount - 1) {
//bring the counter down if this was the last element //bring the counter down if this was the last element
if (id == amount - 1){ if (id == amount - 1) {
amount = id; amount = id;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
@ -489,24 +760,24 @@ namespace IPC {
return; return;
} }
} }
}else{ } else {
if (memcmp(empty, it->mapped + offset, payLen)) { if (memcmp(empty, it->mapped + offset, payLen)) {
//increase the count if needed //increase the count if needed
if (id >= amount){ if (id >= amount) {
amount = id+1; amount = id + 1;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
callback(it->mapped + offset, payLen, id); callback(it->mapped + offset, payLen, id);
}else{ } else {
//stop if we're past the amount counted and we're empty //stop if we're past the amount counted and we're empty
if (id >= amount - 1){ if (id >= amount - 1) {
//bring the counter down if this was the last element //bring the counter down if this was the last element
if (id == amount - 1){ if (id == amount - 1) {
amount = id; amount = id;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount); DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
} }
//stop, we're guaranteed no more pages are full at this point //stop, we're guaranteed no more pages are full at this point
if (empty){ if (empty) {
free(empty); free(empty);
} }
return; return;
@ -517,11 +788,12 @@ namespace IPC {
id ++; id ++;
} }
} }
if (empty){ if (empty) {
free(empty); free(empty);
} }
} }
///\brief Creates an empty shared client
sharedClient::sharedClient() { sharedClient::sharedClient() {
hasCounter = 0; hasCounter = 0;
payLen = 0; payLen = 0;
@ -529,60 +801,67 @@ namespace IPC {
mySemaphore = 0; mySemaphore = 0;
} }
sharedClient::sharedClient(const sharedClient & rhs ) { ///\brief Copy constructor for sharedClients
///\param rhs The client ro copy
sharedClient::sharedClient(const sharedClient & rhs) {
baseName = rhs.baseName; baseName = rhs.baseName;
payLen = rhs.payLen; payLen = rhs.payLen;
hasCounter = rhs.hasCounter; hasCounter = rhs.hasCounter;
#ifdef __APPLE__ #ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably //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 #else
mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR); mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR);
#endif #endif
if (mySemaphore == SEM_FAILED) { if (!mySemaphore) {
DEBUG_MSG(DLVL_FAIL,"Creating semaphore failed: %s", strerror(errno)); DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return; return;
} }
semGuard tmpGuard(mySemaphore); semGuard tmpGuard(mySemaphore);
myPage.init(rhs.myPage.name,rhs.myPage.len,rhs.myPage.master); myPage.init(rhs.myPage.name, rhs.myPage.len, rhs.myPage.master);
offsetOnPage = rhs.offsetOnPage; offsetOnPage = rhs.offsetOnPage;
} }
void sharedClient::operator =(const sharedClient & rhs ) { ///\brief Assignment operator
void sharedClient::operator =(const sharedClient & rhs) {
baseName = rhs.baseName; baseName = rhs.baseName;
payLen = rhs.payLen; payLen = rhs.payLen;
hasCounter = rhs.hasCounter; hasCounter = rhs.hasCounter;
#ifdef __APPLE__ #ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably //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 #else
mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR); mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR);
#endif #endif
if (mySemaphore == SEM_FAILED) { if (!mySemaphore) {
DEBUG_MSG(DLVL_FAIL,"Creating semaphore failed: %s", strerror(errno)); DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return; return;
} }
semGuard tmpGuard(mySemaphore); semGuard tmpGuard(mySemaphore);
myPage.init(rhs.myPage.name,rhs.myPage.len,rhs.myPage.master); myPage.init(rhs.myPage.name, rhs.myPage.len, rhs.myPage.master);
offsetOnPage = rhs.offsetOnPage; 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) { sharedClient::sharedClient(std::string name, int len, bool withCounter) : baseName(name), payLen(len), offsetOnPage(-1), hasCounter(withCounter) {
#ifdef __APPLE__ #ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably //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 #else
mySemaphore = sem_open(std::string("/" + baseName).c_str(), O_RDWR); mySemaphore.open(std::string("/" + baseName).c_str(), O_RDWR);
#endif #endif
if (mySemaphore == SEM_FAILED) { if (!mySemaphore) {
DEBUG_MSG(DLVL_FAIL,"Creating semaphore failed: %s", strerror(errno)); DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return; return;
} }
semGuard tmpGuard(mySemaphore); semGuard tmpGuard(mySemaphore);
char * empty = 0; char * empty = 0;
if (!hasCounter) { if (!hasCounter) {
empty = (char *)malloc(payLen * sizeof(char)); empty = (char *)malloc(payLen * sizeof(char));
if (!empty){ if (!empty) {
DEBUG_MSG(DLVL_FAIL, "Failed to allocate %u bytes for empty payload!", payLen); DEBUG_MSG(DLVL_FAIL, "Failed to allocate %u bytes for empty payload!", payLen);
return; return;
} }
@ -608,15 +887,15 @@ namespace IPC {
free(empty); free(empty);
} }
///\brief The deconstructor
sharedClient::~sharedClient() { sharedClient::~sharedClient() {
if (hasCounter){ if (hasCounter) {
finish(); finish();
} }
if (mySemaphore != SEM_FAILED) { mySemaphore.close();
sem_close(mySemaphore);
}
} }
///\brief Writes data to the shared data
void sharedClient::write(char * data, int len) { void sharedClient::write(char * data, int len) {
if (hasCounter) { if (hasCounter) {
keepAlive(); keepAlive();
@ -624,30 +903,36 @@ namespace IPC {
memcpy(myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0), data, std::min(len, payLen)); 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() { void sharedClient::finish() {
if (!hasCounter) { if (!hasCounter) {
DEBUG_MSG(DLVL_WARN, "Trying to time-out an element without counters"); DEBUG_MSG(DLVL_WARN, "Trying to time-out an element without counters");
return; return;
} }
if (myPage.mapped){ if (myPage.mapped) {
myPage.mapped[offsetOnPage] = 127; myPage.mapped[offsetOnPage] = 127;
} }
} }
///\brief Re-initialize the counter
void sharedClient::keepAlive() { void sharedClient::keepAlive() {
if (!hasCounter) { if (!hasCounter) {
DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element without counters"); DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element without counters");
return; return;
} }
if (myPage.mapped[offsetOnPage] < 128){ if (myPage.mapped[offsetOnPage] < 128) {
myPage.mapped[offsetOnPage] = 1; myPage.mapped[offsetOnPage] = 1;
}else{ } else {
DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element that needs to timeout, ignoring"); DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element that needs to timeout, ignoring");
} }
} }
///\brief Get a pointer to the data of this client
char * sharedClient::getData() { char * sharedClient::getData() {
if (!myPage.mapped){return 0;} if (!myPage.mapped) {
return 0;
}
return (myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0)); return (myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0));
} }
} }

View file

@ -1,12 +1,18 @@
#pragma once #pragma once
#include <string> #include <string>
#include <set> #include <set>
#include <semaphore.h>
#include "timing.h" #include "timing.h"
#ifdef __CYGWIN__
#include <windows.h>
#else
#include <semaphore.h>
#endif
namespace IPC { namespace IPC {
///\brief A class used for the exchange of statistics over shared memory.
class statExchange { class statExchange {
public: public:
statExchange(char * _data); statExchange(char * _data);
@ -27,18 +33,53 @@ namespace IPC {
void connector(std::string name); void connector(std::string name);
std::string connector(); std::string connector();
private: 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; char * data;
}; };
class semGuard { ///\brief A class used for the abstraction of semaphores
class semaphore {
public: public:
semGuard(sem_t * semaphore); semaphore();
~semGuard(); 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: 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{ class sharedPage{
public: public:
sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); 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 { bool operator < (const sharedPage & rhs) const {
return name < rhs.name; 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; int handle;
#endif
///\brief The name of the opened shared memory page
std::string name; std::string name;
///\brief The size in bytes of the opened shared memory page
long long int len; long long int len;
///\brief Whether this class should unlink the shared memory upon deletion or not
bool master; bool master;
///\brief A pointer to the payload of the page
char * mapped; char * mapped;
}; };
#else #else
class sharedPage; class sharedPage;
#endif #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{ class sharedFile{
public: public:
sharedFile(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); sharedFile(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true);
sharedFile(const sharedPage & rhs); sharedFile(const sharedFile & rhs);
~sharedFile(); ~sharedFile();
operator bool() const; operator bool() const;
void init(std::string name_, unsigned int len_, bool master_ = false, bool autoBackoff = true); 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 { bool operator < (const sharedFile & rhs) const {
return name < rhs.name; return name < rhs.name;
} }
void unmap();
///\brief The fd handle of the opened shared file
int handle; int handle;
///\brief The name of the opened shared file
std::string name; std::string name;
///\brief The size in bytes of the opened shared file
long long int len; long long int len;
///\brief Whether this class should unlink the shared file upon deletion or not
bool master; bool master;
///\brief A pointer to the payload of the file file
char * mapped; 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{ class sharedPage: public sharedFile{
public: public:
sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true); sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true);
@ -87,6 +154,15 @@ namespace IPC {
}; };
#endif #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{ class sharedServer{
public: public:
sharedServer(); sharedServer();
@ -95,18 +171,33 @@ namespace IPC {
~sharedServer(); ~sharedServer();
void parseEach(void (*callback)(char * data, size_t len, unsigned int id)); void parseEach(void (*callback)(char * data, size_t len, unsigned int id));
operator bool() const; operator bool() const;
///\brief The amount of connected clients
unsigned int amount; unsigned int amount;
private: private:
bool isInUse(unsigned int id); bool isInUse(unsigned int id);
void newPage(); void newPage();
void deletePage(); void deletePage();
///\brief The basename of the shared pages.
std::string baseName; std::string baseName;
///\brief The length of each consecutive piece of payload
unsigned int payLen; unsigned int payLen;
///\brief The set of sharedPage structures to manage the actual memory
std::set<sharedPage> myPages; 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; 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{ class sharedClient{
public: public:
sharedClient(); sharedClient();
@ -119,11 +210,17 @@ namespace IPC {
void keepAlive(); void keepAlive();
char * getData(); char * getData();
private: private:
///\brief The basename of the shared pages.
std::string baseName; std::string baseName;
///\brief The shared page this client has reserved a space on.
sharedPage myPage; 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; int payLen;
///\brief The offset of the payload reserved for this client within the opened page
int offsetOnPage; int offsetOnPage;
///\brief Whether the payload has a counter, if so, it is added in front of the payload
bool hasCounter; bool hasCounter;
}; };
} }

View file

@ -12,6 +12,7 @@
#include "config.h" #include "config.h"
#include "socket.h" #include "socket.h"
#include "defines.h" #include "defines.h"
#include "shared_memory.h"
std::string Util::getTmpFolder(){ std::string Util::getTmpFolder(){
std::string dir; std::string dir;
@ -119,14 +120,14 @@ bool Util::Stream::getStream(std::string streamname){
JSON::Value ServConf = JSON::fromFile(getTmpFolder() + "streamlist"); JSON::Value ServConf = JSON::fromFile(getTmpFolder() + "streamlist");
if (ServConf["streams"].isMember(streamname)){ if (ServConf["streams"].isMember(streamname)){
//check if the stream is already active, if yes, don't re-activate //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); IPC::semaphore playerLock(std::string("/lock_" + streamname).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1);
if (sem_trywait(playerLock) == -1){ if (!playerLock.tryWait()){
sem_close(playerLock); playerLock.close();
DEBUG_MSG(DLVL_MEDIUM, "Playerlock for %s already active - not re-activating stream", streamname.c_str()); DEBUG_MSG(DLVL_MEDIUM, "Playerlock for %s already active - not re-activating stream", streamname.c_str());
return true; return true;
} }
sem_post(playerLock); playerLock.post();
sem_close(playerLock); playerLock.close();
if (ServConf["streams"][streamname]["source"].asString()[0] == '/'){ if (ServConf["streams"][streamname]["source"].asString()[0] == '/'){
DEBUG_MSG(DLVL_MEDIUM, "Activating VoD stream %s", streamname.c_str()); DEBUG_MSG(DLVL_MEDIUM, "Activating VoD stream %s", streamname.c_str());
return getVod(ServConf["streams"][streamname]["source"].asString(), streamname); return getVod(ServConf["streams"][streamname]["source"].asString(), streamname);