Merged library into mistserver

# Conflicts:
#	.gitignore
#	Doxyfile
#	Makefile
#	README
This commit is contained in:
Thulinma 2015-03-16 04:01:54 +01:00
commit 597dcee303
65 changed files with 26334 additions and 12 deletions

5
.gitignore vendored
View file

@ -3,6 +3,7 @@
*.la *.la
*.lo *.lo
*.swp *.swp
*.orig
*.bak *.bak
*~ *~
.deps .deps
@ -12,6 +13,8 @@ docs
nbproject nbproject
autom4te.cache autom4te.cache
/Mist* /Mist*
/libmist.so
/libmist.a
/configure /configure
/config.* /config.*
/aclocal.m4 /aclocal.m4
@ -37,4 +40,6 @@ server.html*
.cproject .cproject
.project .project
.sync .sync
*.pc
*.swp

View file

@ -1,6 +1,8 @@
prefix = /usr prefix = ./usr
exec_prefix = $(prefix) exec_prefix = $(prefix)
bindir = $(prefix)/bin bindir = $(prefix)/bin
includedir = $(prefix)/include
libdir = $(exec_prefix)/lib
PACKAGE_VERSION := $(shell git describe --tags 2> /dev/null || cat VERSION 2> /dev/null || echo "Unknown") PACKAGE_VERSION := $(shell git describe --tags 2> /dev/null || cat VERSION 2> /dev/null || echo "Unknown")
DEBUG = 4 DEBUG = 4
@ -10,7 +12,7 @@ ifeq ($(PACKAGE_VERSION),Unknown)
$(warning Version is unknown - consider creating a VERSION file or fixing your git setup.) $(warning Version is unknown - consider creating a VERSION file or fixing your git setup.)
endif endif
CPPFLAGS = -Wall -g -O2 CPPFLAGS = -Wall -g -O2 -fPIC
override CPPFLAGS += -funsigned-char -DDEBUG="$(DEBUG)" -DPACKAGE_VERSION="\"$(PACKAGE_VERSION)\"" -DRELEASE="\"$(RELEASE)\"" override CPPFLAGS += -funsigned-char -DDEBUG="$(DEBUG)" -DPACKAGE_VERSION="\"$(PACKAGE_VERSION)\"" -DRELEASE="\"$(RELEASE)\""
ifndef NOSHM ifndef NOSHM
@ -21,13 +23,15 @@ ifdef WITH_THREADNAMES
override CPPFLAGS += -DWITH_THREADNAMES=1 override CPPFLAGS += -DWITH_THREADNAMES=1
endif endif
THREADLIB = -lpthread THREADLIB = -lpthread -lrt
LDLIBS = -lmist -lrt LDLIBS =
LDFLAGS = -I${includedir} -L${libdir} -lmist
.DEFAULT_GOAL := all .DEFAULT_GOAL := all
all: controller analysers inputs outputs lib: libmist.so libmist.a
all: install-lib controller analysers inputs outputs
DOXYGEN := $(shell doxygen -v 2> /dev/null) DOXYGEN := $(shell doxygen -v 2> /dev/null)
ifdef DOXYGEN ifdef DOXYGEN
@ -217,20 +221,42 @@ src/controller/server.html: $(lspDATA) $(lspSOURCES) $(lspSOURCESmin)
src/controller/server.html.h: src/controller/server.html sourcery src/controller/server.html.h: src/controller/server.html sourcery
cd src/controller; ../../sourcery server.html server_html > server.html.h cd src/controller; ../../sourcery server.html server_html > server.html.h
docs: src/* Doxyfile lib_objects := $(patsubst %.cpp,%.o,$(wildcard lib/*.cpp))
libmist.so: $(lib_objects)
$(CXX) -shared -o $@ $(LDLIBS) $^
libmist.a: $(lib_objects)
$(AR) -rcs $@ $^
docs: lib/* src/* Doxyfile Doxyfile
doxygen ./Doxyfile > /dev/null doxygen ./Doxyfile > /dev/null
clean: clean:
rm -f *.o Mist* sourcery src/controller/server.html src/connectors/embed.js.h src/controller/server.html.h rm -f lib/*.o libmist.so libmist.a
rm -rf ./docs rm -rf ./docs
rm -f *.o Mist* sourcery src/controller/server.html src/connectors/embed.js.h src/controller/server.html.h
distclean: clean distclean: clean
install-lib: libmist.so libmist.a lib/*.h
mkdir -p $(DESTDIR)$(includedir)/mist
install -m 644 lib/*.h $(DESTDIR)$(includedir)/mist/
mkdir -p $(DESTDIR)$(libdir)
install -m 644 libmist.a $(DESTDIR)$(libdir)/libmist.a
install -m 644 libmist.so $(DESTDIR)$(libdir)/libmist.so
$(POST_INSTALL)
install: all install: all
if [ "$$USER" = "root" ]; then ldconfig; else echo "run: sudo ldconfig"; fi
mkdir -p $(DESTDIR)$(bindir) mkdir -p $(DESTDIR)$(bindir)
install -m 755 ./Mist* $(DESTDIR)$(bindir) install -m 755 ./Mist* $(DESTDIR)$(bindir)
uninstall: uninstall:
rm -f $(DESTDIR)$(bindir)/Mist* rm -f $(DESTDIR)$(bindir)/Mist*
rm -f $(DESTDIR)$(includedir)/mist/*.h
rmdir $(DESTDIR)$(includedir)/mist
rm -f $(DESTDIR)$(libdir)/libmist.so
rm -f $(DESTDIR)$(libdir)/libmist.a
.PHONY: clean uninstall .PHONY: clean uninstall

12
README
View file

@ -1,11 +1,13 @@
_________________________________________________ _________________________________________________
| MistServer | | MistServer |
| Copyright 2010-2014 DDVTech BV, The Netherlands | | Copyright 2010-2015 DDVTech BV, The Netherlands |
| | | |
| Licensed under the aGPLv3 license | | Licensed under the aGPLv3 license |
| See COPYING file for full license | | See COPYING file for full license |
|_________________________________________________| |_________________________________________________|
NOTE: TinyThread++ is included also, but *not* copyright DDVTech BV.
License and author information for TinyThread++ can be found in the tinythread.h/cpp files.
The latest version of this code can always be found at: The latest version of this code can always be found at:
https://github.com/DDVTECH/mistserver https://github.com/DDVTECH/mistserver
@ -19,9 +21,7 @@ Code contributions and bug reports are welcomed! Please submit at:
To install using default options, simply run: To install using default options, simply run:
make && sudo make install make && sudo make install
Dependencies: Dependencies: none
libmist ( https://github.com/DDVTECH/mistlib )
libpthread
The makefile will listen to the following variables: The makefile will listen to the following variables:
DEBUG Sets the debug message level. 4 is the default (and recommended setting for development), 0 is quiet, 10 is insanely high. DEBUG Sets the debug message level. 4 is the default (and recommended setting for development), 0 is quiet, 10 is insanely high.
@ -29,10 +29,12 @@ The makefile will listen to the following variables:
RELEASE Overrides the release name. You shouldn't need to use this, normally. RELEASE Overrides the release name. You shouldn't need to use this, normally.
prefix Prefix to install files to. Defaults to /usr prefix Prefix to install files to. Defaults to /usr
exec_prefix Prefix to install object code and binaries to. Defaults to $(prefix) exec_prefix Prefix to install object code and binaries to. Defaults to $(prefix)
includedir Directory to install headers to. Defaults to $(prefix)/include
libdir Directory to install libraries to. Defaults to $(exec_prefix)/lib
bindir Directory to install binaries to. Defaults to $(exec_prefix)/bin bindir Directory to install binaries to. Defaults to $(exec_prefix)/bin
DESTDIR Global prefix that will be put in front of any and all other file paths. DESTDIR Global prefix that will be put in front of any and all other file paths.
CPPFLAGS Flags for compiling object files. Defaults to -Wall -g -O2 CPPFLAGS Flags for compiling object files. Defaults to -Wall -g -O2
LDLIBS Libraries to include. Defaults to -lmist LDLIBS Libraries to include. Defaults to none.
THREADLIB Libraries to include for threaded binaries. Defaults to -lpthread THREADLIB Libraries to include for threaded binaries. Defaults to -lpthread
WITH_THREADNAMES If set, this will set names of threads in threaded binaries. Defaults to being unset. WITH_THREADNAMES If set, this will set names of threads in threaded binaries. Defaults to being unset.

1147
lib/amf.cpp Normal file

File diff suppressed because it is too large Load diff

130
lib/amf.h Normal file
View file

@ -0,0 +1,130 @@
/// \file amf.h
/// Holds all headers for the AMF namespace.
#pragma once
#include <vector>
#include <iostream>
#include <string>
/// Holds all AMF parsing and creation related functions and classes.
namespace AMF {
/// Enumerates all possible AMF0 types, adding a special DDVTECH container type for ease of use.
enum obj0type {
AMF0_NUMBER = 0x00,
AMF0_BOOL = 0x01,
AMF0_STRING = 0x02,
AMF0_OBJECT = 0x03,
AMF0_MOVIECLIP = 0x04,
AMF0_NULL = 0x05,
AMF0_UNDEFINED = 0x06,
AMF0_REFERENCE = 0x07,
AMF0_ECMA_ARRAY = 0x08,
AMF0_OBJ_END = 0x09,
AMF0_STRICT_ARRAY = 0x0A,
AMF0_DATE = 0x0B,
AMF0_LONGSTRING = 0x0C,
AMF0_UNSUPPORTED = 0x0D,
AMF0_RECORDSET = 0x0E,
AMF0_XMLDOC = 0x0F,
AMF0_TYPED_OBJ = 0x10,
AMF0_UPGRADE = 0x11,
AMF0_DDV_CONTAINER = 0xFF
};
/// Enumerates all possible AMF3 types, adding a special DDVTECH container type for ease of use.
enum obj3type {
AMF3_UNDEFINED = 0x00,
AMF3_NULL = 0x01,
AMF3_FALSE = 0x02,
AMF3_TRUE = 0x03,
AMF3_INTEGER = 0x04,
AMF3_DOUBLE = 0x05,
AMF3_STRING = 0x06,
AMF3_XMLDOC = 0x07,
AMF3_DATE = 0x08,
AMF3_ARRAY = 0x09,
AMF3_OBJECT = 0x0A,
AMF3_XML = 0x0B,
AMF3_BYTES = 0x0C,
AMF3_DDV_CONTAINER = 0xFF
};
/// Recursive class that holds AMF0 objects.
/// It supports all AMF0 types (defined in AMF::obj0type), adding support for a special DDVTECH container type.
class Object {
public:
std::string Indice();
obj0type GetType();
double NumValue();
std::string StrValue();
const char * Str();
int hasContent();
void addContent(AMF::Object c);
Object * getContentP(unsigned int i);
Object getContent(unsigned int i);
Object * getContentP(std::string s);
Object getContent(std::string s);
Object();
Object(std::string indice, double val, obj0type setType = AMF0_NUMBER);
Object(std::string indice, std::string val, obj0type setType = AMF0_STRING);
Object(std::string indice, obj0type setType = AMF0_OBJECT);
std::string Print(std::string indent = "");
std::string Pack();
protected:
std::string myIndice; ///< Holds this objects indice, if any.
obj0type myType; ///< Holds this objects AMF0 type.
std::string strval; ///< Holds this objects string value, if any.
double numval; ///< Holds this objects numeric value, if any.
std::vector<Object> contents; ///< Holds this objects contents, if any (for container types).
};
//AMFType
/// Parses a C-string to a valid AMF::Object.
Object parse(const unsigned char * data, unsigned int len);
/// Parses a std::string to a valid AMF::Object.
Object parse(std::string data);
/// Parses a single AMF0 type - used recursively by the AMF::parse() functions.
Object parseOne(const unsigned char *& data, unsigned int & len, unsigned int & i, std::string name);
/// Recursive class that holds AMF3 objects.
/// It supports all AMF3 types (defined in AMF::obj3type), adding support for a special DDVTECH container type.
class Object3 {
public:
std::string Indice();
obj3type GetType();
double DblValue();
int IntValue();
std::string StrValue();
const char * Str();
int hasContent();
void addContent(AMF::Object3 c);
Object3 * getContentP(int i);
Object3 getContent(int i);
Object3 * getContentP(std::string s);
Object3 getContent(std::string s);
Object3();
Object3(std::string indice, int val, obj3type setType = AMF3_INTEGER);
Object3(std::string indice, double val, obj3type setType = AMF3_DOUBLE);
Object3(std::string indice, std::string val, obj3type setType = AMF3_STRING);
Object3(std::string indice, obj3type setType = AMF3_OBJECT);
std::string Print(std::string indent = "");
std::string Pack();
protected:
std::string myIndice; ///< Holds this objects indice, if any.
obj3type myType; ///< Holds this objects AMF0 type.
std::string strval; ///< Holds this objects string value, if any.
double dblval; ///< Holds this objects double value, if any.
int intval; ///< Holds this objects int value, if any.
std::vector<Object3> contents; ///< Holds this objects contents, if any (for container types).
};
//AMFType
/// Parses a C-string to a valid AMF::Object3.
Object3 parse3(const unsigned char * data, unsigned int len);
/// Parses a std::string to a valid AMF::Object3.
Object3 parse3(std::string data);
/// Parses a single AMF3 type - used recursively by the AMF::parse3() functions.
Object3 parseOne3(const unsigned char *& data, unsigned int & len, unsigned int & i, std::string name);
} //AMF namespace

337
lib/auth.cpp Normal file
View file

@ -0,0 +1,337 @@
#include "auth.h"
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
#include <sstream>
#include <iomanip>
namespace Secure {
/// Calculates a MD5 digest as per rfc1321, returning it as a hexadecimal alphanumeric string.
std::string md5(std::string input) {
return md5(input.data(), input.size());
}
/// Calculates a MD5 digest as per rfc1321, returning it as a hexadecimal alphanumeric string.
std::string md5(const char * input, const unsigned int in_len){
char output[16];
md5bin(input, in_len, output);
std::stringstream outStr;
for (unsigned int i = 0; i < 16; ++i){
outStr << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)(output[i] & 0xff);
}
return outStr.str();
}
/// Calculates a SHA256 digest as per NSAs SHA-2, returning it as a hexadecimal alphanumeric string.
std::string sha256(std::string input) {
return sha256(input.data(), input.size());
}
/// Calculates a SHA256 digest as per NSAs SHA-2, returning it as a hexadecimal alphanumeric string.
std::string sha256(const char * input, const unsigned int in_len){
char output[32];
sha256bin(input, in_len, output);
std::stringstream outStr;
for (unsigned int i = 0; i < 32; ++i){
outStr << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)(output[i] & 0xff);
}
return outStr.str();
}
/// Adds 64 bytes of data to the current MD5 hash.
/// hash is the current hash, represented by 4 unsigned longs.
/// data is the 64 bytes of data that need to be added.
static inline void md5_add64(uint32_t * hash, const char * data){
//Inspired by the pseudocode as available on Wikipedia on March 2nd, 2015.
uint32_t M[16];
for (unsigned int i = 0; i < 16; ++i){
M[i] = data[i << 2] | (data[(i<<2)+1] << 8) | (data[(i<<2)+2] << 16) | (data[(i<<2)+3] << 24);
}
static unsigned char shift[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
static uint32_t K[] = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
uint32_t A = hash[0];
uint32_t B = hash[1];
uint32_t C = hash[2];
uint32_t D = hash[3];
for (unsigned int i = 0; i < 64; ++i){
uint32_t F, g;
if (i < 16){
F = (B & C) | ((~B) & D);
g = i;
}else if (i < 32){
F = (D & B) | ((~D) & C);
g = (5*i + 1) % 16;
}else if (i < 48){
F = B ^ C ^ D;
g = (3*i + 5) % 16;
}else{
F = C ^ (B | (~D));
g = (7*i) % 16;
}
uint32_t dTemp = D;
D = C;
C = B;
uint32_t x = A + F + K[i] + M[g];
B += (x << shift[i] | (x >> (32-shift[i])));
A = dTemp;
}
hash[0] += A;
hash[1] += B;
hash[2] += C;
hash[3] += D;
}
/// Calculates a MD5 digest as per rfc1321, returning it as binary.
/// Assumes output is big enough to contain 16 bytes of data.
void md5bin(const char * input, const unsigned int in_len, char * output){
//Initialize the hash, according to MD5 spec.
uint32_t hash[] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
//Add as many whole blocks of 64 bytes as possible from the input, until < 64 are left.
unsigned int offset = 0;
while (offset+64 <= in_len){
md5_add64(hash, input+offset);
offset += 64;
}
//now, copy the remainder to a 64 byte buffer.
char buffer[64];
memcpy(buffer, input+offset, in_len-offset);
//Calculate how much we've filled in that buffer
offset = in_len - offset;
//We know at least 1 byte must be empty, so we can safely do this
buffer[offset] = 0x80;//append 0x80
//fill to the end of the buffer with zeroes
memset(buffer+offset+1, 0, 64-offset-1);
if (offset > 55){
//There's no space for the length, add what we have and zero it
md5_add64(hash, buffer);
memset(buffer, 0, 64);
}
unsigned long long bit_len = in_len << 3;
//Write the length into the last 8 bytes
buffer[56] = (bit_len >> 0) & 0xff;
buffer[57] = (bit_len >> 8) & 0xff;
buffer[58] = (bit_len >> 16) & 0xff;
buffer[59] = (bit_len >> 24) & 0xff;
buffer[60] = (bit_len >> 32) & 0xff;
buffer[61] = (bit_len >> 40) & 0xff;
buffer[62] = (bit_len >> 48) & 0xff;
buffer[63] = (bit_len >> 54) & 0xff;
//Add the last bit of buffer
md5_add64(hash, buffer);
//Write to output
//convert hash to hexadecimal string
output[0 ] = (hash[0] >> 0 ) & 0xff;
output[1 ] = (hash[0] >> 8 ) & 0xff;
output[2 ] = (hash[0] >> 16) & 0xff;
output[3 ] = (hash[0] >> 24) & 0xff;
output[4 ] = (hash[1] >> 0 ) & 0xff;
output[5 ] = (hash[1] >> 8 ) & 0xff;
output[6 ] = (hash[1] >> 16) & 0xff;
output[7 ] = (hash[1] >> 24) & 0xff;
output[8 ] = (hash[2] >> 0 ) & 0xff;
output[9 ] = (hash[2] >> 8 ) & 0xff;
output[10] = (hash[2] >> 16) & 0xff;
output[11] = (hash[2] >> 24) & 0xff;
output[12] = (hash[3] >> 0 ) & 0xff;
output[13] = (hash[3] >> 8 ) & 0xff;
output[14] = (hash[3] >> 16) & 0xff;
output[15] = (hash[3] >> 24) & 0xff;
}
/// Right rotate function. Shifts bytes off the least significant end, wrapping them to the most significant end.
static inline uint32_t rr(uint32_t x, uint32_t c){
return ((x << (32 - c)) | ((x & 0xFFFFFFFF) >> c));
}
/// Adds 64 bytes of data to the current SHA256 hash.
/// hash is the current hash, represented by 8 unsigned longs.
/// data is the 64 bytes of data that need to be added.
static inline void sha256_add64(uint32_t * hash, const char * data){
//Inspired by the pseudocode as available on Wikipedia on March 3rd, 2015.
uint32_t w[64];
for (unsigned int i = 0; i < 16; ++i){
w[i] = (uint32_t)data[(i<<2)+3] | ((uint32_t)data[(i<<2)+2] << 8) | ((uint32_t)data[(i<<2)+1] << 16) | ((uint32_t)data[(i<<2)+0] << 24);
}
for (unsigned int i = 16; i < 64; ++i){
uint32_t s0 = rr(w[i-15], 7) ^ rr(w[i-15], 18) ^ ((w[i-15] & 0xFFFFFFFF ) >> 3);
uint32_t s1 = rr(w[i-2], 17) ^ rr(w[i-2], 19) ^ ((w[i-2] & 0xFFFFFFFF) >> 10);
w[i] = w[i-16] + s0 + w[i-7] + s1;
}
static uint32_t k[] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
uint32_t a = hash[0];
uint32_t b = hash[1];
uint32_t c = hash[2];
uint32_t d = hash[3];
uint32_t e = hash[4];
uint32_t f = hash[5];
uint32_t g = hash[6];
uint32_t h = hash[7];
for (unsigned int i = 0; i < 64; ++i){
uint32_t temp1 = h + (rr(e, 6) ^ rr(e, 11) ^ rr(e, 25)) + (g^(e&(f^g))) + k[i] + w[i];
uint32_t temp2 = (rr(a, 2) ^ rr(a, 13) ^ rr(a, 22)) + ((a&b)|(c&(a|b)));
h = g;
g = f;
f = e;
e = d + temp1;
d = c;
c = b;
b = a;
a = temp1 + temp2;
}
hash[0] += a;
hash[1] += b;
hash[2] += c;
hash[3] += d;
hash[4] += e;
hash[5] += f;
hash[6] += g;
hash[7] += h;
}
/// Calculates a SHA256 digest as per NSAs SHA-2, returning it as binary.
/// Assumes output is big enough to contain 16 bytes of data.
void sha256bin(const char * input, const unsigned int in_len, char * output){
//Initialize the hash, according to MD5 spec.
uint32_t hash[] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
//Add as many whole blocks of 64 bytes as possible from the input, until < 64 are left.
unsigned int offset = 0;
while (offset+64 <= in_len){
sha256_add64(hash, input+offset);
offset += 64;
}
//now, copy the remainder to a 64 byte buffer.
char buffer[64];
memcpy(buffer, input+offset, in_len-offset);
//Calculate how much we've filled in that buffer
offset = in_len - offset;
//We know at least 1 byte must be empty, so we can safely do this
buffer[offset] = 0x80;//append 0x80
//fill to the end of the buffer with zeroes
memset(buffer+offset+1, 0, 64-offset-1);
if (offset > 55){
//There's no space for the length, add what we have and zero it
sha256_add64(hash, buffer);
memset(buffer, 0, 64);
}
unsigned long long bit_len = in_len << 3;
//Write the length into the last 8 bytes
buffer[56] = (bit_len >> 54) & 0xff;
buffer[57] = (bit_len >> 48) & 0xff;
buffer[58] = (bit_len >> 40) & 0xff;
buffer[59] = (bit_len >> 32) & 0xff;
buffer[60] = (bit_len >> 24) & 0xff;
buffer[61] = (bit_len >> 16) & 0xff;
buffer[62] = (bit_len >> 8) & 0xff;
buffer[63] = (bit_len >> 0) & 0xff;
//Add the last bit of buffer
sha256_add64(hash, buffer);
//Write result to output
output[3] = hash[0] & 0xff;
output[2] = (hash[0] >> 8) & 0xff;
output[1] = (hash[0] >> 16) & 0xff;
output[0] = (hash[0] >> 24) & 0xff;
output[7] = hash[1] & 0xff;
output[6] = (hash[1] >> 8) & 0xff;
output[5] = (hash[1] >> 16) & 0xff;
output[4] = (hash[1] >> 24) & 0xff;
output[11] = hash[2] & 0xff;
output[10] = (hash[2] >> 8) & 0xff;
output[9] = (hash[2] >> 16) & 0xff;
output[8] = (hash[2] >> 24) & 0xff;
output[15] = hash[3] & 0xff;
output[14] = (hash[3] >> 8) & 0xff;
output[13] = (hash[3] >> 16) & 0xff;
output[12] = (hash[3] >> 24) & 0xff;
output[19] = hash[4] & 0xff;
output[18] = (hash[4] >> 8) & 0xff;
output[17] = (hash[4] >> 16) & 0xff;
output[16] = (hash[4] >> 24) & 0xff;
output[23] = hash[5] & 0xff;
output[22] = (hash[5] >> 8) & 0xff;
output[21] = (hash[5] >> 16) & 0xff;
output[20] = (hash[5] >> 24) & 0xff;
output[27] = hash[6] & 0xff;
output[26] = (hash[6] >> 8) & 0xff;
output[25] = (hash[6] >> 16) & 0xff;
output[24] = (hash[6] >> 24) & 0xff;
output[31] = hash[7] & 0xff;
output[30] = (hash[7] >> 8) & 0xff;
output[29] = (hash[7] >> 16) & 0xff;
output[28] = (hash[7] >> 24) & 0xff;
}
/// Performs HMAC on msg with given key.
/// Uses given hasher function, requires hashSize to be set accordingly.
/// Output is returned as hexadecimal alphanumeric string.
/// The hasher function must be the "bin" version of the hasher to have a compatible function signature.
std::string hmac(std::string msg, std::string key, unsigned int hashSize, void hasher(const char *, const unsigned int, char*), unsigned int blockSize){
return hmac(msg.data(), msg.size(), key.data(), key.size(), hashSize, hasher, blockSize);
}
/// Performs HMAC on msg with given key.
/// Uses given hasher function, requires hashSize to be set accordingly.
/// Output is returned as hexadecimal alphanumeric string.
/// The hasher function must be the "bin" version of the hasher to have a compatible function signature.
std::string hmac(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len, unsigned int hashSize, void hasher(const char *, const unsigned int, char*), unsigned int blockSize){
char output[hashSize];
hmacbin(msg, msg_len, key, key_len, hashSize, hasher, blockSize, output);
std::stringstream outStr;
for (unsigned int i = 0; i < hashSize; ++i){
outStr << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)(output[i] & 0xff);
}
return outStr.str();
}
/// Performs HMAC on msg with given key.
/// Uses given hasher function, requires hashSize to be set accordingly.
/// Output is written in binary form to output, and assumes hashSize bytes are available to be written to.
/// The hasher function must be the "bin" version of the hasher to have a compatible function signature.
void hmacbin(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len, unsigned int hashSize, void hasher(const char*, const unsigned int, char*), unsigned int blockSize, char * output){
char key_data[blockSize];//holds key as used in HMAC algorithm
if (key_len > blockSize){
//If the key given is too big, hash it.
hasher(key, key_len, key_data);
memset(key_data+hashSize, 0, blockSize-hashSize);
}else{
//Otherwise, use as-is, zero-padded if too small.
memcpy(key_data, key, key_len);
memset(key_data+key_len, 0, blockSize-key_len);
}
//key_data now contains hashSize bytes of key data, treated as per spec.
char inner[blockSize+msg_len];//holds data for inner hash
char outer[blockSize+hashSize];//holds data for outer hash
for (unsigned int i = 0; i < blockSize; ++i){
inner[i] = key_data[i] ^ 0x36;
outer[i] = key_data[i] ^ 0x5c;
}
//Copy the message to the inner hash data buffer
memcpy(inner+blockSize, msg, msg_len);
//Calculate the inner hash
hasher(inner, blockSize+msg_len, outer+blockSize);
//Calculate the outer hash
hasher(outer, blockSize+hashSize, output);
}
/// Convenience function that returns the hexadecimal alphanumeric HMAC-SHA256 of msg and key
std::string hmac_sha256(std::string msg, std::string key){
return hmac_sha256(msg.data(), msg.size(), key.data(), key.size());
}
/// Convenience function that returns the hexadecimal alphanumeric HMAC-SHA256 of msg and key
std::string hmac_sha256(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len){
return hmac(msg, msg_len, key, key_len, 32, sha256bin, 64);
}
/// Convenience function that sets output to the HMAC-SHA256 of msg and key in binary format.
/// Assumes at least 32 bytes are available for writing in output.
void hmac_sha256bin(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len, char * output){
return hmacbin(msg, msg_len, key, key_len, 32, sha256bin, 64, output);
}
}

25
lib/auth.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include <string>
namespace Secure {
//MD5 hashing functions
std::string md5(std::string input);
std::string md5(const char * input, const unsigned int in_len);
void md5bin(const char * input, const unsigned int in_len, char * output);
//SHA256 hashing functions
std::string sha256(std::string input);
std::string sha256(const char * input, const unsigned int in_len);
void sha256bin(const char * input, const unsigned int in_len, char * output);
//Generic HMAC functions
std::string hmac(std::string msg, std::string key, unsigned int hashSize, void hasher(const char *, const unsigned int, char*), unsigned int blockSize);
std::string hmac(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len, unsigned int hashSize, void hasher(const char *, const unsigned int, char*), unsigned int blockSize);
void hmacbin(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len, unsigned int hashSize, void hasher(const char*, const unsigned int, char*), unsigned int blockSize, char * output);
//Specific HMAC functions
std::string hmac_sha256(std::string msg, std::string key);
std::string hmac_sha256(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len);
void hmac_sha256bin(const char * msg, const unsigned int msg_len, const char * key, const unsigned int key_len, char * output);
}

86
lib/base64.cpp Normal file
View file

@ -0,0 +1,86 @@
#include "base64.h"
/// Needed for base64_encode function
const std::string Base64::chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/// Helper for base64_decode function
inline bool Base64::is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
/// Used to base64 encode data. Input is the plaintext as std::string, output is the encoded data as std::string.
/// \param input Plaintext data to encode.
/// \returns Base64 encoded data.
std::string Base64::encode(std::string const input) {
std::string ret;
unsigned int in_len = input.size();
char quad[4], triple[3];
unsigned int i, x, n = 3;
for (x = 0; x < in_len; x = x + 3) {
if ((in_len - x) / 3 == 0) {
n = (in_len - x) % 3;
}
for (i = 0; i < 3; i++) {
triple[i] = '0';
}
for (i = 0; i < n; i++) {
triple[i] = input[x + i];
}
quad[0] = chars[(triple[0] & 0xFC) >> 2]; // FC = 11111100
quad[1] = chars[((triple[0] & 0x03) << 4) | ((triple[1] & 0xF0) >> 4)]; // 03 = 11
quad[2] = chars[((triple[1] & 0x0F) << 2) | ((triple[2] & 0xC0) >> 6)]; // 0F = 1111, C0=11110
quad[3] = chars[triple[2] & 0x3F]; // 3F = 111111
if (n < 3) {
quad[3] = '=';
}
if (n < 2) {
quad[2] = '=';
}
for (i = 0; i < 4; i++) {
ret += quad[i];
}
}
return ret;
} //base64_encode
/// Used to base64 decode data. Input is the encoded data as std::string, output is the plaintext data as std::string.
/// \param encoded_string Base64 encoded data to decode.
/// \returns Plaintext decoded data.
std::string Base64::decode(std::string const & encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++ ] = encoded_string[in_];
in_++;
if (i == 4) {
for (i = 0; i < 4; i++) {
char_array_4[i] = chars.find(char_array_4[i]);
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++) {
ret += char_array_3[i];
}
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) {
char_array_4[j] = 0;
}
for (j = 0; j < 4; j++) {
char_array_4[j] = chars.find(char_array_4[j]);
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++)
ret += char_array_3[j];
}
return ret;
}

12
lib/base64.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include <string>
/// Holds base64 decoding and encoding functions.
class Base64 {
private:
static const std::string chars;
static inline bool is_base64(unsigned char c);
public:
static std::string encode(std::string const input);
static std::string decode(std::string const & encoded_string);
};

64
lib/bitfields.cpp Normal file
View file

@ -0,0 +1,64 @@
#include "bitfields.h"
/// Takes a pointer, offset bitcount and data bitcount, returning the unsigned int read from the givens.
/// offsetBits may be > 7, in which case offsetBits / 8 is added to the pointer automatically.
/// This function assumes Most Significant Bits first.
/// If dataBits > 64, only the last 64 bits are returned.
unsigned long long Bit::getMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits){
//If the offset is a whole byte or more, add the whole bytes to the pointer instead.
pointer += offsetBits >> 3;
//The offset is now guaranteed less than a whole byte.
offsetBits &= 0x07;
unsigned long long retVal = 0;
//Now we parse the remaining bytes
while (dataBits){
//Calculate how many bits we're reading from this byte
//We assume all except for the offset
unsigned int curBits = 8 - offsetBits;
//If that is too much, we use the remainder instead
if (curBits > dataBits){
curBits = dataBits;
}
//First, shift the current return value by the amount of bits we're adding
retVal <<= curBits;
//Next, add those bits from the current pointer position at the correct offset, increasing the pointer by one
retVal |= ((int)(*(pointer++)) << offsetBits) >> (8 - curBits);
//Finally, set the offset to zero and remove curBits from dataBits.
offsetBits = 0;
dataBits -= curBits;
}//Loop until we run out of dataBits, then return the result
return retVal;
}
/// Takes a pointer, offset bitcount and data bitcount, setting to given value.
/// offsetBits may be > 7, in which case offsetBits / 8 is added to the pointer automatically.
/// This function assumes Most Significant Bits first.
/// WARNING: UNFINISHED. DO NOT USE.
/// \todo Finish writing this - untested atm.
void Bit::setMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits, unsigned long long value){
//Set the pointer to the last byte we need to be setting
pointer += (offsetBits + dataBits) >> 3;
//The offset is now guaranteed less than a whole byte.
offsetBits = (offsetBits + dataBits) & 0x07;
unsigned long long retVal = 0;
//Now we set the remaining bytes
while (dataBits){
//Calculate how many bits we're setting in this byte
//We assume all that will fit in the current byte
unsigned int curBits = offsetBits;
//If that is too much, we use the remainder instead
if (curBits > dataBits){
curBits = dataBits;
}
//Set the current pointer position at the correct offset, increasing the pointer by one
retVal |= ((int)(*(pointer++)) << offsetBits) >> (8 - curBits);
*pointer = (((*pointer) << offsetBits) >> offsetBits) | ((value & 0xFF) << (8 - offsetBits));
--pointer;
//Finally, shift the current value by the amount of bits we're adding
value >>= offsetBits;
//... and set the offset to eight and remove curBits from dataBits.
offsetBits = 8;
dataBits -= curBits;
}//Loop until we run out of dataBits, then return the result
}

68
lib/bitfields.h Normal file
View file

@ -0,0 +1,68 @@
namespace Bit{
//bitfield getters
unsigned long long getMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits);
unsigned long long getByName(char * pointer);
//bitfield setters
void setMSB(char * pointer, unsigned int offsetBits, unsigned int dataBits, unsigned long long value);
void setByName(char * pointer);
//Host to binary/binary to host functions - similar to kernel ntoh/hton functions.
/// Retrieves a short in network order from the pointer p.
inline unsigned short btohs(char * p) {
return ((unsigned short)p[0] << 8) | p[1];
}
/// Stores a short value of val in network order to the pointer p.
inline void htobs(char * p, unsigned short val) {
p[0] = (val >> 8) & 0xFF;
p[1] = val & 0xFF;
}
/// Retrieves a long in network order from the pointer p.
inline unsigned long btohl(char * p) {
return ((unsigned long)p[0] << 24) | ((unsigned long)p[1] << 16) | ((unsigned long)p[2] << 8) | p[3];
}
/// Stores a long value of val in network order to the pointer p.
inline void htobl(char * p, unsigned long val) {
p[0] = (val >> 24) & 0xFF;
p[1] = (val >> 16) & 0xFF;
p[2] = (val >> 8) & 0xFF;
p[3] = val & 0xFF;
}
/// Retrieves a long in network order from the pointer p.
inline unsigned long btoh24(char * p) {
return ((unsigned long)p[0] << 16) | ((unsigned long)p[1] << 8) | p[2];
}
/// Stores a long value of val in network order to the pointer p.
inline void htob24(char * p, unsigned long val) {
p[0] = (val >> 16) & 0xFF;
p[1] = (val >> 8) & 0xFF;
p[2] = val & 0xFF;
}
/// Retrieves a long long in network order from the pointer p.
inline unsigned long long btohll(char * p) {
return ((unsigned long long)p[0] << 56) | ((unsigned long long)p[1] << 48) | ((unsigned long long)p[2] << 40) | ((unsigned long long)p[3] << 32) | ((unsigned long)p[4] << 24) | ((unsigned long)p[5] << 16) | ((unsigned long)p[6] << 8) | p[7];
}
/// Stores a long value of val in network order to the pointer p.
inline void htobll(char * p, unsigned long long val) {
p[0] = (val >> 56) & 0xFF;
p[1] = (val >> 48) & 0xFF;
p[2] = (val >> 40) & 0xFF;
p[3] = (val >> 32) & 0xFF;
p[4] = (val >> 24) & 0xFF;
p[5] = (val >> 16) & 0xFF;
p[6] = (val >> 8) & 0xFF;
p[7] = val & 0xFF;
}
}

215
lib/bitstream.cpp Normal file
View file

@ -0,0 +1,215 @@
#include "bitstream.h"
#include "defines.h"
#include <stdlib.h>
#include <string.h>
namespace Utils {
bitstream::bitstream() {
data = NULL;
offset = 0;
dataSize = 0;
bufferSize = 0;
}
bool bitstream::checkBufferSize(unsigned int size) {
if (size > bufferSize) {
void * temp = realloc(data, size);
if (temp) {
data = (char *) temp;
bufferSize = size;
return true;
} else {
return false;
}
} else {
return true;
}
}
void bitstream::append(char * input, size_t bytes) {
if (checkBufferSize(dataSize + bytes)) {
memcpy(data + dataSize, input, bytes);
dataSize += bytes;
}
}
void bitstream::append(std::string input) {
append((char *)input.c_str(), input.size());
}
bool bitstream::peekOffset(size_t peekOffset) {
peekOffset += offset;
return ((data[peekOffset >> 3]) >> (7 - (peekOffset & 7))) & 1;
}
long long unsigned int bitstream::peek(size_t count) {
if (count > 64) {
DEBUG_MSG(DLVL_WARN, "Can not read %d bits into a long long unsigned int!", (int)count);
//return 0;
}
if (count > size()) {
DEBUG_MSG(DLVL_ERROR, "Not enough bits left in stream. Left: %d requested: %d", (int)size(), (int)count);
return 0;
}
long long unsigned int retval = 0;
size_t curPlace = 0;
size_t readSize;
size_t readOff;
char readBuff;
while (curPlace < count) {
readBuff = data[(int)((offset + curPlace) / 8)];
readSize = 8;
readOff = (offset + curPlace) % 8; //the reading offset within the byte
if (readOff != 0) {
//if we start our read not on the start of a byte
//curplace and retval should both be 0
//this should be the first read that aligns reading to bytes, if we read over the end of read byte
//we cut the MSb off of the buffer by bit mask
readSize -= readOff;//defining starting bit
readBuff = readBuff & ((1 << readSize) - 1);//bitmasking
}
//up until here we assume we read to the end of the byte
if (count - curPlace < readSize) { //if we do not read to the end of the byte
//we cut off the LSb off of the read buffer by bitshift
readSize = count - curPlace;
readBuff = readBuff >> (8 - readSize - readOff);
}
retval = (retval << readSize) + readBuff;
curPlace += readSize;
}
return retval;
}
long long unsigned int bitstream::get(size_t count) {
if (count <= size()) {
long long unsigned int retVal;
retVal = peek(count);
skip(count);
return retVal;
} else {
return 0;
}
}
void bitstream::skip(size_t count) {
if (count <= size()) {
offset += count;
} else {
offset = dataSize * 8;
}
}
long long unsigned int bitstream::size() {
return (dataSize * 8) - offset;
}
void bitstream::clear() {
dataSize = 0;
offset = 0;
}
void bitstream::flush() {
memmove(data, data + (offset / 8), dataSize - (offset / 8));
dataSize -= offset / 8;
offset %= 8;
}
long long unsigned int bitstream::golombPeeker() {
for (size_t i = 0; i < 64 && i < size(); i++) {
if (peekOffset(i)) {
return peek((i * 2) + 1);
}
}
return 0;
}
long long unsigned int bitstream::golombGetter() {
for (size_t i = 0; i < 64 && i < size(); i++) {
if (peekOffset(i)) {
return get((i * 2) + 1);
}
}
return 0;
}
long long int bitstream::getExpGolomb() {
long long unsigned int temp = golombGetter();
return (temp >> 1) * (1 - ((temp & 1) << 1)); //Is actually return (temp / 2) * (1 - (temp & 1) * 2);
}
long long unsigned int bitstream::getUExpGolomb() {
return golombGetter() - 1;
}
long long int bitstream::peekExpGolomb() {
long long unsigned int temp = golombPeeker();
return (temp >> 1) * (1 - ((temp & 1) << 1)); //Is actually return (temp / 2) * (1 - (temp & 1) * 2);
}
long long unsigned int bitstream::peekUExpGolomb() {
return golombPeeker() - 1;
}
//Note: other bitstream here
bitstreamLSBF::bitstreamLSBF() {
readBufferOffset = 0;
readBuffer = 0;
}
void bitstreamLSBF::append(char * input, size_t bytes) {
data.append(input, bytes);
fixData();
}
void bitstreamLSBF::append(std::string & input) {
data += input;
fixData();
}
long long unsigned int bitstreamLSBF::size() {
return data.size() * 8 + readBufferOffset;
}
long long unsigned int bitstreamLSBF::get(size_t count) {
if (count <= 32 && count <= readBufferOffset) {
long long unsigned int retval = readBuffer & (((long long unsigned int)1 << count) - 1);
readBuffer = readBuffer >> count;
readBufferOffset -= count;
fixData();
return retval;
}
return 42;
}
void bitstreamLSBF::skip(size_t count) {
if (count <= 32 && count <= readBufferOffset) {
readBuffer = readBuffer >> count;
readBufferOffset -= count;
fixData();
}
}
long long unsigned int bitstreamLSBF::peek(size_t count) {
if (count <= 32 && count <= readBufferOffset) {
return readBuffer & ((1 << count) - 1);
}
return 0;
}
void bitstreamLSBF::clear() {
data = "";
readBufferOffset = 0;
readBuffer = 0;
}
void bitstreamLSBF::fixData() {
unsigned int pos=0;
while (readBufferOffset <= 32 && data.size() != 0) {
readBuffer |= (((long long unsigned int)data[pos]) << readBufferOffset);
pos++;
readBufferOffset += 8;
}
data.erase(0, pos);
}
}

60
lib/bitstream.h Normal file
View file

@ -0,0 +1,60 @@
#include<string>
namespace Utils {
class bitstream {
public:
bitstream();
bitstream & operator<< (std::string input) {
append(input);
return *this;
};
bitstream & operator<< (char input) {
append(std::string(input, 1));
return *this;
};
void append(char * input, size_t bytes);
void append(std::string input);
long long unsigned int size();
void skip(size_t count);
long long unsigned int get(size_t count);
long long unsigned int peek(size_t count);
bool peekOffset(size_t peekOffset);
void flush();
void clear();
long long int getExpGolomb();
long long unsigned int getUExpGolomb();
long long int peekExpGolomb();
long long unsigned int peekUExpGolomb();
private:
bool checkBufferSize(unsigned int size);
long long unsigned int golombGetter();
long long unsigned int golombPeeker();
char * data;
size_t offset;
size_t dataSize;
size_t bufferSize;
};
class bitstreamLSBF {
public:
bitstreamLSBF();
bitstreamLSBF & operator<< (std::string input) {
append(input);
return *this;
};
void append(char * input, size_t bytes);
void append(std::string & input);
long long unsigned int size();
void skip(size_t count);
long long unsigned int get(size_t count);
long long unsigned int peek(size_t count);
void clear();
std::string data;
private:
long long unsigned int readBuffer;
unsigned int readBufferOffset;
void fixData();
};
}

229
lib/checksum.h Normal file
View file

@ -0,0 +1,229 @@
namespace checksum {
inline unsigned int crc32c(unsigned int crc, const char * data, size_t len) {
static const unsigned int table[256] = {
0x00000000U, 0x04C11DB7U, 0x09823B6EU, 0x0D4326D9U,
0x130476DCU, 0x17C56B6BU, 0x1A864DB2U, 0x1E475005U,
0x2608EDB8U, 0x22C9F00FU, 0x2F8AD6D6U, 0x2B4BCB61U,
0x350C9B64U, 0x31CD86D3U, 0x3C8EA00AU, 0x384FBDBDU,
0x4C11DB70U, 0x48D0C6C7U, 0x4593E01EU, 0x4152FDA9U,
0x5F15ADACU, 0x5BD4B01BU, 0x569796C2U, 0x52568B75U,
0x6A1936C8U, 0x6ED82B7FU, 0x639B0DA6U, 0x675A1011U,
0x791D4014U, 0x7DDC5DA3U, 0x709F7B7AU, 0x745E66CDU,
0x9823B6E0U, 0x9CE2AB57U, 0x91A18D8EU, 0x95609039U,
0x8B27C03CU, 0x8FE6DD8BU, 0x82A5FB52U, 0x8664E6E5U,
0xBE2B5B58U, 0xBAEA46EFU, 0xB7A96036U, 0xB3687D81U,
0xAD2F2D84U, 0xA9EE3033U, 0xA4AD16EAU, 0xA06C0B5DU,
0xD4326D90U, 0xD0F37027U, 0xDDB056FEU, 0xD9714B49U,
0xC7361B4CU, 0xC3F706FBU, 0xCEB42022U, 0xCA753D95U,
0xF23A8028U, 0xF6FB9D9FU, 0xFBB8BB46U, 0xFF79A6F1U,
0xE13EF6F4U, 0xE5FFEB43U, 0xE8BCCD9AU, 0xEC7DD02DU,
0x34867077U, 0x30476DC0U, 0x3D044B19U, 0x39C556AEU,
0x278206ABU, 0x23431B1CU, 0x2E003DC5U, 0x2AC12072U,
0x128E9DCFU, 0x164F8078U, 0x1B0CA6A1U, 0x1FCDBB16U,
0x018AEB13U, 0x054BF6A4U, 0x0808D07DU, 0x0CC9CDCAU,
0x7897AB07U, 0x7C56B6B0U, 0x71159069U, 0x75D48DDEU,
0x6B93DDDBU, 0x6F52C06CU, 0x6211E6B5U, 0x66D0FB02U,
0x5E9F46BFU, 0x5A5E5B08U, 0x571D7DD1U, 0x53DC6066U,
0x4D9B3063U, 0x495A2DD4U, 0x44190B0DU, 0x40D816BAU,
0xACA5C697U, 0xA864DB20U, 0xA527FDF9U, 0xA1E6E04EU,
0xBFA1B04BU, 0xBB60ADFCU, 0xB6238B25U, 0xB2E29692U,
0x8AAD2B2FU, 0x8E6C3698U, 0x832F1041U, 0x87EE0DF6U,
0x99A95DF3U, 0x9D684044U, 0x902B669DU, 0x94EA7B2AU,
0xE0B41DE7U, 0xE4750050U, 0xE9362689U, 0xEDF73B3EU,
0xF3B06B3BU, 0xF771768CU, 0xFA325055U, 0xFEF34DE2U,
0xC6BCF05FU, 0xC27DEDE8U, 0xCF3ECB31U, 0xCBFFD686U,
0xD5B88683U, 0xD1799B34U, 0xDC3ABDEDU, 0xD8FBA05AU,
0x690CE0EEU, 0x6DCDFD59U, 0x608EDB80U, 0x644FC637U,
0x7A089632U, 0x7EC98B85U, 0x738AAD5CU, 0x774BB0EBU,
0x4F040D56U, 0x4BC510E1U, 0x46863638U, 0x42472B8FU,
0x5C007B8AU, 0x58C1663DU, 0x558240E4U, 0x51435D53U,
0x251D3B9EU, 0x21DC2629U, 0x2C9F00F0U, 0x285E1D47U,
0x36194D42U, 0x32D850F5U, 0x3F9B762CU, 0x3B5A6B9BU,
0x0315D626U, 0x07D4CB91U, 0x0A97ED48U, 0x0E56F0FFU,
0x1011A0FAU, 0x14D0BD4DU, 0x19939B94U, 0x1D528623U,
0xF12F560EU, 0xF5EE4BB9U, 0xF8AD6D60U, 0xFC6C70D7U,
0xE22B20D2U, 0xE6EA3D65U, 0xEBA91BBCU, 0xEF68060BU,
0xD727BBB6U, 0xD3E6A601U, 0xDEA580D8U, 0xDA649D6FU,
0xC423CD6AU, 0xC0E2D0DDU, 0xCDA1F604U, 0xC960EBB3U,
0xBD3E8D7EU, 0xB9FF90C9U, 0xB4BCB610U, 0xB07DABA7U,
0xAE3AFBA2U, 0xAAFBE615U, 0xA7B8C0CCU, 0xA379DD7BU,
0x9B3660C6U, 0x9FF77D71U, 0x92B45BA8U, 0x9675461FU,
0x8832161AU, 0x8CF30BADU, 0x81B02D74U, 0x857130C3U,
0x5D8A9099U, 0x594B8D2EU, 0x5408ABF7U, 0x50C9B640U,
0x4E8EE645U, 0x4A4FFBF2U, 0x470CDD2BU, 0x43CDC09CU,
0x7B827D21U, 0x7F436096U, 0x7200464FU, 0x76C15BF8U,
0x68860BFDU, 0x6C47164AU, 0x61043093U, 0x65C52D24U,
0x119B4BE9U, 0x155A565EU, 0x18197087U, 0x1CD86D30U,
0x029F3D35U, 0x065E2082U, 0x0B1D065BU, 0x0FDC1BECU,
0x3793A651U, 0x3352BBE6U, 0x3E119D3FU, 0x3AD08088U,
0x2497D08DU, 0x2056CD3AU, 0x2D15EBE3U, 0x29D4F654U,
0xC5A92679U, 0xC1683BCEU, 0xCC2B1D17U, 0xC8EA00A0U,
0xD6AD50A5U, 0xD26C4D12U, 0xDF2F6BCBU, 0xDBEE767CU,
0xE3A1CBC1U, 0xE760D676U, 0xEA23F0AFU, 0xEEE2ED18U,
0xF0A5BD1DU, 0xF464A0AAU, 0xF9278673U, 0xFDE69BC4U,
0x89B8FD09U, 0x8D79E0BEU, 0x803AC667U, 0x84FBDBD0U,
0x9ABC8BD5U, 0x9E7D9662U, 0x933EB0BBU, 0x97FFAD0CU,
0xAFB010B1U, 0xAB710D06U, 0xA6322BDFU, 0xA2F33668U,
0xBCB4666DU, 0xB8757BDAU, 0xB5365D03U, 0xB1F740B4U,
};
while (len > 0) {
crc = table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8);
data++;
len--;
}
return crc;
}
inline unsigned int crc32LE(unsigned int crc, const char * data, size_t len) {
static const unsigned int table[256] = {
0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU,
0x076dc419U, 0x706af48fU, 0xe963a535U, 0x9e6495a3U,
0x0edb8832U, 0x79dcb8a4U, 0xe0d5e91eU, 0x97d2d988U,
0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U, 0x90bf1d91U,
0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU,
0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U,
0x136c9856U, 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU,
0x14015c4fU, 0x63066cd9U, 0xfa0f3d63U, 0x8d080df5U,
0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U, 0xa2677172U,
0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU,
0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U,
0x32d86ce3U, 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U,
0x26d930acU, 0x51de003aU, 0xc8d75180U, 0xbfd06116U,
0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U, 0xb8bda50fU,
0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U,
0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU,
0x76dc4190U, 0x01db7106U, 0x98d220bcU, 0xefd5102aU,
0x71b18589U, 0x06b6b51fU, 0x9fbfe4a5U, 0xe8b8d433U,
0x7807c9a2U, 0x0f00f934U, 0x9609a88eU, 0xe10e9818U,
0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U,
0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU,
0x6c0695edU, 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U,
0x65b0d9c6U, 0x12b7e950U, 0x8bbeb8eaU, 0xfcb9887cU,
0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U, 0xfbd44c65U,
0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U,
0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU,
0x4369e96aU, 0x346ed9fcU, 0xad678846U, 0xda60b8d0U,
0x44042d73U, 0x33031de5U, 0xaa0a4c5fU, 0xdd0d7cc9U,
0x5005713cU, 0x270241aaU, 0xbe0b1010U, 0xc90c2086U,
0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU,
0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U,
0x59b33d17U, 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU,
0xedb88320U, 0x9abfb3b6U, 0x03b6e20cU, 0x74b1d29aU,
0xead54739U, 0x9dd277afU, 0x04db2615U, 0x73dc1683U,
0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U,
0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U,
0xf00f9344U, 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU,
0xf762575dU, 0x806567cbU, 0x196c3671U, 0x6e6b06e7U,
0xfed41b76U, 0x89d32be0U, 0x10da7a5aU, 0x67dd4accU,
0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U,
0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U,
0xd1bb67f1U, 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU,
0xd80d2bdaU, 0xaf0a1b4cU, 0x36034af6U, 0x41047a60U,
0xdf60efc3U, 0xa867df55U, 0x316e8eefU, 0x4669be79U,
0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U,
0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU,
0xc5ba3bbeU, 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U,
0xc2d7ffa7U, 0xb5d0cf31U, 0x2cd99e8bU, 0x5bdeae1dU,
0x9b64c2b0U, 0xec63f226U, 0x756aa39cU, 0x026d930aU,
0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U,
0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U,
0x92d28e9bU, 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U,
0x86d3d2d4U, 0xf1d4e242U, 0x68ddb3f8U, 0x1fda836eU,
0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U, 0x18b74777U,
0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU,
0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U,
0xa00ae278U, 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U,
0xa7672661U, 0xd06016f7U, 0x4969474dU, 0x3e6e77dbU,
0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U, 0x37d83bf0U,
0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U,
0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U,
0xbad03605U, 0xcdd70693U, 0x54de5729U, 0x23d967bfU,
0xb3667a2eU, 0xc4614ab8U, 0x5d681b02U, 0x2a6f2b94U,
0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, 0x2d02ef8dU
};
while (len > 0) {
crc = table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8);
data++;
len--;
}
return crc;
}
inline unsigned int crc32(unsigned int crc, const char * data, size_t len) {
static const unsigned int table[256] = {
0x00000000U, 0xB71DC104U, 0x6E3B8209U, 0xD926430DU,
0xDC760413U, 0x6B6BC517U, 0xB24D861AU, 0x0550471EU,
0xB8ED0826U, 0x0FF0C922U, 0xD6D68A2FU, 0x61CB4B2BU,
0x649B0C35U, 0xD386CD31U, 0x0AA08E3CU, 0xBDBD4F38U,
0x70DB114CU, 0xC7C6D048U, 0x1EE09345U, 0xA9FD5241U,
0xACAD155FU, 0x1BB0D45BU, 0xC2969756U, 0x758B5652U,
0xC836196AU, 0x7F2BD86EU, 0xA60D9B63U, 0x11105A67U,
0x14401D79U, 0xA35DDC7DU, 0x7A7B9F70U, 0xCD665E74U,
0xE0B62398U, 0x57ABE29CU, 0x8E8DA191U, 0x39906095U,
0x3CC0278BU, 0x8BDDE68FU, 0x52FBA582U, 0xE5E66486U,
0x585B2BBEU, 0xEF46EABAU, 0x3660A9B7U, 0x817D68B3U,
0x842D2FADU, 0x3330EEA9U, 0xEA16ADA4U, 0x5D0B6CA0U,
0x906D32D4U, 0x2770F3D0U, 0xFE56B0DDU, 0x494B71D9U,
0x4C1B36C7U, 0xFB06F7C3U, 0x2220B4CEU, 0x953D75CAU,
0x28803AF2U, 0x9F9DFBF6U, 0x46BBB8FBU, 0xF1A679FFU,
0xF4F63EE1U, 0x43EBFFE5U, 0x9ACDBCE8U, 0x2DD07DECU,
0x77708634U, 0xC06D4730U, 0x194B043DU, 0xAE56C539U,
0xAB068227U, 0x1C1B4323U, 0xC53D002EU, 0x7220C12AU,
0xCF9D8E12U, 0x78804F16U, 0xA1A60C1BU, 0x16BBCD1FU,
0x13EB8A01U, 0xA4F64B05U, 0x7DD00808U, 0xCACDC90CU,
0x07AB9778U, 0xB0B6567CU, 0x69901571U, 0xDE8DD475U,
0xDBDD936BU, 0x6CC0526FU, 0xB5E61162U, 0x02FBD066U,
0xBF469F5EU, 0x085B5E5AU, 0xD17D1D57U, 0x6660DC53U,
0x63309B4DU, 0xD42D5A49U, 0x0D0B1944U, 0xBA16D840U,
0x97C6A5ACU, 0x20DB64A8U, 0xF9FD27A5U, 0x4EE0E6A1U,
0x4BB0A1BFU, 0xFCAD60BBU, 0x258B23B6U, 0x9296E2B2U,
0x2F2BAD8AU, 0x98366C8EU, 0x41102F83U, 0xF60DEE87U,
0xF35DA999U, 0x4440689DU, 0x9D662B90U, 0x2A7BEA94U,
0xE71DB4E0U, 0x500075E4U, 0x892636E9U, 0x3E3BF7EDU,
0x3B6BB0F3U, 0x8C7671F7U, 0x555032FAU, 0xE24DF3FEU,
0x5FF0BCC6U, 0xE8ED7DC2U, 0x31CB3ECFU, 0x86D6FFCBU,
0x8386B8D5U, 0x349B79D1U, 0xEDBD3ADCU, 0x5AA0FBD8U,
0xEEE00C69U, 0x59FDCD6DU, 0x80DB8E60U, 0x37C64F64U,
0x3296087AU, 0x858BC97EU, 0x5CAD8A73U, 0xEBB04B77U,
0x560D044FU, 0xE110C54BU, 0x38368646U, 0x8F2B4742U,
0x8A7B005CU, 0x3D66C158U, 0xE4408255U, 0x535D4351U,
0x9E3B1D25U, 0x2926DC21U, 0xF0009F2CU, 0x471D5E28U,
0x424D1936U, 0xF550D832U, 0x2C769B3FU, 0x9B6B5A3BU,
0x26D61503U, 0x91CBD407U, 0x48ED970AU, 0xFFF0560EU,
0xFAA01110U, 0x4DBDD014U, 0x949B9319U, 0x2386521DU,
0x0E562FF1U, 0xB94BEEF5U, 0x606DADF8U, 0xD7706CFCU,
0xD2202BE2U, 0x653DEAE6U, 0xBC1BA9EBU, 0x0B0668EFU,
0xB6BB27D7U, 0x01A6E6D3U, 0xD880A5DEU, 0x6F9D64DAU,
0x6ACD23C4U, 0xDDD0E2C0U, 0x04F6A1CDU, 0xB3EB60C9U,
0x7E8D3EBDU, 0xC990FFB9U, 0x10B6BCB4U, 0xA7AB7DB0U,
0xA2FB3AAEU, 0x15E6FBAAU, 0xCCC0B8A7U, 0x7BDD79A3U,
0xC660369BU, 0x717DF79FU, 0xA85BB492U, 0x1F467596U,
0x1A163288U, 0xAD0BF38CU, 0x742DB081U, 0xC3307185U,
0x99908A5DU, 0x2E8D4B59U, 0xF7AB0854U, 0x40B6C950U,
0x45E68E4EU, 0xF2FB4F4AU, 0x2BDD0C47U, 0x9CC0CD43U,
0x217D827BU, 0x9660437FU, 0x4F460072U, 0xF85BC176U,
0xFD0B8668U, 0x4A16476CU, 0x93300461U, 0x242DC565U,
0xE94B9B11U, 0x5E565A15U, 0x87701918U, 0x306DD81CU,
0x353D9F02U, 0x82205E06U, 0x5B061D0BU, 0xEC1BDC0FU,
0x51A69337U, 0xE6BB5233U, 0x3F9D113EU, 0x8880D03AU,
0x8DD09724U, 0x3ACD5620U, 0xE3EB152DU, 0x54F6D429U,
0x7926A9C5U, 0xCE3B68C1U, 0x171D2BCCU, 0xA000EAC8U,
0xA550ADD6U, 0x124D6CD2U, 0xCB6B2FDFU, 0x7C76EEDBU,
0xC1CBA1E3U, 0x76D660E7U, 0xAFF023EAU, 0x18EDE2EEU,
0x1DBDA5F0U, 0xAAA064F4U, 0x738627F9U, 0xC49BE6FDU,
0x09FDB889U, 0xBEE0798DU, 0x67C63A80U, 0xD0DBFB84U,
0xD58BBC9AU, 0x62967D9EU, 0xBBB03E93U, 0x0CADFF97U,
0xB110B0AFU, 0x060D71ABU, 0xDF2B32A6U, 0x6836F3A2U,
0x6D66B4BCU, 0xDA7B75B8U, 0x035D36B5U, 0xB440F7B1U
};
const char * tmpData = data;
const char * end = tmpData + len;
while(tmpData < end){
crc = table[((unsigned char) crc) ^ *tmpData++] ^ (crc >> 8);
}
return crc;
}
}

643
lib/config.cpp Normal file
View file

@ -0,0 +1,643 @@
/// \file config.cpp
/// Contains generic functions for managing configuration.
#include "config.h"
#include "defines.h"
#include "timing.h"
#include "tinythread.h"
#include "stream.h"
#include <string.h>
#include <signal.h>
#ifdef __CYGWIN__
#include <windows.h>
#endif
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__)
#include <sys/wait.h>
#else
#include <wait.h>
#endif
#if defined(__APPLE__)
#include <mach-o/dyld.h>
#endif
#include <errno.h>
#include <iostream>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <getopt.h>
#include <stdlib.h>
#include <fstream>
#include <dirent.h> //for getMyExec
bool Util::Config::is_active = false;
unsigned int Util::Config::printDebugLevel = DEBUG;//
std::string Util::Config::libver = PACKAGE_VERSION;
Util::Config::Config() {
//global options here
vals["debug"]["long"] = "debug";
vals["debug"]["short"] = "g";
vals["debug"]["arg"] = "integer";
vals["debug"]["help"] = "The debug level at which messages need to be printed.";
vals["debug"]["value"].append((long long)DEBUG);
/*capabilities["optional"]["debug level"]["name"] = "debug";
capabilities["optional"]["debug level"]["help"] = "The debug level at which messages need to be printed.";
capabilities["optional"]["debug level"]["option"] = "--debug";
capabilities["optional"]["debug level"]["type"] = "integer";*/
}
/// Creates a new configuration manager.
Util::Config::Config(std::string cmd, std::string version) {
vals.null();
long_count = 2;
vals["cmd"]["value"].append(cmd);
vals["version"]["long"] = "version";
vals["version"]["short"] = "v";
vals["version"]["help"] = "Display library and application version, then exit.";
vals["help"]["long"] = "help";
vals["help"]["short"] = "h";
vals["help"]["help"] = "Display usage and version information, then exit.";
vals["version"]["value"].append((std::string)PACKAGE_VERSION);
vals["version"]["value"].append(version);
vals["debug"]["long"] = "debug";
vals["debug"]["short"] = "g";
vals["debug"]["arg"] = "integer";
vals["debug"]["help"] = "The debug level at which messages need to be printed.";
vals["debug"]["value"].append((long long)DEBUG);
}
/// Adds an option to the configuration parser.
/// The option needs an unique name (doubles will overwrite the previous) and can contain the following in the option itself:
///\code
/// {
/// "short":"o", //The short option letter
/// "long":"onName", //The long option
/// "short_off":"n", //The short option-off letter
/// "long_off":"offName", //The long option-off
/// "arg":"integer", //The type of argument, if required.
/// "value":[], //The default value(s) for this option if it is not given on the commandline.
/// "arg_num":1, //The count this value has on the commandline, after all the options have been processed.
/// "help":"Blahblahblah" //The helptext for this option.
/// }
///\endcode
void Util::Config::addOption(std::string optname, JSON::Value option) {
vals[optname] = option;
if (!vals[optname].isMember("value") && vals[optname].isMember("default")) {
vals[optname]["value"].append(vals[optname]["default"]);
vals[optname].removeMember("default");
}
long_count = 0;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++) {
if (it->second.isMember("long")) {
long_count++;
}
if (it->second.isMember("long_off")) {
long_count++;
}
}
}
/// Prints a usage message to the given output.
void Util::Config::printHelp(std::ostream & output) {
unsigned int longest = 0;
std::map<long long int, std::string> args;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++) {
unsigned int current = 0;
if (it->second.isMember("long")) {
current += it->second["long"].asString().size() + 4;
}
if (it->second.isMember("short")) {
current += it->second["short"].asString().size() + 3;
}
if (current > longest) {
longest = current;
}
current = 0;
if (it->second.isMember("long_off")) {
current += it->second["long_off"].asString().size() + 4;
}
if (it->second.isMember("short_off")) {
current += it->second["short_off"].asString().size() + 3;
}
if (current > longest) {
longest = current;
}
if (it->second.isMember("arg_num")) {
current = it->first.size() + 3;
if (current > longest) {
longest = current;
}
args[it->second["arg_num"].asInt()] = it->first;
}
}
output << "Usage: " << getString("cmd") << " [options]";
for (std::map<long long int, std::string>::iterator i = args.begin(); i != args.end(); i++) {
if (vals[i->second].isMember("value") && vals[i->second]["value"].size()) {
output << " [" << i->second << "]";
} else {
output << " " << i->second;
}
}
output << std::endl << std::endl;
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++) {
std::string f;
if (it->second.isMember("long") || it->second.isMember("short")) {
if (it->second.isMember("long") && it->second.isMember("short")) {
f = "--" + it->second["long"].asString() + ", -" + it->second["short"].asString();
} else {
if (it->second.isMember("long")) {
f = "--" + it->second["long"].asString();
}
if (it->second.isMember("short")) {
f = "-" + it->second["short"].asString();
}
}
while (f.size() < longest) {
f.append(" ");
}
if (it->second.isMember("arg")) {
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
} else {
output << f << it->second["help"].asString() << std::endl;
}
}
if (it->second.isMember("long_off") || it->second.isMember("short_off")) {
if (it->second.isMember("long_off") && it->second.isMember("short_off")) {
f = "--" + it->second["long_off"].asString() + ", -" + it->second["short_off"].asString();
} else {
if (it->second.isMember("long_off")) {
f = "--" + it->second["long_off"].asString();
}
if (it->second.isMember("short_off")) {
f = "-" + it->second["short_off"].asString();
}
}
while (f.size() < longest) {
f.append(" ");
}
if (it->second.isMember("arg")) {
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
} else {
output << f << it->second["help"].asString() << std::endl;
}
}
if (it->second.isMember("arg_num")) {
f = it->first;
while (f.size() < longest) {
f.append(" ");
}
output << f << "(" << it->second["arg"].asString() << ") " << it->second["help"].asString() << std::endl;
}
}
}
/// Parses commandline arguments.
/// Calls exit if an unknown option is encountered, printing a help message.
bool Util::Config::parseArgs(int & argc, char ** & argv) {
int opt = 0;
std::string shortopts;
struct option * longOpts = (struct option *)calloc(long_count + 1, sizeof(struct option));
int long_i = 0;
int arg_count = 0;
if (vals.size()) {
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++) {
if (it->second.isMember("short")) {
shortopts += it->second["short"].asString();
if (it->second.isMember("arg")) {
shortopts += ":";
}
}
if (it->second.isMember("short_off")) {
shortopts += it->second["short_off"].asString();
if (it->second.isMember("arg")) {
shortopts += ":";
}
}
if (it->second.isMember("long")) {
longOpts[long_i].name = it->second["long"].asString().c_str();
longOpts[long_i].val = it->second["short"].asString()[0];
if (it->second.isMember("arg")) {
longOpts[long_i].has_arg = 1;
}
long_i++;
}
if (it->second.isMember("long_off")) {
longOpts[long_i].name = it->second["long_off"].asString().c_str();
longOpts[long_i].val = it->second["short_off"].asString()[0];
if (it->second.isMember("arg")) {
longOpts[long_i].has_arg = 1;
}
long_i++;
}
if (it->second.isMember("arg_num") && !(it->second.isMember("value") && it->second["value"].size())) {
if (it->second["arg_num"].asInt() > arg_count) {
arg_count = it->second["arg_num"].asInt();
}
}
}
}
while ((opt = getopt_long(argc, argv, shortopts.c_str(), longOpts, 0)) != -1) {
switch (opt) {
case 'h':
case '?':
printHelp(std::cout);
case 'v':
std::cout << "Library version: " PACKAGE_VERSION << std::endl;
std::cout << "Application version: " << getString("version") << std::endl;
exit(1);
break;
default:
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++) {
if (it->second.isMember("short") && it->second["short"].asString()[0] == opt) {
if (it->second.isMember("arg")) {
it->second["value"].append((std::string)optarg);
} else {
it->second["value"].append((long long int)1);
}
break;
}
if (it->second.isMember("short_off") && it->second["short_off"].asString()[0] == opt) {
it->second["value"].append((long long int)0);
}
}
break;
}
} //commandline options parser
free(longOpts); //free the long options array
long_i = 1; //re-use long_i as an argument counter
while (optind < argc) { //parse all remaining options, ignoring anything unexpected.
for (JSON::ObjIter it = vals.ObjBegin(); it != vals.ObjEnd(); it++) {
if (it->second.isMember("arg_num") && it->second["arg_num"].asInt() == long_i) {
it->second["value"].append((std::string)argv[optind]);
break;
}
}
optind++;
long_i++;
}
if (long_i <= arg_count) {
return false;
}
printDebugLevel = getInteger("debug");
return true;
}
/// Returns a reference to the current value of an option or default if none was set.
/// If the option does not exist, this exits the application with a return code of 37.
JSON::Value & Util::Config::getOption(std::string optname, bool asArray) {
if (!vals.isMember(optname)) {
std::cout << "Fatal error: a non-existent option '" << optname << "' was accessed." << std::endl;
exit(37);
}
if (!vals[optname].isMember("value") || !vals[optname]["value"].isArray()) {
vals[optname]["value"].append(JSON::Value());
}
if (asArray) {
return vals[optname]["value"];
} else {
int n = vals[optname]["value"].size();
return vals[optname]["value"][n - 1];
}
}
/// Returns the current value of an option or default if none was set as a string.
/// Calls getOption internally.
std::string Util::Config::getString(std::string optname) {
return getOption(optname).asString();
}
/// Returns the current value of an option or default if none was set as a long long int.
/// Calls getOption internally.
long long int Util::Config::getInteger(std::string optname) {
return getOption(optname).asInt();
}
/// Returns the current value of an option or default if none was set as a bool.
/// Calls getOption internally.
bool Util::Config::getBool(std::string optname) {
return getOption(optname).asBool();
}
struct callbackData {
Socket::Connection * sock;
int (*cb)(Socket::Connection &);
};
static void callThreadCallback(void * cDataArg) {
DEBUG_MSG(DLVL_INSANE, "Thread for %p started", cDataArg);
callbackData * cData = (callbackData *)cDataArg;
cData->cb(*(cData->sock));
cData->sock->close();
delete cData->sock;
delete cData;
DEBUG_MSG(DLVL_INSANE, "Thread for %p ended", cDataArg);
}
int Util::Config::threadServer(Socket::Server & server_socket, int (*callback)(Socket::Connection &)) {
while (is_active && server_socket.connected()) {
Socket::Connection S = server_socket.accept();
if (S.connected()) { //check if the new connection is valid
callbackData * cData = new callbackData;
cData->sock = new Socket::Connection(S);
cData->cb = callback;
//spawn a new thread for this connection
tthread::thread T(callThreadCallback, (void *)cData);
//detach it, no need to keep track of it anymore
T.detach();
DEBUG_MSG(DLVL_HIGH, "Spawned new thread for socket %i", S.getSocket());
} else {
Util::sleep(10); //sleep 10ms
}
}
server_socket.close();
return 0;
}
int Util::Config::forkServer(Socket::Server & server_socket, int (*callback)(Socket::Connection &)) {
while (is_active && server_socket.connected()) {
Socket::Connection S = server_socket.accept();
if (S.connected()) { //check if the new connection is valid
pid_t myid = fork();
if (myid == 0) { //if new child, start MAINHANDLER
server_socket.drop();
return callback(S);
} else { //otherwise, do nothing or output debugging text
DEBUG_MSG(DLVL_HIGH, "Forked new process %i for socket %i", (int)myid, S.getSocket());
S.drop();
}
} else {
Util::sleep(10); //sleep 10ms
}
}
server_socket.close();
return 0;
}
int Util::Config::serveThreadedSocket(int (*callback)(Socket::Connection &)) {
Socket::Server server_socket;
if (vals.isMember("socket")) {
server_socket = Socket::Server(Util::getTmpFolder() + getString("socket"));
}
if (vals.isMember("listen_port") && vals.isMember("listen_interface")) {
server_socket = Socket::Server(getInteger("listen_port"), getString("listen_interface"), false);
}
if (!server_socket.connected()) {
DEBUG_MSG(DLVL_DEVEL, "Failure to open socket");
return 1;
}
DEBUG_MSG(DLVL_DEVEL, "Activating threaded server: %s", getString("cmd").c_str());
activate();
return threadServer(server_socket, callback);
}
int Util::Config::serveForkedSocket(int (*callback)(Socket::Connection & S)) {
Socket::Server server_socket;
if (vals.isMember("socket")) {
server_socket = Socket::Server(Util::getTmpFolder() + getString("socket"));
}
if (vals.isMember("listen_port") && vals.isMember("listen_interface")) {
server_socket = Socket::Server(getInteger("listen_port"), getString("listen_interface"), false);
}
if (!server_socket.connected()) {
DEBUG_MSG(DLVL_DEVEL, "Failure to open socket");
return 1;
}
DEBUG_MSG(DLVL_DEVEL, "Activating forked server: %s", getString("cmd").c_str());
activate();
return forkServer(server_socket, callback);
}
/// Activated the stored config. This will:
/// - Drop permissions to the stored "username", if any.
/// - Daemonize the process if "daemonize" exists and is true.
/// - Set is_active to true.
/// - Set up a signal handler to set is_active to false for the SIGINT, SIGHUP and SIGTERM signals.
void Util::Config::activate() {
if (vals.isMember("username")) {
setUser(getString("username"));
vals.removeMember("username");
}
if (vals.isMember("daemonize") && getBool("daemonize")) {
if (vals.isMember("logfile") && getString("logfile") != "") {
Daemonize(true);
} else {
Daemonize(false);
}
vals.removeMember("daemonize");
}
struct sigaction new_action;
struct sigaction cur_action;
new_action.sa_handler = signal_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
sigaction(SIGINT, &new_action, NULL);
sigaction(SIGHUP, &new_action, NULL);
sigaction(SIGTERM, &new_action, NULL);
sigaction(SIGPIPE, &new_action, NULL);
//check if a child signal handler isn't set already, if so, set it.
sigaction(SIGCHLD, 0, &cur_action);
if (cur_action.sa_handler == SIG_DFL || cur_action.sa_handler == SIG_IGN) {
sigaction(SIGCHLD, &new_action, NULL);
}
is_active = true;
}
/// Basic signal handler. Sets is_active to false if it receives
/// a SIGINT, SIGHUP or SIGTERM signal, reaps children for the SIGCHLD
/// signal, and ignores all other signals.
void Util::Config::signal_handler(int signum) {
switch (signum) {
case SIGINT: //these three signals will set is_active to false.
case SIGHUP:
case SIGTERM:
is_active = false;
break;
case SIGCHLD: { //when a child dies, reap it.
int status;
pid_t ret = -1;
while (ret != 0) {
ret = waitpid(-1, &status, WNOHANG);
if (ret < 0 && errno != EINTR) {
break;
}
}
break;
}
default: //other signals are ignored
break;
}
} //signal_handler
/// Adds the default connector options. Also updates the capabilities structure with the default options.
/// Besides the options addBasicConnectorOptions adds, this function also adds port and interface options.
void Util::Config::addConnectorOptions(int port, JSON::Value & capabilities) {
JSON::Value option;
option.null();
option["long"] = "port";
option["short"] = "p";
option["arg"] = "integer";
option["help"] = "TCP port to listen on";
option["value"].append((long long)port);
addOption("listen_port", option);
capabilities["optional"]["port"]["name"] = "TCP port";
capabilities["optional"]["port"]["help"] = "TCP port to listen on - default if unprovided is " + option["value"][0u].asString();
capabilities["optional"]["port"]["type"] = "uint";
capabilities["optional"]["port"]["option"] = "--port";
capabilities["optional"]["port"]["default"] = option["value"][0u];
option.null();
option["long"] = "interface";
option["short"] = "i";
option["arg"] = "string";
option["help"] = "Interface address to listen on, or 0.0.0.0 for all available interfaces.";
option["value"].append("0.0.0.0");
addOption("listen_interface", option);
capabilities["optional"]["interface"]["name"] = "Interface";
capabilities["optional"]["interface"]["help"] = "Address of the interface to listen on - default if unprovided is all interfaces";
capabilities["optional"]["interface"]["option"] = "--interface";
capabilities["optional"]["interface"]["type"] = "str";
addBasicConnectorOptions(capabilities);
} //addConnectorOptions
/// Adds the default connector options. Also updates the capabilities structure with the default options.
void Util::Config::addBasicConnectorOptions(JSON::Value & capabilities) {
JSON::Value option;
option.null();
option["long"] = "username";
option["short"] = "u";
option["arg"] = "string";
option["help"] = "Username to drop privileges to, or root to not drop provileges.";
option["value"].append("root");
addOption("username", option);
capabilities["optional"]["username"]["name"] = "Username";
capabilities["optional"]["username"]["help"] = "Username to drop privileges to - default if unprovided means do not drop privileges";
capabilities["optional"]["username"]["option"] = "--username";
capabilities["optional"]["username"]["type"] = "str";
if (capabilities.isMember("socket")) {
option.null();
option["arg"] = "string";
option["help"] = "Socket name that can be connected to for this connector.";
option["value"].append(capabilities["socket"]);
addOption("socket", option);
}
option.null();
option["long"] = "daemon";
option["short"] = "d";
option["long_off"] = "nodaemon";
option["short_off"] = "n";
option["help"] = "Whether or not to daemonize the process after starting.";
option["value"].append(0ll);
addOption("daemonize", option);
option.null();
option["long"] = "json";
option["short"] = "j";
option["help"] = "Output connector info in JSON format, then exit.";
option["value"].append(0ll);
addOption("json", option);
}
/// Gets directory the current executable is stored in.
std::string Util::getMyPath() {
char mypath[500];
#ifdef __CYGWIN__
GetModuleFileName(0, mypath, 500);
#else
#ifdef __APPLE__
memset(mypath, 0, 500);
unsigned int refSize = 500;
int ret = _NSGetExecutablePath(mypath, &refSize);
#else
int ret = readlink("/proc/self/exe", mypath, 500);
if (ret != -1) {
mypath[ret] = 0;
} else {
mypath[0] = 0;
}
#endif
#endif
std::string tPath = mypath;
size_t slash = tPath.rfind('/');
if (slash == std::string::npos) {
slash = tPath.rfind('\\');
if (slash == std::string::npos) {
return "";
}
}
tPath.resize(slash + 1);
return tPath;
}
/// Gets all executables in getMyPath that start with "Mist".
void Util::getMyExec(std::deque<std::string> & execs) {
std::string path = Util::getMyPath();
#ifdef __CYGWIN__
path += "\\Mist*";
WIN32_FIND_DATA FindFileData;
HANDLE hdl = FindFirstFile(path.c_str(), &FindFileData);
while (hdl != INVALID_HANDLE_VALUE) {
execs.push_back(FindFileData.cFileName);
if (!FindNextFile(hdl, &FindFileData)) {
FindClose(hdl);
hdl = INVALID_HANDLE_VALUE;
}
}
#else
DIR * d = opendir(path.c_str());
if (!d) {
return;
}
struct dirent * dp;
do {
errno = 0;
if ((dp = readdir(d))) {
if (strncmp(dp->d_name, "Mist", 4) == 0) {
execs.push_back(dp->d_name);
}
}
} while (dp != NULL);
closedir(d);
#endif
}
/// Sets the current process' running user
void Util::setUser(std::string username) {
if (username != "root") {
struct passwd * user_info = getpwnam(username.c_str());
if (!user_info) {
DEBUG_MSG(DLVL_ERROR, "Error: could not setuid %s: could not get PID", username.c_str());
return;
} else {
if (setuid(user_info->pw_uid) != 0) {
DEBUG_MSG(DLVL_ERROR, "Error: could not setuid %s: not allowed", username.c_str());
} else {
DEBUG_MSG(DLVL_DEVEL, "Change user to %s", username.c_str());
}
}
}
}
/// Will turn the current process into a daemon.
/// Works by calling daemon(1,0):
/// Does not change directory to root.
/// Does redirect output to /dev/null
void Util::Daemonize(bool notClose) {
DEBUG_MSG(DLVL_DEVEL, "Going into background mode...");
int noClose = 0;
if (notClose) {
noClose = 1;
}
if (daemon(1, noClose) < 0) {
DEBUG_MSG(DLVL_ERROR, "Failed to daemonize: %s", strerror(errno));
}
}

59
lib/config.h Normal file
View file

@ -0,0 +1,59 @@
/// \file config.h
/// Contains generic function headers for managing configuration.
#pragma once
#ifndef PACKAGE_VERSION
#define PACKAGE_VERSION "unknown"
#endif
#include <string>
#include "json.h"
/// Contains utility code, not directly related to streaming media
namespace Util {
/// Deals with parsing configuration from commandline options.
class Config {
private:
JSON::Value vals; ///< Holds all current config values
int long_count;
static void signal_handler(int signum);
public:
//variables
static std::string libver; ///< Version number of the library as a string.
static bool is_active; ///< Set to true by activate(), set to false by the signal handler.
static unsigned int printDebugLevel;
//functions
Config();
Config(std::string cmd, std::string version);
void addOption(std::string optname, JSON::Value option);
void printHelp(std::ostream & output);
bool parseArgs(int & argc, char ** & argv);
JSON::Value & getOption(std::string optname, bool asArray = false);
std::string getString(std::string optname);
long long int getInteger(std::string optname);
bool getBool(std::string optname);
void activate();
int threadServer(Socket::Server & server_socket, int (*callback)(Socket::Connection & S));
int forkServer(Socket::Server & server_socket, int (*callback)(Socket::Connection & S));
int serveThreadedSocket(int (*callback)(Socket::Connection & S));
int serveForkedSocket(int (*callback)(Socket::Connection & S));
int servePlainSocket(int (*callback)(Socket::Connection & S));
void addBasicConnectorOptions(JSON::Value & capabilities);
void addConnectorOptions(int port, JSON::Value & capabilities);
};
/// Gets directory the current executable is stored in.
std::string getMyPath();
/// Gets all executables in getMyPath that start with "Mist".
void getMyExec(std::deque<std::string> & execs);
/// Will set the active user to the named username.
void setUser(std::string user);
/// Will turn the current process into a daemon.
void Daemonize(bool notClose = false);
}

328
lib/converter.cpp Normal file
View file

@ -0,0 +1,328 @@
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sstream>
#include "timing.h"
#include "converter.h"
#include "procs.h"
#include "config.h"
namespace Converter {
///\brief The base constructor
Converter::Converter() {
fillFFMpegEncoders();
}
///\brief A function that fill the internal variables with values provided by examing ffmpeg output
///
///Checks for the following encoders:
/// - AAC
/// - H264
/// - MP3
void Converter::fillFFMpegEncoders() {
std::vector<char *> cmd;
cmd.reserve(3);
cmd.push_back((char *)"ffmpeg");
cmd.push_back((char *)"-encoders");
cmd.push_back(NULL);
int outFD = -1;
Util::Procs::StartPiped("FFMpegInfo", &cmd[0], 0, &outFD, 0);
while (Util::Procs::isActive("FFMpegInfo")) {
Util::sleep(100);
}
FILE * outFile = fdopen(outFD, "r");
char * fileBuf = 0;
size_t fileBufLen = 0;
while (!(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)) {
if (strstr(fileBuf, "aac") || strstr(fileBuf, "AAC")) {
strtok(fileBuf, " \t");
allCodecs["ffmpeg"][strtok(NULL, " \t")] = "aac";
}
if (strstr(fileBuf, "h264") || strstr(fileBuf, "H264")) {
strtok(fileBuf, " \t");
allCodecs["ffmpeg"][strtok(NULL, " \t")] = "h264";
}
if (strstr(fileBuf, "mp3") || strstr(fileBuf, "MP3")) {
strtok(fileBuf, " \t");
allCodecs["ffmpeg"][strtok(NULL, " \t")] = "mp3";
}
}
fclose(outFile);
}
///\brief A function to obtain all available codecs that have been obtained from the encoders.
///\return A reference to the allCodecs member.
converterInfo & Converter::getCodecs() {
return allCodecs;
}
///\brief A function to obtain the available encoders in JSON format.
///\return A JSON::Value containing all encoder:codec pairs.
JSON::Value Converter::getEncoders() {
JSON::Value result;
for (converterInfo::iterator convIt = allCodecs.begin(); convIt != allCodecs.end(); convIt++) {
for (codecInfo::iterator codIt = convIt->second.begin(); codIt != convIt->second.end(); codIt++) {
if (codIt->second == "h264") {
result[convIt->first]["video"][codIt->first] = codIt->second;
} else {
result[convIt->first]["audio"][codIt->first] = codIt->second;
}
}
}
return result;
}
///\brief Looks in a given path for all files that could be converted
///\param myPath The location to look at, this should be a folder.
///\return A JSON::Value containing all media files in the location, with their corresponding metadata values.
JSON::Value Converter::queryPath(std::string myPath) {
char const * cmd[3] = {0, 0, 0};
std::string mistPath = Util::getMyPath() + "MistInfo";
cmd[0] = mistPath.c_str();
JSON::Value result;
DIR * Dirp = opendir(myPath.c_str());
struct stat StatBuf;
if (Dirp) {
dirent * entry;
while ((entry = readdir(Dirp))) {
if (stat(std::string(myPath + "/" + entry->d_name).c_str(), &StatBuf) == -1) {
continue;
}
if ((StatBuf.st_mode & S_IFREG) == 0) {
continue;
}
std::string fileName = entry->d_name;
std::string mijnPad = std::string(myPath + (myPath[myPath.size() - 1] == '/' ? "" : "/") + entry->d_name);
cmd[1] = mijnPad.c_str();
result[fileName] = JSON::fromString(Util::Procs::getOutputOf((char * const *)cmd));
}
}
return result;
}
///\brief Start a conversion with the given parameters
///\param name The name to use for logging the conversion.
///\param parameters The parameters, accepted are the following:
/// - input The input url
/// - output The output url
/// - encoder The encoder to use
/// - video An object containing video parameters, if not existant no video will be output. Values are:
/// - width The width of the resulting video
/// - height The height of the resulting video
/// - codec The codec to encode video in, or copy to use the current codec
/// - fpks The framerate in fps * 1000
/// - audio An object containing audio parameters, if not existant no audio will be output. Values are:
/// - codec The codec to encode audio in, or copy to use the current codec
/// - samplerate The target samplerate for the audio, in hz
void Converter::startConversion(std::string name, JSON::Value parameters) {
if (!parameters.isMember("input")) {
statusHistory[name] = "No input file supplied";
return;
}
if (!parameters.isMember("output")) {
statusHistory[name] = "No output file supplied";
return;
}
struct stat statBuf;
std::string outPath = parameters["output"].asString();
outPath = outPath.substr(0, outPath.rfind('/'));
int statRes = stat(outPath.c_str(), & statBuf);
if (statRes == -1 || !S_ISDIR(statBuf.st_mode)) {
statusHistory[name] = "Output path is either non-existent, or not a path.";
return;
}
if (!parameters.isMember("encoder")) {
statusHistory[name] = "No encoder specified";
return;
}
if (allCodecs.find(parameters["encoder"]) == allCodecs.end()) {
statusHistory[name] = "Can not find encoder " + parameters["encoder"].asString();
return;
}
if (parameters.isMember("video")) {
if (parameters["video"].isMember("width") && !parameters["video"].isMember("height")) {
statusHistory[name] = "No height parameter given";
return;
}
if (parameters["video"].isMember("height") && !parameters["video"].isMember("width")) {
statusHistory[name] = "No width parameter given";
return;
}
}
std::stringstream encoderCommand;
if (parameters["encoder"] == "ffmpeg") {
encoderCommand << "ffmpeg -i ";
encoderCommand << parameters["input"].asString() << " ";
if (parameters.isMember("video")) {
if (!parameters["video"].isMember("codec") || parameters["video"]["codec"] == "copy") {
encoderCommand << "-vcodec copy ";
} else {
codecInfo::iterator vidCodec = allCodecs["ffmpeg"].find(parameters["video"]["codec"]);
if (vidCodec == allCodecs["ffmpeg"].end()) {
statusHistory[name] = "Can not find video codec " + parameters["video"]["codec"].asString();
return;
}
encoderCommand << "-vcodec " << vidCodec->first << " ";
if (parameters["video"]["codec"].asString() == "h264") {
//Enforce baseline
encoderCommand << "-preset slow -profile:v baseline -level 30 ";
}
if (parameters["video"].isMember("fpks")) {
encoderCommand << "-r " << parameters["video"]["fpks"].asInt() / 1000 << " ";
}
if (parameters["video"].isMember("width")) {
encoderCommand << "-s " << parameters["video"]["width"].asInt() << "x" << parameters["video"]["height"].asInt() << " ";
}
///\todo Keyframe interval (different in older and newer versions of ffmpeg?)
}
} else {
encoderCommand << "-vn ";
}
if (parameters.isMember("audio")) {
if (!parameters["audio"].isMember("codec")) {
encoderCommand << "-acodec copy ";
} else {
codecInfo::iterator audCodec = allCodecs["ffmpeg"].find(parameters["audio"]["codec"]);
if (audCodec == allCodecs["ffmpeg"].end()) {
statusHistory[name] = "Can not find audio codec " + parameters["audio"]["codec"].asString();
return;
}
if (audCodec->second == "aac") {
encoderCommand << "-strict -2 ";
}
encoderCommand << "-acodec " << audCodec->first << " ";
if (parameters["audio"].isMember("samplerate")) {
encoderCommand << "-ar " << parameters["audio"]["samplerate"].asInt() << " ";
}
}
} else {
encoderCommand << "-an ";
}
encoderCommand << "-f flv -";
}
int statusFD = -1;
Util::Procs::StartPiped2(name, encoderCommand.str(), Util::getMyPath() + "MistFLV2DTSC -o " + parameters["output"].asString(), 0, 0, &statusFD, 0);
parameters["statusFD"] = statusFD;
allConversions[name] = parameters;
allConversions[name]["status"]["duration"] = "?";
allConversions[name]["status"]["progress"] = 0;
allConversions[name]["status"]["frame"] = 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() {
if (allConversions.size()) {
std::map<std::string, JSON::Value>::iterator cIt;
bool hasChanged = true;
while (hasChanged && allConversions.size()) {
hasChanged = false;
for (cIt = allConversions.begin(); cIt != allConversions.end(); cIt++) {
if (Util::Procs::isActive(cIt->first)) {
int statusFD = dup(cIt->second["statusFD"].asInt());
fsync(statusFD);
FILE * statusFile = fdopen(statusFD, "r");
char * fileBuf = 0;
size_t fileBufLen = 0;
fseek(statusFile, 0, SEEK_END);
std::string line;
int totalTime = 0;
do {
getdelim(&fileBuf, &fileBufLen, '\r', statusFile);
line = fileBuf;
if (line.find("Duration") != std::string::npos) {
int curOffset = line.find("Duration: ") + 10;
totalTime += atoi(line.substr(curOffset, 2).c_str()) * 60 * 60 * 1000;
totalTime += atoi(line.substr(curOffset + 3, 2).c_str()) * 60 * 1000;
totalTime += atoi(line.substr(curOffset + 6, 2).c_str()) * 1000;
totalTime += atoi(line.substr(curOffset + 9, 2).c_str()) * 10;
cIt->second["duration"] = totalTime;
}
} while (!feof(statusFile) && line.find("frame") != 0); //"frame" is the fist word on an actual status line of ffmpeg
if (!feof(statusFile)) {
cIt->second["status"] = parseFFMpegStatus(line);
cIt->second["status"]["duration"] = cIt->second["duration"];
cIt->second["status"]["progress"] = (cIt->second["status"]["time"].asInt() * 100) / cIt->second["duration"].asInt();
} else {
line.erase(line.end() - 1);
line = line.substr(line.rfind("\n") + 1);
cIt->second["status"] = line;
}
free(fileBuf);
fclose(statusFile);
} else {
if (statusHistory.find(cIt->first) == statusHistory.end()) {
statusHistory[cIt->first] = "Conversion successful, running DTSCFix";
Util::Procs::Start(cIt->first + "DTSCFix", Util::getMyPath() + "MistDTSCFix " + cIt->second["output"].asString());
}
allConversions.erase(cIt);
hasChanged = true;
break;
}
}
}
}
if (statusHistory.size()) {
std::map<std::string, std::string>::iterator sIt;
for (sIt = statusHistory.begin(); sIt != statusHistory.end(); sIt++) {
if (statusHistory[sIt->first].find("DTSCFix") != std::string::npos) {
if (Util::Procs::isActive(sIt->first + "DTSCFIX")) {
continue;
}
statusHistory[sIt->first] = "Conversion successful";
}
}
}
}
///\brief Parses a single ffmpeg status line into a JSON format
///\param statusLine The current status of ffmpeg
///\return A JSON::Value with the following values set:
/// - frame The current last encoded frame
/// - time The current last encoded timestamp
JSON::Value Converter::parseFFMpegStatus(std::string statusLine) {
JSON::Value result;
int curOffset = statusLine.find("frame=") + 6;
result["frame"] = atoi(statusLine.substr(curOffset, statusLine.find("fps=") - curOffset).c_str());
curOffset = statusLine.find("time=") + 5;
int myTime = 0;
myTime += atoi(statusLine.substr(curOffset, 2).c_str()) * 60 * 60 * 1000;
myTime += atoi(statusLine.substr(curOffset + 3, 2).c_str()) * 60 * 1000;
myTime += atoi(statusLine.substr(curOffset + 6, 2).c_str()) * 1000;
myTime += atoi(statusLine.substr(curOffset + 9, 2).c_str()) * 10;
result["time"] = myTime;
return result;
}
///\brief Obtain the current internal status of the conversion class
///\return A JSON::Value with the status of each conversion
JSON::Value Converter::getStatus() {
updateStatus();
JSON::Value result;
if (allConversions.size()) {
for (std::map<std::string, JSON::Value>::iterator cIt = allConversions.begin(); cIt != allConversions.end(); cIt++) {
result[cIt->first] = cIt->second["status"];
result[cIt->first]["details"] = cIt->second;
}
}
if (statusHistory.size()) {
std::map<std::string, std::string>::iterator sIt;
for (sIt = statusHistory.begin(); sIt != statusHistory.end(); sIt++) {
result[sIt->first] = sIt->second;
}
}
return result;
}
///\brief Clears the status history of all conversions
void Converter::clearStatus() {
statusHistory.clear();
}
}

35
lib/converter.h Normal file
View file

@ -0,0 +1,35 @@
#include <map>
#include <string>
#include "json.h"
///\brief A typedef to simplify accessing all codecs
typedef std::map<std::string, std::string> codecInfo;
///\brief A typedef to simplify accessing all encoders
typedef std::map<std::string, codecInfo> converterInfo;
///\brief A namespace containing all functions for handling the conversion API
namespace Converter {
///\brief A class containing the basic conversion API functionality
class Converter {
public:
Converter();
converterInfo & getCodecs();
JSON::Value getEncoders();
JSON::Value queryPath(std::string myPath);
void startConversion(std::string name, JSON::Value parameters);
void updateStatus();
JSON::Value getStatus();
void clearStatus();
JSON::Value parseFFMpegStatus(std::string statusLine);
private:
void fillFFMpegEncoders();
///\brief Holds a list of all current known codecs
converterInfo allCodecs;
///\brief Holds a list of all the current conversions
std::map<std::string, JSON::Value> allConversions;
///\brief Stores the status of all conversions, and the history
std::map<std::string, std::string> statusHistory;
};
}

69
lib/defines.h Normal file
View file

@ -0,0 +1,69 @@
// Defines to print debug messages.
#ifndef MIST_DEBUG
#define MIST_DEBUG 1
#define DLVL_NONE 0 // All debugging disabled.
#define DLVL_FAIL 1 // Only messages about failed operations.
#define DLVL_ERROR 2 // Only messages about errors and failed operations.
#define DLVL_WARN 3 // Warnings, errors, and fail messages.
#define DLVL_DEVEL 4 // All of the above, plus status messages handy during development.
#define DLVL_INFO 4 // All of the above, plus status messages handy during development.
#define DLVL_MEDIUM 5 // Slightly more than just development-level messages.
#define DLVL_HIGH 6 // Verbose debugging messages.
#define DLVL_VERYHIGH 7 // Very verbose debugging messages.
#define DLVL_EXTREME 8 // Everything is reported in extreme detail.
#define DLVL_INSANE 9 // Everything is reported in insane detail.
#define DLVL_DONTEVEN 10 // All messages enabled, even pointless ones.
#if DEBUG > -1
#include <stdio.h>
#include <unistd.h>
#include "config.h"
static const char * DBG_LVL_LIST[] = {"NONE", "FAIL", "ERROR", "WARN", "INFO", "MEDIUM", "HIGH", "VERYHIGH", "EXTREME", "INSANE", "DONTEVEN"};
#if !defined(__APPLE__) && !defined(__MACH__) && defined(__GNUC__)
#include <errno.h>
#if DEBUG >= DLVL_DEVEL
#define DEBUG_MSG(lvl, msg, ...) if (Util::Config::printDebugLevel >= lvl){fprintf(stderr, "%s|%s|%d|%s:%d|" msg "\n", DBG_LVL_LIST[lvl], program_invocation_short_name, getpid(), __FILE__, __LINE__, ##__VA_ARGS__);}
#else
#define DEBUG_MSG(lvl, msg, ...) if (Util::Config::printDebugLevel >= lvl){fprintf(stderr, "%s|%s|%d||" msg "\n", DBG_LVL_LIST[lvl], program_invocation_short_name, getpid(), ##__VA_ARGS__);}
#endif
#else
#if DEBUG >= DLVL_DEVEL
#define DEBUG_MSG(lvl, msg, ...) if (Util::Config::printDebugLevel >= lvl){fprintf(stderr, "%s||%d|%s:%d|" msg "\n", DBG_LVL_LIST[lvl], getpid(), __FILE__, __LINE__, ##__VA_ARGS__);}
#else
#define DEBUG_MSG(lvl, msg, ...) if (Util::Config::printDebugLevel >= lvl){fprintf(stderr, "%s||%d||" msg "\n", DBG_LVL_LIST[lvl], getpid(), ##__VA_ARGS__);}
#endif
#endif
#else
#define DEBUG_MSG(lvl, msg, ...) // Debugging disabled.
#endif
#define FAIL_MSG(msg, ...) DEBUG_MSG(DLVL_FAIL, msg, ##__VA_ARGS__)
#define ERROR_MSG(msg, ...) DEBUG_MSG(DLVL_ERROR, msg, ##__VA_ARGS__)
#define WARN_MSG(msg, ...) DEBUG_MSG(DLVL_WARN, msg, ##__VA_ARGS__)
#define DEVEL_MSG(msg, ...) DEBUG_MSG(DLVL_DEVEL, msg, ##__VA_ARGS__)
#define INFO_MSG(msg, ...) DEBUG_MSG(DLVL_DEVEL, msg, ##__VA_ARGS__)
#define MEDIUM_MSG(msg, ...) DEBUG_MSG(DLVL_MEDIUM, msg, ##__VA_ARGS__)
#define HIGH_MSG(msg, ...) DEBUG_MSG(DLVL_HIGH, msg, ##__VA_ARGS__)
#define VERYHIGH_MSG(msg, ...) DEBUG_MSG(DLVL_VERYHIGH, msg, ##__VA_ARGS__)
#define EXTREME_MSG(msg, ...) DEBUG_MSG(DLVL_EXTREME, msg, ##__VA_ARGS__)
#define INSANE_MSG(msg, ...) DEBUG_MSG(DLVL_INSANE, msg, ##__VA_ARGS__)
#define DONTEVEN_MSG(msg, ...) DEBUG_MSG(DLVL_DONTEVEN, msg, ##__VA_ARGS__)
#endif
/// The size used for stream header pages under Windows, where they cannot be size-detected.
#define DEFAULT_META_PAGE_SIZE 16 * 1024 * 1024
/// The size used for stream data pages under Windows, where they cannot be size-detected.
#define DEFAULT_DATA_PAGE_SIZE 25 * 1024 * 1024
/// The size used for server configuration pages.
#define DEFAULT_CONF_PAGE_SIZE 4 * 1024 * 1024
/// The position from where on stream data pages are switched over to the next page.
#define FLIP_DATA_PAGE_SIZE 8 * 1024 * 1024

1093
lib/dtsc.cpp Normal file

File diff suppressed because it is too large Load diff

421
lib/dtsc.h Normal file
View file

@ -0,0 +1,421 @@
/// \file dtsc.h
/// Holds all headers for DDVTECH Stream Container parsing/generation.
#pragma once
#include <vector>
#include <iostream>
#include <stdint.h> //for uint64_t
#include <string>
#include <deque>
#include <set>
#include <stdio.h> //for FILE
#include "json.h"
#include "socket.h"
#include "timing.h"
#define DTSC_INT 0x01
#define DTSC_STR 0x02
#define DTSC_OBJ 0xE0
#define DTSC_ARR 0x0A
#define DTSC_CON 0xFF
namespace DTSC {
///\brief This enum holds all possible datatypes for DTSC packets.
enum datatype {
AUDIO, ///< Stream Audio data
VIDEO, ///< Stream Video data
META, ///< Stream Metadata
PAUSEMARK, ///< Pause marker
MODIFIEDHEADER, ///< Modified header data.
INVALID ///< Anything else or no data available.
};
extern char Magic_Header[]; ///< The magic bytes for a DTSC header
extern char Magic_Packet[]; ///< The magic bytes for a DTSC packet
extern char Magic_Packet2[]; ///< The magic bytes for a DTSC packet version 2
///\brief A simple structure used for ordering byte seek positions.
struct seekPos {
///\brief Less-than comparison for seekPos structures.
///\param rhs The seekPos to compare with.
///\return Whether this object is smaller than rhs.
bool operator < (const seekPos & rhs) const {
if (seekTime < rhs.seekTime) {
return true;
} else {
if (seekTime == rhs.seekTime) {
if (trackID < rhs.trackID) {
return true;
}
}
}
return false;
}
long long unsigned int seekTime;///< Stores the timestamp of the DTSC packet referenced by this structure.
long long unsigned int bytePos;///< Stores the byteposition of the DTSC packet referenced by this structure.
unsigned int trackID;///< Stores the track the DTSC packet referenced by this structure is associated with.
};
enum packType {
DTSC_INVALID,
DTSC_HEAD,
DTSC_V1,
DTSC_V2
};
/// This class allows scanning through raw binary format DTSC data.
/// It can be used as an iterator or as a direct accessor.
class Scan {
public:
Scan();
Scan(char * pointer, size_t len);
operator bool() const;
std::string toPrettyString(unsigned int indent = 0);
bool hasMember(std::string indice);
bool hasMember(const char * indice, const unsigned int ind_len);
Scan getMember(std::string indice);
Scan getMember(const char * indice);
Scan getMember(const char * indice, const unsigned int ind_len);
Scan getIndice(unsigned int num);
std::string getIndiceName(unsigned int num);
unsigned int getSize();
char getType();
bool asBool();
long long asInt();
std::string asString();
void getString(char *& result, unsigned int & len);
JSON::Value asJSON();
private:
char * p;
size_t len;
};
/// DTSC::Packets can currently be three types:
/// DTSC_HEAD packets are the "DTSC" header string, followed by 4 bytes len and packed content.
/// DTSC_V1 packets are "DTPD", followed by 4 bytes len and packed content.
/// DTSC_V2 packets are "DTP2", followed by 4 bytes len, 4 bytes trackID, 8 bytes time, and packed content.
/// The len is always without the first 8 bytes counted.
class Packet {
public:
Packet();
Packet(const Packet & rhs);
Packet(const char * data_, unsigned int len, bool noCopy = false);
~Packet();
void null();
void operator = (const Packet & rhs);
operator bool() const;
packType getVersion() const;
void reInit(const char * data_, unsigned int len, bool noCopy = false);
void genericFill(long long packTime, long long packOffset, long long packTrack, char * packData, long long packDataSize, long long packBytePos, bool isKeyframe);
void getString(const char * identifier, char *& result, unsigned int & len) const;
void getString(const char * identifier, std::string & result) const;
void getInt(const char * identifier, int & result) const;
int getInt(const char * identifier) const;
void getFlag(const char * identifier, bool & result) const;
bool getFlag(const char * identifier) const;
bool hasMember(const char * identifier) const;
long long unsigned int getTime() const;
long int getTrackId() const;
char * getData() const;
int getDataLen() const;
int getPayloadLen() const;
JSON::Value toJSON() const;
Scan getScan() const;
protected:
bool master;
packType version;
void resize(unsigned int size);
char * data;
unsigned int bufferLen;
unsigned int dataLen;
};
/// A simple structure used for ordering byte seek positions.
struct livePos {
livePos() {
seekTime = 0;
trackID = 0;
}
livePos(const livePos & rhs) {
seekTime = rhs.seekTime;
trackID = rhs.trackID;
}
void operator = (const livePos & rhs) {
seekTime = rhs.seekTime;
trackID = rhs.trackID;
}
bool operator == (const livePos & rhs) {
return seekTime == rhs.seekTime && trackID == rhs.trackID;
}
bool operator != (const livePos & rhs) {
return seekTime != rhs.seekTime || trackID != rhs.trackID;
}
bool operator < (const livePos & rhs) const {
if (seekTime < rhs.seekTime) {
return true;
} else {
if (seekTime > rhs.seekTime) {
return false;
}
}
return (trackID < rhs.trackID);
}
long long unsigned int seekTime;
unsigned int trackID;
};
/// A part from the DTSC::Stream ringbuffer.
/// Holds information about a buffer that will stay consistent
class Ring {
public:
Ring(livePos v);
livePos b;
//volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly!
volatile bool waiting; ///< If true, this Ring is currently waiting for a buffer fill.
volatile bool starved; ///< If true, this Ring can no longer receive valid data.
volatile bool updated; ///< If true, this Ring should write a new header.
volatile int playCount;
};
///\brief Basic class for storage of data associated with single DTSC packets, a.k.a. parts.
class Part {
public:
long getSize();
void setSize(long newSize);
short getDuration();
void setDuration(short newDuration);
long getOffset();
void setOffset(long newOffset);
char * getData();
void toPrettyString(std::ostream & str, int indent = 0);
private:
///\brief Data storage for this Part.
///
/// - 3 bytes: MSB storage of the payload size of this packet in bytes.
/// - 2 bytes: MSB storage of the duration of this packet in milliseconds.
/// - 4 bytes: MSB storage of the presentation time offset of this packet in milliseconds.
char data[9];
};
///\brief Basic class for storage of data associated with keyframes.
///
/// When deleting this object, make sure to remove all DTSC::Part associated with it, if any. If you fail doing this, it *will* cause data corruption.
class Key {
public:
unsigned long long getBpos();
void setBpos(unsigned long long newBpos);
unsigned long getLength();
void setLength(unsigned long newLength);
unsigned long getNumber();
void setNumber(unsigned long newNumber);
unsigned short getParts();
void setParts(unsigned short newParts);
unsigned long long getTime();
void setTime(unsigned long long newTime);
char * getData();
void toPrettyString(std::ostream & str, int indent = 0);
private:
///\brief Data storage for this Key.
///
/// - 5 bytes: MSB storage of the position of the first packet of this keyframe within the file.
/// - 3 bytes: MSB storage of the duration of this keyframe.
/// - 2 bytes: MSB storage of the number of this keyframe.
/// - 2 bytes: MSB storage of the amount of parts in this keyframe.
/// - 4 bytes: MSB storage of the timestamp associated with this keyframe's first packet.
char data[16];
};
///\brief Basic class for storage of data associated with fragments.
class Fragment {
public:
unsigned long getDuration();
void setDuration(unsigned long newDuration);
char getLength();
void setLength(char newLength);
unsigned long getNumber();
void setNumber(unsigned long newNumber);
unsigned long getSize();
void setSize(unsigned long newSize);
char * getData();
void toPrettyString(std::ostream & str, int indent = 0);
private:
///\Brief Data storage for this Fragment.
///
/// - 4 bytes: duration (in milliseconds)
/// - 1 byte: length (amount of keyframes)
/// - 2 bytes: number of first keyframe in fragment
/// - 4 bytes: size of fragment in bytes
char data[11];
};
///\brief Class for storage of track data
class Track {
public:
Track();
Track(JSON::Value & trackRef);
Track(Scan & trackRef);
inline operator bool() const {
return (parts.size() && keySizes.size() && (keySizes.size() == keys.size()));
}
void update(long long packTime, long long packOffset, long long packDataSize, long long packBytePos, bool isKeyframe, long long packSendSize, unsigned long segment_size = 5000);
int getSendLen();
void send(Socket::Connection & conn);
void writeTo(char *& p);
JSON::Value toJSON();
std::deque<Fragment> fragments;
std::deque<Key> keys;
std::deque<unsigned long> keySizes;
std::deque<Part> parts;
Key & getKey(unsigned int keyNum);
unsigned int timeToKeynum(unsigned int timestamp);
unsigned int timeToFragnum(unsigned int timestamp);
void reset();
void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0);
std::string getIdentifier();
std::string getWritableIdentifier();
unsigned int trackID;
unsigned long long firstms;
unsigned long long lastms;
int bps;
int missedFrags;
std::string init;
std::string codec;
std::string type;
//audio only
int rate;
int size;
int channels;
//video only
int width;
int height;
int fpks;
};
///\brief Class for storage of meta data
class Meta{
/// \todo Make toJSON().toNetpacked() shorter
public:
Meta();
Meta(const DTSC::Packet & source);
Meta(JSON::Value & meta);
inline operator bool() const { //returns if the object contains valid meta data BY LOOKING AT vod/live FLAGS
return vod || live;
}
void reinit(const DTSC::Packet & source);
void update(DTSC::Packet & pack, unsigned long segment_size = 5000);
void updatePosOverride(DTSC::Packet & pack, unsigned long bpos);
void update(JSON::Value & pack, unsigned long segment_size = 5000);
void update(long long packTime, long long packOffset, long long packTrack, long long packDataSize, long long packBytePos, bool isKeyframe, long long packSendSize = 0, unsigned long segment_size = 5000);
unsigned int getSendLen();
void send(Socket::Connection & conn);
void writeTo(char * p);
JSON::Value toJSON();
void reset();
void toPrettyString(std::ostream & str, int indent = 0, int verbosity = 0);
//members:
std::map<unsigned int, Track> tracks;
bool vod;
bool live;
bool merged;
long long int moreheader;
long long int bufferWindow;
};
/// A simple wrapper class that will open a file and allow easy reading/writing of DTSC data from/to it.
class File {
public:
File();
File(const File & rhs);
File(std::string filename, bool create = false);
File & operator = (const File & rhs);
operator bool() const;
~File();
Meta & getMeta();
long long int getLastReadPos();
bool writeHeader(std::string & header, bool force = false);
long long int addHeader(std::string & header);
long int getBytePosEOF();
long int getBytePos();
bool reachedEOF();
void seekNext();
void parseNext();
DTSC::Packet & getPacket();
bool seek_time(unsigned int ms);
bool seek_time(unsigned int ms, unsigned int trackNo, bool forceSeek = false);
bool seek_bpos(int bpos);
void rewritePacket(std::string & newPacket, int bytePos);
void writePacket(std::string & newPacket);
void writePacket(JSON::Value & newPacket);
bool atKeyframe();
void selectTracks(std::set<unsigned int> & tracks);
private:
long int endPos;
void readHeader(int pos);
DTSC::Packet myPack;
JSON::Value metaStorage;
Meta metadata;
std::map<unsigned int, std::string> trackMapping;
long long int currtime;
long long int lastreadpos;
int currframe;
FILE * F;
unsigned long headerSize;
void * buffer;
bool created;
std::set<seekPos> currentPositions;
std::set<unsigned int> selectedTracks;
};
//FileWriter
/// Holds temporary data for a DTSC stream and provides functions to utilize it.
/// Optionally also acts as a ring buffer of a certain requested size.
/// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe.
class Stream {
public:
Stream();
virtual ~Stream();
Stream(unsigned int buffers, unsigned int bufferTime = 0);
Meta metadata;
JSON::Value & getPacket();
JSON::Value & getPacket(livePos num);
datatype lastType();
std::string & lastData();
bool hasVideo();
bool hasAudio();
bool parsePacket(std::string & buffer);
bool parsePacket(Socket::Buffer & buffer);
std::string & outPacket();
std::string & outPacket(livePos num);
std::string & outHeader();
Ring * getRing();
unsigned int getTime();
void dropRing(Ring * ptr);
int canSeekms(unsigned int ms);
livePos msSeek(unsigned int ms, std::set<unsigned int> & allowedTracks);
void setBufferTime(unsigned int ms);
bool isNewest(DTSC::livePos & pos, std::set<unsigned int> & allowedTracks);
DTSC::livePos getNext(DTSC::livePos & pos, std::set<unsigned int> & allowedTracks);
void endStream();
void waitForMeta(Socket::Connection & sourceSocket, bool closeOnError = true);
void waitForPause(Socket::Connection & sourceSocket);
protected:
void cutOneBuffer();
void resetStream();
std::map<livePos, JSON::Value> buffers;
std::map<int, std::set<livePos> > keyframes;
virtual void addPacket(JSON::Value & newPack);
virtual void addMeta(JSON::Value & newMeta);
datatype datapointertype;
unsigned int buffercount;
unsigned int buffertime;
std::map<unsigned int, std::string> trackMapping;
virtual void deletionCallback(livePos deleting);
};
}

1722
lib/dtscmeta.cpp Normal file

File diff suppressed because it is too large Load diff

318
lib/filesystem.cpp Normal file
View file

@ -0,0 +1,318 @@
#include "filesystem.h"
#include "defines.h"
Filesystem::Directory::Directory(std::string PathName, std::string BasePath) {
MyBase = BasePath;
if (PathName[0] == '/') {
PathName.erase(0, 1);
}
if (BasePath[BasePath.size() - 1] != '/') {
BasePath += "/";
}
MyPath = PathName;
FillEntries();
}
Filesystem::Directory::~Directory() {
}
void Filesystem::Directory::FillEntries() {
ValidDir = true;
struct stat StatBuf;
Entries.clear();
DIR * Dirp = opendir((MyBase + MyPath).c_str());
if (!Dirp) {
ValidDir = false;
} else {
dirent * entry;
while ((entry = readdir(Dirp))) {
if (stat((MyBase + MyPath + "/" + entry->d_name).c_str(), &StatBuf) == -1) {
DEBUG_MSG(DLVL_DEVEL, "Skipping %s, reason %s", entry->d_name, strerror(errno));
continue;
}
///Convert stat to string
Entries[std::string(entry->d_name)] = StatBuf;
}
}
}
void Filesystem::Directory::Print() {
/// \todo Remove? Libraries shouldn't print stuff.
if (!ValidDir) {
DEBUG_MSG(DLVL_ERROR, "%s is not a valid directory", (MyBase + MyPath).c_str());
return;
}
printf("%s:\n", (MyBase + MyPath).c_str());
for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++) {
printf("\t%s\n", (*it).first.c_str());
}
printf("\n");
}
bool Filesystem::Directory::IsDir() {
return ValidDir;
}
std::string Filesystem::Directory::PWD() {
return "/" + MyPath;
}
std::string Filesystem::Directory::LIST(std::vector<std::string> ActiveStreams) {
FillEntries();
int MyPermissions;
std::stringstream Converter;
passwd * pwd; //For Username
group * grp; //For Groupname
tm * tm; //For time localisation
char datestring[256]; //For time localisation
std::string MyLoc = MyBase + MyPath;
if (MyLoc[MyLoc.size() - 1] != '/') {
MyLoc += "/";
}
for (std::map<std::string, struct stat>::iterator it = Entries.begin(); it != Entries.end(); it++) {
bool Active = (std::find(ActiveStreams.begin(), ActiveStreams.end(), (*it).first) != ActiveStreams.end());
if ((Active && (MyVisible[MyPath] & S_ACTIVE)) || ((!Active) && (MyVisible[MyPath] & S_INACTIVE)) || (((*it).second.st_mode / 010000) == 4)) {
if (((*it).second.st_mode / 010000) == 4) {
Converter << 'd';
} else {
Converter << '-';
}
MyPermissions = (((*it).second.st_mode % 010000) / 0100);
if (MyPermissions & 4) {
Converter << 'r';
} else {
Converter << '-';
}
if (MyPermissions & 2) {
Converter << 'w';
} else {
Converter << '-';
}
if (MyPermissions & 1) {
Converter << 'x';
} else {
Converter << '-';
}
MyPermissions = (((*it).second.st_mode % 0100) / 010);
if (MyPermissions & 4) {
Converter << 'r';
} else {
Converter << '-';
}
if (MyPermissions & 2) {
Converter << 'w';
} else {
Converter << '-';
}
if (MyPermissions & 1) {
Converter << 'x';
} else {
Converter << '-';
}
MyPermissions = ((*it).second.st_mode % 010);
if (MyPermissions & 4) {
Converter << 'r';
} else {
Converter << '-';
}
if (MyPermissions & 2) {
Converter << 'w';
} else {
Converter << '-';
}
if (MyPermissions & 1) {
Converter << 'x';
} else {
Converter << '-';
}
Converter << ' ';
Converter << (*it).second.st_nlink;
Converter << ' ';
if ((pwd = getpwuid((*it).second.st_uid))) {
Converter << pwd->pw_name;
} else {
Converter << (*it).second.st_uid;
}
Converter << ' ';
if ((grp = getgrgid((*it).second.st_gid))) {
Converter << grp->gr_name;
} else {
Converter << (*it).second.st_gid;
}
Converter << ' ';
Converter << (*it).second.st_size;
Converter << ' ';
tm = localtime(&((*it).second.st_mtime));
strftime(datestring, sizeof(datestring), "%b %d %H:%M", tm);
Converter << datestring;
Converter << ' ';
Converter << (*it).first;
Converter << '\n';
}
}
return Converter.str();
}
bool Filesystem::Directory::CWD(std::string Path) {
if (Path[0] == '/') {
Path.erase(0, 1);
MyPath = Path;
} else {
if (MyPath != "") {
MyPath += "/";
}
MyPath += Path;
}
FillEntries();
printf("New Path: %s\n", MyPath.c_str());
if (MyPermissions.find(MyPath) != MyPermissions.end()) {
printf("\tPermissions: %d\n", MyPermissions[MyPath]);
}
return SimplifyPath();
}
bool Filesystem::Directory::CDUP() {
return CWD("..");
}
std::string Filesystem::Directory::RETR(std::string Path) {
std::string Result;
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
std::ifstream File;
File.open(FileName.c_str());
while (File.good()) {
Result += File.get();
}
File.close();
return Result;
}
void Filesystem::Directory::STOR(std::string Path, std::string Data) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_STOR)) {
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
std::ofstream File;
File.open(FileName.c_str());
File << Data;
File.close();
}
}
bool Filesystem::Directory::SimplifyPath() {
MyPath += "/";
std::vector<std::string> TempPath;
std::string TempString;
for (std::string::iterator it = MyPath.begin(); it != MyPath.end(); it++) {
if ((*it) == '/') {
if (TempString == "..") {
if (!TempPath.size()) {
return false;
}
TempPath.erase((TempPath.end() - 1));
} else if (TempString != "." && TempString != "") {
TempPath.push_back(TempString);
}
TempString = "";
} else {
TempString += (*it);
}
}
MyPath = "";
for (std::vector<std::string>::iterator it = TempPath.begin(); it != TempPath.end(); it++) {
MyPath += (*it);
if (it != (TempPath.end() - 1)) {
MyPath += "/";
}
}
if (MyVisible.find(MyPath) == MyVisible.end()) {
MyVisible[MyPath] = S_ALL;
}
return true;
}
bool Filesystem::Directory::DELE(std::string Path) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_DELE)) {
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
if (std::remove(FileName.c_str())) {
DEBUG_MSG(DLVL_ERROR, "Removing file %s failed", FileName.c_str());
return false;
}
return true;
}
return false;
}
bool Filesystem::Directory::MKD(std::string Path) {
std::string FileName;
if (Path[0] == '/') {
Path.erase(0, 1);
FileName = MyBase + Path;
} else {
FileName = MyBase + MyPath + "/" + Path;
}
if (mkdir(FileName.c_str(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) {
DEBUG_MSG(DLVL_ERROR, "Creating directory %s failed", FileName.c_str());
return false;
}
MyVisible[FileName] = S_ALL;
return true;
}
bool Filesystem::Directory::Rename(std::string From, std::string To) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & P_RNFT)) {
std::string FileFrom;
if (From[0] == '/') {
From.erase(0, 1);
FileFrom = MyBase + From;
} else {
FileFrom = MyBase + MyPath + "/" + From;
}
std::string FileTo;
if (To[0] == '/') {
FileTo = MyBase + To;
} else {
FileTo = MyBase + MyPath + "/" + To;
}
if (std::rename(FileFrom.c_str(), FileTo.c_str())) {
DEBUG_MSG(DLVL_ERROR, "Renaming %s to %s failed", FileFrom.c_str(), FileTo.c_str());
return false;
}
return true;
}
return false;
}
void Filesystem::Directory::SetPermissions(std::string Path, char Permissions) {
MyPermissions[Path] = Permissions;
}
bool Filesystem::Directory::HasPermission(char Permission) {
if (MyPermissions.find(MyPath) == MyPermissions.end() || (MyPermissions[MyPath] & Permission)) {
return true;
}
return false;
}
void Filesystem::Directory::SetVisibility(std::string Pathname, char Visible) {
MyVisible[Pathname] = Visible;
}

67
lib/filesystem.h Normal file
View file

@ -0,0 +1,67 @@
#pragma once
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <map>
#include <sstream>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <locale.h>
#include <langinfo.h>
#include <stdint.h>
#include <errno.h>
namespace Filesystem {
enum DIR_Permissions {
P_LIST = 0x01, //List
P_RETR = 0x02, //Retrieve
P_STOR = 0x04, //Store
P_RNFT = 0x08, //Rename From/To
P_DELE = 0x10, //Delete
P_MKD = 0x20, //Make directory
P_RMD = 0x40, //Remove directory
};
enum DIR_Show {
S_NONE = 0x00, S_ACTIVE = 0x01, S_INACTIVE = 0x02, S_ALL = 0x03,
};
class Directory {
public:
Directory(std::string PathName = "", std::string BasePath = ".");
~Directory();
void Print();
bool IsDir();
std::string PWD();
std::string LIST(std::vector<std::string> ActiveStreams = std::vector<std::string>());
bool CWD(std::string Path);
bool CDUP();
bool DELE(std::string Path);
bool MKD(std::string Path);
std::string RETR(std::string Path);
void STOR(std::string Path, std::string Data);
bool Rename(std::string From, std::string To);
void SetPermissions(std::string PathName, char Permissions);
bool HasPermission(char Permission);
void SetVisibility(std::string Pathname, char Visible);
private:
bool ValidDir;
bool SimplifyPath();
void FillEntries();
std::string MyBase;
std::string MyPath;
std::map<std::string, struct stat> Entries;
std::map<std::string, char> MyPermissions;
std::map<std::string, char> MyVisible;
};
//Directory Class
}//Filesystem namespace

1264
lib/flv_tag.cpp Normal file

File diff suppressed because it is too large Load diff

74
lib/flv_tag.h Normal file
View file

@ -0,0 +1,74 @@
/// \file flv_tag.h
/// Holds all headers for the FLV namespace.
#pragma once
#include "socket.h"
#include "dtsc.h"
#include "json.h"
#include <string>
//forward declaration of RTMPStream::Chunk to avoid circular dependencies.
namespace RTMPStream {
class Chunk;
}
/// This namespace holds all FLV-parsing related functionality.
namespace FLV {
//variables
extern char Header[13]; ///< Holds the last FLV header parsed.
extern bool Parse_Error; ///< This variable is set to true if a problem is encountered while parsing the FLV.
extern std::string Error_Str; ///< This variable is set if a problem is encountered while parsing the FLV.
//functions
bool check_header(char * header); ///< Checks a FLV Header for validness.
bool is_header(char * header); ///< Checks the first 3 bytes for the string "FLV".
/// This class is used to hold, work with and get information about a single FLV tag.
class Tag {
public:
int len; ///< Actual length of tag.
bool isKeyframe; ///< True if current tag is a video keyframe.
char * data; ///< Pointer to tag buffer.
bool needsInitData(); ///< True if this media type requires init data.
bool isInitData(); ///< True if current tag is init data for this media type.
const char * getAudioCodec(); ///< Returns a c-string with the audio codec name.
const char * getVideoCodec(); ///< Returns a c-string with the video codec name.
std::string tagType(); ///< Returns a std::string describing the tag in detail.
unsigned int tagTime();
void tagTime(unsigned int T);
int offset();
void offset(int o);
Tag(); ///< Constructor for a new, empty, tag.
Tag(const Tag & O); ///< Copy constructor, copies the contents of an existing tag.
Tag & operator=(const Tag & O); ///< Assignment operator - works exactly like the copy constructor.
Tag(const RTMPStream::Chunk & O); ///<Copy constructor from a RTMP chunk.
~Tag(); ///< Generic destructor.
//loader functions
bool ChunkLoader(const RTMPStream::Chunk & O);
bool DTSCLoader(DTSC::Stream & S);
bool DTSCLoader(DTSC::Packet & packData, DTSC::Track & track);
bool DTSCVideoInit(DTSC::Track & video);
bool DTSCAudioInit(DTSC::Track & audio);
bool DTSCMetaInit(DTSC::Meta & M, std::set<long unsigned int> & selTracks);
bool DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef);
JSON::Value toJSON(DTSC::Meta & metadata);
bool MemLoader(char * D, unsigned int S, unsigned int & P);
bool FileLoader(FILE * f);
protected:
int buf; ///< Maximum length of buffer space.
bool done; ///< Body reading done?
unsigned int sofar; ///< How many bytes are read sofar?
void setLen();
bool checkBufferSize();
//loader helper functions
bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P);
bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f);
//JSON writer helpers
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, std::string val);
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, uint64_t val);
bool Meta_Has(JSON::Value & meta, std::string cat, std::string elem);
};
//Tag
}//FLV namespace

557
lib/ftp.cpp Normal file
View file

@ -0,0 +1,557 @@
#include "ftp.h"
FTP::User::User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials) {
Conn = NewConnection;
MyPassivePort = 0;
USER = "";
PASS = "";
MODE = MODE_STREAM;
STRU = STRU_FILE;
TYPE = TYPE_ASCII_NONPRINT;
PORT = 20;
RNFR = "";
AllCredentials = Credentials;
MyDir = Filesystem::Directory("", FTPBasePath);
MyDir.SetPermissions("", Filesystem::P_LIST);
MyDir.SetPermissions("Unconverted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_STOR | Filesystem::P_RETR);
MyDir.SetPermissions("Converted", Filesystem::P_LIST | Filesystem::P_DELE | Filesystem::P_RNFT | Filesystem::P_RETR);
MyDir.SetPermissions("OnDemand", Filesystem::P_LIST | Filesystem::P_RETR);
MyDir.SetPermissions("Live", Filesystem::P_LIST);
MyDir.SetVisibility("Converted", Filesystem::S_INACTIVE);
MyDir.SetVisibility("OnDemand", Filesystem::S_ACTIVE);
}
FTP::User::~User() {
}
int FTP::User::ParseCommand(std::string Command) {
Commands ThisCmd = CMD_NOCMD;
if (Command.substr(0, 4) == "NOOP") {
ThisCmd = CMD_NOOP;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "USER") {
ThisCmd = CMD_USER;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "PASS") {
ThisCmd = CMD_PASS;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "QUIT") {
ThisCmd = CMD_QUIT;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "PORT") {
ThisCmd = CMD_PORT;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RETR") {
ThisCmd = CMD_RETR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "STOR") {
ThisCmd = CMD_STOR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "TYPE") {
ThisCmd = CMD_TYPE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "MODE") {
ThisCmd = CMD_MODE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "STRU") {
ThisCmd = CMD_STRU;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "EPSV") {
ThisCmd = CMD_EPSV;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "PASV") {
ThisCmd = CMD_PASV;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "LIST") {
ThisCmd = CMD_LIST;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "CDUP") {
ThisCmd = CMD_CDUP;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "DELE") {
ThisCmd = CMD_DELE;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RNFR") {
ThisCmd = CMD_RNFR;
Command.erase(0, 5);
}
if (Command.substr(0, 4) == "RNTO") {
ThisCmd = CMD_RNTO;
Command.erase(0, 5);
}
if (Command.substr(0, 3) == "PWD") {
ThisCmd = CMD_PWD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "CWD") {
ThisCmd = CMD_CWD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "RMD") {
ThisCmd = CMD_RMD;
Command.erase(0, 4);
}
if (Command.substr(0, 3) == "MKD") {
ThisCmd = CMD_MKD;
Command.erase(0, 4);
}
if (ThisCmd != CMD_RNTO) {
RNFR = "";
}
switch (ThisCmd) {
case CMD_NOOP: {
return 200; //Command okay.
break;
}
case CMD_USER: {
USER = "";
PASS = "";
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
USER = Command;
return 331; //User name okay, need password.
break;
}
case CMD_PASS: {
if (USER == "") {
return 503;
} //Bad sequence of commands
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
PASS = Command;
if (!LoggedIn()) {
USER = "";
PASS = "";
return 530; //Not logged in.
}
return 230;
break;
}
case CMD_LIST: {
Socket::Connection Connected = Passive.accept();
if (Connected.connected()) {
Conn.SendNow("125 Data connection already open; transfer starting.\n");
} else {
Conn.SendNow("150 File status okay; about to open data connection.\n");
}
while (!Connected.connected()) {
Connected = Passive.accept();
}
std::string tmpstr = MyDir.LIST(ActiveStreams);
Connected.SendNow(tmpstr);
Connected.close();
return 226;
break;
}
case CMD_QUIT: {
return 221; //Service closing control connection. Logged out if appropriate.
break;
}
case CMD_PORT: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
PORT = atoi(Command.c_str());
return 200; //Command okay.
break;
}
case CMD_EPSV: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999);
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
return 229;
break;
}
case CMD_PASV: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
MyPassivePort = (rand() % 9999) + 49152;
Passive = Socket::Server(MyPassivePort, "0.0.0.0", true);
return 227;
break;
}
case CMD_RETR: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_RETR)) {
return 550;
} //Access denied.
Socket::Connection Connected = Passive.accept();
if (Connected.connected()) {
Conn.SendNow("125 Data connection already open; transfer starting.\n");
} else {
Conn.SendNow("150 File status okay; about to open data connection.\n");
}
while (!Connected.connected()) {
Connected = Passive.accept();
}
std::string tmpstr = MyDir.RETR(Command);
Connected.SendNow(tmpstr);
Connected.close();
return 226;
break;
}
case CMD_STOR: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_STOR)) {
return 550;
} //Access denied.
Socket::Connection Connected = Passive.accept();
if (Connected.connected()) {
Conn.SendNow("125 Data connection already open; transfer starting.\n");
} else {
Conn.SendNow("150 File status okay; about to open data connection.\n");
}
while (!Connected.connected()) {
Connected = Passive.accept();
}
std::string Buffer;
while (Connected.spool()) {
}
/// \todo Comment me back in. ^_^
//Buffer = Connected.Received();
MyDir.STOR(Command, Buffer);
return 250;
break;
}
case CMD_TYPE: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1 && Command.size() != 3) {
return 501;
} //Syntax error in parameters or arguments.
switch (Command[0]) {
case 'A': {
if (Command.size() > 1) {
if (Command[1] != ' ') {
return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N') {
return 504;
} //Command not implemented for that parameter.
}
TYPE = TYPE_ASCII_NONPRINT;
break;
}
case 'I': {
if (Command.size() > 1) {
if (Command[1] != ' ') {
return 501;
} //Syntax error in parameters or arguments.
if (Command[2] != 'N') {
return 504;
} //Command not implemented for that parameter.
}
TYPE = TYPE_IMAGE_NONPRINT;
break;
}
default: {
return 504; //Command not implemented for that parameter.
break;
}
}
return 200; //Command okay.
break;
}
case CMD_MODE: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1) {
return 501;
} //Syntax error in parameters or arguments.
if (Command[0] != 'S') {
return 504;
} //Command not implemented for that parameter.
MODE = MODE_STREAM;
return 200; //Command okay.
break;
}
case CMD_STRU: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (Command.size() != 1) {
return 501;
} //Syntax error in parameters or arguments.
switch (Command[0]) {
case 'F': {
STRU = STRU_FILE;
break;
}
case 'R': {
STRU = STRU_RECORD;
break;
}
default: {
return 504; //Command not implemented for that parameter.
break;
}
}
return 200; //Command okay.
break;
}
case CMD_PWD: {
if (!LoggedIn()) {
return 550;
} //Not logged in.
if (Command != "") {
return 501;
} //Syntax error in parameters or arguments.
return 2570; //257 -- 0 to indicate PWD over MKD
break;
}
case CMD_CWD: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
Filesystem::Directory TmpDir = MyDir;
if (TmpDir.CWD(Command)) {
if (TmpDir.IsDir()) {
MyDir = TmpDir;
return 250;
}
}
return 550;
break;
}
case CMD_CDUP: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command != "") {
return 501;
} //Syntax error in parameters or arguments.
Filesystem::Directory TmpDir = MyDir;
if (TmpDir.CDUP()) {
if (TmpDir.IsDir()) {
MyDir = TmpDir;
return 250;
}
}
return 550;
break;
}
case CMD_DELE: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.DELE(Command)) {
return 550;
}
return 250;
break;
}
case CMD_RMD: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_RMD)) {
return 550;
}
if (!MyDir.DELE(Command)) {
return 550;
}
return 250;
break;
}
case CMD_MKD: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (!MyDir.HasPermission(Filesystem::P_MKD)) {
return 550;
}
if (!MyDir.MKD(Command)) {
return 550;
}
return 2571;
break;
}
case CMD_RNFR: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
RNFR = Command;
return 350; //Awaiting further information
}
case CMD_RNTO: {
if (!LoggedIn()) {
return 530;
} //Not logged in.
if (Command == "") {
return 501;
} //Syntax error in parameters or arguments.
if (RNFR == "") {
return 503;
} //Bad sequence of commands
if (!MyDir.Rename(RNFR, Command)) {
return 550;
}
return 250;
}
default: {
return 502; //Command not implemented.
break;
}
}
}
bool FTP::User::LoggedIn() {
if (USER == "" || PASS == "") {
return false;
}
if (!AllCredentials.size()) {
return true;
}
if ((AllCredentials.find(USER) != AllCredentials.end()) && AllCredentials[USER] == PASS) {
return true;
}
return false;
}
std::string FTP::User::NumToMsg(int MsgNum) {
std::string Result;
switch (MsgNum) {
case 200: {
Result = "200 Message okay.\n";
break;
}
case 221: {
Result = "221 Service closing control connection. Logged out if appropriate.\n";
break;
}
case 226: {
Result = "226 Closing data connection.\n";
break;
}
case 227: {
std::stringstream sstr;
sstr << "227 Entering passive mode (0,0,0,0,";
sstr << (MyPassivePort >> 8) % 256;
sstr << ",";
sstr << MyPassivePort % 256;
sstr << ").\n";
Result = sstr.str();
break;
}
case 229: {
std::stringstream sstr;
sstr << "229 Entering extended passive mode (|||";
sstr << MyPassivePort;
sstr << "|).\n";
Result = sstr.str();
break;
}
case 230: {
Result = "230 User logged in, proceed.\n";
break;
}
case 250: {
Result = "250 Requested file action okay, completed.\n";
break;
}
case 2570: { //PWD
Result = "257 \"" + MyDir.PWD() + "\" selected as PWD\n";
break;
}
case 2571: { //MKD
Result = "257 \"" + MyDir.PWD() + "\" created\n";
break;
}
case 331: {
Result = "331 User name okay, need password.\n";
break;
}
case 350: {
Result = "350 Requested file action pending further information\n";
break;
}
case 501: {
Result = "501 Syntax error in parameters or arguments.\n";
break;
}
case 502: {
Result = "502 Command not implemented.\n";
break;
}
case 503: {
Result = "503 Bad sequence of commands.\n";
break;
}
case 504: {
Result = "504 Command not implemented for that parameter.\n";
break;
}
case 530: {
Result = "530 Not logged in.\n";
break;
}
case 550: {
Result = "550 Requested action not taken.\n";
break;
}
default: {
Result = "Error msg not implemented?\n";
break;
}
}
return Result;
}

82
lib/ftp.h Normal file
View file

@ -0,0 +1,82 @@
#include <map>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <sstream>
#include <string>
#include "./socket.h"
#include "./filesystem.h"
#include <unistd.h>
#include "./json.h"
namespace FTP {
static std::string FTPBasePath = "/tmp/mist/OnDemand/";
enum Mode {
MODE_STREAM,
};
//FTP::Mode enumeration
enum Structure {
STRU_FILE, STRU_RECORD,
};
//FTP::Structure enumeration
enum Type {
TYPE_ASCII_NONPRINT, TYPE_IMAGE_NONPRINT,
};
//FTP::Type enumeration
enum Commands {
CMD_NOCMD,
CMD_NOOP,
CMD_USER,
CMD_PASS,
CMD_QUIT,
CMD_PORT,
CMD_RETR,
CMD_STOR,
CMD_TYPE,
CMD_MODE,
CMD_STRU,
CMD_EPSV,
CMD_PASV,
CMD_LIST,
CMD_PWD,
CMD_CWD,
CMD_CDUP,
CMD_DELE,
CMD_RMD,
CMD_MKD,
CMD_RNFR,
CMD_RNTO,
};
//FTP::Commands enumeration
class User {
public:
User(Socket::Connection NewConnection, std::map<std::string, std::string> Credentials);
~User();
int ParseCommand(std::string Command);
bool LoggedIn();
std::string NumToMsg(int MsgNum);
Socket::Connection Conn;
private:
std::map<std::string, std::string> AllCredentials;
std::string USER;
std::string PASS;
Mode MODE;
Structure STRU;
Type TYPE;
int PORT;
Socket::Server Passive;
int MyPassivePort;
Filesystem::Directory MyDir;
std::string RNFR;
std::vector<std::string> ActiveStreams;
};
//FTP::User class
}//FTP Namespace

661
lib/http_parser.cpp Normal file
View file

@ -0,0 +1,661 @@
/// \file http_parser.cpp
/// Holds all code for the HTTP namespace.
#include "http_parser.h"
#include "timing.h"
/// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing.
/// All this constructor does is call HTTP::Parser::Clean().
HTTP::Parser::Parser() {
headerOnly = false;
Clean();
}
/// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage.
void HTTP::Parser::Clean() {
CleanPreserveHeaders();
headers.clear();
}
/// Completely re-initializes the HTTP::Parser, leaving it ready for either reading or writing usage.
void HTTP::Parser::CleanPreserveHeaders() {
seenHeaders = false;
seenReq = false;
getChunks = false;
doingChunk = 0;
bufferChunks = false;
method = "GET";
url = "/";
protocol = "HTTP/1.1";
body.clear();
length = 0;
vars.clear();
}
/// Returns a string containing a valid HTTP 1.0 or 1.1 request, ready for sending.
/// The request is build from internal variables set before this call is made.
/// To be precise, method, url, protocol, headers and body are used.
/// \return A string containing a valid HTTP 1.0 or 1.1 request, ready for sending.
std::string & HTTP::Parser::BuildRequest() {
/// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol[4] != '/') {
protocol = "HTTP/1.0";
}
builder = method + " " + url + " " + protocol + "\r\n";
for (it = headers.begin(); it != headers.end(); it++) {
if ((*it).first != "" && (*it).second != "") {
builder += (*it).first + ": " + (*it).second + "\r\n";
}
}
builder += "\r\n" + body;
return builder;
}
/// Creates and sends a valid HTTP 1.0 or 1.1 request.
/// The request is build from internal variables set before this call is made.
/// To be precise, method, url, protocol, headers and body are used.
void HTTP::Parser::SendRequest(Socket::Connection & conn) {
/// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol[4] != '/') {
protocol = "HTTP/1.0";
}
builder = method + " " + url + " " + protocol + "\r\n";
conn.SendNow(builder);
for (it = headers.begin(); it != headers.end(); it++) {
if ((*it).first != "" && (*it).second != "") {
builder = (*it).first + ": " + (*it).second + "\r\n";
conn.SendNow(builder);
}
}
conn.SendNow("\r\n", 2);
conn.SendNow(body);
}
/// Returns a string containing a valid HTTP 1.0 or 1.1 response, ready for sending.
/// The response is partly build from internal variables set before this call is made.
/// To be precise, protocol, headers and body are used.
/// \param code The HTTP response code. Usually you want 200.
/// \param message The HTTP response message. Usually you want "OK".
/// \return A string containing a valid HTTP 1.0 or 1.1 response, ready for sending.
std::string & HTTP::Parser::BuildResponse(std::string code, std::string message) {
/// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol[4] != '/') {
protocol = "HTTP/1.0";
}
builder = protocol + " " + code + " " + message + "\r\n";
for (it = headers.begin(); it != headers.end(); it++) {
if ((*it).first != "" && (*it).second != "") {
if ((*it).first != "Content-Length" || (*it).second != "0") {
builder += (*it).first + ": " + (*it).second + "\r\n";
}
}
}
builder += "\r\n";
builder += body;
return builder;
}
/// Returns a string containing a valid HTTP 1.0 or 1.1 response, ready for sending.
/// The response is partly build from internal variables set before this call is made.
/// To be precise, protocol, headers and body are used.
/// \return A string containing a valid HTTP 1.0 or 1.1 response, ready for sending.
/// This function calls this->BuildResponse(this->method,this->url)
std::string & HTTP::Parser::BuildResponse() {
return BuildResponse(method,url);
}
/// Creates and sends a valid HTTP 1.0 or 1.1 response.
/// The response is partly build from internal variables set before this call is made.
/// To be precise, protocol, headers and body are used.
/// This call will attempt to buffer as little as possible and block until the whole request is sent.
/// \param code The HTTP response code. Usually you want 200.
/// \param message The HTTP response message. Usually you want "OK".
/// \param conn The Socket::Connection to send the response over.
void HTTP::Parser::SendResponse(std::string code, std::string message, Socket::Connection & conn) {
/// \todo Include GET/POST variable parsing?
std::map<std::string, std::string>::iterator it;
if (protocol.size() < 5 || protocol[4] != '/') {
protocol = "HTTP/1.0";
}
builder = protocol + " " + code + " " + message + "\r\n";
conn.SendNow(builder);
for (it = headers.begin(); it != headers.end(); it++) {
if ((*it).first != "" && (*it).second != "") {
if ((*it).first != "Content-Length" || (*it).second != "0") {
builder = (*it).first + ": " + (*it).second + "\r\n";
conn.SendNow(builder);
}
}
}
conn.SendNow("\r\n", 2);
conn.SendNow(body);
}
/// Creates and sends a valid HTTP 1.0 or 1.1 response, based on the given request.
/// The headers must be set before this call is made.
/// This call sets up chunked transfer encoding if the request was protocol HTTP/1.1, otherwise uses a zero-content-length HTTP/1.0 response.
/// \param code The HTTP response code. Usually you want 200.
/// \param message The HTTP response message. Usually you want "OK".
/// \param request The HTTP request to respond to.
/// \param conn The connection to send over.
void HTTP::Parser::StartResponse(std::string code, std::string message, HTTP::Parser & request, Socket::Connection & conn, bool bufferAllChunks) {
std::string prot = request.protocol;
sendingChunks = (!bufferAllChunks && protocol == "HTTP/1.1" && request.GetHeader("Connection")!="close");
CleanPreserveHeaders();
protocol = prot;
if (sendingChunks){
SetHeader("Transfer-Encoding", "chunked");
} else {
SetHeader("Connection", "close");
}
bufferChunks = bufferAllChunks;
if (!bufferAllChunks){
SendResponse(code, message, conn);
}
}
/// Creates and sends a valid HTTP 1.0 or 1.1 response, based on the given request.
/// The headers must be set before this call is made.
/// This call sets up chunked transfer encoding if the request was protocol HTTP/1.1, otherwise uses a zero-content-length HTTP/1.0 response.
/// This call simply calls StartResponse("200", "OK", request, conn)
/// \param request The HTTP request to respond to.
/// \param conn The connection to send over.
void HTTP::Parser::StartResponse(HTTP::Parser & request, Socket::Connection & conn, bool bufferAllChunks) {
StartResponse("200", "OK", request, conn, bufferAllChunks);
}
/// After receiving a header with this object, this function call will:
/// - Forward the headers to the 'to' Socket::Connection.
/// - Retrieve all the body from the 'from' Socket::Connection.
/// - Forward those contents as-is to the 'to' Socket::Connection.
/// It blocks until completed or either of the connections reaches an error state.
void HTTP::Parser::Proxy(Socket::Connection & from, Socket::Connection & to) {
SendResponse(url, method, to);
if (getChunks) {
unsigned int proxyingChunk = 0;
while (to.connected() && from.connected()) {
if ((from.Received().size() && (from.Received().size() > 1 || *(from.Received().get().rbegin()) == '\n')) || from.spool()) {
if (proxyingChunk) {
while (proxyingChunk && from.Received().size()) {
unsigned int toappend = from.Received().get().size();
if (toappend > proxyingChunk) {
toappend = proxyingChunk;
to.SendNow(from.Received().get().c_str(), toappend);
from.Received().get().erase(0, toappend);
} else {
to.SendNow(from.Received().get());
from.Received().get().clear();
}
proxyingChunk -= toappend;
}
} else {
//Make sure the received data ends in a newline (\n).
if (*(from.Received().get().rbegin()) != '\n') {
if (from.Received().size() > 1) {
//make a copy of the first part
std::string tmp = from.Received().get();
//clear the first part, wiping it from the partlist
from.Received().get().clear();
from.Received().size();
//take the now first (was second) part, insert the stored part in front of it
from.Received().get().insert(0, tmp);
} else {
Util::sleep(100);
}
if (*(from.Received().get().rbegin()) != '\n') {
continue;
}
}
//forward the size and any empty lines
to.SendNow(from.Received().get());
std::string tmpA = from.Received().get().substr(0, from.Received().get().size() - 1);
while (tmpA.find('\r') != std::string::npos) {
tmpA.erase(tmpA.find('\r'));
}
unsigned int chunkLen = 0;
if (!tmpA.empty()) {
for (unsigned int i = 0; i < tmpA.size(); ++i) {
chunkLen = (chunkLen << 4) | unhex(tmpA[i]);
}
if (chunkLen == 0) {
getChunks = false;
to.SendNow("\r\n", 2);
return;
}
proxyingChunk = chunkLen;
}
from.Received().get().clear();
}
} else {
Util::sleep(100);
}
}
} else {
unsigned int bodyLen = length;
while (bodyLen > 0 && to.connected() && from.connected()) {
if (from.Received().size() || from.spool()) {
if (from.Received().get().size() <= bodyLen) {
to.SendNow(from.Received().get());
bodyLen -= from.Received().get().size();
from.Received().get().clear();
} else {
to.SendNow(from.Received().get().c_str(), bodyLen);
from.Received().get().erase(0, bodyLen);
bodyLen = 0;
}
} else {
Util::sleep(100);
}
}
}
}
/// Trims any whitespace at the front or back of the string.
/// Used when getting/setting headers.
/// \param s The string to trim. The string itself will be changed, not returned.
void HTTP::Parser::Trim(std::string & s) {
size_t startpos = s.find_first_not_of(" \t");
size_t endpos = s.find_last_not_of(" \t");
if ((std::string::npos == startpos) || (std::string::npos == endpos)) {
s = "";
} else {
s = s.substr(startpos, endpos - startpos + 1);
}
}
/// Function that sets the body of a response or request, along with the correct Content-Length header.
/// \param s The string to set the body to.
void HTTP::Parser::SetBody(std::string s) {
body = s;
SetHeader("Content-Length", s.length());
}
/// Function that sets the body of a response or request, along with the correct Content-Length header.
/// \param buffer The buffer data to set the body to.
/// \param len Length of the buffer data.
void HTTP::Parser::SetBody(char * buffer, int len) {
body = "";
body.append(buffer, len);
SetHeader("Content-Length", len);
}
/// Returns header i, if set.
std::string HTTP::Parser::getUrl() {
if (url.find('?') != std::string::npos) {
return url.substr(0, url.find('?'));
} else {
return url;
}
}
/// Returns header i, if set.
std::string HTTP::Parser::GetHeader(std::string i) {
return headers[i];
}
/// Returns POST variable i, if set.
std::string HTTP::Parser::GetVar(std::string i) {
return vars[i];
}
/// Sets header i to string value v.
void HTTP::Parser::SetHeader(std::string i, std::string v) {
Trim(i);
Trim(v);
headers[i] = v;
}
/// Sets header i to integer value v.
void HTTP::Parser::SetHeader(std::string i, int v) {
Trim(i);
char val[23]; //ints are never bigger than 22 chars as decimal
sprintf(val, "%i", v);
headers[i] = val;
}
/// Sets POST variable i to string value v.
void HTTP::Parser::SetVar(std::string i, std::string v) {
Trim(i);
Trim(v);
//only set if there is actually a key
if (!i.empty()) {
vars[i] = v;
}
}
/// Attempt to read a whole HTTP request or response from a Socket::Connection.
/// If a whole request could be read, it is removed from the front of the socket buffer and true returned.
/// If not, as much as can be interpreted is removed and false returned.
/// \param conn The socket to read from.
/// \return True if a whole request or response was read, false otherwise.
bool HTTP::Parser::Read(Socket::Connection & conn) {
//Make sure the received data ends in a newline (\n).
while ((!seenHeaders || (getChunks && !doingChunk)) && conn.Received().get().size() && *(conn.Received().get().rbegin()) != '\n') {
if (conn.Received().size() > 1) {
//make a copy of the first part
std::string tmp = conn.Received().get();
//clear the first part, wiping it from the partlist
conn.Received().get().clear();
conn.Received().size();
//take the now first (was second) part, insert the stored part in front of it
conn.Received().get().insert(0, tmp);
} else {
return false;
}
}
//if a parse succeeds, simply return true
if (parse(conn.Received().get())) {
return true;
}
//otherwise, if we have parts left, call ourselves recursively
if (conn.Received().size()) {
return Read(conn);
}
return false;
} //HTTPReader::Read
/// Attempt to read a whole HTTP request or response from a std::string buffer.
/// If a whole request could be read, it is removed from the front of the given buffer and true returned.
/// If not, as much as can be interpreted is removed and false returned.
/// \param strbuf The buffer to read from.
/// \return True if a whole request or response was read, false otherwise.
bool HTTP::Parser::Read(std::string & strbuf) {
return parse(strbuf);
} //HTTPReader::Read
/// Attempt to read a whole HTTP response or request from a data buffer.
/// If succesful, fills its own fields with the proper data and removes the response/request
/// from the data buffer.
/// \param HTTPbuffer The data buffer to read from.
/// \return True on success, false otherwise.
bool HTTP::Parser::parse(std::string & HTTPbuffer) {
size_t f;
std::string tmpA, tmpB, tmpC;
/// \todo Make this not resize HTTPbuffer in parts, but read all at once and then remove the entire request, like doxygen claims it does?
while (!HTTPbuffer.empty()) {
if (!seenHeaders) {
f = HTTPbuffer.find('\n');
if (f == std::string::npos) return false;
tmpA = HTTPbuffer.substr(0, f);
if (f + 1 == HTTPbuffer.size()) {
HTTPbuffer.clear();
} else {
HTTPbuffer.erase(0, f + 1);
}
while (tmpA.find('\r') != std::string::npos) {
tmpA.erase(tmpA.find('\r'));
}
if (!seenReq) {
seenReq = true;
f = tmpA.find(' ');
if (f != std::string::npos) {
if (tmpA.substr(0, 4) == "HTTP") {
protocol = tmpA.substr(0, f);
tmpA.erase(0, f + 1);
f = tmpA.find(' ');
if (f != std::string::npos) {
url = tmpA.substr(0, f);
tmpA.erase(0, f + 1);
method = tmpA;
if (url.find('?') != std::string::npos) {
parseVars(url.substr(url.find('?') + 1)); //parse GET variables
url.erase(url.find('?'));
}
url = urlunescape(url);
} else {
seenReq = false;
}
} else {
method = tmpA.substr(0, f);
tmpA.erase(0, f + 1);
f = tmpA.find(' ');
if (f != std::string::npos) {
url = tmpA.substr(0, f);
tmpA.erase(0, f + 1);
protocol = tmpA;
if (url.find('?') != std::string::npos) {
parseVars(url.substr(url.find('?') + 1)); //parse GET variables
url.erase(url.find('?'));
}
url = urlunescape(url);
} else {
seenReq = false;
}
}
} else {
seenReq = false;
}
} else {
if (tmpA.size() == 0) {
seenHeaders = true;
body.clear();
if (GetHeader("Content-Length") != "") {
length = atoi(GetHeader("Content-Length").c_str());
if (body.capacity() < length) {
body.reserve(length);
}
}
if (GetHeader("Transfer-Encoding") == "chunked") {
getChunks = true;
doingChunk = 0;
}
} else {
f = tmpA.find(':');
if (f == std::string::npos) continue;
tmpB = tmpA.substr(0, f);
tmpC = tmpA.substr(f + 1);
SetHeader(tmpB, tmpC);
}
}
}
if (seenHeaders) {
if (length > 0) {
if (headerOnly) {
return true;
}
unsigned int toappend = length - body.length();
if (toappend > 0) {
body.append(HTTPbuffer, 0, toappend);
HTTPbuffer.erase(0, toappend);
}
if (length == body.length()) {
parseVars(body); //parse POST variables
return true;
} else {
return false;
}
} else {
if (getChunks) {
if (headerOnly) {
return true;
}
if (doingChunk) {
unsigned int toappend = HTTPbuffer.size();
if (toappend > doingChunk) {
toappend = doingChunk;
}
body.append(HTTPbuffer, 0, toappend);
HTTPbuffer.erase(0, toappend);
doingChunk -= toappend;
} else {
f = HTTPbuffer.find('\n');
if (f == std::string::npos) return false;
tmpA = HTTPbuffer.substr(0, f);
while (tmpA.find('\r') != std::string::npos) {
tmpA.erase(tmpA.find('\r'));
}
unsigned int chunkLen = 0;
if (!tmpA.empty()) {
for (unsigned int i = 0; i < tmpA.size(); ++i) {
chunkLen = (chunkLen << 4) | unhex(tmpA[i]);
}
if (chunkLen == 0) {
getChunks = false;
return true;
}
doingChunk = chunkLen;
}
if (f + 1 == HTTPbuffer.size()) {
HTTPbuffer.clear();
} else {
HTTPbuffer.erase(0, f + 1);
}
}
return false;
} else {
return true;
}
}
}
}
return false; //empty input
} //HTTPReader::parse
/// Parses GET or POST-style variable data.
/// Saves to internal variable structure using HTTP::Parser::SetVar.
void HTTP::Parser::parseVars(std::string data) {
std::string varname;
std::string varval;
// position where a part start (e.g. after &)
size_t pos = 0;
while (pos < data.length()) {
size_t nextpos = data.find('&', pos);
if (nextpos == std::string::npos) {
nextpos = data.length();
}
size_t eq_pos = data.find('=', pos);
if (eq_pos < nextpos) {
// there is a key and value
varname = data.substr(pos, eq_pos - pos);
varval = data.substr(eq_pos + 1, nextpos - eq_pos - 1);
} else {
// no value, only a key
varname = data.substr(pos, nextpos - pos);
varval.clear();
}
SetVar(urlunescape(varname), urlunescape(varval));
if (nextpos == std::string::npos) {
// in case the string is gigantic
break;
}
// erase &
pos = nextpos + 1;
}
}
/// Sends a string in chunked format if protocol is HTTP/1.1, sends as-is otherwise.
/// \param bodypart The data to send.
/// \param conn The connection to use for sending.
void HTTP::Parser::Chunkify(const std::string & bodypart, Socket::Connection & conn) {
Chunkify(bodypart.c_str(), bodypart.size(), conn);
}
/// Sends a string in chunked format if protocol is HTTP/1.1, sends as-is otherwise.
/// \param data The data to send.
/// \param size The size of the data to send.
/// \param conn The connection to use for sending.
void HTTP::Parser::Chunkify(const char * data, unsigned int size, Socket::Connection & conn) {
static char hexa[] = "0123456789abcdef";
if (bufferChunks){
if (size){
body.append(data, size);
}else{
SetHeader("Content-Length", body.length());
SendResponse("200", "OK", conn);
Clean();
}
return;
}
if (sendingChunks) {
//prepend the chunk size and \r\n
if (!size){
conn.SendNow("0\r\n\r\n\r\n", 7);
}
size_t offset = 8;
unsigned int t_size = size;
char len[] = "\000\000\000\000\000\000\0000\r\n";
while (t_size && offset < 9){
len[--offset] = hexa[t_size & 0xf];
t_size >>= 4;
}
conn.SendNow(len+offset, 10-offset);
//send the chunk itself
conn.SendNow(data, size);
//append \r\n
conn.SendNow("\r\n", 2);
} else {
//just send the chunk itself
conn.SendNow(data, size);
//close the connection if this was the end of the file
if (!size) {
conn.close();
Clean();
}
}
}
/// Unescapes URLencoded std::string data.
std::string HTTP::Parser::urlunescape(const std::string & in) {
std::string out;
for (unsigned int i = 0; i < in.length(); ++i) {
if (in[i] == '%') {
char tmp = 0;
++i;
if (i < in.length()) {
tmp = unhex(in[i]) << 4;
}
++i;
if (i < in.length()) {
tmp += unhex(in[i]);
}
out += tmp;
} else {
if (in[i] == '+') {
out += ' ';
} else {
out += in[i];
}
}
}
return out;
}
/// Helper function for urlunescape.
/// Takes a single char input and outputs its integer hex value.
int HTTP::Parser::unhex(char c) {
return (c >= '0' && c <= '9' ? c - '0' : c >= 'A' && c <= 'F' ? c - 'A' + 10 : c - 'a' + 10);
}
/// URLencodes std::string data.
std::string HTTP::Parser::urlencode(const std::string & c) {
std::string escaped = "";
int max = c.length();
for (int i = 0; i < max; i++) {
if (('0' <= c[i] && c[i] <= '9') || ('a' <= c[i] && c[i] <= 'z') || ('A' <= c[i] && c[i] <= 'Z')
|| (c[i] == '~' || c[i] == '!' || c[i] == '*' || c[i] == '(' || c[i] == ')' || c[i] == '\'')) {
escaped.append(&c[i], 1);
} else {
escaped.append("%");
escaped.append(hex(c[i]));
}
}
return escaped;
}
/// Helper function for urlescape.
/// Encodes a character as two hex digits.
std::string HTTP::Parser::hex(char dec) {
char dig1 = (dec & 0xF0) >> 4;
char dig2 = (dec & 0x0F);
if (dig1 <= 9) dig1 += 48;
if (10 <= dig1 && dig1 <= 15) dig1 += 97 - 10;
if (dig2 <= 9) dig2 += 48;
if (10 <= dig2 && dig2 <= 15) dig2 += 97 - 10;
std::string r;
r.append(&dig1, 1);
r.append(&dig2, 1);
return r;
}

66
lib/http_parser.h Normal file
View file

@ -0,0 +1,66 @@
/// \file http_parser.h
/// Holds all headers for the HTTP namespace.
#pragma once
#include <map>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include "socket.h"
/// Holds all HTTP processing related code.
namespace HTTP {
/// Simple class for reading and writing HTTP 1.0 and 1.1.
class Parser {
public:
Parser();
bool Read(Socket::Connection & conn);
bool Read(std::string & strbuf);
std::string GetHeader(std::string i);
std::string GetVar(std::string i);
std::string getUrl();
void SetHeader(std::string i, std::string v);
void SetHeader(std::string i, int v);
void SetVar(std::string i, std::string v);
void SetBody(std::string s);
void SetBody(char * buffer, int len);
std::string & BuildRequest();
std::string & BuildResponse();
std::string & BuildResponse(std::string code, std::string message);
void SendRequest(Socket::Connection & conn);
void SendResponse(std::string code, std::string message, Socket::Connection & conn);
void StartResponse(std::string code, std::string message, Parser & request, Socket::Connection & conn, bool bufferAllChunks = false);
void StartResponse(Parser & request, Socket::Connection & conn, bool bufferAllChunks = false);
void Chunkify(const std::string & bodypart, Socket::Connection & conn);
void Chunkify(const char * data, unsigned int size, Socket::Connection & conn);
void Proxy(Socket::Connection & from, Socket::Connection & to);
void Clean();
void CleanPreserveHeaders();
static std::string urlunescape(const std::string & in);
static std::string urlencode(const std::string & in);
std::string body;
std::string method;
std::string url;
std::string protocol;
unsigned int length;
bool headerOnly; ///< If true, do not parse body if the length is a known size.
bool bufferChunks;
private:
bool seenHeaders;
bool seenReq;
bool getChunks;
bool sendingChunks;
unsigned int doingChunk;
bool parse(std::string & HTTPbuffer);
void parseVars(std::string data);
std::string builder;
std::string read_buffer;
std::map<std::string, std::string> headers;
std::map<std::string, std::string> vars;
void Trim(std::string & s);
static int unhex(char c);
static std::string hex(char dec);
};
//HTTP::Parser class
}//HTTP namespace

1155
lib/json.cpp Normal file

File diff suppressed because it is too large Load diff

181
lib/json.h Normal file
View file

@ -0,0 +1,181 @@
/// \file json.h Holds all JSON-related headers.
#pragma once
#include <string>
#include <deque>
#include <map>
#include <istream>
#include <vector>
#include "socket.h"
//empty definition of DTSC::Stream so it can be a friend.
namespace DTSC {
class Stream;
}
/// JSON-related classes and functions
namespace JSON {
/// Lists all types of JSON::Value.
enum ValueType {
EMPTY, BOOL, INTEGER, STRING, ARRAY, OBJECT
};
class Value;
//forward declaration for below typedef
typedef std::map<std::string, Value>::iterator ObjIter;
typedef std::deque<Value>::iterator ArrIter;
typedef std::map<std::string, Value>::const_iterator ObjConstIter;
typedef std::deque<Value>::const_iterator ArrConstIter;
/// A JSON::Value is either a string or an integer, but may also be an object, array or null.
class Value {
private:
ValueType myType;
long long int intVal;
std::string strVal;
std::deque<Value> arrVal;
std::map<std::string, Value> objVal;
public:
//friends
friend class DTSC::Stream; //for access to strVal
//constructors
Value();
Value(std::istream & fromstream);
Value(const std::string & val);
Value(const char * val);
Value(long long int val);
Value(bool val);
//comparison operators
bool operator==(const Value & rhs) const;
bool operator!=(const Value & rhs) const;
//assignment operators
Value & operator=(const std::string & rhs);
Value & operator=(const char * rhs);
Value & operator=(const long long int & rhs);
Value & operator=(const int & rhs);
Value & operator=(const unsigned int & rhs);
Value & operator=(const bool & rhs);
//converts to basic types
operator long long int() const;
operator std::string() const;
operator bool() const;
const std::string asString() const;
const long long int asInt() const;
const bool asBool() const;
const std::string & asStringRef() const;
const char * c_str() const;
//array operator for maps and arrays
Value & operator[](const std::string i);
Value & operator[](const char * i);
Value & operator[](unsigned int i);
const Value & operator[](const std::string i) const;
const Value & operator[](const char * i) const;
const Value & operator[](unsigned int i) const;
//handy functions and others
std::string toPacked() const;
void sendTo(Socket::Connection & socket) const;
unsigned int packedSize() const;
void netPrepare();
std::string & toNetPacked();
std::string toString() const;
std::string toPrettyString(int indentation = 0) const;
void append(const Value & rhs);
void prepend(const Value & rhs);
void shrink(unsigned int size);
void removeMember(const std::string & name);
bool isMember(const std::string & name) const;
bool isInt() const;
bool isString() const;
bool isBool() const;
bool isObject() const;
bool isArray() const;
bool isNull() const;
ObjIter ObjBegin();
ObjIter ObjEnd();
ArrIter ArrBegin();
ArrIter ArrEnd();
ObjConstIter ObjBegin() const;
ObjConstIter ObjEnd() const;
ArrConstIter ArrBegin() const;
ArrConstIter ArrEnd() const;
unsigned int size() const;
void null();
};
Value fromDTMI2(std::string & data);
Value fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i);
Value fromDTMI(std::string & data);
Value fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i);
Value fromString(std::string json);
Value fromFile(std::string filename);
void fromDTMI2(std::string & data, Value & ret);
void fromDTMI2(const unsigned char * data, unsigned int len, unsigned int & i, Value & ret);
void fromDTMI(std::string & data, Value & ret);
void fromDTMI(const unsigned char * data, unsigned int len, unsigned int & i, Value & ret);
template <typename T>
std::string encodeVector(T begin, T end) {
std::string result;
for (T it = begin; it != end; it++) {
long long int tmp = (*it);
while (tmp >= 0xFFFF) {
result += (char)0xFF;
result += (char)0xFF;
tmp -= 0xFFFF;
}
result += (char)(tmp / 256);
result += (char)(tmp % 256);
}
return result;
}
template <typename T>
void decodeVector(std::string input, T & result) {
result.clear();
unsigned int tmp = 0;
for (int i = 0; i < input.size(); i += 2) {
unsigned int curLen = (input[i] << 8) + input[i + 1];
tmp += curLen;
if (curLen != 0xFFFF) {
result.push_back(tmp);
tmp = 0;
}
}
}
template <typename T>
std::string encodeVector4(T begin, T end) {
std::string result;
for (T it = begin; it != end; it++) {
long long int tmp = (*it);
while (tmp >= 0xFFFFFFFF) {
result += (char)0xFF;
result += (char)0xFF;
result += (char)0xFF;
result += (char)0xFF;
tmp -= 0xFFFFFFFF;
}
result += (char)((tmp & 0xFF000000) >> 24);
result += (char)((tmp & 0x00FF0000) >> 16);
result += (char)((tmp & 0x0000FF00) >> 8);
result += (char)((tmp & 0x000000FF));
}
return result;
}
template <typename T>
void decodeVector4(std::string input, T & result) {
result.clear();
unsigned int tmp = 0;
for (int i = 0; i < input.size(); i += 4) {
unsigned int curLen = (input[i] << 24) + (input[i + 1] << 16) + (input[i + 2] << 8) + (input[i + 3]);
tmp += curLen;
if (curLen != 0xFFFFFFFF) {
result.push_back(tmp);
tmp = 0;
}
}
}
}

807
lib/mp4.cpp Normal file
View file

@ -0,0 +1,807 @@
#include <stdlib.h> //for malloc and free
#include <string.h> //for memcpy
#include <arpa/inet.h> //for htonl and friends
#include "mp4.h"
#include "mp4_adobe.h"
#include "mp4_ms.h"
#include "mp4_generic.h"
#include "json.h"
#include "defines.h"
/// Contains all MP4 format related code.
namespace MP4 {
/// Creates a new box, optionally using the indicated pointer for storage.
/// If manage is set to true, the pointer will be realloc'ed when the box needs to be resized.
/// If the datapointer is NULL, manage is assumed to be true even if explicitly given as false.
/// If managed, the pointer will be free'd upon destruction.
Box::Box(char * datapointer, bool manage) {
data = datapointer;
managed = manage;
payloadOffset = 8;
if (data == 0) {
clear();
} else {
data_size = ntohl(((int *)data)[0]);
}
}
Box::Box(const Box & rs) {
data = rs.data;
managed = false;
payloadOffset = rs.payloadOffset;
if (data == 0) {
clear();
} else {
data_size = ntohl(((int *)data)[0]);
}
}
Box & Box::operator = (const Box & rs) {
clear();
if (data) {
free(data);
data = 0;
}
data = rs.data;
managed = false;
payloadOffset = rs.payloadOffset;
if (data == 0) {
clear();
} else {
data_size = ntohl(((int *)data)[0]);
}
return *this;
}
/// If managed, this will free the data pointer.
Box::~Box() {
if (managed && data) {
free(data);
data = 0;
}
}
/// Returns the values at byte positions 4 through 7.
std::string Box::getType() {
return std::string(data + 4, 4);
}
/// Returns true if the given 4-byte boxtype is equal to the values at byte positions 4 through 7.
bool Box::isType(const char * boxType) {
return !memcmp(boxType, data + 4, 4);
}
/// Reads the first 8 bytes and returns
std::string readBoxType(FILE * newData) {
char retVal[8] = {0, 0, 0, 0, 'e', 'r', 'r', 'o'};
long long unsigned int pos = ftell(newData);
fread(retVal, 8, 1, newData);
fseek(newData, pos, SEEK_SET);
return std::string(retVal + 4, 4);
}
///\todo make good working calcBoxSize with size and payloadoffset calculation
unsigned long int calcBoxSize(char readVal[16]) {
return (unsigned int)ntohl(((int *)readVal)[0]);
}
bool skipBox(FILE * newData) {
char readVal[16];
long long unsigned int pos = ftell(newData);
if (fread(readVal, 4, 1, newData)) {
uint64_t size = calcBoxSize(readVal);
if (size == 1) {
if (fread(readVal + 4, 12, 1, newData)) {
size = 0 + ntohl(((int *)readVal)[2]);
size <<= 32;
size += ntohl(((int *)readVal)[3]);
} else {
return false;
}
} else if (size == 0) {
fseek(newData, 0, SEEK_END);
}
DONTEVEN_MSG("skipping size 0x%.8lX", size);
if (fseek(newData, pos + size, SEEK_SET) == 0) {
return true;
} else {
return false;
}
} else {
return false;
}
}
bool Box::read(FILE * newData) {
char readVal[16];
long long unsigned int pos = ftell(newData);
if (fread(readVal, 4, 1, newData)) {
payloadOffset = 8;
uint64_t size = calcBoxSize(readVal);
if (size == 1) {
if (fread(readVal + 4, 12, 1, newData)) {
size = 0 + ntohl(((int *)readVal)[2]);
size <<= 32;
size += ntohl(((int *)readVal)[3]);
payloadOffset = 16;
} else {
return false;
}
}
fseek(newData, pos, SEEK_SET);
data = (char *)realloc(data, size);
data_size = size;
return (fread(data, size, 1, newData) == 1);
} else {
return false;
}
}
/// Reads out a whole box (if possible) from newData, copying to the internal data storage and removing from the input string.
/// \returns True on success, false otherwise.
bool Box::read(std::string & newData) {
if (!managed) {
return false;
}
if (newData.size() > 4) {
payloadOffset = 8;
uint64_t size = ntohl(((int *)newData.c_str())[0]);
if (size == 1) {
if (newData.size() > 16) {
size = 0 + ntohl(((int *)newData.c_str())[2]);
size <<= 32;
size += ntohl(((int *)newData.c_str())[3]);
payloadOffset = 16;
} else {
return false;
}
}
if (newData.size() >= size) {
data = (char *)realloc(data, size);
data_size = size;
memcpy(data, newData.data(), size);
newData.erase(0, size);
return true;
}
}
return false;
}
/// Returns the total boxed size of this box, including the header.
uint64_t Box::boxedSize() {
if (payloadOffset == 16) {
return ((uint64_t)ntohl(((int *)data)[2]) << 32) + ntohl(((int *)data)[3]);
}
return ntohl(((int *)data)[0]);
}
/// Retruns the size of the payload of thix box, excluding the header.
/// This value is defined as boxedSize() - 8.
uint64_t Box::payloadSize() {
return boxedSize() - payloadOffset;
}
/// Returns a copy of the data pointer.
char * Box::asBox() {
return data;
}
char * Box::payload() {
return data + payloadOffset;
}
/// Makes this box managed if it wasn't already, resetting the internal storage to 8 bytes (the minimum).
/// If this box wasn't managed, the original data is left intact - otherwise it is free'd.
/// If it was somehow impossible to allocate 8 bytes (should never happen), this will cause segfaults later.
void Box::clear() {
if (data && managed) {
free(data);
}
managed = true;
payloadOffset = 8;
data = (char *)malloc(8);
if (data) {
data_size = 8;
((int *)data)[0] = htonl(data_size);
} else {
data_size = 0;
}
}
/// Attempts to typecast this Box to a more specific type and call the toPrettyString() function of that type.
/// If this failed, it will print out a message saying pretty-printing is not implemented for boxtype.
std::string Box::toPrettyString(uint32_t indent) {
switch (ntohl(*((int *)(data + 4)))) { //type is at this address
case 0x6D666864:
return ((MFHD *)this)->toPrettyString(indent);
break;
case 0x6D6F6F66:
return ((MOOF *)this)->toPrettyString(indent);
break;
case 0x61627374:
return ((ABST *)this)->toPrettyString(indent);
break;
case 0x61667274:
return ((AFRT *)this)->toPrettyString(indent);
break;
case 0x61667261:
return ((AFRA *)this)->toPrettyString(indent);
break;
case 0x61737274:
return ((ASRT *)this)->toPrettyString(indent);
break;
case 0x7472756E:
return ((TRUN *)this)->toPrettyString(indent);
break;
case 0x74726166:
return ((TRAF *)this)->toPrettyString(indent);
break;
case 0x74666864:
return ((TFHD *)this)->toPrettyString(indent);
break;
case 0x61766343:
return ((AVCC *)this)->toPrettyString(indent);
break;
case 0x73647470:
return ((SDTP *)this)->toPrettyString(indent);
break;
case 0x66747970:
return ((FTYP *)this)->toPrettyString(indent);
break;
case 0x6D6F6F76:
return ((MOOV *)this)->toPrettyString(indent);
break;
case 0x6D766578:
return ((MVEX *)this)->toPrettyString(indent);
break;
case 0x74726578:
return ((TREX *)this)->toPrettyString(indent);
break;
case 0x6D667261:
return ((MFRA *)this)->toPrettyString(indent);
break;
case 0x7472616B:
return ((TRAK *)this)->toPrettyString(indent);
break;
case 0x6D646961:
return ((MDIA *)this)->toPrettyString(indent);
break;
case 0x6D696E66:
return ((MINF *)this)->toPrettyString(indent);
break;
case 0x64696E66:
return ((DINF *)this)->toPrettyString(indent);
break;
case 0x6D66726F:
return ((MFRO *)this)->toPrettyString(indent);
break;
case 0x68646C72:
return ((HDLR *)this)->toPrettyString(indent);
break;
case 0x766D6864:
return ((VMHD *)this)->toPrettyString(indent);
break;
case 0x736D6864:
return ((SMHD *)this)->toPrettyString(indent);
break;
case 0x686D6864:
return ((HMHD *)this)->toPrettyString(indent);
break;
case 0x6E6D6864:
return ((NMHD *)this)->toPrettyString(indent);
break;
case 0x6D656864:
return ((MEHD *)this)->toPrettyString(indent);
break;
case 0x7374626C:
return ((STBL *)this)->toPrettyString(indent);
break;
case 0x64726566:
return ((DREF *)this)->toPrettyString(indent);
break;
case 0x75726C20:
return ((URL *)this)->toPrettyString(indent);
break;
case 0x75726E20:
return ((URN *)this)->toPrettyString(indent);
break;
case 0x6D766864:
return ((MVHD *)this)->toPrettyString(indent);
break;
case 0x74667261:
return ((TFRA *)this)->toPrettyString(indent);
break;
case 0x746B6864:
return ((TKHD *)this)->toPrettyString(indent);
break;
case 0x6D646864:
return ((MDHD *)this)->toPrettyString(indent);
break;
case 0x73747473:
return ((STTS *)this)->toPrettyString(indent);
break;
case 0x63747473:
return ((CTTS *)this)->toPrettyString(indent);
break;
case 0x73747363:
return ((STSC *)this)->toPrettyString(indent);
break;
case 0x7374636F:
return ((STCO *)this)->toPrettyString(indent);
break;
case 0x636F3634:
return ((CO64 *)this)->toPrettyString(indent);
break;
case 0x7374737A:
return ((STSZ *)this)->toPrettyString(indent);
break;
case 0x73747364:
return ((STSD *)this)->toPrettyString(indent);
break;
case 0x6D703461://mp4a
case 0x656E6361://enca
return ((MP4A *)this)->toPrettyString(indent);
break;
case 0x61616320:
return ((AAC *)this)->toPrettyString(indent);
break;
case 0x61766331:
return ((AVC1 *)this)->toPrettyString(indent);
break;
case 0x68323634://h264
case 0x656E6376://encv
return ((H264 *)this)->toPrettyString(indent);
break;
case 0x65647473:
return ((EDTS *)this)->toPrettyString(indent);
break;
case 0x73747373:
return ((STSS *)this)->toPrettyString(indent);
break;
case 0x6D657461:
return ((META *)this)->toPrettyString(indent);
break;
case 0x656C7374:
return ((ELST *)this)->toPrettyString(indent);
break;
case 0x65736473:
return ((ESDS *)this)->toPrettyString(indent);
break;
case 0x75647461:
return ((UDTA *)this)->toPrettyString(indent);
break;
case 0x75756964:
return ((UUID *)this)->toPrettyString(indent);
break;
default:
break;
}
std::string retval = std::string(indent, ' ') + "Unimplemented pretty-printing for box " + std::string(data + 4, 4) + "\n";
/// \todo Implement hexdump for unimplemented boxes?
return retval;
}
/// Sets the 8 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Fails silently if resizing failed.
void Box::setInt8(char newData, size_t index) {
index += payloadOffset;
if (index >= boxedSize()) {
if (!reserve(index, 0, 1)) {
return;
}
}
data[index] = newData;
}
/// Gets the 8 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Returns zero if resizing failed.
char Box::getInt8(size_t index) {
index += payloadOffset;
if (index >= boxedSize()) {
if (!reserve(index, 0, 1)) {
return 0;
}
setInt8(0, index - payloadOffset);
}
return data[index];
}
/// Sets the 16 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Fails silently if resizing failed.
void Box::setInt16(short newData, size_t index) {
index += payloadOffset;
if (index + 1 >= boxedSize()) {
if (!reserve(index, 0, 2)) {
return;
}
}
newData = htons(newData);
memcpy(data + index, (char *) &newData, 2);
}
/// Gets the 16 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Returns zero if resizing failed.
short Box::getInt16(size_t index) {
index += payloadOffset;
if (index + 1 >= boxedSize()) {
if (!reserve(index, 0, 2)) {
return 0;
}
setInt16(0, index - payloadOffset);
}
short result;
memcpy((char *) &result, data + index, 2);
return ntohs(result);
}
/// Sets the 24 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Fails silently if resizing failed.
void Box::setInt24(uint32_t newData, size_t index) {
index += payloadOffset;
if (index + 2 >= boxedSize()) {
if (!reserve(index, 0, 3)) {
return;
}
}
data[index] = (newData & 0x00FF0000) >> 16;
data[index + 1] = (newData & 0x0000FF00) >> 8;
data[index + 2] = (newData & 0x000000FF);
}
/// Gets the 24 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Returns zero if resizing failed.
uint32_t Box::getInt24(size_t index) {
index += payloadOffset;
if (index + 2 >= boxedSize()) {
if (!reserve(index, 0, 3)) {
return 0;
}
setInt24(0, index - payloadOffset);
}
uint32_t result = data[index];
result <<= 8;
result += data[index + 1];
result <<= 8;
result += data[index + 2];
return result;
}
/// Sets the 32 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Fails silently if resizing failed.
void Box::setInt32(uint32_t newData, size_t index) {
index += payloadOffset;
if (index + 3 >= boxedSize()) {
if (!reserve(index, 0, 4)) {
return;
}
}
((int *)(data + index))[0] = htonl(newData);
}
/// Gets the 32 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Returns zero if resizing failed.
uint32_t Box::getInt32(size_t index) {
index += payloadOffset;
if (index + 3 >= boxedSize()) {
if (!reserve(index, 0, 4)) {
return 0;
}
setInt32(0, index - payloadOffset);
}
return ntohl(((int *)(data + index))[0]);
}
/// Sets the 64 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Fails silently if resizing failed.
void Box::setInt64(uint64_t newData, size_t index) {
index += payloadOffset;
if (index + 7 >= boxedSize()) {
if (!reserve(index, 0, 8)) {
return;
}
}
((int *)(data + index))[0] = htonl((int)(newData >> 32));
((int *)(data + index))[1] = htonl((int)(newData & 0xFFFFFFFF));
}
/// Gets the 64 bits integer at the given index.
/// Attempts to resize the data pointer if the index is out of range.
/// Returns zero if resizing failed.
uint64_t Box::getInt64(size_t index) {
index += payloadOffset;
if (index + 7 >= boxedSize()) {
if (!reserve(index, 0, 8)) {
return 0;
}
setInt64(0, index - payloadOffset);
}
uint64_t result = ntohl(((int *)(data + index))[0]);
result <<= 32;
result += ntohl(((int *)(data + index))[1]);
return result;
}
/// Sets the NULL-terminated string at the given index.
/// Will attempt to resize if the string doesn't fit.
/// Fails silently if resizing failed.
void Box::setString(std::string newData, size_t index) {
setString((char *)newData.c_str(), newData.size(), index);
}
/// Sets the NULL-terminated string at the given index.
/// Will attempt to resize if the string doesn't fit.
/// Fails silently if resizing failed.
void Box::setString(char * newData, size_t size, size_t index) {
index += payloadOffset;
if (index >= boxedSize()) {
if (!reserve(index, 0, 1)) {
return;
}
data[index] = 0;
}
if (getStringLen(index) != size) {
if (!reserve(index, getStringLen(index) + 1, size + 1)) {
return;
}
}
memcpy(data + index, newData, size + 1);
}
/// Gets the NULL-terminated string at the given index.
/// Will attempt to resize if the string is out of range.
/// Returns null if resizing failed.
char * Box::getString(size_t index) {
index += payloadOffset;
if (index >= boxedSize()) {
if (!reserve(index, 0, 1)) {
return 0;
}
data[index] = 0;
}
return data + index;
}
/// Returns the length of the NULL-terminated string at the given index.
/// Returns 0 if out of range.
size_t Box::getStringLen(size_t index) {
index += payloadOffset;
if (index >= boxedSize()) {
return 0;
}
return strlen(data + index);
}
/// Gets a reference to the box at the given index.
/// Do not store or copy this reference, for there will be raptors.
/// Will attempt to resize if out of range.
/// Returns an 8-byte error box if resizing failed.
Box & Box::getBox(size_t index) {
static Box retbox = Box((char *)"\000\000\000\010erro", false);
index += payloadOffset;
if (index + 8 > boxedSize()) {
if (!reserve(index, 0, 8)) {
retbox = Box((char *)"\000\000\000\010erro", false);
return retbox;
}
memcpy(data + index, "\000\000\000\010erro", 8);
}
retbox = Box(data + index, false);
return retbox;
}
/// Returns the size of the box at the given position.
/// Returns undefined values if there is no box at the given position.
/// Returns 0 if out of range.
size_t Box::getBoxLen(size_t index) {
if ((index + payloadOffset + 8) > boxedSize()) {
return 0;
}
return getBox(index).boxedSize();
}
/// Replaces the existing box at the given index by the new box newEntry.
/// Will resize if needed, will reserve new space if out of range.
void Box::setBox(Box & newEntry, size_t index) {
int oldlen = getBoxLen(index);
int newlen = newEntry.boxedSize();
if (oldlen != newlen && !reserve(index + payloadOffset, oldlen, newlen)) {
return;
}
memcpy(data + index + payloadOffset, newEntry.asBox(), newlen);
}
/// Attempts to reserve enough space for wanted bytes of data at given position, where current bytes of data is now reserved.
/// This will move any existing data behind the currently reserved space to the proper location after reserving.
/// \returns True on success, false otherwise.
bool Box::reserve(size_t position, size_t current, size_t wanted) {
if (current == wanted) {
return true;
}
if (position > boxedSize()) {
wanted += position - boxedSize();
}
if (current < wanted) {
//make bigger
if (boxedSize() + (wanted - current) > data_size) {
//realloc if managed, otherwise fail
if (!managed) {
return false;
}
void * ret = realloc(data, boxedSize() + (wanted - current));
if (!ret) {
return false;
}
data = (char *)ret;
memset(data + boxedSize(), 0, wanted - current); //initialize to 0
data_size = boxedSize() + (wanted - current);
}
}
//move data behind, if any
if (boxedSize() > (position + current)) {
memmove(data + position + wanted, data + position + current, boxedSize() - (position + current));
}
//calculate and set new size
if (payloadOffset != 16) {
int newSize = boxedSize() + (wanted - current);
((int *)data)[0] = htonl(newSize);
}
return true;
}
fullBox::fullBox() {
setVersion(0);
}
void fullBox::setVersion(char newVersion) {
setInt8(newVersion, 0);
}
char fullBox::getVersion() {
return getInt8(0);
}
void fullBox::setFlags(uint32_t newFlags) {
setInt24(newFlags, 1);
}
uint32_t fullBox::getFlags() {
return getInt24(1);
}
std::string fullBox::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent + 1, ' ') << "Version: " << (int)getVersion() << std::endl;
r << std::string(indent + 1, ' ') << "Flags: " << getFlags() << std::endl;
return r.str();
}
containerBox::containerBox() {
}
uint32_t containerBox::getContentCount() {
int res = 0;
unsigned int tempLoc = 0;
while (tempLoc < boxedSize() - 8) {
res++;
tempLoc += Box(getBox(tempLoc).asBox(), false).boxedSize();
}
return res;
}
void containerBox::setContent(Box & newContent, uint32_t no) {
int tempLoc = 0;
unsigned int contentCount = getContentCount();
for (unsigned int i = 0; i < no; i++) {
if (i < contentCount) {
tempLoc += getBoxLen(tempLoc);
} else {
if (!reserve(tempLoc, 0, (no - contentCount) * 8)) {
return;
};
memset(data + tempLoc, 0, (no - contentCount) * 8);
tempLoc += (no - contentCount) * 8;
break;
}
}
setBox(newContent, tempLoc);
}
Box & containerBox::getContent(uint32_t no) {
static Box ret = Box((char *)"\000\000\000\010erro", false);
if (no > getContentCount()) {
return ret;
}
unsigned int i = 0;
int tempLoc = 0;
while (i < no) {
tempLoc += getBoxLen(tempLoc);
i++;
}
return getBox(tempLoc);
}
std::string containerBox::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent, ' ') << "[" << getType() << "] Container Box (" << boxedSize() << ")" << std::endl;
Box curBox;
int tempLoc = 0;
int contentCount = getContentCount();
for (int i = 0; i < contentCount; i++) {
curBox = getContent(i);
r << curBox.toPrettyString(indent + 1);
tempLoc += getBoxLen(tempLoc);
}
return r.str();
}
uint32_t containerFullBox::getContentCount() {
int res = 0;
unsigned int tempLoc = 4;
while (tempLoc < boxedSize() - 8) {
res++;
tempLoc += getBoxLen(tempLoc);
}
return res;
}
void containerFullBox::setContent(Box & newContent, uint32_t no) {
int tempLoc = 4;
unsigned int contentCount = getContentCount();
for (unsigned int i = 0; i < no; i++) {
if (i < contentCount) {
tempLoc += getBoxLen(tempLoc);
} else {
if (!reserve(tempLoc, 0, (no - contentCount) * 8)) {
return;
};
memset(data + tempLoc, 0, (no - contentCount) * 8);
tempLoc += (no - contentCount) * 8;
break;
}
}
setBox(newContent, tempLoc);
}
Box & containerFullBox::getContent(uint32_t no) {
static Box ret = Box((char *)"\000\000\000\010erro", false);
if (no > getContentCount()) {
return ret;
}
unsigned int i = 0;
int tempLoc = 4;
while (i < no) {
tempLoc += getBoxLen(tempLoc);
i++;
}
return getBox(tempLoc);
}
std::string containerFullBox::toPrettyCFBString(uint32_t indent, std::string boxName) {
std::stringstream r;
r << std::string(indent, ' ') << boxName << " (" << boxedSize() << ")" << std::endl;
r << fullBox::toPrettyString(indent);
Box curBox;
int tempLoc = 4;
int contentCount = getContentCount();
for (int i = 0; i < contentCount; i++) {
curBox = getContent(i);
r << curBox.toPrettyString(indent + 1);
tempLoc += getBoxLen(tempLoc);
}
return r.str();
}
}

95
lib/mp4.h Normal file
View file

@ -0,0 +1,95 @@
#pragma once
#include <string>
#include <vector>
#include <set>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <stdint.h>
#include <sstream>
#include <deque>
#include <algorithm>
#include <vector>
#include "json.h"
#include "dtsc.h"
/// Contains all MP4 format related code.
namespace MP4 {
std::string readBoxType(FILE * newData);
bool skipBox(FILE * newData);
class Box {
public:
Box(char * datapointer = 0, bool manage = true);
Box(const Box & rs);
Box & operator = (const Box & rs);
~Box();
std::string getType();
bool isType(const char * boxType);
bool read(FILE * newData);
bool read(std::string & newData);
uint64_t boxedSize();
uint64_t payloadSize();
char * asBox();
char * payload();
void clear();
std::string toPrettyString(uint32_t indent = 0);
protected:
//integer functions
void setInt8(char newData, size_t index);
char getInt8(size_t index);
void setInt16(short newData, size_t index);
short getInt16(size_t index);
void setInt24(uint32_t newData, size_t index);
uint32_t getInt24(size_t index);
void setInt32(uint32_t newData, size_t index);
uint32_t getInt32(size_t index);
void setInt64(uint64_t newData, size_t index);
uint64_t getInt64(size_t index);
//string functions
void setString(std::string newData, size_t index);
void setString(char * newData, size_t size, size_t index);
char * getString(size_t index);
size_t getStringLen(size_t index);
//box functions
Box & getBox(size_t index);
size_t getBoxLen(size_t index);
void setBox(Box & newEntry, size_t index);
//data functions
bool reserve(size_t position, size_t current, size_t wanted);
//internal variables
char * data; ///< Holds the data of this box
unsigned int data_size; ///< Currently reserved size
bool managed; ///< If false, will not attempt to resize/free the data pointer.
unsigned int payloadOffset; ///<The offset of the payload with regards to the data
};
//Box Class
class fullBox: public Box {
public:
fullBox();
void setVersion(char newVersion);
char getVersion();
void setFlags(uint32_t newFlags);
uint32_t getFlags();
std::string toPrettyString(uint32_t indent = 0);
};
class containerBox: public Box {
public:
containerBox();
uint32_t getContentCount();
void setContent(Box & newContent, uint32_t no);
Box & getContent(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
class containerFullBox: public fullBox {
public:
uint32_t getContentCount();
void setContent(Box & newContent, uint32_t no);
Box & getContent(uint32_t no);
std::string toPrettyCFBString(uint32_t indent, std::string boxName);
};
}

929
lib/mp4_adobe.cpp Normal file
View file

@ -0,0 +1,929 @@
#include "mp4_adobe.h"
namespace MP4 {
ABST::ABST() {
memcpy(data + 4, "abst", 4);
setVersion(0);
setFlags(0);
setBootstrapinfoVersion(0);
setProfile(0);
setLive(1);
setUpdate(0);
setTimeScale(1000);
setCurrentMediaTime(0);
setSmpteTimeCodeOffset(0);
std::string empty;
setMovieIdentifier(empty);
setInt8(0, 30); //set serverentrycount to 0
setInt8(0, 31); //set qualityentrycount to 0
setDrmData(empty);
setMetaData(empty);
}
void ABST::setVersion(char newVersion) {
setInt8(newVersion, 0);
}
char ABST::getVersion() {
return getInt8(0);
}
void ABST::setFlags(uint32_t newFlags) {
setInt24(newFlags, 1);
}
uint32_t ABST::getFlags() {
return getInt24(1);
}
void ABST::setBootstrapinfoVersion(uint32_t newVersion) {
setInt32(newVersion, 4);
}
uint32_t ABST::getBootstrapinfoVersion() {
return getInt32(4);
}
void ABST::setProfile(char newProfile) {
//profile = bit 1 and 2 of byte 8.
setInt8((getInt8(8) & 0x3F) + ((newProfile & 0x03) << 6), 8);
}
char ABST::getProfile() {
return (getInt8(8) & 0xC0);
}
;
void ABST::setLive(bool newLive) {
//live = bit 4 of byte 8.
setInt8((getInt8(8) & 0xDF) + (newLive ? 0x10 : 0), 8);
}
bool ABST::getLive() {
return (getInt8(8) & 0x10);
}
void ABST::setUpdate(bool newUpdate) {
//update = bit 5 of byte 8.
setInt8((getInt8(8) & 0xEF) + (newUpdate ? 0x08 : 0), 8);
}
bool ABST::getUpdate() {
return (getInt8(8) & 0x08);
}
void ABST::setTimeScale(uint32_t newScale) {
setInt32(newScale, 9);
}
uint32_t ABST::getTimeScale() {
return getInt32(9);
}
void ABST::setCurrentMediaTime(uint64_t newTime) {
setInt64(newTime, 13);
}
uint64_t ABST::getCurrentMediaTime() {
return getInt64(13);
}
void ABST::setSmpteTimeCodeOffset(uint64_t newTime) {
setInt64(newTime, 21);
}
uint64_t ABST::getSmpteTimeCodeOffset() {
return getInt64(21);
}
void ABST::setMovieIdentifier(std::string & newIdentifier) {
setString(newIdentifier, 29);
}
char * ABST::getMovieIdentifier() {
return getString(29);
}
uint32_t ABST::getServerEntryCount() {
int countLoc = 29 + getStringLen(29) + 1;
return getInt8(countLoc);
}
void ABST::setServerEntry(std::string & newEntry, uint32_t no) {
int countLoc = 29 + getStringLen(29) + 1;
int tempLoc = countLoc + 1;
//attempt to reach the wanted position
unsigned int i;
for (i = 0; i < getInt8(countLoc) && i < no; ++i) {
tempLoc += getStringLen(tempLoc) + 1;
}
//we are now either at the end, or at the right position
//let's reserve any unreserved space...
if (no + 1 > getInt8(countLoc)) {
int amount = no + 1 - getInt8(countLoc);
if (!reserve(payloadOffset + tempLoc, 0, amount)) {
return;
};
memset(data + payloadOffset + tempLoc, 0, amount);
setInt8(no + 1, countLoc); //set new qualityEntryCount
tempLoc += no - i;
}
//now, tempLoc is at position for string number no, and we have at least 1 byte reserved.
setString(newEntry, tempLoc);
}
///\return Empty string if no > serverEntryCount(), serverEntry[no] otherwise.
const char * ABST::getServerEntry(uint32_t no) {
if (no + 1 > getServerEntryCount()) {
return "";
}
int tempLoc = 29 + getStringLen(29) + 1 + 1; //position of first entry
for (unsigned int i = 0; i < no; i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
return getString(tempLoc);
}
uint32_t ABST::getQualityEntryCount() {
int countLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
countLoc += getStringLen(countLoc) + 1;
}
return getInt8(countLoc);
}
void ABST::setQualityEntry(std::string & newEntry, uint32_t no) {
int countLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
countLoc += getStringLen(countLoc) + 1;
}
int tempLoc = countLoc + 1;
//attempt to reach the wanted position
unsigned int i;
for (i = 0; i < getInt8(countLoc) && i < no; ++i) {
tempLoc += getStringLen(tempLoc) + 1;
}
//we are now either at the end, or at the right position
//let's reserve any unreserved space...
if (no + 1 > getInt8(countLoc)) {
int amount = no + 1 - getInt8(countLoc);
if (!reserve(payloadOffset + tempLoc, 0, amount)) {
return;
};
memset(data + payloadOffset + tempLoc, 0, amount);
setInt8(no + 1, countLoc); //set new qualityEntryCount
tempLoc += no - i;
}
//now, tempLoc is at position for string number no, and we have at least 1 byte reserved.
setString(newEntry, tempLoc);
}
const char * ABST::getQualityEntry(uint32_t no) {
if (no > getQualityEntryCount()) {
return "";
}
int tempLoc = 29 + getStringLen(29) + 1 + 1; //position of serverentries;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += 1; //first qualityentry
for (unsigned int i = 0; i < no; i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
return getString(tempLoc);
}
void ABST::setDrmData(std::string newDrm) {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
setString(newDrm, tempLoc);
}
char * ABST::getDrmData() {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
return getString(tempLoc);
}
void ABST::setMetaData(std::string newMetaData) {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1;
setString(newMetaData, tempLoc);
}
char * ABST::getMetaData() {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1;
return getString(tempLoc);
}
uint32_t ABST::getSegmentRunTableCount() {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1; //DrmData
tempLoc += getStringLen(tempLoc) + 1; //MetaData
return getInt8(tempLoc);
}
void ABST::setSegmentRunTable(ASRT & newSegment, uint32_t no) {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1; //DrmData
tempLoc += getStringLen(tempLoc) + 1; //MetaData
int countLoc = tempLoc;
tempLoc++; //skip segmentRuntableCount
//attempt to reach the wanted position
unsigned int i;
for (i = 0; i < getInt8(countLoc) && i < no; ++i) {
tempLoc += getBoxLen(tempLoc);
}
//we are now either at the end, or at the right position
//let's reserve any unreserved space...
if (no + 1 > getInt8(countLoc)) {
int amount = no + 1 - getInt8(countLoc);
if (!reserve(payloadOffset + tempLoc, 0, amount * 8)) {
return;
};
//set empty erro boxes as contents
for (int j = 0; j < amount; ++j) {
memcpy(data + payloadOffset + tempLoc + j * 8, "\000\000\000\010erro", 8);
}
setInt8(no + 1, countLoc); //set new count
tempLoc += (no - i) * 8;
}
//now, tempLoc is at position for string number no, and we have at least an erro box reserved.
setBox(newSegment, tempLoc);
}
ASRT & ABST::getSegmentRunTable(uint32_t no) {
static Box result;
if (no > getSegmentRunTableCount()) {
static Box res;
return (ASRT &)res;
}
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1; //DrmData
tempLoc += getStringLen(tempLoc) + 1; //MetaData
tempLoc++; //segmentRuntableCount
for (unsigned int i = 0; i < no; ++i) {
tempLoc += getBoxLen(tempLoc);
}
return (ASRT &)getBox(tempLoc);
}
uint32_t ABST::getFragmentRunTableCount() {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1; //DrmData
tempLoc += getStringLen(tempLoc) + 1; //MetaData
for (unsigned int i = getInt8(tempLoc++); i != 0; --i) {
tempLoc += getBoxLen(tempLoc);
}
return getInt8(tempLoc);
}
void ABST::setFragmentRunTable(AFRT & newFragment, uint32_t no) {
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1; //DrmData
tempLoc += getStringLen(tempLoc) + 1; //MetaData
for (unsigned int i = getInt8(tempLoc++); i != 0; --i) {
tempLoc += getBoxLen(tempLoc);
}
int countLoc = tempLoc;
tempLoc++;
//attempt to reach the wanted position
unsigned int i;
for (i = 0; i < getInt8(countLoc) && i < no; ++i) {
tempLoc += getBoxLen(tempLoc);
}
//we are now either at the end, or at the right position
//let's reserve any unreserved space...
if (no + 1 > getInt8(countLoc)) {
unsigned int amount = no + 1 - getInt8(countLoc);
if (!reserve(payloadOffset + tempLoc, 0, amount * 8)) {
return;
};
//set empty erro boxes as contents
for (unsigned int j = 0; j < amount; ++j) {
memcpy(data + payloadOffset + tempLoc + j * 8, "\000\000\000\010erro", 8);
}
setInt8(no + 1, countLoc); //set new count
tempLoc += (no - i) * 8;
}
//now, tempLoc is at position for string number no, and we have at least 1 byte reserved.
setBox(newFragment, tempLoc);
}
AFRT & ABST::getFragmentRunTable(uint32_t no) {
static Box result;
if (no >= getFragmentRunTableCount()) {
static Box res;
return (AFRT &)res;
}
uint32_t tempLoc = 29 + getStringLen(29) + 1 + 1;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc++;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += getStringLen(tempLoc) + 1; //DrmData
tempLoc += getStringLen(tempLoc) + 1; //MetaData
for (unsigned int i = getInt8(tempLoc++); i != 0; --i) {
tempLoc += getBoxLen(tempLoc);
}
tempLoc++;
for (unsigned int i = 0; i < no; i++) {
tempLoc += getBoxLen(tempLoc);
}
return (AFRT &)getBox(tempLoc);
}
std::string ABST::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent, ' ') << "[abst] Bootstrap Info (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "Version " << (int)getVersion() << std::endl;
r << std::string(indent + 1, ' ') << "BootstrapinfoVersion " << getBootstrapinfoVersion() << std::endl;
r << std::string(indent + 1, ' ') << "Profile " << (int)getProfile() << std::endl;
if (getLive()) {
r << std::string(indent + 1, ' ') << "Live" << std::endl;
} else {
r << std::string(indent + 1, ' ') << "Recorded" << std::endl;
}
if (getUpdate()) {
r << std::string(indent + 1, ' ') << "Update" << std::endl;
} else {
r << std::string(indent + 1, ' ') << "Replacement or new table" << std::endl;
}
r << std::string(indent + 1, ' ') << "Timescale " << getTimeScale() << std::endl;
r << std::string(indent + 1, ' ') << "CurrMediaTime " << getCurrentMediaTime() << std::endl;
r << std::string(indent + 1, ' ') << "SmpteTimeCodeOffset " << getSmpteTimeCodeOffset() << std::endl;
r << std::string(indent + 1, ' ') << "MovieIdentifier " << getMovieIdentifier() << std::endl;
r << std::string(indent + 1, ' ') << "ServerEntryTable (" << getServerEntryCount() << ")" << std::endl;
for (unsigned int i = 0; i < getServerEntryCount(); i++) {
r << std::string(indent + 2, ' ') << i << ": " << getServerEntry(i) << std::endl;
}
r << std::string(indent + 1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
r << std::string(indent + 2, ' ') << i << ": " << getQualityEntry(i) << std::endl;
}
r << std::string(indent + 1, ' ') << "DrmData " << getDrmData() << std::endl;
r << std::string(indent + 1, ' ') << "MetaData " << getMetaData() << std::endl;
r << std::string(indent + 1, ' ') << "SegmentRunTableEntries (" << getSegmentRunTableCount() << ")" << std::endl;
for (uint32_t i = 0; i < getSegmentRunTableCount(); i++) {
r << ((Box)getSegmentRunTable(i)).toPrettyString(indent + 2);
}
r << std::string(indent + 1, ' ') + "FragmentRunTableEntries (" << getFragmentRunTableCount() << ")" << std::endl;
for (uint32_t i = 0; i < getFragmentRunTableCount(); i++) {
r << ((Box)getFragmentRunTable(i)).toPrettyString(indent + 2);
}
return r.str();
}
AFRT::AFRT() {
memcpy(data + 4, "afrt", 4);
setVersion(0);
setUpdate(0);
setTimeScale(1000);
}
void AFRT::setVersion(char newVersion) {
setInt8(newVersion, 0);
}
uint32_t AFRT::getVersion() {
return getInt8(0);
}
void AFRT::setUpdate(uint32_t newUpdate) {
setInt24(newUpdate, 1);
}
uint32_t AFRT::getUpdate() {
return getInt24(1);
}
void AFRT::setTimeScale(uint32_t newScale) {
setInt32(newScale, 4);
}
uint32_t AFRT::getTimeScale() {
return getInt32(4);
}
uint32_t AFRT::getQualityEntryCount() {
return getInt8(8);
}
void AFRT::setQualityEntry(std::string & newEntry, uint32_t no) {
int countLoc = 8;
int tempLoc = countLoc + 1;
//attempt to reach the wanted position
unsigned int i;
for (i = 0; i < getQualityEntryCount() && i < no; ++i) {
tempLoc += getStringLen(tempLoc) + 1;
}
//we are now either at the end, or at the right position
//let's reserve any unreserved space...
if (no + 1 > getQualityEntryCount()) {
int amount = no + 1 - getQualityEntryCount();
if (!reserve(payloadOffset + tempLoc, 0, amount)) {
return;
};
memset(data + payloadOffset + tempLoc, 0, amount);
setInt8(no + 1, countLoc); //set new qualityEntryCount
tempLoc += no - i;
}
//now, tempLoc is at position for string number no, and we have at least 1 byte reserved.
setString(newEntry, tempLoc);
}
const char * AFRT::getQualityEntry(uint32_t no) {
if (no + 1 > getQualityEntryCount()) {
return "";
}
int tempLoc = 9; //position of first quality entry
for (unsigned int i = 0; i < no; i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
return getString(tempLoc);
}
uint32_t AFRT::getFragmentRunCount() {
int tempLoc = 9;
for (unsigned int i = 0; i < getQualityEntryCount(); ++i) {
tempLoc += getStringLen(tempLoc) + 1;
}
return getInt32(tempLoc);
}
void AFRT::setFragmentRun(afrt_runtable newRun, uint32_t no) {
int tempLoc = 9;
for (unsigned int i = 0; i < getQualityEntryCount(); ++i) {
tempLoc += getStringLen(tempLoc) + 1;
}
int countLoc = tempLoc;
unsigned int count = getInt32(countLoc);
tempLoc += 4;
for (unsigned int i = 0; i < no; i++) {
if (i + 1 > count) {
setInt32(0, tempLoc);
setInt64(0, tempLoc + 4);
setInt32(1, tempLoc + 12);
}
if (getInt32(tempLoc + 12) == 0) {
tempLoc += 17;
} else {
tempLoc += 16;
}
}
setInt32(newRun.firstFragment, tempLoc);
setInt64(newRun.firstTimestamp, tempLoc + 4);
setInt32(newRun.duration, tempLoc + 12);
if (newRun.duration == 0) {
setInt8(newRun.discontinuity, tempLoc + 16);
}
if (count < no + 1) {
setInt32(no + 1, countLoc);
}
}
afrt_runtable AFRT::getFragmentRun(uint32_t no) {
afrt_runtable res;
if (no > getFragmentRunCount()) {
return res;
}
int tempLoc = 9;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += 4;
for (unsigned int i = 0; i < no; i++) {
if (getInt32(tempLoc + 12) == 0) {
tempLoc += 17;
} else {
tempLoc += 16;
}
}
res.firstFragment = getInt32(tempLoc);
res.firstTimestamp = getInt64(tempLoc + 4);
res.duration = getInt32(tempLoc + 12);
if (res.duration) {
res.discontinuity = getInt8(tempLoc + 16);
} else {
res.discontinuity = 0;
}
return res;
}
std::string AFRT::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent, ' ') << "[afrt] Fragment Run Table (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "Version " << (int)getVersion() << std::endl;
if (getUpdate()) {
r << std::string(indent + 1, ' ') << "Update" << std::endl;
} else {
r << std::string(indent + 1, ' ') << "Replacement or new table" << std::endl;
}
r << std::string(indent + 1, ' ') << "Timescale " << getTimeScale() << std::endl;
r << std::string(indent + 1, ' ') << "QualitySegmentUrlModifiers (" << getQualityEntryCount() << ")" << std::endl;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
r << std::string(indent + 2, ' ') << i << ": " << getQualityEntry(i) << std::endl;
}
r << std::string(indent + 1, ' ') << "FragmentRunEntryTable (" << getFragmentRunCount() << ")" << std::endl;
for (unsigned int i = 0; i < getFragmentRunCount(); i++) {
afrt_runtable myRun = getFragmentRun(i);
if (myRun.duration) {
r << std::string(indent + 2, ' ') << i << ": " << myRun.firstFragment << " is at " << ((double)myRun.firstTimestamp / (double)getTimeScale())
<< "s, " << ((double)myRun.duration / (double)getTimeScale()) << "s per fragment." << std::endl;
} else {
r << std::string(indent + 2, ' ') << i << ": " << myRun.firstFragment << " is at " << ((double)myRun.firstTimestamp / (double)getTimeScale())
<< "s, discontinuity type " << myRun.discontinuity << std::endl;
}
}
return r.str();
}
ASRT::ASRT() {
memcpy(data + 4, "asrt", 4);
setVersion(0);
setUpdate(0);
}
void ASRT::setVersion(char newVersion) {
setInt8(newVersion, 0);
}
uint32_t ASRT::getVersion() {
return getInt8(0);
}
void ASRT::setUpdate(uint32_t newUpdate) {
setInt24(newUpdate, 1);
}
uint32_t ASRT::getUpdate() {
return getInt24(1);
}
uint32_t ASRT::getQualityEntryCount() {
return getInt8(4);
}
void ASRT::setQualityEntry(std::string & newEntry, uint32_t no) {
int countLoc = 4;
int tempLoc = countLoc + 1;
//attempt to reach the wanted position
unsigned int i;
for (i = 0; i < getQualityEntryCount() && i < no; ++i) {
tempLoc += getStringLen(tempLoc) + 1;
}
//we are now either at the end, or at the right position
//let's reserve any unreserved space...
if (no + 1 > getQualityEntryCount()) {
int amount = no + 1 - getQualityEntryCount();
if (!reserve(payloadOffset + tempLoc, 0, amount)) {
return;
};
memset(data + payloadOffset + tempLoc, 0, amount);
setInt8(no + 1, countLoc); //set new qualityEntryCount
tempLoc += no - i;
}
//now, tempLoc is at position for string number no, and we have at least 1 byte reserved.
setString(newEntry, tempLoc);
}
const char * ASRT::getQualityEntry(uint32_t no) {
if (no > getQualityEntryCount()) {
return "";
}
int tempLoc = 5; //position of qualityentry count;
for (unsigned int i = 0; i < no; i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
return getString(tempLoc);
}
uint32_t ASRT::getSegmentRunEntryCount() {
int tempLoc = 5; //position of qualityentry count;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
return getInt32(tempLoc);
}
void ASRT::setSegmentRun(uint32_t firstSegment, uint32_t fragmentsPerSegment, uint32_t no) {
int tempLoc = 5; //position of qualityentry count;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
tempLoc += getStringLen(tempLoc) + 1;
}
int countLoc = tempLoc;
tempLoc += 4 + no * 8;
if (no + 1 > getInt32(countLoc)) {
setInt32(no + 1, countLoc); //set new qualityEntryCount
}
setInt32(firstSegment, tempLoc);
setInt32(fragmentsPerSegment, tempLoc + 4);
}
asrt_runtable ASRT::getSegmentRun(uint32_t no) {
asrt_runtable res;
if (no >= getSegmentRunEntryCount()) {
return res;
}
int tempLoc = 5; //position of qualityentry count;
for (unsigned int i = 0; i < getQualityEntryCount(); ++i) {
tempLoc += getStringLen(tempLoc) + 1;
}
tempLoc += 4 + 8 * no;
res.firstSegment = getInt32(tempLoc);
res.fragmentsPerSegment = getInt32(tempLoc + 4);
return res;
}
std::string ASRT::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent, ' ') << "[asrt] Segment Run Table (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "Version " << getVersion() << std::endl;
if (getUpdate()) {
r << std::string(indent + 1, ' ') << "Update" << std::endl;
} else {
r << std::string(indent + 1, ' ') << "Replacement or new table" << std::endl;
}
r << std::string(indent + 1, ' ') << "QualityEntryTable (" << getQualityEntryCount() << ")" << std::endl;
for (unsigned int i = 0; i < getQualityEntryCount(); i++) {
r << std::string(indent + 2, ' ') << i << ": " << getQualityEntry(i) << std::endl;
}
r << std::string(indent + 1, ' ') << "SegmentRunEntryTable (" << getSegmentRunEntryCount() << ")" << std::endl;
for (unsigned int i = 0; i < getSegmentRunEntryCount(); i++) {
r << std::string(indent + 2, ' ') << i << ": First=" << getSegmentRun(i).firstSegment << ", FragmentsPerSegment="
<< getSegmentRun(i).fragmentsPerSegment << std::endl;
}
return r.str();
}
AFRA::AFRA() {
memcpy(data + 4, "afra", 4);
setInt32(0, 9); //entrycount = 0
setFlags(0);
}
void AFRA::setVersion(uint32_t newVersion) {
setInt8(newVersion, 0);
}
uint32_t AFRA::getVersion() {
return getInt8(0);
}
void AFRA::setFlags(uint32_t newFlags) {
setInt24(newFlags, 1);
}
uint32_t AFRA::getFlags() {
return getInt24(1);
}
void AFRA::setLongIDs(bool newVal) {
if (newVal) {
setInt8((getInt8(4) & 0x7F) + 0x80, 4);
} else {
setInt8((getInt8(4) & 0x7F), 4);
}
}
bool AFRA::getLongIDs() {
return getInt8(4) & 0x80;
}
void AFRA::setLongOffsets(bool newVal) {
if (newVal) {
setInt8((getInt8(4) & 0xBF) + 0x40, 4);
} else {
setInt8((getInt8(4) & 0xBF), 4);
}
}
bool AFRA::getLongOffsets() {
return getInt8(4) & 0x40;
}
void AFRA::setGlobalEntries(bool newVal) {
if (newVal) {
setInt8((getInt8(4) & 0xDF) + 0x20, 4);
} else {
setInt8((getInt8(4) & 0xDF), 4);
}
}
bool AFRA::getGlobalEntries() {
return getInt8(4) & 0x20;
}
void AFRA::setTimeScale(uint32_t newVal) {
setInt32(newVal, 5);
}
uint32_t AFRA::getTimeScale() {
return getInt32(5);
}
uint32_t AFRA::getEntryCount() {
return getInt32(9);
}
void AFRA::setEntry(afraentry newEntry, uint32_t no) {
int entrysize = 12;
if (getLongOffsets()) {
entrysize = 16;
}
setInt64(newEntry.time, 13 + entrysize * no);
if (getLongOffsets()) {
setInt64(newEntry.offset, 21 + entrysize * no);
} else {
setInt32(newEntry.offset, 21 + entrysize * no);
}
if (no + 1 > getEntryCount()) {
setInt32(no + 1, 9);
}
}
afraentry AFRA::getEntry(uint32_t no) {
afraentry ret;
int entrysize = 12;
if (getLongOffsets()) {
entrysize = 16;
}
ret.time = getInt64(13 + entrysize * no);
if (getLongOffsets()) {
ret.offset = getInt64(21 + entrysize * no);
} else {
ret.offset = getInt32(21 + entrysize * no);
}
return ret;
}
uint32_t AFRA::getGlobalEntryCount() {
if (!getGlobalEntries()) {
return 0;
}
int entrysize = 12;
if (getLongOffsets()) {
entrysize = 16;
}
return getInt32(13 + entrysize * getEntryCount());
}
void AFRA::setGlobalEntry(globalafraentry newEntry, uint32_t no) {
int offset = 13 + 12 * getEntryCount() + 4;
if (getLongOffsets()) {
offset = 13 + 16 * getEntryCount() + 4;
}
int entrysize = 20;
if (getLongIDs()) {
entrysize += 4;
}
if (getLongOffsets()) {
entrysize += 8;
}
setInt64(newEntry.time, offset + entrysize * no);
if (getLongIDs()) {
setInt32(newEntry.segment, offset + entrysize * no + 8);
setInt32(newEntry.fragment, offset + entrysize * no + 12);
} else {
setInt16(newEntry.segment, offset + entrysize * no + 8);
setInt16(newEntry.fragment, offset + entrysize * no + 10);
}
if (getLongOffsets()) {
setInt64(newEntry.afraoffset, offset + entrysize * no + entrysize - 16);
setInt64(newEntry.offsetfromafra, offset + entrysize * no + entrysize - 8);
} else {
setInt32(newEntry.afraoffset, offset + entrysize * no + entrysize - 8);
setInt32(newEntry.offsetfromafra, offset + entrysize * no + entrysize - 4);
}
if (getInt32(offset - 4) < no + 1) {
setInt32(no + 1, offset - 4);
}
}
globalafraentry AFRA::getGlobalEntry(uint32_t no) {
globalafraentry ret;
int offset = 13 + 12 * getEntryCount() + 4;
if (getLongOffsets()) {
offset = 13 + 16 * getEntryCount() + 4;
}
int entrysize = 20;
if (getLongIDs()) {
entrysize += 4;
}
if (getLongOffsets()) {
entrysize += 8;
}
ret.time = getInt64(offset + entrysize * no);
if (getLongIDs()) {
ret.segment = getInt32(offset + entrysize * no + 8);
ret.fragment = getInt32(offset + entrysize * no + 12);
} else {
ret.segment = getInt16(offset + entrysize * no + 8);
ret.fragment = getInt16(offset + entrysize * no + 10);
}
if (getLongOffsets()) {
ret.afraoffset = getInt64(offset + entrysize * no + entrysize - 16);
ret.offsetfromafra = getInt64(offset + entrysize * no + entrysize - 8);
} else {
ret.afraoffset = getInt32(offset + entrysize * no + entrysize - 8);
ret.offsetfromafra = getInt32(offset + entrysize * no + entrysize - 4);
}
return ret;
}
std::string AFRA::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent, ' ') << "[afra] Fragment Random Access (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "Version " << getVersion() << std::endl;
r << std::string(indent + 1, ' ') << "Flags " << getFlags() << std::endl;
r << std::string(indent + 1, ' ') << "Long IDs " << getLongIDs() << std::endl;
r << std::string(indent + 1, ' ') << "Long Offsets " << getLongOffsets() << std::endl;
r << std::string(indent + 1, ' ') << "Global Entries " << getGlobalEntries() << std::endl;
r << std::string(indent + 1, ' ') << "TimeScale " << getTimeScale() << std::endl;
uint32_t count = getEntryCount();
r << std::string(indent + 1, ' ') << "Entries (" << count << ") " << std::endl;
for (uint32_t i = 0; i < count; ++i) {
afraentry tmpent = getEntry(i);
r << std::string(indent + 1, ' ') << i << ": Time " << tmpent.time << ", Offset " << tmpent.offset << std::endl;
}
if (getGlobalEntries()) {
count = getGlobalEntryCount();
r << std::string(indent + 1, ' ') << "Global Entries (" << count << ") " << std::endl;
for (uint32_t i = 0; i < count; ++i) {
globalafraentry tmpent = getGlobalEntry(i);
r << std::string(indent + 1, ' ') << i << ": T " << tmpent.time << ", S" << tmpent.segment << "F" << tmpent.fragment << ", "
<< tmpent.afraoffset << "/" << tmpent.offsetfromafra << std::endl;
}
}
return r.str();
}
}

139
lib/mp4_adobe.h Normal file
View file

@ -0,0 +1,139 @@
#pragma once
#include "mp4.h"
#include <stdint.h>
namespace MP4 {
//class Box;
struct afrt_runtable {
uint32_t firstFragment;
uint64_t firstTimestamp;
uint32_t duration;
uint32_t discontinuity;
};
//fragmentRun
/// AFRT Box class
class AFRT: public Box {
public:
AFRT();
void setVersion(char newVersion);
uint32_t getVersion();
void setUpdate(uint32_t newUpdate);
uint32_t getUpdate();
void setTimeScale(uint32_t newScale);
uint32_t getTimeScale();
uint32_t getQualityEntryCount();
void setQualityEntry(std::string & newQuality, uint32_t no);
const char * getQualityEntry(uint32_t no);
uint32_t getFragmentRunCount();
void setFragmentRun(afrt_runtable newRun, uint32_t no);
afrt_runtable getFragmentRun(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
//AFRT Box
struct asrt_runtable {
uint32_t firstSegment;
uint32_t fragmentsPerSegment;
};
/// ASRT Box class
class ASRT: public Box {
public:
ASRT();
void setVersion(char newVersion);
uint32_t getVersion();
void setUpdate(uint32_t newUpdate);
uint32_t getUpdate();
uint32_t getQualityEntryCount();
void setQualityEntry(std::string & newQuality, uint32_t no);
const char * getQualityEntry(uint32_t no);
uint32_t getSegmentRunEntryCount();
void setSegmentRun(uint32_t firstSegment, uint32_t fragmentsPerSegment, uint32_t no);
asrt_runtable getSegmentRun(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
//ASRT Box
/// ABST Box class
class ABST: public Box {
public:
ABST();
void setVersion(char newVersion);
char getVersion();
void setFlags(uint32_t newFlags);
uint32_t getFlags();
void setBootstrapinfoVersion(uint32_t newVersion);
uint32_t getBootstrapinfoVersion();
void setProfile(char newProfile);
char getProfile();
void setLive(bool newLive);
bool getLive();
void setUpdate(bool newUpdate);
bool getUpdate();
void setTimeScale(uint32_t newTimeScale);
uint32_t getTimeScale();
void setCurrentMediaTime(uint64_t newTime);
uint64_t getCurrentMediaTime();
void setSmpteTimeCodeOffset(uint64_t newTime);
uint64_t getSmpteTimeCodeOffset();
void setMovieIdentifier(std::string & newIdentifier);
char * getMovieIdentifier();
uint32_t getServerEntryCount();
void setServerEntry(std::string & entry, uint32_t no);
const char * getServerEntry(uint32_t no);
uint32_t getQualityEntryCount();
void setQualityEntry(std::string & entry, uint32_t no);
const char * getQualityEntry(uint32_t no);
void setDrmData(std::string newDrm);
char * getDrmData();
void setMetaData(std::string newMetaData);
char * getMetaData();
uint32_t getSegmentRunTableCount();
void setSegmentRunTable(ASRT & table, uint32_t no);
ASRT & getSegmentRunTable(uint32_t no);
uint32_t getFragmentRunTableCount();
void setFragmentRunTable(AFRT & table, uint32_t no);
AFRT & getFragmentRunTable(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
//ABST Box
struct afraentry {
uint64_t time;
uint64_t offset;
};
struct globalafraentry {
uint64_t time;
uint32_t segment;
uint32_t fragment;
uint64_t afraoffset;
uint64_t offsetfromafra;
};
class AFRA: public Box {
public:
AFRA();
void setVersion(uint32_t newVersion);
uint32_t getVersion();
void setFlags(uint32_t newFlags);
uint32_t getFlags();
void setLongIDs(bool newVal);
bool getLongIDs();
void setLongOffsets(bool newVal);
bool getLongOffsets();
void setGlobalEntries(bool newVal);
bool getGlobalEntries();
void setTimeScale(uint32_t newVal);
uint32_t getTimeScale();
uint32_t getEntryCount();
void setEntry(afraentry newEntry, uint32_t no);
afraentry getEntry(uint32_t no);
uint32_t getGlobalEntryCount();
void setGlobalEntry(globalafraentry newEntry, uint32_t no);
globalafraentry getGlobalEntry(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
}

3001
lib/mp4_generic.cpp Normal file

File diff suppressed because it is too large Load diff

677
lib/mp4_generic.h Normal file
View file

@ -0,0 +1,677 @@
#include "mp4.h"
namespace MP4 {
class MFHD: public Box {
public:
MFHD();
void setSequenceNumber(uint32_t newSequenceNumber);
uint32_t getSequenceNumber();
std::string toPrettyString(uint32_t indent = 0);
};
//MFHD Box
class MOOF: public containerBox {
public:
MOOF();
};
//MOOF Box
class TRAF: public Box {
public:
TRAF();
uint32_t getContentCount();
void setContent(Box & newContent, uint32_t no);
Box & getContent(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
//TRAF Box
struct trunSampleInformation {
uint32_t sampleDuration;
uint32_t sampleSize;
uint32_t sampleFlags;
uint32_t sampleOffset;
};
enum trunflags {
trundataOffset = 0x00000001,
trunfirstSampleFlags = 0x00000004,
trunsampleDuration = 0x00000100,
trunsampleSize = 0x00000200,
trunsampleFlags = 0x00000400,
trunsampleOffsets = 0x00000800
};
enum sampleflags {
noIPicture = 0x01000000,
isIPicture = 0x02000000,
noDisposable = 0x00400000,
isDisposable = 0x00800000,
isRedundant = 0x00100000,
noRedundant = 0x00200000,
noKeySample = 0x00010000,
isKeySample = 0x00000000,
MUST_BE_PRESENT = 0x1
};
std::string prettySampleFlags(uint32_t flag);
class TRUN: public Box {
public:
TRUN();
void setFlags(uint32_t newFlags);
uint32_t getFlags();
void setDataOffset(uint32_t newOffset);
uint32_t getDataOffset();
void setFirstSampleFlags(uint32_t newSampleFlags);
uint32_t getFirstSampleFlags();
uint32_t getSampleInformationCount();
void setSampleInformation(trunSampleInformation newSample, uint32_t no);
trunSampleInformation getSampleInformation(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
enum tfhdflags {
tfhdBaseOffset = 0x000001,
tfhdSampleDesc = 0x000002,
tfhdSampleDura = 0x000008,
tfhdSampleSize = 0x000010,
tfhdSampleFlag = 0x000020,
tfhdNoDuration = 0x010000,
};
class TFHD: public Box {
public:
TFHD();
void setFlags(uint32_t newFlags);
uint32_t getFlags();
void setTrackID(uint32_t newID);
uint32_t getTrackID();
void setBaseDataOffset(uint64_t newOffset);
uint64_t getBaseDataOffset();
void setSampleDescriptionIndex(uint32_t newIndex);
uint32_t getSampleDescriptionIndex();
void setDefaultSampleDuration(uint32_t newDuration);
uint32_t getDefaultSampleDuration();
void setDefaultSampleSize(uint32_t newSize);
uint32_t getDefaultSampleSize();
void setDefaultSampleFlags(uint32_t newFlags);
uint32_t getDefaultSampleFlags();
std::string toPrettyString(uint32_t indent = 0);
};
class AVCC: public Box {
public:
AVCC();
void setVersion(uint32_t newVersion);
uint32_t getVersion();
void setProfile(uint32_t newProfile);
uint32_t getProfile();
void setCompatibleProfiles(uint32_t newCompatibleProfiles);
uint32_t getCompatibleProfiles();
void setLevel(uint32_t newLevel);
uint32_t getLevel();
void setSPSNumber(uint32_t newSPSNumber);
uint32_t getSPSNumber();
void setSPS(std::string newSPS);
uint32_t getSPSLen();
char * getSPS();
void setPPSNumber(uint32_t newPPSNumber);
uint32_t getPPSNumber();
void setPPS(std::string newPPS);
uint32_t getPPSLen();
char * getPPS();
std::string asAnnexB();
void setPayload(std::string newPayload);
std::string toPrettyString(uint32_t indent = 0);
};
class Descriptor{
public:
Descriptor();
Descriptor(const char* pointer, const unsigned long length, const bool master = false);
char getTag();///< Get type of descriptor
void setTag(char t);///< Set type of descriptor
unsigned long getDataSize();///< Get internal size of descriptor
unsigned long getFullSize();///< Get external size of descriptor
void resize(unsigned long t);///< Resize descriptor
char* getData();///< Returns pointer to start of internal data
std::string toPrettyString(uint32_t indent = 0);///< put it into a pretty string
protected:
unsigned long size;///< Length of data
char* data;///< Pointer to data in memory
bool master;
};
/// Implements ISO 14496-1 DecSpecificInfoTag
class DSDescriptor: public Descriptor{
public:
DSDescriptor (const char* pointer, const unsigned long length, const bool master = false);
std::string toPrettyString(uint32_t indent = 0);///< put it into a pretty string
std::string toString(); ///< Return decoder specific info as standard string in binary format.
};
/// Implements ISO 14496-1 DecoderConfigDescrTag
class DCDescriptor: public Descriptor{
public:
DCDescriptor (const char* pointer, const unsigned long length, const bool master = false);
bool isAAC(); ///< Returns true if this track is AAC.
DSDescriptor getSpecific();
std::string toPrettyString(uint32_t indent = 0);///< put it into a pretty string
};
/// Implements ISO 14496-1 ES_DescrTag
class ESDescriptor: public Descriptor{
public:
ESDescriptor (const char* pointer, const unsigned long length, const bool master = false);
DCDescriptor getDecoderConfig();
std::string toPrettyString(uint32_t indent = 0);///< put it into a pretty string
};
/// Implements ISO 14496-1 SLConfigDescrTag
class SLCDescriptor: public Descriptor{
};
class ESDS: public fullBox {
public:
ESDS();
ESDS(std::string init);
ESDescriptor getESDescriptor();
bool isAAC();
std::string getInitData();
std::string toPrettyString(uint32_t indent = 0);
};
class FTYP: public Box {
public:
FTYP();
void setMajorBrand(const char * newMajorBrand);
std::string getMajorBrand();
void setMinorVersion(const char * newMinorVersion);
std::string getMinorVersion();
size_t getCompatibleBrandsCount();
void setCompatibleBrands(const char * newCompatibleBrand, size_t index);
std::string getCompatibleBrands(size_t index);
std::string toPrettyString(uint32_t indent = 0);
};
class MOOV: public containerBox {
public:
MOOV();
};
class MVEX: public containerBox {
public:
MVEX();
};
class TREX: public Box {
public:
TREX();
void setTrackID(uint32_t newTrackID);
uint32_t getTrackID();
void setDefaultSampleDescriptionIndex(uint32_t newDefaultSampleDescriptionIndex);
uint32_t getDefaultSampleDescriptionIndex();
void setDefaultSampleDuration(uint32_t newDefaultSampleDuration);
uint32_t getDefaultSampleDuration();
void setDefaultSampleSize(uint32_t newDefaultSampleSize);
uint32_t getDefaultSampleSize();
void setDefaultSampleFlags(uint32_t newDefaultSampleFlags);
uint32_t getDefaultSampleFlags();
std::string toPrettyString(uint32_t indent = 0);
};
class MFRA: public containerBox {
public:
MFRA();
};
class TRAK: public containerBox {
public:
TRAK();
};
class MDIA: public containerBox {
public:
MDIA();
};
class MINF: public containerBox {
public:
MINF();
};
class DINF: public containerBox {
public:
DINF();
};
class MFRO: public Box {
public:
MFRO();
void setSize(uint32_t newSize);
uint32_t getSize();
std::string toPrettyString(uint32_t indent = 0);
};
class HDLR: public Box {
public:
HDLR(std::string & type, std::string name);
void setHandlerType(const char * newHandlerType);
std::string getHandlerType();
void setName(std::string newName);
std::string getName();
std::string toPrettyString(uint32_t indent = 0);
};
class VMHD: public fullBox {
public:
VMHD();
void setGraphicsMode(uint16_t newGraphicsMode);
uint16_t getGraphicsMode();
uint32_t getOpColorCount();
void setOpColor(uint16_t newOpColor, size_t index);
uint16_t getOpColor(size_t index);
std::string toPrettyString(uint32_t indent = 0);
};
class SMHD: public fullBox {
public:
SMHD();
void setBalance(int16_t newBalance);
int16_t getBalance();
std::string toPrettyString(uint32_t indent = 0);
};
class HMHD: public fullBox {
public:
HMHD();
void setMaxPDUSize(uint16_t newMaxPDUSize);
uint16_t getMaxPDUSize();
void setAvgPDUSize(uint16_t newAvgPDUSize);
uint16_t getAvgPDUSize();
void setMaxBitRate(uint32_t newMaxBitRate);
uint32_t getMaxBitRate();
void setAvgBitRate(uint32_t newAvgBitRate);
uint32_t getAvgBitRate();
std::string toPrettyString(uint32_t indent = 0);
};
class NMHD: public fullBox {
public:
NMHD();
std::string toPrettyString(uint32_t indent = 0);
};
class MEHD: public fullBox {
public:
MEHD();
void setFragmentDuration(uint64_t newFragmentDuration);
uint64_t getFragmentDuration();
std::string toPrettyString(uint32_t indent = 0);
};
class STBL: public containerBox {
public:
STBL();
};
class URL: public fullBox {
public:
URL();
void setLocation(std::string newLocation);
std::string getLocation();
std::string toPrettyString(uint32_t indent = 0);
};
class URN: public fullBox {
public:
URN();
void setName(std::string newName);
std::string getName();
void setLocation(std::string newLocation);
std::string getLocation();
std::string toPrettyString(uint32_t indent = 0);
};
class DREF: public fullBox {
public:
DREF();
uint32_t getEntryCount();
void setDataEntry(fullBox & newDataEntry, size_t index);
Box & getDataEntry(size_t index);
std::string toPrettyString(uint32_t indent = 0);
};
class MVHD: public fullBox {
public:
MVHD(long long unsigned int duration);
void setCreationTime(uint64_t newCreationTime);
uint64_t getCreationTime();
void setModificationTime(uint64_t newModificationTime);
uint64_t getModificationTime();
void setTimeScale(uint32_t newTimeScale);
uint32_t getTimeScale();
void setDuration(uint64_t newDuration);
uint64_t getDuration();
void setRate(uint32_t newRate);
uint32_t getRate();
void setVolume(uint16_t newVolume);
uint16_t getVolume();
uint32_t getMatrixCount();
void setMatrix(int32_t newMatrix, size_t index);
int32_t getMatrix(size_t index);
void setTrackID(uint32_t newTrackID);
uint32_t getTrackID();
std::string toPrettyString(uint32_t indent = 0);
};
struct TFRAEntry {
uint64_t time;
uint64_t moofOffset;
uint32_t trafNumber;
uint32_t trunNumber;
uint32_t sampleNumber;
};
class TFRA: public fullBox {
public:
TFRA();
void setTrackID(uint32_t newTrackID);
uint32_t getTrackID();
void setLengthSizeOfTrafNum(char newVal);
char getLengthSizeOfTrafNum();
void setLengthSizeOfTrunNum(char newVal);
char getLengthSizeOfTrunNum();
void setLengthSizeOfSampleNum(char newVal);
char getLengthSizeOfSampleNum();
void setNumberOfEntry(uint32_t newNumberOfEntry);
uint32_t getNumberOfEntry();
void setTFRAEntry(TFRAEntry newTFRAEntry, uint32_t no);
TFRAEntry & getTFRAEntry(uint32_t no);
uint32_t getTFRAEntrySize();
std::string toPrettyString(uint32_t indent = 0);
};
class TKHD: public fullBox {
public:
TKHD(uint32_t trackId, uint64_t duration, uint32_t width, uint32_t height);
void setCreationTime(uint64_t newCreationTime);
uint64_t getCreationTime();
void setModificationTime(uint64_t newModificationTime);
uint64_t getModificationTime();
void setTrackID(uint32_t newTrackID);
uint32_t getTrackID();
void setDuration(uint64_t newDuration);
uint64_t getDuration();
void setLayer(uint16_t newLayer);
uint16_t getLayer();
void setAlternateGroup(uint16_t newAlternateGroup);
uint16_t getAlternateGroup();
void setVolume(uint16_t newVolume);
uint16_t getVolume();
uint32_t getMatrixCount();
void setMatrix(int32_t newMatrix, size_t index);
int32_t getMatrix(size_t index);
void setWidth(uint32_t newWidth);
uint32_t getWidth();
void setHeight(uint32_t newHeight);
uint32_t getHeight();
std::string toPrettyString(uint32_t indent = 0);
};
class MDHD: public fullBox {
public:
MDHD(uint64_t duration);
void setCreationTime(uint64_t newCreationTime);
uint64_t getCreationTime();
void setModificationTime(uint64_t newModificationTime);
uint64_t getModificationTime();
void setTimeScale(uint32_t newTimeScale);
uint32_t getTimeScale();
void setDuration(uint64_t newDuration);
uint64_t getDuration();
///\todo return language properly
void setLanguage(uint16_t newLanguage);
uint16_t getLanguage();
std::string toPrettyString(uint32_t indent = 0);
};
struct STTSEntry {
uint32_t sampleCount;
uint32_t sampleDelta;
};
class STTS: public fullBox {
public:
STTS(char v = 1, uint32_t f = 0);
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
void setSTTSEntry(STTSEntry newSTTSEntry, uint32_t no);
STTSEntry getSTTSEntry(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
struct CTTSEntry {
uint32_t sampleCount;
uint32_t sampleOffset;
};
class CTTS: public fullBox {
public:
CTTS();
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
void setCTTSEntry(CTTSEntry newCTTSEntry, uint32_t no);
CTTSEntry getCTTSEntry(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
struct STSCEntry {
uint32_t firstChunk;
uint32_t samplesPerChunk;
uint32_t sampleDescriptionIndex;
};
class STSC: public fullBox {
public:
STSC(char v = 1, uint32_t f = 0);
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
void setSTSCEntry(STSCEntry newSTSCEntry, uint32_t no);
STSCEntry getSTSCEntry(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
class STCO: public fullBox {
public:
STCO(char v = 1, uint32_t f = 0);
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
void setChunkOffset(uint32_t newChunkOffset, uint32_t no);
uint32_t getChunkOffset(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
class CO64: public fullBox {
public:
CO64(char v = 1, uint32_t f = 0);
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
void setChunkOffset(uint64_t newChunkOffset, uint32_t no);
uint64_t getChunkOffset(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
class STSZ: public fullBox {
public:
STSZ(char v = 1, uint32_t f = 0);
void setSampleSize(uint32_t newSampleSize);
uint32_t getSampleSize();
void setSampleCount(uint32_t newSampleCount);
uint32_t getSampleCount();
void setEntrySize(uint32_t newEntrySize, uint32_t no);
uint32_t getEntrySize(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
class SampleEntry: public Box {
public:
SampleEntry();
void setDataReferenceIndex(uint16_t newDataReferenceIndex);
uint16_t getDataReferenceIndex();
std::string toPrettySampleString(uint32_t index);
};
class CLAP: public Box { //CleanApertureBox
public:
CLAP();
void setCleanApertureWidthN(uint32_t newVal);
uint32_t getCleanApertureWidthN();
void setCleanApertureWidthD(uint32_t newVal);
uint32_t getCleanApertureWidthD();
void setCleanApertureHeightN(uint32_t newVal);
uint32_t getCleanApertureHeightN();
void setCleanApertureHeightD(uint32_t newVal);
uint32_t getCleanApertureHeightD();
void setHorizOffN(uint32_t newVal);
uint32_t getHorizOffN();
void setHorizOffD(uint32_t newVal);
uint32_t getHorizOffD();
void setVertOffN(uint32_t newVal);
uint32_t getVertOffN();
void setVertOffD(uint32_t newVal);
uint32_t getVertOffD();
std::string toPrettyString(uint32_t indent = 0);
};
class PASP: public Box { //PixelAspectRatioBox
public:
PASP();
void setHSpacing(uint32_t newVal);
uint32_t getHSpacing();
void setVSpacing(uint32_t newVal);
uint32_t getVSpacing();
std::string toPrettyString(uint32_t indent = 0);
};
class VisualSampleEntry: public SampleEntry {
///\todo set default values
public:
VisualSampleEntry();
void setCodec(const char * newCodec);
void setWidth(uint16_t newWidth);
uint16_t getWidth();
void setHeight(uint16_t newHeight);
uint16_t getHeight();
void setHorizResolution(uint32_t newHorizResolution);
uint32_t getHorizResolution();
void setVertResolution(uint32_t newVertResolution);
uint32_t getVertResolution();
void setFrameCount(uint16_t newFrameCount);
uint16_t getFrameCount();
void setCompressorName(std::string newCompressorName);
std::string getCompressorName();
void setDepth(uint16_t newDepth);
uint16_t getDepth();
Box & getCLAP();
void setCLAP(Box & clap);
Box & getPASP();
std::string toPrettyVisualString(uint32_t index = 0, std::string = "");
};
class AudioSampleEntry: public SampleEntry {
public:
///\todo set default values
AudioSampleEntry();
void setCodec(const char * newCodec);
void setChannelCount(uint16_t newChannelCount);
uint16_t getChannelCount();
void setSampleSize(uint16_t newSampleSize);
uint16_t getSampleSize();
void setPreDefined(uint16_t newPreDefined);
uint16_t getPreDefined();
void setSampleRate(uint32_t newSampleRate);
uint16_t toAACInit();
uint32_t getSampleRate();
void setCodecBox(Box & newBox);
Box & getCodecBox();
std::string toPrettyAudioString(uint32_t indent = 0, std::string name = "");
};
class MP4A: public AudioSampleEntry {
public:
MP4A();
std::string toPrettyString(uint32_t indent = 0);
};
class AAC: public AudioSampleEntry {
public:
AAC();
std::string toPrettyString(uint32_t indent = 0);
};
class AVC1: public VisualSampleEntry {
public:
AVC1();
std::string toPrettyString(uint32_t indent = 0);
};
class H264: public VisualSampleEntry {
public:
H264();
std::string toPrettyString(uint32_t indent = 0);
};
class STSD: public fullBox {
public:
STSD(char v = 1, uint32_t f = 0);
void setEntryCount(uint32_t newEntryCount);
uint32_t getEntryCount();
void setEntry(Box & newContent, uint32_t no);
Box & getEntry(uint32_t no);
std::string toPrettyString(uint32_t indent = 0);
};
class EDTS: public containerBox {
public:
EDTS();
};
class UDTA: public containerBox {
public:
UDTA();
};
class STSS: public fullBox {
public:
STSS(char v = 1, uint32_t f = 0);
void setEntryCount(uint32_t newVal);
uint32_t getEntryCount();
void setSampleNumber(uint32_t newVal, uint32_t index);
uint32_t getSampleNumber(uint32_t index);
std::string toPrettyString(uint32_t indent = 0);
};
class META: public containerFullBox {
public:
META();
std::string toPrettyString(uint32_t indent = 0);
};
class ELST: public fullBox {
public:
ELST();
void setSegmentDuration(uint64_t newVal);
uint64_t getSegmentDuration();
void setMediaTime(uint64_t newVal);
uint64_t getMediaTime();
void setMediaRateInteger(uint16_t newVal);
uint16_t getMediaRateInteger();
void setMediaRateFraction(uint16_t newVal);
uint16_t getMediaRateFraction();
std::string toPrettyString(uint32_t indent = 0);
};
}

208
lib/mp4_ms.cpp Normal file
View file

@ -0,0 +1,208 @@
#include "mp4_ms.h"
namespace MP4 {
static char c2hex(int c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return 0;
}
SDTP::SDTP() {
memcpy(data + 4, "sdtp", 4);
}
void SDTP::setVersion(uint32_t newVersion) {
setInt8(newVersion, 0);
}
uint32_t SDTP::getVersion() {
return getInt8(0);
}
void SDTP::setValue(uint32_t newValue, size_t index) {
setInt8(newValue, index);
}
uint32_t SDTP::getValue(size_t index) {
return getInt8(index);
}
std::string SDTP::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent, ' ') << "[sdtp] Sample Dependancy Type (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "Samples: " << (boxedSize() - 12) << std::endl;
for (size_t i = 1; i <= boxedSize() - 12; ++i) {
uint32_t val = getValue(i + 3);
r << std::string(indent + 2, ' ') << "[" << i << "] = ";
switch (val & 3) {
case 0:
r << " ";
break;
case 1:
r << "Redundant, ";
break;
case 2:
r << "Not redundant, ";
break;
case 3:
r << "Error, ";
break;
}
switch (val & 12) {
case 0:
r << " ";
break;
case 4:
r << "Not disposable, ";
break;
case 8:
r << "Disposable, ";
break;
case 12:
r << "Error, ";
break;
}
switch (val & 48) {
case 0:
r << " ";
break;
case 16:
r << "IFrame, ";
break;
case 32:
r << "Not IFrame, ";
break;
case 48:
r << "Error, ";
break;
}
r << "(" << val << ")" << std::endl;
}
return r.str();
}
UUID::UUID() {
memcpy(data + 4, "uuid", 4);
setInt64(0, 0);
setInt64(0, 8);
}
std::string UUID::getUUID() {
std::stringstream r;
r << std::hex;
for (int i = 0; i < 16; ++i) {
if (i == 4 || i == 6 || i == 8 || i == 10) {
r << "-";
}
r << std::setfill('0') << std::setw(2) << std::right << (int)(data[8 + i]);
}
return r.str();
}
void UUID::setUUID(const std::string & uuid_string) {
//reset UUID to zero
for (int i = 0; i < 4; ++i) {
((uint32_t *)(data + 8))[i] = 0;
}
//set the UUID from the string, char by char
int i = 0;
for (size_t j = 0; j < uuid_string.size(); ++j) {
if (uuid_string[j] == '-') {
continue;
}
data[8 + i / 2] |= (c2hex(uuid_string[j]) << ((~i & 1) << 2));
++i;
}
}
void UUID::setUUID(const char * raw_uuid) {
memcpy(data + 8, raw_uuid, 16);
}
std::string UUID::toPrettyString(uint32_t indent) {
std::string UUID = getUUID();
if (UUID == "d4807ef2-ca39-4695-8e54-26cb9e46a79f") {
return ((UUID_TrackFragmentReference *)this)->toPrettyString(indent);
}
std::stringstream r;
r << std::string(indent, ' ') << "[uuid] Extension box (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "UUID: " << UUID << std::endl;
r << std::string(indent + 1, ' ') << "Unknown UUID - ignoring contents." << std::endl;
return r.str();
}
UUID_TrackFragmentReference::UUID_TrackFragmentReference() {
setUUID((std::string)"d4807ef2-ca39-4695-8e54-26cb9e46a79f");
}
void UUID_TrackFragmentReference::setVersion(uint32_t newVersion) {
setInt8(newVersion, 16);
}
uint32_t UUID_TrackFragmentReference::getVersion() {
return getInt8(16);
}
void UUID_TrackFragmentReference::setFlags(uint32_t newFlags) {
setInt24(newFlags, 17);
}
uint32_t UUID_TrackFragmentReference::getFlags() {
return getInt24(17);
}
void UUID_TrackFragmentReference::setFragmentCount(uint32_t newCount) {
setInt8(newCount, 20);
}
uint32_t UUID_TrackFragmentReference::getFragmentCount() {
return getInt8(20);
}
void UUID_TrackFragmentReference::setTime(size_t num, uint64_t newTime) {
if (getVersion() == 0) {
setInt32(newTime, 21 + (num * 8));
} else {
setInt64(newTime, 21 + (num * 16));
}
}
uint64_t UUID_TrackFragmentReference::getTime(size_t num) {
if (getVersion() == 0) {
return getInt32(21 + (num * 8));
} else {
return getInt64(21 + (num * 16));
}
}
void UUID_TrackFragmentReference::setDuration(size_t num, uint64_t newDuration) {
if (getVersion() == 0) {
setInt32(newDuration, 21 + (num * 8) + 4);
} else {
setInt64(newDuration, 21 + (num * 16) + 8);
}
}
uint64_t UUID_TrackFragmentReference::getDuration(size_t num) {
if (getVersion() == 0) {
return getInt32(21 + (num * 8) + 4);
} else {
return getInt64(21 + (num * 16) + 8);
}
}
std::string UUID_TrackFragmentReference::toPrettyString(uint32_t indent) {
std::stringstream r;
r << std::string(indent, ' ') << "[d4807ef2-ca39-4695-8e54-26cb9e46a79f] Track Fragment Reference (" << boxedSize() << ")" << std::endl;
r << std::string(indent + 1, ' ') << "Version: " << getVersion() << std::endl;
r << std::string(indent + 1, ' ') << "Fragments: " << getFragmentCount() << std::endl;
int j = getFragmentCount();
for (int i = 0; i < j; ++i) {
r << std::string(indent + 2, ' ') << "[" << i << "] Time = " << getTime(i) << ", Duration = " << getDuration(i) << std::endl;
}
return r.str();
}
}

40
lib/mp4_ms.h Normal file
View file

@ -0,0 +1,40 @@
#pragma once
#include "mp4.h"
namespace MP4 {
class SDTP: public Box {
public:
SDTP();
void setVersion(uint32_t newVersion);
uint32_t getVersion();
void setValue(uint32_t newValue, size_t index);
uint32_t getValue(size_t index);
std::string toPrettyString(uint32_t indent = 0);
};
class UUID: public Box {
public:
UUID();
std::string getUUID();
void setUUID(const std::string & uuid_string);
void setUUID(const char * raw_uuid);
std::string toPrettyString(uint32_t indent = 0);
};
class UUID_TrackFragmentReference: public UUID {
public:
UUID_TrackFragmentReference();
void setVersion(uint32_t newVersion);
uint32_t getVersion();
void setFlags(uint32_t newFlags);
uint32_t getFlags();
void setFragmentCount(uint32_t newCount);
uint32_t getFragmentCount();
void setTime(size_t num, uint64_t newTime);
uint64_t getTime(size_t num);
void setDuration(size_t num, uint64_t newDuration);
uint64_t getDuration(size_t num);
std::string toPrettyString(uint32_t indent = 0);
};
}

507
lib/nal.cpp Normal file
View file

@ -0,0 +1,507 @@
#include "nal.h"
#include "bitstream.h"
#include "defines.h"
#include <iostream>
#include <iomanip>
#include <math.h>//for log
namespace h264 {
///empty constructor of NAL
NAL::NAL() {
}
///Constructor capable of directly inputting NAL data from a string
///\param InputData the nal input data, as a string
NAL::NAL(std::string & InputData) {
ReadData(InputData);
}
///Gets the raw NAL unit data, as a string
///\return the raw NAL unit data
std::string NAL::getData() {
return MyData;
}
///Reads data from a string,either raw or annexed
///\param InputData the h264 data, as string
///\param raw a boolean that determines whether the string contains raw h264 data or not
bool NAL::ReadData(std::string & InputData, bool raw) {
if (raw) {
MyData = InputData;
InputData.clear();
return true;
}
std::string FullAnnexB;
FullAnnexB += (char)0x00;
FullAnnexB += (char)0x00;
FullAnnexB += (char)0x00;
FullAnnexB += (char)0x01;
std::string ShortAnnexB;
ShortAnnexB += (char)0x00;
ShortAnnexB += (char)0x00;
ShortAnnexB += (char)0x01;
if (InputData.size() < 3) {
//DEBUG_MSG(DLVL_DEVEL, "fal1");
return false;
}
bool AnnexB = false;
if (InputData.substr(0, 3) == ShortAnnexB) {
AnnexB = true;
}
if (InputData.substr(0, 4) == FullAnnexB) {
InputData.erase(0, 1);
AnnexB = true;
}
if (AnnexB) {
MyData = "";
InputData.erase(0, 3); //Intro Bytes
int Location = std::min(InputData.find(ShortAnnexB), InputData.find(FullAnnexB));
MyData = InputData.substr(0, Location);
InputData.erase(0, Location);
} else {
if (InputData.size() < 4) {
DEBUG_MSG(DLVL_DEVEL, "fal2");
return false;
}
unsigned int UnitLen = (InputData[0] << 24) + (InputData[1] << 16) + (InputData[2] << 8) + InputData[3];
if (InputData.size() < 4 + UnitLen) {
DEBUG_MSG(DLVL_DEVEL, "fal3");
return false;
}
InputData.erase(0, 4); //Remove Length
MyData = InputData.substr(0, UnitLen);
InputData.erase(0, UnitLen); //Remove this unit from the string
}
//DEBUG_MSG(DLVL_DEVEL, "tru");
return true;
}
///Returns an annex B prefix
///\param LongIntro determines whether it is a short or long annex B
///\return the desired annex B prefix
std::string NAL::AnnexB(bool LongIntro) {
std::string Result;
if (MyData.size()) {
if (LongIntro) {
Result += (char)0x00;
}
Result += (char)0x00;
Result += (char)0x00;
Result += (char)0x01; //Annex B Lead-In
Result += MyData;
}
return Result;
}
///Returns raw h264 data as Size Prepended
///\return the h264 data as Size prepended
std::string NAL::SizePrepended() {
std::string Result;
if (MyData.size()) {
int DataSize = MyData.size();
Result += (char)((DataSize & 0xFF000000) >> 24);
Result += (char)((DataSize & 0x00FF0000) >> 16);
Result += (char)((DataSize & 0x0000FF00) >> 8);
Result += (char)(DataSize & 0x000000FF); //Size Lead-In
Result += MyData;
}
return Result;
}
///returns the nal unit type
///\return the nal unit type
int NAL::Type() {
return (MyData[0] & 0x1F);
}
SPS::SPS(std::string & input, bool raw) : NAL() {
ReadData(input, raw);
}
///computes SPS data from an SPS nal unit, and saves them in a useful
///more human-readable format in the parameter spsmeta
///The function is based on the analyzeSPS() function. If more data needs to be stored in sps meta,
///refer to that function to determine which variable comes at which place (as all couts have been removed).
///\param spsmeta the sps metadata, in which data from the sps is stored
///\todo some h264 sps data types are not supported (due to them containing matrixes and have never been encountered in practice). If needed, these need to be implemented
SPSMeta SPS::getCharacteristics() {
SPSMeta result;
//For calculating width
unsigned int widthInMbs = 0;
unsigned int cropHorizontal = 0;
//For calculating height
bool mbsOnlyFlag = 0;
unsigned int heightInMapUnits = 0;
unsigned int cropVertical = 0;
Utils::bitstream bs;
for (unsigned int i = 1; i < MyData.size(); i++) {
if (i + 2 < MyData.size() && MyData.substr(i, 3) == std::string("\000\000\003", 3)) {
bs << MyData.substr(i, 2);
i += 2;
} else {
bs << MyData.substr(i, 1);
}
}
char profileIdc = bs.get(8);
//Start skipping unused data
bs.skip(16);
bs.getUExpGolomb();
if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128) {
//chroma format idc
if (bs.getUExpGolomb() == 3) {
bs.skip(1);
}
bs.getUExpGolomb();
bs.getUExpGolomb();
bs.skip(1);
if (bs.get(1)) {
DEBUG_MSG(DLVL_DEVEL, "Scaling matrix not implemented yet");
}
}
bs.getUExpGolomb();
unsigned int pic_order_cnt_type = bs.getUExpGolomb();
if (!pic_order_cnt_type) {
bs.getUExpGolomb();
} else if (pic_order_cnt_type == 1) {
DEBUG_MSG(DLVL_DEVEL, "This part of the implementation is incomplete(2), to be continued. If this message is shown, contact developers immediately.");
}
bs.getUExpGolomb();
bs.skip(1);
//Stop skipping data and start doing usefull stuff
widthInMbs = bs.getUExpGolomb() + 1;
heightInMapUnits = bs.getUExpGolomb() + 1;
mbsOnlyFlag = bs.get(1);//Gets used in height calculation
if (!mbsOnlyFlag) {
bs.skip(1);
}
bs.skip(1);
//cropping flag
if (bs.get(1)) {
cropHorizontal = bs.getUExpGolomb();//leftOffset
cropHorizontal += bs.getUExpGolomb();//rightOffset
cropVertical = bs.getUExpGolomb();//topOffset
cropVertical += bs.getUExpGolomb();//bottomOffset
}
//vuiParameters
if (bs.get(1)) {
//Skipping all the paramters we dont use
if (bs.get(1)) {
if (bs.get(8) == 255) {
bs.skip(32);
}
}
if (bs.get(1)) {
bs.skip(1);
}
if (bs.get(1)) {
bs.skip(4);
if (bs.get(1)) {
bs.skip(24);
}
}
if (bs.get(1)) {
bs.getUExpGolomb();
bs.getUExpGolomb();
}
//Decode timing info
if (bs.get(1)) {
unsigned int unitsInTick = bs.get(32);
unsigned int timeScale = bs.get(32);
result.fps = (double)timeScale / (2 * unitsInTick);
bs.skip(1);
}
}
result.width = (widthInMbs * 16) - (cropHorizontal * 2);
result.height = ((mbsOnlyFlag ? 1 : 2) * heightInMapUnits * 16) - (cropVertical * 2);
return result;
}
///Analyzes an SPS nal unit, and prints the values of all existing fields
///\todo some h264 sps data types are not supported (due to them containing matrixes and have not yet been encountered in practice). If needed, these need to be implemented
void SPS::analyzeSPS() {
if (Type() != 7) {
DEBUG_MSG(DLVL_DEVEL, "This is not an SPS, but type %d", Type());
return;
}
Utils::bitstream bs;
//put rbsp bytes in mydata
for (unsigned int i = 1; i < MyData.size(); i++) {
//DEBUG_MSG(DLVL_DEVEL, "position %u out of %lu",i,MyData.size());
if (i + 2 < MyData.size() && MyData.substr(i, 3) == std::string("\000\000\003", 3)) {
bs << MyData.substr(i, 2);
//DEBUG_MSG(DLVL_DEVEL, "0x000003 encountered at i = %u",i);
i += 2;
} else {
bs << MyData.substr(i, 1);
}
}
//bs contains all rbsp bytes, now we can analyze them
std::cout << "seq_parameter_set_data()" << std::endl;
std::cout << std::hex << std::setfill('0') << std::setw(2);
char profileIdc = bs.get(8);
std::cout << "profile idc: " << (unsigned int) profileIdc << std::endl;
std::cout << "constraint_set0_flag: " << bs.get(1) << std::endl;
std::cout << "constraint_set1_flag: " << bs.get(1) << std::endl;
std::cout << "constraint_set2_flag: " << bs.get(1) << std::endl;
std::cout << "constraint_set3_flag: " << bs.get(1) << std::endl;
std::cout << "constraint_set4_flag: " << bs.get(1) << std::endl;
std::cout << "constraint_set5_flag: " << bs.get(1) << std::endl;
std::cout << "reserved_zero_2bits: " << bs.get(2) << std::endl;
std::cout << "level idc: " << bs.get(8) << std::endl;
std::cout << "seq_parameter_set_id: " << bs.getUExpGolomb() << std::endl;
if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244 || profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118 || profileIdc == 128) {
chroma_format_idc = bs.getUExpGolomb();
std::cout << "chroma_format_idc: " << chroma_format_idc << std::endl;
if (chroma_format_idc == 3) {
std::cout << "separate_colour_plane_flag" << bs.get(1) << std::endl;
}
std::cout << "bit_depth_luma_minus8: " << bs.getUExpGolomb() << std::endl;
std::cout << "bit_depth_chroma_minus8: " << bs.getUExpGolomb() << std::endl;
std::cout << "qpprime_y_zero_transform_bypass_flag: " << bs.get(1) << std::endl;
unsigned int seq_scaling_matrix_present_flag = bs.get(1);
std::cout << "seq_scaling_matrix_present_flag: " << seq_scaling_matrix_present_flag << std::endl;
if (seq_scaling_matrix_present_flag) {
for (unsigned int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12) ; i++) {
unsigned int seq_scaling_list_present_flag = bs.get(1);
std::cout << "seq_scaling_list_present_flag: " << seq_scaling_list_present_flag << std::endl;
DEBUG_MSG(DLVL_DEVEL, "not implemented, ending");
return;
if (seq_scaling_list_present_flag) {
//LevelScale4x4( m, i, j ) = weightScale4x4( i, j ) * normAdjust4x4( m, i, j )
//
if (i < 6) {
//scaling)list(ScalingList4x4[i],16,UseDefaultScalingMatrix4x4Flag[i]
} else {
//scaling)list(ScalingList4x4[i-6],64,UseDefaultScalingMatrix4x4Flag[i-6]
}
}
}
}
}
std::cout << "log2_max_frame_num_minus4: " << bs.getUExpGolomb() << std::endl;
unsigned int pic_order_cnt_type = bs.getUExpGolomb();
std::cout << "pic_order_cnt_type: " << pic_order_cnt_type << std::endl;
if (pic_order_cnt_type == 0) {
std::cout << "log2_max_pic_order_cnt_lsb_minus4: " << bs.getUExpGolomb() << std::endl;
} else if (pic_order_cnt_type == 1) {
DEBUG_MSG(DLVL_DEVEL, "This part of the implementation is incomplete(2), to be continued. If this message is shown, contact developers immediately.");
return;
}
std::cout << "max_num_ref_frames: " << bs.getUExpGolomb() << std::endl;
std::cout << "gaps_in_frame_num_allowed_flag: " << bs.get(1) << std::endl;
std::cout << "pic_width_in_mbs_minus1: " << bs.getUExpGolomb() << std::endl;
std::cout << "pic_height_in_map_units_minus1: " << bs.getUExpGolomb() << std::endl;
unsigned int frame_mbs_only_flag = bs.get(1);
std::cout << "frame_mbs_only_flag: " << frame_mbs_only_flag << std::endl;
if (frame_mbs_only_flag == 0) {
std::cout << "mb_adaptive_frame_field_flag: " << bs.get(1) << std::endl;
}
std::cout << "direct_8x8_inference_flag: " << bs.get(1) << std::endl;
unsigned int frame_cropping_flag = bs.get(1);
std::cout << "frame_cropping_flag: " << frame_cropping_flag << std::endl;
if (frame_cropping_flag != 0) {
std::cout << "frame_crop_left_offset: " << bs.getUExpGolomb() << std::endl;
std::cout << "frame_crop_right_offset: " << bs.getUExpGolomb() << std::endl;
std::cout << "frame_crop_top_offset: " << bs.getUExpGolomb() << std::endl;
std::cout << "frame_crop_bottom_offset: " << bs.getUExpGolomb() << std::endl;
}
unsigned int vui_parameters_present_flag = bs.get(1);
std::cout << "vui_parameters_present_flag: " << vui_parameters_present_flag << std::endl;
if (vui_parameters_present_flag != 0) {
//vuiParameters
unsigned int aspect_ratio_info_present_flag = bs.get(1);
std::cout << "aspect_ratio_info_present_flag: " << aspect_ratio_info_present_flag << std::endl;
if (aspect_ratio_info_present_flag != 0) {
unsigned int aspect_ratio_idc = bs.get(8);
std::cout << "aspect_ratio_idc: " << aspect_ratio_idc << std::endl;
if (aspect_ratio_idc == 255) {
std::cout << "sar_width: " << bs.get(16) << std::endl;
std::cout << "sar_height: " << bs.get(16) << std::endl;
}
}
unsigned int overscan_info_present_flag = bs.get(1);
std::cout << "overscan_info_present_flag: " << overscan_info_present_flag << std::endl;
if (overscan_info_present_flag) {
std::cout << "overscan_appropriate_flag: " << bs.get(1) << std::endl;
}
unsigned int video_signal_type_present_flag = bs.get(1);
std::cout << "video_signal_type_present_flag: " << video_signal_type_present_flag << std::endl;
if (video_signal_type_present_flag) {
std::cout << "video_format: " << bs.get(3) << std::endl;
std::cout << "video_full_range_flag: " << bs.get(1) << std::endl;
unsigned int colour_description_present_flag = bs.get(1);
std::cout << "colour_description_present_flag: " << colour_description_present_flag << std::endl;
if (colour_description_present_flag) {
std::cout << "colour_primaries: " << bs.get(8) << std::endl;
std::cout << "transfer_characteristics: " << bs.get(8) << std::endl;
std::cout << "matrix_coefficients: " << bs.get(8) << std::endl;
}
}
unsigned int chroma_loc_info_present_flag = bs.get(1);
std::cout << "chroma_loc_info_present_flag: " << chroma_loc_info_present_flag << std::endl;
if (chroma_loc_info_present_flag) {
std::cout << "chroma_sample_loc_type_top_field: " << bs.getUExpGolomb() << std::endl;
std::cout << "chroma_sample_loc_type_bottom_field: " << bs.getUExpGolomb() << std::endl;
}
unsigned int timing_info_present_flag = bs.get(1);
std::cout << "timing_info_present_flag: " << timing_info_present_flag << std::endl;
if (timing_info_present_flag) {
std::cout << "num_units_in_tick: " << bs.get(32) << std::endl;
std::cout << "time_scale: " << bs.get(32) << std::endl;
std::cout << "fixed_frame_rate_flag: " << bs.get(1) << std::endl;
}
unsigned int nal_hrd_parameters_present_flag = bs.get(1);
std::cout << "nal_hrd_parameters_present_flag: " << nal_hrd_parameters_present_flag << std::endl;
if (nal_hrd_parameters_present_flag) {
unsigned int cpb_cnt_minus1 = bs.getUExpGolomb();
std::cout << "cpb_cnt_minus1: " << cpb_cnt_minus1 << std::endl;
std::cout << "bit_rate_scale: " << bs.get(4) << std::endl;
std::cout << "cpb_rate_scale: " << bs.get(4) << std::endl;
for (unsigned int ssi = 0; ssi <= cpb_cnt_minus1 ; ssi++) {
std::cout << "bit_rate_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl;
std::cout << "cpb_size_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl;
std::cout << "cbr_flag[" << ssi << "]: " << bs.get(1) << std::endl;
}
std::cout << "initial_cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl;
std::cout << "cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl;
std::cout << "dpb_output_delay_length_minus1: " << bs.get(5) << std::endl;
std::cout << "time_offset_length: " << bs.get(5) << std::endl;
}
unsigned int vcl_hrd_parameters_present_flag = bs.get(1);
std::cout << "vcl_hrd_parameters_present_flag: " << vcl_hrd_parameters_present_flag << std::endl;
if (vcl_hrd_parameters_present_flag) {
unsigned int cpb_cnt_minus1 = bs.getUExpGolomb();
std::cout << "cpb_cnt_minus1: " << cpb_cnt_minus1 << std::endl;
std::cout << "bit_rate_scale: " << bs.get(4) << std::endl;
std::cout << "cpb_rate_scale: " << bs.get(4) << std::endl;
for (unsigned int ssi = 0; ssi <= cpb_cnt_minus1 ; ssi++) {
std::cout << "bit_rate_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl;
std::cout << "cpb_size_value_minus1[" << ssi << "]: " << bs.getUExpGolomb() << std::endl;
std::cout << "cbr_flag[" << ssi << "]: " << bs.get(1) << std::endl;
}
std::cout << "initial_cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl;
std::cout << "cpb_removal_delay_length_minus1: " << bs.get(5) << std::endl;
std::cout << "dpb_output_delay_length_minus1: " << bs.get(5) << std::endl;
std::cout << "time_offset_length: " << bs.get(5) << std::endl;
}
if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
std::cout << "low_delay_hrd_flag: " << bs.get(1) << std::endl;
}
std::cout << "pic_struct_present_flag: " << bs.get(1) << std::endl;
unsigned int bitstream_restriction_flag = bs.get(1);
std::cout << "bitstream_restriction_flag: " << bitstream_restriction_flag << std::endl;
if (bitstream_restriction_flag) {
std::cout << "motion_vectors_over_pic_boundaries_flag: " << bs.get(1) << std::endl;
std::cout << "max_bytes_per_pic_denom: " << bs.getUExpGolomb() << std::endl;
std::cout << "max_bits_per_mb_denom: " << bs.getUExpGolomb() << std::endl;
std::cout << "log2_max_mv_length_horizontal: " << bs.getUExpGolomb() << std::endl;
std::cout << "log2_max_mv_length_vertical: " << bs.getUExpGolomb() << std::endl;
std::cout << "num_reorder_frames: " << bs.getUExpGolomb() << std::endl;
std::cout << "max_dec_frame_buffering: " << bs.getUExpGolomb() << std::endl;
}
}
std::cout << std::dec << std::endl;
//DEBUG_MSG(DLVL_DEVEL, "SPS analyser");
}
///Prints the values of all the fields of a PPS nal unit in a human readable format.
///\todo some features, including analyzable matrices, are not implemented. They were never encountered in practice, so far
void PPS::analyzePPS() {
if (Type() != 8) {
DEBUG_MSG(DLVL_DEVEL, "This is not a PPS, but type %d", Type());
return;
}
Utils::bitstream bs;
//put rbsp bytes in mydata
for (unsigned int i = 1; i < MyData.size(); i++) {
if (i + 2 < MyData.size() && MyData.substr(i, 3) == std::string("\000\000\003", 3)) {
bs << MyData.substr(i, 2);
i += 2;
} else {
bs << MyData.substr(i, 1);
}
}
//bs contains all rbsp bytes, now we can analyze them
std::cout << "pic_parameter_set_id: " << bs.getUExpGolomb() << std::endl;
std::cout << "seq_parameter_set_id: " << bs.getUExpGolomb() << std::endl;
std::cout << "entropy_coding_mode_flag: " << bs.get(1) << std::endl;
std::cout << "bottom_field_pic_order_in_frame_present_flag: " << bs.get(1) << std::endl;
unsigned int num_slice_groups_minus1 = bs.getUExpGolomb();
std::cout << "num_slice_groups_minus1: " << num_slice_groups_minus1 << std::endl;
if (num_slice_groups_minus1 > 0) {
unsigned int slice_group_map_type = bs.getUExpGolomb();
std::cout << "slice_group_map_type: " << slice_group_map_type << std::endl;
if (slice_group_map_type == 0) {
for (unsigned int ig = 0; ig <= num_slice_groups_minus1; ig++) {
std::cout << "runlengthminus1[" << ig << "]: " << bs.getUExpGolomb() << std::endl;
}
} else if (slice_group_map_type == 2) {
for (unsigned int ig = 0; ig <= num_slice_groups_minus1; ig++) {
std::cout << "top_left[" << ig << "]: " << bs.getUExpGolomb() << std::endl;
std::cout << "bottom_right[" << ig << "]: " << bs.getUExpGolomb() << std::endl;
}
} else if (slice_group_map_type == 3 || slice_group_map_type == 4 || slice_group_map_type == 5) {
std::cout << "slice_group_change_direction_flag: " << bs.get(1) << std::endl;
std::cout << "slice_group_change_rate_minus1: " << bs.getUExpGolomb() << std::endl;
} else if (slice_group_map_type == 6) {
unsigned int pic_size_in_map_units_minus1 = bs.getUExpGolomb();
std::cout << "pic_size_in_map_units_minus1: " << pic_size_in_map_units_minus1 << std::endl;
for (unsigned int i = 0; i <= pic_size_in_map_units_minus1; i++) {
std::cout << "slice_group_id[" << i << "]: " << bs.get((unsigned int)(ceil(log(num_slice_groups_minus1 + 1) / log(2)))) << std::endl;
}
}
}
std::cout << "num_ref_idx_l0_default_active_minus1: " << bs.getUExpGolomb() << std::endl;
std::cout << "num_ref_idx_l1_default_active_minus1: " << bs.getUExpGolomb() << std::endl;
std::cout << "weighted_pred_flag: " << bs.get(1) << std::endl;
std::cout << "weighted_bipred_idc: " << bs.get(2) << std::endl;
std::cout << "pic_init_qp_minus26: " << bs.getExpGolomb() << std::endl;
std::cout << "pic_init_qs_minus26: " << bs.getExpGolomb() << std::endl;
std::cout << "chroma_qp_index_offset: " << bs.getExpGolomb() << std::endl;
std::cout << "deblocking_filter_control_present_flag: " << bs.get(1) << std::endl;
std::cout << "constrained_intra_pred_flag: " << bs.get(1) << std::endl;
std::cout << "redundant_pic_cnt_present_flag: " << bs.get(1) << std::endl;
//check for more data
if (bs.size() == 0) {
return;
}
unsigned int transform_8x8_mode_flag = bs.get(1);
std::cout << "transform_8x8_mode_flag: " << transform_8x8_mode_flag << std::endl;
unsigned int pic_scaling_matrix_present_flag = bs.get(1);
std::cout << "pic_scaling_matrix_present_flag: " << pic_scaling_matrix_present_flag << std::endl;
if (pic_scaling_matrix_present_flag) {
for (unsigned int i = 0; i < 6 + ((chroma_format_idc != 3) ? 2 : 6)*transform_8x8_mode_flag ; i++) {
unsigned int pic_scaling_list_present_flag = bs.get(1);
std::cout << "pic_scaling_list_present_flag[" << i << "]: " << pic_scaling_list_present_flag << std::endl;
if (pic_scaling_list_present_flag) {
std::cout << "under development, pslpf" << std::endl;
return;
if (i < 6) {
//scaling list(ScalingList4x4[i],16,UseDefaultScalingMatrix4x4Flag[ i ])
} else {
//scaling_list(ScalingList4x4[i],64,UseDefaultScalingMatrix4x4Flag[ i-6 ])
}
}
}
}
std::cout << "second_chroma_qp_index_offset: " << bs.getExpGolomb() << std::endl;
}
}

45
lib/nal.h Normal file
View file

@ -0,0 +1,45 @@
#include <string>
#include <cstdio>
namespace h264 {
///Struct containing pre-calculated metadata of an SPS nal unit. Width and height in pixels, fps in Hz
struct SPSMeta {
unsigned int width;
unsigned int height;
double fps;
};
///Class for analyzing generic nal units
class NAL {
public:
NAL();
NAL(std::string & InputData);
bool ReadData(std::string & InputData, bool raw = false);
std::string AnnexB(bool LongIntro = false);
std::string SizePrepended();
int Type();
std::string getData();
protected:
unsigned int chroma_format_idc;///<the value of chroma_format_idc
std::string MyData;///<The h264 nal unit data
};
//NAL class
///Special instance of NAL class for analyzing SPS nal units
class SPS: public NAL {
public:
SPS(): NAL() {};
SPS(std::string & InputData, bool raw = false);
SPSMeta getCharacteristics();
void analyzeSPS();
};
///Special instance of NAL class for analyzing PPS nal units
class PPS: public NAL {
public:
PPS(): NAL() {};
PPS(std::string & InputData): NAL(InputData) {};
void analyzePPS();
};
}//ns h264

637
lib/ogg.cpp Normal file
View file

@ -0,0 +1,637 @@
#include "ogg.h"
#include "defines.h"
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include <arpa/inet.h>
#include <iomanip>
#include "bitstream.h"
namespace OGG {
oggSegment::oggSegment(){
isKeyframe = 0;
frameNumber = 0;
timeStamp = 0;
framesSinceKeyFrame = 0;
}
std::deque<unsigned int> decodeXiphSize(char * data, size_t len){
std::deque<unsigned int> res;
res.push_back(0);
for (unsigned int i = 0; i < len; i++){
*res.rbegin() += data[i];
if (data[i] != 0xFF){
res.push_back(0);
}
}
if (*res.rbegin() == 0){
res.resize(res.size() - 1);
}
return res;
}
inline long long unsigned int get_64(char * data){
long long unsigned int temp = 0;
for (int i = 7; i >= 0; --i){
temp <<= 8;
temp += data[i];
}
return temp;
}
inline long unsigned int get_32(char * data){
long unsigned int temp = 0;
for (int i = 3; i >= 0; --i){
temp <<= 8;
temp += data[i];
}
return temp;
}
inline void set_64(char * data, long unsigned int val){
for (int i = 0; i < 8; ++i){
data[i] = val & 0xFF;
val >>= 8;
}
}
inline void set_32(char * data, long unsigned int val){
for (int i = 0; i < 4; ++i){
data[i] = val & 0xFF;
val >>= 8;
}
}
Page::Page(){
framesSeen = 0;
lastKeyFrame = 0;
sampleRate = 0;
firstSample = 0;
pageSequenceNumber = 0;
totalFrames = 0;
memset(data, 0, 282);
}
Page::Page(const Page & rhs){
framesSeen=rhs.framesSeen;
pageSequenceNumber = rhs.pageSequenceNumber;
lastKeyFrame = rhs.lastKeyFrame;
sampleRate = rhs.sampleRate;
firstSample = rhs.firstSample;
totalFrames= rhs.totalFrames;
memcpy(data, rhs.data, 282);
segments = rhs.segments;
}
void Page::operator = (const Page & rhs){
framesSeen=rhs.framesSeen;
pageSequenceNumber = rhs.pageSequenceNumber;
lastKeyFrame = rhs.lastKeyFrame;
firstSample = rhs.firstSample;
sampleRate = rhs.sampleRate;
totalFrames= rhs.totalFrames;
memcpy(data, rhs.data, 282);
segments = rhs.segments;
}
unsigned int Page::calcPayloadSize(){
unsigned int retVal = 0;
for (unsigned int i = 0; i < segments.size(); i++){
retVal += segments[i].size();
}
return retVal;
}
/// Reads an OGG Page from the source and if valid, removes it from source.
bool Page::read(std::string & newData){
int len = newData.size();
segments.clear();
if (newData.size() < 27){
return false;
}
if (newData.substr(0, 4) != "OggS"){
DEBUG_MSG(DLVL_FAIL, "Invalid Ogg page encountered (magic number wrong: %s) - cannot continue", newData.c_str());
return false;
}
memcpy(data, newData.c_str(), 27);//copying the header, always 27 bytes
if (newData.size() < 27u + getPageSegments()){ //check input size
return false;
}
newData.erase(0, 27);
memcpy(data + 27, newData.c_str(), getPageSegments());
newData.erase(0, getPageSegments());
std::deque<unsigned int> segSizes = decodeXiphSize(data + 27, getPageSegments());
for (std::deque<unsigned int>::iterator it = segSizes.begin(); it != segSizes.end(); it++){
segments.push_back(std::string(newData.data(), *it));
newData.erase(0, *it);
}
INFO_MSG("Erased %lu bytes from the input", len - newData.size());
return true;
}
bool Page::read(FILE * inFile){
segments.clear();
int oriPos = ftell(inFile);
//INFO_MSG("pos: %d", oriPos);
if (!fread(data, 27, 1, inFile)){
fseek(inFile, oriPos, SEEK_SET);
FAIL_MSG("failed to fread(data, 27, 1, inFile) @ pos %d", oriPos);
return false;
}
if (std::string(data, 4) != "OggS"){
DEBUG_MSG(DLVL_FAIL, "Invalid Ogg page encountered (magic number wrong: %s) - cannot continue bytePos %d", data, oriPos);
return false;
}
if (!fread(data + 27, getPageSegments(), 1, inFile)){
fseek(inFile, oriPos, SEEK_SET);
FAIL_MSG("failed to fread(data + 27, getPageSegments() %d, 1, inFile) @ pos %d", getPageSegments(), oriPos);
return false;
}
std::deque<unsigned int> segSizes = decodeXiphSize(data + 27, getPageSegments());
for (std::deque<unsigned int>::iterator it = segSizes.begin(); it != segSizes.end(); it++){
if (*it){
char * thisSeg = (char *)malloc(*it * sizeof(char));
if (!thisSeg){
DEBUG_MSG(DLVL_WARN, "malloc failed");
}
if (!fread(thisSeg, *it, 1, inFile)){
DEBUG_MSG(DLVL_WARN, "Unable to read a segment @ pos %d segment size: %d getPageSegments: %d", oriPos, *it, getPageSegments());
fseek(inFile, oriPos, SEEK_SET);
return false;
}
segments.push_back(std::string(thisSeg, *it));
free(thisSeg);
}else{
segments.push_back(std::string("", 0));
}
}
return true;
}
bool Page::getSegment(unsigned int index, std::string & ret){
if (index >= segments.size()){
ret.clear();
return false;
}
ret = segments[index];
return true;
}
const char * Page::getSegment(unsigned int index){
if (index >= segments.size()){
return 0;
}
return segments[index].data();
}
unsigned long Page::getSegmentLen(unsigned int index){
if (index >= segments.size()){
return 0;
}
return segments[index].size();
}
void Page::setMagicNumber(){
memcpy(data, "OggS", 4);
}
char Page::getVersion(){
return data[4];
}
void Page::setVersion(char newVal){
data[4] = newVal;
}
char Page::getHeaderType(){
return data[5];
}
void Page::setHeaderType(char newVal){
data[5] = newVal;
}
long long unsigned int Page::getGranulePosition(){
return get_64(data + 6);
}
void Page::setGranulePosition(long long unsigned int newVal){
set_64(data + 6, newVal);
}
long unsigned int Page::getBitstreamSerialNumber(){
//return ntohl(((long unsigned int*)(data+14))[0]);
return get_32(data + 14);
}
void Page::setBitstreamSerialNumber(long unsigned int newVal){
set_32(data + 14, newVal);
}
long unsigned int Page::getPageSequenceNumber(){
return get_32(data + 18);
}
void Page::setPageSequenceNumber(long unsigned int newVal){
set_32(data + 18, newVal);
}
long unsigned int Page::getCRCChecksum(){
return get_32(data + 22);
}
void Page::setCRCChecksum(long unsigned int newVal){
set_32(data + 22, newVal);
}
char Page::getPageSegments(){
return data[26];
}
inline void Page::setPageSegments(char newVal){
data[26] = newVal;
}
bool Page::verifyChecksum(){
if (getCRCChecksum() == calcChecksum()){ //NOTE: calcChecksum is currently not functional (it will always return 0)
return true;
} else {
return false;
}
}
bool Page::possiblyContinued(){
if (getPageSegments() == 255){
if (data[281] == 255){
return true;
}
}
return false;
}
std::string Page::toPrettyString(size_t indent){
std::stringstream r;
r << std::string(indent, ' ') << "Ogg page" << std::endl;
r << std::string(indent + 2, ' ') << "Version: " << (int)getVersion() << std::endl;
r << std::string(indent + 2, ' ') << "Header type:";
if (!getHeaderType()){
r << " Normal";
} else {
if (getHeaderType() & Continued){
r << " Continued";
}
if (getHeaderType() & BeginOfStream){
r << " BeginOfStream";
}
if (getHeaderType() & EndOfStream){
r << " EndOfStream";
}
}
r << " (" << (int)getHeaderType() << ")" << std::endl;
r << std::string(indent + 2, ' ') << "Granule position: " << std::hex << std::setw(16) << std::setfill('0') << getGranulePosition() << std::dec << std::endl;
r << std::string(indent + 2, ' ') << "Bitstream number: " << getBitstreamSerialNumber() << std::endl;
r << std::string(indent + 2, ' ') << "Sequence number: " << getPageSequenceNumber() << std::endl;
r << std::string(indent + 2, ' ') << "Checksum Correct: " << verifyChecksum() << std::endl;
//r << " Calced Checksum: " << std::hex << calcChecksum() << std::dec << std::endl;
r << std::string(indent + 2, ' ') << "Checksum: " << getCRCChecksum() << std::endl;
r << std::string(indent + 2, ' ') << (int)getPageSegments() << " segments:" << std::endl;
r << std::string(indent + 3, ' ');
for (unsigned int i = 0; i < segments.size(); i++){
r << " " << segments[i].size();
}
r << std::endl;
return r.str();
}
inline unsigned int crc32(unsigned int crc, const char * data, size_t len){
static const unsigned int table[256] = {
0x00000000U, 0x04C11DB7U, 0x09823B6EU, 0x0D4326D9U,
0x130476DCU, 0x17C56B6BU, 0x1A864DB2U, 0x1E475005U,
0x2608EDB8U, 0x22C9F00FU, 0x2F8AD6D6U, 0x2B4BCB61U,
0x350C9B64U, 0x31CD86D3U, 0x3C8EA00AU, 0x384FBDBDU,
0x4C11DB70U, 0x48D0C6C7U, 0x4593E01EU, 0x4152FDA9U,
0x5F15ADACU, 0x5BD4B01BU, 0x569796C2U, 0x52568B75U,
0x6A1936C8U, 0x6ED82B7FU, 0x639B0DA6U, 0x675A1011U,
0x791D4014U, 0x7DDC5DA3U, 0x709F7B7AU, 0x745E66CDU,
0x9823B6E0U, 0x9CE2AB57U, 0x91A18D8EU, 0x95609039U,
0x8B27C03CU, 0x8FE6DD8BU, 0x82A5FB52U, 0x8664E6E5U,
0xBE2B5B58U, 0xBAEA46EFU, 0xB7A96036U, 0xB3687D81U,
0xAD2F2D84U, 0xA9EE3033U, 0xA4AD16EAU, 0xA06C0B5DU,
0xD4326D90U, 0xD0F37027U, 0xDDB056FEU, 0xD9714B49U,
0xC7361B4CU, 0xC3F706FBU, 0xCEB42022U, 0xCA753D95U,
0xF23A8028U, 0xF6FB9D9FU, 0xFBB8BB46U, 0xFF79A6F1U,
0xE13EF6F4U, 0xE5FFEB43U, 0xE8BCCD9AU, 0xEC7DD02DU,
0x34867077U, 0x30476DC0U, 0x3D044B19U, 0x39C556AEU,
0x278206ABU, 0x23431B1CU, 0x2E003DC5U, 0x2AC12072U,
0x128E9DCFU, 0x164F8078U, 0x1B0CA6A1U, 0x1FCDBB16U,
0x018AEB13U, 0x054BF6A4U, 0x0808D07DU, 0x0CC9CDCAU,
0x7897AB07U, 0x7C56B6B0U, 0x71159069U, 0x75D48DDEU,
0x6B93DDDBU, 0x6F52C06CU, 0x6211E6B5U, 0x66D0FB02U,
0x5E9F46BFU, 0x5A5E5B08U, 0x571D7DD1U, 0x53DC6066U,
0x4D9B3063U, 0x495A2DD4U, 0x44190B0DU, 0x40D816BAU,
0xACA5C697U, 0xA864DB20U, 0xA527FDF9U, 0xA1E6E04EU,
0xBFA1B04BU, 0xBB60ADFCU, 0xB6238B25U, 0xB2E29692U,
0x8AAD2B2FU, 0x8E6C3698U, 0x832F1041U, 0x87EE0DF6U,
0x99A95DF3U, 0x9D684044U, 0x902B669DU, 0x94EA7B2AU,
0xE0B41DE7U, 0xE4750050U, 0xE9362689U, 0xEDF73B3EU,
0xF3B06B3BU, 0xF771768CU, 0xFA325055U, 0xFEF34DE2U,
0xC6BCF05FU, 0xC27DEDE8U, 0xCF3ECB31U, 0xCBFFD686U,
0xD5B88683U, 0xD1799B34U, 0xDC3ABDEDU, 0xD8FBA05AU,
0x690CE0EEU, 0x6DCDFD59U, 0x608EDB80U, 0x644FC637U,
0x7A089632U, 0x7EC98B85U, 0x738AAD5CU, 0x774BB0EBU,
0x4F040D56U, 0x4BC510E1U, 0x46863638U, 0x42472B8FU,
0x5C007B8AU, 0x58C1663DU, 0x558240E4U, 0x51435D53U,
0x251D3B9EU, 0x21DC2629U, 0x2C9F00F0U, 0x285E1D47U,
0x36194D42U, 0x32D850F5U, 0x3F9B762CU, 0x3B5A6B9BU,
0x0315D626U, 0x07D4CB91U, 0x0A97ED48U, 0x0E56F0FFU,
0x1011A0FAU, 0x14D0BD4DU, 0x19939B94U, 0x1D528623U,
0xF12F560EU, 0xF5EE4BB9U, 0xF8AD6D60U, 0xFC6C70D7U,
0xE22B20D2U, 0xE6EA3D65U, 0xEBA91BBCU, 0xEF68060BU,
0xD727BBB6U, 0xD3E6A601U, 0xDEA580D8U, 0xDA649D6FU,
0xC423CD6AU, 0xC0E2D0DDU, 0xCDA1F604U, 0xC960EBB3U,
0xBD3E8D7EU, 0xB9FF90C9U, 0xB4BCB610U, 0xB07DABA7U,
0xAE3AFBA2U, 0xAAFBE615U, 0xA7B8C0CCU, 0xA379DD7BU,
0x9B3660C6U, 0x9FF77D71U, 0x92B45BA8U, 0x9675461FU,
0x8832161AU, 0x8CF30BADU, 0x81B02D74U, 0x857130C3U,
0x5D8A9099U, 0x594B8D2EU, 0x5408ABF7U, 0x50C9B640U,
0x4E8EE645U, 0x4A4FFBF2U, 0x470CDD2BU, 0x43CDC09CU,
0x7B827D21U, 0x7F436096U, 0x7200464FU, 0x76C15BF8U,
0x68860BFDU, 0x6C47164AU, 0x61043093U, 0x65C52D24U,
0x119B4BE9U, 0x155A565EU, 0x18197087U, 0x1CD86D30U,
0x029F3D35U, 0x065E2082U, 0x0B1D065BU, 0x0FDC1BECU,
0x3793A651U, 0x3352BBE6U, 0x3E119D3FU, 0x3AD08088U,
0x2497D08DU, 0x2056CD3AU, 0x2D15EBE3U, 0x29D4F654U,
0xC5A92679U, 0xC1683BCEU, 0xCC2B1D17U, 0xC8EA00A0U,
0xD6AD50A5U, 0xD26C4D12U, 0xDF2F6BCBU, 0xDBEE767CU,
0xE3A1CBC1U, 0xE760D676U, 0xEA23F0AFU, 0xEEE2ED18U,
0xF0A5BD1DU, 0xF464A0AAU, 0xF9278673U, 0xFDE69BC4U,
0x89B8FD09U, 0x8D79E0BEU, 0x803AC667U, 0x84FBDBD0U,
0x9ABC8BD5U, 0x9E7D9662U, 0x933EB0BBU, 0x97FFAD0CU,
0xAFB010B1U, 0xAB710D06U, 0xA6322BDFU, 0xA2F33668U,
0xBCB4666DU, 0xB8757BDAU, 0xB5365D03U, 0xB1F740B4U,
};
while (len > 0){
crc = table[*data ^ ((crc >> 24) & 0xff)] ^ (crc << 8);
data++;
len--;
}
return crc;
}
long unsigned int Page::calcChecksum(){ //implement in sending out page, probably delete this -- probably don't delete this because this function appears to be in use
long unsigned int retVal = 0;
/*
long unsigned int oldChecksum = getCRCChecksum();
std::string fullPayload;
for (int i = 0; i < segments.size(); i++){
fullPayload += segments[i];
}
setCRCChecksum (0);
retVal = crc32(
crc32(0, data, 27 + getPageSegments()),//checksum over pageheader
fullPayload.data(),
fullPayload.size()
);//checksum over content
setCRCChecksum (oldChecksum);
*/
return retVal;
}
int Page::getPayloadSize(){
size_t res = 0;
for (unsigned int i = 0; i < segments.size(); i++){
res += segments[i].size();
}
return res;
}
const std::deque<std::string> & Page::getAllSegments(){
return segments;
}
void Page::prepareNext(bool continueMe){
clear(0, -1, getBitstreamSerialNumber(), getPageSequenceNumber() + 1);
if (continueMe){ //noting that the page will be continued
setHeaderType(OGG::Continued);
}
}
void Page::clear(char HeaderType, long long unsigned int GranPos, long unsigned int BSN, long unsigned int PSN){
memset(data, 0, 27);
setMagicNumber();
setVersion();
setHeaderType(HeaderType);
setGranulePosition(GranPos);
setBitstreamSerialNumber(BSN);
setPageSequenceNumber(PSN);
}
unsigned int Page::addSegment(const std::string & payload){ //returns added bytes
segments.push_back(payload);
return payload.size();
}
unsigned int Page::addSegment(const char * content, unsigned int length){
segments.push_back(std::string(content, length));
return length;
}
unsigned int Page::overFlow(){ //returns the amount of bytes that don't fit in this page from the segments;
unsigned int retVal = 0;
unsigned int curSegNum = 0;//the current segment number we are looking at
unsigned int segAmount = 0;
for (unsigned int i = 0; i < segments.size(); i++){
segAmount = (segments[i].size() / 255) + 1;
if (segAmount + curSegNum > 255){
retVal += ((segAmount - (255 - curSegNum + 1)) * 255) + (segments[i].size() % 255);//calculate the extra bytes that overflow
curSegNum = 255;//making sure the segment numbers are at maximum
} else {
curSegNum += segAmount;
}
}
return retVal;
}
void Page::vorbisStuff(){
Utils::bitstreamLSBF packet;
packet.append(oggSegments.rbegin()->dataString);//this is a heavy operation, it needs to be optimised //todo?
int curPCMSamples = 0;
long long unsigned int packetType = packet.get(1);
if (packetType == 0){
int tempModes = vorbis::ilog(vorbisModes.size() - 1);
int tempPacket = packet.get(tempModes);
int curBlockFlag = vorbisModes[tempPacket].blockFlag;
curPCMSamples = (1 << blockSize[curBlockFlag]);
if (prevBlockFlag != -1){
if (curBlockFlag == prevBlockFlag){
curPCMSamples /= 2;
} else {
curPCMSamples -= (1 << blockSize[0]) / 4 + (1 << blockSize[1]) / 4;
}
}
prevBlockFlag = curBlockFlag;
} else {
ERROR_MSG("Error, Vorbis packet type !=0 actual type: %llu",packetType );
}
//add to granule position
lastKeyFrame += curPCMSamples;
oggSegments.rbegin()->lastKeyFrameSeen = lastKeyFrame;
}
///this calculates the granule position for a DTSC packet
long long unsigned int Page::calculateGranule(oggSegment & currentSegment){
long long unsigned int tempGranule = 0;
if (codec == OGG::THEORA){
tempGranule = (currentSegment.lastKeyFrameSeen << split) | currentSegment.framesSinceKeyFrame;
} else if (codec == OGG::VORBIS){
tempGranule = currentSegment.lastKeyFrameSeen;
}
return tempGranule;
}
bool Page::shouldSend(){
unsigned int totalSegmentSize = 0;
if (!oggSegments.size()){
return false;
}
if (oggSegments.rbegin()->isKeyframe){
return true;
}
if (codec == OGG::VORBIS){
if (lastKeyFrame - firstSample >= sampleRate){
return true;
}
}
for (unsigned int i = 0; i < oggSegments.size(); i++){
totalSegmentSize += (oggSegments[i].dataString.size() / 255) + 1;
}
if (totalSegmentSize >= 255) return true;
return false;
}
///\todo Rewrite this
void Page::sendTo(Socket::Connection & destination, int calcGranule){ //combines all data and sends it to socket
if (!oggSegments.size()){
DEBUG_MSG(DLVL_HIGH, "!segments.size()");
return;
}
if (codec == OGG::VORBIS){
firstSample = lastKeyFrame;
}
int temp = 0;
long unsigned int checksum = 0; //reset checksum
setCRCChecksum(0);
unsigned int numSegments = oggSegments.size();
int tableIndex = 0;
char tableSize = 0;
//char table[255];
char * table = (char *)malloc(255);
unsigned int bytesLeft = 0;
for (unsigned int i = 0; i < numSegments; i++){
//calculate amount of 255 bytes needed to store size (remainder not counted)
temp = (oggSegments[i].dataString.size() / 255);
//if everything still fits in the table
if ((temp + tableIndex + 1) <= 255){
//set the 255 bytes
memset(table + tableIndex, 255, temp);
//increment tableIndex with 255 byte count
tableIndex += temp;
//set the last table entry to the remainder, and increase tableIndex with one
table[tableIndex++] = (oggSegments[i].dataString.size() % 255);
//update tableSize to be equal to tableIndex
tableSize = tableIndex;
bytesLeft = 0;
} else {
//stuff doesn't fit
//set the rest of the table to 255s
memset(table + tableIndex, 255, (255 - tableIndex));
//table size is full table in use
tableSize = 255;
//space left on current page, for this segment: (255-tableIndex)*255
bytesLeft = (255 - tableIndex) * 255;
if (oggSegments[i].dataString.size() == bytesLeft){
bytesLeft = 0; //segment barely fits.
}
break;
}
}
if (calcGranule < -1){
if (numSegments == 1 && bytesLeft){ //no segment ends on this page.
granules = -1;
} else {
unsigned int tempIndex = numSegments - 1;
if (bytesLeft != 0){
tempIndex = numSegments - 2;
}
granules = calculateGranule(oggSegments[tempIndex]);
}
} else {
granules = calcGranule; //force granule
}
setGranulePosition(granules);
checksum = crc32(checksum, data, 22);//calculating the checksum over the first part of the page
checksum = crc32(checksum, &tableSize, 1); //calculating the checksum over the segment Table Size
checksum = crc32(checksum, table, tableSize);//calculating the checksum over the segment Table
DEBUG_MSG(DLVL_DONTEVEN, "numSegments: %d", numSegments);
for (unsigned int i = 0; i < numSegments; i++){
//INFO_MSG("checksum, i: %d", i);
if (bytesLeft != 0 && ((i + 1) == numSegments)){
checksum = crc32(checksum, oggSegments[i].dataString.data(), bytesLeft);
//take only part of this segment
} else { //take the entire segment
checksum = crc32(checksum, oggSegments[i].dataString.data(), oggSegments[i].dataString.size());
}
}
setCRCChecksum(checksum);
destination.SendNow(data, 26);
destination.SendNow(&tableSize, 1);
destination.SendNow(table, tableSize);
for (unsigned int i = 0; i < numSegments; i++){
if (bytesLeft != 0 && ((i + 1) == numSegments)){
destination.SendNow(oggSegments.begin()->dataString.data(), bytesLeft);
oggSegments.begin()->dataString.erase(0, bytesLeft);
setHeaderType(OGG::Continued);//set continuation flag
break;
//for loop WILL exit after this.
} else {
destination.SendNow(oggSegments.begin()->dataString.data(), oggSegments.begin()->dataString.size());
//this segment WILL be deleted
oggSegments.erase(oggSegments.begin());
setHeaderType(OGG::Plain);//not a continuation
}
}
//done sending, assume start of new page.
//inc. page num, write to header
pageSequenceNumber++;
setPageSequenceNumber(pageSequenceNumber);
//granule still requires setting!
free(table);
return;
}
}

141
lib/ogg.h Normal file
View file

@ -0,0 +1,141 @@
#pragma once
#include <cstdlib>
#include <string>
#include <vector>
#include <deque>
#include <sstream>
#include "dtsc.h"
#include "theora.h"
#include "vorbis.h"
#include "socket.h"
namespace OGG {
class oggSegment {
public:
oggSegment();
std::string dataString;
int isKeyframe;
long long unsigned int lastKeyFrameSeen;
long long unsigned int framesSinceKeyFrame;
unsigned int frameNumber;
long long unsigned int timeStamp;
};
enum oggCodec {THEORA, VORBIS, OPUS};
enum HeaderType {
Plain = 0,
Continued = 1,
BeginOfStream = 2,
EndOfStream = 4
};
std::deque<unsigned int> decodeXiphSize(char * data, size_t len);
class Page {
public:
Page();
Page(const Page & rhs);
void operator = (const Page & rhs);
bool read(std::string & newData);
bool read(FILE * inFile);
bool getSegment(unsigned int index, std::string & ret);
const char * getSegment(unsigned int index);
unsigned long getSegmentLen(unsigned int index);
void setMagicNumber();
char getVersion();
void setVersion(char newVal = 0);
char getHeaderType();
void setHeaderType(char newVal);
long long unsigned int getGranulePosition();
void setGranulePosition(long long unsigned int newVal);
long unsigned int getBitstreamSerialNumber();
void setBitstreamSerialNumber(long unsigned int newVal);
long unsigned int getCRCChecksum();
void setCRCChecksum(long unsigned int newVal);
long unsigned int getPageSequenceNumber();
void setPageSequenceNumber(long unsigned int newVal);
char getPageSegments();//get the amount of page segments
inline void setPageSegments(char newVal);//set the amount of page segments
int getPayloadSize();
const std::deque<std::string> & getAllSegments();
bool possiblyContinued();
std::string toPrettyString(size_t indent = 0);
long unsigned int calcChecksum();
bool verifyChecksum();
unsigned int calcPayloadSize();
//void clear();
void clear(char HeaderType, long long unsigned int GranPos, long unsigned int BSN, long unsigned int PSN);
void prepareNext(bool continueMe = false);//prepare the page to become the next page
bool setPayload(char * newData, unsigned int length); //probably obsolete
unsigned int addSegment(const std::string & content); //add a segment to the page, returns added bytes
unsigned int addSegment(const char * content, unsigned int length); //add a segment to the page, returns added bytes
void sendTo(Socket::Connection & destination, int calcGranule = -2); //combines all data and sends it to socket
unsigned int setNextSegmentTableEntry(unsigned int entrySize);//returns the size that could not be added to the table
unsigned int overFlow();//returns the amount of bytes that don't fit in this page from the segments;
long long unsigned int calculateGranule(oggSegment & currentSegment);
bool shouldSend();
void vorbisStuff();//process most recent segment
long long unsigned int totalFrames;
int granules;
OGG::oggCodec codec;
std::deque<oggSegment> oggSegments; //used for ogg output
unsigned int pageSequenceNumber;
unsigned int framesSeen;
unsigned int lastKeyFrame;
unsigned int firstSample;//for vorbis, to handle "when to send the page"
unsigned int sampleRate;//for vorbis, to handle the sampleRate
int prevBlockFlag;
char blockSize[2];
std::deque<vorbis::mode> vorbisModes;//modes for vorbis
unsigned int split; //KFGShift for theora
private:
char data[282];//Fulldata
std::deque<std::string> segments;
};
class oggTrack {
public:
oggTrack() : KFGShift(0), lastTime(0), parsedHeaders(false), lastPageOffset(0), nxtSegment(0){ }
oggCodec codec;
std::string name;
std::string contBuffer;//buffer for continuing pages
long long unsigned int dtscID;
char KFGShift;
double lastTime;
long long unsigned int lastGran;
bool parsedHeaders;
long long unsigned int lastPageOffset;
unsigned int nxtSegment;
double msPerFrame;
Page myPage;
//Codec specific elements
//theora
// theora::header idHeader;//needed to determine keyframe //bullshit?
//vorbis
std::deque<vorbis::mode> vModes;
char channels;
long long unsigned int blockSize[2];
//unsigned long getBlockSize(unsigned int vModeIndex);
};
class headerPages {
public:
std::map <long long unsigned int, unsigned int> DTSCID2OGGSerial;
std::map <long long unsigned int, unsigned int> DTSCID2seqNum;
std::string parsedPages;
};
}

737
lib/procs.cpp Normal file
View file

@ -0,0 +1,737 @@
/// \file procs.cpp
/// Contains generic functions for managing processes.
#include "procs.h"
#include "defines.h"
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__MACH__)
#include <sys/wait.h>
#else
#include <wait.h>
#endif
#include <errno.h>
#include <iostream>
#include <sys/types.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdlib.h>
#include <stdio.h>
#include "timing.h"
std::map<pid_t, std::string> Util::Procs::plist;
std::map<pid_t, Util::TerminationNotifier> Util::Procs::exitHandlers;
bool Util::Procs::handler_set = false;
static bool childRunning(pid_t p) {
pid_t ret = waitpid(p, 0, WNOHANG);
if (ret == p) {
return false;
}
if (ret < 0 && errno == EINTR) {
return childRunning(p);
}
return !kill(p, 0);
}
/// sends sig 0 to process (pid). returns true if process is running
bool Util::Procs::isRunnning(pid_t pid){
return !kill(pid, 0);
}
/// Called at exit of any program that used a Start* function.
/// Waits up to 1 second, then sends SIGINT signal to all managed processes.
/// After that waits up to 5 seconds for children to exit, then sends SIGKILL to
/// all remaining children. Waits one more second for cleanup to finish, then exits.
void Util::Procs::exit_handler() {
int waiting = 0;
std::map<pid_t, std::string> listcopy = plist;
std::map<pid_t, std::string>::iterator it;
if (listcopy.empty()) {
return;
}
//wait up to 0.5 second for applications to shut down
while (!listcopy.empty() && waiting <= 25) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if (!childRunning((*it).first)) {
listcopy.erase(it);
break;
}
if (!listcopy.empty()) {
Util::sleep(20);
++waiting;
}
}
}
if (listcopy.empty()) {
return;
}
DEBUG_MSG(DLVL_DEVEL, "Sending SIGINT to remaining %d children", (int)listcopy.size());
//send sigint to all remaining
if (!listcopy.empty()) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
DEBUG_MSG(DLVL_DEVEL, "SIGINT %d: %s", (*it).first, (*it).second.c_str());
kill((*it).first, SIGINT);
}
}
DEBUG_MSG(DLVL_DEVEL, "Waiting up to 5 seconds for %d children to terminate.", (int)listcopy.size());
waiting = 0;
//wait up to 5 seconds for applications to shut down
while (!listcopy.empty() && waiting <= 250) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if (!childRunning((*it).first)) {
listcopy.erase(it);
break;
}
if (!listcopy.empty()) {
Util::sleep(20);
++waiting;
}
}
}
if (listcopy.empty()) {
return;
}
DEBUG_MSG(DLVL_DEVEL, "Sending SIGKILL to remaining %d children", (int)listcopy.size());
//send sigkill to all remaining
if (!listcopy.empty()) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
DEBUG_MSG(DLVL_DEVEL, "SIGKILL %d: %s", (*it).first, (*it).second.c_str());
kill((*it).first, SIGKILL);
}
}
DEBUG_MSG(DLVL_DEVEL, "Waiting up to a second for %d children to terminate.", (int)listcopy.size());
waiting = 0;
//wait up to 1 second for applications to shut down
while (!listcopy.empty() && waiting <= 50) {
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if (!childRunning((*it).first)) {
listcopy.erase(it);
break;
}
if (!listcopy.empty()) {
Util::sleep(20);
++waiting;
}
}
}
if (listcopy.empty()) {
return;
}
DEBUG_MSG(DLVL_DEVEL, "Giving up with %d children left.", (int)listcopy.size());
}
/// Sets up exit and childsig handlers.
/// Called by every Start* function.
void Util::Procs::setHandler() {
if (!handler_set) {
struct sigaction new_action;
new_action.sa_handler = childsig_handler;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
sigaction(SIGCHLD, &new_action, NULL);
atexit(exit_handler);
handler_set = true;
}
}
/// Used internally to capture child signals and update plist.
void Util::Procs::childsig_handler(int signum) {
if (signum != SIGCHLD) {
return;
}
int status;
pid_t ret = -1;
while (ret != 0) {
ret = waitpid(-1, &status, WNOHANG);
if (ret <= 0) { //ignore, would block otherwise
if (ret == 0 || errno != EINTR) {
return;
}
continue;
}
int exitcode;
if (WIFEXITED(status)) {
exitcode = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
exitcode = -WTERMSIG(status);
} else { // not possible
return;
}
#if DEBUG >= DLVL_HIGH
std::string pname = plist[ret];
#endif
plist.erase(ret);
#if DEBUG >= DLVL_HIGH
if (!isActive(pname)) {
DEBUG_MSG(DLVL_HIGH, "Process %s fully terminated", pname.c_str());
} else {
DEBUG_MSG(DLVL_HIGH, "Child process %d exited", ret);
}
#endif
if (exitHandlers.count(ret) > 0) {
TerminationNotifier tn = exitHandlers[ret];
exitHandlers.erase(ret);
tn(ret, exitcode);
}
}
}
/// Runs the given command and returns the stdout output as a string.
std::string Util::Procs::getOutputOf(char * const * argv) {
std::string ret;
int fin = 0, fout = -1, ferr = 0;
StartPiped("output_getter", argv, &fin, &fout, &ferr);
while (isActive("output_getter")) {
Util::sleep(100);
}
FILE * outFile = fdopen(fout, "r");
char * fileBuf = 0;
size_t fileBufLen = 0;
while (!(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)) {
ret += fileBuf;
}
fclose(outFile);
free(fileBuf);
return ret;
}
/// Runs the given command and returns the stdout output as a string.
std::string Util::Procs::getOutputOf(std::string cmd) {
std::string ret;
int fin = 0, fout = -1, ferr = 0;
StartPiped("output_getter", cmd, &fin, &fout, &ferr);
while (isActive("output_getter")) {
Util::sleep(100);
}
FILE * outFile = fdopen(fout, "r");
char * fileBuf = 0;
size_t fileBufLen = 0;
while (!(feof(outFile) || ferror(outFile)) && (getline(&fileBuf, &fileBufLen, outFile) != -1)) {
ret += fileBuf;
}
free(fileBuf);
fclose(outFile);
return ret;
}
/// Attempts to run the command cmd.
/// Replaces the current process - use after forking first!
/// This function will never return - it will either run the given
/// command or kill itself with return code 42.
void Util::Procs::runCmd(std::string & cmd) {
//split cmd into arguments
//supports a maximum of 20 arguments
char * tmp = (char *)cmd.c_str();
char * tmp2 = 0;
char * args[21];
int i = 0;
tmp2 = strtok(tmp, " ");
args[0] = tmp2;
while (tmp2 != 0 && (i < 20)) {
tmp2 = strtok(0, " ");
++i;
args[i] = tmp2;
}
if (i == 20) {
args[20] = 0;
}
//execute the command
execvp(args[0], args);
DEBUG_MSG(DLVL_ERROR, "Error running %s: %s", cmd.c_str(), strerror(errno));
_exit(42);
}
/// Starts a new process if the name is not already active.
/// \return 0 if process was not started, process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for this process.
pid_t Util::Procs::Start(std::string name, std::string cmd) {
if (isActive(name)) {
return getPid(name);
}
setHandler();
pid_t ret = fork();
if (ret == 0) {
runCmd(cmd);
} else {
if (ret > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PID %d: %s", name.c_str(), ret, cmd.c_str());
plist.insert(std::pair<pid_t, std::string>(ret, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started: fork() failed", name.c_str());
return 0;
}
}
return ret;
}
/// Starts two piped processes if the name is not already active.
/// \return 0 if process was not started, sub (sending) process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for sub (sending) process.
/// \arg cmd2 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2) {
if (isActive(name)) {
return getPid(name);
}
setHandler();
int pfildes[2];
if (pipe(pfildes) == -1) {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str());
return 0;
}
int devnull = open("/dev/null", O_RDWR);
pid_t ret = fork();
if (ret == 0) {
close(pfildes[0]);
dup2(pfildes[1], STDOUT_FILENO);
close(pfildes[1]);
dup2(devnull, STDIN_FILENO);
dup2(devnull, STDERR_FILENO);
runCmd(cmd);
} else {
if (ret > 0) {
plist.insert(std::pair<pid_t, std::string>(ret, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
}
pid_t ret2 = fork();
if (ret2 == 0) {
close(pfildes[1]);
dup2(pfildes[0], STDIN_FILENO);
close(pfildes[0]);
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
runCmd(cmd2);
} else {
if (ret2 > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PIDs (%d, %d): %s | %s", name.c_str(), ret, ret2, cmd.c_str(), cmd2.c_str());
plist.insert(std::pair<pid_t, std::string>(ret2, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
Stop(name);
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
}
close(pfildes[1]);
close(pfildes[0]);
return ret;
}
/// Starts three piped processes if the name is not already active.
/// \return 0 if process was not started, sub (sending) process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Commandline for sub (sending) process.
/// \arg cmd2 Commandline for sub (middle) process.
/// \arg cmd3 Commandline for main (receiving) process.
pid_t Util::Procs::Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3) {
if (isActive(name)) {
return getPid(name);
}
setHandler();
int pfildes[2];
int pfildes2[2];
if (pipe(pfildes) == -1) {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str());
return 0;
}
if (pipe(pfildes2) == -1) {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. Pipe creation failed.", name.c_str());
return 0;
}
int devnull = open("/dev/null", O_RDWR);
pid_t ret = fork();
if (ret == 0) {
close(pfildes[0]);
dup2(pfildes[1], STDOUT_FILENO);
close(pfildes[1]);
dup2(devnull, STDIN_FILENO);
dup2(devnull, STDERR_FILENO);
close(pfildes2[1]);
close(pfildes2[0]);
runCmd(cmd);
} else {
if (ret > 0) {
plist.insert(std::pair<pid_t, std::string>(ret, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
close(pfildes2[0]);
return 0;
}
}
pid_t ret2 = fork();
if (ret2 == 0) {
close(pfildes[1]);
close(pfildes2[0]);
dup2(pfildes[0], STDIN_FILENO);
close(pfildes[0]);
dup2(pfildes2[1], STDOUT_FILENO);
close(pfildes2[1]);
dup2(devnull, STDERR_FILENO);
runCmd(cmd2);
} else {
if (ret2 > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PIDs (%d, %d): %s | %s", name.c_str(), ret, ret2, cmd.c_str(), cmd2.c_str());
plist.insert(std::pair<pid_t, std::string>(ret2, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
Stop(name);
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
close(pfildes2[0]);
return 0;
}
}
close(pfildes[1]);
close(pfildes[0]);
pid_t ret3 = fork();
if (ret3 == 0) {
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
dup2(pfildes2[0], STDIN_FILENO);
close(pfildes2[0]);
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
runCmd(cmd3);
} else {
if (ret3 > 0) {
DEBUG_MSG(DLVL_HIGH, "Process %s started, PIDs (%d, %d, %d): %s | %s | %s", name.c_str(), ret, ret2, ret3, cmd.c_str(), cmd2.c_str(), cmd3.c_str());
plist.insert(std::pair<pid_t, std::string>(ret3, name));
} else {
DEBUG_MSG(DLVL_ERROR, "Process %s could not be started. fork() failed.", name.c_str());
Stop(name);
close(pfildes[1]);
close(pfildes[0]);
close(pfildes2[1]);
close(pfildes2[0]);
return 0;
}
}
return ret3;
}
/// Starts a new process with given fds if the name is not already active.
/// \return 0 if process was not started, process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg argv Command for this process.
/// \arg fdin Standard input file descriptor. If null, /dev/null is assumed. Otherwise, if arg contains -1, a new fd is automatically allocated and written into this arg. Then the arg will be used as fd.
/// \arg fdout Same as fdin, but for stdout.
/// \arg fdout Same as fdin, but for stderr.
pid_t Util::Procs::StartPiped(std::string name, char * const * argv, int * fdin, int * fdout, int * fderr) {
if (isActive(name)) {
DEBUG_MSG(DLVL_WARN, "Process %s already active - skipping start", name.c_str());
return getPid(name);
}
int pidtemp = StartPiped(argv, fdin, fdout, fderr);
if (pidtemp > 0) {
plist.insert(std::pair<pid_t, std::string>(pidtemp, name));
}
return pidtemp;
}
pid_t Util::Procs::StartPiped(char * const * argv, int * fdin, int * fdout, int * fderr) {
pid_t pid;
int pipein[2], pipeout[2], pipeerr[2];
//DEBUG_MSG(DLVL_DEVEL, "setHandler");
setHandler();
if (fdin && *fdin == -1 && pipe(pipein) < 0) {
DEBUG_MSG(DLVL_ERROR, "stdin pipe creation failed for process %s, reason: %s", argv[0], strerror(errno));
return 0;
}
if (fdout && *fdout == -1 && pipe(pipeout) < 0) {
DEBUG_MSG(DLVL_ERROR, "stdout pipe creation failed for process %s, reason: %s", argv[0], strerror(errno));
if (*fdin == -1) {
close(pipein[0]);
close(pipein[1]);
}
return 0;
}
if (fderr && *fderr == -1 && pipe(pipeerr) < 0) {
DEBUG_MSG(DLVL_ERROR, "stderr pipe creation failed for process %s, reason: %s", argv[0], strerror(errno));
if (*fdin == -1) {
close(pipein[0]);
close(pipein[1]);
}
if (*fdout == -1) {
close(pipeout[0]);
close(pipeout[1]);
}
return 0;
}
int devnull = -1;
if (!fdin || !fdout || !fderr) {
devnull = open("/dev/null", O_RDWR);
if (devnull == -1) {
DEBUG_MSG(DLVL_ERROR, "Could not open /dev/null for process %s, reason: %s", argv[0], strerror(errno));
if (*fdin == -1) {
close(pipein[0]);
close(pipein[1]);
}
if (*fdout == -1) {
close(pipeout[0]);
close(pipeout[1]);
}
if (*fderr == -1) {
close(pipeerr[0]);
close(pipeerr[1]);
}
return 0;
}
}
pid = fork();
if (pid == 0) { //child
if (!fdin) {
dup2(devnull, STDIN_FILENO);
} else if (*fdin == -1) {
close(pipein[1]); // close unused write end
dup2(pipein[0], STDIN_FILENO);
close(pipein[0]);
} else if (*fdin != STDIN_FILENO) {
dup2(*fdin, STDIN_FILENO);
}
if (!fdout) {
dup2(devnull, STDOUT_FILENO);
} else if (*fdout == -1) {
close(pipeout[0]); // close unused read end
dup2(pipeout[1], STDOUT_FILENO);
close(pipeout[1]);
} else if (*fdout != STDOUT_FILENO) {
dup2(*fdout, STDOUT_FILENO);
}
if (!fderr) {
dup2(devnull, STDERR_FILENO);
} else if (*fderr == -1) {
close(pipeerr[0]); // close unused read end
dup2(pipeerr[1], STDERR_FILENO);
close(pipeerr[1]);
} else if (*fderr != STDERR_FILENO) {
dup2(*fderr, STDERR_FILENO);
}
if (fdin && *fdin != -1 && *fdin != STDIN_FILENO) {
close(*fdin);
}
if (fdout && *fdout != -1 && *fdout != STDOUT_FILENO) {
close(*fdout);
}
if (fderr && *fderr != -1 && *fderr != STDERR_FILENO) {
close(*fderr);
}
if (devnull != -1) {
close(devnull);
}
execvp(argv[0], argv);
DEBUG_MSG(DLVL_ERROR, "execvp failed for process %s, reason: %s", argv[0], strerror(errno));
exit(42);
} else if (pid == -1) {
DEBUG_MSG(DLVL_ERROR, "fork failed for process %s, reason: %s", argv[0], strerror(errno));
if (fdin && *fdin == -1) {
close(pipein[0]);
close(pipein[1]);
}
if (fdout && *fdout == -1) {
close(pipeout[0]);
close(pipeout[1]);
}
if (fderr && *fderr == -1) {
close(pipeerr[0]);
close(pipeerr[1]);
}
if (devnull != -1) {
close(devnull);
}
return 0;
} else { //parent
DEBUG_MSG(DLVL_HIGH, "Piped process %s started, PID %d", argv[0], pid);
if (devnull != -1) {
close(devnull);
}
if (fdin && *fdin == -1) {
close(pipein[0]); // close unused end end
*fdin = pipein[1];
}
if (fdout && *fdout == -1) {
close(pipeout[1]); // close unused write end
*fdout = pipeout[0];
}
if (fderr && *fderr == -1) {
close(pipeerr[1]); // close unused write end
*fderr = pipeerr[0];
}
}
return pid;
}
/// Starts a new process with given fds if the name is not already active.
/// \return 0 if process was not started, process PID otherwise.
/// \arg name Name for this process - only used internally.
/// \arg cmd Command for this process.
/// \arg fdin Standard input file descriptor. If null, /dev/null is assumed. Otherwise, if arg contains -1, a new fd is automatically allocated and written into this arg. Then the arg will be used as fd.
/// \arg fdout Same as fdin, but for stdout.
/// \arg fdout Same as fdin, but for stderr.
pid_t Util::Procs::StartPiped(std::string name, std::string cmd, int * fdin, int * fdout, int * fderr) {
//Convert the given command to a char * []
char * tmp = (char *)cmd.c_str();
char * tmp2 = 0;
char * args[21];
int i = 0;
tmp2 = strtok(tmp, " ");
args[0] = tmp2;
while (tmp2 != 0 && (i < 20)) {
tmp2 = strtok(0, " ");
++i;
args[i] = tmp2;
}
if (i == 20) {
args[20] = 0;
}
return StartPiped(name, args, fdin, fdout, fderr);
}
pid_t Util::Procs::StartPiped2(std::string name, std::string cmd1, std::string cmd2, int * fdin, int * fdout, int * fderr1, int * fderr2) {
int pfildes[2];
if (pipe(pfildes) == -1) {
DEBUG_MSG(DLVL_ERROR, "Pipe creation failed for process %s", name.c_str());
return 0;
}
pid_t res1 = StartPiped(name, cmd1, fdin, &pfildes[1], fderr1);
if (!res1) {
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
pid_t res2 = StartPiped(name + "receiving", cmd2, &pfildes[0], fdout, fderr2);
if (!res2) {
Stop(res1);
close(pfildes[1]);
close(pfildes[0]);
return 0;
}
//we can close these because the fork in StartPiped() copies them.
close(pfildes[1]);
close(pfildes[0]);
return res1;
}
/// Stops the named process, if running.
/// \arg name (Internal) name of process to stop
void Util::Procs::Stop(std::string name) {
int max = 5;
while (isActive(name)) {
Stop(getPid(name));
max--;
if (max <= 0) {
return;
}
}
}
/// Stops the process with this pid, if running.
/// \arg name The PID of the process to stop.
void Util::Procs::Stop(pid_t name) {
kill(name, SIGTERM);
}
/// Stops the process with this pid, if running.
/// \arg name The PID of the process to murder.
void Util::Procs::Murder(pid_t name) {
kill(name, SIGKILL);
}
/// (Attempts to) stop all running child processes.
void Util::Procs::StopAll() {
std::map<pid_t, std::string> listcopy = plist;
std::map<pid_t, std::string>::iterator it;
for (it = listcopy.begin(); it != listcopy.end(); it++) {
Stop((*it).first);
}
}
/// Returns the number of active child processes.
int Util::Procs::Count() {
return plist.size();
}
/// Returns true if a process by this name is currently active.
bool Util::Procs::isActive(std::string name) {
std::map<pid_t, std::string> listcopy = plist;
std::map<pid_t, std::string>::iterator it;
for (it = listcopy.begin(); it != listcopy.end(); it++) {
if ((*it).second == name) {
if (childRunning((*it).first)) {
return true;
} else {
plist.erase((*it).first);
}
}
}
return false;
}
/// Returns true if a process with this PID is currently active.
bool Util::Procs::isActive(pid_t name) {
return (plist.count(name) == 1) && (kill(name, 0) == 0);
}
/// Gets PID for this named process, if active.
/// \return NULL if not active, process PID otherwise.
pid_t Util::Procs::getPid(std::string name) {
std::map<pid_t, std::string>::iterator it;
for (it = plist.begin(); it != plist.end(); it++) {
if ((*it).second == name) {
return (*it).first;
}
}
return 0;
}
/// Gets name for this process PID, if active.
/// \return Empty string if not active, name otherwise.
std::string Util::Procs::getName(pid_t name) {
if (plist.count(name) == 1) {
return plist[name];
}
return "";
}
/// Registers one notifier function for when a process indentified by PID terminates.
/// \return true if the notifier could be registered, false otherwise.
bool Util::Procs::SetTerminationNotifier(pid_t pid, TerminationNotifier notifier) {
if (plist.find(pid) != plist.end()) {
exitHandlers[pid] = notifier;
return true;
}
return false;
}

48
lib/procs.h Normal file
View file

@ -0,0 +1,48 @@
/// \file procs.h
/// Contains generic function headers for managing processes.
#pragma once
#include <unistd.h>
#include <string>
#include <map>
#include <vector>
/// Contains utility code, not directly related to streaming media
namespace Util {
typedef void (*TerminationNotifier)(pid_t pid, int exitCode);
/// Deals with spawning, monitoring and stopping child processes
class Procs {
private:
static std::map<pid_t, std::string> plist; ///< Holds active processes
static std::map<pid_t, TerminationNotifier> exitHandlers; ///< termination function, if any
static bool handler_set; ///< If true, the sigchld handler has been setup.
static void childsig_handler(int signum);
static void exit_handler();
static void runCmd(std::string & cmd);
static void setHandler();
public:
static std::string getOutputOf(char * const * argv);
static std::string getOutputOf(std::string cmd);
static pid_t Start(std::string name, std::string cmd);
static pid_t Start(std::string name, std::string cmd, std::string cmd2);
static pid_t Start(std::string name, std::string cmd, std::string cmd2, std::string cmd3);
static pid_t StartPiped(char * const * argv, int * fdin, int * fdout, int * fderr);
static pid_t StartPiped(std::string name, char * const * argv, int * fdin, int * fdout, int * fderr);
static pid_t StartPiped(std::string name, std::string cmd, int * fdin, int * fdout, int * fderr);
static pid_t StartPiped2(std::string name, std::string cmd1, std::string cmd2, int * fdin, int * fdout, int * fderr1, int * fderr2);
static void Stop(std::string name);
static void Stop(pid_t name);
static void Murder(pid_t name);
static void StopAll();
static int Count();
static bool isActive(std::string name);
static bool isActive(pid_t name);
static bool isRunnning(pid_t pid);
static pid_t getPid(std::string name);
static std::string getName(pid_t name);
static bool SetTerminationNotifier(pid_t pid, TerminationNotifier notifier);
};
}

678
lib/rtmpchunks.cpp Normal file
View file

@ -0,0 +1,678 @@
/// \file rtmpchunks.cpp
/// Holds all code for the RTMPStream namespace.
#include "rtmpchunks.h"
#include "defines.h"
#include "flv_tag.h"
#include "timing.h"
#include "auth.h"
#ifndef FILLER_DATA
#define FILLER_DATA "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros tortor commodo neque, vitae hendrerit nunc sem ut odio."
#endif
std::string RTMPStream::handshake_in; ///< Input for the handshake.
std::string RTMPStream::handshake_out; ///< Output for the handshake.
unsigned int RTMPStream::chunk_rec_max = 128;
unsigned int RTMPStream::chunk_snd_max = 128;
unsigned int RTMPStream::rec_window_size = 2500000;
unsigned int RTMPStream::snd_window_size = 2500000;
unsigned int RTMPStream::rec_window_at = 0;
unsigned int RTMPStream::snd_window_at = 0;
unsigned int RTMPStream::rec_cnt = 0;
unsigned int RTMPStream::snd_cnt = 0;
timeval RTMPStream::lastrec;
/// Holds the last sent chunk for every msg_id.
std::map<unsigned int, RTMPStream::Chunk> RTMPStream::lastsend;
/// Holds the last received chunk for every msg_id.
std::map<unsigned int, RTMPStream::Chunk> RTMPStream::lastrecv;
#define P1024 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
char genuineFMSKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
0x4d, 0x65, 0x64, 0x69, 0x61, 0x20, 0x53, 0x65, 0x72,
0x76, // Genuine Adobe Flash Media Server 001
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec,
0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
}; // 68
char genuineFPKey[] = {0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, 0x61, 0x73, 0x68, 0x20,
0x50, 0x6c, 0x61,
0x79, // Genuine Adobe Flash Player 001
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, 0x6e, 0xec,
0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
}; // 62
inline uint32_t GetDigestOffset(uint8_t * pBuffer, uint8_t scheme) {
if (scheme == 0) {
return ((pBuffer[8] + pBuffer[9] + pBuffer[10] + pBuffer[11]) % 728) + 12;
} else {
return ((pBuffer[772] + pBuffer[773] + pBuffer[774] + pBuffer[775]) % 728) + 776;
}
}
bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) {
uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme);
uint8_t pTempBuffer[1536-32];
memcpy(pTempBuffer, pBuffer, clientDigestOffset);
memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32);
char pTempHash[32];
Secure::hmac_sha256bin((char*)pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
bool result = (memcmp(pBuffer + clientDigestOffset, pTempHash, 32) == 0);
DEBUG_MSG(DLVL_MEDIUM, "Client scheme validation %hhi %s", scheme, result ? "success" : "failed");
return result;
}
/// Packs up the chunk for sending over the network.
/// \warning Do not call if you are not actually sending the resulting data!
/// \returns A std::string ready to be sent.
std::string & RTMPStream::Chunk::Pack() {
static std::string output;
output.clear();
bool allow_short = lastsend.count(cs_id);
RTMPStream::Chunk prev = lastsend[cs_id];
unsigned int tmpi;
unsigned char chtype = 0x00;
if (allow_short && (prev.cs_id == cs_id)) {
if (msg_stream_id == prev.msg_stream_id) {
chtype = 0x40; //do not send msg_stream_id
if (len == prev.len) {
if (msg_type_id == prev.msg_type_id) {
chtype = 0x80; //do not send len and msg_type_id
if (timestamp - prev.timestamp == prev.ts_delta) {
chtype = 0xC0; //do not send timestamp
}
}
}
}
//override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel
if (timestamp < prev.timestamp) {
chtype = 0x00;
}
}
if (cs_id <= 63) {
output += (unsigned char)(chtype | cs_id);
} else {
if (cs_id <= 255 + 64) {
output += (unsigned char)(chtype | 0);
output += (unsigned char)(cs_id - 64);
} else {
output += (unsigned char)(chtype | 1);
output += (unsigned char)((cs_id - 64) % 256);
output += (unsigned char)((cs_id - 64) / 256);
}
}
unsigned int ntime = 0;
if (chtype != 0xC0) {
//timestamp or timestamp diff
if (chtype == 0x00) {
tmpi = timestamp;
} else {
tmpi = timestamp - prev.timestamp;
}
ts_delta = tmpi;
if (tmpi >= 0x00ffffff) {
ntime = tmpi;
tmpi = 0x00ffffff;
}
output += (unsigned char)((tmpi >> 16) & 0xff);
output += (unsigned char)((tmpi >> 8) & 0xff);
output += (unsigned char)(tmpi & 0xff);
if (chtype != 0x80) {
//len
tmpi = len;
output += (unsigned char)((tmpi >> 16) & 0xff);
output += (unsigned char)((tmpi >> 8) & 0xff);
output += (unsigned char)(tmpi & 0xff);
//msg type id
output += (unsigned char)msg_type_id;
if (chtype != 0x40) {
//msg stream id
output += (unsigned char)(msg_stream_id % 256);
output += (unsigned char)(msg_stream_id / 256);
output += (unsigned char)(msg_stream_id / (256 * 256));
output += (unsigned char)(msg_stream_id / (256 * 256 * 256));
}
}
}
//support for 0x00ffffff timestamps
if (ntime) {
output += (unsigned char)(ntime & 0xff);
output += (unsigned char)((ntime >> 8) & 0xff);
output += (unsigned char)((ntime >> 16) & 0xff);
output += (unsigned char)((ntime >> 24) & 0xff);
}
len_left = 0;
while (len_left < len) {
tmpi = len - len_left;
if (tmpi > RTMPStream::chunk_snd_max) {
tmpi = RTMPStream::chunk_snd_max;
}
output.append(data, len_left, tmpi);
len_left += tmpi;
if (len_left < len) {
if (cs_id <= 63) {
output += (unsigned char)(0xC0 + cs_id);
} else {
if (cs_id <= 255 + 64) {
output += (unsigned char)(0xC0);
output += (unsigned char)(cs_id - 64);
} else {
output += (unsigned char)(0xC1);
output += (unsigned char)((cs_id - 64) % 256);
output += (unsigned char)((cs_id - 64) / 256);
}
}
}
}
lastsend[cs_id] = *this;
RTMPStream::snd_cnt += output.size();
return output;
} //SendChunk
/// Default constructor, creates an empty chunk with all values initialized to zero.
RTMPStream::Chunk::Chunk() {
headertype = 0;
cs_id = 0;
timestamp = 0;
len = 0;
real_len = 0;
len_left = 0;
msg_type_id = 0;
msg_stream_id = 0;
data = "";
} //constructor
/// Packs up a chunk with the given arguments as properties.
std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data) {
static RTMPStream::Chunk ch;
ch.cs_id = cs_id;
ch.timestamp = Util::getMS();
ch.len = data.size();
ch.real_len = data.size();
ch.len_left = 0;
ch.msg_type_id = msg_type_id;
ch.msg_stream_id = msg_stream_id;
ch.data = data;
return ch.Pack();
} //constructor
/// Packs up a chunk with media contents.
/// \param msg_type_id Type number of the media, as per FLV standard.
/// \param data Contents of the media data.
/// \param len Length of the media data, in bytes.
/// \param ts Timestamp of the media data, relative to current system time.
std::string & RTMPStream::SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts) {
static RTMPStream::Chunk ch;
ch.cs_id = msg_type_id + 42;
ch.timestamp = ts;
ch.len = len;
ch.real_len = len;
ch.len_left = 0;
ch.msg_type_id = msg_type_id;
ch.msg_stream_id = 1;
ch.data = std::string((char *)data, (size_t)len);
return ch.Pack();
} //SendMedia
/// Packs up a chunk with media contents.
/// \param tag FLV::Tag with media to send.
std::string & RTMPStream::SendMedia(FLV::Tag & tag) {
static RTMPStream::Chunk ch;
//Commented bit is more efficient and correct according to RTMP spec.
//Simply passing "4" is the only thing that actually plays correctly, though.
//Adobe, if you're ever reading this... wtf? Seriously.
ch.cs_id = 4;//((unsigned char)tag.data[0]);
ch.timestamp = tag.tagTime();
ch.len_left = 0;
ch.msg_type_id = (unsigned char)tag.data[0];
ch.msg_stream_id = 1;
ch.data = std::string(tag.data + 11, (size_t)(tag.len - 15));
ch.len = ch.data.size();
ch.real_len = ch.len;
return ch.Pack();
} //SendMedia
/// Packs up a chunk for a control message with 1 argument.
std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data) {
static RTMPStream::Chunk ch;
ch.cs_id = 2;
ch.timestamp = Util::getMS();
ch.len = 4;
ch.real_len = 4;
ch.len_left = 0;
ch.msg_type_id = type;
ch.msg_stream_id = 0;
ch.data.resize(4);
*(int *)((char *)ch.data.c_str()) = htonl(data);
return ch.Pack();
} //SendCTL
/// Packs up a chunk for a control message with 2 arguments.
std::string & RTMPStream::SendCTL(unsigned char type, unsigned int data, unsigned char data2) {
static RTMPStream::Chunk ch;
ch.cs_id = 2;
ch.timestamp = Util::getMS();
ch.len = 5;
ch.real_len = 5;
ch.len_left = 0;
ch.msg_type_id = type;
ch.msg_stream_id = 0;
ch.data.resize(5);
*(unsigned int *)((char *)ch.data.c_str()) = htonl(data);
ch.data[4] = data2;
return ch.Pack();
} //SendCTL
/// Packs up a chunk for a user control message with 1 argument.
std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data) {
static RTMPStream::Chunk ch;
ch.cs_id = 2;
ch.timestamp = Util::getMS();
ch.len = 6;
ch.real_len = 6;
ch.len_left = 0;
ch.msg_type_id = 4;
ch.msg_stream_id = 0;
ch.data.resize(6);
*(unsigned int *)(((char *)ch.data.c_str()) + 2) = htonl(data);
ch.data[0] = 0;
ch.data[1] = type;
return ch.Pack();
} //SendUSR
/// Packs up a chunk for a user control message with 2 arguments.
std::string & RTMPStream::SendUSR(unsigned char type, unsigned int data, unsigned int data2) {
static RTMPStream::Chunk ch;
ch.cs_id = 2;
ch.timestamp = Util::getMS();
ch.len = 10;
ch.real_len = 10;
ch.len_left = 0;
ch.msg_type_id = 4;
ch.msg_stream_id = 0;
ch.data.resize(10);
*(unsigned int *)(((char *)ch.data.c_str()) + 2) = htonl(data);
*(unsigned int *)(((char *)ch.data.c_str()) + 6) = htonl(data2);
ch.data[0] = 0;
ch.data[1] = type;
return ch.Pack();
} //SendUSR
/// Parses the argument string into the current chunk.
/// Tries to read a whole chunk, removing data from the input string as it reads.
/// If only part of a chunk is read, it will remove the part and call itself again.
/// This has the effect of only causing a "true" reponse in the case a *whole* chunk
/// is read, not just part of a chunk.
/// \param indata The input string to parse and update.
/// \warning This function will destroy the current data in this chunk!
/// \returns True if a whole chunk could be read, false otherwise.
bool RTMPStream::Chunk::Parse(std::string & indata) {
gettimeofday(&RTMPStream::lastrec, 0);
unsigned int i = 0;
if (indata.size() < 1) return false; //need at least a byte
unsigned char chunktype = indata[i++ ];
//read the chunkstream ID properly
switch (chunktype & 0x3F) {
case 0:
if (indata.size() < 2) return false; //need at least 2 bytes to continue
cs_id = indata[i++ ] + 64;
break;
case 1:
if (indata.size() < 3) return false; //need at least 3 bytes to continue
cs_id = indata[i++ ] + 64;
cs_id += indata[i++ ] * 256;
break;
default:
cs_id = chunktype & 0x3F;
break;
}
bool allow_short = lastrecv.count(cs_id);
RTMPStream::Chunk prev = lastrecv[cs_id];
//process the rest of the header, for each chunk type
headertype = chunktype & 0xC0;
DEBUG_MSG(DLVL_DONTEVEN, "Parsing RTMP chunk header (%#.2hhX) at offset %#X", chunktype, RTMPStream::rec_cnt);
switch (headertype) {
case 0x00:
if (indata.size() < i + 11) return false; //can't read whole header
timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
ts_delta = timestamp;
len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256;
len += indata[i++ ];
len_left = 0;
msg_type_id = indata[i++ ];
msg_stream_id = indata[i++ ];
msg_stream_id += indata[i++ ] * 256;
msg_stream_id += indata[i++ ] * 256 * 256;
msg_stream_id += indata[i++ ] * 256 * 256 * 256;
break;
case 0x40:
if (indata.size() < i + 7) return false; //can't read whole header
if (!allow_short) {
DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x40 with no valid previous chunk!");
}
timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
if (timestamp != 0x00ffffff) {
ts_delta = timestamp;
timestamp = prev.timestamp + ts_delta;
}
len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256;
len += indata[i++ ];
len_left = 0;
msg_type_id = indata[i++ ];
msg_stream_id = prev.msg_stream_id;
break;
case 0x80:
if (indata.size() < i + 3) return false; //can't read whole header
if (!allow_short) {
DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x80 with no valid previous chunk!");
}
timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
if (timestamp != 0x00ffffff) {
ts_delta = timestamp;
timestamp = prev.timestamp + ts_delta;
}
len = prev.len;
len_left = prev.len_left;
msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id;
break;
case 0xC0:
if (!allow_short) {
DEBUG_MSG(DLVL_WARN, "Warning: Header type 0xC0 with no valid previous chunk!");
}
timestamp = prev.timestamp + prev.ts_delta;
ts_delta = prev.ts_delta;
len = prev.len;
len_left = prev.len_left;
if (len_left > 0){
timestamp = prev.timestamp;
}
msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id;
break;
}
//calculate chunk length, real length, and length left till complete
if (len_left > 0) {
real_len = len_left;
len_left -= real_len;
} else {
real_len = len;
}
if (real_len > RTMPStream::chunk_rec_max) {
len_left += real_len - RTMPStream::chunk_rec_max;
real_len = RTMPStream::chunk_rec_max;
}
DEBUG_MSG(DLVL_DONTEVEN, "Parsing RTMP chunk result: len_left=%d, real_len=%d", len_left, real_len);
//read extended timestamp, if neccesary
if (timestamp == 0x00ffffff) {
if (indata.size() < i + 4) return false; //can't read whole header
timestamp = indata[i++ ] * 256 * 256 * 256;
timestamp += indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
ts_delta = timestamp;
}
//read data if length > 0, and allocate it
if (real_len > 0) {
if (prev.len_left > 0) {
data = prev.data;
} else {
data = "";
}
if (indata.size() < i + real_len) return false; //can't read all data (yet)
data.append(indata, i, real_len);
indata = indata.substr(i + real_len);
lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i + real_len;
if (len_left == 0) {
return true;
} else {
return Parse(indata);
}
} else {
data = "";
indata = indata.substr(i + real_len);
lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i + real_len;
return true;
}
} //Parse
/// Parses the argument Socket::Buffer into the current chunk.
/// Tries to read a whole chunk, removing data from the Buffer as it reads.
/// If a single packet contains a partial chunk, it will remove the packet and
/// call itself again. This has the effect of only causing a "true" reponse in
/// the case a *whole* chunk is read, not just part of a chunk.
/// \param buffer The input to parse and update.
/// \warning This function will destroy the current data in this chunk!
/// \returns True if a whole chunk could be read, false otherwise.
bool RTMPStream::Chunk::Parse(Socket::Buffer & buffer) {
gettimeofday(&RTMPStream::lastrec, 0);
unsigned int i = 0;
if (!buffer.available(3)) {
return false;
} //we want at least 3 bytes
std::string indata = buffer.copy(3);
unsigned char chunktype = indata[i++ ];
//read the chunkstream ID properly
switch (chunktype & 0x3F) {
case 0:
cs_id = indata[i++ ] + 64;
break;
case 1:
cs_id = indata[i++ ] + 64;
cs_id += indata[i++ ] * 256;
break;
default:
cs_id = chunktype & 0x3F;
break;
}
RTMPStream::Chunk prev = lastrecv[cs_id];
//process the rest of the header, for each chunk type
headertype = chunktype & 0xC0;
switch (headertype) {
case 0x00:
if (!buffer.available(i + 11)) {
return false;
} //can't read whole header
indata = buffer.copy(i + 11);
timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
ts_delta = timestamp;
len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256;
len += indata[i++ ];
len_left = 0;
msg_type_id = indata[i++ ];
msg_stream_id = indata[i++ ];
msg_stream_id += indata[i++ ] * 256;
msg_stream_id += indata[i++ ] * 256 * 256;
msg_stream_id += indata[i++ ] * 256 * 256 * 256;
break;
case 0x40:
if (!buffer.available(i + 7)) {
return false;
} //can't read whole header
indata = buffer.copy(i + 7);
if (prev.msg_type_id == 0) {
DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x40 with no valid previous chunk!");
}
timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
if (timestamp != 0x00ffffff) {
ts_delta = timestamp;
timestamp = prev.timestamp + ts_delta;
}
len = indata[i++ ] * 256 * 256;
len += indata[i++ ] * 256;
len += indata[i++ ];
len_left = 0;
msg_type_id = indata[i++ ];
msg_stream_id = prev.msg_stream_id;
break;
case 0x80:
if (!buffer.available(i + 3)) {
return false;
} //can't read whole header
indata = buffer.copy(i + 3);
if (prev.msg_type_id == 0) {
DEBUG_MSG(DLVL_WARN, "Warning: Header type 0x80 with no valid previous chunk!");
}
timestamp = indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
if (timestamp != 0x00ffffff) {
ts_delta = timestamp;
timestamp = prev.timestamp + ts_delta;
}
len = prev.len;
len_left = prev.len_left;
msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id;
break;
case 0xC0:
if (prev.msg_type_id == 0) {
DEBUG_MSG(DLVL_WARN, "Warning: Header type 0xC0 with no valid previous chunk!");
}
timestamp = prev.timestamp + prev.ts_delta;
ts_delta = prev.ts_delta;
len = prev.len;
len_left = prev.len_left;
if (len_left > 0){
timestamp = prev.timestamp;
}
msg_type_id = prev.msg_type_id;
msg_stream_id = prev.msg_stream_id;
break;
}
//calculate chunk length, real length, and length left till complete
if (len_left > 0) {
real_len = len_left;
len_left -= real_len;
} else {
real_len = len;
}
if (real_len > RTMPStream::chunk_rec_max) {
len_left += real_len - RTMPStream::chunk_rec_max;
real_len = RTMPStream::chunk_rec_max;
}
//read extended timestamp, if neccesary
if (timestamp == 0x00ffffff) {
if (!buffer.available(i + 4)) {
return false;
} //can't read timestamp
indata = buffer.copy(i + 4);
timestamp = indata[i++ ] * 256 * 256 * 256;
timestamp += indata[i++ ] * 256 * 256;
timestamp += indata[i++ ] * 256;
timestamp += indata[i++ ];
ts_delta = timestamp;
}
//read data if length > 0, and allocate it
if (real_len > 0) {
if (!buffer.available(i + real_len)) {
return false;
} //can't read all data (yet)
buffer.remove(i); //remove the header
if (prev.len_left > 0) {
data = prev.data + buffer.remove(real_len); //append the data and remove from buffer
} else {
data = buffer.remove(real_len); //append the data and remove from buffer
}
lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i + real_len;
if (len_left == 0) {
return true;
} else {
return Parse(buffer);
}
} else {
buffer.remove(i); //remove the header
data = "";
indata = indata.substr(i + real_len);
lastrecv[cs_id] = *this;
RTMPStream::rec_cnt += i + real_len;
return true;
}
} //Parse
#include <iomanip>
/// Does the handshake. Expects handshake_in to be filled, and fills handshake_out.
/// After calling this function, don't forget to read and ignore 1536 extra bytes,
/// these are the handshake response and not interesting for us because we don't do client
/// verification.
bool RTMPStream::doHandshake() {
char Version;
//Read C0
if (handshake_in.size() < 1537) {
DEBUG_MSG(DLVL_FAIL, "Handshake wasn't filled properly (%lu/1537) - aborting!", handshake_in.size());
return false;
}
Version = RTMPStream::handshake_in[0];
uint8_t * Client = (uint8_t *)RTMPStream::handshake_in.data() + 1;
RTMPStream::handshake_out.resize(3073);
uint8_t * Server = (uint8_t *)RTMPStream::handshake_out.data() + 1;
RTMPStream::rec_cnt += 1537;
//Build S1 Packet
*((uint32_t *)Server) = 0; //time zero
*(((uint32_t *)(Server + 4))) = htonl(0x01020304); //version 1 2 3 4
for (int i = 8; i < 3072; ++i) {
Server[i] = FILLER_DATA[i % sizeof(FILLER_DATA)];
} //"random" data
bool encrypted = (Version == 6);
DEBUG_MSG(DLVL_HIGH, "Handshake version is %hhi", Version);
uint8_t _validationScheme = 5;
if (ValidateClientScheme(Client, 0)) _validationScheme = 0;
if (ValidateClientScheme(Client, 1)) _validationScheme = 1;
DEBUG_MSG(DLVL_HIGH, "Handshake type is %hhi, encryption is %s", _validationScheme, encrypted ? "on" : "off");
uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme);
uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme);
//FIRST 1536 bytes for server response
char pTempBuffer[1504];
memcpy(pTempBuffer, Server, serverDigestOffset);
memcpy(pTempBuffer + serverDigestOffset, Server + serverDigestOffset + 32, 1504 - serverDigestOffset);
Secure::hmac_sha256bin(pTempBuffer, 1504, genuineFMSKey, 36, (char*)Server + serverDigestOffset);
//SECOND 1536 bytes for server response
char pTempHash[32];
Secure::hmac_sha256bin((char*)Client + keyChallengeIndex, 32, genuineFMSKey, 68, pTempHash);
Secure::hmac_sha256bin((char*)Server + 1536, 1536 - 32, pTempHash, 32, (char*)Server + 1536 * 2 - 32);
Server[ -1] = Version;
RTMPStream::snd_cnt += 3073;
return true;
}

70
lib/rtmpchunks.h Normal file
View file

@ -0,0 +1,70 @@
/// \file rtmpchunks.h
/// Holds all headers for the RTMPStream namespace.
#pragma once
#include <map>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string>
#include <arpa/inet.h>
#include "socket.h"
//forward declaration of FLV::Tag to avoid circular dependencies.
namespace FLV {
class Tag;
}
/// Contains all functions and classes needed for RTMP connections.
namespace RTMPStream {
extern unsigned int chunk_rec_max; ///< Maximum size for a received chunk.
extern unsigned int chunk_snd_max; ///< Maximum size for a sent chunk.
extern unsigned int rec_window_size; ///< Window size for receiving.
extern unsigned int snd_window_size; ///< Window size for sending.
extern unsigned int rec_window_at; ///< Current position of the receiving window.
extern unsigned int snd_window_at; ///< Current position of the sending window.
extern unsigned int rec_cnt; ///< Counter for total data received, in bytes.
extern unsigned int snd_cnt; ///< Counter for total data sent, in bytes.
extern timeval lastrec; ///< Timestamp of last time data was received.
/// Holds a single RTMP chunk, either send or receive direction.
class Chunk {
public:
unsigned char headertype; ///< For input chunks, the type of header. This is calculated automatically for output chunks.
unsigned int cs_id; ///< ContentStream ID
unsigned int timestamp; ///< Timestamp of this chunk.
unsigned int ts_delta; ///< Last timestamp delta.
unsigned int len; ///< Length of the complete chunk.
unsigned int real_len; ///< Length of this particular part of it.
unsigned int len_left; ///< Length not yet received, out of complete chunk.
unsigned char msg_type_id; ///< Message Type ID
unsigned int msg_stream_id; ///< Message Stream ID
std::string data; ///< Payload of chunk.
Chunk();
bool Parse(std::string & data);
bool Parse(Socket::Buffer & data);
std::string & Pack();
};
//RTMPStream::Chunk
extern std::map<unsigned int, Chunk> lastsend;
extern std::map<unsigned int, Chunk> lastrecv;
std::string & SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data);
std::string & SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts);
std::string & SendMedia(FLV::Tag & tag);
std::string & SendCTL(unsigned char type, unsigned int data);
std::string & SendCTL(unsigned char type, unsigned int data, unsigned char data2);
std::string & SendUSR(unsigned char type, unsigned int data);
std::string & SendUSR(unsigned char type, unsigned int data, unsigned int data2);
/// This value should be set to the first 1537 bytes received.
extern std::string handshake_in;
/// This value is the handshake response that is to be sent out.
extern std::string handshake_out;
/// Does the handshake. Expects handshake_in to be filled, and fills handshake_out.
bool doHandshake();
} //RTMPStream namespace

990
lib/shared_memory.cpp Normal file
View file

@ -0,0 +1,990 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <iostream>
#include "defines.h"
#include "shared_memory.h"
#include "stream.h"
#include "procs.h"
namespace IPC {
/// Stores a long value of val in network order to the pointer p.
static void htobl(char * p, long val) {
p[0] = (val >> 24) & 0xFF;
p[1] = (val >> 16) & 0xFF;
p[2] = (val >> 8) & 0xFF;
p[3] = val & 0xFF;
}
/// Stores a short value of val in network order to the pointer p.
static void htobs(char * p, short val) {
p[0] = (val >> 8) & 0xFF;
p[1] = val & 0xFF;
}
/// Stores a long long value of val in network order to the pointer p.
static void htobll(char * p, long long val) {
p[0] = (val >> 56) & 0xFF;
p[1] = (val >> 48) & 0xFF;
p[2] = (val >> 40) & 0xFF;
p[3] = (val >> 32) & 0xFF;
p[4] = (val >> 24) & 0xFF;
p[5] = (val >> 16) & 0xFF;
p[6] = (val >> 8) & 0xFF;
p[7] = val & 0xFF;
}
/// Reads a long value of p in host order to val.
static void btohl(char * p, long & val) {
val = ((long)p[0] << 24) | ((long)p[1] << 16) | ((long)p[2] << 8) | p[3];
}
/// Reads a short value of p in host order to val.
static void btohs(char * p, unsigned short & val) {
val = ((short)p[0] << 8) | p[1];
}
/// Reads a long value of p in host order to val.
static void btohl(char * p, unsigned int & val) {
val = ((long)p[0] << 24) | ((long)p[1] << 16) | ((long)p[2] << 8) | p[3];
}
/// Reads a long long value of p in host order to val.
static void btohll(char * p, long long & val) {
val = ((long long)p[0] << 56) | ((long long)p[1] << 48) | ((long long)p[2] << 40) | ((long long)p[3] << 32) | ((long long)p[4] << 24) | ((long long)p[5] << 16) | ((long long)p[6] << 8) | p[7];
}
///\brief Empty semaphore constructor, clears all values
semaphore::semaphore() {
#ifdef __CYGWIN__
mySem = 0;
#else
mySem = SEM_FAILED;
#endif
}
///\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() {
close();
}
///\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();
int timer = 0;
while (!(*this) && timer++ < 10){
#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
if (!(*this)){
if (errno == ENOENT){
Util::wait(500);
}else{
break;
}
}
}
if (!(*this)){
DEBUG_MSG(DLVL_VERYHIGH, "Attempt to open semaphore %s: %s", name, strerror(errno));
}
myName = (char *)name;
}
///\brief Returns the current value of the semaphore
int semaphore::getVal() const {
#ifdef __CYGWIN__
LONG res;
ReleaseSemaphore(mySem, 0, &res);//not really release.... just checking to see if I can get the value this way
#else
int res;
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.size()) {
sem_unlink(myName.c_str());
}
#endif
myName.clear();
}
///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;
len = 0;
master = false;
mapped = 0;
init(name_, len_, master_, autoBackoff);
}
///\brief Creates a copy of a shared page
///\param rhs The page to copy
sharedPage::sharedPage(const sharedPage & rhs) {
handle = 0;
len = 0;
master = false;
mapped = 0;
init(rhs.name, rhs.len, rhs.master);
}
///\brief Default destructor
sharedPage::~sharedPage() {
close();
}
#ifdef SHM_ENABLED
///\brief Unmaps a shared page if allowed
void sharedPage::unmap() {
if (mapped && len) {
#ifdef __CYGWIN__
//under Cygwin, the mapped location is shifted by 4 to contain the page size.
UnmapViewOfFile(mapped-4);
#else
munmap(mapped, len);
#endif
mapped = 0;
len = 0;
}
}
///\brief Closes a shared page if allowed
void sharedPage::close() {
unmap();
if (handle > 0) {
#ifdef __CYGWIN__
CloseHandle(handle);
#else
::close(handle);
if (master && name != "") {
shm_unlink(name.c_str());
}
#endif
handle = 0;
}
}
///\brief Returns whether the shared page is valid or not
sharedPage::operator bool() const {
return mapped != 0;
}
///\brief Assignment operator
void sharedPage::operator =(sharedPage & rhs) {
init(rhs.name, rhs.len, rhs.master);
/// \todo This is bad. The assignment operator changes the rhs value? What the hell?
rhs.master = false;//Make sure the memory does not get unlinked
}
///\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) {
close();
name = name_;
len = len_;
master = master_;
mapped = 0;
if (name.size()) {
#ifdef __CYGWIN__
if (master) {
//Under cygwin, all pages are 4 bytes longer than claimed.
handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, len+4, name.c_str());
} else {
int i = 0;
do {
if (i != 0) {
Util::sleep(1000);
}
handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name.c_str());
i++;
} while (i < 10 && !handle && autoBackoff);
}
if (!handle) {
FAIL_MSG("%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) {
FAIL_MSG("MapViewOfFile for page %s failed: %s", name.c_str(), strerror(errno));
return;
}
//Under cygwin, the extra 4 bytes contain the real size of the page.
if (master){
((unsigned int*)mapped)[0] = len_;
}else{
len = ((unsigned int*)mapped)[0];
}
//Now shift by those 4 bytes.
mapped += 4;
#else
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());
handle = shm_open(name.c_str(), O_CREAT | O_RDWR, ACCESSPERMS);
} else {
int i = 0;
while (i < 10 && handle == -1 && autoBackoff) {
i++;
Util::sleep(1000);
handle = shm_open(name.c_str(), O_RDWR, ACCESSPERMS);
}
}
}
if (handle == -1) {
FAIL_MSG("shm_open for page %s failed: %s", name.c_str(), strerror(errno));
return;
}
if (master) {
if (ftruncate(handle, 0) < 0) {
FAIL_MSG("truncate to zero for page %s failed: %s", name.c_str(), strerror(errno));
return;
}
if (ftruncate(handle, len) < 0) {
FAIL_MSG("truncate to %lld for page %s failed: %s", len, name.c_str(), strerror(errno));
return;
}
} else {
struct stat buffStats;
int xRes = fstat(handle, &buffStats);
if (xRes < 0) {
return;
}
len = buffStats.st_size;
}
mapped = (char *)mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0);
if (mapped == MAP_FAILED) {
FAIL_MSG("mmap for page %s failed: %s", name.c_str(), strerror(errno));
mapped = 0;
return;
}
#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) {
handle = 0;
name = name_;
len = len_;
master = master_;
mapped = 0;
init(name_, len_, master_, autoBackoff);
}
///\brief Creates a copy of a shared page
///\param rhs The page to copy
sharedFile::sharedFile(const sharedFile & rhs) {
handle = 0;
name = "";
len = 0;
master = false;
mapped = 0;
init(rhs.name, rhs.len, rhs.master);
}
///\brief Returns whether the shared file is valid or not
sharedFile::operator bool() const {
return mapped != 0;
}
///\brief Assignment operator
void sharedFile::operator =(sharedFile & rhs) {
init(rhs.name, rhs.len, rhs.master);
rhs.master = false;//Make sure the memory does not get unlinked
}
///\brief Unmaps a shared file if allowed
void sharedFile::unmap() {
if (mapped && len) {
munmap(mapped, len);
mapped = 0;
len = 0;
}
}
/// Unmaps, closes and unlinks (if master and name is set) the shared file.
void sharedFile::close() {
unmap();
if (handle > 0) {
::close(handle);
if (master && name != "") {
unlink(name.c_str());
}
handle = 0;
}
}
///\brief Initialize a page, de-initialize before if needed
///\param name_ The name of the page to be created
///\param len_ The size to make the page
///\param master_ Whether to create or merely open the page
///\param autoBackoff When only opening the page, wait for it to appear or fail
void sharedFile::init(std::string name_, unsigned int len_, bool master_, bool autoBackoff) {
close();
name = name_;
len = len_;
master = master_;
mapped = 0;
if (name.size()) {
/// \todo Use ACCESSPERMS instead of 0600?
handle = open(std::string(Util::getTmpFolder() + name).c_str(), (master ? O_CREAT | O_TRUNC | O_EXCL : 0) | O_RDWR, (mode_t)0600);
if (handle == -1) {
if (master) {
DEBUG_MSG(DLVL_HIGH, "Overwriting old file for %s", name.c_str());
handle = open(std::string(Util::getTmpFolder() + name).c_str(), O_CREAT | O_TRUNC | O_RDWR, (mode_t)0600);
} else {
int i = 0;
while (i < 10 && handle == -1 && autoBackoff) {
i++;
Util::sleep(1000);
handle = open(std::string(Util::getTmpFolder() + name).c_str(), O_RDWR, (mode_t)0600);
}
}
}
if (handle == -1) {
perror(std::string("open for file " + name + " failed").c_str());
return;
}
if (master) {
if (ftruncate(handle, len) < 0) {
perror(std::string("ftruncate to len for file " + name + " failed").c_str());
return;
}
} else {
struct stat buffStats;
int xRes = fstat(handle, &buffStats);
if (xRes < 0) {
return;
}
len = buffStats.st_size;
}
mapped = (char *)mmap(0, len, PROT_READ | PROT_WRITE, MAP_SHARED, handle, 0);
if (mapped == MAP_FAILED) {
mapped = 0;
return;
}
}
}
///\brief Default destructor
sharedFile::~sharedFile() {
close();
}
///\brief StatExchange constructor, sets the datapointer to the given value
statExchange::statExchange(char * _data) : data(_data) {}
///\brief Sets timestamp of the current stats
void statExchange::now(long long int time) {
htobll(data, time);
}
///\brief Gets timestamp of the current stats
long long int statExchange::now() {
long long int result;
btohll(data, result);
return result;
}
///\brief Sets time currently connected
void statExchange::time(long time) {
htobl(data + 8, time);
}
///\brief Gets time currently connected
long statExchange::time() {
long result;
btohl(data + 8, result);
return result;
}
///\brief Sets the last viewing second of this user
void statExchange::lastSecond(long time) {
htobl(data + 12, time);
}
///\brief Gets the last viewing second of this user
long statExchange::lastSecond() {
long result;
btohl(data + 12, result);
return result;
}
///\brief Sets the amount of bytes received
void statExchange::down(long long int bytes) {
htobll(data + 16, bytes);
}
///\brief Gets the amount of bytes received
long long int statExchange::down() {
long long int result;
btohll(data + 16, result);
return result;
}
///\brief Sets the amount of bytes sent
void statExchange::up(long long int bytes) {
htobll(data + 24, bytes);
}
///\brief Gets the amount of bytes sent
long long int statExchange::up() {
long long int result;
btohll(data + 24, result);
return result;
}
///\brief Sets the host of this connection
void statExchange::host(std::string name) {
if (name.size() < 16){
memset(data+32, 0, 16);
}
memcpy(data + 32, name.c_str(), std::min((int)name.size(), 16));
}
///\brief Gets the host of this connection
std::string statExchange::host() {
return std::string(data + 32, 16);
}
///\brief Sets the name of the stream this user is viewing
void statExchange::streamName(std::string name) {
size_t splitChar = name.find_first_of("+ ");
if (splitChar != std::string::npos){
name[splitChar] = '+';
}
memcpy(data + 48, name.c_str(), std::min((int)name.size(), 100));
}
///\brief Gets the name of the stream this user is viewing
std::string statExchange::streamName() {
return std::string(data + 48, strnlen(data + 48, 100));
}
///\brief Sets the name of the connector through which this user is viewing
void statExchange::connector(std::string name) {
memcpy(data + 148, name.c_str(), std::min((int)name.size(), 20));
}
///\brief Gets the name of the connector through which this user is viewing
std::string statExchange::connector() {
return std::string(data + 148, std::min((int)strlen(data + 148), 20));
}
///\brief Sets checksum field
void statExchange::crc(unsigned int sum) {
htobl(data + 186, sum);
}
///\brief Gets checksum field
unsigned int statExchange::crc() {
unsigned int result;
btohl(data + 186, result);
return result;
}
///\brief Creates a semaphore guard, locks the semaphore on call
semGuard::semGuard(semaphore * thisSemaphore) : mySemaphore(thisSemaphore) {
mySemaphore->wait();
}
///\brief Destructs a semaphore guard, unlocks the semaphore on call
semGuard::~semGuard() {
mySemaphore->post();
}
///\brief Default constructor, erases all the values
sharedServer::sharedServer() {
payLen = 0;
hasCounter = false;
amount = 0;
}
///\brief Desired constructor, initializes after cleaning.
///\param name The basename of this server
///\param len The lenght of the payload
///\param withCounter Whether the content should have a counter
sharedServer::sharedServer(std::string name, int len, bool withCounter) {
sharedServer();
init(name, len, withCounter);
}
///\brief Initialize the server
///\param name The basename of this server
///\param len The lenght of the payload
///\param withCounter Whether the content should have a counter
void sharedServer::init(std::string name, int len, bool withCounter) {
amount = 0;
if (mySemaphore) {
mySemaphore.close();
}
if (baseName != "") {
mySemaphore.unlink();
}
myPages.clear();
baseName = "/" + name;
payLen = len;
hasCounter = withCounter;
mySemaphore.open(baseName.c_str(), O_CREAT | O_EXCL | O_RDWR, ACCESSPERMS, 1);
if (!mySemaphore) {
mySemaphore.open(baseName.c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1);
}
if (!mySemaphore) {
DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return;
}
semGuard tmpGuard(&mySemaphore);
newPage();
}
///\brief The deconstructor
sharedServer::~sharedServer() {
mySemaphore.close();
mySemaphore.unlink();
}
///\brief Determines whether a sharedServer is valid
sharedServer::operator bool() const {
return myPages.size();
}
///\brief Creates the next page with the correct size
void sharedServer::newPage() {
sharedPage tmp(std::string(baseName.substr(1) + (char)(myPages.size() + (int)'A')), std::min(((8192 * 2)<< myPages.size()), (32 * 1024 * 1024)), true);
myPages.insert(tmp);
tmp.master = false;
DEBUG_MSG(DLVL_VERYHIGH, "Created a new page: %s", tmp.name.c_str());
}
///\brief Deletes the highest allocated page
void sharedServer::deletePage() {
if (myPages.size() == 1) {
DEBUG_MSG(DLVL_WARN, "Can't remove last page for %s", baseName.c_str());
return;
}
myPages.erase((*myPages.rbegin()));
}
///\brief Determines whether an id is currently in use or not
bool sharedServer::isInUse(unsigned int id) {
unsigned int i = 0;
for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) {
//return if we reached the end
if (!it->mapped || !it->len) {
return false;
}
//not on this page? skip to next.
if (it->len < (id - i)*payLen) {
i += it->len / payLen;
continue;
}
if (hasCounter) {
//counter? return true if it is non-zero.
return (it->mapped[(id - i) * payLen] != 0);
} else {
//no counter - check the entire size for being all zeroes.
for (unsigned int j = 0; j < payLen; ++j) {
if (it->mapped[(id - i)*payLen + j]) {
return true;
}
}
return false;
}
}
//only happens if we run out of pages
return false;
}
///\brief Parse each of the possible payload pieces, and runs a callback on it if in use.
void sharedServer::parseEach(void (*callback)(char * data, size_t len, unsigned int id)) {
char * empty = 0;
if (!hasCounter) {
empty = (char *)malloc(payLen * sizeof(char));
memset(empty, 0, payLen);
}
semGuard tmpGuard(&mySemaphore);
unsigned int id = 0;
unsigned int userCount=0;
unsigned int emptyCount = 0;
for (std::set<sharedPage>::iterator it = myPages.begin(); it != myPages.end(); it++) {
if (!it->mapped || !it->len) {
DEBUG_MSG(DLVL_FAIL, "Something went terribly wrong?");
break;
}
userCount = 0;
unsigned int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= it->len) {
if (hasCounter) {
if (it->mapped[offset] != 0) {
char * counter = it->mapped+offset;
//increase the count if needed
++userCount;
if (id >= amount) {
amount = id + 1;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
}
unsigned short tmpPID = *((unsigned short *)(it->mapped+1+offset+payLen-2));
if(!Util::Procs::isRunnning(tmpPID) && !(*counter == 126 || *counter == 127 || *counter == 254 || *counter == 255)){
WARN_MSG("process disappeared, timing out. (pid %d)", tmpPID);
*counter = 126; //if process is already dead, instant timeout.
}
callback(it->mapped + offset + 1, payLen, id);
switch (*counter) {
case 127:
DEBUG_MSG(DLVL_HIGH, "Client %u requested disconnect", id);
break;
case 126:
DEBUG_MSG(DLVL_WARN, "Client %u timed out", id);
break;
case 255:
DEBUG_MSG(DLVL_HIGH, "Client %u disconnected on request", id);
break;
case 254:
DEBUG_MSG(DLVL_WARN, "Client %u disconnect timed out", id);
break;
default:
if(*counter > 10 && *counter < 126 ){
if(*counter < 30){
if (*counter > 15){
WARN_MSG("Process %d is unresponsive",tmpPID);
}
Util::Procs::Stop(tmpPID); //soft kill
} else {
ERROR_MSG("Killing unresponsive process %d", tmpPID);
Util::Procs::Murder(tmpPID); //improved kill
}
}
break;
}
if (*counter == 127 || *counter == 126 || *counter == 255 || *counter == 254) {
memset(it->mapped + offset + 1, 0, payLen);
it->mapped[offset] = 0;
} else {
it->mapped[offset] ++;
}
} else {
//stop if we're past the amount counted and we're empty
if (id >= amount - 1) {
//bring the counter down if this was the last element
if (id == amount - 1) {
amount = id;
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
break;
}
}
} else {
if (memcmp(empty, it->mapped + offset, payLen)) {
++userCount;
//increase the count if needed
if (id >= amount) {
amount = id + 1;
DEBUG_MSG(DLVL_VERYHIGH, "Shared memory %s is now at count %u", baseName.c_str(), amount);
}
callback(it->mapped + offset, payLen, id);
} else {
//stop if we're past the amount counted and we're empty
if (id >= amount - 1) {
//bring the counter down if this was the last element
if (id == amount - 1) {
amount = id;
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
if (empty) {
free(empty);
}
break;
}
}
}
offset += payLen + (hasCounter ? 1 : 0);
id ++;
}
if(userCount==0) {
++emptyCount;
} else {
emptyCount=0;
}
}
if( emptyCount > 1){
deletePage();
} else if( !emptyCount ){
newPage();
}
if (empty) {
free(empty);
}
}
///\brief Creates an empty shared client
sharedClient::sharedClient() {
hasCounter = 0;
payLen = 0;
offsetOnPage = 0;
}
///\brief Copy constructor for sharedClients
///\param rhs The client ro copy
sharedClient::sharedClient(const sharedClient & rhs) {
baseName = rhs.baseName;
payLen = rhs.payLen;
hasCounter = rhs.hasCounter;
#ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
#else
mySemaphore.open(baseName.c_str(), O_RDWR);
#endif
if (!mySemaphore) {
DEBUG_MSG(DLVL_FAIL, "Creating semaphore failed: %s", strerror(errno));
return;
}
semGuard tmpGuard(&mySemaphore);
myPage.init(rhs.myPage.name, rhs.myPage.len, rhs.myPage.master);
offsetOnPage = rhs.offsetOnPage;
}
///\brief Assignment operator
void sharedClient::operator =(const sharedClient & rhs) {
baseName = rhs.baseName;
payLen = rhs.payLen;
hasCounter = rhs.hasCounter;
#ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
#else
mySemaphore.open(baseName.c_str(), O_RDWR);
#endif
if (!mySemaphore) {
DEBUG_MSG(DLVL_FAIL, "Creating copy of semaphore %s failed: %s", baseName.c_str(), strerror(errno));
return;
}
semGuard tmpGuard(&mySemaphore);
myPage.init(rhs.myPage.name, rhs.myPage.len, rhs.myPage.master);
offsetOnPage = rhs.offsetOnPage;
}
///\brief SharedClient Constructor, allocates space on the correct page.
///\param name The basename of the server to connect to
///\param len The size of the payload to allocate
///\param withCounter Whether or not this payload has a counter
sharedClient::sharedClient(std::string name, int len, bool withCounter) : baseName("/"+name), payLen(len), offsetOnPage(-1), hasCounter(withCounter) {
#ifdef __APPLE__
//note: O_CREAT is only needed for mac, probably
mySemaphore.open(baseName.c_str(), O_RDWR | O_CREAT, 0);
#else
mySemaphore.open(baseName.c_str(), O_RDWR);
#endif
if (!mySemaphore) {
DEBUG_MSG(DLVL_FAIL, "Creating semaphore %s failed: %s", baseName.c_str(), strerror(errno));
return;
}
char * empty = 0;
if (!hasCounter) {
empty = (char *)malloc(payLen * sizeof(char));
if (!empty) {
DEBUG_MSG(DLVL_FAIL, "Failed to allocate %u bytes for empty payload!", payLen);
return;
}
memset(empty, 0, payLen);
}
while (offsetOnPage == -1){
{
semGuard tmpGuard(&mySemaphore);
for (char i = 'A'; i <= 'Z'; i++) {
myPage.init(baseName.substr(1) + i, (4096 << (i - 'A')), false, false);
if (!myPage.mapped){
break;
}
int offset = 0;
while (offset + payLen + (hasCounter ? 1 : 0) <= myPage.len) {
if ((hasCounter && myPage.mapped[offset] == 0) || (!hasCounter && !memcmp(myPage.mapped + offset, empty, payLen))) {
offsetOnPage = offset;
if (hasCounter) {
myPage.mapped[offset] = 1;
*((unsigned short *)(myPage.mapped+1+offset+len-2))=getpid();
}
break;
}
offset += payLen + (hasCounter ? 1 : 0);
}
if (offsetOnPage != -1) {
break;
}
}
}
if (offsetOnPage == -1){
Util::wait(500);
}
}
free(empty);
}
///\brief The deconstructor
sharedClient::~sharedClient() {
mySemaphore.close();
}
///\brief Writes data to the shared data
void sharedClient::write(char * data, int len) {
if (hasCounter) {
keepAlive();
}
memcpy(myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0), data, std::min(len, payLen));
}
///\brief Indicate that the process is done using this piece of memory, set the counter to finished
void sharedClient::finish() {
if (!myPage.mapped) {
return;
}
if (!hasCounter) {
DEBUG_MSG(DLVL_WARN, "Trying to time-out an element without counters");
return;
}
if (myPage.mapped) {
semGuard tmpGuard(&mySemaphore);
myPage.mapped[offsetOnPage] = 127;
}
}
///\brief Re-initialize the counter
void sharedClient::keepAlive() {
if (!hasCounter) {
DEBUG_MSG(DLVL_WARN, "Trying to keep-alive an element without counters");
return;
}
if (myPage.mapped[offsetOnPage] < 128) {
myPage.mapped[offsetOnPage] = 1;
} else {
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() {
if (!myPage.mapped) {
return 0;
}
return (myPage.mapped + offsetOnPage + (hasCounter ? 1 : 0));
}
}

224
lib/shared_memory.h Normal file
View file

@ -0,0 +1,224 @@
#pragma once
#include <string>
#include <set>
#include "timing.h"
#ifdef __CYGWIN__
#include <windows.h>
#else
#include <semaphore.h>
#endif
#define STAT_EX_SIZE 172
#define PLAY_EX_SIZE 32
namespace IPC {
///\brief A class used for the exchange of statistics over shared memory.
class statExchange {
public:
statExchange(char * _data);
void now(long long int time);
long long int now();
void time(long time);
long time();
void lastSecond(long time);
long lastSecond();
void down(long long int bytes);
long long int down();
void up(long long int bytes);
long long int up();
void host(std::string name);
std::string host();
void streamName(std::string name);
std::string streamName();
void connector(std::string name);
std::string connector();
void crc(unsigned int sum);
unsigned int crc();
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)
/// - 100 byte - streamName (name of the stream peer is viewing)
/// - 20 byte - connector (name of the connector the peer is using)
/// - 4 byte - CRC32 of user agent (or zero if none)
char * data;
};
///\brief A class used for the abstraction of semaphores
class semaphore {
public:
semaphore();
semaphore(const char * name, int oflag, mode_t mode, unsigned int value);
~semaphore();
operator bool() const;
void open(const char * name, int oflag, mode_t mode = 0, unsigned int value = 0);
int getVal() const;
void post();
void wait();
bool tryWait();
void close();
void unlink();
private:
#ifdef __CYGWIN__
HANDLE mySem;
#else
sem_t * mySem;
#endif
std::string myName;
};
///\brief A class used as a semaphore guard
class semGuard {
public:
semGuard(semaphore * thisSemaphore);
~semGuard();
private:
///\brief The semaphore to guard.
semaphore * mySemaphore;
};
///\brief A class for managing shared files.
class sharedFile {
public:
sharedFile(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true);
sharedFile(const sharedFile & rhs);
~sharedFile();
operator bool() const;
void init(std::string name_, unsigned int len_, bool master_ = false, bool autoBackoff = true);
void operator =(sharedFile & rhs);
bool operator < (const sharedFile & rhs) const {
return name < rhs.name;
}
void close();
void unmap();
///\brief The fd handle of the opened shared file
int handle;
///\brief The name of the opened shared file
std::string name;
///\brief The size in bytes of the opened shared file
long long int len;
///\brief Whether this class should unlink the shared file upon deletion or not
bool master;
///\brief A pointer to the payload of the file file
char * mapped;
};
#ifdef SHM_ENABLED
///\brief A class for managing shared memory pages.
class sharedPage {
public:
sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true);
sharedPage(const sharedPage & rhs);
~sharedPage();
operator bool() const;
void init(std::string name_, unsigned int len_, bool master_ = false, bool autoBackoff = true);
void operator =(sharedPage & rhs);
bool operator < (const sharedPage & rhs) const {
return name < rhs.name;
}
void unmap();
void close();
#ifdef __CYGWIN__
///\brief The handle of the opened shared memory page
HANDLE handle;
#else
///\brief The fd handle of the opened shared memory page
int handle;
#endif
///\brief The name of the opened shared memory page
std::string name;
///\brief The size in bytes of the opened shared memory page
long long int len;
///\brief Whether this class should unlink the shared memory upon deletion or not
bool master;
///\brief A pointer to the payload of the page
char * mapped;
};
#else
///\brief A class for handling shared memory pages.
///Uses shared files at its backbone, defined for portability
class sharedPage: public sharedFile {
public:
sharedPage(std::string name_ = "", unsigned int len_ = 0, bool master_ = false, bool autoBackoff = true);
sharedPage(const sharedPage & rhs);
~sharedPage();
};
#endif
///\brief The server part of a server/client model for shared memory.
///
///The server manages the shared memory pages, and allocates new pages when needed.
///
///Pages are created with a basename + index, where index is in the range of 'A' - 'Z'
///Each time a page is nearly full, the next page is created with a size double to the previous one.
///
///Clients should allocate payLen bytes at a time, possibly with the addition of a counter.
///If no such length can be allocated, the next page should be tried, and so on.
class sharedServer {
public:
sharedServer();
sharedServer(std::string name, int len, bool withCounter = false);
void init(std::string name, int len, bool withCounter = false);
~sharedServer();
void parseEach(void (*callback)(char * data, size_t len, unsigned int id));
operator bool() const;
///\brief The amount of connected clients
unsigned int amount;
private:
bool isInUse(unsigned int id);
void newPage();
void deletePage();
///\brief The basename of the shared pages.
std::string baseName;
///\brief The length of each consecutive piece of payload
unsigned int payLen;
///\brief The set of sharedPage structures to manage the actual memory
std::set<sharedPage> myPages;
///\brief A semaphore that is locked upon creation and deletion of the page, to ensure no new data is allocated during this step.
semaphore mySemaphore;
///\brief Whether the payload has a counter, if so, it is added in front of the payload
bool hasCounter;
};
///\brief The client part of a server/client model for shared memory.
///
///The server manages the shared memory pages, and allocates new pages when needed.
///
///Pages are created with a basename + index, where index is in the range of 'A' - 'Z'
///Each time a page is nearly full, the next page is created with a size double to the previous one.
///
///Clients should allocate payLen bytes at a time, possibly with the addition of a counter.
///If no such length can be allocated, the next page should be tried, and so on.
class sharedClient {
public:
sharedClient();
sharedClient(const sharedClient & rhs);
sharedClient(std::string name, int len, bool withCounter = false);
void operator = (const sharedClient & rhs);
~sharedClient();
void write(char * data, int len);
void finish();
void keepAlive();
char * getData();
private:
///\brief The basename of the shared pages.
std::string baseName;
///\brief The shared page this client has reserved a space on.
sharedPage myPage;
///\brief A semaphore that is locked upon trying to allocate space on a page
semaphore mySemaphore;
///\brief The size in bytes of the opened page
int payLen;
///\brief The offset of the payload reserved for this client within the opened page
int offsetOnPage;
///\brief Whether the payload has a counter, if so, it is added in front of the payload
bool hasCounter;
};
}

1187
lib/socket.cpp Normal file

File diff suppressed because it is too large Load diff

149
lib/socket.h Normal file
View file

@ -0,0 +1,149 @@
/// \file socket.h
/// A handy Socket wrapper library.
/// Written by Jaron Vietor in 2010 for DDVTech
#pragma once
#include <string>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <deque>
//for being friendly with Socket::Connection down below
namespace Buffer {
class user;
}
///Holds Socket tools.
namespace Socket {
/// A buffer made out of std::string objects that can be efficiently read from and written to.
class Buffer {
private:
std::deque<std::string> data;
public:
unsigned int size();
unsigned int bytes(unsigned int max);
void append(const std::string & newdata);
void append(const char * newdata, const unsigned int newdatasize);
void prepend(const std::string & newdata);
void prepend(const char * newdata, const unsigned int newdatasize);
std::string & get();
bool available(unsigned int count);
std::string remove(unsigned int count);
std::string copy(unsigned int count);
void clear();
};
//Buffer
/// This class is for easy communicating through sockets, either TCP or Unix.
class Connection {
private:
int sock; ///< Internally saved socket number.
int pipes[2]; ///< Internally saved file descriptors for pipe socket simulation.
std::string remotehost; ///< Stores remote host address.
unsigned int up;
unsigned int down;
long long int conntime;
Buffer downbuffer; ///< Stores temporary data coming in.
int iread(void * buffer, int len, int flags = 0); ///< Incremental read call.
unsigned int iwrite(const void * buffer, int len); ///< Incremental write call.
bool iread(Buffer & buffer, int flags = 0); ///< Incremental write call that is compatible with Socket::Buffer.
bool iwrite(std::string & buffer); ///< Write call that is compatible with std::string.
public:
//friends
friend class ::Buffer::user;
//constructors
Connection(); ///< Create a new disconnected base socket.
Connection(int sockNo); ///< Create a new base socket.
Connection(std::string hostname, int port, bool nonblock); ///< Create a new TCP socket.
Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket.
Connection(int write, int read); ///< Simulate a socket using two file descriptors.
//generic methods
void close(); ///< Close connection.
void drop(); ///< Close connection without shutdown.
void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false).
bool isBlocking(); ///< Check if this socket is blocking (true) or nonblocking (false).
std::string getHost(); ///< Gets hostname for connection, if available.
std::string getBinHost();
void setHost(std::string host); ///< Sets hostname for connection manually.
int getSocket(); ///< Returns internal socket number.
int getPureSocket(); ///< Returns non-piped internal socket number.
std::string getError(); ///< Returns a string describing the last error that occured.
bool connected() const; ///< Returns the connected-state for this socket.
bool isAddress(std::string addr);
//buffered i/o methods
bool spool(); ///< Updates the downbufferinternal variables.
bool peek(); ///< Clears the downbuffer and fills it with peek
Buffer & Received(); ///< Returns a reference to the download buffer.
void SendNow(const std::string & data); ///< Will not buffer anything but always send right away. Blocks.
void SendNow(const char * data); ///< Will not buffer anything but always send right away. Blocks.
void SendNow(const char * data, size_t len); ///< Will not buffer anything but always send right away. Blocks.
//stats related methods
unsigned int connTime();///< Returns the time this socket has been connected.
unsigned int dataUp(); ///< Returns total amount of bytes sent.
unsigned int dataDown(); ///< Returns total amount of bytes received.
std::string getStats(std::string C); ///< Returns a std::string of stats, ended by a newline.
friend class Server;
bool Error; ///< Set to true if a socket error happened.
bool Blocking; ///< Set to true if a socket is currently or wants to be blocking.
//overloaded operators
bool operator==(const Connection & B) const;
bool operator!=(const Connection & B) const;
operator bool() const;
};
/// This class is for easily setting up listening socket, either TCP or Unix.
class Server {
private:
std::string errors; ///< Stores errors that may have occured.
int sock; ///< Internally saved socket number.
bool IPv6bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv6 socket
bool IPv4bind(int port, std::string hostname, bool nonblock); ///< Attempt to bind an IPv4 socket
public:
Server(); ///< Create a new base Server.
Server(int port, std::string hostname = "0.0.0.0", bool nonblock = false); ///< Create a new TCP Server.
Server(std::string adres, bool nonblock = false); ///< Create a new Unix Server.
Connection accept(bool nonblock = false); ///< Accept any waiting connections.
void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false).
bool connected() const; ///< Returns the connected-state for this socket.
bool isBlocking(); ///< Check if this socket is blocking (true) or nonblocking (false).
void close(); ///< Close connection.
void drop(); ///< Close connection without shutdown.
int getSocket(); ///< Returns internal socket number.
};
class UDPConnection {
private:
int sock; ///< Internally saved socket number.
std::string remotehost;///< Stores remote host address
void * destAddr;///< Destination address pointer.
unsigned int destAddr_size;///< Size of the destination address pointer.
unsigned int up;///< Amount of bytes transferred up.
unsigned int down;///< Amount of bytes transferred down.
unsigned int data_size;///< The size in bytes of the allocated space in the data pointer.
public:
char * data;///< Holds the last received packet.
unsigned int data_len; ///< The size in bytes of the last received packet.
UDPConnection(const UDPConnection & o);
UDPConnection(bool nonblock = false);
~UDPConnection();
int getSock();
int bind(int port);
void setBlocking(bool blocking);
void SetDestination(std::string hostname, uint32_t port);
void GetDestination(std::string & hostname, uint32_t & port);
bool Receive();
void SendNow(const std::string & data);
void SendNow(const char * data);
void SendNow(const char * data, size_t len);
};
}

205
lib/stream.cpp Normal file
View file

@ -0,0 +1,205 @@
/// \file stream.cpp
/// Utilities for handling streams.
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include "json.h"
#include "stream.h"
#include "procs.h"
#include "config.h"
#include "socket.h"
#include "defines.h"
#include "shared_memory.h"
#include "dtsc.h"
std::string Util::getTmpFolder() {
std::string dir;
char * tmp_char = 0;
if (!tmp_char) {
tmp_char = getenv("TMP");
}
if (!tmp_char) {
tmp_char = getenv("TEMP");
}
if (!tmp_char) {
tmp_char = getenv("TMPDIR");
}
if (tmp_char) {
dir = tmp_char;
dir += "/mist";
} else {
#if defined(_WIN32) || defined(_CYGWIN_)
dir = "C:/tmp/mist";
#else
dir = "/tmp/mist";
#endif
}
if (access(dir.c_str(), 0) != 0) {
mkdir(dir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); //attempt to create mist folder - ignore failures
}
return dir + "/";
}
/// Filters the streamname, removing invalid characters and converting all
/// letters to lowercase. If a '?' character is found, everything following
/// that character is deleted. The original string is modified. If a '+' or space
/// exists, then only the part before that is sanitized.
void Util::sanitizeName(std::string & streamname) {
//strip anything that isn't numbers, digits or underscores
size_t index = streamname.find_first_of("+ ");
if(index != std::string::npos){
std::string preplus = streamname.substr(0,index);
sanitizeName(preplus);
streamname = preplus+"+"+streamname.substr(index+1);
return;
}
for (std::string::iterator i = streamname.end() - 1; i >= streamname.begin(); --i) {
if (*i == '?') {
streamname.erase(i, streamname.end());
break;
}
if ( !isalpha( *i) && !isdigit( *i) && *i != '_' && *i != '.'){
streamname.erase(i);
} else {
*i = tolower(*i);
}
}
}
/// Starts a process for a VoD stream.
bool Util::startInput(std::string streamname, std::string filename, bool forkFirst) {
if (streamname.size() > 100){
FAIL_MSG("Stream opening denied: %s is longer than 100 characters (%lu).", streamname.c_str(), streamname.size());
return false;
}
IPC::sharedPage mistConfOut("!mistConfig", DEFAULT_CONF_PAGE_SIZE);
IPC::semaphore configLock("!mistConfLock", O_CREAT | O_RDWR, ACCESSPERMS, 1);
configLock.wait();
DTSC::Scan config = DTSC::Scan(mistConfOut.mapped, mistConfOut.len);
sanitizeName(streamname);
std::string smp = streamname.substr(0, streamname.find_first_of("+ "));
//check if smp (everything before + or space) exists
DTSC::Scan stream_cfg = config.getMember("streams").getMember(smp);
if (!stream_cfg){
DEBUG_MSG(DLVL_MEDIUM, "Stream %s not configured", streamname.c_str());
configLock.post();//unlock the config semaphore
return false;
}
//If starting without filename parameter, check if the stream is already active.
//If yes, don't activate again to prevent duplicate inputs.
//It's still possible a duplicate starts anyway, this is caught in the inputs initializer.
//Note: this uses the _whole_ stream name, including + (if any).
//This means "test+a" and "test+b" have separate locks and do not interact with each other.
if (!filename.size()){
IPC::semaphore playerLock(std::string("/lock_" + streamname).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 1);
if (!playerLock.tryWait()) {
playerLock.close();
DEBUG_MSG(DLVL_MEDIUM, "Stream %s already active - not activating again", streamname.c_str());
configLock.post();//unlock the config semaphore
return true;
}
playerLock.post();
playerLock.close();
filename = stream_cfg.getMember("source").asString();
}
std::string player_bin;
bool selected = false;
long long int curPrio = -1;
//check in curConf for capabilities-inputs-<naam>-priority/source_match
DTSC::Scan inputs = config.getMember("capabilities").getMember("inputs");
DTSC::Scan input;
unsigned int input_size = inputs.getSize();
for (unsigned int i = 0; i < input_size; ++i){
input = inputs.getIndice(i);
//if match voor current stream && priority is hoger dan wat we al hebben
if (curPrio < input.getMember("priority").asInt()){
std::string source = input.getMember("source_match").asString();
std::string front = source.substr(0,source.find('*'));
std::string back = source.substr(source.find('*')+1);
DEBUG_MSG(DLVL_MEDIUM, "Checking input %s: %s (%s)", inputs.getIndiceName(i).c_str(), input.getMember("name").asString().c_str(), source.c_str());
if (filename.substr(0,front.size()) == front && filename.substr(filename.size()-back.size()) == back){
player_bin = Util::getMyPath() + "MistIn" + input.getMember("name").asString();
curPrio = input.getMember("priority").asInt();
selected = true;
}
}
}
if (!selected){
configLock.post();//unlock the config semaphore
FAIL_MSG("No compatible input found for stream %s: %s", streamname.c_str(), filename.c_str());
return false;
}
//copy the neccessary arguments to separate storage so we can unlock the config semaphore safely
std::map<std::string, std::string> str_args;
//check required parameters
DTSC::Scan required = input.getMember("required");
unsigned int req_size = required.getSize();
for (unsigned int i = 0; i < req_size; ++i){
std::string opt = required.getIndiceName(i);
if (!stream_cfg.getMember(opt)){
configLock.post();//unlock the config semaphore
FAIL_MSG("Required parameter %s for stream %s missing", opt.c_str(), streamname.c_str());
return false;
}
str_args[required.getIndice(i).getMember("option").asString()] = stream_cfg.getMember(opt).asString();
}
//check optional parameters
DTSC::Scan optional = input.getMember("optional");
unsigned int opt_size = optional.getSize();
for (unsigned int i = 0; i < opt_size; ++i){
std::string opt = optional.getIndiceName(i);
DEBUG_MSG(DLVL_VERYHIGH, "Checking optional %u: %s", i, opt.c_str());
if (stream_cfg.getMember(opt)){
str_args[optional.getIndice(i).getMember("option").asString()] = stream_cfg.getMember(opt).asString();
}
}
//finally, unlock the config semaphore
configLock.post();
DEBUG_MSG(DLVL_MEDIUM, "Starting %s -s %s %s", player_bin.c_str(), streamname.c_str(), filename.c_str());
char * argv[30] = {(char *)player_bin.c_str(), (char *)"-s", (char *)streamname.c_str(), (char *)filename.c_str()};
int argNum = 3;
std::string debugLvl;
if (Util::Config::printDebugLevel != DEBUG && !str_args.count("--debug")){
debugLvl = JSON::Value((long long)Util::Config::printDebugLevel).asString();
argv[++argNum] = (char *)"--debug";
argv[++argNum] = (char *)debugLvl.c_str();
}
for (std::map<std::string, std::string>::iterator it = str_args.begin(); it != str_args.end(); ++it){
argv[++argNum] = (char *)it->first.c_str();
argv[++argNum] = (char *)it->second.c_str();
}
argv[++argNum] = (char *)0;
int pid = 0;
if (forkFirst){
DEBUG_MSG(DLVL_DONTEVEN, "Forking");
pid = fork();
if (pid == -1) {
FAIL_MSG("Forking process for stream %s failed: %s", streamname.c_str(), strerror(errno));
return false;
}
}else{
DEBUG_MSG(DLVL_DONTEVEN, "Not forking");
}
if (pid == 0){
DEBUG_MSG(DLVL_DONTEVEN, "execvp");
execvp(argv[0], argv);
FAIL_MSG("Starting process %s for stream %s failed: %s", argv[0], streamname.c_str(), strerror(errno));
_exit(42);
}
return true;
}

12
lib/stream.h Normal file
View file

@ -0,0 +1,12 @@
/// \file stream.h
/// Utilities for handling streams.
#pragma once
#include <string>
#include "socket.h"
namespace Util {
std::string getTmpFolder();
void sanitizeName(std::string & streamname);
bool startInput(std::string streamname, std::string filename = "", bool forkFirst = true);
}

365
lib/theora.cpp Normal file
View file

@ -0,0 +1,365 @@
#include "theora.h"
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sstream>
#include "defines.h"
namespace theora {
bool header::checkDataSize(unsigned int size){
if (size > datasize){
void * tmp = realloc(data, size);
if (tmp){
data = (char *)tmp;
datasize = size;
return true;
} else {
return false;
}
} else {
return true;
}
}
uint32_t header::getInt32(size_t index){
if (datasize >= (index + 3)){
return (data[index] << 24) + (data[index + 1] << 16) + (data[index + 2] << 8) + data[index + 3];
}
return 0;
}
uint32_t header::getInt24(size_t index){
if (datasize >= (index + 2)){
return 0 + (data[index] << 16) + (data[index + 1] << 8) + data[index + 2];
}
return 0;
}
uint16_t header::getInt16(size_t index){
if (datasize >= (index + 1)){
return 0 + (data[index] << 8) + data[index + 1];
}
return 0;
}
uint32_t header::commentLen(size_t index){
if (datasize >= index + 3){
return data[index] + (data[index + 1] << 8) + (data[index + 2] << 16) + (data[index + 3] << 24);
}
return 0;
}
header::header(char * newData, unsigned int length){
data = newData;
datasize = length;
}
header::~header(){
}
bool isHeader(const char * newData, unsigned int length){
if (length < 7){
return false;
}
if (! newData[0] & 0x80){
DEBUG_MSG(DLVL_FAIL, "newdata != 0x80: %.2X", newData[0]);
return false;
}
if (memcmp(newData + 1, "theora", 6) != 0){
return false;
}
return true;
}
int header::getHeaderType(){
return (data[0] & 0x7F);
}
char header::getVMAJ(){
if (getHeaderType() == 0){
return data[7];
}
return 0;
}
char header::getVMIN(){
if (getHeaderType() == 0){
return data[8];
}
return 0;
}
char header::getVREV(){
if (getHeaderType() == 0){
return data[9];
}
return 0;
}
short header::getFMBW(){
if (getHeaderType() == 0){
return getInt16(10);
}
return 0;
}
short header::getFMBH(){
if (getHeaderType() == 0){
return getInt16(12);
}
return 0;
}
char header::getPICX(){
if (getHeaderType() == 0){
return data[20];
}
return 0;
}
char header::getPICY(){
if (getHeaderType() == 0){
return data[21];
}
return 0;
}
char header::getKFGShift(){
if (getHeaderType() == 0){
return (getInt16(40) >> 5) & 0x1F;
}
return 0;
}
long unsigned int header::getFRN(){
if (getHeaderType() == 0){
return getInt32(22);
}
return 0;
}
long unsigned int header::getPICH(){
if (getHeaderType() == 0){
return getInt24(17);
}
return 0;
}
long unsigned int header::getPICW(){
if (getHeaderType() == 0){
return getInt24(14);
}
return 0;
}
long unsigned int header::getFRD(){
if (getHeaderType() == 0){
return getInt32(26);
}
return 0;
}
long unsigned int header::getPARN(){
if (getHeaderType() == 0){
return getInt24(30);
}
return 0;
}
long unsigned int header::getPARD(){
if (getHeaderType() == 0){
return getInt24(33);
}
return 0;
}
char header::getCS(){
if (getHeaderType() == 0){
return data[36];
}
return 0;
}
long unsigned int header::getNOMBR(){
if (getHeaderType() == 0){
return getInt24(37);
}
return 0;
}
char header::getQUAL(){
if (getHeaderType() == 0){
return (data[40] >> 3) & 0x1F;
}
return 0;
}
char header::getPF(){
if (getHeaderType() == 0){
return (data[41] >> 3) & 0x03;
}
return 0;
}
std::string header::getVendor(){
if (getHeaderType() != 1){
return "";
}
return std::string(data + 11, commentLen(7));
}
long unsigned int header::getNComments(){
if (getHeaderType() != 1){
return 0;
}
int offset = 11 + commentLen(7);
return commentLen(offset);
}
char header::getLFLIMS(size_t index){
if (getHeaderType() != 2){
return 0;
}
if (index >= 64){
return 0;
}
char NBITS = (data[0] >> 5) & 0x07;
return NBITS;
}
std::string header::getUserComment(size_t index){
if (index >= getNComments()){
return "";
}
int offset = 11 + commentLen(7) + 4;
for (size_t i = 0; i < index; i++){
offset += 4 + commentLen(offset);
}
return std::string(data + offset + 4, commentLen(offset));
}
char header::getFTYPE(){
return (data[0] >> 6) & 0x01;
}
bool header::isHeader(){
return ::theora::isHeader(data, datasize);
}
std::string header::toPrettyString(size_t indent){
std::stringstream result;
if(!isHeader()){
result << std::string(indent, ' ') << "Theora Frame" << std::endl;
result << std::string(indent + 2, ' ') << "FType: " << (int)getFTYPE() << std::endl;
return result.str();
}
result << std::string(indent, ' ') << "Theora header" << std::endl;
result << std::string(indent + 2, ' ') << "HeaderType: " << getHeaderType() << std::endl;
switch (getHeaderType()){
case 0:
result << std::string(indent + 2, ' ') << "VMAJ: " << (int)getVMAJ() << std::endl;
result << std::string(indent + 2, ' ') << "VMIN: " << (int)getVMIN() << std::endl;
result << std::string(indent + 2, ' ') << "VREV: " << (int)getVREV() << std::endl;
result << std::string(indent + 2, ' ') << "FMBW: " << getFMBW() << std::endl;
result << std::string(indent + 2, ' ') << "FMBH: " << getFMBH() << std::endl;
result << std::string(indent + 2, ' ') << "PICH: " << getPICH() << std::endl;
result << std::string(indent + 2, ' ') << "PICW: " << getPICW() << std::endl;
result << std::string(indent + 2, ' ') << "PICX: " << (int)getPICX() << std::endl;
result << std::string(indent + 2, ' ') << "PICY: " << (int)getPICY() << std::endl;
result << std::string(indent + 2, ' ') << "FRN: " << getFRN() << std::endl;
result << std::string(indent + 2, ' ') << "FRD: " << getFRD() << std::endl;
result << std::string(indent + 2, ' ') << "PARN: " << getPARN() << std::endl;
result << std::string(indent + 2, ' ') << "PARD: " << getPARD() << std::endl;
result << std::string(indent + 2, ' ') << "CS: " << (int)getCS() << std::endl;
result << std::string(indent + 2, ' ') << "NOMBR: " << getNOMBR() << std::endl;
result << std::string(indent + 2, ' ') << "QUAL: " << (int)getQUAL() << std::endl;
result << std::string(indent + 2, ' ') << "KFGShift: " << (int)getKFGShift() << std::endl;
break;
case 1:
result << std::string(indent + 2, ' ') << "Vendor: " << getVendor() << std::endl;
result << std::string(indent + 2, ' ') << "User Comments (" << getNComments() << "):" << std::endl;
for (long unsigned int i = 0; i < getNComments(); i++){
result << std::string(indent + 4, ' ') << "[" << i << "] " << getUserComment(i) << std::endl;
}
break;
case 2:
result << std::string(indent + 2, ' ') << "NBITS: " << (int)getLFLIMS(0) << std::endl;
}
return result.str();
}
/*
frame::frame(){
data = NULL;
datasize = 0;
}
frame::~frame(){
if (data){
free(data);
}
data = 0;
}
bool frame::checkDataSize(unsigned int size){
if (size > datasize){
void * tmp = realloc(data, size);
if (tmp){
data = (char *)tmp;
datasize = size;
return true;
} else {
return false;
}
} else {
return true;
}
}
bool frame::read(const char * newData, unsigned int length){
if (length < 7){
return false;
}
if ((newData[0] & 0x80)){
return false;
}
if (checkDataSize(length)){
memcpy(data, newData, length);
} else {
return false;
}
return true;
}
char frame::getNQIS(){
return 0;
}
char frame::getQIS(size_t index){
if (index >= 3){
return 0;
}
return 0;
}
*/
/*
long long unsigned int header::parseGranuleUpper(long long unsigned int granPos){
return granPos >> getKFGShift();
}
long long unsigned int header::parseGranuleLower(long long unsigned int granPos){
return (granPos & ((1 << getKFGShift()) - 1));
}*/
/*
std::string frame::toPrettyString(size_t indent){
std::stringstream result;
result << std::string(indent, ' ') << "Theora Frame" << std::endl;
result << std::string(indent + 2, ' ') << "FType: " << (int)getFTYPE() << std::endl;
return result.str();
}*/
}

68
lib/theora.h Normal file
View file

@ -0,0 +1,68 @@
#pragma once
#include<sys/types.h>
#include<stdint.h>
#include<string>
namespace theora {
bool isHeader(const char * newData, unsigned int length);
class header {
public:
~header();
header(char * newData, unsigned int length); // if managed is true, this class manages the data pointer
int getHeaderType();
char getVMAJ();
char getVMIN();
char getVREV();
short getFMBW();
short getFMBH();
long unsigned int getPICW();//movie width
long unsigned int getPICH();//movie height
char getPICX();
char getPICY();
long unsigned int getFRN();//frame rate numerator
long unsigned int getFRD();//frame rate denominator
long unsigned int getPARN();
long unsigned int getPARD();
char getCS();
long unsigned int getNOMBR();
char getQUAL();
char getPF();
char getKFGShift();
std::string getVendor();
long unsigned int getNComments();
std::string getUserComment(size_t index);
char getLFLIMS(size_t index);
std::string toPrettyString(size_t indent = 0); //update this, it should check (header) type and output relevant stuff only.
//long long unsigned int parseGranuleUpper(long long unsigned int granPos);
//long long unsigned int parseGranuleLower(long long unsigned int granPos);
///\todo put this back in pravate
unsigned int datasize;
bool isHeader();
char getFTYPE();//for frames
protected:
uint32_t getInt32(size_t index);
uint32_t getInt24(size_t index);
uint16_t getInt16(size_t index);
uint32_t commentLen(size_t index);
private:
char * data;
bool checkDataSize(unsigned int size);
};
/*
class frame{ // we don't need this. I hope.
public:
frame();
~frame();
bool read(const char* newData, unsigned int length);
char getFTYPE();
char getNQIS();
char getQIS(size_t index);
std::string toPrettyString(size_t indent = 0);
private:
char * data;
unsigned int datasize;
bool checkDataSize(unsigned int size);
};*/
}

99
lib/timing.cpp Normal file
View file

@ -0,0 +1,99 @@
/// \file timing.cpp
/// Utilities for handling time and timestamps.
#include "timing.h"
#include <sys/time.h>//for gettimeofday
#include <time.h>//for time and nanosleep
//emulate clock_gettime() for OSX compatibility
#if defined(__APPLE__) || defined(__MACH__)
#include <mach/clock.h>
#include <mach/mach.h>
#define CLOCK_REALTIME CALENDAR_CLOCK
#define CLOCK_MONOTONIC SYSTEM_CLOCK
void clock_gettime(int ign, struct timespec * ts) {
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), ign, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);
ts->tv_sec = mts.tv_sec;
ts->tv_nsec = mts.tv_nsec;
}
#endif
/// Sleeps for the indicated amount of milliseconds or longer.
/// Will not sleep if ms is negative.
/// Will not sleep for longer than 10 minutes (600000ms).
/// If interrupted by signal, resumes sleep until at least ms milliseconds have passed.
/// Can be slightly off (in positive direction only) depending on OS accuracy.
void Util::wait(int ms){
if (ms < 0) {
return;
}
if (ms > 600000) {
ms = 600000;
}
long long int start = getMS();
long long int now = start;
while (now < start+ms){
sleep(start+ms-now);
now = getMS();
}
}
/// Sleeps for roughly the indicated amount of milliseconds.
/// Will not sleep if ms is negative.
/// Will not sleep for longer than 100 seconds (100000ms).
/// Can be interrupted early by a signal, no guarantee of minimum sleep time.
/// Can be slightly off depending on OS accuracy.
void Util::sleep(int ms) {
if (ms < 0) {
return;
}
if (ms > 100000) {
ms = 100000;
}
struct timespec T;
T.tv_sec = ms / 1000;
T.tv_nsec = 1000000 * (ms % 1000);
nanosleep(&T, 0);
}
long long Util::getNTP() {
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return ((((long long int)t.tv_sec) + 2208988800) << 32) + (t.tv_nsec * 4.2949);
}
/// Gets the current time in milliseconds.
long long int Util::getMS() {
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return ((long long int)t.tv_sec) * 1000 + t.tv_nsec / 1000000;
}
long long int Util::bootSecs() {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return t.tv_sec;
}
/// Gets the current time in microseconds.
long long unsigned int Util::getMicros() {
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return ((long long unsigned int)t.tv_sec) * 1000000 + t.tv_nsec / 1000;
}
/// Gets the time difference in microseconds.
long long unsigned int Util::getMicros(long long unsigned int previous) {
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return ((long long unsigned int)t.tv_sec) * 1000000 + t.tv_nsec / 1000 - previous;
}
/// Gets the amount of seconds since 01/01/1970.
long long int Util::epoch() {
return time(0);
}

15
lib/timing.h Normal file
View file

@ -0,0 +1,15 @@
/// \file timing.h
/// Utilities for handling time and timestamps.
#pragma once
namespace Util {
void wait(int ms); ///< Sleeps for the indicated amount of milliseconds or longer.
void sleep(int ms); ///< Sleeps for roughly the indicated amount of milliseconds.
long long int getMS(); ///< Gets the current time in milliseconds.
long long int bootSecs(); ///< Gets the current system uptime in seconds.
long long unsigned int getMicros();///<Gets the current time in microseconds.
long long unsigned int getMicros(long long unsigned int previous);///<Gets the time difference in microseconds.
long long int getNTP();
long long int epoch(); ///< Gets the amount of seconds since 01/01/1970.
}

291
lib/tinythread.cpp Normal file
View file

@ -0,0 +1,291 @@
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#include <exception>
#include "tinythread.h"
#if defined(_TTHREAD_POSIX_)
#include <unistd.h>
#include <map>
#elif defined(_TTHREAD_WIN32_)
#include <process.h>
#endif
namespace tthread {
//------------------------------------------------------------------------------
// condition_variable
//------------------------------------------------------------------------------
// NOTE 1: The Win32 implementation of the condition_variable class is based on
// the corresponding implementation in GLFW, which in turn is based on a
// description by Douglas C. Schmidt and Irfan Pyarali:
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
//
// NOTE 2: Windows Vista actually has native support for condition variables
// (InitializeConditionVariable, WakeConditionVariable, etc), but we want to
// be portable with pre-Vista Windows versions, so TinyThread++ does not use
// Vista condition variables.
//------------------------------------------------------------------------------
#if defined(_TTHREAD_WIN32_)
#define _CONDITION_EVENT_ONE 0
#define _CONDITION_EVENT_ALL 1
#endif
#if defined(_TTHREAD_WIN32_)
condition_variable::condition_variable() : mWaitersCount(0) {
mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
InitializeCriticalSection(&mWaitersCountLock);
}
#endif
#if defined(_TTHREAD_WIN32_)
condition_variable::~condition_variable() {
CloseHandle(mEvents[_CONDITION_EVENT_ONE]);
CloseHandle(mEvents[_CONDITION_EVENT_ALL]);
DeleteCriticalSection(&mWaitersCountLock);
}
#endif
#if defined(_TTHREAD_WIN32_)
void condition_variable::_wait() {
// Wait for either event to become signaled due to notify_one() or
// notify_all() being called
int result = WaitForMultipleObjects(2, mEvents, FALSE, INFINITE);
// Check if we are the last waiter
EnterCriticalSection(&mWaitersCountLock);
-- mWaitersCount;
bool lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
(mWaitersCount == 0);
LeaveCriticalSection(&mWaitersCountLock);
// If we are the last waiter to be notified to stop waiting, reset the event
if (lastWaiter)
ResetEvent(mEvents[_CONDITION_EVENT_ALL]);
}
#endif
#if defined(_TTHREAD_WIN32_)
void condition_variable::notify_one() {
// Are there any waiters?
EnterCriticalSection(&mWaitersCountLock);
bool haveWaiters = (mWaitersCount > 0);
LeaveCriticalSection(&mWaitersCountLock);
// If we have any waiting threads, send them a signal
if (haveWaiters)
SetEvent(mEvents[_CONDITION_EVENT_ONE]);
}
#endif
#if defined(_TTHREAD_WIN32_)
void condition_variable::notify_all() {
// Are there any waiters?
EnterCriticalSection(&mWaitersCountLock);
bool haveWaiters = (mWaitersCount > 0);
LeaveCriticalSection(&mWaitersCountLock);
// If we have any waiting threads, send them a signal
if (haveWaiters)
SetEvent(mEvents[_CONDITION_EVENT_ALL]);
}
#endif
//------------------------------------------------------------------------------
// POSIX pthread_t to unique thread::id mapping logic.
// Note: Here we use a global thread safe std::map to convert instances of
// pthread_t to small thread identifier numbers (unique within one process).
// This method should be portable across different POSIX implementations.
//------------------------------------------------------------------------------
#if defined(_TTHREAD_POSIX_)
static thread::id _pthread_t_to_ID(const pthread_t & aHandle) {
static mutex idMapLock;
static std::map<pthread_t, unsigned long int> idMap;
static unsigned long int idCount(1);
lock_guard<mutex> guard(idMapLock);
if (idMap.find(aHandle) == idMap.end())
idMap[aHandle] = idCount ++;
return thread::id(idMap[aHandle]);
}
#endif // _TTHREAD_POSIX_
//------------------------------------------------------------------------------
// thread
//------------------------------------------------------------------------------
/// Information to pass to the new thread (what to run).
struct _thread_start_info {
void (*mFunction)(void *); ///< Pointer to the function to be executed.
void * mArg; ///< Function argument for the thread function.
thread * mThread; ///< Pointer to the thread object.
};
// Thread wrapper function.
#if defined(_TTHREAD_WIN32_)
unsigned WINAPI thread::wrapper_function(void * aArg)
#elif defined(_TTHREAD_POSIX_)
void * thread::wrapper_function(void * aArg)
#endif
{
// Get thread startup information
_thread_start_info * ti = (_thread_start_info *) aArg;
try {
// Call the actual client thread function
ti->mFunction(ti->mArg);
} catch (...) {
// Uncaught exceptions will terminate the application (default behavior
// according to C++11)
std::terminate();
}
// The thread is no longer executing
if (ti->mThread) {
lock_guard<mutex> guard(ti->mThread->mDataMutex);
ti->mThread->mNotAThread = true;
ti->mThread->ti_copy = 0;
}
// The thread is responsible for freeing the startup information
delete ti;
return 0;
}
thread::thread(void (*aFunction)(void *), void * aArg) {
// Serialize access to this thread structure
lock_guard<mutex> guard(mDataMutex);
// Fill out the thread startup information (passed to the thread wrapper,
// which will eventually free it)
_thread_start_info * ti = new _thread_start_info;
ti_copy = ti;
ti->mFunction = aFunction;
ti->mArg = aArg;
ti->mThread = this;
// The thread is now alive
mNotAThread = false;
// Create the thread
#if defined(_TTHREAD_WIN32_)
mHandle = (HANDLE) _beginthreadex(0, 0, wrapper_function, (void *) ti, 0, &mWin32ThreadID);
#elif defined(_TTHREAD_POSIX_)
if (pthread_create(&mHandle, NULL, wrapper_function, (void *) ti) != 0)
mHandle = 0;
#endif
// Did we fail to create the thread?
if (!mHandle) {
mNotAThread = true;
ti_copy = 0;
delete ti;
}
}
thread::~thread() {
if (ti_copy) {
((_thread_start_info *)ti_copy)->mThread = 0;
}
if (joinable())
std::terminate();
}
void thread::join() {
if (joinable()) {
#if defined(_TTHREAD_WIN32_)
WaitForSingleObject(mHandle, INFINITE);
CloseHandle(mHandle);
#elif defined(_TTHREAD_POSIX_)
pthread_join(mHandle, NULL);
#endif
}
}
bool thread::joinable() const {
mDataMutex.lock();
bool result = !mNotAThread;
mDataMutex.unlock();
return result;
}
void thread::detach() {
mDataMutex.lock();
if (!mNotAThread) {
#if defined(_TTHREAD_WIN32_)
CloseHandle(mHandle);
#elif defined(_TTHREAD_POSIX_)
pthread_detach(mHandle);
#endif
mNotAThread = true;
}
mDataMutex.unlock();
}
thread::id thread::get_id() const {
if (!joinable())
return id();
#if defined(_TTHREAD_WIN32_)
return id((unsigned long int) mWin32ThreadID);
#elif defined(_TTHREAD_POSIX_)
return _pthread_t_to_ID(mHandle);
#endif
}
unsigned thread::hardware_concurrency() {
#if defined(_TTHREAD_WIN32_)
SYSTEM_INFO si;
GetSystemInfo(&si);
return (int) si.dwNumberOfProcessors;
#elif defined(_SC_NPROCESSORS_ONLN)
return (int) sysconf(_SC_NPROCESSORS_ONLN);
#elif defined(_SC_NPROC_ONLN)
return (int) sysconf(_SC_NPROC_ONLN);
#else
// The standard requires this function to return zero if the number of
// hardware cores could not be determined.
return 0;
#endif
}
//------------------------------------------------------------------------------
// this_thread
//------------------------------------------------------------------------------
thread::id this_thread::get_id() {
#if defined(_TTHREAD_WIN32_)
return thread::id((unsigned long int) GetCurrentThreadId());
#elif defined(_TTHREAD_POSIX_)
return _pthread_t_to_ID(pthread_self());
#endif
}
}

688
lib/tinythread.h Normal file
View file

@ -0,0 +1,688 @@
/* -*- mode: c++; tab-width: 2; indent-tabs-mode: nil; -*-
Copyright (c) 2010-2012 Marcus Geelnard
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef _TINYTHREAD_H_
#define _TINYTHREAD_H_
/// @file
/// @mainpage TinyThread++ API Reference
///
/// @section intro_sec Introduction
/// TinyThread++ is a minimal, portable implementation of basic threading
/// classes for C++.
///
/// They closely mimic the functionality and naming of the C++11 standard, and
/// should be easily replaceable with the corresponding std:: variants.
///
/// @section port_sec Portability
/// The Win32 variant uses the native Win32 API for implementing the thread
/// classes, while for other systems, the POSIX threads API (pthread) is used.
///
/// @section class_sec Classes
/// In order to mimic the threading API of the C++11 standard, subsets of
/// several classes are provided. The fundamental classes are:
/// @li tthread::thread
/// @li tthread::mutex
/// @li tthread::recursive_mutex
/// @li tthread::condition_variable
/// @li tthread::lock_guard
/// @li tthread::fast_mutex
///
/// @section misc_sec Miscellaneous
/// The following special keywords are available: #thread_local.
///
/// For more detailed information (including additional classes), browse the
/// different sections of this documentation. A good place to start is:
/// tinythread.h.
// Which platform are we on?
#if !defined(_TTHREAD_PLATFORM_DEFINED_)
#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
#define _TTHREAD_WIN32_
#else
#define _TTHREAD_POSIX_
#endif
#define _TTHREAD_PLATFORM_DEFINED_
#endif
// Platform specific includes
#if defined(_TTHREAD_WIN32_)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#define __UNDEF_LEAN_AND_MEAN
#endif
#include <windows.h>
#ifdef __UNDEF_LEAN_AND_MEAN
#undef WIN32_LEAN_AND_MEAN
#undef __UNDEF_LEAN_AND_MEAN
#endif
#else
#include <pthread.h>
#include <signal.h>
#include <sched.h>
#include <unistd.h>
#endif
// Generic includes
#include <ostream>
/// TinyThread++ version (major number).
#define TINYTHREAD_VERSION_MAJOR 1
/// TinyThread++ version (minor number).
#define TINYTHREAD_VERSION_MINOR 1
/// TinyThread++ version (full version).
#define TINYTHREAD_VERSION (TINYTHREAD_VERSION_MAJOR * 100 + TINYTHREAD_VERSION_MINOR)
// Do we have a fully featured C++11 compiler?
#if (__cplusplus > 199711L) || (defined(__STDCXX_VERSION__) && (__STDCXX_VERSION__ >= 201001L))
#define _TTHREAD_CPP11_
#endif
// ...at least partial C++11?
#if defined(_TTHREAD_CPP11_) || defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__)
#define _TTHREAD_CPP11_PARTIAL_
#endif
// Macro for disabling assignments of objects.
#ifdef _TTHREAD_CPP11_PARTIAL_
#define _TTHREAD_DISABLE_ASSIGNMENT(name) \
name(const name&) = delete; \
name& operator=(const name&) = delete;
#else
#define _TTHREAD_DISABLE_ASSIGNMENT(name) \
name(const name&); \
name& operator=(const name&);
#endif
/// @def thread_local
/// Thread local storage keyword.
/// A variable that is declared with the @c thread_local keyword makes the
/// value of the variable local to each thread (known as thread-local storage,
/// or TLS). Example usage:
/// @code
/// // This variable is local to each thread.
/// thread_local int variable;
/// @endcode
/// @note The @c thread_local keyword is a macro that maps to the corresponding
/// compiler directive (e.g. @c __declspec(thread)). While the C++11 standard
/// allows for non-trivial types (e.g. classes with constructors and
/// destructors) to be declared with the @c thread_local keyword, most pre-C++11
/// compilers only allow for trivial types (e.g. @c int). So, to guarantee
/// portable code, only use trivial types for thread local storage.
/// @note This directive is currently not supported on Mac OS X (it will give
/// a compiler error), since compile-time TLS is not supported in the Mac OS X
/// executable format. Also, some older versions of MinGW (before GCC 4.x) do
/// not support this directive.
/// @hideinitializer
#if !defined(_TTHREAD_CPP11_) && !defined(thread_local)
#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
#define thread_local __thread
#else
#define thread_local __declspec(thread)
#endif
#endif
/// Main name space for TinyThread++.
/// This namespace is more or less equivalent to the @c std namespace for the
/// C++11 thread classes. For instance, the tthread::mutex class corresponds to
/// the std::mutex class.
namespace tthread {
/// Mutex class.
/// This is a mutual exclusion object for synchronizing access to shared
/// memory areas for several threads. The mutex is non-recursive (i.e. a
/// program may deadlock if the thread that owns a mutex object calls lock()
/// on that object).
/// @see recursive_mutex
class mutex {
public:
/// Constructor.
mutex()
#if defined(_TTHREAD_WIN32_)
: mAlreadyLocked(false)
#endif
{
#if defined(_TTHREAD_WIN32_)
InitializeCriticalSection(&mHandle);
#else
pthread_mutex_init(&mHandle, NULL);
#endif
}
/// Destructor.
~mutex() {
#if defined(_TTHREAD_WIN32_)
DeleteCriticalSection(&mHandle);
#else
pthread_mutex_destroy(&mHandle);
#endif
}
/// Lock the mutex.
/// The method will block the calling thread until a lock on the mutex can
/// be obtained. The mutex remains locked until @c unlock() is called.
/// @see lock_guard
inline void lock() {
#if defined(_TTHREAD_WIN32_)
EnterCriticalSection(&mHandle);
while (mAlreadyLocked) Sleep(1000); // Simulate deadlock...
mAlreadyLocked = true;
#else
pthread_mutex_lock(&mHandle);
#endif
}
/// Try to lock the mutex.
/// The method will try to lock the mutex. If it fails, the function will
/// return immediately (non-blocking).
/// @return @c true if the lock was acquired, or @c false if the lock could
/// not be acquired.
inline bool try_lock() {
#if defined(_TTHREAD_WIN32_)
bool ret = (TryEnterCriticalSection(&mHandle) ? true : false);
if (ret && mAlreadyLocked) {
LeaveCriticalSection(&mHandle);
ret = false;
}
return ret;
#else
return (pthread_mutex_trylock(&mHandle) == 0) ? true : false;
#endif
}
/// Unlock the mutex.
/// If any threads are waiting for the lock on this mutex, one of them will
/// be unblocked.
inline void unlock() {
#if defined(_TTHREAD_WIN32_)
mAlreadyLocked = false;
LeaveCriticalSection(&mHandle);
#else
pthread_mutex_unlock(&mHandle);
#endif
}
_TTHREAD_DISABLE_ASSIGNMENT(mutex)
private:
#if defined(_TTHREAD_WIN32_)
CRITICAL_SECTION mHandle;
bool mAlreadyLocked;
#else
pthread_mutex_t mHandle;
#endif
friend class condition_variable;
};
/// Recursive mutex class.
/// This is a mutual exclusion object for synchronizing access to shared
/// memory areas for several threads. The mutex is recursive (i.e. a thread
/// may lock the mutex several times, as long as it unlocks the mutex the same
/// number of times).
/// @see mutex
class recursive_mutex {
public:
/// Constructor.
recursive_mutex() {
#if defined(_TTHREAD_WIN32_)
InitializeCriticalSection(&mHandle);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mHandle, &attr);
#endif
}
/// Destructor.
~recursive_mutex() {
#if defined(_TTHREAD_WIN32_)
DeleteCriticalSection(&mHandle);
#else
pthread_mutex_destroy(&mHandle);
#endif
}
/// Lock the mutex.
/// The method will block the calling thread until a lock on the mutex can
/// be obtained. The mutex remains locked until @c unlock() is called.
/// @see lock_guard
inline void lock() {
#if defined(_TTHREAD_WIN32_)
EnterCriticalSection(&mHandle);
#else
pthread_mutex_lock(&mHandle);
#endif
}
/// Try to lock the mutex.
/// The method will try to lock the mutex. If it fails, the function will
/// return immediately (non-blocking).
/// @return @c true if the lock was acquired, or @c false if the lock could
/// not be acquired.
inline bool try_lock() {
#if defined(_TTHREAD_WIN32_)
return TryEnterCriticalSection(&mHandle) ? true : false;
#else
return (pthread_mutex_trylock(&mHandle) == 0) ? true : false;
#endif
}
/// Unlock the mutex.
/// If any threads are waiting for the lock on this mutex, one of them will
/// be unblocked.
inline void unlock() {
#if defined(_TTHREAD_WIN32_)
LeaveCriticalSection(&mHandle);
#else
pthread_mutex_unlock(&mHandle);
#endif
}
_TTHREAD_DISABLE_ASSIGNMENT(recursive_mutex)
private:
#if defined(_TTHREAD_WIN32_)
CRITICAL_SECTION mHandle;
#else
pthread_mutex_t mHandle;
#endif
friend class condition_variable;
};
/// Lock guard class.
/// The constructor locks the mutex, and the destructor unlocks the mutex, so
/// the mutex will automatically be unlocked when the lock guard goes out of
/// scope. Example usage:
/// @code
/// mutex m;
/// int counter;
///
/// void increment()
/// {
/// lock_guard<mutex> guard(m);
/// ++ counter;
/// }
/// @endcode
template <class T>
class lock_guard {
public:
typedef T mutex_type;
lock_guard() : mMutex(0) {}
/// The constructor locks the mutex.
explicit lock_guard(mutex_type & aMutex) {
mMutex = &aMutex;
mMutex->lock();
}
/// The destructor unlocks the mutex.
~lock_guard() {
if (mMutex)
mMutex->unlock();
}
private:
mutex_type * mMutex;
};
/// Condition variable class.
/// This is a signalling object for synchronizing the execution flow for
/// several threads. Example usage:
/// @code
/// // Shared data and associated mutex and condition variable objects
/// int count;
/// mutex m;
/// condition_variable cond;
///
/// // Wait for the counter to reach a certain number
/// void wait_counter(int targetCount)
/// {
/// lock_guard<mutex> guard(m);
/// while(count < targetCount)
/// cond.wait(m);
/// }
///
/// // Increment the counter, and notify waiting threads
/// void increment()
/// {
/// lock_guard<mutex> guard(m);
/// ++ count;
/// cond.notify_all();
/// }
/// @endcode
class condition_variable {
public:
/// Constructor.
#if defined(_TTHREAD_WIN32_)
condition_variable();
#else
condition_variable() {
pthread_cond_init(&mHandle, NULL);
}
#endif
/// Destructor.
#if defined(_TTHREAD_WIN32_)
~condition_variable();
#else
~condition_variable() {
pthread_cond_destroy(&mHandle);
}
#endif
/// Wait for the condition.
/// The function will block the calling thread until the condition variable
/// is woken by @c notify_one(), @c notify_all() or a spurious wake up.
/// @param[in] aMutex A mutex that will be unlocked when the wait operation
/// starts, an locked again as soon as the wait operation is finished.
template <class _mutexT>
inline void wait(_mutexT & aMutex) {
#if defined(_TTHREAD_WIN32_)
// Increment number of waiters
EnterCriticalSection(&mWaitersCountLock);
++ mWaitersCount;
LeaveCriticalSection(&mWaitersCountLock);
// Release the mutex while waiting for the condition (will decrease
// the number of waiters when done)...
aMutex.unlock();
_wait();
aMutex.lock();
#else
pthread_cond_wait(&mHandle, &aMutex.mHandle);
#endif
}
/// Notify one thread that is waiting for the condition.
/// If at least one thread is blocked waiting for this condition variable,
/// one will be woken up.
/// @note Only threads that started waiting prior to this call will be
/// woken up.
#if defined(_TTHREAD_WIN32_)
void notify_one();
#else
inline void notify_one() {
pthread_cond_signal(&mHandle);
}
#endif
/// Notify all threads that are waiting for the condition.
/// All threads that are blocked waiting for this condition variable will
/// be woken up.
/// @note Only threads that started waiting prior to this call will be
/// woken up.
#if defined(_TTHREAD_WIN32_)
void notify_all();
#else
inline void notify_all() {
pthread_cond_broadcast(&mHandle);
}
#endif
_TTHREAD_DISABLE_ASSIGNMENT(condition_variable)
private:
#if defined(_TTHREAD_WIN32_)
void _wait();
HANDLE mEvents[2]; ///< Signal and broadcast event HANDLEs.
unsigned int mWaitersCount; ///< Count of the number of waiters.
CRITICAL_SECTION mWaitersCountLock; ///< Serialize access to mWaitersCount.
#else
pthread_cond_t mHandle;
#endif
};
/// Thread class.
class thread {
public:
#if defined(_TTHREAD_WIN32_)
typedef HANDLE native_handle_type;
#else
typedef pthread_t native_handle_type;
#endif
class id;
/// Default constructor.
/// Construct a @c thread object without an associated thread of execution
/// (i.e. non-joinable).
thread() : mHandle(0), mNotAThread(true)
#if defined(_TTHREAD_WIN32_)
, mWin32ThreadID(0)
#endif
{}
/// Thread starting constructor.
/// Construct a @c thread object with a new thread of execution.
/// @param[in] aFunction A function pointer to a function of type:
/// <tt>void fun(void * arg)</tt>
/// @param[in] aArg Argument to the thread function.
/// @note This constructor is not fully compatible with the standard C++
/// thread class. It is more similar to the pthread_create() (POSIX) and
/// CreateThread() (Windows) functions.
thread(void (*aFunction)(void *), void * aArg);
/// Destructor.
/// @note If the thread is joinable upon destruction, @c std::terminate()
/// will be called, which terminates the process. It is always wise to do
/// @c join() before deleting a thread object.
~thread();
/// Wait for the thread to finish (join execution flows).
/// After calling @c join(), the thread object is no longer associated with
/// a thread of execution (i.e. it is not joinable, and you may not join
/// with it nor detach from it).
void join();
/// Check if the thread is joinable.
/// A thread object is joinable if it has an associated thread of execution.
bool joinable() const;
/// Detach from the thread.
/// After calling @c detach(), the thread object is no longer assicated with
/// a thread of execution (i.e. it is not joinable). The thread continues
/// execution without the calling thread blocking, and when the thread
/// ends execution, any owned resources are released.
void detach();
/// Return the thread ID of a thread object.
id get_id() const;
/// Get the native handle for this thread.
/// @note Under Windows, this is a @c HANDLE, and under POSIX systems, this
/// is a @c pthread_t.
inline native_handle_type native_handle() {
return mHandle;
}
/// Determine the number of threads which can possibly execute concurrently.
/// This function is useful for determining the optimal number of threads to
/// use for a task.
/// @return The number of hardware thread contexts in the system.
/// @note If this value is not defined, the function returns zero (0).
static unsigned hardware_concurrency();
_TTHREAD_DISABLE_ASSIGNMENT(thread)
private:
native_handle_type mHandle; ///< Thread handle.
mutable mutex mDataMutex; ///< Serializer for access to the thread private data.
bool mNotAThread; ///< True if this object is not a thread of execution.
void * ti_copy;
#if defined(_TTHREAD_WIN32_)
unsigned int mWin32ThreadID; ///< Unique thread ID (filled out by _beginthreadex).
#endif
// This is the internal thread wrapper function.
#if defined(_TTHREAD_WIN32_)
static unsigned WINAPI wrapper_function(void * aArg);
#else
static void * wrapper_function(void * aArg);
#endif
};
/// Thread ID.
/// The thread ID is a unique identifier for each thread.
/// @see thread::get_id()
class thread::id {
public:
/// Default constructor.
/// The default constructed ID is that of thread without a thread of
/// execution.
id() : mId(0) {};
id(unsigned long int aId) : mId(aId) {};
id(const id & aId) : mId(aId.mId) {};
inline id & operator=(const id & aId) {
mId = aId.mId;
return *this;
}
inline friend bool operator==(const id & aId1, const id & aId2) {
return (aId1.mId == aId2.mId);
}
inline friend bool operator!=(const id & aId1, const id & aId2) {
return (aId1.mId != aId2.mId);
}
inline friend bool operator<=(const id & aId1, const id & aId2) {
return (aId1.mId <= aId2.mId);
}
inline friend bool operator<(const id & aId1, const id & aId2) {
return (aId1.mId < aId2.mId);
}
inline friend bool operator>=(const id & aId1, const id & aId2) {
return (aId1.mId >= aId2.mId);
}
inline friend bool operator>(const id & aId1, const id & aId2) {
return (aId1.mId > aId2.mId);
}
inline friend std::ostream & operator <<(std::ostream & os, const id & obj) {
os << obj.mId;
return os;
}
private:
unsigned long int mId;
};
// Related to <ratio> - minimal to be able to support chrono.
typedef long long __intmax_t;
/// Minimal implementation of the @c ratio class. This class provides enough
/// functionality to implement some basic @c chrono classes.
template <__intmax_t N, __intmax_t D = 1> class ratio {
public:
static double _as_double() {
return double(N) / double(D);
}
};
/// Minimal implementation of the @c chrono namespace.
/// The @c chrono namespace provides types for specifying time intervals.
namespace chrono {
/// Duration template class. This class provides enough functionality to
/// implement @c this_thread::sleep_for().
template <class _Rep, class _Period = ratio<1> > class duration {
private:
_Rep rep_;
public:
typedef _Rep rep;
typedef _Period period;
/// Construct a duration object with the given duration.
template <class _Rep2>
explicit duration(const _Rep2 & r) : rep_(r) {};
/// Return the value of the duration object.
rep count() const {
return rep_;
}
};
// Standard duration types.
typedef duration<__intmax_t, ratio<1, 1000000000> > nanoseconds; ///< Duration with the unit nanoseconds.
typedef duration<__intmax_t, ratio<1, 1000000> > microseconds; ///< Duration with the unit microseconds.
typedef duration<__intmax_t, ratio<1, 1000> > milliseconds; ///< Duration with the unit milliseconds.
typedef duration<__intmax_t> seconds; ///< Duration with the unit seconds.
typedef duration<__intmax_t, ratio<60> > minutes; ///< Duration with the unit minutes.
typedef duration<__intmax_t, ratio<3600> > hours; ///< Duration with the unit hours.
}
/// The namespace @c this_thread provides methods for dealing with the
/// calling thread.
namespace this_thread {
/// Return the thread ID of the calling thread.
thread::id get_id();
/// Yield execution to another thread.
/// Offers the operating system the opportunity to schedule another thread
/// that is ready to run on the current processor.
inline void yield() {
#if defined(_TTHREAD_WIN32_)
Sleep(0);
#else
sched_yield();
#endif
}
/// Blocks the calling thread for a period of time.
/// @param[in] aTime Minimum time to put the thread to sleep.
/// Example usage:
/// @code
/// // Sleep for 100 milliseconds
/// this_thread::sleep_for(chrono::milliseconds(100));
/// @endcode
/// @note Supported duration types are: nanoseconds, microseconds,
/// milliseconds, seconds, minutes and hours.
template <class _Rep, class _Period> void sleep_for(const chrono::duration<_Rep, _Period> & aTime) {
#if defined(_TTHREAD_WIN32_)
Sleep(int(double(aTime.count()) * (1000.0 * _Period::_as_double()) + 0.5));
#else
usleep(int(double(aTime.count()) * (1000000.0 * _Period::_as_double()) + 0.5));
#endif
}
}
}
// Define/macro cleanup
#undef _TTHREAD_DISABLE_ASSIGNMENT
#endif // _TINYTHREAD_H_

1041
lib/ts_packet.cpp Normal file

File diff suppressed because it is too large Load diff

203
lib/ts_packet.h Normal file
View file

@ -0,0 +1,203 @@
/// \file ts_packet.h
/// Holds all headers for the TS Namespace.
#pragma once
#include <string>
#include <map>
#include <cmath>
#include <stdint.h>//for uint64_t
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include "dtsc.h"
#include "checksum.h"
/// Holds all TS processing related code.
namespace TS {
///Class for reading and writing TS Streams. The class is capable of analyzing a packet of 188 bytes
///and calculating key values
class Packet {
public:
//Constructors and fillers
Packet();
~Packet();
bool FromPointer(const char * data);
bool FromFile(FILE * data);
//Base properties
void setPID(int NewPID);
unsigned int getPID() const;
void setContinuityCounter(int NewContinuity);
int getContinuityCounter() const;
void setPCR(int64_t NewVal);
int64_t getPCR() const;
int64_t getOPCR() const;
void setAdaptationField(int NewVal);
int getAdaptationField() const;
int getAdaptationFieldLen() const;
unsigned int getTransportScramblingControl() const;
//Flags
void setUnitStart(bool newVal);
bool getUnitStart() const;
void setRandomAccess(bool newVal);
bool getRandomAccess() const;
void setDiscontinuity(bool newVal);
bool hasDiscontinuity() const;
bool hasPCR() const;
bool hasOPCR() const;
bool hasSplicingPoint() const;
bool hasTransportError() const;
bool hasPriority() const;
bool hasESpriority() const;
//Helper functions
operator bool() const;
bool isPMT() const;
void clear();
void setDefaultPAT();
unsigned int getDataSize() const;
unsigned int getBytesFree() const;
int fillFree(const char * PackageData, int maxLen);
void addStuffing();
void updPos(unsigned int newPos);
//PES helpers
static std::string & getPESVideoLeadIn(unsigned int len, unsigned long long PTS, unsigned long long offset, bool isAligned);
static std::string & getPESAudioLeadIn(unsigned int len, unsigned long long PTS);
//Printers and writers
std::string toPrettyString(size_t indent = 0, int detailLevel = 3) const;
const char * getPayload() const;
int getPayloadLength() const;
const char * checkAndGetBuffer() const;
protected:
char strBuf[189];
unsigned int pos;
};
class ProgramAssociationTable : public Packet {
public:
char getOffset() const;
char getTableId() const;
short getSectionLength() const;
short getTransportStreamId() const;
char getVersionNumber() const;
bool getCurrentNextIndicator() const;
char getSectionNumber() const;
char getLastSectionNumber() const;
short getProgramCount() const;
short getProgramNumber(short index) const;
short getProgramPID(short index) const;
int getCRC() const;
std::string toPrettyString(size_t indent) const;
};
class ProgramMappingEntry {
public:
ProgramMappingEntry(char * begin, char * end);
operator bool() const;
int getStreamType() const;
std::string getCodec() const;
std::string getStreamTypeString() const;
int getElementaryPid() const;
int getESInfoLength() const;
char * getESInfo() const;
void advance();
private:
char* data;
char* boundary;
};
class ProgramMappingTable : public Packet {
public:
ProgramMappingTable();
char getOffset() const;
void setOffset(char newVal);
char getTableId() const;
void setTableId(char newVal);
short getSectionLength() const;
void setSectionLength(short newVal);
short getProgramNumber() const;
void setProgramNumber(short newVal);
char getVersionNumber() const;
void setVersionNumber(char newVal);
bool getCurrentNextIndicator() const;
void setCurrentNextIndicator(bool newVal);
char getSectionNumber() const;
void setSectionNumber(char newVal);
char getLastSectionNumber() const;
void setLastSectionNumber(char newVal);
short getPCRPID() const;
void setPCRPID(short newVal);
short getProgramInfoLength() const;
void setProgramInfoLength(short newVal);
short getProgramCount() const;
void setProgramCount(short newVal);
ProgramMappingEntry getEntry(int index) const;
void setStreamType(char newVal, short index);
char getStreamType(short index) const;
void setElementaryPID(short newVal, short index);
short getElementaryPID(short index) const;
void setESInfoLength(short newVal,short index);
short getESInfoLength(short index) const;
int getCRC() const;
void calcCRC();
std::string toPrettyString(size_t indent) const;
};
/// Constructs an audio header to be used on each audio frame.
/// The length of this header will ALWAYS be 7 bytes, and has to be
/// prepended on each audio frame.
/// \param FrameLen the length of the current audio frame.
/// \param initData A string containing the initalization data for this track's codec.
static inline std::string getAudioHeader(int FrameLen, std::string initData) {
char StandardHeader[7] = {0xFF, 0xF1, 0x00, 0x00, 0x00, 0x1F, 0xFC};
FrameLen += 7;
StandardHeader[2] = ((((initData[0] >> 3) - 1) << 6) & 0xC0); //AAC Profile - 1 ( First two bits )
StandardHeader[2] |= ((((initData[0] & 0x07) << 1) | ((initData[1] >> 7) & 0x01)) << 2); //AAC Frequency Index
StandardHeader[2] |= ((initData[1] & 0x20) >> 5); //AAC Channel Config
StandardHeader[3] = ((initData[1] & 0x18) << 3); //AAC Channel Config (cont.)
StandardHeader[3] |= ((FrameLen & 0x00001800) >> 11);
StandardHeader[4] = ((FrameLen & 0x000007F8) >> 3);
StandardHeader[5] |= ((FrameLen & 0x00000007) << 5);
return std::string(StandardHeader, 7);
}
/// A standard Program Association Table, as generated by FFMPEG.
/// Seems to be independent of the stream.
//0x47 = sync byte
//0x4000 = transport error(1) = 0, payload unit start(1) = 1, priority(1) = 0, PID(13) = 0
//0x10 = transportscrambling(2) = 0, adaptation(2) = 1, continuity(4) = 0
//0x00 = pointer = 0
//0x00 = table ID = 0 = PAT
//0xB00D = section syntax(1) = 1, 0(1)=0, reserved(2) = 3, section_len(12) = 13
//0x0001 = transport stream id = 1
//0xC1 = reserved(2) = 3, version(5)=0, curr_next_indi(1) = 1
//0x00 = section_number = 0
//0x00 = last_section_no = 0
//0x0001 = ProgNo = 1
//0xF000 = reserved(3) = 7, network pid = 4096
//0x2AB104B2 = CRC32
static char PAT[188] = {0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xB0, 0x0D, 0x00, 0x01, 0xC1, 0x00, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x2A, 0xB1, 0x04,
0xB2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
const char * createPMT(std::set<unsigned long>& selectedTracks, DTSC::Meta& myMeta, int contCounter=0);
} //TS namespace

438
lib/vorbis.cpp Normal file
View file

@ -0,0 +1,438 @@
#include "vorbis.h"
#include "defines.h"
#include <stdlib.h>
#include <string.h>
#include <sstream>
#include <arpa/inet.h>
#include "bitstream.h"
#include <deque>
#include <cstdio>
#include <iostream>
namespace vorbis{
long long unsigned int reverseByte16(long long unsigned int input){
return ((input & 0xFF00) >> 8) | ((input & 0xFF) << 8);
}
long long unsigned int reverseByte24(long long unsigned int input){
return ((input & 0xFF0000) >> 16)| (input & 0xFF00) | ((input & 0xFF) << 16);
}
long long unsigned int reverseByte32(long long unsigned int input){
return ((input & 0xFF000000) >> 24)| ((input & 0xFF0000) >> 8) | ((input & 0xFF00) << 8) | ((input & 0xFF) << 24);
}
header::header(char * newData, unsigned int length){
data = newData;
datasize = length;
}
header::~header(){
}
int header::getHeaderType(){
return (int)(data[0]);
}
long unsigned int header::getVorbisVersion(){
if (getHeaderType() == 1){
return getInt32(7);
}else{
return 0;
}
}
char header::getAudioChannels(){
if (getHeaderType() == 1){
return data[11];
}else{
return 0;
}
}
long unsigned int header::getAudioSampleRate(){
if (getHeaderType() == 1){
return ntohl(getInt32(12));
}else{
return 0;
}
}
long unsigned int header::getBitrateMaximum(){
if (getHeaderType() == 1){
return getInt32(16);
}else{
return 0;
}
}
long unsigned int header::getBitrateNominal(){
if (getHeaderType() == 1){
return getInt32(20);
}else{
return 0;
}
}
long unsigned int header::getBitrateMinimum(){
if (getHeaderType() == 1){
return getInt32(24);
}else{
return 0;
}
}
char header::getBlockSize0(){
if (getHeaderType() == 1){
return data[28] & 0x0F;
}else{
return 0;
}
}
char header::getBlockSize1(){
if (getHeaderType() == 1){
return (data[28]>>4) & 0x0F;
}else{
return 0;
}
}
char header::getFramingFlag(){
if (getHeaderType() == 1){
return data[29];
}else{
return 0;
}
}
bool header::checkDataSize(unsigned int size){
if (size > datasize){
void* tmp = realloc(data,size);
if (tmp){
data = (char*)tmp;
datasize = size;
return true;
}else{
return false;
}
}else{
return true;
}
}
bool header::validate(){
switch(getHeaderType()){
case 1://ID header
if (datasize!=30){
return false;
}
if (getVorbisVersion()!=0){
return false;
}
if (getAudioChannels()<=0){
return false;
};
if (getAudioSampleRate()<=0) {
return false;
}
if (getBlockSize0()>getBlockSize1()){
return false;
};
if (getFramingFlag()!=1){
return false;
};
break;
case 3://comment header
break;
case 5://init header
break;
default:
return false;
break;
}
return true;
}
bool isHeader(const char * newData, unsigned int length){
if (length < 7){
return false;
}
if(memcmp(newData+1, "vorbis", 6)!=0){
return false;
}
return true;
}
bool header::isHeader(){
return ::vorbis::isHeader(data, datasize);
}
/*
bool header::read(const char* newData, unsigned int length){
if (length < 7){
return false;
}
if(memcmp(newData+1, "vorbis", 6)!=0){
return false;
}
if (checkDataSize(length)){
memcpy(data, newData, length);
}else{
return false;
}
return true;
}*/
std::deque<mode> header::readModeDeque(char audioChannels){
Utils::bitstreamLSBF stream;
stream.append(data,datasize);
stream.skip(28); //skipping common header part
stream.skip(28); //skipping common header part
char codebook_count = stream.get(8) + 1;
for (int i = 0; i < codebook_count; i++){
long long unsigned int CMN = stream.get(24);
if (CMN != 0x564342){
DEBUG_MSG(DLVL_WARN,"Is dit het? VCB != %c%c%c", (char)(CMN >> 16), (char)(CMN >> 8), (char)CMN);
exit(1);
}
unsigned short codebook_dimensions = stream.get(16);
unsigned int codebook_entries = stream.get(24);
bool orderedFlag = stream.get(1);
if (!orderedFlag){
bool sparseFlag = stream.get(1);
if (sparseFlag){//sparse flag
//sparse handling
for (unsigned int o = 0; o < codebook_entries; o++){
if (stream.get(1)){
stream.skip(5);
}
}
}else{
for (unsigned int o = 0; o < codebook_entries; o++){
stream.skip(5);
}
}
}else{
//ordered handling
stream.skip(5);
for (unsigned int o = 0; o < codebook_entries; o++){
int readnow = (std::log(codebook_entries-o))/(std::log(2))+1;
o+=stream.get(readnow);
}
}
char codebook_lookup_type = stream.get(4);
if (codebook_lookup_type != 0){
stream.skip(32);
stream.skip(32);
char codebook_value_bits = stream.get(4) + 1;
stream.skip(1);
unsigned int codebook_lookup_value;
if (codebook_lookup_type == 1){
codebook_lookup_value = std::pow(codebook_entries, (1.0/codebook_dimensions));
}else{
codebook_lookup_value = codebook_entries * codebook_dimensions;
}
for (unsigned int i = 0; i < codebook_lookup_value; i++){
stream.skip(codebook_value_bits);
}
}
}
//end of codebooks
//time domain transforms
long long unsigned int TDT = stream.get(6) + 1;
for (unsigned int i = 0; i < TDT; i++){
stream.skip(16);
}
//Floors
long long unsigned int floors = stream.get(6) + 1;
for (unsigned int i = 0; i < floors; i++){
long long unsigned int floorType = stream.get(16);
switch(floorType){
case 0:{
DEBUG_MSG(DLVL_WARN, "FloorType 0 in vorbis setup header not tested!");
stream.skip(8);//order
stream.skip(16);//rate
stream.skip(16);//bark_map_size
stream.skip(6);//amplitude bits
stream.skip(8);//amplitude offset
long long unsigned int numberOfBooks = stream.get(4)+1;
for (unsigned int o = 0; o < numberOfBooks; o++){
stream.skip(8);//book list array
}
break;
}
case 1:{
long long unsigned int floorPartitions = stream.get(5);
long long int max = -1;
std::deque<int> partition_class;
for (unsigned int o = 0; o < floorPartitions; o++){
long long int temp = stream.get(4);
partition_class.push_back(temp);
if (temp>max) max = temp;
}
std::deque<int> class_dimensions;
for (int o = 0; o <= max; o++){
class_dimensions.push_back(stream.get(3)+1);//class dimensions PUT IN ARRAY!
int class_subclass = stream.get(2);
if (class_subclass !=0){
stream.skip(8);//class_master_books
}
for (int p = 0; p < (1<<class_subclass); p++){
stream.skip(8);
}
}
stream.skip(2);//floor1_multiplier
int rangebits = stream.get(4);//rangebits
long long unsigned int count = 0;
long long unsigned int skipper = 0;
for (unsigned int o = 0; o < floorPartitions; o++){
count += class_dimensions[(partition_class[o])];
while (skipper < count){
stream.skip(rangebits);
skipper ++;
}
}
break;
}
default:
exit(0);
}
}
//Residues
long long unsigned int residues = stream.get(6) + 1;
for(unsigned int i = 0; i < residues; i++){
std::deque<char> residueCascade;
long long unsigned int residueType = stream.get(16);
if(residueType<=2){
stream.skip(24);//residue begin
stream.skip(24);//residue end
stream.skip(24);//residue partition size
long long unsigned int residueClass = stream.get(6)+1;//residue classifications
stream.skip(8);//residue classbook
for (unsigned int o = 0; o < residueClass; o++){
char temp = stream.get(3);//low bits
bool bitFlag = stream.get(1);
if (bitFlag){
temp += stream.get(5) << 3;
}
residueCascade.push_back(temp);
}
for (unsigned int o = 0; o < residueClass; o++){
for (unsigned int p = 0; p < 7; p++){
if (((residueCascade[o] >> p) & 1) == 1){
stream.skip(8);
}else{
}
}
}
}else{
exit(0);
}
}
//Mappings
long long unsigned int mappings = stream.get(6) + 1;
for(unsigned int i = 0; i < mappings; i++){
long long unsigned int mapType = stream.get(16);
if (mapType == 0){
char mappingSubmaps = 1;
if (stream.get(1)==1){
mappingSubmaps = stream.get(4);//vorbis mapping submaps
}
long long unsigned int coupling_steps = 0;
if (stream.get(1)==1){
coupling_steps = stream.get(8)+1;
for (unsigned int o = 0; o<coupling_steps; o++){
int temp = (std::log((audioChannels-o)-1))/(std::log(2)) + 1;
if (temp>0){
stream.skip(temp);//mapping magnitude
stream.skip(temp);//mapping angle
}
}
}
char meh = stream.get(2);
if (meh != 0){
DEBUG_MSG(DLVL_ERROR, "Sanity check ==0 : %i", (int)meh);
exit(0);
}
if (mappingSubmaps > 1){
for (int o = 0; o < audioChannels; o++){
stream.skip(4);
}
}
for (int o = 0; o < mappingSubmaps; o++){
stream.skip(8);//placeholder
stream.skip(8);//vorbis Mapping subfloor
stream.skip(8);//vorbis mapping submap residue
}
}else{
exit(0);
}
}
//Modes
long long unsigned int modes = stream.get(6) + 1;
std::deque<mode> retVal;
for (unsigned int i = 0; i < modes; i++){
mode temp;
temp.blockFlag = stream.get(1);
temp.windowType = stream.get(16);
temp.transformType = stream.get(16);
temp.mapping = stream.get(8);
retVal.push_back(temp);
}
stream.skip(1);
return retVal;
}
uint32_t header::getInt32(size_t index){
if (datasize >= (index + 3)){
return (data[index] << 24) + (data[index + 1] << 16) + (data[index + 2] << 8) + data[index + 3];
}
return 0;
}
uint32_t header::getInt24(size_t index){
if (datasize >= (index + 2)){
return 0 + (data[index] << 16) + (data[index + 1] << 8) + data[index + 2];
}
return 0;
}
uint16_t header::getInt16(size_t index){
if (datasize >= (index + 1)){
return 0 + (data[index] << 8) + data[index + 1];
}
return 0;
}
std::string header::toPrettyString(size_t indent){
std::stringstream r;
r << std::string(indent+1,' ') << "Vorbis Header" << std::endl;
r << std::string(indent+2,' ') << "Magic Number: " << std::string(data + 1,6) << std::endl;
r << std::string(indent+2,' ') << "Header Type: " << getHeaderType() << std::endl;
if (getHeaderType() == 1){
r << std::string(indent+2,' ') << "ID Header" << std::endl;
r << std::string(indent+2,' ') << "VorbisVersion: " << getVorbisVersion() << std::endl;
r << std::string(indent+2,' ') << "AudioChannels: " << (int)getAudioChannels() << std::endl;
r << std::string(indent+2,' ') << "BitrateMaximum: " << std::hex << getBitrateMaximum() << std::dec << std::endl;
r << std::string(indent+2,' ') << "BitrateNominal: " << std::hex << getBitrateNominal() << std::dec << std::endl;
r << std::string(indent+2,' ') << "BitrateMinimum: " << std::hex << getBitrateMinimum() << std::dec << std::endl;
r << std::string(indent+2,' ') << "BlockSize0: " << (int)getBlockSize0() << std::endl;
r << std::string(indent+2,' ') << "BlockSize1: " << (int)getBlockSize1() << std::endl;
r << std::string(indent+2,' ') << "FramingFlag: " << (int)getFramingFlag() << std::endl;
} else if (getHeaderType() == 3){
r << std::string(indent+2,' ') << "Comment Header" << std::endl;
} else if (getHeaderType() == 5){
r << std::string(indent+2,' ') << "Setup Header" << std::endl;
}
return r.str();
}
}

52
lib/vorbis.h Normal file
View file

@ -0,0 +1,52 @@
#pragma once
#include <cmath>
#include <sys/types.h>
#include <stdint.h>
#include <string>
#include <deque>
namespace vorbis{
struct mode{
bool blockFlag;
unsigned short windowType;
unsigned short transformType;
char mapping;
};
inline unsigned int ilog(unsigned int input){
return (std::log(input))/(std::log(2))+1;
}
bool isHeader(const char * newData, unsigned int length);
class header{
public:
~header();
header(char* newData, unsigned int length);
int getHeaderType();
long unsigned int getVorbisVersion();
char getAudioChannels();
long unsigned int getAudioSampleRate();
long unsigned int getBitrateMaximum();
long unsigned int getBitrateNominal();
long unsigned int getBitrateMinimum();
char getBlockSize0();
char getBlockSize1();
char getFramingFlag();
std::string toPrettyString(size_t indent = 0);
std::deque<mode> readModeDeque(char audioChannels);
bool isHeader();
unsigned int getDataSize(){
return datasize;
}
protected:
uint32_t getInt32(size_t index);
uint32_t getInt24(size_t index);
uint16_t getInt16(size_t index);
private:
std::deque<mode> modes;
char* data;
unsigned int datasize;
bool checkDataSize(unsigned int size);
bool validate();
};
}

335
test/abst_test.cpp Normal file
View file

@ -0,0 +1,335 @@
/// \file abst_test.cpp
/// Tests the bootstrap generation functions by comparing to a known good bootstrap.
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <string.h>
#include <mist/mp4.h>
/// This is a known good bootstrap retrieved from a wowza demo server.
unsigned char __data[] = {
0x00, 0x00, 0x0c, 0xce, 0x61, 0x62, 0x73, 0x74, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x09, 0x19, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1a, 0x61, 0x73, 0x72, 0x74, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc7, 0x01,
0x00, 0x00, 0x0c, 0x86, 0x61, 0x66, 0x72, 0x74, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x17, 0x70, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x28, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2e, 0xe0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3a, 0x98, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x50, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x52, 0x08, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x5d, 0xc0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x78, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x75, 0x30, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0xe8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xa0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x98, 0x58, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xa4, 0x10, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xc8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xbb, 0x80, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc7, 0x38, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0xf0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xde, 0xa8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xea, 0x60, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x18, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x01, 0xd0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x0d, 0x88, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x19, 0x40, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x24, 0xf8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x30, 0xb0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x68, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x48, 0x20, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x53, 0xd8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x5f, 0x90, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x6b, 0x48, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x77, 0x00, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x82, 0xb8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x8e, 0x70, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x9a, 0x28, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa5, 0xe0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xb1, 0x98, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xbd, 0x50, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc9, 0x08, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xd4, 0xc0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xe0, 0x78, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xec, 0x30, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xf7, 0xe8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x03, 0xa0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0f, 0x58, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x1b, 0x10, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x26, 0xc8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x32, 0x80, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x3e, 0x38, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x49, 0xf0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x55, 0xa8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x61, 0x60, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x6d, 0x18, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0xd0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0x84, 0x88, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x90, 0x40, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x9b, 0xf8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0xa7, 0xb0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0xb3, 0x68, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x20, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0xca, 0xd8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0xd6, 0x90, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xe2, 0x48, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0xee, 0x00, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0xf9, 0xb8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x70, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x11, 0x28, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x1c, 0xe0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x28, 0x98, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x34, 0x50, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x40, 0x08, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4b, 0xc0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x57, 0x78, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x63, 0x30, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x6e, 0xe8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x7a, 0xa0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x86, 0x58, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x92, 0x10, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x9d, 0xc8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0xa9, 0x80, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xb5, 0x38, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0xc0, 0xf0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0xcc, 0xa8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x60, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0xe4, 0x18, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0xef, 0xd0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xfb, 0x88, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x07, 0x40, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x12, 0xf8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1e, 0xb0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x2a, 0x68, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x36, 0x20, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x41, 0xd8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x4d, 0x90, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x59, 0x48, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x65, 0x00, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x70, 0xb8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x7c, 0x70, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x88, 0x28, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x93, 0xe0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x9f, 0x98, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xab, 0x50, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0xb7, 0x08, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0xc2, 0xc0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xce, 0x78, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0xda, 0x30, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0xe5, 0xe8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xf1, 0xa0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0xfd, 0x58, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x09, 0x10, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x14, 0xc8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0x20, 0x80, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x2c, 0x38, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x37, 0xf0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0x43, 0xa8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x4f, 0x60, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x5b, 0x18, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0x66, 0xd0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x72, 0x88, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7e, 0x40, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0x89, 0xf8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0x95, 0xb0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xa1, 0x68, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0xad, 0x20, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0xb8, 0xd8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc4, 0x90, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0xd0, 0x48, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0xdc, 0x00, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xe7, 0xb8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
0xf3, 0x70, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00,
0x00, 0x00, 0x00, 0x05, 0xff, 0x28, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x0a, 0xe0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x16, 0x98, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x22, 0x50, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x2e, 0x08, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x39, 0xc0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x45, 0x78, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x51, 0x30, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x5c, 0xe8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x68, 0xa0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x74, 0x58, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x80, 0x10, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x8b, 0xc8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x97, 0x80, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0xa3, 0x38, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0xae, 0xf0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xba, 0xa8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0xc6, 0x60, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0xd2, 0x18, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xdd, 0xd0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0xe9, 0x88, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0xf5, 0x40, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0xf8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x0c, 0xb0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x18, 0x68, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x24, 0x20, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x2f, 0xd8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x3b, 0x90, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x48, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x53, 0x00, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xa2, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x5e, 0xb8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x6a, 0x70, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x76, 0x28, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x81, 0xe0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x8d, 0x98, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0x99, 0x50, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xa5, 0x08, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb0, 0xc0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xbc, 0x78, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xc8, 0x30, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd3, 0xe8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xdf, 0xa0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xeb, 0x58, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf7, 0x10, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0x02, 0xc8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x0e, 0x80, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1a, 0x38, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0x25, 0xf0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x31, 0xa8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3d, 0x60, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0x49, 0x18, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x54, 0xd0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x60, 0x88, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0x6c, 0x40, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xba, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x77, 0xf8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x83, 0xb0, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0x8f, 0x68, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x9b, 0x20, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xa6, 0xd8, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0xb2, 0x90, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0xbe, 0x48, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xca, 0x00, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0xd5, 0xb8, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0xe1, 0x70, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xed, 0x28, 0x00, 0x00,
0x0b, 0xb8, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0xf8, 0xe0, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00,
0x00, 0x00, 0x00, 0x09, 0x04, 0x98, 0x00, 0x00, 0x0b, 0xb8, 0x00, 0x00,
0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, 0x50, 0x00, 0x00,
0x09, 0x9a
};
unsigned int __data_len = 3278;
/// Generates a bootstrap equal to the above file, then compares.
/// \returns Zero if they are equal (test success), other values otherwise.
int main(int argc, char ** argv) {
std::string temp;
MP4::ASRT asrt;
asrt.setVersion(1);
asrt.setQualityEntry(temp, 0);
asrt.setSegmentRun(1, 199, 0);
MP4::AFRT afrt;
afrt.setVersion(1);
afrt.setTimeScale(1000);
afrt.setQualityEntry(temp, 0);
MP4::afrt_runtable afrtrun;
for (int i = 0; i < 198; i++){
afrtrun.firstFragment = i+1;
afrtrun.firstTimestamp = 3000*i;
afrtrun.duration = 3000;
afrt.setFragmentRun(afrtrun, i);
}
afrtrun.firstFragment = 199;
afrtrun.firstTimestamp = 594000;
afrtrun.duration = 2458;
afrt.setFragmentRun(afrtrun, 198);
MP4::ABST abst;
abst.setVersion(1);
abst.setBootstrapinfoVersion(1);
abst.setProfile(0);
abst.setLive(false);
abst.setUpdate(false);
abst.setTimeScale(1000);
abst.setCurrentMediaTime(596458);
abst.setSmpteTimeCodeOffset(0);
abst.setMovieIdentifier(temp);
abst.setServerEntry(temp, 0);
abst.setQualityEntry(temp, 0);
abst.setDrmData(temp);
abst.setMetaData(temp);
abst.setSegmentRunTable(asrt, 0);
abst.setFragmentRunTable(afrt, 0);
if (abst.boxedSize() != __data_len){return 42;}
return memcmp(abst.asBox(), __data, __data_len);
}